From 2aa7f868b244637dbc3e6835675e0cc6feccd890 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 22 Dec 2025 02:16:51 +0700 Subject: [PATCH 01/23] Update issue templates (#128) * Bump the npm_and_yarn group across 1 directory with 1 update Bumps the npm_and_yarn group with 1 update in the / directory: [express](https://github.com/expressjs/express). Updates `express` from 4.18.2 to 4.19.2 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: direct:development dependency-group: npm_and_yarn-security-group ... Signed-off-by: dependabot[bot] * Create SECURITY.md * Set up CI with Azure Pipelines [skip ci] * Create CNAME * Create fortify.yml * Update issue templates * Update CNAME * fix: upgrade @tanstack/react-query from 5.45.1 to 5.64.2 Snyk has created this PR to upgrade @tanstack/react-query from 5.45.1 to 5.64.2. See this package in npm: @tanstack/react-query See this project in Snyk: https://app.snyk.io/org/dargon789/project/bb845543-cbee-4e11-8cf9-8bfdf9205bf1?utm_source=github&utm_medium=referral&page=upgrade-pr * Create config.yml (#46) Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Support multiple identity signers in sessions configuration * Device signers can approve implicit sessions * Remove invalid test * Fix recursion * Fix comment * Improve test stability by reducing race conditions * Do not set passkey signer as identity signer * Use length checks * Throw on missing identity signer * Encoding requires identity signer to encode * Fix test * Refactor/types namings tsdoc redundant code (#880) * refactor types, namings, ts doc * fix session response payload * change parameter name * change parameter name * change type in tests * improve types and dapp client methods * fix session test to use new types * refactor * refactor implicit sessions array in chain session manager * remove unused types * remove unused types and add ConnectionError * update pnpm lock * move reusable session types to wallet-core * Update some imports and update some response type names --------- Co-authored-by: Tolgahan Arikan * Fix check for explicit session for the updated type in dapp-client * Update api.gen.ts and relayer.gen.ts * Add missing chainId for dapp client event * Fix initializing new chain session manager on redirect * Add support for non-viem, custom Sequence chains (#882) * Provider sent to prepareBlankEnvelope * Update fortify.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Add session signature decoding * Add feeTokens endpoint to relayer (#885) * const for node length * Clearer blacklist size encoding * identity signer node length * add getFeeTokens to dapp client (#889) * add getFeeTokens to dapp client * fix typo * make getFeeTokens independent of chain session manager and initialize state (#890) * make getFeeTokens independent of chain session manager and initialized state * remove getFeeTokens from chain session manager * Throw specific error when trying to sign with an expired session (#887) * Throw when supported session signer is expired * Fix tests * Make dapp-client implicit sessions chain agnostic (#893) * Add Monad, remove LAOS and Root Network * Add support for sessionless dapp connection (#896) * Refactor relayer package & update dependant packages (#891) * refactor exports for relayer (#900) * Add Arc Testnet * Fix changelog config * Sessionless connection upgrade and error handling in DappClient (#902) * dapp-client: add sessionless snapshot restore flow * Bump the npm_and_yarn group across 3 directories with 1 update Bumps the npm_and_yarn group with 1 update in the / directory: [happy-dom](https://github.com/capricorn86/happy-dom). Bumps the npm_and_yarn group with 1 update in the /packages/wallet/dapp-client directory: [happy-dom](https://github.com/capricorn86/happy-dom). Bumps the npm_and_yarn group with 1 update in the /packages/wallet/wdk directory: [happy-dom](https://github.com/capricorn86/happy-dom). Updates `happy-dom` from 17.6.3 to 20.0.2 - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](https://github.com/capricorn86/happy-dom/compare/v17.6.3...v20.0.2) Updates `happy-dom` from 17.6.3 to 20.0.2 - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](https://github.com/capricorn86/happy-dom/compare/v17.6.3...v20.0.2) Updates `happy-dom` from 17.6.3 to 20.0.2 - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](https://github.com/capricorn86/happy-dom/compare/v17.6.3...v20.0.2) --- updated-dependencies: - dependency-name: happy-dom dependency-version: 20.0.2 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: happy-dom dependency-version: 20.0.2 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: happy-dom dependency-version: 20.0.2 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] * Allow to logout a wallet with skipRemoveDevice even if the wallet is not in a ready state to allow force removing of wallets (#906) * Pass request to PromptCodeHandler in guard registerUI (#909) * Pass request to PromptCodeHandler in guard registerUI * Fixing guard registerUI test * guard: allow using recovery code as 2FA token (#910) * guard: allow using recovery code as 2FA token * Cleanup types of ResponseFn --------- Co-authored-by: Corban Riley * Add a way to reset 2fa when using a backup code (#911) * Add a way to reset 2fa when using a backup code * use the GuardToken type instead of breaking out the props * Update package.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update SECURITY.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update wagmi-project/package.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update wagmi-project/package.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update wagmi-project/src/App.tsx Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create FUNDING.json (#90) Enhancements: Include FUNDING.json to display GitHub sponsorship options in the repository Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Create config.yml (#91) Add initial CircleCI configuration to enable automated builds using a custom Docker executor and a defined workflow. Build: Add .circleci/config.yml with version 2.1 specification and custom Docker executor. CI: Define web3-defi-game-project job with checkout step. Set up my-custom-workflow to run the job. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Add rc4 contracts * Set rc4 as default and add it to lists * Session enhanced replay protection * New sessions replay protection hashes payload * Use the 4337 factory wrapper * Update keymachine url in dapp-client constants * Update keymachine url in Provider constructor * SSR safety (#915) * SSR safety test * Fix CI job * Guard dapp-client for SSR (lazy transport, browser checks, gated storage) * Fix guard topology (#918) * Use proper guard topology * Test and fixes * login and setup tests * Switch prod manager settings (#917) * Add prod guard and identity instrument info * Remove completed TODOs * Small JS tweaks (#919) * Fix type exports to built declarations * Update repository links to current package paths * Improve Next app tooling and React typings * Expose primitives CLI bin and use base lint config * Update relayer.gen.ts and TransactionPrecondition interface * Update api.gen.ts * Update metadata.gen.ts * Update marketplace.gen.ts * Update guard.gen.ts * Support multiple identity signers in sessions configuration * Device signers can approve implicit sessions * Remove invalid test * Fix recursion * Fix comment * Improve test stability by reducing race conditions * Do not set passkey signer as identity signer * Use length checks * Throw on missing identity signer * Encoding requires identity signer to encode * Fix test * Refactor/types namings tsdoc redundant code (#880) * refactor types, namings, ts doc * fix session response payload * change parameter name * change parameter name * change type in tests * improve types and dapp client methods * fix session test to use new types * refactor * refactor implicit sessions array in chain session manager * remove unused types * remove unused types and add ConnectionError * update pnpm lock * move reusable session types to wallet-core * Update some imports and update some response type names --------- Co-authored-by: Tolgahan Arikan * Fix check for explicit session for the updated type in dapp-client * Update api.gen.ts and relayer.gen.ts * Add missing chainId for dapp client event * Fix initializing new chain session manager on redirect * Add support for non-viem, custom Sequence chains (#882) * Provider sent to prepareBlankEnvelope * Add session signature decoding * const for node length * Clearer blacklist size encoding * identity signer node length * Add feeTokens endpoint to relayer (#885) * add getFeeTokens to dapp client (#889) * add getFeeTokens to dapp client * fix typo * make getFeeTokens independent of chain session manager and initialize state (#890) * make getFeeTokens independent of chain session manager and initialized state * remove getFeeTokens from chain session manager * Throw specific error when trying to sign with an expired session (#887) * Throw when supported session signer is expired * Fix tests * Make dapp-client implicit sessions chain agnostic (#893) * Add Monad, remove LAOS and Root Network * Add support for sessionless dapp connection (#896) * Refactor relayer package & update dependant packages (#891) * refactor exports for relayer (#900) * Add Arc Testnet * Fix changelog config * Sessionless connection upgrade and error handling in DappClient (#902) * dapp-client: add sessionless snapshot restore flow * Allow to logout a wallet with skipRemoveDevice even if the wallet is not in a ready state to allow force removing of wallets (#906) * Pass request to PromptCodeHandler in guard registerUI (#909) * Pass request to PromptCodeHandler in guard registerUI * Fixing guard registerUI test * guard: allow using recovery code as 2FA token (#910) * guard: allow using recovery code as 2FA token * Cleanup types of ResponseFn --------- Co-authored-by: Corban Riley * Add a way to reset 2fa when using a backup code (#911) * Add a way to reset 2fa when using a backup code * use the GuardToken type instead of breaking out the props * Add rc4 contracts * Set rc4 as default and add it to lists * Session enhanced replay protection * New sessions replay protection hashes payload * Use the 4337 factory wrapper * Update keymachine url in dapp-client constants * Update keymachine url in Provider constructor * SSR safety (#915) * Guard dapp-client for SSR (lazy transport, browser checks, gated storage) * Fix guard topology (#918) * Use proper guard topology * Test and fixes * login and setup tests * Switch prod manager settings (#917) * Add prod guard and identity instrument info * Remove completed TODOs * Small JS tweaks (#919) * Fix type exports to built declarations * Update repository links to current package paths * Improve Next app tooling and React typings * Expose primitives CLI bin and use base lint config * Update relayer.gen.ts and TransactionPrecondition interface * Update relayer.gen.ts and TransactionPrecondition interface (#920) * 3.0.0-beta.1 * identity-instrument: generate nonce from current time (#921) * Remove publish-dists.yml github action (#923) * 3.0.0-beta.2 * Clean up changeset config * Improve test stability by removing race conditions * Ensure build before test * Updating happy-dom to 20.0.10 (#926) * Add support for custom auth providers (authcode & authcode-pkce only) (#894) * Add support for custom auth providers (authcode & authcode-pkce only) * fix authcode tests * Updating Deps November 2025 (#927) * Updating deps for the workspace root * Updating deps for wallet/wdk * Fixing sessions test for latest vitest * Lets not upgrade to the latest typescript quite yet * Updating to latest vitest * Updating deps for wallet/core * Updating deps for wallet/primitives-cli * Updating deps for wallet/dapp-client * Adding syncpack to check for dep version inconsistencies * Setup syncpack versionGroups for pnpm workspace:^ * Fixing dep versions mismatches * Fixing @types/node mismatches * Adding syncpack to pre commit hook * Remove the syncpack format script. * Update ox to v9.17.0 (#928) * Upgrading ox to 9.17.0 * WrappedSignature renamed to SignatureErc6492 * Fixing PasskeySignatureValidator interface * Lock ox lib dep to use the same version with pnpm overrides and update viem to latest * Fix explicitSessionRequested check in dapp client * Typescript 5.9.3 (#930) * Upgrading to typescript v5.9.3 * Fix type errors that arose from typescript upgrade related to Bytes and Buffer source typings. * Don't catch errors thrown by Guard 2FA or reject early to allow multiple attempts on incorrect TOTP (#931) * Update pnpm * Mark @0xsequence/wallet-primitives-cli as private * 3.0.0-beta.3 * changeset cleanup * Fix rc4 4337 factory (#933) * Add rc5 and set it as default (#934) * 3.0.0-beta.4 * Update SECURITY.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update wagmi-project/package.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update wagmi-project/package.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Bump next from 15.5.5 to 15.5.7 (#936) Bumps [next](https://github.com/vercel/next.js) from 15.5.5 to 15.5.7. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.5.5...v15.5.7) --- updated-dependencies: - dependency-name: next dependency-version: 15.5.7 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * add userdata service client (#940) * Skip LocalDevice identity signers not on current device (#942) * Skip LocalDevice identity signers not on current device * Update log * 3.0.0-beta.5 * Update config.yml (#102) * Update config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * Update config.yml (#103) * Update config.yml Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update .circleci/config.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> * fix: extras/web/package.json to reduce vulnerabilities (#101) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NEXT-14173355 Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> * fix: extras/docs/package.json to reduce vulnerabilities (#100) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NEXT-14173355 Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> * fix: package.json to reduce vulnerabilities (#104) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-BABELHELPERS-9397697 - https://snyk.io/vuln/SNYK-JS-BABELRUNTIME-10044504 - https://snyk.io/vuln/SNYK-JS-BRACEEXPANSION-9789073 - https://snyk.io/vuln/SNYK-JS-CROSSSPAWN-8303230 - https://snyk.io/vuln/SNYK-JS-ELLIPTIC-7577916 - https://snyk.io/vuln/SNYK-JS-ELLIPTIC-7577917 - https://snyk.io/vuln/SNYK-JS-ELLIPTIC-7577918 - https://snyk.io/vuln/SNYK-JS-ELLIPTIC-8187303 - https://snyk.io/vuln/SNYK-JS-ELLIPTIC-8720086 - https://snyk.io/vuln/SNYK-JS-IMAGESIZE-9634164 - https://snyk.io/vuln/SNYK-JS-INFLIGHT-6095116 - https://snyk.io/vuln/SNYK-JS-JSYAML-13961110 - https://snyk.io/vuln/SNYK-JS-MICROMATCH-6838728 - https://snyk.io/vuln/SNYK-JS-NODEFORGE-14114940 - https://snyk.io/vuln/SNYK-JS-NODEFORGE-14125097 - https://snyk.io/vuln/SNYK-JS-NODEFORGE-14125745 - https://snyk.io/vuln/SNYK-JS-ONHEADERS-10773729 - https://snyk.io/vuln/SNYK-JS-ROLLUP-8073097 - https://snyk.io/vuln/SNYK-JS-SECP256K1-8237220 - https://snyk.io/vuln/SNYK-JS-SEND-7926862 - https://snyk.io/vuln/SNYK-JS-SERVESTATIC-7926865 - https://snyk.io/vuln/SNYK-JS-SHAJS-12089400 Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> * Revert "Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/npm_and_yarn-318c02e2da'" This reverts commit fd0fdf9ecc6ad9056447e381de7fc5bb19f78e47, reversing changes made to cba78943db9942a4635bb530b7a43fc5d18b0ab4. * fix: extras/web/package.json to reduce vulnerabilities (#109) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NEXT-14173355 Co-authored-by: snyk-bot * fix: extras/docs/package.json to reduce vulnerabilities (#106) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NEXT-14400636 - https://snyk.io/vuln/SNYK-JS-NEXT-14400644 Co-authored-by: snyk-bot * Bump next in the npm_and_yarn group across 1 directory (#110) Bumps the npm_and_yarn group with 1 update in the / directory: [next](https://github.com/vercel/next.js). Updates `next` from 15.5.7 to 15.5.9 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.5.7...v15.5.9) --- updated-dependencies: - dependency-name: next dependency-version: 15.5.9 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Delete .github/workflows/fortify.yml (#111) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * fix: extras/web/package.json to reduce vulnerabilities (#107) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NEXT-14400636 - https://snyk.io/vuln/SNYK-JS-NEXT-14400644 Co-authored-by: snyk-bot * Bump the npm_and_yarn group across 1 directory with 3 updates (#115) Bumps the npm_and_yarn group with 1 update in the / directory: [next](https://github.com/vercel/next.js). Updates `next` from 15.5.5 to 15.5.9 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.5.5...v15.5.9) Updates `happy-dom` from 17.6.3 to 20.0.11 - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](https://github.com/capricorn86/happy-dom/compare/v17.6.3...v20.0.11) Updates `vite` from 7.1.10 to 7.2.7 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v7.2.7/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v7.2.7/packages/vite) --- updated-dependencies: - dependency-name: next dependency-version: 15.5.9 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: happy-dom dependency-version: 20.0.11 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: vite dependency-version: 7.2.7 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump next from 15.5.7 to 15.5.9 (#944) Bumps [next](https://github.com/vercel/next.js) from 15.5.7 to 15.5.9. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.5.7...v15.5.9) --- updated-dependencies: - dependency-name: next dependency-version: 15.5.9 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Pin foundry to v1.5.0 instead of nightly (#947) * Include repo and extras in syncpack config to ensure deps are synced (#945) * Include repo and extras in syncpack config to ensure deps are synced across all * Updating support deps * Updating deps * Updating pnpm lock * Fixing type errors within wdk tests * Short circuit 404s (#949) * skip witness on signers that don't support it * add passkey to test * 3.0.0-beta.6 * Update tests.yml (#119) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update config.yml (#120) Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update packages/services/identity-instrument/src/index.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: snyk-bot Co-authored-by: Michael Standen Co-authored-by: Gabi <56271768+VGabriel45@users.noreply.github.com> Co-authored-by: Tolgahan Arikan Co-authored-by: Taylan Pince Co-authored-by: Corban Brook Co-authored-by: Patryk Kalinowski Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Agusx1211 Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> --- .changeset/config.json | 4 +- .changeset/cyan-radios-relax.md | 18 + .changeset/goofy-laws-serve.md | 21 + .changeset/open-toes-marry.md | 20 + .changeset/plain-feet-stare.md | 17 + .changeset/pre.json | 33 + .changeset/wild-feet-carry.md | 17 + .changeset/wise-heads-buy.md | 17 + .circleci/config.yml | 26 + .codesandbox/tasks.json | 118 + .github/workflows/publish-dists.yml | 88 - .github/workflows/tests.yml | 5 +- CNAME | 1 + FUNDING.json | 10 + SECURITY.md | 14 +- azure-pipelines.yml | 21 + extras/docs/eslint.config.js | 7 +- extras/docs/next.config.js | 12 +- extras/docs/package.json | 23 +- extras/web/eslint.config.js | 7 +- extras/web/next.config.js | 12 +- extras/web/package.json | 23 +- lefthook.yml | 5 + package.json | 44 +- packages/services/api/CHANGELOG.md | 38 +- packages/services/api/package.json | 10 +- packages/services/api/src/api.gen.ts | 5272 +++++++++-------- packages/services/builder/CHANGELOG.md | 36 + packages/services/builder/package.json | 10 +- packages/services/guard/CHANGELOG.md | 37 +- packages/services/guard/package.json | 12 +- .../services/guard/src/client/guard.gen.ts | 9 +- .../services/identity-instrument/CHANGELOG.md | 37 + .../services/identity-instrument/package.json | 10 +- packages/services/indexer/CHANGELOG.md | 38 +- packages/services/indexer/package.json | 10 +- packages/services/marketplace/CHANGELOG.md | 36 + packages/services/marketplace/package.json | 10 +- .../marketplace/src/marketplace.gen.ts | 1373 ++++- packages/services/metadata/CHANGELOG.md | 38 +- packages/services/metadata/package.json | 10 +- .../services/metadata/src/metadata.gen.ts | 2264 ++++--- packages/services/relayer/CHANGELOG.md | 50 +- packages/services/relayer/hardhat.config.js | 15 - packages/services/relayer/package.json | 15 +- packages/services/relayer/src/index.ts | 4 +- .../services/relayer/src/local-relayer.ts | 125 - .../relayer}/src/preconditions/codec.ts | 71 +- .../relayer}/src/preconditions/index.ts | 0 .../relayer}/src/preconditions/selectors.ts | 19 +- .../relayer}/src/preconditions/types.ts | 0 .../services/relayer/src/provider-relayer.ts | 284 - .../relayer/src/relayer/index.ts} | 41 +- .../services/relayer/src/relayer/relayer.ts | 37 + .../relayer/src/relayer/rpc-relayer}/index.ts | 136 +- .../src/relayer/rpc-relayer/relayer.gen.ts | 2268 +++++++ .../relayer}/src/relayer/standard/abi.ts | 0 .../relayer}/src/relayer/standard/eip6963.ts | 10 +- .../relayer}/src/relayer/standard/index.ts | 1 - .../relayer}/src/relayer/standard/local.ts | 14 +- .../src/relayer/standard/pk-relayer.ts | 7 +- .../relayer}/src/relayer/standard/sequence.ts | 23 +- .../services/relayer/src/rpc-relayer/index.ts | 1 - .../relayer/src/rpc-relayer/relayer.gen.ts | 1900 ------ .../relayer}/test/preconditions/codec.test.ts | 297 +- .../test/preconditions}/preconditions.test.ts | 6 +- .../test/preconditions/selectors.test.ts | 34 +- .../relayer}/test/preconditions/types.test.ts | 0 .../relayer}/test/relayer/relayer.test.ts | 71 +- packages/services/userdata/CHANGELOG.md | 13 + packages/services/userdata/README.md | 3 + packages/services/userdata/package.json | 28 + packages/services/userdata/src/index.ts | 36 + .../services/userdata/src/userdata.gen.ts | 686 +++ packages/services/userdata/tsconfig.json | 10 + packages/utils/abi/CHANGELOG.md | 38 +- packages/utils/abi/package.json | 10 +- packages/wallet/core/CHANGELOG.md | 61 + packages/wallet/core/package.json | 18 +- .../core/src/{relayer => bundler}/bundler.ts | 4 +- .../{relayer => bundler}/bundlers/index.ts | 0 .../{relayer => bundler}/bundlers/pimlico.ts | 4 +- .../core/src/{relayer => bundler}/index.ts | 2 - packages/wallet/core/src/index.ts | 9 +- .../src/relayer/standard/rpc/relayer.gen.ts | 2037 ------- packages/wallet/core/src/signers/guard.ts | 7 +- .../wallet/core/src/signers/pk/encrypted.ts | 4 +- .../core/src/signers/session-manager.ts | 50 +- .../core/src/signers/session/explicit.ts | 5 +- .../core/src/signers/session/implicit.ts | 17 +- .../core/src/signers/session/session.ts | 8 + packages/wallet/core/src/state/local/index.ts | 2 +- .../wallet/core/src/state/sequence/index.ts | 30 +- .../src/utils/session/permission-builder.ts | 4 +- .../wallet/core/src/utils/session/types.ts | 33 + packages/wallet/core/src/wallet.ts | 8 +- packages/wallet/core/test/constants.ts | 7 +- .../wallet/core/test/relayer/bundler.test.ts | 6 +- .../wallet/core/test/session-manager.test.ts | 330 +- .../test/signers-session-implicit.test.ts | 4 +- packages/wallet/core/vitest.config.ts | 9 - packages/wallet/dapp-client/CHANGELOG.md | 67 + packages/wallet/dapp-client/eslint.config.mjs | 4 +- packages/wallet/dapp-client/package.json | 21 +- .../dapp-client/src/ChainSessionManager.ts | 303 +- packages/wallet/dapp-client/src/DappClient.ts | 608 +- .../wallet/dapp-client/src/DappTransport.ts | 90 +- packages/wallet/dapp-client/src/index.ts | 22 +- .../wallet/dapp-client/src/types/index.ts | 92 +- .../wallet/dapp-client/src/utils/constants.ts | 4 +- .../wallet/dapp-client/src/utils/storage.ts | 126 +- .../wallet/primitives-cli/eslint.config.mjs | 4 +- packages/wallet/primitives-cli/package.json | 20 +- .../primitives-cli/src/subcommands/address.ts | 2 +- .../primitives-cli/src/subcommands/server.ts | 3 +- .../primitives-cli/src/subcommands/session.ts | 20 +- packages/wallet/primitives/CHANGELOG.md | 37 + packages/wallet/primitives/eslint.config.mjs | 4 +- packages/wallet/primitives/package.json | 10 +- packages/wallet/primitives/src/config.ts | 46 + packages/wallet/primitives/src/constants.ts | 3 +- packages/wallet/primitives/src/context.ts | 42 + packages/wallet/primitives/src/erc-6492.ts | 14 +- .../wallet/primitives/src/extensions/index.ts | 12 + packages/wallet/primitives/src/network.ts | 115 +- packages/wallet/primitives/src/payload.ts | 4 + packages/wallet/primitives/src/permission.ts | 4 +- .../wallet/primitives/src/session-config.ts | 243 +- .../primitives/src/session-signature.ts | 173 +- .../wallet/primitives/test/address.test.ts | 28 +- .../wallet/primitives/test/config.test.ts | 63 + .../wallet/primitives/test/erc-6492.test.ts | 14 +- .../primitives/test/session-config.test.ts | 297 +- .../primitives/test/session-signature.test.ts | 235 +- packages/wallet/wdk/.env.test | 5 - packages/wallet/wdk/CHANGELOG.md | 73 + packages/wallet/wdk/package.json | 26 +- .../wallet/wdk/src/dbs/auth-commitments.ts | 2 +- packages/wallet/wdk/src/identity/signer.ts | 2 +- packages/wallet/wdk/src/sequence/guards.ts | 27 +- .../src/sequence/handlers/authcode-pkce.ts | 8 +- .../wdk/src/sequence/handlers/authcode.ts | 19 +- .../wallet/wdk/src/sequence/handlers/guard.ts | 33 +- .../wdk/src/sequence/handlers/mnemonic.ts | 8 +- .../wallet/wdk/src/sequence/handlers/otp.ts | 10 +- packages/wallet/wdk/src/sequence/manager.ts | 168 +- packages/wallet/wdk/src/sequence/sessions.ts | 192 +- packages/wallet/wdk/src/sequence/signers.ts | 19 +- .../wallet/wdk/src/sequence/transactions.ts | 17 +- .../wallet/wdk/src/sequence/types/module.ts | 2 +- .../wallet/wdk/src/sequence/types/sessions.ts | 6 + .../wallet/wdk/src/sequence/types/signer.ts | 2 +- .../src/sequence/types/transaction-request.ts | 3 +- packages/wallet/wdk/src/sequence/wallets.ts | 89 +- .../wallet/wdk/test/authcode-pkce.test.ts | 22 +- packages/wallet/wdk/test/authcode.test.ts | 90 +- packages/wallet/wdk/test/constants.ts | 17 +- packages/wallet/wdk/test/guard.test.ts | 76 +- .../wallet/wdk/test/identity-auth-dbs.test.ts | 8 +- .../wallet/wdk/test/identity-signer.test.ts | 32 +- packages/wallet/wdk/test/messages.test.ts | 30 +- packages/wallet/wdk/test/otp.test.ts | 16 +- packages/wallet/wdk/test/passkeys.test.ts | 8 +- packages/wallet/wdk/test/recovery.test.ts | 12 +- packages/wallet/wdk/test/sessions.test.ts | 512 +- packages/wallet/wdk/test/setup.ts | 2 +- .../wallet/wdk/test/signers-kindof.test.ts | 40 + packages/wallet/wdk/test/test-ssr-safety.mjs | 308 + packages/wallet/wdk/test/transactions.test.ts | 78 +- packages/wallet/wdk/test/wallets.test.ts | 167 +- packages/wallet/wdk/vitest.config.ts | 1 - pnpm-lock.yaml | 3538 ++++++----- repo/eslint-config/CHANGELOG.md | 13 + repo/eslint-config/package.json | 20 +- repo/typescript-config/CHANGELOG.md | 13 + repo/typescript-config/package.json | 2 +- repo/ui/CHANGELOG.md | 13 + repo/ui/package.json | 18 +- turbo.json | 3 +- ...e_moduleszS@preconstructzSclizSbin.js.BLOB | Bin 0 -> 1584680 bytes ...de_moduleszS@preconstructzSclizSbin.js.MAP | 1 + ...e_moduleszS@preconstructzSclizSbin.js.BLOB | Bin 0 -> 1583672 bytes ...de_moduleszS@preconstructzSclizSbin.js.MAP | 1 + wagmi-project/.gitignore | 25 + wagmi-project/.npmrc | 1 + wagmi-project/README.md | 1 + wagmi-project/biome.json | 13 + wagmi-project/index.html | 12 + wagmi-project/package.json | 29 + wagmi-project/src/App.tsx | 46 + wagmi-project/src/index.css | 21 + wagmi-project/src/main.tsx | 24 + wagmi-project/src/vite-env.d.ts | 1 + wagmi-project/src/wagmi.ts | 22 + wagmi-project/tsconfig.json | 25 + wagmi-project/tsconfig.node.json | 10 + wagmi-project/vite.config.ts | 7 + 197 files changed, 15987 insertions(+), 11457 deletions(-) create mode 100644 .changeset/cyan-radios-relax.md create mode 100644 .changeset/goofy-laws-serve.md create mode 100644 .changeset/open-toes-marry.md create mode 100644 .changeset/plain-feet-stare.md create mode 100644 .changeset/pre.json create mode 100644 .changeset/wild-feet-carry.md create mode 100644 .changeset/wise-heads-buy.md create mode 100644 .circleci/config.yml create mode 100644 .codesandbox/tasks.json delete mode 100644 .github/workflows/publish-dists.yml create mode 100644 CNAME create mode 100644 FUNDING.json create mode 100644 azure-pipelines.yml create mode 100644 packages/services/identity-instrument/CHANGELOG.md delete mode 100644 packages/services/relayer/hardhat.config.js delete mode 100644 packages/services/relayer/src/local-relayer.ts rename packages/{wallet/core => services/relayer}/src/preconditions/codec.ts (73%) rename packages/{wallet/core => services/relayer}/src/preconditions/index.ts (100%) rename packages/{wallet/core => services/relayer}/src/preconditions/selectors.ts (57%) rename packages/{wallet/core => services/relayer}/src/preconditions/types.ts (100%) delete mode 100644 packages/services/relayer/src/provider-relayer.ts rename packages/{wallet/core/src/relayer/relayer.ts => services/relayer/src/relayer/index.ts} (50%) create mode 100644 packages/services/relayer/src/relayer/relayer.ts rename packages/{wallet/core/src/relayer/standard/rpc => services/relayer/src/relayer/rpc-relayer}/index.ts (78%) create mode 100644 packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts rename packages/{wallet/core => services/relayer}/src/relayer/standard/abi.ts (100%) rename packages/{wallet/core => services/relayer}/src/relayer/standard/eip6963.ts (85%) rename packages/{wallet/core => services/relayer}/src/relayer/standard/index.ts (77%) rename packages/{wallet/core => services/relayer}/src/relayer/standard/local.ts (96%) rename packages/{wallet/core => services/relayer}/src/relayer/standard/pk-relayer.ts (96%) rename packages/{wallet/core => services/relayer}/src/relayer/standard/sequence.ts (82%) delete mode 100644 packages/services/relayer/src/rpc-relayer/index.ts delete mode 100644 packages/services/relayer/src/rpc-relayer/relayer.gen.ts rename packages/{wallet/core => services/relayer}/test/preconditions/codec.test.ts (69%) rename packages/{wallet/core/test => services/relayer/test/preconditions}/preconditions.test.ts (98%) rename packages/{wallet/core => services/relayer}/test/preconditions/selectors.test.ts (92%) rename packages/{wallet/core => services/relayer}/test/preconditions/types.test.ts (100%) rename packages/{wallet/core => services/relayer}/test/relayer/relayer.test.ts (86%) create mode 100644 packages/services/userdata/CHANGELOG.md create mode 100644 packages/services/userdata/README.md create mode 100644 packages/services/userdata/package.json create mode 100644 packages/services/userdata/src/index.ts create mode 100644 packages/services/userdata/src/userdata.gen.ts create mode 100644 packages/services/userdata/tsconfig.json create mode 100644 packages/wallet/core/CHANGELOG.md rename packages/wallet/core/src/{relayer => bundler}/bundler.ts (85%) rename packages/wallet/core/src/{relayer => bundler}/bundlers/index.ts (100%) rename packages/wallet/core/src/{relayer => bundler}/bundlers/pimlico.ts (97%) rename packages/wallet/core/src/{relayer => bundler}/index.ts (67%) delete mode 100644 packages/wallet/core/src/relayer/standard/rpc/relayer.gen.ts create mode 100644 packages/wallet/core/src/utils/session/types.ts delete mode 100644 packages/wallet/core/vitest.config.ts create mode 100644 packages/wallet/dapp-client/CHANGELOG.md create mode 100644 packages/wallet/primitives/CHANGELOG.md delete mode 100644 packages/wallet/wdk/.env.test create mode 100644 packages/wallet/wdk/CHANGELOG.md create mode 100644 packages/wallet/wdk/src/sequence/types/sessions.ts create mode 100644 packages/wallet/wdk/test/signers-kindof.test.ts create mode 100644 packages/wallet/wdk/test/test-ssr-safety.mjs create mode 100644 repo/eslint-config/CHANGELOG.md create mode 100644 repo/typescript-config/CHANGELOG.md create mode 100644 repo/ui/CHANGELOG.md create mode 100644 v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSsequence.jszSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.BLOB create mode 100644 v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSsequence.jszSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.MAP create mode 100644 v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSworkspacezSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.BLOB create mode 100644 v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSworkspacezSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.MAP create mode 100644 wagmi-project/.gitignore create mode 100644 wagmi-project/.npmrc create mode 100644 wagmi-project/README.md create mode 100644 wagmi-project/biome.json create mode 100644 wagmi-project/index.html create mode 100644 wagmi-project/package.json create mode 100644 wagmi-project/src/App.tsx create mode 100644 wagmi-project/src/index.css create mode 100644 wagmi-project/src/main.tsx create mode 100644 wagmi-project/src/vite-env.d.ts create mode 100644 wagmi-project/src/wagmi.ts create mode 100644 wagmi-project/tsconfig.json create mode 100644 wagmi-project/tsconfig.node.json create mode 100644 wagmi-project/vite.config.ts diff --git a/.changeset/config.json b/.changeset/config.json index 6b372552ca..4f8345f464 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -5,7 +5,7 @@ "fixed": [], "linked": [], "access": "restricted", - "baseBranch": "main", + "baseBranch": "master", "updateInternalDependencies": "patch", - "ignore": [] + "ignore": ["@0xsequence/wallet-primitives-cli", "docs", "web"] } diff --git a/.changeset/cyan-radios-relax.md b/.changeset/cyan-radios-relax.md new file mode 100644 index 0000000000..ec408d6cd9 --- /dev/null +++ b/.changeset/cyan-radios-relax.md @@ -0,0 +1,18 @@ +--- +'@0xsequence/api': patch +'@0xsequence/builder': patch +'@0xsequence/guard': patch +'@0xsequence/identity-instrument': patch +'@0xsequence/indexer': patch +'@0xsequence/marketplace': patch +'@0xsequence/metadata': patch +'@0xsequence/relayer': patch +'@0xsequence/userdata': patch +'@0xsequence/abi': patch +'@0xsequence/wallet-core': patch +'@0xsequence/dapp-client': patch +'@0xsequence/wallet-primitives': patch +'@0xsequence/wallet-wdk': patch +--- + +Fix signer 404 error, minor fixes diff --git a/.changeset/goofy-laws-serve.md b/.changeset/goofy-laws-serve.md new file mode 100644 index 0000000000..690a5f76b4 --- /dev/null +++ b/.changeset/goofy-laws-serve.md @@ -0,0 +1,21 @@ +--- +'@0xsequence/api': patch +'@0xsequence/builder': patch +'@0xsequence/guard': patch +'@0xsequence/identity-instrument': patch +'@0xsequence/indexer': patch +'@0xsequence/marketplace': patch +'@0xsequence/metadata': patch +'@0xsequence/relayer': patch +'@0xsequence/userdata': patch +'@0xsequence/abi': patch +'@0xsequence/wallet-core': patch +'@0xsequence/dapp-client': patch +'@0xsequence/wallet-primitives': patch +'@0xsequence/wallet-wdk': patch +'@repo/eslint-config': patch +'@repo/typescript-config': patch +'@repo/ui': patch +--- + +Beta release for v3 diff --git a/.changeset/open-toes-marry.md b/.changeset/open-toes-marry.md new file mode 100644 index 0000000000..ec5bf32174 --- /dev/null +++ b/.changeset/open-toes-marry.md @@ -0,0 +1,20 @@ +--- +'@0xsequence/api': patch +'@0xsequence/builder': patch +'@0xsequence/guard': patch +'@0xsequence/identity-instrument': patch +'@0xsequence/indexer': patch +'@0xsequence/marketplace': patch +'@0xsequence/metadata': patch +'@0xsequence/relayer': patch +'@0xsequence/abi': patch +'@0xsequence/wallet-core': patch +'@0xsequence/dapp-client': patch +'@0xsequence/wallet-primitives': patch +'@0xsequence/wallet-wdk': patch +'@repo/eslint-config': patch +'@repo/typescript-config': patch +'@repo/ui': patch +--- + +3.0.0-beta.3 with fixes diff --git a/.changeset/plain-feet-stare.md b/.changeset/plain-feet-stare.md new file mode 100644 index 0000000000..c99c82026b --- /dev/null +++ b/.changeset/plain-feet-stare.md @@ -0,0 +1,17 @@ +--- +'@0xsequence/api': patch +'@0xsequence/builder': patch +'@0xsequence/guard': patch +'@0xsequence/identity-instrument': patch +'@0xsequence/indexer': patch +'@0xsequence/marketplace': patch +'@0xsequence/metadata': patch +'@0xsequence/relayer': patch +'@0xsequence/abi': patch +'@0xsequence/wallet-core': patch +'@0xsequence/dapp-client': patch +'@0xsequence/wallet-primitives': patch +'@0xsequence/wallet-wdk': patch +--- + +3.0.0-beta.2 with identity instrument updates diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..73184ae444 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,33 @@ +{ + "mode": "exit", + "tag": "beta", + "initialVersions": { + "docs": "0.1.0", + "web": "0.1.0", + "@0xsequence/api": "3.0.0-beta.5", + "@0xsequence/builder": "3.0.0-beta.5", + "@0xsequence/guard": "3.0.0-beta.5", + "@0xsequence/identity-instrument": "3.0.0-beta.5", + "@0xsequence/indexer": "3.0.0-beta.5", + "@0xsequence/marketplace": "3.0.0-beta.5", + "@0xsequence/metadata": "3.0.0-beta.5", + "@0xsequence/relayer": "3.0.0-beta.5", + "@0xsequence/userdata": "3.0.0-beta.5", + "@0xsequence/abi": "3.0.0-beta.5", + "@0xsequence/wallet-core": "3.0.0-beta.5", + "@0xsequence/dapp-client": "3.0.0-beta.5", + "@0xsequence/wallet-primitives": "3.0.0-beta.5", + "@0xsequence/wallet-wdk": "3.0.0-beta.5", + "@repo/eslint-config": "0.0.1-beta.1", + "@repo/typescript-config": "0.0.1-beta.1", + "@repo/ui": "0.0.1-beta.1" + }, + "changesets": [ + "cyan-radios-relax", + "goofy-laws-serve", + "open-toes-marry", + "plain-feet-stare", + "wild-feet-carry", + "wise-heads-buy" + ] +} diff --git a/.changeset/wild-feet-carry.md b/.changeset/wild-feet-carry.md new file mode 100644 index 0000000000..962942831c --- /dev/null +++ b/.changeset/wild-feet-carry.md @@ -0,0 +1,17 @@ +--- +'@0xsequence/api': patch +'@0xsequence/builder': patch +'@0xsequence/guard': patch +'@0xsequence/identity-instrument': patch +'@0xsequence/indexer': patch +'@0xsequence/marketplace': patch +'@0xsequence/metadata': patch +'@0xsequence/relayer': patch +'@0xsequence/abi': patch +'@0xsequence/wallet-core': patch +'@0xsequence/dapp-client': patch +'@0xsequence/wallet-primitives': patch +'@0xsequence/wallet-wdk': patch +--- + +3.0.0-beta.1 diff --git a/.changeset/wise-heads-buy.md b/.changeset/wise-heads-buy.md new file mode 100644 index 0000000000..1c35a4d355 --- /dev/null +++ b/.changeset/wise-heads-buy.md @@ -0,0 +1,17 @@ +--- +'@0xsequence/api': patch +'@0xsequence/builder': patch +'@0xsequence/guard': patch +'@0xsequence/identity-instrument': patch +'@0xsequence/indexer': patch +'@0xsequence/marketplace': patch +'@0xsequence/metadata': patch +'@0xsequence/relayer': patch +'@0xsequence/abi': patch +'@0xsequence/wallet-core': patch +'@0xsequence/dapp-client': patch +'@0xsequence/wallet-primitives': patch +'@0xsequence/wallet-wdk': patch +--- + +RC5 upgrade diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..ad53a8e498 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000..5be606cdf2 --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,118 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [ + { + "name": "Install Dependencies", + "command": "pnpm install" + } + ], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": { + "build": { + "name": "build", + "command": "pnpm build", + "runAtStart": false + }, + "watch": { + "name": "watch", + "command": "pnpm watch", + "runAtStart": false + }, + "clean": { + "name": "clean", + "command": "pnpm clean", + "runAtStart": false + }, + "changeset": { + "name": "changeset", + "command": "pnpm changeset", + "runAtStart": false + }, + "version-packages": { + "name": "version-packages", + "command": "pnpm version-packages", + "runAtStart": false + }, + "release": { + "name": "release", + "command": "pnpm release", + "runAtStart": false + }, + "snapshot": { + "name": "snapshot", + "command": "pnpm snapshot", + "runAtStart": false + }, + "update-version": { + "name": "update-version", + "command": "pnpm update-version", + "runAtStart": false + }, + "test": { + "name": "test", + "command": "pnpm test", + "runAtStart": false + }, + "test:parallel": { + "name": "test:parallel", + "command": "pnpm test:parallel", + "runAtStart": false + }, + "lint": { + "name": "lint", + "command": "pnpm lint", + "runAtStart": false + }, + "lint:fix": { + "name": "lint:fix", + "command": "pnpm lint:fix", + "runAtStart": false + }, + "lint:tests": { + "name": "lint:tests", + "command": "pnpm lint:tests", + "runAtStart": false + }, + "lint:tests:fix": { + "name": "lint:tests:fix", + "command": "pnpm lint:tests:fix", + "runAtStart": false + }, + "format": { + "name": "format", + "command": "pnpm format", + "runAtStart": false + }, + "audit:fix": { + "name": "audit:fix", + "command": "pnpm audit:fix", + "runAtStart": false + }, + "typecheck": { + "name": "typecheck", + "command": "pnpm typecheck", + "runAtStart": false + }, + "dev": { + "name": "dev", + "command": "pnpm dev", + "runAtStart": true + }, + "postinstall": { + "name": "postinstall", + "command": "pnpm postinstall", + "runAtStart": false + }, + "coverage": { + "name": "coverage", + "command": "pnpm coverage", + "runAtStart": false + }, + "prepare": { + "name": "prepare", + "command": "pnpm prepare", + "runAtStart": false + } + } +} diff --git a/.github/workflows/publish-dists.yml b/.github/workflows/publish-dists.yml deleted file mode 100644 index fd4bb79054..0000000000 --- a/.github/workflows/publish-dists.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Publish Dists for Packages - -on: - workflow_dispatch: - push: - branches: - - master - -jobs: - build-and-push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: ./.github/actions/install-dependencies - - - name: Build package - run: pnpm run build - - - name: Prepare dist branch - run: | - PACKAGES=("services/guard" "services/identity-instrument" "services/relayer" "wallet/core" "wallet/primitives" "wallet/wdk" "wallet/dapp-client") - - for PACKAGE in "${PACKAGES[@]}"; do - BRANCH="dists/$PACKAGE" - PKG_DIR="packages/$PACKAGE" - - echo "📦 Publishing $PACKAGE to $BRANCH" - - mkdir -p /tmp/$PACKAGE - shopt -s dotglob - cp -r $PKG_DIR/* /tmp/$PACKAGE || true - - cd /tmp/$PACKAGE - git init - git checkout -b $BRANCH - - git config user.name "github-actions" - git config user.email "actions@github.com" - - echo "🔧 Rewriting workspace: deps in package.json..." - node -e ' - const fs = require("fs"); - const path = require("path"); - const pkgPath = path.resolve("package.json"); - const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); - const repo = "github:0xsequence/sequence.js"; - - const versions = { - "@0xsequence/guard": `${repo}#dists/services/guard`, - "@0xsequence/identity-instrument": `${repo}#dists/services/identity-instrument`, - "@0xsequence/relayer": `${repo}#dists/services/relayer`, - "@0xsequence/wallet-core": `${repo}#dists/wallet/core`, - "@0xsequence/wallet-primitives": `${repo}#dists/wallet/primitives`, - "@0xsequence/wallet-wdk": `${repo}#dists/wallet/wdk`, - }; - - const rewrite = (deps = {}) => { - for (const k in deps) { - if (deps[k].startsWith("workspace:")) { - const version = versions[k]; - - if (!version) { - console.warn(`No version found for ${k}, skipping...`); - continue; - } - - deps[k] = version; - console.log(`→ ${k} → ${deps[k]}`); - } - } - }; - - rewrite(pkg.dependencies); - fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)); - ' - - git add . - git commit -m "Build: publish $PACKAGE dist" - - git remote add origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git - git push -f origin HEAD:$BRANCH - - cd - - done diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 62fc357db0..20d4777291 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,16 +23,17 @@ jobs: tests: name: Run all tests runs-on: ubuntu-latest - needs: [install] + needs: [build] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install-dependencies - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly + version: v1.5.0 - name: Start Anvil in background run: anvil --fork-url https://nodes.sequence.app/arbitrum & + - run: pnpm build - run: pnpm test # NOTE: if you'd like to see example of how to run diff --git a/CNAME b/CNAME new file mode 100644 index 0000000000..aa0085b19f --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +sequence.app \ No newline at end of file diff --git a/FUNDING.json b/FUNDING.json new file mode 100644 index 0000000000..cb7bbaf783 --- /dev/null +++ b/FUNDING.json @@ -0,0 +1,10 @@ +{ + "drips": { + "ethereum": { + "ownedBy": "0x9a72807e1BC8A5e1E178f51E26239d58F511EB3D" + } + }, + "opRetro": { + "projectId": "0x62408999652f3bfa1be746d256bf5a4eb4719b993d40f07d2d60aaebee015018" + } +} diff --git a/SECURITY.md b/SECURITY.md index 7f1c49926a..6112730aab 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,16 +2,18 @@ ## Supported Versions -The following table lists which versions of this project currently receive security updates. +Use this section to tell people about which versions of your project are +currently being supported with security updates. | Version | Supported | | ------- | ------------------ | -| 3.x.x | :white_check_mark: | -| 2.x.x | :x: | -| < 2.0.0 | :x: | +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| < 4.0 | :x: | ## Reporting a Vulnerability -We take all security vulnerabilities seriously. To report a security vulnerability, please send an email with the details to [security@sequence.xyz](mailto:security@sequence.xyz). +Use this section to tell people how to report a vulnerability. -You can expect a response from our team within 48 hours to acknowledge receipt of your report. If the vulnerability is accepted, we will work with you to coordinate a release and public disclosure. We appreciate your efforts to responsibly disclose your findings. +Tell them to email [your-security-email@example.com], and they can expect an initial response within 48 hours. We will provide regular updates on the status of the reported vulnerability. diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000000..676233afaf --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,21 @@ +# Node.js +# Build a general Node.js project with npm. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- master + +pool: + vmImage: ubuntu-latest + +steps: +- task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + +- script: | + npm install + npm run build + displayName: 'npm install and build' diff --git a/extras/docs/eslint.config.js b/extras/docs/eslint.config.js index 3d2c2e9d49..0fbeffd979 100644 --- a/extras/docs/eslint.config.js +++ b/extras/docs/eslint.config.js @@ -1,4 +1,9 @@ import { nextJsConfig } from '@repo/eslint-config/next-js' /** @type {import("eslint").Linter.Config} */ -export default nextJsConfig +export default [ + ...nextJsConfig, + { + ignores: ['next-env.d.ts'], + }, +] diff --git a/extras/docs/next.config.js b/extras/docs/next.config.js index 1d6147825a..2963459c42 100644 --- a/extras/docs/next.config.js +++ b/extras/docs/next.config.js @@ -1,4 +1,14 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const workspaceRoot = path.join(__dirname, '..', '..') + /** @type {import('next').NextConfig} */ -const nextConfig = {} +const nextConfig = { + // Anchor output tracing to the monorepo root so Next.js doesn't pick up + // sibling lockfiles and mis-detect the workspace boundary during lint/build. + outputFileTracingRoot: workspaceRoot, +} export default nextConfig diff --git a/extras/docs/package.json b/extras/docs/package.json index d88f6aab18..29fbc4bc74 100644 --- a/extras/docs/package.json +++ b/extras/docs/package.json @@ -7,22 +7,23 @@ "dev": "next dev --turbopack --port 3001", "build": "next build", "start": "next start", - "lint": "next lint --max-warnings 0", + "lint": "eslint . --max-warnings 0", "typecheck": "tsc --noEmit", "clean": "rimraf .next" }, "dependencies": { - "@repo/ui": "workspace:*", - "next": "^15.4.7", - "react": "^19.1.0", - "react-dom": "^19.1.0" + "@repo/ui": "workspace:^", + "next": "^15.5.9", + "react": "^19.2.3", + "react-dom": "^19.2.3" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", - "@repo/typescript-config": "workspace:*", - "@types/node": "^20.17.57", - "@types/react": "18.3.1", - "@types/react-dom": "18.3.0", - "typescript": "5.5.4" + "@repo/eslint-config": "workspace:^", + "@repo/typescript-config": "workspace:^", + "@types/node": "^25.0.2", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "eslint": "^9.39.2", + "typescript": "^5.9.3" } } diff --git a/extras/web/eslint.config.js b/extras/web/eslint.config.js index 3d2c2e9d49..0fbeffd979 100644 --- a/extras/web/eslint.config.js +++ b/extras/web/eslint.config.js @@ -1,4 +1,9 @@ import { nextJsConfig } from '@repo/eslint-config/next-js' /** @type {import("eslint").Linter.Config} */ -export default nextJsConfig +export default [ + ...nextJsConfig, + { + ignores: ['next-env.d.ts'], + }, +] diff --git a/extras/web/next.config.js b/extras/web/next.config.js index 1d6147825a..2963459c42 100644 --- a/extras/web/next.config.js +++ b/extras/web/next.config.js @@ -1,4 +1,14 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const workspaceRoot = path.join(__dirname, '..', '..') + /** @type {import('next').NextConfig} */ -const nextConfig = {} +const nextConfig = { + // Anchor output tracing to the monorepo root so Next.js doesn't pick up + // sibling lockfiles and mis-detect the workspace boundary during lint/build. + outputFileTracingRoot: workspaceRoot, +} export default nextConfig diff --git a/extras/web/package.json b/extras/web/package.json index 8a0ffdf213..b0b621e95f 100644 --- a/extras/web/package.json +++ b/extras/web/package.json @@ -7,22 +7,23 @@ "dev": "next dev --turbopack --port 3000", "build": "next build", "start": "next start", - "lint": "next lint --max-warnings 0", + "lint": "eslint . --max-warnings 0", "typecheck": "tsc --noEmit", "clean": "rimraf .next" }, "dependencies": { - "@repo/ui": "workspace:*", - "next": "^15.4.7", - "react": "^19.1.0", - "react-dom": "^19.1.0" + "@repo/ui": "workspace:^", + "next": "^15.5.9", + "react": "^19.2.3", + "react-dom": "^19.2.3" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", - "@repo/typescript-config": "workspace:*", - "@types/node": "^20.17.57", - "@types/react": "18.3.1", - "@types/react-dom": "18.3.0", - "typescript": "5.5.4" + "@repo/eslint-config": "workspace:^", + "@repo/typescript-config": "workspace:^", + "@types/node": "^25.0.2", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "eslint": "^9.39.2", + "typescript": "^5.9.3" } } diff --git a/lefthook.yml b/lefthook.yml index c66bba8e98..7b9926c50d 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -3,6 +3,11 @@ pre-commit: prettier: glob: '**/*.{js,jsx,ts,tsx,json,md,yml,yaml}' run: pnpm prettier --write {staged_files} && git add {staged_files} + syncpack: + glob: + - "package.json" + - "packages/**/package.json" + run: pnpm deps:lint pre-push: commands: diff --git a/package.json b/package.json index 023823b952..53a8caae3e 100644 --- a/package.json +++ b/package.json @@ -15,18 +15,46 @@ "dev:server": "node packages/wallet/primitives-cli/dist/index.js server", "reinstall": "rimraf -g ./**/node_modules && pnpm install", "test:anvil": "anvil --fork-url https://nodes.sequence.app/arbitrum", - "clean": "turbo clean" + "clean": "turbo clean", + "deps:lint": "syncpack list-mismatches", + "deps:fix": "syncpack fix-mismatches" }, "devDependencies": { - "@changesets/cli": "^2.29.4", - "lefthook": "^1.11.13", - "prettier": "^3.5.3", - "rimraf": "^6.0.1", - "turbo": "^2.5.4", - "typescript": "5.8.3" + "@changesets/cli": "^2.29.8", + "lefthook": "^2.0.12", + "prettier": "^3.7.4", + "rimraf": "^6.1.2", + "syncpack": "^13.0.4", + "turbo": "^2.6.3", + "typescript": "^5.9.3" }, - "packageManager": "pnpm@10.14.0", + "pnpm": { + "overrides": { + "ox": "^0.9.17" + } + }, + "packageManager": "pnpm@10.24.0", "engines": { "node": ">=18" + }, + "syncpack": { + "source": [ + "package.json", + "packages/**/package.json", + "extras/**/package.json", + "repo/**/package.json" + ], + "versionGroups": [ + { + "label": "Use workspace protocol when developing local packages", + "dependencyTypes": [ + "!local" + ], + "dependencies": [ + "$LOCAL" + ], + "pinVersion": "workspace:^" + } + ] } } diff --git a/packages/services/api/CHANGELOG.md b/packages/services/api/CHANGELOG.md index 0a41064826..11007faa91 100644 --- a/packages/services/api/CHANGELOG.md +++ b/packages/services/api/CHANGELOG.md @@ -1,5 +1,41 @@ # @0xsequence/api +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 + ## 2.3.8 ### Patch Changes @@ -1422,7 +1458,6 @@ - relayer: fix Relayer.wait() interface The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - delay: the polling interval, i.e. the time to wait between requests - maxFails: the maximum number of hard failures to tolerate before giving up @@ -1787,7 +1822,6 @@ ### Minor Changes - major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - separate per-chain indexer service, API no longer handles indexing - single contract metadata service, API no longer serves metadata diff --git a/packages/services/api/package.json b/packages/services/api/package.json index bba908e695..6191c7940d 100644 --- a/packages/services/api/package.json +++ b/packages/services/api/package.json @@ -1,8 +1,8 @@ { "name": "@0xsequence/api", - "version": "3.0.0", + "version": "3.0.0-beta.6", "description": "api sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/api", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/api", "author": "Sequence Platforms Inc.", "license": "Apache-2.0", "publishConfig": { @@ -16,13 +16,13 @@ }, "exports": { ".": { - "types": "./src/index.ts", + "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3" + "@types/node": "^25.0.2", + "typescript": "^5.9.3" } } diff --git a/packages/services/api/src/api.gen.ts b/packages/services/api/src/api.gen.ts index a42d143d6d..bf07aa0399 100644 --- a/packages/services/api/src/api.gen.ts +++ b/packages/services/api/src/api.gen.ts @@ -1,138 +1,554 @@ /* eslint-disable */ -// sequence-api v0.4.0 d43a5aac616814072c69e63f2f81fe65ea10a7e0 +// sequence-api v0.4.0 d7026da603b2c29baf21c6aceeebc86eada372d8 // -- -// Code generated by webrpc-gen@v0.25.3 with typescript generator. DO NOT EDIT. +// Code generated by Webrpc-gen@v0.31.0 with typescript generator. DO NOT EDIT. // // webrpc-gen -schema=api.ridl -target=typescript -client -out=./clients/api.gen.ts -export const WebrpcHeader = "Webrpc" - -export const WebrpcHeaderValue = "webrpc@v0.25.3;gen-typescript@v0.17.0;sequence-api@v0.4.0" - -// WebRPC description and code-gen version -export const WebRPCVersion = "v1" +// Webrpc description and code-gen version +export const WebrpcVersion = 'v1' // Schema version of your RIDL schema -export const WebRPCSchemaVersion = "v0.4.0" +export const WebrpcSchemaVersion = 'v0.4.0' // Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = "d43a5aac616814072c69e63f2f81fe65ea10a7e0" +export const WebrpcSchemaHash = 'd7026da603b2c29baf21c6aceeebc86eada372d8' -type WebrpcGenVersions = { - webrpcGenVersion: string; - codeGenName: string; - codeGenVersion: string; - schemaName: string; - schemaVersion: string; -}; +// +// Client interface +// -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader); - if (!headerValue) { - return { - webrpcGenVersion: "", - codeGenName: "", - codeGenVersion: "", - schemaName: "", - schemaVersion: "", - }; - } +export interface APIClient { + /** + * + * Runtime + * + */ + ping(headers?: object, signal?: AbortSignal): Promise - return parseWebrpcGenVersions(headerValue); -} + version(headers?: object, signal?: AbortSignal): Promise -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(";"); - if (versions.length < 3) { - return { - webrpcGenVersion: "", - codeGenName: "", - codeGenVersion: "", - schemaName: "", - schemaVersion: "", - }; - } + runtimeStatus(headers?: object, signal?: AbortSignal): Promise - const [_, webrpcGenVersion] = versions[0]!.split("@"); - const [codeGenName, codeGenVersion] = versions[1]!.split("@"); - const [schemaName, schemaVersion] = versions[2]!.split("@"); + clock(headers?: object, signal?: AbortSignal): Promise - return { - webrpcGenVersion: webrpcGenVersion ?? "", - codeGenName: codeGenName ?? "", - codeGenVersion: codeGenVersion ?? "", - schemaName: schemaName ?? "", - schemaVersion: schemaVersion ?? "", - }; + getSequenceContext(headers?: object, signal?: AbortSignal): Promise + + /** + * + * Auth + * + * TODO: rename 'ewtString' arg to 'ethauthProof' + */ + getAuthToken(req: GetAuthTokenRequest, headers?: object, signal?: AbortSignal): Promise + + getAuthToken2(req: GetAuthToken2Request, headers?: object, signal?: AbortSignal): Promise + + sendPasswordlessLink( + req: SendPasswordlessLinkRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + registerPublicKey( + req: RegisterPublicKeyRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + getPublicKey(req: GetPublicKeyRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * + * Contacts / Friends + * + */ + friendList(req: FriendListRequest, headers?: object, signal?: AbortSignal): Promise + + getFriendByAddress( + req: GetFriendByAddressRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + searchFriends(req: SearchFriendsRequest, headers?: object, signal?: AbortSignal): Promise + + addFriend(req: AddFriendRequest, headers?: object, signal?: AbortSignal): Promise + + updateFriendNickname( + req: UpdateFriendNicknameRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + removeFriend(req: RemoveFriendRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * + * Chain-Utils + * + */ + contractCall(req: ContractCallRequest, headers?: object, signal?: AbortSignal): Promise + + decodeContractCall( + req: DecodeContractCallRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + lookupContractCallSelectors( + req: LookupContractCallSelectorsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * User Storage + * + */ + userStorageFetch( + req: UserStorageFetchRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + userStorageSave(req: UserStorageSaveRequest, headers?: object, signal?: AbortSignal): Promise + + userStorageDelete( + req: UserStorageDeleteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + userStorageFetchAll( + req: UserStorageFetchAllRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * Wallet utils + * + */ + getMoonpayLink(req: GetMoonpayLinkRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * - IsUsingGoogleMail(domain: string) => (yes: bool) + */ + resolveENSAddress( + req: ResolveENSAddressRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * TODO: we can add walletContext optional in the future when we need it + * NOTE: chainId can be either a number or canonical name + */ + isValidSignature( + req: IsValidSignatureRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + isValidMessageSignature( + req: IsValidMessageSignatureRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + isValidTypedDataSignature( + req: IsValidTypedDataSignatureRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + isValidETHAuthProof( + req: IsValidETHAuthProofRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + getOnRampURL(req: GetOnRampURLRequest, headers?: object, signal?: AbortSignal): Promise + + transakGetCountries(headers?: object, signal?: AbortSignal): Promise + + transakGetCryptoCurrencies(headers?: object, signal?: AbortSignal): Promise + + transakGetFiatCurrencies(headers?: object, signal?: AbortSignal): Promise + + transakGetPrice(req: TransakGetPriceRequest, headers?: object, signal?: AbortSignal): Promise + + transakGetSupportedNFTCheckoutChains( + headers?: object, + signal?: AbortSignal, + ): Promise + + transakGetWidgetURL( + req: TransakGetWidgetURLRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * Price Feed + * + */ + getCoinPrices(req: GetCoinPricesRequest, headers?: object, signal?: AbortSignal): Promise + + getCollectiblePrices( + req: GetCollectiblePricesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * Price Feed utils + * + */ + getExchangeRate(req: GetExchangeRateRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * + * Util / misc + * + */ + memoryStore(req: MemoryStoreRequest, headers?: object, signal?: AbortSignal): Promise + + memoryLoad(req: MemoryLoadRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * + * Legacy + * + */ + getInviteInfo(headers?: object, signal?: AbortSignal): Promise + + /** + * NOTE: we're still using this from SW-API to Sequence-API to claim invite code + */ + isValidAccessCode( + req: IsValidAccessCodeRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + internalClaimAccessCode( + req: InternalClaimAccessCodeRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Utils + */ + blockNumberAtTime( + req: BlockNumberAtTimeRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * Paper + * TODO: deprecate in the future + * + */ + paperSessionSecret( + req: PaperSessionSecretRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + paperSessionSecret2( + req: PaperSessionSecret2Request, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * Linked wallets (v0 -- simple support) + * + */ + linkWallet(req: LinkWalletRequest, headers?: object, signal?: AbortSignal): Promise + + getLinkedWallets( + req: GetLinkedWalletsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + removeLinkedWallet( + req: RemoveLinkedWalletRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * NOTE: these methods are deprecated, please do not use them. We may resurface them in the future, but just wanted + * to be clear, they are not necessary for our linked wallets. + */ + generateWaaSVerificationURL( + req: GenerateWaaSVerificationURLRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + validateWaaSVerificationNonce( + req: ValidateWaaSVerificationNonceRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * + * WaaS child wallet adoption + * + */ + listAdoptedWallets( + req: ListAdoptedWalletsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + getLifiChains(headers?: object, signal?: AbortSignal): Promise + + getLifiTokens(req: GetLifiTokensRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * All parameters except `params` are deprecated. + * Use only the `params` object to pass values. + */ + getLifiSwapRoutes( + req: GetLifiSwapRoutesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + getLifiSwapQuote( + req: GetLifiSwapQuoteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * Chain abstraction + * + */ + getIntentCallsPayloads( + req: GetIntentCallsPayloadsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + commitIntentConfig( + req: CommitIntentConfigRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + getIntentConfig(req: GetIntentConfigRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * + * Inventory, payments and management + * + */ + listCurrencyGroups(headers?: object, signal?: AbortSignal): Promise + + addOffchainInventory( + req: AddOffchainInventoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + getOffchainInventory( + req: GetOffchainInventoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + listOffchainInventories( + req: ListOffchainInventoriesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + updateOffchainInventory( + req: UpdateOffchainInventoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + deleteOffchainInventory( + req: DeleteOffchainInventoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + requestOffchainPayment( + req: RequestOffchainPaymentRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + listOffchainPayments( + req: ListOffchainPaymentsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * Packs + * + */ + savePack(req: SavePackRequest, headers?: object, signal?: AbortSignal): Promise + + getPack(req: GetPackRequest, headers?: object, signal?: AbortSignal): Promise + + getPackIds(req: GetPackIdsRequest, headers?: object, signal?: AbortSignal): Promise + + deletePack(req: DeletePackRequest, headers?: object, signal?: AbortSignal): Promise + + updatePackContent( + req: UpdatePackContentRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + getRevealTxData(req: GetRevealTxDataRequest, headers?: object, signal?: AbortSignal): Promise + + checkoutOptionsPrimary( + req: CheckoutOptionsPrimaryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + checkoutOptionsSecondary( + req: CheckoutOptionsSecondaryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + checkoutOptionsGetTransakContractID( + req: CheckoutOptionsGetTransakContractIDRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + fortePayCreateIntent( + req: FortePayCreateIntentRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + fortePayGetPaymentStatuses( + req: FortePayGetPaymentStatusesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * CCTP + * + */ + getCCTPTransfer(req: GetCCTPTransferRequest, headers?: object, signal?: AbortSignal): Promise + + queueCCTPTransfer( + req: QueueCCTPTransferRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * + * Intent Machine Worker + * + */ + queueIntentConfigExecution( + req: QueueIntentConfigExecutionRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + getIntentConfigExecutionStatus( + req: GetIntentConfigExecutionStatusRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + listIntentConfigs( + req: ListIntentConfigsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + queueMetaTxnReceipt( + req: QueueMetaTxnReceiptRequest, + headers?: object, + signal?: AbortSignal, + ): Promise } // -// Types +// Schema types // - export enum SortOrder { DESC = 'DESC', - ASC = 'ASC' -} - -export enum SardinePaymentType { - ach = 'ach', - debit = 'debit', - credit = 'credit', - us_debit = 'us_debit', - international_debit = 'international_debit', - international_credit = 'international_credit' -} - -export enum SardineQuoteType { - buy = 'buy', - sell = 'sell' + ASC = 'ASC', } export enum GetLifiSwapRouteDirection { to = 'to', - from = 'from' + from = 'from', } export enum TokenType { ERC20 = 'ERC20', ERC721 = 'ERC721', - ERC1155 = 'ERC1155' + ERC1155 = 'ERC1155', } export enum TransakBuySell { UNKNOWN = 'UNKNOWN', BUY = 'BUY', - SELL = 'SELL' + SELL = 'SELL', } export enum TradeType { EXACT_INPUT = 'EXACT_INPUT', - EXACT_OUTPUT = 'EXACT_OUTPUT' + EXACT_OUTPUT = 'EXACT_OUTPUT', } export enum CheckoutOptionCrypto { none = 'none', partially = 'partially', - all = 'all' + all = 'all', } export enum CheckoutOptionNFTCheckoutProvider { unknown = 'unknown', - sardine = 'sardine', - transak = 'transak' + transak = 'transak', } export enum CheckoutOptionOnRampProvider { unknown = 'unknown', - sardine = 'sardine', - transak = 'transak' + transak = 'transak', } export enum CheckoutOptionSwapProvider { unknown = 'unknown', - lifi = 'lifi' + lifi = 'lifi', } export interface Version { @@ -150,7 +566,7 @@ export interface RuntimeStatus { branch: string commitHash: string checks: RuntimeChecks - numTxnsRelayed: {[key: string]: NumTxnsRelayed} + numTxnsRelayed: { [key: string]: NumTxnsRelayed } } export interface NumTxnsRelayed { @@ -160,8 +576,7 @@ export interface NumTxnsRelayed { period: number } -export interface RuntimeChecks { -} +export interface RuntimeChecks {} export interface SequenceContext { factory: string @@ -372,159 +787,6 @@ export interface SortBy { order: SortOrder } -export interface SardineNFTCheckoutParams { - name: string - imageUrl: string - network: string - recipientAddress: string - blockchainNftId: string - contractAddress: string - quantity: number - decimals?: number - tokenAmount: string - tokenAddress: string - tokenSymbol: string - tokenDecimals?: number - calldata: string - platform: string - approvedSpenderAddress?: string -} - -export interface SardineNFTCheckout { - token: string - expiresAt: string - orderId: string -} - -export interface SardineOrder { - id: string - createdAt?: string - referenceId: string - status: string - fiatCurrency: string - fiatExchangeRateUSD: number - transactionId: string - expiresAt?: string - total: number - subTotal: number - transactionFee: number - networkFee: number - paymentCurrency?: string - paymentMethodType?: string - transactionType: string - name: string - price: number - imageUrl: string - contractAddress?: string - transactionHash?: string - recipientAddress: string -} - -export interface SardineRegion { - countryCode: string - isAllowedOnRamp: boolean - isAllowedOnNFT: boolean - isBasicKycRequired: Array - isSsnRequired: Array - name: string - currencyCode: string - isPayrollSupported: boolean - supportedDocuments: Array - paymentMethods: Array - states: Array -} - -export interface SardineRegionPaymentMethod { - name: string - isAllowedOnRamp: boolean - isAllowedOnNFT: boolean - subTypes: Array - type: string - subType: string -} - -export interface SardineRegionState { - code: string - name: string - isAllowedOnRamp: boolean - isAllowedOnNFT: boolean -} - -export interface SardineSupportedToken { - network: string - assetSymbol: string - assetName: string - chainId: string - tokenName: string - token: string - tokenAddress: string -} - -export interface SardineSupportedTokenForSwap { - isSupported: boolean - isSupportedForAbstraction: boolean - currentBalance: string -} - -export interface SardineEnabledToken { - network: string - assetSymbol: string - assetName: string - chainId: string - tokenName: string - token: string - tokenAddress: string -} - -export interface SardineGetQuoteParams { - assetType: string - network: string - total: number - currency?: string - paymentType?: SardinePaymentType - quoteType?: SardineQuoteType - walletAddress?: string -} - -export interface SardineQuote { - quantity: number - network: string - assetType: string - total: number - currency: string - expiresAt: string - paymentType: string - price: number - subtotal: number - transactionFee: number - networkFee: number - highNetworkFee: boolean - minTransactionValue: number - maxTransactionValue: number - liquidityProvider: string -} - -export interface SardineFiatCurrency { - currencyCode: string - name: string - currencySymbol: string - paymentOptions: Array - supportingCountries: Array -} - -export interface SardinePaymentOption { - name: string - dailyLimit: number - weeklyLimit: number - monthlyLimit: number - maxAmount: number - minAmount: number - subTypes: Array - type: string - subType: string - processingTime: string -} - export interface LifiToken { chainId: number address: string @@ -806,6 +1068,36 @@ export interface TransakGetPriceParams { quoteCountryCode: string } +export interface TransakNFTData { + imageUrl: string + nftName: string + collectionAddress: string + tokenIds: Array + prices: Array + quantity: number + nftType: string +} + +export interface TransakGetWidgetURLParams { + targetContractAddress?: string + isNft?: boolean + calldata?: string + cryptoCurrencyCode?: string + estimatedGasLimit?: number + nftData: Array + walletAddress?: string + disableWalletAddressForm?: boolean + partnerOrderId?: string + network?: string + referrerDomain?: string + fiatAmount?: string + fiatCurrency?: string + defaultFiatAmount?: string + defaultCryptoCurrency?: string + cryptoCurrencyList?: string + networks?: string +} + export interface TransakChain { name: string chainId: number @@ -960,357 +1252,146 @@ export interface IntentQuote { quoteProvider: string quoteProviderRequestId: string quoteProviderFeeUsd: string - feeQuotes: {[key: string]: string} + feeQuotes: { [key: string]: string } } -export interface API { - /** - * - * Runtime - * - */ - ping(headers?: object, signal?: AbortSignal): Promise - version(headers?: object, signal?: AbortSignal): Promise - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - clock(headers?: object, signal?: AbortSignal): Promise - getSequenceContext(headers?: object, signal?: AbortSignal): Promise - /** - * - * Auth - * - * TODO: rename 'ewtString' arg to 'ethauthProof' - */ - getAuthToken(args: GetAuthTokenArgs, headers?: object, signal?: AbortSignal): Promise - getAuthToken2(args: GetAuthToken2Args, headers?: object, signal?: AbortSignal): Promise - sendPasswordlessLink(args: SendPasswordlessLinkArgs, headers?: object, signal?: AbortSignal): Promise - registerPublicKey(args: RegisterPublicKeyArgs, headers?: object, signal?: AbortSignal): Promise - getPublicKey(args: GetPublicKeyArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Contacts / Friends - * - */ - friendList(args: FriendListArgs, headers?: object, signal?: AbortSignal): Promise - getFriendByAddress(args: GetFriendByAddressArgs, headers?: object, signal?: AbortSignal): Promise - searchFriends(args: SearchFriendsArgs, headers?: object, signal?: AbortSignal): Promise - addFriend(args: AddFriendArgs, headers?: object, signal?: AbortSignal): Promise - updateFriendNickname(args: UpdateFriendNicknameArgs, headers?: object, signal?: AbortSignal): Promise - removeFriend(args: RemoveFriendArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Chain-Utils - * - */ - contractCall(args: ContractCallArgs, headers?: object, signal?: AbortSignal): Promise - decodeContractCall(args: DecodeContractCallArgs, headers?: object, signal?: AbortSignal): Promise - lookupContractCallSelectors(args: LookupContractCallSelectorsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * User Storage - * - */ - userStorageFetch(args: UserStorageFetchArgs, headers?: object, signal?: AbortSignal): Promise - userStorageSave(args: UserStorageSaveArgs, headers?: object, signal?: AbortSignal): Promise - userStorageDelete(args: UserStorageDeleteArgs, headers?: object, signal?: AbortSignal): Promise - userStorageFetchAll(args: UserStorageFetchAllArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Wallet utils - * - */ - getMoonpayLink(args: GetMoonpayLinkArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - IsUsingGoogleMail(domain: string) => (yes: bool) - */ - resolveENSAddress(args: ResolveENSAddressArgs, headers?: object, signal?: AbortSignal): Promise - /** - * TODO: we can add walletContext optional in the future when we need it - * NOTE: chainId can be either a number or canonical name - */ - isValidSignature(args: IsValidSignatureArgs, headers?: object, signal?: AbortSignal): Promise - isValidMessageSignature(args: IsValidMessageSignatureArgs, headers?: object, signal?: AbortSignal): Promise - isValidTypedDataSignature(args: IsValidTypedDataSignatureArgs, headers?: object, signal?: AbortSignal): Promise - isValidETHAuthProof(args: IsValidETHAuthProofArgs, headers?: object, signal?: AbortSignal): Promise - getOnRampURL(args: GetOnRampURLArgs, headers?: object, signal?: AbortSignal): Promise - sardineGetClientToken(headers?: object, signal?: AbortSignal): Promise - sardineGetNFTCheckoutToken(args: SardineGetNFTCheckoutTokenArgs, headers?: object, signal?: AbortSignal): Promise - sardineGetNFTCheckoutOrderStatus(args: SardineGetNFTCheckoutOrderStatusArgs, headers?: object, signal?: AbortSignal): Promise - sardineGetSupportedRegions(headers?: object, signal?: AbortSignal): Promise - sardineGetSupportedFiatCurrencies(headers?: object, signal?: AbortSignal): Promise - sardineGetSupportedTokens(headers?: object, signal?: AbortSignal): Promise - sardineGetSupportedTokenForSwap(args: SardineGetSupportedTokenForSwapArgs, headers?: object, signal?: AbortSignal): Promise - sardineGetEnabledTokens(headers?: object, signal?: AbortSignal): Promise - sardineGetQuote(args: SardineGetQuoteArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Deprecated. Use SardineGetClientToken() instead. - */ - getSardineClientToken(headers?: object, signal?: AbortSignal): Promise - /** - * Deprecated. Use SardineGetNFTCheckoutToken() instead. - */ - getSardineNFTCheckoutToken(args: GetSardineNFTCheckoutTokenArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Deprecated. Use SardineGetNFTCheckoutOrderStatus() instead. - */ - getSardineNFTCheckoutOrderStatus(args: GetSardineNFTCheckoutOrderStatusArgs, headers?: object, signal?: AbortSignal): Promise - transakGetCountries(headers?: object, signal?: AbortSignal): Promise - transakGetCryptoCurrencies(headers?: object, signal?: AbortSignal): Promise - transakGetFiatCurrencies(headers?: object, signal?: AbortSignal): Promise - transakGetPrice(args: TransakGetPriceArgs, headers?: object, signal?: AbortSignal): Promise - transakGetSupportedNFTCheckoutChains(headers?: object, signal?: AbortSignal): Promise - /** - * - * Price Feed - * - */ - getCoinPrices(args: GetCoinPricesArgs, headers?: object, signal?: AbortSignal): Promise - getCollectiblePrices(args: GetCollectiblePricesArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Price Feed utils - * - */ - getExchangeRate(args: GetExchangeRateArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Util / misc - * - */ - memoryStore(args: MemoryStoreArgs, headers?: object, signal?: AbortSignal): Promise - memoryLoad(args: MemoryLoadArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Legacy - * - */ - getInviteInfo(headers?: object, signal?: AbortSignal): Promise - /** - * NOTE: we're still using this from SW-API to Sequence-API to claim invite code - */ - isValidAccessCode(args: IsValidAccessCodeArgs, headers?: object, signal?: AbortSignal): Promise - internalClaimAccessCode(args: InternalClaimAccessCodeArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Utils - */ - blockNumberAtTime(args: BlockNumberAtTimeArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Paper - * TODO: deprecate in the future - * - */ - paperSessionSecret(args: PaperSessionSecretArgs, headers?: object, signal?: AbortSignal): Promise - paperSessionSecret2(args: PaperSessionSecret2Args, headers?: object, signal?: AbortSignal): Promise - /** - * - * Linked wallets (v0 -- simple support) - * - */ - linkWallet(args: LinkWalletArgs, headers?: object, signal?: AbortSignal): Promise - getLinkedWallets(args: GetLinkedWalletsArgs, headers?: object, signal?: AbortSignal): Promise - removeLinkedWallet(args: RemoveLinkedWalletArgs, headers?: object, signal?: AbortSignal): Promise - /** - * NOTE: these methods are deprecated, please do not use them. We may resurface them in the future, but just wanted - * to be clear, they are not necessary for our linked wallets. - */ - generateWaaSVerificationURL(args: GenerateWaaSVerificationURLArgs, headers?: object, signal?: AbortSignal): Promise - validateWaaSVerificationNonce(args: ValidateWaaSVerificationNonceArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * - * WaaS child wallet adoption - * - */ - listAdoptedWallets(args: ListAdoptedWalletsArgs, headers?: object, signal?: AbortSignal): Promise - getLifiChains(headers?: object, signal?: AbortSignal): Promise - getLifiTokens(args: GetLifiTokensArgs, headers?: object, signal?: AbortSignal): Promise - /** - * All parameters except `params` are deprecated. - * Use only the `params` object to pass values. - */ - getLifiSwapRoutes(args: GetLifiSwapRoutesArgs, headers?: object, signal?: AbortSignal): Promise - getLifiSwapQuote(args: GetLifiSwapQuoteArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Chain abstraction - * - */ - getIntentCallsPayloads(args: GetIntentCallsPayloadsArgs, headers?: object, signal?: AbortSignal): Promise - commitIntentConfig(args: CommitIntentConfigArgs, headers?: object, signal?: AbortSignal): Promise - getIntentConfig(args: GetIntentConfigArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Inventory, payments and management - * - */ - listCurrencyGroups(headers?: object, signal?: AbortSignal): Promise - addOffchainInventory(args: AddOffchainInventoryArgs, headers?: object, signal?: AbortSignal): Promise - getOffchainInventory(args: GetOffchainInventoryArgs, headers?: object, signal?: AbortSignal): Promise - listOffchainInventories(args: ListOffchainInventoriesArgs, headers?: object, signal?: AbortSignal): Promise - updateOffchainInventory(args: UpdateOffchainInventoryArgs, headers?: object, signal?: AbortSignal): Promise - deleteOffchainInventory(args: DeleteOffchainInventoryArgs, headers?: object, signal?: AbortSignal): Promise - requestOffchainPayment(args: RequestOffchainPaymentArgs, headers?: object, signal?: AbortSignal): Promise - listOffchainPayments(args: ListOffchainPaymentsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Packs - * - */ - savePack(args: SavePackArgs, headers?: object, signal?: AbortSignal): Promise - getPack(args: GetPackArgs, headers?: object, signal?: AbortSignal): Promise - getPackIds(args: GetPackIdsArgs, headers?: object, signal?: AbortSignal): Promise - deletePack(args: DeletePackArgs, headers?: object, signal?: AbortSignal): Promise - updatePackContent(args: UpdatePackContentArgs, headers?: object, signal?: AbortSignal): Promise - getRevealTxData(args: GetRevealTxDataArgs, headers?: object, signal?: AbortSignal): Promise - checkoutOptionsPrimary(args: CheckoutOptionsPrimaryArgs, headers?: object, signal?: AbortSignal): Promise - checkoutOptionsSecondary(args: CheckoutOptionsSecondaryArgs, headers?: object, signal?: AbortSignal): Promise - checkoutOptionsGetTransakContractID(args: CheckoutOptionsGetTransakContractIDArgs, headers?: object, signal?: AbortSignal): Promise - fortePayCreateIntent(args: FortePayCreateIntentArgs, headers?: object, signal?: AbortSignal): Promise - fortePayGetPaymentStatuses(args: FortePayGetPaymentStatusesArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * CCTP - * - */ - getCCTPTransfer(args: GetCCTPTransferArgs, headers?: object, signal?: AbortSignal): Promise - queueCCTPTransfer(args: QueueCCTPTransferArgs, headers?: object, signal?: AbortSignal): Promise - /** - * - * Intent Machine Worker - * - */ - queueIntentConfigExecution(args: QueueIntentConfigExecutionArgs, headers?: object, signal?: AbortSignal): Promise - getIntentConfigExecutionStatus(args: GetIntentConfigExecutionStatusArgs, headers?: object, signal?: AbortSignal): Promise - listIntentConfigs(args: ListIntentConfigsArgs, headers?: object, signal?: AbortSignal): Promise - queueMetaTxnReceipt(args: QueueMetaTxnReceiptArgs, headers?: object, signal?: AbortSignal): Promise -} +export interface PingRequest {} -export interface PingArgs { +export interface PingResponse { + status: boolean } -export interface PingReturn { - status: boolean -} -export interface VersionArgs { -} +export interface VersionRequest {} -export interface VersionReturn { - version: Version -} -export interface RuntimeStatusArgs { +export interface VersionResponse { + version: Version } -export interface RuntimeStatusReturn { - status: RuntimeStatus -} -export interface ClockArgs { -} +export interface RuntimeStatusRequest {} -export interface ClockReturn { - serverTime: string +export interface RuntimeStatusResponse { + status: RuntimeStatus } -export interface GetSequenceContextArgs { + +export interface ClockRequest {} + +export interface ClockResponse { + serverTime: string } -export interface GetSequenceContextReturn { - data: SequenceContext +export interface GetSequenceContextRequest {} + +export interface GetSequenceContextResponse { + data: SequenceContext } -export interface GetAuthTokenArgs { + +export interface GetAuthTokenRequest { ewtString: string testnetMode?: boolean } -export interface GetAuthTokenReturn { +export interface GetAuthTokenResponse { status: boolean jwtToken: string address: string - user?: User + user?: User } -export interface GetAuthToken2Args { + +export interface GetAuthToken2Request { ewtString: string chainID: string } -export interface GetAuthToken2Return { +export interface GetAuthToken2Response { status: boolean jwtToken: string address: string - user?: User + user?: User } -export interface SendPasswordlessLinkArgs { + +export interface SendPasswordlessLinkRequest { email: string redirectUri: string intent: string } -export interface SendPasswordlessLinkReturn { - status: boolean +export interface SendPasswordlessLinkResponse { + status: boolean } -export interface RegisterPublicKeyArgs { + +export interface RegisterPublicKeyRequest { publicKey: PublicKey } -export interface RegisterPublicKeyReturn { - status: boolean +export interface RegisterPublicKeyResponse { + status: boolean } -export interface GetPublicKeyArgs { + +export interface GetPublicKeyRequest { id: string } -export interface GetPublicKeyReturn { - publicKey: PublicKey +export interface GetPublicKeyResponse { + publicKey: PublicKey } -export interface FriendListArgs { + +export interface FriendListRequest { nickname?: string page?: Page } -export interface FriendListReturn { +export interface FriendListResponse { page: Page - friends: Array + friends: Array } -export interface GetFriendByAddressArgs { + +export interface GetFriendByAddressRequest { friendAddress: string } -export interface GetFriendByAddressReturn { +export interface GetFriendByAddressResponse { status: boolean - friend: Friend + friend: Friend } -export interface SearchFriendsArgs { + +export interface SearchFriendsRequest { filterUsername: string page?: Page } -export interface SearchFriendsReturn { - friends: Array +export interface SearchFriendsResponse { + friends: Array } -export interface AddFriendArgs { + +export interface AddFriendRequest { friendAddress: string optionalNickname?: string } -export interface AddFriendReturn { +export interface AddFriendResponse { status: boolean - friend?: Friend + friend?: Friend } -export interface UpdateFriendNicknameArgs { + +export interface UpdateFriendNicknameRequest { friendAddress: string nickname: string } -export interface UpdateFriendNicknameReturn { +export interface UpdateFriendNicknameResponse { status: boolean - friend?: Friend + friend?: Friend } -export interface RemoveFriendArgs { + +export interface RemoveFriendRequest { friendAddress: string } -export interface RemoveFriendReturn { - status: boolean +export interface RemoveFriendResponse { + status: boolean } -export interface ContractCallArgs { + +export interface ContractCallRequest { chainID: string contract: string inputExpr: string @@ -1318,309 +1399,263 @@ export interface ContractCallArgs { args: Array } -export interface ContractCallReturn { - returns: Array +export interface ContractCallResponse { + returns: Array } -export interface DecodeContractCallArgs { + +export interface DecodeContractCallRequest { callData: string } -export interface DecodeContractCallReturn { - call: ContractCall +export interface DecodeContractCallResponse { + call: ContractCall } -export interface LookupContractCallSelectorsArgs { + +export interface LookupContractCallSelectorsRequest { selectors: Array } -export interface LookupContractCallSelectorsReturn { - signatures: Array> +export interface LookupContractCallSelectorsResponse { + signatures: Array> } -export interface UserStorageFetchArgs { + +export interface UserStorageFetchRequest { key: string } -export interface UserStorageFetchReturn { - object: any +export interface UserStorageFetchResponse { + object: any } -export interface UserStorageSaveArgs { + +export interface UserStorageSaveRequest { key: string object: any } -export interface UserStorageSaveReturn { - ok: boolean +export interface UserStorageSaveResponse { + ok: boolean } -export interface UserStorageDeleteArgs { + +export interface UserStorageDeleteRequest { key: string } -export interface UserStorageDeleteReturn { - ok: boolean +export interface UserStorageDeleteResponse { + ok: boolean } -export interface UserStorageFetchAllArgs { + +export interface UserStorageFetchAllRequest { keys?: Array } -export interface UserStorageFetchAllReturn { - objects: {[key: string]: any} +export interface UserStorageFetchAllResponse { + objects: { [key: string]: any } } -export interface GetMoonpayLinkArgs { + +export interface GetMoonpayLinkRequest { url: string } -export interface GetMoonpayLinkReturn { - signedUrl: string +export interface GetMoonpayLinkResponse { + signedUrl: string } -export interface ResolveENSAddressArgs { + +export interface ResolveENSAddressRequest { ens: string } -export interface ResolveENSAddressReturn { +export interface ResolveENSAddressResponse { address: string - ok: boolean + ok: boolean } -export interface IsValidSignatureArgs { + +export interface IsValidSignatureRequest { chainId: string walletAddress: string digest: string signature: string } -export interface IsValidSignatureReturn { - isValid: boolean +export interface IsValidSignatureResponse { + isValid: boolean } -export interface IsValidMessageSignatureArgs { + +export interface IsValidMessageSignatureRequest { chainId: string walletAddress: string message: string signature: string } -export interface IsValidMessageSignatureReturn { - isValid: boolean +export interface IsValidMessageSignatureResponse { + isValid: boolean } -export interface IsValidTypedDataSignatureArgs { + +export interface IsValidTypedDataSignatureRequest { chainId: string walletAddress: string typedData: any signature: string } -export interface IsValidTypedDataSignatureReturn { - isValid: boolean +export interface IsValidTypedDataSignatureResponse { + isValid: boolean } -export interface IsValidETHAuthProofArgs { + +export interface IsValidETHAuthProofRequest { chainId: string walletAddress: string ethAuthProofString: string } -export interface IsValidETHAuthProofReturn { - isValid: boolean -} -export interface GetOnRampURLArgs { - chainId: string +export interface IsValidETHAuthProofResponse { + isValid: boolean } -export interface GetOnRampURLReturn { - url: string -} -export interface SardineGetClientTokenArgs { +export interface GetOnRampURLRequest { + chainId: string } -export interface SardineGetClientTokenReturn { - token: string -} -export interface SardineGetNFTCheckoutTokenArgs { - params: SardineNFTCheckoutParams +export interface GetOnRampURLResponse { + url: string } -export interface SardineGetNFTCheckoutTokenReturn { - resp: SardineNFTCheckout -} -export interface SardineGetNFTCheckoutOrderStatusArgs { - orderId: string -} +export interface TransakGetCountriesRequest {} -export interface SardineGetNFTCheckoutOrderStatusReturn { - resp: SardineOrder -} -export interface SardineGetSupportedRegionsArgs { +export interface TransakGetCountriesResponse { + regions: Array } -export interface SardineGetSupportedRegionsReturn { - regions: Array -} -export interface SardineGetSupportedFiatCurrenciesArgs { -} +export interface TransakGetCryptoCurrenciesRequest {} -export interface SardineGetSupportedFiatCurrenciesReturn { - tokens: Array -} -export interface SardineGetSupportedTokensArgs { +export interface TransakGetCryptoCurrenciesResponse { + currencies: Array } -export interface SardineGetSupportedTokensReturn { - tokens: Array -} -export interface SardineGetSupportedTokenForSwapArgs { - network: string - tokenAddress: string -} +export interface TransakGetFiatCurrenciesRequest {} -export interface SardineGetSupportedTokenForSwapReturn { - token: SardineSupportedTokenForSwap -} -export interface SardineGetEnabledTokensArgs { +export interface TransakGetFiatCurrenciesResponse { + currencies: Array } -export interface SardineGetEnabledTokensReturn { - tokens: Array -} -export interface SardineGetQuoteArgs { - params: SardineGetQuoteParams +export interface TransakGetPriceRequest { + params: TransakGetPriceParams } -export interface SardineGetQuoteReturn { - quote: SardineQuote -} -export interface GetSardineClientTokenArgs { +export interface TransakGetPriceResponse { + price: TransakPrice } -export interface GetSardineClientTokenReturn { - token: string -} -export interface GetSardineNFTCheckoutTokenArgs { - params: SardineNFTCheckoutParams -} +export interface TransakGetSupportedNFTCheckoutChainsRequest {} -export interface GetSardineNFTCheckoutTokenReturn { - resp: SardineNFTCheckout -} -export interface GetSardineNFTCheckoutOrderStatusArgs { - orderId: string +export interface TransakGetSupportedNFTCheckoutChainsResponse { + chains: Array } -export interface GetSardineNFTCheckoutOrderStatusReturn { - resp: SardineOrder -} -export interface TransakGetCountriesArgs { +export interface TransakGetWidgetURLRequest { + params: TransakGetWidgetURLParams } -export interface TransakGetCountriesReturn { - regions: Array -} -export interface TransakGetCryptoCurrenciesArgs { -} - -export interface TransakGetCryptoCurrenciesReturn { - currencies: Array -} -export interface TransakGetFiatCurrenciesArgs { +export interface TransakGetWidgetURLResponse { + url: string } -export interface TransakGetFiatCurrenciesReturn { - currencies: Array -} -export interface TransakGetPriceArgs { - params: TransakGetPriceParams +export interface GetCoinPricesRequest { + tokens: Array } -export interface TransakGetPriceReturn { - price: TransakPrice -} -export interface TransakGetSupportedNFTCheckoutChainsArgs { +export interface GetCoinPricesResponse { + tokenPrices: Array } -export interface TransakGetSupportedNFTCheckoutChainsReturn { - chains: Array -} -export interface GetCoinPricesArgs { +export interface GetCollectiblePricesRequest { tokens: Array } -export interface GetCoinPricesReturn { - tokenPrices: Array -} -export interface GetCollectiblePricesArgs { - tokens: Array +export interface GetCollectiblePricesResponse { + tokenPrices: Array } -export interface GetCollectiblePricesReturn { - tokenPrices: Array -} -export interface GetExchangeRateArgs { +export interface GetExchangeRateRequest { toCurrency: string } -export interface GetExchangeRateReturn { - exchangeRate: ExchangeRate +export interface GetExchangeRateResponse { + exchangeRate: ExchangeRate } -export interface MemoryStoreArgs { + +export interface MemoryStoreRequest { key: string value: string } -export interface MemoryStoreReturn { - ok: boolean +export interface MemoryStoreResponse { + ok: boolean } -export interface MemoryLoadArgs { + +export interface MemoryLoadRequest { key: string } -export interface MemoryLoadReturn { - value: string -} -export interface GetInviteInfoArgs { +export interface MemoryLoadResponse { + value: string } -export interface GetInviteInfoReturn { - inviteInfo: InviteInfo +export interface GetInviteInfoRequest {} + +export interface GetInviteInfoResponse { + inviteInfo: InviteInfo } -export interface IsValidAccessCodeArgs { + +export interface IsValidAccessCodeRequest { accessCode: string } -export interface IsValidAccessCodeReturn { - status: boolean +export interface IsValidAccessCodeResponse { + status: boolean } -export interface InternalClaimAccessCodeArgs { + +export interface InternalClaimAccessCodeRequest { address: string accessCode: string } -export interface InternalClaimAccessCodeReturn { - status: boolean +export interface InternalClaimAccessCodeResponse { + status: boolean } -export interface BlockNumberAtTimeArgs { + +export interface BlockNumberAtTimeRequest { chainId: number timestamps: Array } -export interface BlockNumberAtTimeReturn { - blocks: Array +export interface BlockNumberAtTimeResponse { + blocks: Array } -export interface PaperSessionSecretArgs { + +export interface PaperSessionSecretRequest { chainName: string contractAddress: string paramsJson: string contractType: string } -export interface PaperSessionSecretReturn { - secret: string +export interface PaperSessionSecretResponse { + secret: string } -export interface PaperSessionSecret2Args { + +export interface PaperSessionSecret2Request { chainName: string contractAddress: string paramsJson: string abi: string } -export interface PaperSessionSecret2Return { - secret: string +export interface PaperSessionSecret2Response { + secret: string } -export interface LinkWalletArgs { + +export interface LinkWalletRequest { parentWalletAddress: string parentWalletMessage: string parentWalletSignature: string @@ -1631,20 +1666,22 @@ export interface LinkWalletArgs { linkedWalletType?: string } -export interface LinkWalletReturn { - status: boolean +export interface LinkWalletResponse { + status: boolean } -export interface GetLinkedWalletsArgs { + +export interface GetLinkedWalletsRequest { parentWalletAddress: string parentWalletMessage: string parentWalletSignature: string signatureChainId: string } -export interface GetLinkedWalletsReturn { - linkedWallets: Array +export interface GetLinkedWalletsResponse { + linkedWallets: Array } -export interface RemoveLinkedWalletArgs { + +export interface RemoveLinkedWalletRequest { parentWalletAddress: string parentWalletMessage: string parentWalletSignature: string @@ -1652,49 +1689,54 @@ export interface RemoveLinkedWalletArgs { signatureChainId: string } -export interface RemoveLinkedWalletReturn { - status: boolean +export interface RemoveLinkedWalletResponse { + status: boolean } -export interface GenerateWaaSVerificationURLArgs { + +export interface GenerateWaaSVerificationURLRequest { walletAddress: string } -export interface GenerateWaaSVerificationURLReturn { +export interface GenerateWaaSVerificationURLResponse { nonce: string - verificationURL: string + verificationURL: string } -export interface ValidateWaaSVerificationNonceArgs { + +export interface ValidateWaaSVerificationNonceRequest { nonce: string signature: string sessionId: string chainId: string } -export interface ValidateWaaSVerificationNonceReturn { - walletAddress: string +export interface ValidateWaaSVerificationNonceResponse { + walletAddress: string } -export interface ListAdoptedWalletsArgs { + +export interface ListAdoptedWalletsRequest { page?: Page } -export interface ListAdoptedWalletsReturn { +export interface ListAdoptedWalletsResponse { page: Page - wallets: Array -} -export interface GetLifiChainsArgs { + wallets: Array } -export interface GetLifiChainsReturn { - chains: Array +export interface GetLifiChainsRequest {} + +export interface GetLifiChainsResponse { + chains: Array } -export interface GetLifiTokensArgs { + +export interface GetLifiTokensRequest { chainIds: Array } -export interface GetLifiTokensReturn { - tokens: Array +export interface GetLifiTokensResponse { + tokens: Array } -export interface GetLifiSwapRoutesArgs { + +export interface GetLifiSwapRoutesRequest { params: GetLifiSwapRouteParams chainId: number toTokenAddress: string @@ -1702,17 +1744,19 @@ export interface GetLifiSwapRoutesArgs { walletAddress: string } -export interface GetLifiSwapRoutesReturn { - routes: Array +export interface GetLifiSwapRoutesResponse { + routes: Array } -export interface GetLifiSwapQuoteArgs { + +export interface GetLifiSwapQuoteRequest { params: GetLifiSwapQuoteParams } -export interface GetLifiSwapQuoteReturn { - quote: LifiSwapQuote +export interface GetLifiSwapQuoteResponse { + quote: LifiSwapQuote } -export interface GetIntentCallsPayloadsArgs { + +export interface GetIntentCallsPayloadsRequest { userAddress: string destinationChainId: number destinationTokenAddress: string @@ -1731,17 +1775,18 @@ export interface GetIntentCallsPayloadsArgs { tradeType?: TradeType } -export interface GetIntentCallsPayloadsReturn { +export interface GetIntentCallsPayloadsResponse { calls: Array preconditions: Array metaTxns: Array trailsFee: TrailsFee quote: IntentQuote - feeQuotes: {[key: string]: string} + feeQuotes: { [key: string]: string } originIntentAddress: string - destinationIntentAddress: string + destinationIntentAddress: string } -export interface CommitIntentConfigArgs { + +export interface CommitIntentConfigRequest { originIntentAddress: string destinationIntentAddress: string mainSigner: string @@ -1750,126 +1795,140 @@ export interface CommitIntentConfigArgs { addressOverrides?: AddressOverrides } -export interface CommitIntentConfigReturn { - config: IntentConfig +export interface CommitIntentConfigResponse { + config: IntentConfig } -export interface GetIntentConfigArgs { + +export interface GetIntentConfigRequest { intentAddress: string } -export interface GetIntentConfigReturn { - config: IntentConfig -} -export interface ListCurrencyGroupsArgs { +export interface GetIntentConfigResponse { + config: IntentConfig } -export interface ListCurrencyGroupsReturn { - currencyGroups: Array +export interface ListCurrencyGroupsRequest {} + +export interface ListCurrencyGroupsResponse { + currencyGroups: Array } -export interface AddOffchainInventoryArgs { + +export interface AddOffchainInventoryRequest { inventory: OffchainInventory } -export interface AddOffchainInventoryReturn { - inventoryId: number +export interface AddOffchainInventoryResponse { + inventoryId: number } -export interface GetOffchainInventoryArgs { + +export interface GetOffchainInventoryRequest { inventoryId: number } -export interface GetOffchainInventoryReturn { - inventory: OffchainInventory +export interface GetOffchainInventoryResponse { + inventory: OffchainInventory } -export interface ListOffchainInventoriesArgs { + +export interface ListOffchainInventoriesRequest { projectId: number } -export interface ListOffchainInventoriesReturn { - inventory: Array +export interface ListOffchainInventoriesResponse { + inventory: Array } -export interface UpdateOffchainInventoryArgs { + +export interface UpdateOffchainInventoryRequest { inventory: OffchainInventory } -export interface UpdateOffchainInventoryReturn { -} -export interface DeleteOffchainInventoryArgs { +export interface UpdateOffchainInventoryResponse {} + +export interface DeleteOffchainInventoryRequest { inventoryId: number } -export interface DeleteOffchainInventoryReturn { - ok: boolean +export interface DeleteOffchainInventoryResponse { + ok: boolean } -export interface RequestOffchainPaymentArgs { + +export interface RequestOffchainPaymentRequest { inventoryId: number recipient: string chainId?: number tokenAddress?: string } -export interface RequestOffchainPaymentReturn { - payment: PaymentResponse +export interface RequestOffchainPaymentResponse { + payment: PaymentResponse } -export interface ListOffchainPaymentsArgs { + +export interface ListOffchainPaymentsRequest { inventoryId: number page?: Page } -export interface ListOffchainPaymentsReturn { +export interface ListOffchainPaymentsResponse { page: Page - payments: Array + payments: Array } -export interface SavePackArgs { + +export interface SavePackRequest { pack: Pack } -export interface SavePackReturn { - merkleRoot: string +export interface SavePackResponse { + merkleRoot: string } -export interface GetPackArgs { + +export interface GetPackRequest { contractAddress: string packId: string chainId: number } -export interface GetPackReturn { - pack: Pack +export interface GetPackResponse { + pack: Pack } -export interface GetPackIdsArgs { + +export interface GetPackIdsRequest { contractAddress: string chainId: number } -export interface GetPackIdsReturn { - packIds: Array +export interface GetPackIdsResponse { + packIds: Array } -export interface DeletePackArgs { + +export interface DeletePackRequest { contractAddress: string packId: string chainId: number } -export interface DeletePackReturn { - status: boolean +export interface DeletePackResponse { + status: boolean } -export interface UpdatePackContentArgs { + +export interface UpdatePackContentRequest { pack: Pack } -export interface UpdatePackContentReturn { - merkleRoot: string +export interface UpdatePackContentResponse { + merkleRoot: string } -export interface GetRevealTxDataArgs { + +export interface GetRevealTxDataRequest { contractAddress: string packId: string chainId: number userAddress: string } -export interface GetRevealTxDataReturn { - txData: string +export interface GetRevealTxDataResponse { + txData: string } -export interface CheckoutOptionsPrimaryArgs { + +export interface CheckoutOptionsPrimaryRequest { chainId: number wallet: string contractAddress: string @@ -1877,94 +1936,103 @@ export interface CheckoutOptionsPrimaryArgs { params: Array } -export interface CheckoutOptionsPrimaryReturn { - options: CheckoutOptions +export interface CheckoutOptionsPrimaryResponse { + options: CheckoutOptions } -export interface CheckoutOptionsSecondaryArgs { + +export interface CheckoutOptionsSecondaryRequest { chainId: number wallet: string params: Array } -export interface CheckoutOptionsSecondaryReturn { - options: CheckoutOptions +export interface CheckoutOptionsSecondaryResponse { + options: CheckoutOptions } -export interface CheckoutOptionsGetTransakContractIDArgs { + +export interface CheckoutOptionsGetTransakContractIDRequest { chainId: number contractAddress: string } -export interface CheckoutOptionsGetTransakContractIDReturn { - contractId: string +export interface CheckoutOptionsGetTransakContractIDResponse { + contractId: string } -export interface FortePayCreateIntentArgs { + +export interface FortePayCreateIntentRequest { intent: FortePayCreateIntent } -export interface FortePayCreateIntentReturn { - resp: FortePayIntent +export interface FortePayCreateIntentResponse { + resp: FortePayIntent } -export interface FortePayGetPaymentStatusesArgs { + +export interface FortePayGetPaymentStatusesRequest { paymentIntentIds: Array } -export interface FortePayGetPaymentStatusesReturn { - statuses: Array +export interface FortePayGetPaymentStatusesResponse { + statuses: Array } -export interface GetCCTPTransferArgs { + +export interface GetCCTPTransferRequest { id: string } -export interface GetCCTPTransferReturn { - transfer: CCTPTransfer +export interface GetCCTPTransferResponse { + transfer: CCTPTransfer } -export interface QueueCCTPTransferArgs { + +export interface QueueCCTPTransferRequest { sourceTxHash?: string metaTxHash?: string sourceChainId: number destinationChainId: number } -export interface QueueCCTPTransferReturn { - transfer: CCTPTransfer +export interface QueueCCTPTransferResponse { + transfer: CCTPTransfer } -export interface QueueIntentConfigExecutionArgs { + +export interface QueueIntentConfigExecutionRequest { intentConfigId: number } -export interface QueueIntentConfigExecutionReturn { - status: boolean +export interface QueueIntentConfigExecutionResponse { + status: boolean } -export interface GetIntentConfigExecutionStatusArgs { + +export interface GetIntentConfigExecutionStatusRequest { intentConfigId: number } -export interface GetIntentConfigExecutionStatusReturn { - executionStatus: string +export interface GetIntentConfigExecutionStatusResponse { + executionStatus: string } -export interface ListIntentConfigsArgs { + +export interface ListIntentConfigsRequest { page?: Page executionStatus?: string } -export interface ListIntentConfigsReturn { +export interface ListIntentConfigsResponse { page: Page - intentConfigs: Array + intentConfigs: Array } -export interface QueueMetaTxnReceiptArgs { + +export interface QueueMetaTxnReceiptRequest { metaTxID: string } -export interface QueueMetaTxnReceiptReturn { - status: boolean +export interface QueueMetaTxnReceiptResponse { + status: boolean } - - // // Client // -export class API implements API { + +export class API implements APIClient { protected hostname: string protected fetch: Fetch protected path = '/rpc/API/' @@ -1977,1930 +2045,2351 @@ export class API implements API { private url(name: string): string { return this.hostname + this.path + name } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('Ping'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('Version'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - version: (_data.version), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('RuntimeStatus'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - clock = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('Clock'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - serverTime: (_data.serverTime), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getSequenceContext = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetSequenceContext'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - data: (_data.data), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getAuthToken = (args: GetAuthTokenArgs, headers?: object, signal?: AbortSignal): Promise => { + + queryKey = { + ping: () => ['API', 'ping'] as const, + version: () => ['API', 'version'] as const, + runtimeStatus: () => ['API', 'runtimeStatus'] as const, + clock: () => ['API', 'clock'] as const, + getSequenceContext: () => ['API', 'getSequenceContext'] as const, + getAuthToken: (req: GetAuthTokenRequest) => ['API', 'getAuthToken', req] as const, + getAuthToken2: (req: GetAuthToken2Request) => ['API', 'getAuthToken2', req] as const, + sendPasswordlessLink: (req: SendPasswordlessLinkRequest) => ['API', 'sendPasswordlessLink', req] as const, + registerPublicKey: (req: RegisterPublicKeyRequest) => ['API', 'registerPublicKey', req] as const, + getPublicKey: (req: GetPublicKeyRequest) => ['API', 'getPublicKey', req] as const, + friendList: (req: FriendListRequest) => ['API', 'friendList', req] as const, + getFriendByAddress: (req: GetFriendByAddressRequest) => ['API', 'getFriendByAddress', req] as const, + searchFriends: (req: SearchFriendsRequest) => ['API', 'searchFriends', req] as const, + addFriend: (req: AddFriendRequest) => ['API', 'addFriend', req] as const, + updateFriendNickname: (req: UpdateFriendNicknameRequest) => ['API', 'updateFriendNickname', req] as const, + removeFriend: (req: RemoveFriendRequest) => ['API', 'removeFriend', req] as const, + contractCall: (req: ContractCallRequest) => ['API', 'contractCall', req] as const, + decodeContractCall: (req: DecodeContractCallRequest) => ['API', 'decodeContractCall', req] as const, + lookupContractCallSelectors: (req: LookupContractCallSelectorsRequest) => + ['API', 'lookupContractCallSelectors', req] as const, + userStorageFetch: (req: UserStorageFetchRequest) => ['API', 'userStorageFetch', req] as const, + userStorageSave: (req: UserStorageSaveRequest) => ['API', 'userStorageSave', req] as const, + userStorageDelete: (req: UserStorageDeleteRequest) => ['API', 'userStorageDelete', req] as const, + userStorageFetchAll: (req: UserStorageFetchAllRequest) => ['API', 'userStorageFetchAll', req] as const, + getMoonpayLink: (req: GetMoonpayLinkRequest) => ['API', 'getMoonpayLink', req] as const, + resolveENSAddress: (req: ResolveENSAddressRequest) => ['API', 'resolveENSAddress', req] as const, + isValidSignature: (req: IsValidSignatureRequest) => ['API', 'isValidSignature', req] as const, + isValidMessageSignature: (req: IsValidMessageSignatureRequest) => ['API', 'isValidMessageSignature', req] as const, + isValidTypedDataSignature: (req: IsValidTypedDataSignatureRequest) => + ['API', 'isValidTypedDataSignature', req] as const, + isValidETHAuthProof: (req: IsValidETHAuthProofRequest) => ['API', 'isValidETHAuthProof', req] as const, + getOnRampURL: (req: GetOnRampURLRequest) => ['API', 'getOnRampURL', req] as const, + transakGetCountries: () => ['API', 'transakGetCountries'] as const, + transakGetCryptoCurrencies: () => ['API', 'transakGetCryptoCurrencies'] as const, + transakGetFiatCurrencies: () => ['API', 'transakGetFiatCurrencies'] as const, + transakGetPrice: (req: TransakGetPriceRequest) => ['API', 'transakGetPrice', req] as const, + transakGetSupportedNFTCheckoutChains: () => ['API', 'transakGetSupportedNFTCheckoutChains'] as const, + transakGetWidgetURL: (req: TransakGetWidgetURLRequest) => ['API', 'transakGetWidgetURL', req] as const, + getCoinPrices: (req: GetCoinPricesRequest) => ['API', 'getCoinPrices', req] as const, + getCollectiblePrices: (req: GetCollectiblePricesRequest) => ['API', 'getCollectiblePrices', req] as const, + getExchangeRate: (req: GetExchangeRateRequest) => ['API', 'getExchangeRate', req] as const, + memoryStore: (req: MemoryStoreRequest) => ['API', 'memoryStore', req] as const, + memoryLoad: (req: MemoryLoadRequest) => ['API', 'memoryLoad', req] as const, + getInviteInfo: () => ['API', 'getInviteInfo'] as const, + isValidAccessCode: (req: IsValidAccessCodeRequest) => ['API', 'isValidAccessCode', req] as const, + internalClaimAccessCode: (req: InternalClaimAccessCodeRequest) => ['API', 'internalClaimAccessCode', req] as const, + blockNumberAtTime: (req: BlockNumberAtTimeRequest) => ['API', 'blockNumberAtTime', req] as const, + paperSessionSecret: (req: PaperSessionSecretRequest) => ['API', 'paperSessionSecret', req] as const, + paperSessionSecret2: (req: PaperSessionSecret2Request) => ['API', 'paperSessionSecret2', req] as const, + linkWallet: (req: LinkWalletRequest) => ['API', 'linkWallet', req] as const, + getLinkedWallets: (req: GetLinkedWalletsRequest) => ['API', 'getLinkedWallets', req] as const, + removeLinkedWallet: (req: RemoveLinkedWalletRequest) => ['API', 'removeLinkedWallet', req] as const, + generateWaaSVerificationURL: (req: GenerateWaaSVerificationURLRequest) => + ['API', 'generateWaaSVerificationURL', req] as const, + validateWaaSVerificationNonce: (req: ValidateWaaSVerificationNonceRequest) => + ['API', 'validateWaaSVerificationNonce', req] as const, + listAdoptedWallets: (req: ListAdoptedWalletsRequest) => ['API', 'listAdoptedWallets', req] as const, + getLifiChains: () => ['API', 'getLifiChains'] as const, + getLifiTokens: (req: GetLifiTokensRequest) => ['API', 'getLifiTokens', req] as const, + getLifiSwapRoutes: (req: GetLifiSwapRoutesRequest) => ['API', 'getLifiSwapRoutes', req] as const, + getLifiSwapQuote: (req: GetLifiSwapQuoteRequest) => ['API', 'getLifiSwapQuote', req] as const, + getIntentCallsPayloads: (req: GetIntentCallsPayloadsRequest) => ['API', 'getIntentCallsPayloads', req] as const, + commitIntentConfig: (req: CommitIntentConfigRequest) => ['API', 'commitIntentConfig', req] as const, + getIntentConfig: (req: GetIntentConfigRequest) => ['API', 'getIntentConfig', req] as const, + listCurrencyGroups: () => ['API', 'listCurrencyGroups'] as const, + addOffchainInventory: (req: AddOffchainInventoryRequest) => ['API', 'addOffchainInventory', req] as const, + getOffchainInventory: (req: GetOffchainInventoryRequest) => ['API', 'getOffchainInventory', req] as const, + listOffchainInventories: (req: ListOffchainInventoriesRequest) => ['API', 'listOffchainInventories', req] as const, + updateOffchainInventory: (req: UpdateOffchainInventoryRequest) => ['API', 'updateOffchainInventory', req] as const, + deleteOffchainInventory: (req: DeleteOffchainInventoryRequest) => ['API', 'deleteOffchainInventory', req] as const, + requestOffchainPayment: (req: RequestOffchainPaymentRequest) => ['API', 'requestOffchainPayment', req] as const, + listOffchainPayments: (req: ListOffchainPaymentsRequest) => ['API', 'listOffchainPayments', req] as const, + savePack: (req: SavePackRequest) => ['API', 'savePack', req] as const, + getPack: (req: GetPackRequest) => ['API', 'getPack', req] as const, + getPackIds: (req: GetPackIdsRequest) => ['API', 'getPackIds', req] as const, + deletePack: (req: DeletePackRequest) => ['API', 'deletePack', req] as const, + updatePackContent: (req: UpdatePackContentRequest) => ['API', 'updatePackContent', req] as const, + getRevealTxData: (req: GetRevealTxDataRequest) => ['API', 'getRevealTxData', req] as const, + checkoutOptionsPrimary: (req: CheckoutOptionsPrimaryRequest) => ['API', 'checkoutOptionsPrimary', req] as const, + checkoutOptionsSecondary: (req: CheckoutOptionsSecondaryRequest) => + ['API', 'checkoutOptionsSecondary', req] as const, + checkoutOptionsGetTransakContractID: (req: CheckoutOptionsGetTransakContractIDRequest) => + ['API', 'checkoutOptionsGetTransakContractID', req] as const, + fortePayCreateIntent: (req: FortePayCreateIntentRequest) => ['API', 'fortePayCreateIntent', req] as const, + fortePayGetPaymentStatuses: (req: FortePayGetPaymentStatusesRequest) => + ['API', 'fortePayGetPaymentStatuses', req] as const, + getCCTPTransfer: (req: GetCCTPTransferRequest) => ['API', 'getCCTPTransfer', req] as const, + queueCCTPTransfer: (req: QueueCCTPTransferRequest) => ['API', 'queueCCTPTransfer', req] as const, + queueIntentConfigExecution: (req: QueueIntentConfigExecutionRequest) => + ['API', 'queueIntentConfigExecution', req] as const, + getIntentConfigExecutionStatus: (req: GetIntentConfigExecutionStatusRequest) => + ['API', 'getIntentConfigExecutionStatus', req] as const, + listIntentConfigs: (req: ListIntentConfigsRequest) => ['API', 'listIntentConfigs', req] as const, + queueMetaTxnReceipt: (req: QueueMetaTxnReceiptRequest) => ['API', 'queueMetaTxnReceipt', req] as const, + } + + ping = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PingResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + version = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'VersionResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'RuntimeStatusResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + clock = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Clock'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ClockResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getSequenceContext = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetSequenceContext'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetSequenceContextResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getAuthToken = (req: GetAuthTokenRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('GetAuthToken'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - jwtToken: (_data.jwtToken), - address: (_data.address), - user: (_data.user), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getAuthToken2 = (args: GetAuthToken2Args, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetAuthTokenRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetAuthTokenResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getAuthToken2 = ( + req: GetAuthToken2Request, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetAuthToken2'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - jwtToken: (_data.jwtToken), - address: (_data.address), - user: (_data.user), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sendPasswordlessLink = (args: SendPasswordlessLinkArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetAuthToken2Request'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetAuthToken2Response') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + sendPasswordlessLink = ( + req: SendPasswordlessLinkRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('SendPasswordlessLink'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - registerPublicKey = (args: RegisterPublicKeyArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'SendPasswordlessLinkRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'SendPasswordlessLinkResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + registerPublicKey = ( + req: RegisterPublicKeyRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('RegisterPublicKey'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getPublicKey = (args: GetPublicKeyArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'RegisterPublicKeyRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'RegisterPublicKeyResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getPublicKey = (req: GetPublicKeyRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('GetPublicKey'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - publicKey: (_data.publicKey), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - friendList = (args: FriendListArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetPublicKeyRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetPublicKeyResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + friendList = (req: FriendListRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('FriendList'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - friends: >(_data.friends), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getFriendByAddress = (args: GetFriendByAddressArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'FriendListRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'FriendListResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getFriendByAddress = ( + req: GetFriendByAddressRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetFriendByAddress'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - friend: (_data.friend), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - searchFriends = (args: SearchFriendsArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetFriendByAddressRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetFriendByAddressResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + searchFriends = ( + req: SearchFriendsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('SearchFriends'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - friends: >(_data.friends), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - addFriend = (args: AddFriendArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'SearchFriendsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'SearchFriendsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + addFriend = (req: AddFriendRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('AddFriend'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - friend: (_data.friend), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - updateFriendNickname = (args: UpdateFriendNicknameArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'AddFriendRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'AddFriendResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + updateFriendNickname = ( + req: UpdateFriendNicknameRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('UpdateFriendNickname'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - friend: (_data.friend), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - removeFriend = (args: RemoveFriendArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'UpdateFriendNicknameRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UpdateFriendNicknameResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + removeFriend = (req: RemoveFriendRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('RemoveFriend'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - contractCall = (args: ContractCallArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'RemoveFriendRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'RemoveFriendResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + contractCall = (req: ContractCallRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('ContractCall'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - returns: >(_data.returns), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - decodeContractCall = (args: DecodeContractCallArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'ContractCallRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ContractCallResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + decodeContractCall = ( + req: DecodeContractCallRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('DecodeContractCall'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - call: (_data.call), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - lookupContractCallSelectors = (args: LookupContractCallSelectorsArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'DecodeContractCallRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DecodeContractCallResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + lookupContractCallSelectors = ( + req: LookupContractCallSelectorsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('LookupContractCallSelectors'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - signatures: >>(_data.signatures), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - userStorageFetch = (args: UserStorageFetchArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'LookupContractCallSelectorsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'LookupContractCallSelectorsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + userStorageFetch = ( + req: UserStorageFetchRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('UserStorageFetch'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - object: (_data.object), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - userStorageSave = (args: UserStorageSaveArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'UserStorageFetchRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UserStorageFetchResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + userStorageSave = ( + req: UserStorageSaveRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('UserStorageSave'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - ok: (_data.ok), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - userStorageDelete = (args: UserStorageDeleteArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'UserStorageSaveRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UserStorageSaveResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + userStorageDelete = ( + req: UserStorageDeleteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('UserStorageDelete'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - ok: (_data.ok), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - userStorageFetchAll = (args: UserStorageFetchAllArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'UserStorageDeleteRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UserStorageDeleteResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + userStorageFetchAll = ( + req: UserStorageFetchAllRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('UserStorageFetchAll'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - objects: <{[key: string]: any}>(_data.objects), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getMoonpayLink = (args: GetMoonpayLinkArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'UserStorageFetchAllRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UserStorageFetchAllResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getMoonpayLink = ( + req: GetMoonpayLinkRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetMoonpayLink'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - signedUrl: (_data.signedUrl), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - resolveENSAddress = (args: ResolveENSAddressArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetMoonpayLinkRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetMoonpayLinkResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + resolveENSAddress = ( + req: ResolveENSAddressRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('ResolveENSAddress'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - address: (_data.address), - ok: (_data.ok), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - isValidSignature = (args: IsValidSignatureArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'ResolveENSAddressRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ResolveENSAddressResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + isValidSignature = ( + req: IsValidSignatureRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('IsValidSignature'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - isValid: (_data.isValid), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - isValidMessageSignature = (args: IsValidMessageSignatureArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'IsValidSignatureRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'IsValidSignatureResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + isValidMessageSignature = ( + req: IsValidMessageSignatureRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('IsValidMessageSignature'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - isValid: (_data.isValid), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - isValidTypedDataSignature = (args: IsValidTypedDataSignatureArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'IsValidMessageSignatureRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'IsValidMessageSignatureResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + isValidTypedDataSignature = ( + req: IsValidTypedDataSignatureRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('IsValidTypedDataSignature'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - isValid: (_data.isValid), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - isValidETHAuthProof = (args: IsValidETHAuthProofArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'IsValidTypedDataSignatureRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'IsValidTypedDataSignatureResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + isValidETHAuthProof = ( + req: IsValidETHAuthProofRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('IsValidETHAuthProof'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - isValid: (_data.isValid), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getOnRampURL = (args: GetOnRampURLArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'IsValidETHAuthProofRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'IsValidETHAuthProofResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getOnRampURL = (req: GetOnRampURLRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('GetOnRampURL'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - url: (_data.url), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetClientToken = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetClientToken'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - token: (_data.token), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetNFTCheckoutToken = (args: SardineGetNFTCheckoutTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetNFTCheckoutToken'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - resp: (_data.resp), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetNFTCheckoutOrderStatus = (args: SardineGetNFTCheckoutOrderStatusArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetNFTCheckoutOrderStatus'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - resp: (_data.resp), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetSupportedRegions = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetSupportedRegions'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - regions: >(_data.regions), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetSupportedFiatCurrencies = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetSupportedFiatCurrencies'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - tokens: >(_data.tokens), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetSupportedTokens = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetSupportedTokens'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - tokens: >(_data.tokens), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetSupportedTokenForSwap = (args: SardineGetSupportedTokenForSwapArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetSupportedTokenForSwap'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - token: (_data.token), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetEnabledTokens = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetEnabledTokens'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - tokens: >(_data.tokens), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sardineGetQuote = (args: SardineGetQuoteArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SardineGetQuote'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - quote: (_data.quote), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getSardineClientToken = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetSardineClientToken'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - token: (_data.token), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getSardineNFTCheckoutToken = (args: GetSardineNFTCheckoutTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetSardineNFTCheckoutToken'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - resp: (_data.resp), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getSardineNFTCheckoutOrderStatus = (args: GetSardineNFTCheckoutOrderStatusArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetSardineNFTCheckoutOrderStatus'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - resp: (_data.resp), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - transakGetCountries = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('TransakGetCountries'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - regions: >(_data.regions), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - transakGetCryptoCurrencies = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('TransakGetCryptoCurrencies'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - currencies: >(_data.currencies), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - transakGetFiatCurrencies = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('TransakGetFiatCurrencies'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - currencies: >(_data.currencies), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - transakGetPrice = (args: TransakGetPriceArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetOnRampURLRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetOnRampURLResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + transakGetCountries = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('TransakGetCountries'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'TransakGetCountriesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + transakGetCryptoCurrencies = ( + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('TransakGetCryptoCurrencies'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'TransakGetCryptoCurrenciesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + transakGetFiatCurrencies = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('TransakGetFiatCurrencies'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'TransakGetFiatCurrenciesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + transakGetPrice = ( + req: TransakGetPriceRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('TransakGetPrice'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - price: (_data.price), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - transakGetSupportedNFTCheckoutChains = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('TransakGetSupportedNFTCheckoutChains'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - chains: >(_data.chains), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getCoinPrices = (args: GetCoinPricesArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'TransakGetPriceRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'TransakGetPriceResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + transakGetSupportedNFTCheckoutChains = ( + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('TransakGetSupportedNFTCheckoutChains'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode( + _data, + 'TransakGetSupportedNFTCheckoutChainsResponse', + ) + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + transakGetWidgetURL = ( + req: TransakGetWidgetURLRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('TransakGetWidgetURL'), + createHttpRequest(JsonEncode(req, 'TransakGetWidgetURLRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'TransakGetWidgetURLResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getCoinPrices = ( + req: GetCoinPricesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetCoinPrices'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - tokenPrices: >(_data.tokenPrices), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getCollectiblePrices = (args: GetCollectiblePricesArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetCoinPricesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetCoinPricesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getCollectiblePrices = ( + req: GetCollectiblePricesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetCollectiblePrices'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - tokenPrices: >(_data.tokenPrices), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getExchangeRate = (args: GetExchangeRateArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetCollectiblePricesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetCollectiblePricesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getExchangeRate = ( + req: GetExchangeRateRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetExchangeRate'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - exchangeRate: (_data.exchangeRate), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - memoryStore = (args: MemoryStoreArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetExchangeRateRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetExchangeRateResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + memoryStore = (req: MemoryStoreRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('MemoryStore'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - ok: (_data.ok), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - memoryLoad = (args: MemoryLoadArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'MemoryStoreRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'MemoryStoreResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + memoryLoad = (req: MemoryLoadRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('MemoryLoad'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - value: (_data.value), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getInviteInfo = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetInviteInfo'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - inviteInfo: (_data.inviteInfo), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - isValidAccessCode = (args: IsValidAccessCodeArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'MemoryLoadRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'MemoryLoadResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getInviteInfo = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetInviteInfo'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetInviteInfoResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + isValidAccessCode = ( + req: IsValidAccessCodeRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('IsValidAccessCode'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - internalClaimAccessCode = (args: InternalClaimAccessCodeArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'IsValidAccessCodeRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'IsValidAccessCodeResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + internalClaimAccessCode = ( + req: InternalClaimAccessCodeRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('InternalClaimAccessCode'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - blockNumberAtTime = (args: BlockNumberAtTimeArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'InternalClaimAccessCodeRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'InternalClaimAccessCodeResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + blockNumberAtTime = ( + req: BlockNumberAtTimeRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('BlockNumberAtTime'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - blocks: >(_data.blocks), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - paperSessionSecret = (args: PaperSessionSecretArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'BlockNumberAtTimeRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'BlockNumberAtTimeResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + paperSessionSecret = ( + req: PaperSessionSecretRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('PaperSessionSecret'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - secret: (_data.secret), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - paperSessionSecret2 = (args: PaperSessionSecret2Args, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'PaperSessionSecretRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PaperSessionSecretResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + paperSessionSecret2 = ( + req: PaperSessionSecret2Request, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('PaperSessionSecret2'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - secret: (_data.secret), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - linkWallet = (args: LinkWalletArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'PaperSessionSecret2Request'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PaperSessionSecret2Response') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + linkWallet = (req: LinkWalletRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('LinkWallet'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getLinkedWallets = (args: GetLinkedWalletsArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'LinkWalletRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'LinkWalletResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getLinkedWallets = ( + req: GetLinkedWalletsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetLinkedWallets'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - linkedWallets: >(_data.linkedWallets), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - removeLinkedWallet = (args: RemoveLinkedWalletArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetLinkedWalletsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetLinkedWalletsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + removeLinkedWallet = ( + req: RemoveLinkedWalletRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('RemoveLinkedWallet'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - generateWaaSVerificationURL = (args: GenerateWaaSVerificationURLArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'RemoveLinkedWalletRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'RemoveLinkedWalletResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + generateWaaSVerificationURL = ( + req: GenerateWaaSVerificationURLRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GenerateWaaSVerificationURL'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - nonce: (_data.nonce), - verificationURL: (_data.verificationURL), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - validateWaaSVerificationNonce = (args: ValidateWaaSVerificationNonceArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GenerateWaaSVerificationURLRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GenerateWaaSVerificationURLResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + validateWaaSVerificationNonce = ( + req: ValidateWaaSVerificationNonceRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('ValidateWaaSVerificationNonce'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - walletAddress: (_data.walletAddress), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - listAdoptedWallets = (args: ListAdoptedWalletsArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'ValidateWaaSVerificationNonceRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ValidateWaaSVerificationNonceResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listAdoptedWallets = ( + req: ListAdoptedWalletsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('ListAdoptedWallets'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - wallets: >(_data.wallets), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getLifiChains = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetLifiChains'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - chains: >(_data.chains), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getLifiTokens = (args: GetLifiTokensArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'ListAdoptedWalletsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListAdoptedWalletsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getLifiChains = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetLifiChains'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetLifiChainsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getLifiTokens = ( + req: GetLifiTokensRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetLifiTokens'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - tokens: >(_data.tokens), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getLifiSwapRoutes = (args: GetLifiSwapRoutesArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetLifiTokensRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetLifiTokensResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getLifiSwapRoutes = ( + req: GetLifiSwapRoutesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetLifiSwapRoutes'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - routes: >(_data.routes), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getLifiSwapQuote = (args: GetLifiSwapQuoteArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetLifiSwapRoutesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetLifiSwapRoutesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getLifiSwapQuote = ( + req: GetLifiSwapQuoteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetLifiSwapQuote'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - quote: (_data.quote), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getIntentCallsPayloads = (args: GetIntentCallsPayloadsArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetLifiSwapQuoteRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetLifiSwapQuoteResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getIntentCallsPayloads = ( + req: GetIntentCallsPayloadsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetIntentCallsPayloads'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - calls: >(_data.calls), - preconditions: >(_data.preconditions), - metaTxns: >(_data.metaTxns), - trailsFee: (_data.trailsFee), - quote: (_data.quote), - feeQuotes: <{[key: string]: string}>(_data.feeQuotes), - originIntentAddress: (_data.originIntentAddress), - destinationIntentAddress: (_data.destinationIntentAddress), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - commitIntentConfig = (args: CommitIntentConfigArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetIntentCallsPayloadsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetIntentCallsPayloadsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + commitIntentConfig = ( + req: CommitIntentConfigRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('CommitIntentConfig'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - config: (_data.config), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getIntentConfig = (args: GetIntentConfigArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'CommitIntentConfigRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'CommitIntentConfigResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getIntentConfig = ( + req: GetIntentConfigRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetIntentConfig'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - config: (_data.config), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - listCurrencyGroups = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('ListCurrencyGroups'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - currencyGroups: >(_data.currencyGroups), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - addOffchainInventory = (args: AddOffchainInventoryArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetIntentConfigRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetIntentConfigResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listCurrencyGroups = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('ListCurrencyGroups'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListCurrencyGroupsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + addOffchainInventory = ( + req: AddOffchainInventoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('AddOffchainInventory'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - inventoryId: (_data.inventoryId), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getOffchainInventory = (args: GetOffchainInventoryArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'AddOffchainInventoryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'AddOffchainInventoryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getOffchainInventory = ( + req: GetOffchainInventoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetOffchainInventory'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - inventory: (_data.inventory), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - listOffchainInventories = (args: ListOffchainInventoriesArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetOffchainInventoryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetOffchainInventoryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listOffchainInventories = ( + req: ListOffchainInventoriesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('ListOffchainInventories'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - inventory: >(_data.inventory), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - updateOffchainInventory = (args: UpdateOffchainInventoryArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'ListOffchainInventoriesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListOffchainInventoriesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + updateOffchainInventory = ( + req: UpdateOffchainInventoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('UpdateOffchainInventory'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return {} - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - deleteOffchainInventory = (args: DeleteOffchainInventoryArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'UpdateOffchainInventoryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UpdateOffchainInventoryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deleteOffchainInventory = ( + req: DeleteOffchainInventoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('DeleteOffchainInventory'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - ok: (_data.ok), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - requestOffchainPayment = (args: RequestOffchainPaymentArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'DeleteOffchainInventoryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeleteOffchainInventoryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + requestOffchainPayment = ( + req: RequestOffchainPaymentRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('RequestOffchainPayment'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - payment: (_data.payment), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - listOffchainPayments = (args: ListOffchainPaymentsArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'RequestOffchainPaymentRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'RequestOffchainPaymentResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listOffchainPayments = ( + req: ListOffchainPaymentsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('ListOffchainPayments'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - payments: >(_data.payments), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - savePack = (args: SavePackArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'ListOffchainPaymentsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListOffchainPaymentsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + savePack = (req: SavePackRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('SavePack'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - merkleRoot: (_data.merkleRoot), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getPack = (args: GetPackArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetPack'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - pack: (_data.pack), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getPackIds = (args: GetPackIdsArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'SavePackRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'SavePackResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getPack = (req: GetPackRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetPack'), createHttpRequest(JsonEncode(req, 'GetPackRequest'), headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetPackResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getPackIds = (req: GetPackIdsRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('GetPackIds'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - packIds: >(_data.packIds), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - deletePack = (args: DeletePackArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetPackIdsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetPackIdsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deletePack = (req: DeletePackRequest, headers?: object, signal?: AbortSignal): Promise => { return this.fetch( this.url('DeletePack'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - updatePackContent = (args: UpdatePackContentArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'DeletePackRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeletePackResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + updatePackContent = ( + req: UpdatePackContentRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('UpdatePackContent'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - merkleRoot: (_data.merkleRoot), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getRevealTxData = (args: GetRevealTxDataArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'UpdatePackContentRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UpdatePackContentResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getRevealTxData = ( + req: GetRevealTxDataRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetRevealTxData'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - txData: (_data.txData), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - checkoutOptionsPrimary = (args: CheckoutOptionsPrimaryArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetRevealTxDataRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetRevealTxDataResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + checkoutOptionsPrimary = ( + req: CheckoutOptionsPrimaryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('CheckoutOptionsPrimary'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - options: (_data.options), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - checkoutOptionsSecondary = (args: CheckoutOptionsSecondaryArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'CheckoutOptionsPrimaryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'CheckoutOptionsPrimaryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + checkoutOptionsSecondary = ( + req: CheckoutOptionsSecondaryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('CheckoutOptionsSecondary'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - options: (_data.options), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - checkoutOptionsGetTransakContractID = (args: CheckoutOptionsGetTransakContractIDArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'CheckoutOptionsSecondaryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'CheckoutOptionsSecondaryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + checkoutOptionsGetTransakContractID = ( + req: CheckoutOptionsGetTransakContractIDRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('CheckoutOptionsGetTransakContractID'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - contractId: (_data.contractId), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - fortePayCreateIntent = (args: FortePayCreateIntentArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'CheckoutOptionsGetTransakContractIDRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode( + _data, + 'CheckoutOptionsGetTransakContractIDResponse', + ) + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + fortePayCreateIntent = ( + req: FortePayCreateIntentRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('FortePayCreateIntent'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - resp: (_data.resp), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - fortePayGetPaymentStatuses = (args: FortePayGetPaymentStatusesArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'FortePayCreateIntentRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'FortePayCreateIntentResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + fortePayGetPaymentStatuses = ( + req: FortePayGetPaymentStatusesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('FortePayGetPaymentStatuses'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - statuses: >(_data.statuses), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getCCTPTransfer = (args: GetCCTPTransferArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'FortePayGetPaymentStatusesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'FortePayGetPaymentStatusesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getCCTPTransfer = ( + req: GetCCTPTransferRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetCCTPTransfer'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - transfer: (_data.transfer), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - queueCCTPTransfer = (args: QueueCCTPTransferArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetCCTPTransferRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetCCTPTransferResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + queueCCTPTransfer = ( + req: QueueCCTPTransferRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('QueueCCTPTransfer'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - transfer: (_data.transfer), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - queueIntentConfigExecution = (args: QueueIntentConfigExecutionArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'QueueCCTPTransferRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'QueueCCTPTransferResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + queueIntentConfigExecution = ( + req: QueueIntentConfigExecutionRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('QueueIntentConfigExecution'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getIntentConfigExecutionStatus = (args: GetIntentConfigExecutionStatusArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'QueueIntentConfigExecutionRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'QueueIntentConfigExecutionResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getIntentConfigExecutionStatus = ( + req: GetIntentConfigExecutionStatusRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('GetIntentConfigExecutionStatus'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - executionStatus: (_data.executionStatus), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - listIntentConfigs = (args: ListIntentConfigsArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'GetIntentConfigExecutionStatusRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetIntentConfigExecutionStatusResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listIntentConfigs = ( + req: ListIntentConfigsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('ListIntentConfigs'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - intentConfigs: >(_data.intentConfigs), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - queueMetaTxnReceipt = (args: QueueMetaTxnReceiptArgs, headers?: object, signal?: AbortSignal): Promise => { + createHttpRequest(JsonEncode(req, 'ListIntentConfigsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListIntentConfigsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + queueMetaTxnReceipt = ( + req: QueueMetaTxnReceiptRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { return this.fetch( this.url('QueueMetaTxnReceipt'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) + createHttpRequest(JsonEncode(req, 'QueueMetaTxnReceiptRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'QueueMetaTxnReceiptResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) } - } - const createHTTPRequest = (body: object = {}, headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: {[key: string]: string} = { ...headers, 'Content-Type': 'application/json' } - reqHeaders[WebrpcHeader] = WebrpcHeaderValue - - return { - method: 'POST', - headers: reqHeaders, - body: JSON.stringify(body || {}), - signal +const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { + const reqHeaders: { [key: string]: string } = { + ...headers, + 'Content-Type': 'application/json', + [WebrpcHeader]: WebrpcHeaderValue, } + return { method: 'POST', headers: reqHeaders, body, signal } } const buildResponse = (res: Response): Promise => { - return res.text().then(text => { + return res.text().then((text) => { let data try { data = JSON.parse(text) - } catch(error) { - let message = '' - if (error instanceof Error) { - message = error.message - } + } catch (error) { throw WebrpcBadResponseError.new({ status: res.status, - cause: `JSON.parse(): ${message}: response text: ${text}`}, - ) + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, + }) } if (!res.ok) { - const code: number = (typeof data.code === 'number') ? data.code : 0 + const code: number = typeof data.code === 'number' ? data.code : 0 throw (webrpcErrorByCode[code] || WebrpcError).new(data) } return data }) } +export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise + +export const JsonEncode = (obj: T, _typ: string = ''): string => { + return JSON.stringify(obj) +} + +export const JsonDecode = (data: string | any, _typ: string = ''): T => { + let parsed: any = data + if (typeof data === 'string') { + try { + parsed = JSON.parse(data) + } catch (err) { + throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) + } + } + return parsed as T +} + // // Errors // +type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } + export class WebrpcError extends Error { - name: string code: number - message: string status: number - cause?: string - - /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ - msg: string - - constructor(name: string, code: number, message: string, status: number, cause?: string) { - super(message) - this.name = name || 'WebrpcError' - this.code = typeof code === 'number' ? code : 0 - this.message = message || `endpoint error ${this.code}` - this.msg = this.message - this.status = typeof status === 'number' ? status : 0 - this.cause = cause + + constructor(error: WebrpcErrorParams = {}) { + super(error.message) + this.name = error.name || 'WebrpcEndpointError' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcError.prototype) } static new(payload: any): WebrpcError { - return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) + return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) } } -// Webrpc errors - export class WebrpcEndpointError extends WebrpcError { - constructor( - name: string = 'WebrpcEndpoint', - code: number = 0, - message: string = `endpoint error`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcEndpoint' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcEndpointError.prototype) } } export class WebrpcRequestFailedError extends WebrpcError { - constructor( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = `request failed`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcRequestFailed' + this.code = typeof error.code === 'number' ? error.code : -1 + this.message = error.message || `request failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) } } export class WebrpcBadRouteError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = `bad route`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRoute' + this.code = typeof error.code === 'number' ? error.code : -2 + this.message = error.message || `bad route` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) } } export class WebrpcBadMethodError extends WebrpcError { - constructor( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = `bad method`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadMethod' + this.code = typeof error.code === 'number' ? error.code : -3 + this.message = error.message || `bad method` + this.status = typeof error.status === 'number' ? error.status : 405 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) } } export class WebrpcBadRequestError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = `bad request`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRequest' + this.code = typeof error.code === 'number' ? error.code : -4 + this.message = error.message || `bad request` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) } } export class WebrpcBadResponseError extends WebrpcError { - constructor( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = `bad response`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadResponse' + this.code = typeof error.code === 'number' ? error.code : -5 + this.message = error.message || `bad response` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) } } export class WebrpcServerPanicError extends WebrpcError { - constructor( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = `server panic`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcServerPanic' + this.code = typeof error.code === 'number' ? error.code : -6 + this.message = error.message || `server panic` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) } } export class WebrpcInternalErrorError extends WebrpcError { - constructor( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = `internal error`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcInternalError' + this.code = typeof error.code === 'number' ? error.code : -7 + this.message = error.message || `internal error` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) } } -export class WebrpcClientDisconnectedError extends WebrpcError { - constructor( - name: string = 'WebrpcClientDisconnected', - code: number = -8, - message: string = `client disconnected`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype) +export class WebrpcClientAbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcClientAborted' + this.code = typeof error.code === 'number' ? error.code : -8 + this.message = error.message || `request aborted by client` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) } } export class WebrpcStreamLostError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = `stream lost`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamLost' + this.code = typeof error.code === 'number' ? error.code : -9 + this.message = error.message || `stream lost` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) } } export class WebrpcStreamFinishedError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = `stream finished`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamFinished' + this.code = typeof error.code === 'number' ? error.code : -10 + this.message = error.message || `stream finished` + this.status = typeof error.status === 'number' ? error.status : 200 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) } } - +// // Schema errors +// export class UnauthorizedError extends WebrpcError { - constructor( - name: string = 'Unauthorized', - code: number = 1000, - message: string = `Unauthorized access`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unauthorized' + this.code = typeof error.code === 'number' ? error.code : 1000 + this.message = error.message || `Unauthorized access` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, UnauthorizedError.prototype) } } export class PermissionDeniedError extends WebrpcError { - constructor( - name: string = 'PermissionDenied', - code: number = 1001, - message: string = `Permission denied`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'PermissionDenied' + this.code = typeof error.code === 'number' ? error.code : 1001 + this.message = error.message || `Permission denied` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, PermissionDeniedError.prototype) } } export class SessionExpiredError extends WebrpcError { - constructor( - name: string = 'SessionExpired', - code: number = 1002, - message: string = `Session expired`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'SessionExpired' + this.code = typeof error.code === 'number' ? error.code : 1002 + this.message = error.message || `Session expired` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, SessionExpiredError.prototype) } } export class MethodNotFoundError extends WebrpcError { - constructor( - name: string = 'MethodNotFound', - code: number = 1003, - message: string = `Method not found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MethodNotFound' + this.code = typeof error.code === 'number' ? error.code : 1003 + this.message = error.message || `Method not found` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, MethodNotFoundError.prototype) } } export class RequestConflictError extends WebrpcError { - constructor( - name: string = 'RequestConflict', - code: number = 1004, - message: string = `Conflict with target resource`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RequestConflict' + this.code = typeof error.code === 'number' ? error.code : 1004 + this.message = error.message || `Conflict with target resource` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, RequestConflictError.prototype) } } export class AbortedError extends WebrpcError { - constructor( - name: string = 'Aborted', - code: number = 1005, - message: string = `Request aborted`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Aborted' + this.code = typeof error.code === 'number' ? error.code : 1005 + this.message = error.message || `Request aborted` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, AbortedError.prototype) } } export class GeoblockedError extends WebrpcError { - constructor( - name: string = 'Geoblocked', - code: number = 1006, - message: string = `Geoblocked region`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Geoblocked' + this.code = typeof error.code === 'number' ? error.code : 1006 + this.message = error.message || `Geoblocked region` + this.status = typeof error.status === 'number' ? error.status : 451 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, GeoblockedError.prototype) } } export class RateLimitedError extends WebrpcError { - constructor( - name: string = 'RateLimited', - code: number = 1007, - message: string = `Rate-limited. Please slow down.`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RateLimited' + this.code = typeof error.code === 'number' ? error.code : 1007 + this.message = error.message || `Rate-limited. Please slow down.` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, RateLimitedError.prototype) } } export class ProjectNotFoundError extends WebrpcError { - constructor( - name: string = 'ProjectNotFound', - code: number = 1008, - message: string = `Project not found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ProjectNotFound' + this.code = typeof error.code === 'number' ? error.code : 1008 + this.message = error.message || `Project not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, ProjectNotFoundError.prototype) } } export class AccessKeyNotFoundError extends WebrpcError { - constructor( - name: string = 'AccessKeyNotFound', - code: number = 1101, - message: string = `Access key not found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AccessKeyNotFound' + this.code = typeof error.code === 'number' ? error.code : 1101 + this.message = error.message || `Access key not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) } } export class AccessKeyMismatchError extends WebrpcError { - constructor( - name: string = 'AccessKeyMismatch', - code: number = 1102, - message: string = `Access key mismatch`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AccessKeyMismatch' + this.code = typeof error.code === 'number' ? error.code : 1102 + this.message = error.message || `Access key mismatch` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) } } export class InvalidOriginError extends WebrpcError { - constructor( - name: string = 'InvalidOrigin', - code: number = 1103, - message: string = `Invalid origin for Access Key`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidOrigin' + this.code = typeof error.code === 'number' ? error.code : 1103 + this.message = error.message || `Invalid origin for Access Key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, InvalidOriginError.prototype) } } export class InvalidServiceError extends WebrpcError { - constructor( - name: string = 'InvalidService', - code: number = 1104, - message: string = `Service not enabled for Access key`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidService' + this.code = typeof error.code === 'number' ? error.code : 1104 + this.message = error.message || `Service not enabled for Access key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, InvalidServiceError.prototype) } } export class UnauthorizedUserError extends WebrpcError { - constructor( - name: string = 'UnauthorizedUser', - code: number = 1105, - message: string = `Unauthorized user`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'UnauthorizedUser' + this.code = typeof error.code === 'number' ? error.code : 1105 + this.message = error.message || `Unauthorized user` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, UnauthorizedUserError.prototype) } } export class QuotaExceededError extends WebrpcError { - constructor( - name: string = 'QuotaExceeded', - code: number = 1200, - message: string = `Quota request exceeded`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QuotaExceeded' + this.code = typeof error.code === 'number' ? error.code : 1200 + this.message = error.message || `Quota request exceeded` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, QuotaExceededError.prototype) } } export class QuotaRateLimitError extends WebrpcError { - constructor( - name: string = 'QuotaRateLimit', - code: number = 1201, - message: string = `Quota rate limit exceeded`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QuotaRateLimit' + this.code = typeof error.code === 'number' ? error.code : 1201 + this.message = error.message || `Quota rate limit exceeded` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, QuotaRateLimitError.prototype) } } export class NoDefaultKeyError extends WebrpcError { - constructor( - name: string = 'NoDefaultKey', - code: number = 1300, - message: string = `No default access key found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NoDefaultKey' + this.code = typeof error.code === 'number' ? error.code : 1300 + this.message = error.message || `No default access key found` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, NoDefaultKeyError.prototype) } } export class MaxAccessKeysError extends WebrpcError { - constructor( - name: string = 'MaxAccessKeys', - code: number = 1301, - message: string = `Access keys limit reached`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MaxAccessKeys' + this.code = typeof error.code === 'number' ? error.code : 1301 + this.message = error.message || `Access keys limit reached` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, MaxAccessKeysError.prototype) } } export class AtLeastOneKeyError extends WebrpcError { - constructor( - name: string = 'AtLeastOneKey', - code: number = 1302, - message: string = `You need at least one Access Key`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AtLeastOneKey' + this.code = typeof error.code === 'number' ? error.code : 1302 + this.message = error.message || `You need at least one Access Key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) } } export class TimeoutError extends WebrpcError { - constructor( - name: string = 'Timeout', - code: number = 1900, - message: string = `Request timed out`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Timeout' + this.code = typeof error.code === 'number' ? error.code : 1900 + this.message = error.message || `Request timed out` + this.status = typeof error.status === 'number' ? error.status : 408 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, TimeoutError.prototype) } } export class InvalidArgumentError extends WebrpcError { - constructor( - name: string = 'InvalidArgument', - code: number = 2000, - message: string = `Invalid argument`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidArgument' + this.code = typeof error.code === 'number' ? error.code : 2000 + this.message = error.message || `Invalid argument` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, InvalidArgumentError.prototype) } } export class UnavailableError extends WebrpcError { - constructor( - name: string = 'Unavailable', - code: number = 2002, - message: string = `Unavailable resource`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unavailable' + this.code = typeof error.code === 'number' ? error.code : 2002 + this.message = error.message || `Unavailable resource` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, UnavailableError.prototype) } } export class QueryFailedError extends WebrpcError { - constructor( - name: string = 'QueryFailed', - code: number = 2003, - message: string = `Query failed`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QueryFailed' + this.code = typeof error.code === 'number' ? error.code : 2003 + this.message = error.message || `Query failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, QueryFailedError.prototype) } } export class NotFoundError extends WebrpcError { - constructor( - name: string = 'NotFound', - code: number = 3000, - message: string = `Resource not found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NotFound' + this.code = typeof error.code === 'number' ? error.code : 3000 + this.message = error.message || `Resource not found` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, NotFoundError.prototype) } } export class UnsupportedNetworkError extends WebrpcError { - constructor( - name: string = 'UnsupportedNetwork', - code: number = 3008, - message: string = `Unsupported network`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'UnsupportedNetwork' + this.code = typeof error.code === 'number' ? error.code : 3008 + this.message = error.message || `Unsupported network` + this.status = typeof error.status === 'number' ? error.status : 422 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, UnsupportedNetworkError.prototype) } } - export enum errors { WebrpcEndpoint = 'WebrpcEndpoint', WebrpcRequestFailed = 'WebrpcRequestFailed', @@ -3910,7 +4399,7 @@ export enum errors { WebrpcBadResponse = 'WebrpcBadResponse', WebrpcServerPanic = 'WebrpcServerPanic', WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', + WebrpcClientAborted = 'WebrpcClientAborted', WebrpcStreamLost = 'WebrpcStreamLost', WebrpcStreamFinished = 'WebrpcStreamFinished', Unauthorized = 'Unauthorized', @@ -3949,7 +4438,7 @@ export enum WebrpcErrorCodes { WebrpcBadResponse = -5, WebrpcServerPanic = -6, WebrpcInternalError = -7, - WebrpcClientDisconnected = -8, + WebrpcClientAborted = -8, WebrpcStreamLost = -9, WebrpcStreamFinished = -10, Unauthorized = 1000, @@ -3988,7 +4477,7 @@ export const webrpcErrorByCode: { [code: number]: any } = { [-5]: WebrpcBadResponseError, [-6]: WebrpcServerPanicError, [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, + [-8]: WebrpcClientAbortedError, [-9]: WebrpcStreamLostError, [-10]: WebrpcStreamFinishedError, [1000]: UnauthorizedError, @@ -4018,5 +4507,58 @@ export const webrpcErrorByCode: { [code: number]: any } = { [3008]: UnsupportedNetworkError, } -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise +// +// Webrpc +// + +export const WebrpcHeader = 'Webrpc' + +export const WebrpcHeaderValue = 'webrpc@v0.31.0;gen-typescript@v0.22.5;sequence-api@v0.4.0' + +type WebrpcGenVersions = { + WebrpcGenVersion: string + codeGenName: string + codeGenVersion: string + schemaName: string + schemaVersion: string +} + +export function VersionFromHeader(headers: Headers): WebrpcGenVersions { + const headerValue = headers.get(WebrpcHeader) + if (!headerValue) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + } + } + + return parseWebrpcGenVersions(headerValue) +} +function parseWebrpcGenVersions(header: string): WebrpcGenVersions { + const versions = header.split(';') + if (versions.length < 3) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + } + } + + const [_, WebrpcGenVersion] = versions[0]!.split('@') + const [codeGenName, codeGenVersion] = versions[1]!.split('@') + const [schemaName, schemaVersion] = versions[2]!.split('@') + + return { + WebrpcGenVersion: WebrpcGenVersion ?? '', + codeGenName: codeGenName ?? '', + codeGenVersion: codeGenVersion ?? '', + schemaName: schemaName ?? '', + schemaVersion: schemaVersion ?? '', + } +} diff --git a/packages/services/builder/CHANGELOG.md b/packages/services/builder/CHANGELOG.md index 0a86b4aa5c..2877d5538e 100644 --- a/packages/services/builder/CHANGELOG.md +++ b/packages/services/builder/CHANGELOG.md @@ -1,5 +1,41 @@ # @0xsequence/builder +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 + ## 2.3.8 ### Patch Changes diff --git a/packages/services/builder/package.json b/packages/services/builder/package.json index e5668967a9..de33fd7307 100644 --- a/packages/services/builder/package.json +++ b/packages/services/builder/package.json @@ -1,8 +1,8 @@ { "name": "@0xsequence/builder", - "version": "3.0.0", + "version": "3.0.0-beta.6", "description": "builder sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/builder", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/builder", "author": "Sequence Platforms Inc.", "license": "Apache-2.0", "publishConfig": { @@ -16,13 +16,13 @@ }, "exports": { ".": { - "types": "./src/index.ts", + "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3" + "@types/node": "^25.0.2", + "typescript": "^5.9.3" } } diff --git a/packages/services/guard/CHANGELOG.md b/packages/services/guard/CHANGELOG.md index 63fd273bc7..7adaddd55c 100644 --- a/packages/services/guard/CHANGELOG.md +++ b/packages/services/guard/CHANGELOG.md @@ -1,5 +1,41 @@ # @0xsequence/guard +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 + ## 2.3.8 ### Patch Changes @@ -2302,7 +2338,6 @@ - relayer: fix Relayer.wait() interface The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - delay: the polling interval, i.e. the time to wait between requests - maxFails: the maximum number of hard failures to tolerate before giving up diff --git a/packages/services/guard/package.json b/packages/services/guard/package.json index a7ec3a3d49..5c93bdfc15 100644 --- a/packages/services/guard/package.json +++ b/packages/services/guard/package.json @@ -1,8 +1,8 @@ { "name": "@0xsequence/guard", - "version": "3.0.0", + "version": "3.0.0-beta.6", "description": "guard sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/guard", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/guard", "author": "Sequence Platforms Inc.", "license": "Apache-2.0", "type": "module", @@ -25,11 +25,11 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3", - "vitest": "^3.2.1" + "@types/node": "^25.0.2", + "typescript": "^5.9.3", + "vitest": "^4.0.15" }, "dependencies": { - "ox": "^0.7.2" + "ox": "^0.9.17" } } diff --git a/packages/services/guard/src/client/guard.gen.ts b/packages/services/guard/src/client/guard.gen.ts index ec0af4487a..4eea436eeb 100644 --- a/packages/services/guard/src/client/guard.gen.ts +++ b/packages/services/guard/src/client/guard.gen.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// sequence-guard v0.4.0 b62e755c3f81d6b5a8e7462abc063a57a744cdef +// sequence-guard v0.5.0 910e01c32ffb24b42386d4ca6be119b0acc55c5f // -- // Code generated by webrpc-gen@v0.25.3 with typescript generator. DO NOT EDIT. // @@ -7,16 +7,16 @@ export const WebrpcHeader = 'Webrpc' -export const WebrpcHeaderValue = 'webrpc@v0.25.3;gen-typescript@v0.17.0;sequence-guard@v0.4.0' +export const WebrpcHeaderValue = 'webrpc@v0.25.3;gen-typescript@v0.17.0;sequence-guard@v0.5.0' // WebRPC description and code-gen version export const WebRPCVersion = 'v1' // Schema version of your RIDL schema -export const WebRPCSchemaVersion = 'v0.4.0' +export const WebRPCSchemaVersion = 'v0.5.0' // Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = 'b62e755c3f81d6b5a8e7462abc063a57a744cdef' +export const WebRPCSchemaHash = '910e01c32ffb24b42386d4ca6be119b0acc55c5f' type WebrpcGenVersions = { webrpcGenVersion: string @@ -131,6 +131,7 @@ export interface OwnershipProof { export interface AuthToken { id: string token: string + resetAuth?: boolean } export interface RecoveryCode { diff --git a/packages/services/identity-instrument/CHANGELOG.md b/packages/services/identity-instrument/CHANGELOG.md new file mode 100644 index 0000000000..8667827147 --- /dev/null +++ b/packages/services/identity-instrument/CHANGELOG.md @@ -0,0 +1,37 @@ +# @0xsequence/identity-instrument + +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 diff --git a/packages/services/identity-instrument/package.json b/packages/services/identity-instrument/package.json index 3accab80e9..026b8c2914 100644 --- a/packages/services/identity-instrument/package.json +++ b/packages/services/identity-instrument/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/identity-instrument", - "version": "0.0.0", + "version": "3.0.0-beta.6", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -20,13 +20,13 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3", - "vitest": "^3.2.1" + "@types/node": "^25.0.2", + "typescript": "^5.9.3", + "vitest": "^4.0.15" }, "dependencies": { "json-canonicalize": "^2.0.0", "jwt-decode": "^4.0.0", - "ox": "^0.7.2" + "ox": "^0.9.17" } } diff --git a/packages/services/indexer/CHANGELOG.md b/packages/services/indexer/CHANGELOG.md index 6b1ea35bec..e320c4307e 100644 --- a/packages/services/indexer/CHANGELOG.md +++ b/packages/services/indexer/CHANGELOG.md @@ -1,5 +1,41 @@ # @0xsequence/indexer +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 + ## 2.3.8 ### Patch Changes @@ -1422,7 +1458,6 @@ - relayer: fix Relayer.wait() interface The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - delay: the polling interval, i.e. the time to wait between requests - maxFails: the maximum number of hard failures to tolerate before giving up @@ -1767,7 +1802,6 @@ ### Minor Changes - major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - separate per-chain indexer service, API no longer handles indexing - single contract metadata service, API no longer serves metadata diff --git a/packages/services/indexer/package.json b/packages/services/indexer/package.json index 0eec0d29d4..cb91b590ce 100644 --- a/packages/services/indexer/package.json +++ b/packages/services/indexer/package.json @@ -1,8 +1,8 @@ { "name": "@0xsequence/indexer", - "version": "3.0.0", + "version": "3.0.0-beta.6", "description": "indexer sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/indexer", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/indexer", "author": "Sequence Platforms Inc.", "license": "Apache-2.0", "publishConfig": { @@ -16,13 +16,13 @@ }, "exports": { ".": { - "types": "./src/index.ts", + "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3" + "@types/node": "^25.0.2", + "typescript": "^5.9.3" } } diff --git a/packages/services/marketplace/CHANGELOG.md b/packages/services/marketplace/CHANGELOG.md index 6295f3498c..6e33420f0a 100644 --- a/packages/services/marketplace/CHANGELOG.md +++ b/packages/services/marketplace/CHANGELOG.md @@ -1,5 +1,41 @@ # @0xsequence/marketplace +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 + ## 2.3.8 ### Patch Changes diff --git a/packages/services/marketplace/package.json b/packages/services/marketplace/package.json index 106c486daf..58959af0d2 100644 --- a/packages/services/marketplace/package.json +++ b/packages/services/marketplace/package.json @@ -1,8 +1,8 @@ { "name": "@0xsequence/marketplace", - "version": "3.0.0", + "version": "3.0.0-beta.6", "description": "marketplace sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/marketplace", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/marketplace", "author": "Sequence Platforms Inc.", "license": "Apache-2.0", "publishConfig": { @@ -16,13 +16,13 @@ }, "exports": { ".": { - "types": "./src/index.ts", + "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3" + "@types/node": "^25.0.2", + "typescript": "^5.9.3" } } diff --git a/packages/services/marketplace/src/marketplace.gen.ts b/packages/services/marketplace/src/marketplace.gen.ts index f251bce5d3..6a316623df 100644 --- a/packages/services/marketplace/src/marketplace.gen.ts +++ b/packages/services/marketplace/src/marketplace.gen.ts @@ -1,14 +1,14 @@ /* eslint-disable */ -// marketplace-api 7ab3354385f317680dd861e82a18aa351d8579d5 +// marketplace-api 652676d9951ceb12f6846907c7c4b5160c73c57a // -- -// Code generated by webrpc-gen@v0.25.1 with typescript generator. DO NOT EDIT. +// Code generated by webrpc-gen@v0.30.2 with github.com/webrpc/gen-typescript@v0.19.0 generator. DO NOT EDIT. // -// webrpc-gen -schema=marketplace.ridl -target=typescript -client -out=./clients/marketplace.gen.ts +// webrpc-gen -schema=./schema/schema.ridl -target=github.com/webrpc/gen-typescript@v0.19.0 -client -out=./clients/marketplace.gen.ts export const WebrpcHeader = 'Webrpc' export const WebrpcHeaderValue = - 'webrpc@v0.25.1;gen-typescript@v0.17.0;marketplace-api@v0.0.0-7ab3354385f317680dd861e82a18aa351d8579d5' + 'webrpc@v0.30.2;gen-typescript@v0.19.0;marketplace-api@v0.0.0-652676d9951ceb12f6846907c7c4b5160c73c57a' // WebRPC description and code-gen version export const WebRPCVersion = 'v1' @@ -17,7 +17,7 @@ export const WebRPCVersion = 'v1' export const WebRPCSchemaVersion = '' // Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = '7ab3354385f317680dd861e82a18aa351d8579d5' +export const WebRPCSchemaHash = '652676d9951ceb12f6846907c7c4b5160c73c57a' type WebrpcGenVersions = { webrpcGenVersion: string @@ -71,41 +71,9 @@ function parseWebrpcGenVersions(header: string): WebrpcGenVersions { // Types // -export interface TokenMetadata { - tokenId: string - name: string - description?: string - image?: string - video?: string - audio?: string - properties?: { [key: string]: any } - attributes: Array<{ [key: string]: any }> - image_data?: string - external_url?: string - background_color?: string - animation_url?: string - decimals?: number - updatedAt?: string - assets?: Array -} - -export interface Asset { - id: number - collectionId: number - tokenId: string - url?: string - metadataField: string - name?: string - filesize?: number - mimeType?: string - width?: number - height?: number - updatedAt?: string -} - export enum SortOrder { - DESC = 'DESC', ASC = 'ASC', + DESC = 'DESC', } export enum PropertyType { @@ -127,6 +95,7 @@ export enum MarketplaceKind { alienswap = 'alienswap', payment_processor = 'payment_processor', mintify = 'mintify', + magic_eden = 'magic_eden', } export enum OrderbookKind { @@ -138,6 +107,7 @@ export enum OrderbookKind { looks_rare = 'looks_rare', reservoir = 'reservoir', x2y2 = 'x2y2', + magic_eden = 'magic_eden', } export enum SourceKind { @@ -145,6 +115,8 @@ export enum SourceKind { external = 'external', sequence_marketplace_v1 = 'sequence_marketplace_v1', sequence_marketplace_v2 = 'sequence_marketplace_v2', + opensea = 'opensea', + magic_eden = 'magic_eden', } export enum OrderSide { @@ -153,6 +125,12 @@ export enum OrderSide { offer = 'offer', } +export enum OfferType { + unknown = 'unknown', + item = 'item', + collection = 'collection', +} + export enum OrderStatus { unknown = 'unknown', active = 'active', @@ -180,12 +158,6 @@ export enum CollectionPriority { export enum CollectionStatus { unknown = 'unknown', created = 'created', - syncing_contract_metadata = 'syncing_contract_metadata', - synced_contract_metadata = 'synced_contract_metadata', - syncing_metadata = 'syncing_metadata', - synced_metadata = 'synced_metadata', - syncing_tokens = 'syncing_tokens', - synced_tokens = 'synced_tokens', syncing_orders = 'syncing_orders', active = 'active', failed = 'failed', @@ -199,12 +171,30 @@ export enum ProjectStatus { inactive = 'inactive', } +export enum ItemsContractStatus { + unknown = 'unknown', + created = 'created', + syncing_contract_metadata = 'syncing_contract_metadata', + synced_contract_metadata = 'synced_contract_metadata', + syncing_tokens = 'syncing_tokens', + synced_tokens = 'synced_tokens', + active = 'active', + inactive = 'inactive', + incompatible_type = 'incompatible_type', +} + export enum CollectibleStatus { unknown = 'unknown', active = 'active', inactive = 'inactive', } +export enum CollectibleSource { + unknown = 'unknown', + indexer = 'indexer', + manual = 'manual', +} + export enum CurrencyStatus { unknown = 'unknown', created = 'created', @@ -238,24 +228,27 @@ export enum TransactionCrypto { export enum TransactionNFTCheckoutProvider { unknown = 'unknown', - sardine = 'sardine', transak = 'transak', + sardine = 'sardine', } export enum TransactionOnRampProvider { unknown = 'unknown', - sardine = 'sardine', transak = 'transak', + sardine = 'sardine', } export enum TransactionSwapProvider { unknown = 'unknown', - zerox = 'zerox', + lifi = 'lifi', } export enum ExecuteType { unknown = 'unknown', order = 'order', + createListing = 'createListing', + createItemOffer = 'createItemOffer', + createTraitOffer = 'createTraitOffer', } export enum ActivityAction { @@ -269,6 +262,33 @@ export enum ActivityAction { transfer = 'transfer', } +export enum PrimarySaleContractStatus { + unknown = 'unknown', + created = 'created', + syncing_items = 'syncing_items', + active = 'active', + inactive = 'inactive', + incompatible_type = 'incompatible_type', + failed = 'failed', +} + +export enum PrimarySaleVersion { + v0 = 'v0', + v1 = 'v1', +} + +export enum PrimarySaleItemDetailType { + unknown = 'unknown', + global = 'global', + individual = 'individual', +} + +export enum MetadataStatus { + NOT_AVAILABLE = 'NOT_AVAILABLE', + REFRESHING = 'REFRESHING', + AVAILABLE = 'AVAILABLE', +} + export interface Page { page: number pageSize: number @@ -305,6 +325,26 @@ export interface CollectiblesFilter { ordersNotCreatedBy?: Array inCurrencyAddresses?: Array notInCurrencyAddresses?: Array + prices?: Array +} + +export interface OrdersFilter { + searchText?: string + properties?: Array + marketplaces?: Array + inAccounts?: Array + notInAccounts?: Array + ordersCreatedBy?: Array + ordersNotCreatedBy?: Array + inCurrencyAddresses?: Array + notInCurrencyAddresses?: Array + prices?: Array +} + +export interface PriceFilter { + contractAddress: string + min?: string + max?: string } export interface Order { @@ -314,6 +354,7 @@ export interface Order { status: OrderStatus chainId: number originName: string + slug: string collectionContractAddress: string tokenId?: string createdBy: string @@ -386,6 +427,8 @@ export interface CollectionConfig { export interface CollectionLastSynced { allOrders: string newOrders: string + names: Array + cursors: { [key: string]: string } } export interface Project { @@ -398,12 +441,22 @@ export interface Project { deletedAt?: string } -export interface Collectible { +export interface ItemsContract { + status: ItemsContractStatus chainId: number contractAddress: string + contractType: ContractType + lastSynced: string + createdAt: string + updatedAt: string + deletedAt?: string +} + +export interface Collectible { status: CollectibleStatus tokenId: string decimals: number + source: CollectibleSource createdAt: string updatedAt: string deletedAt?: string @@ -420,6 +473,8 @@ export interface Currency { exchangeRate: number defaultChainCurrency: boolean nativeCurrency: boolean + openseaListing: boolean + openseaOffer: boolean createdAt: string updatedAt: string deletedAt?: string @@ -499,6 +554,16 @@ export interface CheckoutOptions { onRamp: Array } +export interface ExecuteInput { + chainId: string + signature: string + method: string + endpoint: string + executeType: ExecuteType + body: any + slug?: string +} + export interface Activity { chainId: number contractAddress: string @@ -520,6 +585,91 @@ export interface Activity { deletedAt?: string } +export interface PrimarySaleContract { + chainId: number + contractAddress: string + collectionAddress: string + contractType: ContractType + version: PrimarySaleVersion + currencyAddress: string + priceDecimals: number + status: PrimarySaleContractStatus + lastSynced: string + createdAt: string + updatedAt: string + deletedAt?: string +} + +export interface PrimarySaleItem { + itemAddress: string + contractType: ContractType + tokenId: string + itemType: PrimarySaleItemDetailType + startDate: string + endDate: string + currencyAddress: string + priceDecimals: number + priceAmount: string + priceAmountFormatted: string + priceUsd: number + priceUsdFormatted: string + supply: string + supplyCap: string + unlimitedSupply: boolean + createdAt: string + updatedAt: string + deletedAt?: string +} + +export interface CollectiblePrimarySaleItem { + metadata: TokenMetadata + primarySaleItem: PrimarySaleItem +} + +export interface PrimarySaleItemsFilter { + includeEmpty: boolean + searchText?: string + properties?: Array + detailTypes?: Array + startDateAfter?: string + startDateBefore?: string + endDateAfter?: string + endDateBefore?: string +} + +export interface TokenMetadata { + tokenId: string + name: string + description?: string + image?: string + video?: string + audio?: string + properties?: { [key: string]: any } + attributes: Array<{ [key: string]: any }> + image_data?: string + external_url?: string + background_color?: string + animation_url?: string + decimals?: number + updatedAt?: string + assets?: Array + status: MetadataStatus +} + +export interface Asset { + id: number + collectionId: number + tokenId: string + url?: string + metadataField: string + name?: string + filesize?: number + mimeType?: string + width?: number + height?: number + updatedAt?: string +} + export interface Admin { createCollection(args: CreateCollectionArgs, headers?: object, signal?: AbortSignal): Promise getCollection(args: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise @@ -530,14 +680,29 @@ export interface Admin { * determine what should happen here */ syncCollection(args: SyncCollectionArgs, headers?: object, signal?: AbortSignal): Promise + createPrimarySaleContract( + args: CreatePrimarySaleContractArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + deletePrimarySaleContract( + args: DeletePrimarySaleContractArgs, + headers?: object, + signal?: AbortSignal, + ): Promise createCurrency(args: CreateCurrencyArgs, headers?: object, signal?: AbortSignal): Promise createCurrencies(args: CreateCurrenciesArgs, headers?: object, signal?: AbortSignal): Promise updateCurrency(args: UpdateCurrencyArgs, headers?: object, signal?: AbortSignal): Promise - listCurrencies(headers?: object, signal?: AbortSignal): Promise + listCurrencies(args: ListCurrenciesArgs, headers?: object, signal?: AbortSignal): Promise deleteCurrency(args: DeleteCurrencyArgs, headers?: object, signal?: AbortSignal): Promise + /** + * This for manual adding of non minted ERC1155 tokens, it's used for purposes of Shop. + */ + addCollectibles(args: AddCollectiblesArgs, headers?: object, signal?: AbortSignal): Promise } export interface CreateCollectionArgs { + chainId: string projectId: number contractAddress: string } @@ -546,6 +711,7 @@ export interface CreateCollectionReturn { collection: Collection } export interface GetCollectionArgs { + chainId: string projectId: number contractAddress: string } @@ -554,6 +720,7 @@ export interface GetCollectionReturn { collection: Collection } export interface UpdateCollectionArgs { + chainId: string collection: Collection } @@ -561,6 +728,7 @@ export interface UpdateCollectionReturn { collection: Collection } export interface ListCollectionsArgs { + chainId: string projectId: number page?: Page } @@ -570,6 +738,7 @@ export interface ListCollectionsReturn { page?: Page } export interface DeleteCollectionArgs { + chainId: string projectId: number contractAddress: string } @@ -578,14 +747,30 @@ export interface DeleteCollectionReturn { collection: Collection } export interface SyncCollectionArgs { - projectId: number + chainId: string contractAddress: string } -export interface SyncCollectionReturn { - collection: Collection +export interface SyncCollectionReturn {} +export interface CreatePrimarySaleContractArgs { + chainId: string + projectId: number + primarySaleContractAddress: string + itemsContractAddress: string +} + +export interface CreatePrimarySaleContractReturn { + primarySaleContract: PrimarySaleContract } +export interface DeletePrimarySaleContractArgs { + chainId: string + projectId: number + primarySaleContractAddress: string +} + +export interface DeletePrimarySaleContractReturn {} export interface CreateCurrencyArgs { + chainId: string currency: Currency } @@ -593,6 +778,7 @@ export interface CreateCurrencyReturn { currency: Currency } export interface CreateCurrenciesArgs { + chainId: string currencies: Array } @@ -600,33 +786,53 @@ export interface CreateCurrenciesReturn { currency: { [key: string]: Currency } } export interface UpdateCurrencyArgs { + chainId: string currency: Currency } export interface UpdateCurrencyReturn { currency: Currency } -export interface ListCurrenciesArgs {} +export interface ListCurrenciesArgs { + chainId: string +} export interface ListCurrenciesReturn { currencies: Array } export interface DeleteCurrencyArgs { - chainId: number + chainId: string contractAddress: string } export interface DeleteCurrencyReturn { currency: Currency } +export interface AddCollectiblesArgs { + chainId: string + itemsContractAddress: string + tokenIds: Array +} + +export interface AddCollectiblesReturn {} export interface Marketplace { - listCurrencies(headers?: object, signal?: AbortSignal): Promise + listCurrencies(args: ListCurrenciesArgs, headers?: object, signal?: AbortSignal): Promise getCollectionDetail( args: GetCollectionDetailArgs, headers?: object, signal?: AbortSignal, ): Promise + getCollectionActiveListingsCurrencies( + args: GetCollectionActiveListingsCurrenciesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + getCollectionActiveOffersCurrencies( + args: GetCollectionActiveOffersCurrenciesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise getCollectible(args: GetCollectibleArgs, headers?: object, signal?: AbortSignal): Promise getLowestPriceOfferForCollectible( args: GetLowestPriceOfferForCollectibleArgs, @@ -658,6 +864,23 @@ export interface Marketplace { headers?: object, signal?: AbortSignal, ): Promise + listOrdersWithCollectibles( + args: ListOrdersWithCollectiblesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + getCountOfAllOrders( + args: GetCountOfAllOrdersArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + getCountOfFilteredOrders( + args: GetCountOfFilteredOrdersArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + listListings(args: ListListingsArgs, headers?: object, signal?: AbortSignal): Promise + listOffers(args: ListOffersArgs, headers?: object, signal?: AbortSignal): Promise getCountOfListingsForCollectible( args: GetCountOfListingsForCollectibleArgs, headers?: object, @@ -745,7 +968,7 @@ export interface Marketplace { signal?: AbortSignal, ): Promise /** - * only used in a case of external transactions ( when we create off-chain transactions ) for instance opensea market + * only used in a case of external transactions ( when we create off-chain transactions ) for instance opensea market, use only ExecuteInput params and leave other root inputs empty, they are depracated and kept only for backward compatibility */ execute(args: ExecuteArgs, headers?: object, signal?: AbortSignal): Promise /** @@ -796,21 +1019,61 @@ export interface Marketplace { headers?: object, signal?: AbortSignal, ): Promise + supportedMarketplaces( + args: SupportedMarketplacesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + getPrimarySaleItem( + args: GetPrimarySaleItemArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + listPrimarySaleItems( + args: ListPrimarySaleItemsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + getCountOfPrimarySaleItems( + args: GetCountOfPrimarySaleItemsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise } -export interface ListCurrenciesArgs {} +export interface ListCurrenciesArgs { + chainId: string +} export interface ListCurrenciesReturn { currencies: Array } export interface GetCollectionDetailArgs { + chainId: string contractAddress: string } export interface GetCollectionDetailReturn { collection: Collection } +export interface GetCollectionActiveListingsCurrenciesArgs { + chainId: string + contractAddress: string +} + +export interface GetCollectionActiveListingsCurrenciesReturn { + currencies: Array +} +export interface GetCollectionActiveOffersCurrenciesArgs { + chainId: string + contractAddress: string +} + +export interface GetCollectionActiveOffersCurrenciesReturn { + currencies: Array +} export interface GetCollectibleArgs { + chainId: string contractAddress: string tokenId: string } @@ -819,6 +1082,7 @@ export interface GetCollectibleReturn { metadata: TokenMetadata } export interface GetLowestPriceOfferForCollectibleArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -828,6 +1092,7 @@ export interface GetLowestPriceOfferForCollectibleReturn { order: Order } export interface GetHighestPriceOfferForCollectibleArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -837,6 +1102,7 @@ export interface GetHighestPriceOfferForCollectibleReturn { order: Order } export interface GetLowestPriceListingForCollectibleArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -846,6 +1112,7 @@ export interface GetLowestPriceListingForCollectibleReturn { order: Order } export interface GetHighestPriceListingForCollectibleArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -855,6 +1122,7 @@ export interface GetHighestPriceListingForCollectibleReturn { order: Order } export interface ListListingsForCollectibleArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -866,6 +1134,7 @@ export interface ListListingsForCollectibleReturn { page?: Page } export interface ListOffersForCollectibleArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -876,7 +1145,61 @@ export interface ListOffersForCollectibleReturn { offers: Array page?: Page } +export interface ListOrdersWithCollectiblesArgs { + chainId: string + side: OrderSide + contractAddress: string + filter?: OrdersFilter + page?: Page +} + +export interface ListOrdersWithCollectiblesReturn { + collectibles: Array + page?: Page +} +export interface GetCountOfAllOrdersArgs { + chainId: string + side: OrderSide + contractAddress: string +} + +export interface GetCountOfAllOrdersReturn { + count: number +} +export interface GetCountOfFilteredOrdersArgs { + chainId: string + side: OrderSide + contractAddress: string + filter?: OrdersFilter +} + +export interface GetCountOfFilteredOrdersReturn { + count: number +} +export interface ListListingsArgs { + chainId: string + contractAddress: string + filter?: OrderFilter + page?: Page +} + +export interface ListListingsReturn { + listings: Array + page?: Page +} +export interface ListOffersArgs { + chainId: string + contractAddress: string + filter?: OrderFilter + page?: Page +} + +export interface ListOffersReturn { + offers: Array + page?: Page +} export interface GetCountOfListingsForCollectibleArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -886,6 +1209,7 @@ export interface GetCountOfListingsForCollectibleReturn { count: number } export interface GetCountOfOffersForCollectibleArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -895,6 +1219,7 @@ export interface GetCountOfOffersForCollectibleReturn { count: number } export interface GetCollectibleLowestOfferArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -904,6 +1229,7 @@ export interface GetCollectibleLowestOfferReturn { order?: Order } export interface GetCollectibleHighestOfferArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -913,6 +1239,7 @@ export interface GetCollectibleHighestOfferReturn { order?: Order } export interface GetCollectibleLowestListingArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -922,6 +1249,7 @@ export interface GetCollectibleLowestListingReturn { order?: Order } export interface GetCollectibleHighestListingArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -931,6 +1259,7 @@ export interface GetCollectibleHighestListingReturn { order?: Order } export interface ListCollectibleListingsArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -942,6 +1271,7 @@ export interface ListCollectibleListingsReturn { page?: Page } export interface ListCollectibleOffersArgs { + chainId: string contractAddress: string tokenId: string filter?: OrderFilter @@ -953,6 +1283,7 @@ export interface ListCollectibleOffersReturn { page?: Page } export interface GenerateBuyTransactionArgs { + chainId: string collectionAddress: string buyer: string marketplace: MarketplaceKind @@ -965,6 +1296,7 @@ export interface GenerateBuyTransactionReturn { steps: Array } export interface GenerateSellTransactionArgs { + chainId: string collectionAddress: string seller: string marketplace: MarketplaceKind @@ -977,11 +1309,13 @@ export interface GenerateSellTransactionReturn { steps: Array } export interface GenerateListingTransactionArgs { + chainId: string collectionAddress: string owner: string contractType: ContractType orderbook: OrderbookKind listing: CreateReq + additionalFees: Array walletType?: WalletKind } @@ -989,18 +1323,22 @@ export interface GenerateListingTransactionReturn { steps: Array } export interface GenerateOfferTransactionArgs { + chainId: string collectionAddress: string maker: string contractType: ContractType orderbook: OrderbookKind offer: CreateReq + additionalFees: Array walletType?: WalletKind + offerType: OfferType } export interface GenerateOfferTransactionReturn { steps: Array } export interface GenerateCancelTransactionArgs { + chainId: string collectionAddress: string maker: string marketplace: MarketplaceKind @@ -1011,17 +1349,20 @@ export interface GenerateCancelTransactionReturn { steps: Array } export interface ExecuteArgs { - signature: string - method: string - endpoint: string - executeType: ExecuteType - body: any + params: ExecuteInput + chainId?: string + signature?: string + method?: string + endpoint?: string + executeType?: ExecuteType + body?: any } export interface ExecuteReturn { orderId: string } export interface ListCollectiblesArgs { + chainId: string side: OrderSide contractAddress: string filter?: CollectiblesFilter @@ -1033,6 +1374,7 @@ export interface ListCollectiblesReturn { page?: Page } export interface GetCountOfAllCollectiblesArgs { + chainId: string contractAddress: string } @@ -1040,6 +1382,7 @@ export interface GetCountOfAllCollectiblesReturn { count: number } export interface GetCountOfFilteredCollectiblesArgs { + chainId: string side: OrderSide contractAddress: string filter?: CollectiblesFilter @@ -1049,6 +1392,7 @@ export interface GetCountOfFilteredCollectiblesReturn { count: number } export interface GetFloorOrderArgs { + chainId: string contractAddress: string filter?: CollectiblesFilter } @@ -1057,6 +1401,7 @@ export interface GetFloorOrderReturn { collectible: CollectibleOrder } export interface ListCollectionActivitiesArgs { + chainId: string contractAddress: string page?: Page } @@ -1066,6 +1411,7 @@ export interface ListCollectionActivitiesReturn { page?: Page } export interface ListCollectibleActivitiesArgs { + chainId: string contractAddress: string tokenId: string page?: Page @@ -1076,6 +1422,7 @@ export interface ListCollectibleActivitiesReturn { page?: Page } export interface ListCollectiblesWithLowestListingArgs { + chainId: string contractAddress: string filter?: CollectiblesFilter page?: Page @@ -1086,6 +1433,7 @@ export interface ListCollectiblesWithLowestListingReturn { page?: Page } export interface ListCollectiblesWithHighestOfferArgs { + chainId: string contractAddress: string filter?: CollectiblesFilter page?: Page @@ -1096,16 +1444,19 @@ export interface ListCollectiblesWithHighestOfferReturn { page?: Page } export interface SyncOrderArgs { + chainId: string order: Order } export interface SyncOrderReturn {} export interface SyncOrdersArgs { + chainId: string orders: Array } export interface SyncOrdersReturn {} export interface GetOrdersArgs { + chainId: string input: Array page?: Page } @@ -1115,6 +1466,7 @@ export interface GetOrdersReturn { page?: Page } export interface CheckoutOptionsMarketplaceArgs { + chainId: string wallet: string orders: Array additionalFee: number @@ -1124,6 +1476,7 @@ export interface CheckoutOptionsMarketplaceReturn { options: CheckoutOptions } export interface CheckoutOptionsSalesContractArgs { + chainId: string wallet: string contractAddress: string collectionAddress: string @@ -1133,6 +1486,42 @@ export interface CheckoutOptionsSalesContractArgs { export interface CheckoutOptionsSalesContractReturn { options: CheckoutOptions } +export interface SupportedMarketplacesArgs { + chainId: string +} + +export interface SupportedMarketplacesReturn { + marketplaces: Array +} +export interface GetPrimarySaleItemArgs { + chainId: string + primarySaleContractAddress: string + tokenId: string +} + +export interface GetPrimarySaleItemReturn { + item: CollectiblePrimarySaleItem +} +export interface ListPrimarySaleItemsArgs { + chainId: string + primarySaleContractAddress: string + filter?: PrimarySaleItemsFilter + page?: Page +} + +export interface ListPrimarySaleItemsReturn { + primarySaleItems: Array + page?: Page +} +export interface GetCountOfPrimarySaleItemsArgs { + chainId: string + primarySaleContractAddress: string + filter?: PrimarySaleItemsFilter +} + +export interface GetCountOfPrimarySaleItemsReturn { + count: number +} // // Client @@ -1165,7 +1554,9 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1180,7 +1571,9 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1199,7 +1592,9 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1219,7 +1614,9 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1238,7 +1635,9 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1249,15 +1648,55 @@ export class Admin implements Admin { signal?: AbortSignal, ): Promise => { return this.fetch(this.url('SyncCollection'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return {} + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + createPrimarySaleContract = ( + args: CreatePrimarySaleContractArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('CreatePrimarySaleContract'), createHTTPRequest(args, headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return { - collection: _data.collection, + primarySaleContract: _data.primarySaleContract, } }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deletePrimarySaleContract = ( + args: DeletePrimarySaleContractArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('DeletePrimarySaleContract'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return {} + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1276,7 +1715,9 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1295,7 +1736,9 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1314,13 +1757,19 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - listCurrencies = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('ListCurrencies'), createHTTPRequest({}, headers, signal)).then( + listCurrencies = ( + args: ListCurrenciesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('ListCurrencies'), createHTTPRequest(args, headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return { @@ -1329,7 +1778,9 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1348,7 +1799,28 @@ export class Admin implements Admin { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + addCollectibles = ( + args: AddCollectiblesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('AddCollectibles'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return {} + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1367,8 +1839,12 @@ export class Marketplace implements Marketplace { return this.hostname + this.path + name } - listCurrencies = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('ListCurrencies'), createHTTPRequest({}, headers, signal)).then( + listCurrencies = ( + args: ListCurrenciesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('ListCurrencies'), createHTTPRequest(args, headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { return { @@ -1377,7 +1853,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1396,7 +1874,51 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getCollectionActiveListingsCurrencies = ( + args: GetCollectionActiveListingsCurrenciesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('GetCollectionActiveListingsCurrencies'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + currencies: >_data.currencies, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getCollectionActiveOffersCurrencies = ( + args: GetCollectionActiveOffersCurrenciesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('GetCollectionActiveOffersCurrencies'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + currencies: >_data.currencies, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1415,7 +1937,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1434,7 +1958,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1453,7 +1979,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1472,7 +2000,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1491,7 +2021,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1511,7 +2043,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1531,7 +2065,109 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listOrdersWithCollectibles = ( + args: ListOrdersWithCollectiblesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('ListOrdersWithCollectibles'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + collectibles: >_data.collectibles, + page: _data.page, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getCountOfAllOrders = ( + args: GetCountOfAllOrdersArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('GetCountOfAllOrders'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + count: _data.count, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getCountOfFilteredOrders = ( + args: GetCountOfFilteredOrdersArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('GetCountOfFilteredOrders'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + count: _data.count, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listListings = (args: ListListingsArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('ListListings'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + listings: >_data.listings, + page: _data.page, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listOffers = (args: ListOffersArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('ListOffers'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + offers: >_data.offers, + page: _data.page, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1550,7 +2186,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1569,7 +2207,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1588,7 +2228,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1607,7 +2249,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1626,7 +2270,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1645,7 +2291,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1665,7 +2313,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1685,7 +2335,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1704,7 +2356,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1723,7 +2377,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1742,7 +2398,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1761,7 +2419,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1780,7 +2440,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1795,7 +2457,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1815,7 +2479,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1834,7 +2500,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1853,7 +2521,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1868,7 +2538,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1888,7 +2560,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1908,7 +2582,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1928,7 +2604,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1948,7 +2626,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1961,7 +2641,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1974,7 +2656,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -1990,7 +2674,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -2009,7 +2695,9 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -2028,7 +2716,94 @@ export class Marketplace implements Marketplace { }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + supportedMarketplaces = ( + args: SupportedMarketplacesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('SupportedMarketplaces'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + marketplaces: >_data.marketplaces, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getPrimarySaleItem = ( + args: GetPrimarySaleItemArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('GetPrimarySaleItem'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + item: _data.item, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listPrimarySaleItems = ( + args: ListPrimarySaleItemsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('ListPrimarySaleItems'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + primarySaleItems: >_data.primarySaleItems, + page: _data.page, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getCountOfPrimarySaleItems = ( + args: GetCountOfPrimarySaleItemsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch(this.url('GetCountOfPrimarySaleItems'), createHTTPRequest(args, headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return { + count: _data.count, + } + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } @@ -2052,13 +2827,9 @@ const buildResponse = (res: Response): Promise => { try { data = JSON.parse(text) } catch (error) { - let message = '' - if (error instanceof Error) { - message = error.message - } throw WebrpcBadResponseError.new({ status: res.status, - cause: `JSON.parse(): ${message}: response text: ${text}`, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, }) } if (!res.ok) { @@ -2106,7 +2877,7 @@ export class WebrpcEndpointError extends WebrpcError { name: string = 'WebrpcEndpoint', code: number = 0, message: string = `endpoint error`, - status: number = 0, + status: number = 400, cause?: string, ) { super(name, code, message, status, cause) @@ -2119,7 +2890,7 @@ export class WebrpcRequestFailedError extends WebrpcError { name: string = 'WebrpcRequestFailed', code: number = -1, message: string = `request failed`, - status: number = 0, + status: number = 400, cause?: string, ) { super(name, code, message, status, cause) @@ -2132,7 +2903,7 @@ export class WebrpcBadRouteError extends WebrpcError { name: string = 'WebrpcBadRoute', code: number = -2, message: string = `bad route`, - status: number = 0, + status: number = 404, cause?: string, ) { super(name, code, message, status, cause) @@ -2145,7 +2916,7 @@ export class WebrpcBadMethodError extends WebrpcError { name: string = 'WebrpcBadMethod', code: number = -3, message: string = `bad method`, - status: number = 0, + status: number = 405, cause?: string, ) { super(name, code, message, status, cause) @@ -2158,7 +2929,7 @@ export class WebrpcBadRequestError extends WebrpcError { name: string = 'WebrpcBadRequest', code: number = -4, message: string = `bad request`, - status: number = 0, + status: number = 400, cause?: string, ) { super(name, code, message, status, cause) @@ -2171,7 +2942,7 @@ export class WebrpcBadResponseError extends WebrpcError { name: string = 'WebrpcBadResponse', code: number = -5, message: string = `bad response`, - status: number = 0, + status: number = 500, cause?: string, ) { super(name, code, message, status, cause) @@ -2184,7 +2955,7 @@ export class WebrpcServerPanicError extends WebrpcError { name: string = 'WebrpcServerPanic', code: number = -6, message: string = `server panic`, - status: number = 0, + status: number = 500, cause?: string, ) { super(name, code, message, status, cause) @@ -2197,7 +2968,7 @@ export class WebrpcInternalErrorError extends WebrpcError { name: string = 'WebrpcInternalError', code: number = -7, message: string = `internal error`, - status: number = 0, + status: number = 500, cause?: string, ) { super(name, code, message, status, cause) @@ -2205,16 +2976,16 @@ export class WebrpcInternalErrorError extends WebrpcError { } } -export class WebrpcClientDisconnectedError extends WebrpcError { +export class WebrpcClientAbortedError extends WebrpcError { constructor( - name: string = 'WebrpcClientDisconnected', + name: string = 'WebrpcClientAborted', code: number = -8, - message: string = `client disconnected`, - status: number = 0, + message: string = `request aborted by client`, + status: number = 400, cause?: string, ) { super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype) + Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) } } @@ -2223,7 +2994,7 @@ export class WebrpcStreamLostError extends WebrpcError { name: string = 'WebrpcStreamLost', code: number = -9, message: string = `stream lost`, - status: number = 0, + status: number = 400, cause?: string, ) { super(name, code, message, status, cause) @@ -2236,7 +3007,7 @@ export class WebrpcStreamFinishedError extends WebrpcError { name: string = 'WebrpcStreamFinished', code: number = -10, message: string = `stream finished`, - status: number = 0, + status: number = 200, cause?: string, ) { super(name, code, message, status, cause) @@ -2251,7 +3022,7 @@ export class UnauthorizedError extends WebrpcError { name: string = 'Unauthorized', code: number = 1000, message: string = `Unauthorized access`, - status: number = 0, + status: number = 401, cause?: string, ) { super(name, code, message, status, cause) @@ -2264,7 +3035,7 @@ export class PermissionDeniedError extends WebrpcError { name: string = 'PermissionDenied', code: number = 1001, message: string = `Permission denied`, - status: number = 0, + status: number = 403, cause?: string, ) { super(name, code, message, status, cause) @@ -2277,7 +3048,7 @@ export class SessionExpiredError extends WebrpcError { name: string = 'SessionExpired', code: number = 1002, message: string = `Session expired`, - status: number = 0, + status: number = 403, cause?: string, ) { super(name, code, message, status, cause) @@ -2290,7 +3061,7 @@ export class MethodNotFoundError extends WebrpcError { name: string = 'MethodNotFound', code: number = 1003, message: string = `Method not found`, - status: number = 0, + status: number = 404, cause?: string, ) { super(name, code, message, status, cause) @@ -2298,64 +3069,64 @@ export class MethodNotFoundError extends WebrpcError { } } -export class TimeoutError extends WebrpcError { +export class RequestConflictError extends WebrpcError { constructor( - name: string = 'Timeout', - code: number = 2000, - message: string = `Request timed out`, - status: number = 0, + name: string = 'RequestConflict', + code: number = 1004, + message: string = `Conflict with target resource`, + status: number = 409, cause?: string, ) { super(name, code, message, status, cause) - Object.setPrototypeOf(this, TimeoutError.prototype) + Object.setPrototypeOf(this, RequestConflictError.prototype) } } -export class InvalidArgumentError extends WebrpcError { +export class AbortedError extends WebrpcError { constructor( - name: string = 'InvalidArgument', - code: number = 2001, - message: string = `Invalid argument`, - status: number = 0, + name: string = 'Aborted', + code: number = 1005, + message: string = `Request aborted`, + status: number = 400, cause?: string, ) { super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidArgumentError.prototype) + Object.setPrototypeOf(this, AbortedError.prototype) } } -export class NotFoundError extends WebrpcError { +export class GeoblockedError extends WebrpcError { constructor( - name: string = 'NotFound', - code: number = 3000, - message: string = `Resource not found`, - status: number = 0, + name: string = 'Geoblocked', + code: number = 1006, + message: string = `Geoblocked region`, + status: number = 451, cause?: string, ) { super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotFoundError.prototype) + Object.setPrototypeOf(this, GeoblockedError.prototype) } } -export class UserNotFoundError extends WebrpcError { +export class RateLimitedError extends WebrpcError { constructor( - name: string = 'UserNotFound', - code: number = 3001, - message: string = `User not found`, - status: number = 0, + name: string = 'RateLimited', + code: number = 1007, + message: string = `Rate-limited. Please slow down.`, + status: number = 429, cause?: string, ) { super(name, code, message, status, cause) - Object.setPrototypeOf(this, UserNotFoundError.prototype) + Object.setPrototypeOf(this, RateLimitedError.prototype) } } export class ProjectNotFoundError extends WebrpcError { constructor( name: string = 'ProjectNotFound', - code: number = 3002, + code: number = 1008, message: string = `Project not found`, - status: number = 0, + status: number = 401, cause?: string, ) { super(name, code, message, status, cause) @@ -2363,29 +3134,198 @@ export class ProjectNotFoundError extends WebrpcError { } } -export class InvalidTierError extends WebrpcError { +export class SecretKeyCorsDisallowedError extends WebrpcError { + constructor( + name: string = 'SecretKeyCorsDisallowed', + code: number = 1009, + message: string = `CORS disallowed. Admin API Secret Key can't be used from a web app.`, + status: number = 403, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, SecretKeyCorsDisallowedError.prototype) + } +} + +export class AccessKeyNotFoundError extends WebrpcError { + constructor( + name: string = 'AccessKeyNotFound', + code: number = 1101, + message: string = `Access key not found`, + status: number = 401, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) + } +} + +export class AccessKeyMismatchError extends WebrpcError { + constructor( + name: string = 'AccessKeyMismatch', + code: number = 1102, + message: string = `Access key mismatch`, + status: number = 403, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) + } +} + +export class InvalidOriginError extends WebrpcError { + constructor( + name: string = 'InvalidOrigin', + code: number = 1103, + message: string = `Invalid origin for Access Key`, + status: number = 403, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, InvalidOriginError.prototype) + } +} + +export class InvalidServiceError extends WebrpcError { + constructor( + name: string = 'InvalidService', + code: number = 1104, + message: string = `Service not enabled for Access key`, + status: number = 403, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, InvalidServiceError.prototype) + } +} + +export class UnauthorizedUserError extends WebrpcError { constructor( - name: string = 'InvalidTier', - code: number = 3003, - message: string = `Invalid subscription tier`, - status: number = 0, + name: string = 'UnauthorizedUser', + code: number = 1105, + message: string = `Unauthorized user`, + status: number = 403, cause?: string, ) { super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidTierError.prototype) + Object.setPrototypeOf(this, UnauthorizedUserError.prototype) } } -export class ProjectLimitReachedError extends WebrpcError { +export class InvalidChainError extends WebrpcError { constructor( - name: string = 'ProjectLimitReached', - code: number = 3005, - message: string = `Project limit reached`, - status: number = 0, + name: string = 'InvalidChain', + code: number = 1106, + message: string = `Network not enabled for Access key`, + status: number = 403, cause?: string, ) { super(name, code, message, status, cause) - Object.setPrototypeOf(this, ProjectLimitReachedError.prototype) + Object.setPrototypeOf(this, InvalidChainError.prototype) + } +} + +export class QuotaExceededError extends WebrpcError { + constructor( + name: string = 'QuotaExceeded', + code: number = 1200, + message: string = `Quota request exceeded`, + status: number = 429, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, QuotaExceededError.prototype) + } +} + +export class QuotaRateLimitError extends WebrpcError { + constructor( + name: string = 'QuotaRateLimit', + code: number = 1201, + message: string = `Quota rate limit exceeded`, + status: number = 429, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, QuotaRateLimitError.prototype) + } +} + +export class NoDefaultKeyError extends WebrpcError { + constructor( + name: string = 'NoDefaultKey', + code: number = 1300, + message: string = `No default access key found`, + status: number = 403, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, NoDefaultKeyError.prototype) + } +} + +export class MaxAccessKeysError extends WebrpcError { + constructor( + name: string = 'MaxAccessKeys', + code: number = 1301, + message: string = `Access keys limit reached`, + status: number = 403, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, MaxAccessKeysError.prototype) + } +} + +export class AtLeastOneKeyError extends WebrpcError { + constructor( + name: string = 'AtLeastOneKey', + code: number = 1302, + message: string = `You need at least one Access Key`, + status: number = 403, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) + } +} + +export class TimeoutError extends WebrpcError { + constructor( + name: string = 'Timeout', + code: number = 1900, + message: string = `Request timed out`, + status: number = 408, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, TimeoutError.prototype) + } +} + +export class NotFoundError extends WebrpcError { + constructor( + name: string = 'NotFound', + code: number = 2000, + message: string = `Resource not found`, + status: number = 400, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, NotFoundError.prototype) + } +} + +export class InvalidArgumentError extends WebrpcError { + constructor( + name: string = 'InvalidArgument', + code: number = 2001, + message: string = `Invalid argument`, + status: number = 400, + cause?: string, + ) { + super(name, code, message, status, cause) + Object.setPrototypeOf(this, InvalidArgumentError.prototype) } } @@ -2394,7 +3334,7 @@ export class NotImplementedError extends WebrpcError { name: string = 'NotImplemented', code: number = 9999, message: string = `Not Implemented`, - status: number = 0, + status: number = 500, cause?: string, ) { super(name, code, message, status, cause) @@ -2411,20 +3351,33 @@ export enum errors { WebrpcBadResponse = 'WebrpcBadResponse', WebrpcServerPanic = 'WebrpcServerPanic', WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', + WebrpcClientAborted = 'WebrpcClientAborted', WebrpcStreamLost = 'WebrpcStreamLost', WebrpcStreamFinished = 'WebrpcStreamFinished', Unauthorized = 'Unauthorized', PermissionDenied = 'PermissionDenied', SessionExpired = 'SessionExpired', MethodNotFound = 'MethodNotFound', + RequestConflict = 'RequestConflict', + Aborted = 'Aborted', + Geoblocked = 'Geoblocked', + RateLimited = 'RateLimited', + ProjectNotFound = 'ProjectNotFound', + SecretKeyCorsDisallowed = 'SecretKeyCorsDisallowed', + AccessKeyNotFound = 'AccessKeyNotFound', + AccessKeyMismatch = 'AccessKeyMismatch', + InvalidOrigin = 'InvalidOrigin', + InvalidService = 'InvalidService', + UnauthorizedUser = 'UnauthorizedUser', + InvalidChain = 'InvalidChain', + QuotaExceeded = 'QuotaExceeded', + QuotaRateLimit = 'QuotaRateLimit', + NoDefaultKey = 'NoDefaultKey', + MaxAccessKeys = 'MaxAccessKeys', + AtLeastOneKey = 'AtLeastOneKey', Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', NotFound = 'NotFound', - UserNotFound = 'UserNotFound', - ProjectNotFound = 'ProjectNotFound', - InvalidTier = 'InvalidTier', - ProjectLimitReached = 'ProjectLimitReached', + InvalidArgument = 'InvalidArgument', NotImplemented = 'NotImplemented', } @@ -2437,20 +3390,33 @@ export enum WebrpcErrorCodes { WebrpcBadResponse = -5, WebrpcServerPanic = -6, WebrpcInternalError = -7, - WebrpcClientDisconnected = -8, + WebrpcClientAborted = -8, WebrpcStreamLost = -9, WebrpcStreamFinished = -10, Unauthorized = 1000, PermissionDenied = 1001, SessionExpired = 1002, MethodNotFound = 1003, - Timeout = 2000, + RequestConflict = 1004, + Aborted = 1005, + Geoblocked = 1006, + RateLimited = 1007, + ProjectNotFound = 1008, + SecretKeyCorsDisallowed = 1009, + AccessKeyNotFound = 1101, + AccessKeyMismatch = 1102, + InvalidOrigin = 1103, + InvalidService = 1104, + UnauthorizedUser = 1105, + InvalidChain = 1106, + QuotaExceeded = 1200, + QuotaRateLimit = 1201, + NoDefaultKey = 1300, + MaxAccessKeys = 1301, + AtLeastOneKey = 1302, + Timeout = 1900, + NotFound = 2000, InvalidArgument = 2001, - NotFound = 3000, - UserNotFound = 3001, - ProjectNotFound = 3002, - InvalidTier = 3003, - ProjectLimitReached = 3005, NotImplemented = 9999, } @@ -2463,20 +3429,33 @@ export const webrpcErrorByCode: { [code: number]: any } = { [-5]: WebrpcBadResponseError, [-6]: WebrpcServerPanicError, [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, + [-8]: WebrpcClientAbortedError, [-9]: WebrpcStreamLostError, [-10]: WebrpcStreamFinishedError, [1000]: UnauthorizedError, [1001]: PermissionDeniedError, [1002]: SessionExpiredError, [1003]: MethodNotFoundError, - [2000]: TimeoutError, + [1004]: RequestConflictError, + [1005]: AbortedError, + [1006]: GeoblockedError, + [1007]: RateLimitedError, + [1008]: ProjectNotFoundError, + [1009]: SecretKeyCorsDisallowedError, + [1101]: AccessKeyNotFoundError, + [1102]: AccessKeyMismatchError, + [1103]: InvalidOriginError, + [1104]: InvalidServiceError, + [1105]: UnauthorizedUserError, + [1106]: InvalidChainError, + [1200]: QuotaExceededError, + [1201]: QuotaRateLimitError, + [1300]: NoDefaultKeyError, + [1301]: MaxAccessKeysError, + [1302]: AtLeastOneKeyError, + [1900]: TimeoutError, + [2000]: NotFoundError, [2001]: InvalidArgumentError, - [3000]: NotFoundError, - [3001]: UserNotFoundError, - [3002]: ProjectNotFoundError, - [3003]: InvalidTierError, - [3005]: ProjectLimitReachedError, [9999]: NotImplementedError, } diff --git a/packages/services/metadata/CHANGELOG.md b/packages/services/metadata/CHANGELOG.md index b51ee450c1..4fc3e61cc0 100644 --- a/packages/services/metadata/CHANGELOG.md +++ b/packages/services/metadata/CHANGELOG.md @@ -1,5 +1,41 @@ # @0xsequence/metadata +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 + ## 2.3.8 ### Patch Changes @@ -1422,7 +1458,6 @@ - relayer: fix Relayer.wait() interface The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - delay: the polling interval, i.e. the time to wait between requests - maxFails: the maximum number of hard failures to tolerate before giving up @@ -1775,7 +1810,6 @@ ### Minor Changes - major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - separate per-chain indexer service, API no longer handles indexing - single contract metadata service, API no longer serves metadata diff --git a/packages/services/metadata/package.json b/packages/services/metadata/package.json index af9069cd66..9218059ef1 100644 --- a/packages/services/metadata/package.json +++ b/packages/services/metadata/package.json @@ -1,11 +1,11 @@ { "name": "@0xsequence/metadata", - "version": "3.0.0", + "version": "3.0.0-beta.6", "publishConfig": { "access": "public" }, "description": "metadata sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/metadata", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/metadata", "author": "Sequence Platforms Inc.", "license": "Apache-2.0", "scripts": { @@ -16,13 +16,13 @@ }, "exports": { ".": { - "types": "./src/index.ts", + "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3" + "@types/node": "^25.0.2", + "typescript": "^5.9.3" } } diff --git a/packages/services/metadata/src/metadata.gen.ts b/packages/services/metadata/src/metadata.gen.ts index 05cdfb1943..9390aee762 100644 --- a/packages/services/metadata/src/metadata.gen.ts +++ b/packages/services/metadata/src/metadata.gen.ts @@ -1,73 +1,357 @@ /* eslint-disable */ -// sequence-metadata v0.4.0 5cb74ff169ce80c2e42e65d6bbc98b1daaa0945f +// sequence-metadata v0.4.0 673a5fa528008c7f9558810fbb24aad978ed7a84 // -- -// Code generated by webrpc-gen@v0.25.3 with typescript generator. DO NOT EDIT. +// Code generated by Webrpc-gen@v0.31.0 with typescript generator. DO NOT EDIT. // -// webrpc-gen -schema=metadata.ridl -target=typescript -client -ignore=@deprecated -out=./clients/metadata.gen.ts +// webrpc-gen -schema=metadata.ridl -target=typescript -client -ignore=@deprecated -compat -out=./clients/metadata.gen.ts -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.25.3;gen-typescript@v0.17.0;sequence-metadata@v0.4.0' - -// WebRPC description and code-gen version -export const WebRPCVersion = 'v1' +// Webrpc description and code-gen version +export const WebrpcVersion = 'v1' // Schema version of your RIDL schema -export const WebRPCSchemaVersion = 'v0.4.0' +export const WebrpcSchemaVersion = 'v0.4.0' // Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = '5cb74ff169ce80c2e42e65d6bbc98b1daaa0945f' +export const WebrpcSchemaHash = '673a5fa528008c7f9558810fbb24aad978ed7a84' -type WebrpcGenVersions = { - webrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string +// +// Client interface +// + +export interface MetadataClient { + ping(headers?: object, signal?: AbortSignal): Promise + + version(headers?: object, signal?: AbortSignal): Promise + + runtimeStatus(headers?: object, signal?: AbortSignal): Promise + + getTask(req: GetTaskArgs, headers?: object, signal?: AbortSignal): Promise + + getTaskStatus(req: GetTaskStatusArgs, headers?: object, signal?: AbortSignal): Promise + + /** + * Contract Info -- returns contract meta-info for contracts found in registered chain's token-lists + */ + getContractInfo(req: GetContractInfoArgs, headers?: object, signal?: AbortSignal): Promise + + getContractInfoBatch( + req: GetContractInfoBatchArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Find Contract Info across all chains token-lists. Similar to GetContractInfo above, + * but it will traverse all chains and results from all. + */ + findContractInfo(req: FindContractInfoArgs, headers?: object, signal?: AbortSignal): Promise + + /** + * map of contractAddress :: []ContractInfo + */ + findContractInfoBatch( + req: FindContractInfoBatchArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Refresh Contract Info -- refresh contract meta-info + */ + refreshContractInfo( + req: RefreshContractInfoArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + refreshContractInfoBatch( + req: RefreshContractInfoBatchArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Search for contract infos using a query string + */ + searchContractsByQuery( + req: SearchContractsByQueryArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * GetTokenMetadata - fetch token metadata for a particular contract and respective tokenIDs + */ + getTokenMetadata(req: GetTokenMetadataArgs, headers?: object, signal?: AbortSignal): Promise + + /** + * GetTokenMetadataBatch allows you to query the token metadata of a batch of contracts and respective tokenIDs + * where map is contractAddress::[]tokenID => contractAddress::[]TokenMetadata + * + * Note, we limit each request to 50 contracts max and 50 tokens max per contract. + */ + getTokenMetadataBatch( + req: GetTokenMetadataBatchArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * RefreshTokenMetadata allows you to refresh a contract metadata for contract-level and token-level metadata. + */ + refreshTokenMetadata( + req: RefreshTokenMetadataArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Search ERC721 & ERC1155 token metadata by query string 'q' + */ + searchTokenMetadataByQuery( + req: SearchTokenMetadataByQueryArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Search ERC721 & ERC1155 token metadata by filter object 'filter' + * which allows to search by text or properties. + */ + searchTokenMetadata( + req: SearchTokenMetadataArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Search ERC721 & ERC1155 for token IDs by filter object 'filter' + * which allows to search by text or properties. + */ + searchTokenMetadataTokenIDs( + req: SearchTokenMetadataTokenIDsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Get token metadata property filters for a contract address + */ + getTokenMetadataPropertyFilters( + req: GetTokenMetadataPropertyFiltersArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Gets Token Directory supported networks + */ + getTokenDirectoryNetworks( + req: GetTokenDirectoryNetworksArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Gets Token Directory entries + */ + getTokenDirectory( + req: GetTokenDirectoryArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Search in Token Directory + */ + searchTokenDirectory( + req: SearchTokenDirectoryArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Niftyswap querying data + */ + getNiftyswapTokenQuantity( + req: GetNiftyswapTokenQuantityArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * map of tokenID :: quantity + */ + getNiftyswapUnitPrices( + req: GetNiftyswapUnitPricesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * map of tokenID :: price + */ + getNiftyswapUnitPricesWithQuantities( + req: GetNiftyswapUnitPricesWithQuantitiesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise } +export interface CollectionsClient { + createCollection(req: CreateCollectionArgs, headers?: object, signal?: AbortSignal): Promise -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } + getCollection(req: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise - return parseWebrpcGenVersions(headerValue) + listCollections(req: ListCollectionsArgs, headers?: object, signal?: AbortSignal): Promise + + updateCollection(req: UpdateCollectionArgs, headers?: object, signal?: AbortSignal): Promise + + deleteCollection(req: DeleteCollectionArgs, headers?: object, signal?: AbortSignal): Promise + + publishCollection( + req: PublishCollectionArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + unpublishCollection( + req: UnpublishCollectionArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + createContractCollection( + req: CreateContractCollectionArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + getContractCollection( + req: GetContractCollectionArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + listContractCollections( + req: ListContractCollectionsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + updateContractCollection( + req: UpdateContractCollectionArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + deleteContractCollection( + req: DeleteContractCollectionArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + createToken(req: CreateTokenArgs, headers?: object, signal?: AbortSignal): Promise + + getToken(req: GetTokenArgs, headers?: object, signal?: AbortSignal): Promise + + listTokens(req: ListTokensArgs, headers?: object, signal?: AbortSignal): Promise + + updateToken(req: UpdateTokenArgs, headers?: object, signal?: AbortSignal): Promise + + deleteToken(req: DeleteTokenArgs, headers?: object, signal?: AbortSignal): Promise + + createAsset(req: CreateAssetArgs, headers?: object, signal?: AbortSignal): Promise + + getAsset(req: GetAssetArgs, headers?: object, signal?: AbortSignal): Promise + + updateAsset(req: UpdateAssetArgs, headers?: object, signal?: AbortSignal): Promise + + deleteAsset(req: DeleteAssetArgs, headers?: object, signal?: AbortSignal): Promise } +export interface AdminClient { + /** + * ContractInfo + */ + refreshContractInfoUpdatedBefore( + req: RefreshContractInfoUpdatedBeforeArgs, + headers?: object, + signal?: AbortSignal, + ): Promise -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } + /** + * TokenMetadata + */ + refreshTokenMetadataUpdatedBefore( + req: RefreshTokenMetadataUpdatedBeforeArgs, + headers?: object, + signal?: AbortSignal, + ): Promise - const [_, webrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') + /** + * Contract Info Overrides + */ + getContractInfoOverride( + req: GetContractInfoOverrideArgs, + headers?: object, + signal?: AbortSignal, + ): Promise - return { - webrpcGenVersion: webrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } + getContractInfoOverrides( + req: GetContractInfoOverridesArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + addContractInfoOverride( + req: AddContractInfoOverrideArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + updateContractInfoOverride( + req: UpdateContractInfoOverrideArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + removeContractInfoOverride( + req: RemoveContractInfoOverrideArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Token Directory + */ + isInTokenDirectory( + req: IsInTokenDirectoryArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + setTokenDirectoryFeatureIndex( + req: SetTokenDirectoryFeatureIndexArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + addContractToTokenDirectory( + req: AddContractToTokenDirectoryArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + removeContractFromTokenDirectory( + req: RemoveContractFromTokenDirectoryArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + refreshTokenDirectory(headers?: object, signal?: AbortSignal): Promise } // -// Types +// Schema types // export enum ContractType { @@ -366,177 +650,24 @@ export interface Task { result: Array } -export interface Metadata { - ping(headers?: object, signal?: AbortSignal): Promise - version(headers?: object, signal?: AbortSignal): Promise - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - getTask(args: GetTaskArgs, headers?: object, signal?: AbortSignal): Promise - getTaskStatus(args: GetTaskStatusArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Contract Info -- returns contract meta-info for contracts found in registered chain's token-lists - */ - getContractInfo(args: GetContractInfoArgs, headers?: object, signal?: AbortSignal): Promise - getContractInfoBatch( - args: GetContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Find Contract Info across all chains token-lists. Similar to GetContractInfo above, - * but it will traverse all chains and results from all. - */ - findContractInfo(args: FindContractInfoArgs, headers?: object, signal?: AbortSignal): Promise - /** - * map of contractAddress :: []ContractInfo - */ - findContractInfoBatch( - args: FindContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Refresh Contract Info -- refresh contract meta-info - */ - refreshContractInfo( - args: RefreshContractInfoArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - refreshContractInfoBatch( - args: RefreshContractInfoBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Search for contract infos using a query string - */ - searchContractsByQuery( - args: SearchContractsByQueryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * GetTokenMetadata - fetch token metadata for a particular contract and respective tokenIDs - */ - getTokenMetadata(args: GetTokenMetadataArgs, headers?: object, signal?: AbortSignal): Promise - /** - * GetTokenMetadataBatch allows you to query the token metadata of a batch of contracts and respective tokenIDs - * where map is contractAddress::[]tokenID => contractAddress::[]TokenMetadata - * - * Note, we limit each request to 50 contracts max and 50 tokens max per contract. - */ - getTokenMetadataBatch( - args: GetTokenMetadataBatchArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * RefreshTokenMetadata allows you to refresh a contract metadata for contract-level and token-level metadata. - */ - refreshTokenMetadata( - args: RefreshTokenMetadataArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Search ERC721 & ERC1155 token metadata by query string 'q' - */ - searchTokenMetadataByQuery( - args: SearchTokenMetadataByQueryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Search ERC721 & ERC1155 token metadata by filter object 'filter' - * which allows to search by text or properties. - */ - searchTokenMetadata( - args: SearchTokenMetadataArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Search ERC721 & ERC1155 for token IDs by filter object 'filter' - * which allows to search by text or properties. - */ - searchTokenMetadataTokenIDs( - args: SearchTokenMetadataTokenIDsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Get token metadata property filters for a contract address - */ - getTokenMetadataPropertyFilters( - args: GetTokenMetadataPropertyFiltersArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Gets Token Directory supported networks - */ - getTokenDirectoryNetworks( - args: GetTokenDirectoryNetworksArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Gets Token Directory entries - */ - getTokenDirectory( - args: GetTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Search in Token Directory - */ - searchTokenDirectory( - args: SearchTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Niftyswap querying data - */ - getNiftyswapTokenQuantity( - args: GetNiftyswapTokenQuantityArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * map of tokenID :: quantity - */ - getNiftyswapUnitPrices( - args: GetNiftyswapUnitPricesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * map of tokenID :: price - */ - getNiftyswapUnitPricesWithQuantities( - args: GetNiftyswapUnitPricesWithQuantitiesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise -} - export interface PingArgs {} export interface PingReturn { status: boolean } + export interface VersionArgs {} export interface VersionReturn { version: Version } + export interface RuntimeStatusArgs {} export interface RuntimeStatusReturn { status: RuntimeStatus } + export interface GetTaskArgs { taskId: number } @@ -544,6 +675,7 @@ export interface GetTaskArgs { export interface GetTaskReturn { task: Task } + export interface GetTaskStatusArgs { taskId: number } @@ -551,6 +683,7 @@ export interface GetTaskStatusArgs { export interface GetTaskStatusReturn { status?: TaskStatus } + export interface GetContractInfoArgs { chainID: string contractAddress: string @@ -560,6 +693,7 @@ export interface GetContractInfoReturn { contractInfo: ContractInfo taskID?: number } + export interface GetContractInfoBatchArgs { chainID: string contractAddresses: Array @@ -569,6 +703,7 @@ export interface GetContractInfoBatchReturn { contractInfoMap: { [key: string]: ContractInfo } taskID?: number } + export interface FindContractInfoArgs { contractAddress: string } @@ -576,6 +711,7 @@ export interface FindContractInfoArgs { export interface FindContractInfoReturn { contractInfoList: Array } + export interface FindContractInfoBatchArgs { contractAddresses: Array } @@ -583,6 +719,7 @@ export interface FindContractInfoBatchArgs { export interface FindContractInfoBatchReturn { contractInfoByChain: { [key: string]: Array } } + export interface RefreshContractInfoArgs { chainID: string contractAddress: string @@ -591,6 +728,7 @@ export interface RefreshContractInfoArgs { export interface RefreshContractInfoReturn { taskID?: number } + export interface RefreshContractInfoBatchArgs { chainID: string contractAddresses: Array @@ -599,6 +737,7 @@ export interface RefreshContractInfoBatchArgs { export interface RefreshContractInfoBatchReturn { taskID?: number } + export interface SearchContractsByQueryArgs { q: string chainID?: string @@ -611,6 +750,7 @@ export interface SearchContractsByQueryReturn { contractInfo: Array nextPage: Page } + export interface GetTokenMetadataArgs { chainID: string contractAddress: string @@ -621,6 +761,7 @@ export interface GetTokenMetadataReturn { tokenMetadata: Array taskID?: number } + export interface GetTokenMetadataBatchArgs { chainID: string contractTokenMap: { [key: string]: Array } @@ -630,16 +771,18 @@ export interface GetTokenMetadataBatchReturn { contractTokenMetadata: { [key: string]: Array } taskID?: number } + export interface RefreshTokenMetadataArgs { chainID: string contractAddress: string tokenIDs?: Array - refreshAll?: boolean + newMints?: boolean } export interface RefreshTokenMetadataReturn { taskID: number } + export interface SearchTokenMetadataByQueryArgs { q: string chainID?: string @@ -651,6 +794,7 @@ export interface SearchTokenMetadataByQueryReturn { tokenMetadata: Array nextPage: Page } + export interface SearchTokenMetadataArgs { chainID: string contractAddress: string @@ -662,6 +806,7 @@ export interface SearchTokenMetadataReturn { page: Page tokenMetadata: Array } + export interface SearchTokenMetadataTokenIDsArgs { chainID: string contractAddress: string @@ -673,6 +818,7 @@ export interface SearchTokenMetadataTokenIDsReturn { page: Page tokenIDs: Array } + export interface GetTokenMetadataPropertyFiltersArgs { chainID: string contractAddress: string @@ -683,6 +829,7 @@ export interface GetTokenMetadataPropertyFiltersArgs { export interface GetTokenMetadataPropertyFiltersReturn { filters: Array } + export interface GetTokenDirectoryNetworksArgs { includeTestnets?: boolean onlyFeatured?: boolean @@ -692,6 +839,7 @@ export interface GetTokenDirectoryNetworksReturn { chainIDs: Array networks: Array } + export interface GetTokenDirectoryArgs { chainID?: string includeTestnets?: boolean @@ -703,6 +851,7 @@ export interface GetTokenDirectoryReturn { contracts: Array page: Page } + export interface SearchTokenDirectoryArgs { query: string chainID?: number @@ -715,6 +864,7 @@ export interface SearchTokenDirectoryReturn { contracts: Array page: Page } + export interface GetNiftyswapTokenQuantityArgs { chainID: string contractAddress: string @@ -724,6 +874,7 @@ export interface GetNiftyswapTokenQuantityArgs { export interface GetNiftyswapTokenQuantityReturn { quantity: { [key: string]: string } } + export interface GetNiftyswapUnitPricesArgs { chainID: string contractAddress: string @@ -734,6 +885,7 @@ export interface GetNiftyswapUnitPricesArgs { export interface GetNiftyswapUnitPricesReturn { prices: { [key: string]: string } } + export interface GetNiftyswapUnitPricesWithQuantitiesArgs { chainID: string contractAddress: string @@ -745,58 +897,6 @@ export interface GetNiftyswapUnitPricesWithQuantitiesReturn { prices: { [key: string]: GetNiftyswapUnitPricesResponse } } -export interface Collections { - createCollection(args: CreateCollectionArgs, headers?: object, signal?: AbortSignal): Promise - getCollection(args: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise - listCollections(args: ListCollectionsArgs, headers?: object, signal?: AbortSignal): Promise - updateCollection(args: UpdateCollectionArgs, headers?: object, signal?: AbortSignal): Promise - deleteCollection(args: DeleteCollectionArgs, headers?: object, signal?: AbortSignal): Promise - publishCollection( - args: PublishCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - unpublishCollection( - args: UnpublishCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - createContractCollection( - args: CreateContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getContractCollection( - args: GetContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listContractCollections( - args: ListContractCollectionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - updateContractCollection( - args: UpdateContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - deleteContractCollection( - args: DeleteContractCollectionArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - createToken(args: CreateTokenArgs, headers?: object, signal?: AbortSignal): Promise - getToken(args: GetTokenArgs, headers?: object, signal?: AbortSignal): Promise - listTokens(args: ListTokensArgs, headers?: object, signal?: AbortSignal): Promise - updateToken(args: UpdateTokenArgs, headers?: object, signal?: AbortSignal): Promise - deleteToken(args: DeleteTokenArgs, headers?: object, signal?: AbortSignal): Promise - createAsset(args: CreateAssetArgs, headers?: object, signal?: AbortSignal): Promise - getAsset(args: GetAssetArgs, headers?: object, signal?: AbortSignal): Promise - updateAsset(args: UpdateAssetArgs, headers?: object, signal?: AbortSignal): Promise - deleteAsset(args: DeleteAssetArgs, headers?: object, signal?: AbortSignal): Promise -} - export interface CreateCollectionArgs { projectId?: number collection: Collection @@ -805,6 +905,7 @@ export interface CreateCollectionArgs { export interface CreateCollectionReturn { collection: Collection } + export interface GetCollectionArgs { projectId?: number collectionId: number @@ -813,6 +914,7 @@ export interface GetCollectionArgs { export interface GetCollectionReturn { collection: Collection } + export interface ListCollectionsArgs { projectId?: number page?: Page @@ -822,6 +924,7 @@ export interface ListCollectionsReturn { page: Page collections: Array } + export interface UpdateCollectionArgs { projectId?: number collection: Collection @@ -830,6 +933,7 @@ export interface UpdateCollectionArgs { export interface UpdateCollectionReturn { collection: Collection } + export interface DeleteCollectionArgs { projectId?: number collectionId: number @@ -838,6 +942,7 @@ export interface DeleteCollectionArgs { export interface DeleteCollectionReturn { status: boolean } + export interface PublishCollectionArgs { projectId?: number collectionId: number @@ -847,6 +952,7 @@ export interface PublishCollectionArgs { export interface PublishCollectionReturn { collection: Collection } + export interface UnpublishCollectionArgs { projectId?: number collectionId: number @@ -855,6 +961,7 @@ export interface UnpublishCollectionArgs { export interface UnpublishCollectionReturn { collection: Collection } + export interface CreateContractCollectionArgs { projectId: number contractCollection: ContractCollection @@ -863,6 +970,7 @@ export interface CreateContractCollectionArgs { export interface CreateContractCollectionReturn { contractCollection: ContractCollection } + export interface GetContractCollectionArgs { projectId: number chainId: number @@ -872,6 +980,7 @@ export interface GetContractCollectionArgs { export interface GetContractCollectionReturn { contractCollection: ContractCollection } + export interface ListContractCollectionsArgs { projectId: number collectionId?: number @@ -883,6 +992,7 @@ export interface ListContractCollectionsReturn { collections: Array page: Page } + export interface UpdateContractCollectionArgs { projectId: number contractCollection: ContractCollection @@ -891,6 +1001,7 @@ export interface UpdateContractCollectionArgs { export interface UpdateContractCollectionReturn { ok: boolean } + export interface DeleteContractCollectionArgs { projectId: number chainId: number @@ -900,6 +1011,7 @@ export interface DeleteContractCollectionArgs { export interface DeleteContractCollectionReturn { ok: boolean } + export interface CreateTokenArgs { projectId?: number collectionId: number @@ -911,6 +1023,7 @@ export interface CreateTokenReturn { token: TokenMetadata assets: Array } + export interface GetTokenArgs { projectId?: number collectionId: number @@ -921,6 +1034,7 @@ export interface GetTokenReturn { token: TokenMetadata assets: Array } + export interface ListTokensArgs { projectId?: number collectionId: number @@ -931,6 +1045,7 @@ export interface ListTokensReturn { page: Page tokens: Array } + export interface UpdateTokenArgs { projectId?: number collectionId: number @@ -942,6 +1057,7 @@ export interface UpdateTokenArgs { export interface UpdateTokenReturn { token: TokenMetadata } + export interface DeleteTokenArgs { projectId?: number collectionId: number @@ -951,6 +1067,7 @@ export interface DeleteTokenArgs { export interface DeleteTokenReturn { status: boolean } + export interface CreateAssetArgs { projectId?: number asset: Asset @@ -959,6 +1076,7 @@ export interface CreateAssetArgs { export interface CreateAssetReturn { asset: Asset } + export interface GetAssetArgs { projectId?: number assetId: number @@ -967,6 +1085,7 @@ export interface GetAssetArgs { export interface GetAssetReturn { asset: Asset } + export interface UpdateAssetArgs { projectId?: number asset: Asset @@ -975,6 +1094,7 @@ export interface UpdateAssetArgs { export interface UpdateAssetReturn { asset: Asset } + export interface DeleteAssetArgs { projectId?: number assetId: number @@ -984,77 +1104,6 @@ export interface DeleteAssetReturn { status: boolean } -export interface Admin { - /** - * ContractInfo - */ - refreshContractInfoUpdatedBefore( - args: RefreshContractInfoUpdatedBeforeArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * TokenMetadata - */ - refreshTokenMetadataUpdatedBefore( - args: RefreshTokenMetadataUpdatedBeforeArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Contract Info Overrides - */ - getContractInfoOverride( - args: GetContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getContractInfoOverrides( - args: GetContractInfoOverridesArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - addContractInfoOverride( - args: AddContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - updateContractInfoOverride( - args: UpdateContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - removeContractInfoOverride( - args: RemoveContractInfoOverrideArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Token Directory - */ - isInTokenDirectory( - args: IsInTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - setTokenDirectoryFeatureIndex( - args: SetTokenDirectoryFeatureIndexArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - addContractToTokenDirectory( - args: AddContractToTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - removeContractFromTokenDirectory( - args: RemoveContractFromTokenDirectoryArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - refreshTokenDirectory(headers?: object, signal?: AbortSignal): Promise -} - export interface RefreshContractInfoUpdatedBeforeArgs { before: string maxContractNumber: number @@ -1063,6 +1112,7 @@ export interface RefreshContractInfoUpdatedBeforeArgs { export interface RefreshContractInfoUpdatedBeforeReturn { taskIDs: Array } + export interface RefreshTokenMetadataUpdatedBeforeArgs { before: string maxTokenNumber: number @@ -1071,6 +1121,7 @@ export interface RefreshTokenMetadataUpdatedBeforeArgs { export interface RefreshTokenMetadataUpdatedBeforeReturn { taskIDs: Array } + export interface GetContractInfoOverrideArgs { chainID: string contractAddress: string @@ -1079,6 +1130,7 @@ export interface GetContractInfoOverrideArgs { export interface GetContractInfoOverrideReturn { contractInfoOverride: ContractInfoOverride } + export interface GetContractInfoOverridesArgs { chainID?: string page?: Page @@ -1088,6 +1140,7 @@ export interface GetContractInfoOverridesReturn { contractInfoOverrides: Array page: Page } + export interface AddContractInfoOverrideArgs { chainID: string contractAddress: string @@ -1097,6 +1150,7 @@ export interface AddContractInfoOverrideArgs { export interface AddContractInfoOverrideReturn { ok: boolean } + export interface UpdateContractInfoOverrideArgs { chainID: string contractAddress: string @@ -1106,6 +1160,7 @@ export interface UpdateContractInfoOverrideArgs { export interface UpdateContractInfoOverrideReturn { ok: boolean } + export interface RemoveContractInfoOverrideArgs { chainID: string contractAddress: string @@ -1114,6 +1169,7 @@ export interface RemoveContractInfoOverrideArgs { export interface RemoveContractInfoOverrideReturn { ok: boolean } + export interface IsInTokenDirectoryArgs { chainID: string contractAddress: string @@ -1123,6 +1179,7 @@ export interface IsInTokenDirectoryReturn { ok: boolean featureIndex: number } + export interface SetTokenDirectoryFeatureIndexArgs { chainID: string contractAddress: string @@ -1132,6 +1189,7 @@ export interface SetTokenDirectoryFeatureIndexArgs { export interface SetTokenDirectoryFeatureIndexReturn { ok: boolean } + export interface AddContractToTokenDirectoryArgs { chainID: string contractAddress: string @@ -1140,6 +1198,7 @@ export interface AddContractToTokenDirectoryArgs { export interface AddContractToTokenDirectoryReturn { ok: boolean } + export interface RemoveContractFromTokenDirectoryArgs { chainID: string contractAddress: string @@ -1148,6 +1207,7 @@ export interface RemoveContractFromTokenDirectoryArgs { export interface RemoveContractFromTokenDirectoryReturn { ok: boolean } + export interface RefreshTokenDirectoryArgs {} export interface RefreshTokenDirectoryReturn { @@ -1157,7 +1217,8 @@ export interface RefreshTokenDirectoryReturn { // // Client // -export class Metadata implements Metadata { + +export class Metadata implements MetadataClient { protected hostname: string protected fetch: Fetch protected path = '/rpc/Metadata/' @@ -1171,473 +1232,563 @@ export class Metadata implements Metadata { return this.hostname + this.path + name } + queryKey = { + ping: () => ['Metadata', 'ping'] as const, + version: () => ['Metadata', 'version'] as const, + runtimeStatus: () => ['Metadata', 'runtimeStatus'] as const, + getTask: (req: GetTaskArgs) => ['Metadata', 'getTask', req] as const, + getTaskStatus: (req: GetTaskStatusArgs) => ['Metadata', 'getTaskStatus', req] as const, + getContractInfo: (req: GetContractInfoArgs) => ['Metadata', 'getContractInfo', req] as const, + getContractInfoBatch: (req: GetContractInfoBatchArgs) => ['Metadata', 'getContractInfoBatch', req] as const, + findContractInfo: (req: FindContractInfoArgs) => ['Metadata', 'findContractInfo', req] as const, + findContractInfoBatch: (req: FindContractInfoBatchArgs) => ['Metadata', 'findContractInfoBatch', req] as const, + refreshContractInfo: (req: RefreshContractInfoArgs) => ['Metadata', 'refreshContractInfo', req] as const, + refreshContractInfoBatch: (req: RefreshContractInfoBatchArgs) => + ['Metadata', 'refreshContractInfoBatch', req] as const, + searchContractsByQuery: (req: SearchContractsByQueryArgs) => ['Metadata', 'searchContractsByQuery', req] as const, + getTokenMetadata: (req: GetTokenMetadataArgs) => ['Metadata', 'getTokenMetadata', req] as const, + getTokenMetadataBatch: (req: GetTokenMetadataBatchArgs) => ['Metadata', 'getTokenMetadataBatch', req] as const, + refreshTokenMetadata: (req: RefreshTokenMetadataArgs) => ['Metadata', 'refreshTokenMetadata', req] as const, + searchTokenMetadataByQuery: (req: SearchTokenMetadataByQueryArgs) => + ['Metadata', 'searchTokenMetadataByQuery', req] as const, + searchTokenMetadata: (req: SearchTokenMetadataArgs) => ['Metadata', 'searchTokenMetadata', req] as const, + searchTokenMetadataTokenIDs: (req: SearchTokenMetadataTokenIDsArgs) => + ['Metadata', 'searchTokenMetadataTokenIDs', req] as const, + getTokenMetadataPropertyFilters: (req: GetTokenMetadataPropertyFiltersArgs) => + ['Metadata', 'getTokenMetadataPropertyFilters', req] as const, + getTokenDirectoryNetworks: (req: GetTokenDirectoryNetworksArgs) => + ['Metadata', 'getTokenDirectoryNetworks', req] as const, + getTokenDirectory: (req: GetTokenDirectoryArgs) => ['Metadata', 'getTokenDirectory', req] as const, + searchTokenDirectory: (req: SearchTokenDirectoryArgs) => ['Metadata', 'searchTokenDirectory', req] as const, + getNiftyswapTokenQuantity: (req: GetNiftyswapTokenQuantityArgs) => + ['Metadata', 'getNiftyswapTokenQuantity', req] as const, + getNiftyswapUnitPrices: (req: GetNiftyswapUnitPricesArgs) => ['Metadata', 'getNiftyswapUnitPrices', req] as const, + getNiftyswapUnitPricesWithQuantities: (req: GetNiftyswapUnitPricesWithQuantitiesArgs) => + ['Metadata', 'getNiftyswapUnitPricesWithQuantities', req] as const, + } + ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHTTPRequest({}, headers, signal)).then( + return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'PingReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Version'), createHTTPRequest({}, headers, signal)).then( + return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - version: _data.version, - } + return JsonDecode(_data, 'VersionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RuntimeStatus'), createHTTPRequest({}, headers, signal)).then( + return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'RuntimeStatusReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTask = (args: GetTaskArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetTask'), createHTTPRequest(args, headers, signal)).then( + getTask = (req: GetTaskArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetTask'), createHttpRequest(JsonEncode(req, 'GetTaskArgs'), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - task: _data.task, - } + return JsonDecode(_data, 'GetTaskReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getTaskStatus = (args: GetTaskStatusArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetTaskStatus'), createHTTPRequest(args, headers, signal)).then( + getTaskStatus = (req: GetTaskStatusArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('GetTaskStatus'), + createHttpRequest(JsonEncode(req, 'GetTaskStatusArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'GetTaskStatusReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getContractInfo = ( - args: GetContractInfoArgs, + req: GetContractInfoArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetContractInfo'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetContractInfo'), + createHttpRequest(JsonEncode(req, 'GetContractInfoArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractInfo: _data.contractInfo, - taskID: _data.taskID, - } + return JsonDecode(_data, 'GetContractInfoReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getContractInfoBatch = ( - args: GetContractInfoBatchArgs, + req: GetContractInfoBatchArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetContractInfoBatch'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetContractInfoBatch'), + createHttpRequest(JsonEncode(req, 'GetContractInfoBatchArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractInfoMap: <{ [key: string]: ContractInfo }>_data.contractInfoMap, - taskID: _data.taskID, - } + return JsonDecode(_data, 'GetContractInfoBatchReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } findContractInfo = ( - args: FindContractInfoArgs, + req: FindContractInfoArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('FindContractInfo'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('FindContractInfo'), + createHttpRequest(JsonEncode(req, 'FindContractInfoArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractInfoList: >_data.contractInfoList, - } + return JsonDecode(_data, 'FindContractInfoReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } findContractInfoBatch = ( - args: FindContractInfoBatchArgs, + req: FindContractInfoBatchArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('FindContractInfoBatch'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('FindContractInfoBatch'), + createHttpRequest(JsonEncode(req, 'FindContractInfoBatchArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractInfoByChain: <{ [key: string]: Array }>_data.contractInfoByChain, - } + return JsonDecode(_data, 'FindContractInfoBatchReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } refreshContractInfo = ( - args: RefreshContractInfoArgs, + req: RefreshContractInfoArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('RefreshContractInfo'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('RefreshContractInfo'), + createHttpRequest(JsonEncode(req, 'RefreshContractInfoArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - taskID: _data.taskID, - } + return JsonDecode(_data, 'RefreshContractInfoReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } refreshContractInfoBatch = ( - args: RefreshContractInfoBatchArgs, + req: RefreshContractInfoBatchArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('RefreshContractInfoBatch'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('RefreshContractInfoBatch'), + createHttpRequest(JsonEncode(req, 'RefreshContractInfoBatchArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - taskID: _data.taskID, - } + return JsonDecode(_data, 'RefreshContractInfoBatchReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } searchContractsByQuery = ( - args: SearchContractsByQueryArgs, + req: SearchContractsByQueryArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('SearchContractsByQuery'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('SearchContractsByQuery'), + createHttpRequest(JsonEncode(req, 'SearchContractsByQueryArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractInfo: >_data.contractInfo, - nextPage: _data.nextPage, - } + return JsonDecode(_data, 'SearchContractsByQueryReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getTokenMetadata = ( - args: GetTokenMetadataArgs, + req: GetTokenMetadataArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetTokenMetadata'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetTokenMetadata'), + createHttpRequest(JsonEncode(req, 'GetTokenMetadataArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - tokenMetadata: >_data.tokenMetadata, - taskID: _data.taskID, - } + return JsonDecode(_data, 'GetTokenMetadataReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getTokenMetadataBatch = ( - args: GetTokenMetadataBatchArgs, + req: GetTokenMetadataBatchArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetTokenMetadataBatch'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetTokenMetadataBatch'), + createHttpRequest(JsonEncode(req, 'GetTokenMetadataBatchArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractTokenMetadata: <{ [key: string]: Array }>_data.contractTokenMetadata, - taskID: _data.taskID, - } + return JsonDecode(_data, 'GetTokenMetadataBatchReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } refreshTokenMetadata = ( - args: RefreshTokenMetadataArgs, + req: RefreshTokenMetadataArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('RefreshTokenMetadata'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('RefreshTokenMetadata'), + createHttpRequest(JsonEncode(req, 'RefreshTokenMetadataArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - taskID: _data.taskID, - } + return JsonDecode(_data, 'RefreshTokenMetadataReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } searchTokenMetadataByQuery = ( - args: SearchTokenMetadataByQueryArgs, + req: SearchTokenMetadataByQueryArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('SearchTokenMetadataByQuery'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('SearchTokenMetadataByQuery'), + createHttpRequest(JsonEncode(req, 'SearchTokenMetadataByQueryArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - tokenMetadata: >_data.tokenMetadata, - nextPage: _data.nextPage, - } + return JsonDecode(_data, 'SearchTokenMetadataByQueryReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } searchTokenMetadata = ( - args: SearchTokenMetadataArgs, + req: SearchTokenMetadataArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('SearchTokenMetadata'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('SearchTokenMetadata'), + createHttpRequest(JsonEncode(req, 'SearchTokenMetadataArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - tokenMetadata: >_data.tokenMetadata, - } + return JsonDecode(_data, 'SearchTokenMetadataReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } searchTokenMetadataTokenIDs = ( - args: SearchTokenMetadataTokenIDsArgs, + req: SearchTokenMetadataTokenIDsArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('SearchTokenMetadataTokenIDs'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('SearchTokenMetadataTokenIDs'), + createHttpRequest(JsonEncode(req, 'SearchTokenMetadataTokenIDsArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - tokenIDs: >_data.tokenIDs, - } + return JsonDecode(_data, 'SearchTokenMetadataTokenIDsReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getTokenMetadataPropertyFilters = ( - args: GetTokenMetadataPropertyFiltersArgs, + req: GetTokenMetadataPropertyFiltersArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetTokenMetadataPropertyFilters'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetTokenMetadataPropertyFilters'), + createHttpRequest(JsonEncode(req, 'GetTokenMetadataPropertyFiltersArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - filters: >_data.filters, - } + return JsonDecode(_data, 'GetTokenMetadataPropertyFiltersReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getTokenDirectoryNetworks = ( - args: GetTokenDirectoryNetworksArgs, + req: GetTokenDirectoryNetworksArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetTokenDirectoryNetworks'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetTokenDirectoryNetworks'), + createHttpRequest(JsonEncode(req, 'GetTokenDirectoryNetworksArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - chainIDs: >_data.chainIDs, - networks: >_data.networks, - } + return JsonDecode(_data, 'GetTokenDirectoryNetworksReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getTokenDirectory = ( - args: GetTokenDirectoryArgs, + req: GetTokenDirectoryArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetTokenDirectory'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetTokenDirectory'), + createHttpRequest(JsonEncode(req, 'GetTokenDirectoryArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contracts: >_data.contracts, - page: _data.page, - } + return JsonDecode(_data, 'GetTokenDirectoryReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } searchTokenDirectory = ( - args: SearchTokenDirectoryArgs, + req: SearchTokenDirectoryArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('SearchTokenDirectory'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('SearchTokenDirectory'), + createHttpRequest(JsonEncode(req, 'SearchTokenDirectoryArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contracts: >_data.contracts, - page: _data.page, - } + return JsonDecode(_data, 'SearchTokenDirectoryReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getNiftyswapTokenQuantity = ( - args: GetNiftyswapTokenQuantityArgs, + req: GetNiftyswapTokenQuantityArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetNiftyswapTokenQuantity'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetNiftyswapTokenQuantity'), + createHttpRequest(JsonEncode(req, 'GetNiftyswapTokenQuantityArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - quantity: <{ [key: string]: string }>_data.quantity, - } + return JsonDecode(_data, 'GetNiftyswapTokenQuantityReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getNiftyswapUnitPrices = ( - args: GetNiftyswapUnitPricesArgs, + req: GetNiftyswapUnitPricesArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetNiftyswapUnitPrices'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetNiftyswapUnitPrices'), + createHttpRequest(JsonEncode(req, 'GetNiftyswapUnitPricesArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - prices: <{ [key: string]: string }>_data.prices, - } + return JsonDecode(_data, 'GetNiftyswapUnitPricesReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getNiftyswapUnitPricesWithQuantities = ( - args: GetNiftyswapUnitPricesWithQuantitiesArgs, + req: GetNiftyswapUnitPricesWithQuantitiesArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetNiftyswapUnitPricesWithQuantities'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetNiftyswapUnitPricesWithQuantities'), + createHttpRequest(JsonEncode(req, 'GetNiftyswapUnitPricesWithQuantitiesArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - prices: <{ [key: string]: GetNiftyswapUnitPricesResponse }>_data.prices, - } + return JsonDecode( + _data, + 'GetNiftyswapUnitPricesWithQuantitiesReturn', + ) }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } } -export class Collections implements Collections { +export class Collections implements CollectionsClient { protected hostname: string protected fetch: Fetch protected path = '/rpc/Collections/' @@ -1651,372 +1802,451 @@ export class Collections implements Collections { return this.hostname + this.path + name } + queryKey = { + createCollection: (req: CreateCollectionArgs) => ['Collections', 'createCollection', req] as const, + getCollection: (req: GetCollectionArgs) => ['Collections', 'getCollection', req] as const, + listCollections: (req: ListCollectionsArgs) => ['Collections', 'listCollections', req] as const, + updateCollection: (req: UpdateCollectionArgs) => ['Collections', 'updateCollection', req] as const, + deleteCollection: (req: DeleteCollectionArgs) => ['Collections', 'deleteCollection', req] as const, + publishCollection: (req: PublishCollectionArgs) => ['Collections', 'publishCollection', req] as const, + unpublishCollection: (req: UnpublishCollectionArgs) => ['Collections', 'unpublishCollection', req] as const, + createContractCollection: (req: CreateContractCollectionArgs) => + ['Collections', 'createContractCollection', req] as const, + getContractCollection: (req: GetContractCollectionArgs) => ['Collections', 'getContractCollection', req] as const, + listContractCollections: (req: ListContractCollectionsArgs) => + ['Collections', 'listContractCollections', req] as const, + updateContractCollection: (req: UpdateContractCollectionArgs) => + ['Collections', 'updateContractCollection', req] as const, + deleteContractCollection: (req: DeleteContractCollectionArgs) => + ['Collections', 'deleteContractCollection', req] as const, + createToken: (req: CreateTokenArgs) => ['Collections', 'createToken', req] as const, + getToken: (req: GetTokenArgs) => ['Collections', 'getToken', req] as const, + listTokens: (req: ListTokensArgs) => ['Collections', 'listTokens', req] as const, + updateToken: (req: UpdateTokenArgs) => ['Collections', 'updateToken', req] as const, + deleteToken: (req: DeleteTokenArgs) => ['Collections', 'deleteToken', req] as const, + createAsset: (req: CreateAssetArgs) => ['Collections', 'createAsset', req] as const, + getAsset: (req: GetAssetArgs) => ['Collections', 'getAsset', req] as const, + updateAsset: (req: UpdateAssetArgs) => ['Collections', 'updateAsset', req] as const, + deleteAsset: (req: DeleteAssetArgs) => ['Collections', 'deleteAsset', req] as const, + } + createCollection = ( - args: CreateCollectionArgs, + req: CreateCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('CreateCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('CreateCollection'), + createHttpRequest(JsonEncode(req, 'CreateCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } + return JsonDecode(_data, 'CreateCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getCollection = (args: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetCollection'), createHTTPRequest(args, headers, signal)).then( + getCollection = (req: GetCollectionArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('GetCollection'), + createHttpRequest(JsonEncode(req, 'GetCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } + return JsonDecode(_data, 'GetCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } listCollections = ( - args: ListCollectionsArgs, + req: ListCollectionsArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('ListCollections'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('ListCollections'), + createHttpRequest(JsonEncode(req, 'ListCollectionsArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - collections: >_data.collections, - } + return JsonDecode(_data, 'ListCollectionsReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } updateCollection = ( - args: UpdateCollectionArgs, + req: UpdateCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('UpdateCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('UpdateCollection'), + createHttpRequest(JsonEncode(req, 'UpdateCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } + return JsonDecode(_data, 'UpdateCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } deleteCollection = ( - args: DeleteCollectionArgs, + req: DeleteCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('DeleteCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('DeleteCollection'), + createHttpRequest(JsonEncode(req, 'DeleteCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'DeleteCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } publishCollection = ( - args: PublishCollectionArgs, + req: PublishCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('PublishCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('PublishCollection'), + createHttpRequest(JsonEncode(req, 'PublishCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } + return JsonDecode(_data, 'PublishCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } unpublishCollection = ( - args: UnpublishCollectionArgs, + req: UnpublishCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('UnpublishCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('UnpublishCollection'), + createHttpRequest(JsonEncode(req, 'UnpublishCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - collection: _data.collection, - } + return JsonDecode(_data, 'UnpublishCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } createContractCollection = ( - args: CreateContractCollectionArgs, + req: CreateContractCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('CreateContractCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('CreateContractCollection'), + createHttpRequest(JsonEncode(req, 'CreateContractCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractCollection: _data.contractCollection, - } + return JsonDecode(_data, 'CreateContractCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getContractCollection = ( - args: GetContractCollectionArgs, + req: GetContractCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetContractCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetContractCollection'), + createHttpRequest(JsonEncode(req, 'GetContractCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractCollection: _data.contractCollection, - } + return JsonDecode(_data, 'GetContractCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } listContractCollections = ( - args: ListContractCollectionsArgs, + req: ListContractCollectionsArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('ListContractCollections'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('ListContractCollections'), + createHttpRequest(JsonEncode(req, 'ListContractCollectionsArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractCollections: >_data.contractCollections, - collections: >_data.collections, - page: _data.page, - } + return JsonDecode(_data, 'ListContractCollectionsReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } updateContractCollection = ( - args: UpdateContractCollectionArgs, + req: UpdateContractCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('UpdateContractCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('UpdateContractCollection'), + createHttpRequest(JsonEncode(req, 'UpdateContractCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } + return JsonDecode(_data, 'UpdateContractCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } deleteContractCollection = ( - args: DeleteContractCollectionArgs, + req: DeleteContractCollectionArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('DeleteContractCollection'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('DeleteContractCollection'), + createHttpRequest(JsonEncode(req, 'DeleteContractCollectionArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } + return JsonDecode(_data, 'DeleteContractCollectionReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - createToken = (args: CreateTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('CreateToken'), createHTTPRequest(args, headers, signal)).then( + createToken = (req: CreateTokenArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('CreateToken'), + createHttpRequest(JsonEncode(req, 'CreateTokenArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - token: _data.token, - assets: >_data.assets, - } + return JsonDecode(_data, 'CreateTokenReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getToken = (args: GetTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetToken'), createHTTPRequest(args, headers, signal)).then( + getToken = (req: GetTokenArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetToken'), createHttpRequest(JsonEncode(req, 'GetTokenArgs'), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - token: _data.token, - assets: >_data.assets, - } + return JsonDecode(_data, 'GetTokenReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - listTokens = (args: ListTokensArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('ListTokens'), createHTTPRequest(args, headers, signal)).then( + listTokens = (req: ListTokensArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('ListTokens'), + createHttpRequest(JsonEncode(req, 'ListTokensArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - page: _data.page, - tokens: >_data.tokens, - } + return JsonDecode(_data, 'ListTokensReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - updateToken = (args: UpdateTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('UpdateToken'), createHTTPRequest(args, headers, signal)).then( + updateToken = (req: UpdateTokenArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('UpdateToken'), + createHttpRequest(JsonEncode(req, 'UpdateTokenArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - token: _data.token, - } + return JsonDecode(_data, 'UpdateTokenReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - deleteToken = (args: DeleteTokenArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('DeleteToken'), createHTTPRequest(args, headers, signal)).then( + deleteToken = (req: DeleteTokenArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('DeleteToken'), + createHttpRequest(JsonEncode(req, 'DeleteTokenArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'DeleteTokenReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - createAsset = (args: CreateAssetArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('CreateAsset'), createHTTPRequest(args, headers, signal)).then( + createAsset = (req: CreateAssetArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('CreateAsset'), + createHttpRequest(JsonEncode(req, 'CreateAssetArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - asset: _data.asset, - } + return JsonDecode(_data, 'CreateAssetReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - getAsset = (args: GetAssetArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetAsset'), createHTTPRequest(args, headers, signal)).then( + getAsset = (req: GetAssetArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetAsset'), createHttpRequest(JsonEncode(req, 'GetAssetArgs'), headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - asset: _data.asset, - } + return JsonDecode(_data, 'GetAssetReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - updateAsset = (args: UpdateAssetArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('UpdateAsset'), createHTTPRequest(args, headers, signal)).then( + updateAsset = (req: UpdateAssetArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('UpdateAsset'), + createHttpRequest(JsonEncode(req, 'UpdateAssetArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - asset: _data.asset, - } + return JsonDecode(_data, 'UpdateAssetReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } - deleteAsset = (args: DeleteAssetArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('DeleteAsset'), createHTTPRequest(args, headers, signal)).then( + deleteAsset = (req: DeleteAssetArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('DeleteAsset'), + createHttpRequest(JsonEncode(req, 'DeleteAssetArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - status: _data.status, - } + return JsonDecode(_data, 'DeleteAssetReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } } -export class Admin implements Admin { +export class Admin implements AdminClient { protected hostname: string protected fetch: Fetch protected path = '/rpc/Admin/' @@ -2030,243 +2260,294 @@ export class Admin implements Admin { return this.hostname + this.path + name } + queryKey = { + refreshContractInfoUpdatedBefore: (req: RefreshContractInfoUpdatedBeforeArgs) => + ['Admin', 'refreshContractInfoUpdatedBefore', req] as const, + refreshTokenMetadataUpdatedBefore: (req: RefreshTokenMetadataUpdatedBeforeArgs) => + ['Admin', 'refreshTokenMetadataUpdatedBefore', req] as const, + getContractInfoOverride: (req: GetContractInfoOverrideArgs) => ['Admin', 'getContractInfoOverride', req] as const, + getContractInfoOverrides: (req: GetContractInfoOverridesArgs) => + ['Admin', 'getContractInfoOverrides', req] as const, + addContractInfoOverride: (req: AddContractInfoOverrideArgs) => ['Admin', 'addContractInfoOverride', req] as const, + updateContractInfoOverride: (req: UpdateContractInfoOverrideArgs) => + ['Admin', 'updateContractInfoOverride', req] as const, + removeContractInfoOverride: (req: RemoveContractInfoOverrideArgs) => + ['Admin', 'removeContractInfoOverride', req] as const, + isInTokenDirectory: (req: IsInTokenDirectoryArgs) => ['Admin', 'isInTokenDirectory', req] as const, + setTokenDirectoryFeatureIndex: (req: SetTokenDirectoryFeatureIndexArgs) => + ['Admin', 'setTokenDirectoryFeatureIndex', req] as const, + addContractToTokenDirectory: (req: AddContractToTokenDirectoryArgs) => + ['Admin', 'addContractToTokenDirectory', req] as const, + removeContractFromTokenDirectory: (req: RemoveContractFromTokenDirectoryArgs) => + ['Admin', 'removeContractFromTokenDirectory', req] as const, + refreshTokenDirectory: () => ['Admin', 'refreshTokenDirectory'] as const, + } + refreshContractInfoUpdatedBefore = ( - args: RefreshContractInfoUpdatedBeforeArgs, + req: RefreshContractInfoUpdatedBeforeArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('RefreshContractInfoUpdatedBefore'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('RefreshContractInfoUpdatedBefore'), + createHttpRequest(JsonEncode(req, 'RefreshContractInfoUpdatedBeforeArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - taskIDs: >_data.taskIDs, - } + return JsonDecode(_data, 'RefreshContractInfoUpdatedBeforeReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } refreshTokenMetadataUpdatedBefore = ( - args: RefreshTokenMetadataUpdatedBeforeArgs, + req: RefreshTokenMetadataUpdatedBeforeArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('RefreshTokenMetadataUpdatedBefore'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('RefreshTokenMetadataUpdatedBefore'), + createHttpRequest(JsonEncode(req, 'RefreshTokenMetadataUpdatedBeforeArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - taskIDs: >_data.taskIDs, - } + return JsonDecode(_data, 'RefreshTokenMetadataUpdatedBeforeReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getContractInfoOverride = ( - args: GetContractInfoOverrideArgs, + req: GetContractInfoOverrideArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetContractInfoOverride'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetContractInfoOverride'), + createHttpRequest(JsonEncode(req, 'GetContractInfoOverrideArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractInfoOverride: _data.contractInfoOverride, - } + return JsonDecode(_data, 'GetContractInfoOverrideReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } getContractInfoOverrides = ( - args: GetContractInfoOverridesArgs, + req: GetContractInfoOverridesArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('GetContractInfoOverrides'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('GetContractInfoOverrides'), + createHttpRequest(JsonEncode(req, 'GetContractInfoOverridesArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - contractInfoOverrides: >_data.contractInfoOverrides, - page: _data.page, - } + return JsonDecode(_data, 'GetContractInfoOverridesReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } addContractInfoOverride = ( - args: AddContractInfoOverrideArgs, + req: AddContractInfoOverrideArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('AddContractInfoOverride'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('AddContractInfoOverride'), + createHttpRequest(JsonEncode(req, 'AddContractInfoOverrideArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } + return JsonDecode(_data, 'AddContractInfoOverrideReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } updateContractInfoOverride = ( - args: UpdateContractInfoOverrideArgs, + req: UpdateContractInfoOverrideArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('UpdateContractInfoOverride'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('UpdateContractInfoOverride'), + createHttpRequest(JsonEncode(req, 'UpdateContractInfoOverrideArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } + return JsonDecode(_data, 'UpdateContractInfoOverrideReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } removeContractInfoOverride = ( - args: RemoveContractInfoOverrideArgs, + req: RemoveContractInfoOverrideArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('RemoveContractInfoOverride'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('RemoveContractInfoOverride'), + createHttpRequest(JsonEncode(req, 'RemoveContractInfoOverrideArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } + return JsonDecode(_data, 'RemoveContractInfoOverrideReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } isInTokenDirectory = ( - args: IsInTokenDirectoryArgs, + req: IsInTokenDirectoryArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('IsInTokenDirectory'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('IsInTokenDirectory'), + createHttpRequest(JsonEncode(req, 'IsInTokenDirectoryArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - featureIndex: _data.featureIndex, - } + return JsonDecode(_data, 'IsInTokenDirectoryReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } setTokenDirectoryFeatureIndex = ( - args: SetTokenDirectoryFeatureIndexArgs, + req: SetTokenDirectoryFeatureIndexArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('SetTokenDirectoryFeatureIndex'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('SetTokenDirectoryFeatureIndex'), + createHttpRequest(JsonEncode(req, 'SetTokenDirectoryFeatureIndexArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } + return JsonDecode(_data, 'SetTokenDirectoryFeatureIndexReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } addContractToTokenDirectory = ( - args: AddContractToTokenDirectoryArgs, + req: AddContractToTokenDirectoryArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('AddContractToTokenDirectory'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('AddContractToTokenDirectory'), + createHttpRequest(JsonEncode(req, 'AddContractToTokenDirectoryArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } + return JsonDecode(_data, 'AddContractToTokenDirectoryReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } removeContractFromTokenDirectory = ( - args: RemoveContractFromTokenDirectoryArgs, + req: RemoveContractFromTokenDirectoryArgs, headers?: object, signal?: AbortSignal, ): Promise => { - return this.fetch(this.url('RemoveContractFromTokenDirectory'), createHTTPRequest(args, headers, signal)).then( + return this.fetch( + this.url('RemoveContractFromTokenDirectory'), + createHttpRequest(JsonEncode(req, 'RemoveContractFromTokenDirectoryArgs'), headers, signal), + ).then( (res) => { return buildResponse(res).then((_data) => { - return { - ok: _data.ok, - } + return JsonDecode(_data, 'RemoveContractFromTokenDirectoryReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } refreshTokenDirectory = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RefreshTokenDirectory'), createHTTPRequest({}, headers, signal)).then( + return this.fetch(this.url('RefreshTokenDirectory'), createHttpRequest('{}', headers, signal)).then( (res) => { return buildResponse(res).then((_data) => { - return { - taskID: _data.taskID, - } + return JsonDecode(_data, 'RefreshTokenDirectoryReturn') }) }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) }, ) } } -const createHTTPRequest = (body: object = {}, headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } - reqHeaders[WebrpcHeader] = WebrpcHeaderValue - - return { - method: 'POST', - headers: reqHeaders, - body: JSON.stringify(body || {}), - signal, +const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { + const reqHeaders: { [key: string]: string } = { + ...headers, + 'Content-Type': 'application/json', + [WebrpcHeader]: WebrpcHeaderValue, } + return { method: 'POST', headers: reqHeaders, body, signal } } const buildResponse = (res: Response): Promise => { @@ -2275,13 +2556,9 @@ const buildResponse = (res: Response): Promise => { try { data = JSON.parse(text) } catch (error) { - let message = '' - if (error instanceof Error) { - message = error.message - } throw WebrpcBadResponseError.new({ status: res.status, - cause: `JSON.parse(): ${message}: response text: ${text}`, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, }) } if (!res.ok) { @@ -2292,426 +2569,409 @@ const buildResponse = (res: Response): Promise => { }) } +export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise + +export const JsonEncode = (obj: T, _typ: string = ''): string => { + return JSON.stringify(obj) +} + +export const JsonDecode = (data: string | any, _typ: string = ''): T => { + let parsed: any = data + if (typeof data === 'string') { + try { + parsed = JSON.parse(data) + } catch (err) { + throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) + } + } + return parsed as T +} + // // Errors // +type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } + export class WebrpcError extends Error { - name: string code: number - message: string status: number - cause?: string - - /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ - msg: string - - constructor(name: string, code: number, message: string, status: number, cause?: string) { - super(message) - this.name = name || 'WebrpcError' - this.code = typeof code === 'number' ? code : 0 - this.message = message || `endpoint error ${this.code}` - this.msg = this.message - this.status = typeof status === 'number' ? status : 0 - this.cause = cause + + constructor(error: WebrpcErrorParams = {}) { + super(error.message) + this.name = error.name || 'WebrpcEndpointError' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcError.prototype) } static new(payload: any): WebrpcError { - return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) + return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) } } -// Webrpc errors - export class WebrpcEndpointError extends WebrpcError { - constructor( - name: string = 'WebrpcEndpoint', - code: number = 0, - message: string = `endpoint error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcEndpoint' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcEndpointError.prototype) } } export class WebrpcRequestFailedError extends WebrpcError { - constructor( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = `request failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcRequestFailed' + this.code = typeof error.code === 'number' ? error.code : -1 + this.message = error.message || `request failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) } } export class WebrpcBadRouteError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = `bad route`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRoute' + this.code = typeof error.code === 'number' ? error.code : -2 + this.message = error.message || `bad route` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) } } export class WebrpcBadMethodError extends WebrpcError { - constructor( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = `bad method`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadMethod' + this.code = typeof error.code === 'number' ? error.code : -3 + this.message = error.message || `bad method` + this.status = typeof error.status === 'number' ? error.status : 405 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) } } export class WebrpcBadRequestError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = `bad request`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRequest' + this.code = typeof error.code === 'number' ? error.code : -4 + this.message = error.message || `bad request` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) } } export class WebrpcBadResponseError extends WebrpcError { - constructor( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = `bad response`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadResponse' + this.code = typeof error.code === 'number' ? error.code : -5 + this.message = error.message || `bad response` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) } } export class WebrpcServerPanicError extends WebrpcError { - constructor( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = `server panic`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcServerPanic' + this.code = typeof error.code === 'number' ? error.code : -6 + this.message = error.message || `server panic` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) } } export class WebrpcInternalErrorError extends WebrpcError { - constructor( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = `internal error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcInternalError' + this.code = typeof error.code === 'number' ? error.code : -7 + this.message = error.message || `internal error` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) } } -export class WebrpcClientDisconnectedError extends WebrpcError { - constructor( - name: string = 'WebrpcClientDisconnected', - code: number = -8, - message: string = `client disconnected`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype) +export class WebrpcClientAbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcClientAborted' + this.code = typeof error.code === 'number' ? error.code : -8 + this.message = error.message || `request aborted by client` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) } } export class WebrpcStreamLostError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = `stream lost`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamLost' + this.code = typeof error.code === 'number' ? error.code : -9 + this.message = error.message || `stream lost` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) } } export class WebrpcStreamFinishedError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = `stream finished`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamFinished' + this.code = typeof error.code === 'number' ? error.code : -10 + this.message = error.message || `stream finished` + this.status = typeof error.status === 'number' ? error.status : 200 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) } } +// // Schema errors +// export class UnauthorizedError extends WebrpcError { - constructor( - name: string = 'Unauthorized', - code: number = 1000, - message: string = `Unauthorized access`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unauthorized' + this.code = typeof error.code === 'number' ? error.code : 1000 + this.message = error.message || `Unauthorized access` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, UnauthorizedError.prototype) } } export class PermissionDeniedError extends WebrpcError { - constructor( - name: string = 'PermissionDenied', - code: number = 1001, - message: string = `Permission denied`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'PermissionDenied' + this.code = typeof error.code === 'number' ? error.code : 1001 + this.message = error.message || `Permission denied` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, PermissionDeniedError.prototype) } } export class SessionExpiredError extends WebrpcError { - constructor( - name: string = 'SessionExpired', - code: number = 1002, - message: string = `Session expired`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'SessionExpired' + this.code = typeof error.code === 'number' ? error.code : 1002 + this.message = error.message || `Session expired` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, SessionExpiredError.prototype) } } export class MethodNotFoundError extends WebrpcError { - constructor( - name: string = 'MethodNotFound', - code: number = 1003, - message: string = `Method not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MethodNotFound' + this.code = typeof error.code === 'number' ? error.code : 1003 + this.message = error.message || `Method not found` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, MethodNotFoundError.prototype) } } export class RequestConflictError extends WebrpcError { - constructor( - name: string = 'RequestConflict', - code: number = 1004, - message: string = `Conflict with target resource`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RequestConflict' + this.code = typeof error.code === 'number' ? error.code : 1004 + this.message = error.message || `Conflict with target resource` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, RequestConflictError.prototype) } } export class FailError extends WebrpcError { - constructor( - name: string = 'Fail', - code: number = 1005, - message: string = `Request Failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Fail' + this.code = typeof error.code === 'number' ? error.code : 1005 + this.message = error.message || `Request Failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, FailError.prototype) } } export class GeoblockedError extends WebrpcError { - constructor( - name: string = 'Geoblocked', - code: number = 1006, - message: string = `Geoblocked region`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Geoblocked' + this.code = typeof error.code === 'number' ? error.code : 1006 + this.message = error.message || `Geoblocked region` + this.status = typeof error.status === 'number' ? error.status : 451 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, GeoblockedError.prototype) } } export class TaskFailedError extends WebrpcError { - constructor( - name: string = 'TaskFailed', - code: number = 1007, - message: string = `Task failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'TaskFailed' + this.code = typeof error.code === 'number' ? error.code : 1007 + this.message = error.message || `Task failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, TaskFailedError.prototype) } } export class DeprecatedError extends WebrpcError { - constructor( - name: string = 'Deprecated', - code: number = 1008, - message: string = `RPC method is deprecated`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Deprecated' + this.code = typeof error.code === 'number' ? error.code : 1008 + this.message = error.message || `RPC method is deprecated` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, DeprecatedError.prototype) } } export class TimeoutError extends WebrpcError { - constructor( - name: string = 'Timeout', - code: number = 2000, - message: string = `Request timed out`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Timeout' + this.code = typeof error.code === 'number' ? error.code : 2000 + this.message = error.message || `Request timed out` + this.status = typeof error.status === 'number' ? error.status : 408 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, TimeoutError.prototype) } } export class InvalidArgumentError extends WebrpcError { - constructor( - name: string = 'InvalidArgument', - code: number = 2001, - message: string = `Invalid argument`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidArgument' + this.code = typeof error.code === 'number' ? error.code : 2001 + this.message = error.message || `Invalid argument` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, InvalidArgumentError.prototype) } } export class RequiredArgumentError extends WebrpcError { - constructor( - name: string = 'RequiredArgument', - code: number = 2002, - message: string = `Required argument missing`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RequiredArgument' + this.code = typeof error.code === 'number' ? error.code : 2002 + this.message = error.message || `Required argument missing` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, RequiredArgumentError.prototype) } } export class QueryFailedError extends WebrpcError { - constructor( - name: string = 'QueryFailed', - code: number = 2003, - message: string = `Query failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QueryFailed' + this.code = typeof error.code === 'number' ? error.code : 2003 + this.message = error.message || `Query failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, QueryFailedError.prototype) } } export class ValidationFailedError extends WebrpcError { - constructor( - name: string = 'ValidationFailed', - code: number = 2004, - message: string = `Validation failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ValidationFailed' + this.code = typeof error.code === 'number' ? error.code : 2004 + this.message = error.message || `Validation failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, ValidationFailedError.prototype) } } export class RateLimitedError extends WebrpcError { - constructor( - name: string = 'RateLimited', - code: number = 2005, - message: string = `Rate limited`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RateLimited' + this.code = typeof error.code === 'number' ? error.code : 2005 + this.message = error.message || `Rate limited` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, RateLimitedError.prototype) } } export class NotFoundError extends WebrpcError { - constructor( - name: string = 'NotFound', - code: number = 3000, - message: string = `Resource not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NotFound' + this.code = typeof error.code === 'number' ? error.code : 3000 + this.message = error.message || `Resource not found` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, NotFoundError.prototype) } } export class ProjectNotFoundError extends WebrpcError { - constructor( - name: string = 'ProjectNotFound', - code: number = 3002, - message: string = `Project not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ProjectNotFound' + this.code = typeof error.code === 'number' ? error.code : 3002 + this.message = error.message || `Project not found` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, ProjectNotFoundError.prototype) } } export class ChainNotFoundError extends WebrpcError { - constructor( - name: string = 'ChainNotFound', - code: number = 3003, - message: string = `Chain not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ChainNotFound' + this.code = typeof error.code === 'number' ? error.code : 3003 + this.message = error.message || `Chain not found` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, ChainNotFoundError.prototype) } } export class TokenDirectoryDisabledError extends WebrpcError { - constructor( - name: string = 'TokenDirectoryDisabled', - code: number = 4001, - message: string = `Token Directory is disabled`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'TokenDirectoryDisabled' + this.code = typeof error.code === 'number' ? error.code : 4001 + this.message = error.message || `Token Directory is disabled` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause Object.setPrototypeOf(this, TokenDirectoryDisabledError.prototype) } } @@ -2725,7 +2985,7 @@ export enum errors { WebrpcBadResponse = 'WebrpcBadResponse', WebrpcServerPanic = 'WebrpcServerPanic', WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', + WebrpcClientAborted = 'WebrpcClientAborted', WebrpcStreamLost = 'WebrpcStreamLost', WebrpcStreamFinished = 'WebrpcStreamFinished', Unauthorized = 'Unauthorized', @@ -2758,7 +3018,7 @@ export enum WebrpcErrorCodes { WebrpcBadResponse = -5, WebrpcServerPanic = -6, WebrpcInternalError = -7, - WebrpcClientDisconnected = -8, + WebrpcClientAborted = -8, WebrpcStreamLost = -9, WebrpcStreamFinished = -10, Unauthorized = 1000, @@ -2791,7 +3051,7 @@ export const webrpcErrorByCode: { [code: number]: any } = { [-5]: WebrpcBadResponseError, [-6]: WebrpcServerPanicError, [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, + [-8]: WebrpcClientAbortedError, [-9]: WebrpcStreamLostError, [-10]: WebrpcStreamFinishedError, [1000]: UnauthorizedError, @@ -2815,4 +3075,58 @@ export const webrpcErrorByCode: { [code: number]: any } = { [4001]: TokenDirectoryDisabledError, } -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise +// +// Webrpc +// + +export const WebrpcHeader = 'Webrpc' + +export const WebrpcHeaderValue = 'webrpc@v0.31.0;gen-typescript@v0.22.5;sequence-metadata@v0.4.0' + +type WebrpcGenVersions = { + WebrpcGenVersion: string + codeGenName: string + codeGenVersion: string + schemaName: string + schemaVersion: string +} + +export function VersionFromHeader(headers: Headers): WebrpcGenVersions { + const headerValue = headers.get(WebrpcHeader) + if (!headerValue) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + } + } + + return parseWebrpcGenVersions(headerValue) +} + +function parseWebrpcGenVersions(header: string): WebrpcGenVersions { + const versions = header.split(';') + if (versions.length < 3) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + } + } + + const [_, WebrpcGenVersion] = versions[0]!.split('@') + const [codeGenName, codeGenVersion] = versions[1]!.split('@') + const [schemaName, schemaVersion] = versions[2]!.split('@') + + return { + WebrpcGenVersion: WebrpcGenVersion ?? '', + codeGenName: codeGenName ?? '', + codeGenVersion: codeGenVersion ?? '', + schemaName: schemaName ?? '', + schemaVersion: schemaVersion ?? '', + } +} diff --git a/packages/services/relayer/CHANGELOG.md b/packages/services/relayer/CHANGELOG.md index c35614659b..00efdf828f 100644 --- a/packages/services/relayer/CHANGELOG.md +++ b/packages/services/relayer/CHANGELOG.md @@ -1,5 +1,53 @@ # @0xsequence/relayer +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.0-beta.6 + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.0-beta.5 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.0-beta.4 + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.0-beta.3 + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.0-beta.2 + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.0-beta.1 + ## 2.3.8 ### Patch Changes @@ -2476,7 +2524,6 @@ - relayer: fix Relayer.wait() interface The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - delay: the polling interval, i.e. the time to wait between requests - maxFails: the maximum number of hard failures to tolerate before giving up @@ -3139,7 +3186,6 @@ ### Minor Changes - major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - separate per-chain indexer service, API no longer handles indexing - single contract metadata service, API no longer serves metadata diff --git a/packages/services/relayer/hardhat.config.js b/packages/services/relayer/hardhat.config.js deleted file mode 100644 index fd760378b1..0000000000 --- a/packages/services/relayer/hardhat.config.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - solidity: '0.7.6', - - networks: { - hardhat: { - chainId: 31337, - accounts: { - mnemonic: 'ripple axis someone ridge uniform wrist prosper there frog rate olympic knee', - }, - }, - }, -} diff --git a/packages/services/relayer/package.json b/packages/services/relayer/package.json index 55bf3f7f2a..9e49fe2995 100644 --- a/packages/services/relayer/package.json +++ b/packages/services/relayer/package.json @@ -1,12 +1,12 @@ { "name": "@0xsequence/relayer", - "version": "3.0.0", + "version": "3.0.0-beta.6", "type": "module", "publishConfig": { "access": "public" }, "description": "relayer sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/relayer", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/relayer", "author": "Sequence Platforms Inc.", "license": "Apache-2.0", "scripts": { @@ -27,7 +27,14 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3" + "@types/node": "^25.0.2", + "typescript": "^5.9.3", + "vitest": "^4.0.15" + }, + "dependencies": { + "@0xsequence/wallet-primitives": "workspace:^", + "mipd": "^0.0.7", + "ox": "^0.9.17", + "viem": "^2.40.3" } } diff --git a/packages/services/relayer/src/index.ts b/packages/services/relayer/src/index.ts index 4859839b55..dc28bfc771 100644 --- a/packages/services/relayer/src/index.ts +++ b/packages/services/relayer/src/index.ts @@ -1 +1,3 @@ -export * from './rpc-relayer/index.js' +export * as Relayer from './relayer/index.js' +export * as RpcRelayerGen from './relayer/rpc-relayer/relayer.gen.js' +export * as Preconditions from './preconditions/index.js' diff --git a/packages/services/relayer/src/local-relayer.ts b/packages/services/relayer/src/local-relayer.ts deleted file mode 100644 index 29850be344..0000000000 --- a/packages/services/relayer/src/local-relayer.ts +++ /dev/null @@ -1,125 +0,0 @@ -// import { ethers } from 'ethers' -// import { logger } from '@0xsequence/utils' -// import { FeeOption, FeeQuote, proto, Relayer } from '.' -// import { ProviderRelayer, ProviderRelayerOptions } from './provider-relayer' -// import { commons } from '@0xsequence/core' - -// export type LocalRelayerOptions = Omit & { -// signer: ethers.Signer -// } - -// export function isLocalRelayerOptions(obj: any): obj is LocalRelayerOptions { -// return typeof obj === 'object' && isAbstractSigner(obj.signer) -// } - -// export class LocalRelayer extends ProviderRelayer implements Relayer { -// private signer: ethers.Signer -// private txnOptions: ethers.TransactionRequest - -// constructor(options: LocalRelayerOptions | ethers.AbstractSigner) { -// super(isAbstractSigner(options) ? { provider: options.provider! } : { ...options, provider: options.signer.provider! }) -// this.signer = isAbstractSigner(options) ? options : options.signer -// if (!this.signer.provider) throw new Error('Signer must have a provider') -// } - -// async getFeeOptions(_address: string, ..._transactions: commons.transaction.Transaction[]): Promise<{ options: FeeOption[] }> { -// return { options: [] } -// } - -// async getFeeOptionsRaw( -// _entrypoint: string, -// _data: ethers.BytesLike, -// _options?: { -// simulate?: boolean -// } -// ): Promise<{ options: FeeOption[] }> { -// return { options: [] } -// } - -// async gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise { -// const { options } = await this.getFeeOptions(address, ...transactions) -// return options -// } - -// setTransactionOptions(transactionRequest: ethers.TransactionRequest) { -// this.txnOptions = transactionRequest -// } - -// async relay( -// signedTxs: commons.transaction.IntendedTransactionBundle, -// quote?: FeeQuote, -// waitForReceipt: boolean = true -// ): Promise> { -// if (quote !== undefined) { -// logger.warn(`LocalRelayer doesn't accept fee quotes`) -// } - -// const data = commons.transaction.encodeBundleExecData(signedTxs) - -// // TODO: think about computing gas limit individually, summing together and passing across -// // NOTE: we expect that all txns have set their gasLimit ahead of time through proper estimation -// // const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum + tx.gasLimit, 0n) -// // txRequest.gasLimit = gasLimit - -// const responsePromise = this.signer.sendTransaction({ -// to: signedTxs.entrypoint, -// data, -// ...this.txnOptions, -// gasLimit: 9000000 -// }) - -// if (waitForReceipt) { -// const response: commons.transaction.TransactionResponse = await responsePromise -// response.receipt = await response.wait() -// return response -// } else { -// return responsePromise -// } -// } - -// async getMetaTransactions( -// projectId: number, -// page?: proto.Page -// ): Promise<{ -// page: proto.Page -// transactions: proto.MetaTxnLog[] -// }> { -// return { page: { page: 0, pageSize: 100 }, transactions: [] } -// } - -// async getTransactionCost( -// projectId: number, -// from: string, -// to: string -// ): Promise<{ -// cost: number -// }> { -// return { cost: 0 } -// } - -// async listGasSponsors(args: proto.ListGasSponsorsArgs): Promise { -// return { page: { page: 0, pageSize: 100 }, gasSponsors: [] } -// } - -// async addGasSponsor(args: proto.AddGasSponsorArgs): Promise { -// return { status: true, gasSponsor: {} as proto.GasSponsor } -// } - -// async updateGasSponsor(args: proto.UpdateGasSponsorArgs): Promise { -// return { status: true, gasSponsor: {} as proto.GasSponsor } -// } - -// async removeGasSponsor(args: proto.RemoveGasSponsorArgs): Promise { -// return { status: true } -// } -// } - -// function isAbstractSigner(signer: any): signer is ethers.AbstractSigner { -// return ( -// signer && -// typeof signer === 'object' && -// typeof signer.provider === 'object' && -// typeof signer.getAddress === 'function' && -// typeof signer.connect === 'function' -// ) -// } diff --git a/packages/wallet/core/src/preconditions/codec.ts b/packages/services/relayer/src/preconditions/codec.ts similarity index 73% rename from packages/wallet/core/src/preconditions/codec.ts rename to packages/services/relayer/src/preconditions/codec.ts index a6c2a13f7e..74f83154be 100644 --- a/packages/wallet/core/src/preconditions/codec.ts +++ b/packages/services/relayer/src/preconditions/codec.ts @@ -10,12 +10,15 @@ import { Erc1155ApprovalPrecondition, } from './types.js' -export interface IntentPrecondition { +export interface TransactionPrecondition { type: string - data: string + chainId: number + ownerAddress: string + tokenAddress: string + minAmount: bigint } -export function decodePreconditions(preconditions: IntentPrecondition[]): Precondition[] { +export function decodePreconditions(preconditions: TransactionPrecondition[]): Precondition[] { const decodedPreconditions: Precondition[] = [] for (const p of preconditions) { @@ -28,7 +31,7 @@ export function decodePreconditions(preconditions: IntentPrecondition[]): Precon return decodedPreconditions } -export function decodePrecondition(p: IntentPrecondition): Precondition | undefined { +export function decodePrecondition(p: TransactionPrecondition): Precondition | undefined { if (!p) { return undefined } @@ -36,70 +39,64 @@ export function decodePrecondition(p: IntentPrecondition): Precondition | undefi let precondition: Precondition | undefined try { - const data = JSON.parse(p.data) - switch (p.type) { case 'native-balance': - precondition = new NativeBalancePrecondition( - Address.from(data.address), - data.min ? BigInt(data.min) : undefined, - data.max ? BigInt(data.max) : undefined, - ) + precondition = new NativeBalancePrecondition(Address.from(p.ownerAddress), p.minAmount, undefined) break case 'erc20-balance': precondition = new Erc20BalancePrecondition( - Address.from(data.address), - Address.from(data.token), - data.min ? BigInt(data.min) : undefined, - data.max ? BigInt(data.max) : undefined, + Address.from(p.ownerAddress), + Address.from(p.tokenAddress), + p.minAmount, + undefined, ) break case 'erc20-approval': precondition = new Erc20ApprovalPrecondition( - Address.from(data.address), - Address.from(data.token), - Address.from(data.operator), - BigInt(data.min), + Address.from(p.ownerAddress), + Address.from(p.tokenAddress), + Address.from(p.ownerAddress), + p.minAmount, ) break case 'erc721-ownership': precondition = new Erc721OwnershipPrecondition( - Address.from(data.address), - Address.from(data.token), - BigInt(data.tokenId), - data.owned, + Address.from(p.ownerAddress), + Address.from(p.tokenAddress), + BigInt(0), + true, ) break case 'erc721-approval': precondition = new Erc721ApprovalPrecondition( - Address.from(data.address), - Address.from(data.token), - BigInt(data.tokenId), - Address.from(data.operator), + Address.from(p.ownerAddress), + Address.from(p.tokenAddress), + BigInt(0), + Address.from(p.ownerAddress), ) break case 'erc1155-balance': precondition = new Erc1155BalancePrecondition( - Address.from(data.address), - Address.from(data.token), - BigInt(data.tokenId), - data.min ? BigInt(data.min) : undefined, - data.max ? BigInt(data.max) : undefined, + Address.from(p.ownerAddress), + Address.from(p.tokenAddress), + BigInt(0), + p.minAmount, + undefined, ) break case 'erc1155-approval': precondition = new Erc1155ApprovalPrecondition( - Address.from(data.address), - Address.from(data.token), - BigInt(data.tokenId), - Address.from(data.operator), - BigInt(data.min), + Address.from(p.ownerAddress), + Address.from(p.tokenAddress), + BigInt(0), + Address.from(p.ownerAddress), + p.minAmount, ) break diff --git a/packages/wallet/core/src/preconditions/index.ts b/packages/services/relayer/src/preconditions/index.ts similarity index 100% rename from packages/wallet/core/src/preconditions/index.ts rename to packages/services/relayer/src/preconditions/index.ts diff --git a/packages/wallet/core/src/preconditions/selectors.ts b/packages/services/relayer/src/preconditions/selectors.ts similarity index 57% rename from packages/wallet/core/src/preconditions/selectors.ts rename to packages/services/relayer/src/preconditions/selectors.ts index 42b8cbb3b8..d5985a8622 100644 --- a/packages/wallet/core/src/preconditions/selectors.ts +++ b/packages/services/relayer/src/preconditions/selectors.ts @@ -1,20 +1,15 @@ import { Precondition, NativeBalancePrecondition, Erc20BalancePrecondition } from './types.js' -import { IntentPrecondition, decodePreconditions } from './codec.js' +import { TransactionPrecondition, decodePreconditions } from './codec.js' -export function extractChainID(precondition: IntentPrecondition): number | undefined { +export function extractChainID(precondition: TransactionPrecondition): number | undefined { if (!precondition) { return undefined } - try { - const data = JSON.parse(precondition.data) - return data.chainID ? Number(data.chainID) : undefined - } catch (e) { - return undefined - } + return precondition.chainId } -export function extractSupportedPreconditions(preconditions: IntentPrecondition[]): Precondition[] { +export function extractSupportedPreconditions(preconditions: TransactionPrecondition[]): Precondition[] { if (!preconditions || preconditions.length === 0) { return [] } @@ -22,7 +17,9 @@ export function extractSupportedPreconditions(preconditions: IntentPrecondition[ return decodePreconditions(preconditions) } -export function extractNativeBalancePreconditions(preconditions: IntentPrecondition[]): NativeBalancePrecondition[] { +export function extractNativeBalancePreconditions( + preconditions: TransactionPrecondition[], +): NativeBalancePrecondition[] { if (!preconditions || preconditions.length === 0) { return [] } @@ -31,7 +28,7 @@ export function extractNativeBalancePreconditions(preconditions: IntentPrecondit return decoded.filter((p): p is NativeBalancePrecondition => p.type() === 'native-balance') } -export function extractERC20BalancePreconditions(preconditions: IntentPrecondition[]): Erc20BalancePrecondition[] { +export function extractERC20BalancePreconditions(preconditions: TransactionPrecondition[]): Erc20BalancePrecondition[] { if (!preconditions || preconditions.length === 0) { return [] } diff --git a/packages/wallet/core/src/preconditions/types.ts b/packages/services/relayer/src/preconditions/types.ts similarity index 100% rename from packages/wallet/core/src/preconditions/types.ts rename to packages/services/relayer/src/preconditions/types.ts diff --git a/packages/services/relayer/src/provider-relayer.ts b/packages/services/relayer/src/provider-relayer.ts deleted file mode 100644 index 85e9257f24..0000000000 --- a/packages/services/relayer/src/provider-relayer.ts +++ /dev/null @@ -1,284 +0,0 @@ -// import { ethers } from 'ethers' -// import { walletContracts } from '@0xsequence/abi' -// import { FeeOption, FeeQuote, proto, Relayer, SimulateResult } from '.' -// import { logger, Optionals } from '@0xsequence/utils' -// import { commons } from '@0xsequence/core' - -// const DEFAULT_GAS_LIMIT = 800000n - -// export interface ProviderRelayerOptions { -// provider: ethers.Provider -// waitPollRate?: number -// deltaBlocksLog?: number -// fromBlockLog?: number -// } - -// export const ProviderRelayerDefaults: Required> = { -// waitPollRate: 1000, -// deltaBlocksLog: 12, -// fromBlockLog: -1024 -// } - -// export function isProviderRelayerOptions(obj: any): obj is ProviderRelayerOptions { -// return typeof obj === 'object' && isAbstractProvider(obj.provider) -// } - -// export abstract class ProviderRelayer implements Relayer { -// public provider: ethers.Provider -// public waitPollRate: number -// public deltaBlocksLog: number -// public fromBlockLog: number - -// constructor(options: ProviderRelayerOptions) { -// const opts = { ...ProviderRelayerDefaults, ...options } - -// this.provider = opts.provider -// this.waitPollRate = opts.waitPollRate -// this.deltaBlocksLog = opts.deltaBlocksLog -// this.fromBlockLog = opts.fromBlockLog -// } - -// abstract getFeeOptions( -// address: string, -// ...transactions: commons.transaction.Transaction[] -// ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> - -// abstract getFeeOptionsRaw( -// entrypoint: string, -// data: ethers.BytesLike, -// options?: { -// simulate?: boolean -// } -// ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> - -// abstract gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise - -// abstract relay( -// signedTxs: commons.transaction.IntendedTransactionBundle, -// quote?: FeeQuote, -// waitForReceipt?: boolean -// ): Promise - -// abstract getTransactionCost( -// projectId: number, -// from: string, -// to: string -// ): Promise<{ -// cost: number -// }> - -// abstract getMetaTransactions( -// projectId: number, -// page?: proto.Page -// ): Promise<{ -// page: proto.Page -// transactions: proto.MetaTxnLog[] -// }> - -// abstract listGasSponsors(args: proto.ListGasSponsorsArgs): Promise - -// abstract addGasSponsor(args: proto.AddGasSponsorArgs): Promise - -// abstract updateGasSponsor(args: proto.UpdateGasSponsorArgs): Promise - -// abstract removeGasSponsor(args: proto.RemoveGasSponsorArgs): Promise - -// async simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise { -// return ( -// await Promise.all( -// transactions.map(async tx => { -// // Respect gasLimit request of the transaction (as long as its not 0) -// if (tx.gasLimit && BigInt(tx.gasLimit || 0) !== 0n) { -// return tx.gasLimit -// } - -// // Fee can't be estimated locally for delegateCalls -// if (tx.delegateCall) { -// return DEFAULT_GAS_LIMIT -// } - -// // Fee can't be estimated for self-called if wallet hasn't been deployed -// if (tx.to === wallet && (await this.provider.getCode(wallet).then(code => ethers.getBytes(code).length === 0))) { -// return DEFAULT_GAS_LIMIT -// } - -// if (!this.provider) { -// throw new Error('signer.provider is not set, but is required') -// } - -// // TODO: If the wallet address has been deployed, gas limits can be -// // estimated with more accurately by using self-calls with the batch transactions one by one -// return this.provider.estimateGas({ -// from: wallet, -// to: tx.to, -// data: tx.data, -// value: tx.value -// }) -// }) -// ) -// ).map(gasLimit => ({ -// executed: true, -// succeeded: true, -// gasUsed: Number(gasLimit), -// gasLimit: Number(gasLimit) -// })) -// } - -// async getNonce(address: string, space?: ethers.BigNumberish, blockTag?: ethers.BlockTag): Promise { -// if (!this.provider) { -// throw new Error('provider is not set') -// } - -// if ((await this.provider.getCode(address)) === '0x') { -// return 0 -// } - -// if (space === undefined) { -// space = 0 -// } - -// const module = new ethers.Contract(address, walletContracts.mainModule.abi, this.provider) -// const nonce = await module.readNonce(space, { blockTag: blockTag }) -// return commons.transaction.encodeNonce(space, nonce) -// } - -// async wait( -// metaTxnId: string | commons.transaction.SignedTransactionBundle, -// timeoutDuration?: number, -// delay: number = this.waitPollRate, -// maxFails: number = 5 -// ): Promise { -// if (typeof metaTxnId !== 'string') { -// metaTxnId = commons.transaction.intendedTransactionID(metaTxnId) -// } - -// let timedOut = false - -// const retry = async (f: () => Promise, errorMessage: string): Promise => { -// let fails = 0 - -// while (!timedOut) { -// try { -// return await f() -// } catch (error) { -// fails++ - -// if (maxFails !== undefined && fails >= maxFails) { -// logger.error(`giving up after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`, error) -// throw error -// } else { -// logger.warn(`attempt #${fails} failed${errorMessage ? `: ${errorMessage}` : ''}`, error) -// } -// } - -// if (delay > 0) { -// await new Promise(resolve => setTimeout(resolve, delay)) -// } -// } - -// throw new Error(`timed out after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`) -// } - -// const waitReceipt = async (): Promise => { -// // Transactions can only get executed on nonce change -// // get all nonce changes and look for metaTxnIds in between logs -// let lastBlock: number = this.fromBlockLog - -// if (lastBlock < 0) { -// const block = await retry(() => this.provider.getBlockNumber(), 'unable to get latest block number') -// lastBlock = block + lastBlock -// } - -// if (typeof metaTxnId !== 'string') { -// throw new Error('impossible') -// } - -// const normalMetaTxnId = metaTxnId.replace('0x', '') - -// while (!timedOut) { -// const block = await retry(() => this.provider.getBlockNumber(), 'unable to get latest block number') - -// const logs = await retry( -// () => -// this.provider.getLogs({ -// fromBlock: Math.max(0, lastBlock - this.deltaBlocksLog), -// toBlock: block, -// // Nonce change event topic -// topics: ['0x1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881'] -// }), -// `unable to get NonceChange logs for blocks ${Math.max(0, lastBlock - this.deltaBlocksLog)} to ${block}` -// ) - -// lastBlock = block - -// // Get receipts of all transactions -// const txs = await Promise.all( -// logs.map(l => -// retry( -// () => this.provider.getTransactionReceipt(l.transactionHash), -// `unable to get receipt for transaction ${l.transactionHash}` -// ) -// ) -// ) - -// // Find a transaction with a TxExecuted log -// const found = txs.find(tx => -// tx?.logs.find( -// l => -// (l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId) || -// (l.topics.length === 1 && -// // TxFailed event topic -// l.topics[0] === '0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7' && -// l.data.length >= 64 && -// l.data.replace('0x', '').startsWith(normalMetaTxnId)) -// ) -// ) - -// // If found return that -// if (found) { -// const response = await retry(() => this.provider.getTransaction(found.hash), `unable to get transaction ${found.hash}`) -// if (!response) { -// throw new Error(`Transaction response not found for ${metaTxnId}`) -// } - -// // NOTE: we have to do this, because ethers-v6 uses private fields -// // and we can't just extend the class and override the method, so -// // we just modify the response object directly by adding the receipt to it. -// const out: any = response -// out.receipt = found -// return out -// } - -// // Otherwise wait and try again -// if (!timedOut) { -// await new Promise(r => setTimeout(r, delay)) -// } -// } - -// throw new Error(`Timeout waiting for transaction receipt ${metaTxnId}`) -// } - -// if (timeoutDuration !== undefined) { -// return Promise.race([ -// waitReceipt(), -// new Promise((_, reject) => -// setTimeout(() => { -// timedOut = true -// reject(`Timeout waiting for transaction receipt ${metaTxnId}`) -// }, timeoutDuration) -// ) -// ]) -// } else { -// return waitReceipt() -// } -// } -// } - -// function isAbstractProvider(provider: any): provider is ethers.AbstractProvider { -// return ( -// provider && -// typeof provider === 'object' && -// typeof provider.getNetwork === 'function' && -// typeof provider.getBlockNumber === 'function' -// ) -// } diff --git a/packages/wallet/core/src/relayer/relayer.ts b/packages/services/relayer/src/relayer/index.ts similarity index 50% rename from packages/wallet/core/src/relayer/relayer.ts rename to packages/services/relayer/src/relayer/index.ts index db28608fca..52362d5c95 100644 --- a/packages/wallet/core/src/relayer/relayer.ts +++ b/packages/services/relayer/src/relayer/index.ts @@ -1,6 +1,10 @@ -import { Payload, Precondition } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' -import { FeeToken, GetMetaTxnReceiptReturn } from './standard/rpc/index.js' +import { Hex } from 'ox' +import type { FeeToken, GetMetaTxnReceiptReturn } from './rpc-relayer/relayer.gen.js' + +export * from './rpc-relayer/index.js' +export * from './standard/index.js' +export * from './relayer.js' +export type { FeeToken } from './rpc-relayer/relayer.gen.js' export interface FeeOption { token: FeeToken @@ -54,34 +58,3 @@ export type OperationStatus = | OperationPendingPreconditionStatus | OperationConfirmedStatus | OperationFailedStatus - -export interface Relayer { - kind: 'relayer' - - type: string - id: string - - isAvailable(wallet: Address.Address, chainId: number): Promise - - feeOptions( - wallet: Address.Address, - chainId: number, - calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> - - relay(to: Address.Address, data: Hex.Hex, chainId: number, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> - - status(opHash: Hex.Hex, chainId: number): Promise - - checkPrecondition(precondition: Precondition.Precondition): Promise -} - -export function isRelayer(relayer: any): relayer is Relayer { - return ( - 'isAvailable' in relayer && - 'feeOptions' in relayer && - 'relay' in relayer && - 'status' in relayer && - 'checkPrecondition' in relayer - ) -} diff --git a/packages/services/relayer/src/relayer/relayer.ts b/packages/services/relayer/src/relayer/relayer.ts new file mode 100644 index 0000000000..3ed5a69627 --- /dev/null +++ b/packages/services/relayer/src/relayer/relayer.ts @@ -0,0 +1,37 @@ +import { Address, Hex } from 'ox' +import { FeeToken } from './rpc-relayer/relayer.gen.js' +import { FeeOption, FeeQuote, OperationStatus } from './index.js' +import { Payload, Precondition } from '@0xsequence/wallet-primitives' + +export interface Relayer { + kind: 'relayer' + + type: string + id: string + + isAvailable(wallet: Address.Address, chainId: number): Promise + + feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> + + feeOptions( + wallet: Address.Address, + chainId: number, + calls: Payload.Call[], + ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> + + relay(to: Address.Address, data: Hex.Hex, chainId: number, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> + + status(opHash: Hex.Hex, chainId: number): Promise + + checkPrecondition(precondition: Precondition.Precondition): Promise +} + +export function isRelayer(relayer: any): relayer is Relayer { + return ( + 'isAvailable' in relayer && + 'feeOptions' in relayer && + 'relay' in relayer && + 'status' in relayer && + 'checkPrecondition' in relayer + ) +} diff --git a/packages/wallet/core/src/relayer/standard/rpc/index.ts b/packages/services/relayer/src/relayer/rpc-relayer/index.ts similarity index 78% rename from packages/wallet/core/src/relayer/standard/rpc/index.ts rename to packages/services/relayer/src/relayer/rpc-relayer/index.ts index e18bf7d94d..04db6aa40c 100644 --- a/packages/wallet/core/src/relayer/standard/rpc/index.ts +++ b/packages/services/relayer/src/relayer/rpc-relayer/index.ts @@ -3,13 +3,14 @@ import { SendMetaTxnReturn as RpcSendMetaTxnReturn, MetaTxn as RpcMetaTxn, FeeTokenType, - IntentPrecondition, + FeeToken as RpcFeeToken, + TransactionPrecondition, + ETHTxnStatus, } from './relayer.gen.js' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../../relayer.js' import { Address, Hex, Bytes, AbiFunction } from 'ox' -import { Constants, Payload } from '@0xsequence/wallet-primitives' -import { ETHTxnStatus, FeeToken as RpcFeeToken } from './relayer.gen.js' -import { decodePrecondition } from '../../../preconditions/index.js' +import { Constants, Payload, Network } from '@0xsequence/wallet-primitives' +import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' +import { decodePrecondition } from '../../preconditions/index.js' import { erc20BalanceOf, erc20Allowance, @@ -17,20 +18,61 @@ import { erc721GetApproved, erc1155BalanceOf, erc1155IsApprovedForAll, -} from '../abi.js' +} from '../standard/abi.js' import { PublicClient, createPublicClient, http, Chain } from 'viem' import * as chains from 'viem/chains' -export * from './relayer.gen.js' - export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise +/** + * Convert a Sequence Network to a viem Chain + */ +const networkToChain = (network: Network.Network): Chain => { + return { + id: network.chainId, + name: network.title || network.name, + nativeCurrency: { + name: network.nativeCurrency.name, + symbol: network.nativeCurrency.symbol, + decimals: network.nativeCurrency.decimals, + }, + rpcUrls: { + default: { + http: [network.rpcUrl], + }, + }, + blockExplorers: network.blockExplorer + ? { + default: { + name: network.blockExplorer.name || 'Explorer', + url: network.blockExplorer.url, + }, + } + : undefined, + contracts: network.ensAddress + ? { + ensUniversalResolver: { + address: network.ensAddress as `0x${string}`, + }, + } + : undefined, + } as Chain +} + export const getChain = (chainId: number): Chain => { - const chain = Object.values(chains).find((c: any) => typeof c === 'object' && 'id' in c && c.id === chainId) - if (!chain) { - throw new Error(`Chain with id ${chainId} not found`) + // First try to get the chain from Sequence's network configurations + const sequenceNetwork = Network.getNetworkFromChainId(chainId) + if (sequenceNetwork) { + return networkToChain(sequenceNetwork) } - return chain as Chain + + // Fall back to viem's built-in chains + const viemChain = Object.values(chains).find((c: any) => typeof c === 'object' && 'id' in c && c.id === chainId) + if (viemChain) { + return viemChain as Chain + } + + throw new Error(`Chain with id ${chainId} not found in Sequence networks or viem chains`) } export class RpcRelayer implements Relayer { @@ -41,10 +83,12 @@ export class RpcRelayer implements Relayer { private client: GenRelayer private fetch: Fetch private provider: PublicClient + private readonly projectAccessKey?: string - constructor(hostname: string, chainId: number, rpcUrl: string, fetchImpl?: Fetch) { + constructor(hostname: string, chainId: number, rpcUrl: string, fetchImpl?: Fetch, projectAccessKey?: string) { this.id = `rpc:${hostname}` this.chainId = chainId + this.projectAccessKey = projectAccessKey const effectiveFetch = fetchImpl || (typeof window !== 'undefined' ? window.fetch.bind(window) : undefined) if (!effectiveFetch) { throw new Error('Fetch implementation is required but not available in this environment.') @@ -66,6 +110,27 @@ export class RpcRelayer implements Relayer { return Promise.resolve(this.chainId === chainId) } + async feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: RpcFeeToken[]; paymentAddress?: Address.Address }> { + try { + const { isFeeRequired, tokens, paymentAddress } = await this.client.feeTokens() + if (isFeeRequired) { + Address.assert(paymentAddress) + return { + isFeeRequired, + tokens, + paymentAddress, + } + } + // Not required + return { + isFeeRequired, + } + } catch (e) { + console.warn('RpcRelayer.feeTokens failed:', e) + return { isFeeRequired: false } + } + } + async feeOptions( wallet: Address.Address, chainId: number, @@ -75,11 +140,14 @@ export class RpcRelayer implements Relayer { const data = Payload.encode(callsStruct) try { - const result = await this.client.feeOptions({ - wallet: wallet, - to: wallet, - data: Bytes.toHex(data), - }) + const result = await this.client.feeOptions( + { + wallet: wallet, + to: wallet, + data: Bytes.toHex(data), + }, + { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, + ) const quote = result.quote ? ({ _tag: 'FeeQuote', _quote: result.quote } as FeeQuote) : undefined const options = result.options.map((option) => ({ @@ -105,7 +173,7 @@ export class RpcRelayer implements Relayer { data: Hex.Hex, chainId: number, quote?: FeeQuote, - preconditions?: IntentPrecondition[], + preconditions?: TransactionPrecondition[], ): Promise<{ opHash: Hex.Hex }> { console.log('sendMetaTxn', walletAddress, to, data, chainId, quote, preconditions) const rpcCall: RpcMetaTxn = { @@ -114,11 +182,14 @@ export class RpcRelayer implements Relayer { input: data, } - const result: RpcSendMetaTxnReturn = await this.client.sendMetaTxn({ - call: rpcCall, - quote: quote ? JSON.stringify(quote._quote) : undefined, - preconditions: preconditions, - }) + const result: RpcSendMetaTxnReturn = await this.client.sendMetaTxn( + { + call: rpcCall, + quote: quote ? JSON.stringify(quote._quote) : undefined, + preconditions: preconditions, + }, + { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, + ) if (!result.status) { console.error('RpcRelayer.relay failed', result) @@ -133,7 +204,7 @@ export class RpcRelayer implements Relayer { data: Hex.Hex, chainId: number, quote?: FeeQuote, - preconditions?: IntentPrecondition[], + preconditions?: TransactionPrecondition[], ): Promise<{ opHash: Hex.Hex }> { console.log('relay', to, data, chainId, quote, preconditions) const rpcCall: RpcMetaTxn = { @@ -142,11 +213,14 @@ export class RpcRelayer implements Relayer { input: data, } - const result: RpcSendMetaTxnReturn = await this.client.sendMetaTxn({ - call: rpcCall, - quote: quote ? JSON.stringify(quote._quote) : undefined, - preconditions: preconditions, - }) + const result: RpcSendMetaTxnReturn = await this.client.sendMetaTxn( + { + call: rpcCall, + quote: quote ? JSON.stringify(quote._quote) : undefined, + preconditions: preconditions, + }, + { ...(this.projectAccessKey ? { 'X-Access-Key': this.projectAccessKey } : undefined) }, + ) if (!result.status) { console.error('RpcRelayer.relay failed', result) @@ -199,7 +273,7 @@ export class RpcRelayer implements Relayer { } } - async checkPrecondition(precondition: IntentPrecondition): Promise { + async checkPrecondition(precondition: TransactionPrecondition): Promise { const decoded = decodePrecondition(precondition) if (!decoded) { diff --git a/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts b/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts new file mode 100644 index 0000000000..ca5dbe9c82 --- /dev/null +++ b/packages/services/relayer/src/relayer/rpc-relayer/relayer.gen.ts @@ -0,0 +1,2268 @@ +/* eslint-disable */ +// sequence-relayer v0.4.1 7f8a4b83b00e0b6849c76c2ff0e23931e26b3d9f +// -- +// Code generated by Webrpc-gen@v0.30.2 with typescript generator. DO NOT EDIT. +// +// webrpc-gen -schema=relayer.ridl -target=typescript -client -out=./clients/relayer.gen.ts -compat + +// Webrpc description and code-gen version +export const WebrpcVersion = 'v1' + +// Schema version of your RIDL schema +export const WebrpcSchemaVersion = 'v0.4.1' + +// Schema hash generated from your RIDL schema +export const WebrpcSchemaHash = '7f8a4b83b00e0b6849c76c2ff0e23931e26b3d9f' + +// +// Client interface +// + +export interface RelayerClient { + ping(headers?: object, signal?: AbortSignal): Promise + + version(headers?: object, signal?: AbortSignal): Promise + + runtimeStatus(headers?: object, signal?: AbortSignal): Promise + + getSequenceContext(headers?: object, signal?: AbortSignal): Promise + + getChainID(headers?: object, signal?: AbortSignal): Promise + + /** + * + * Transactions + * + * TODO (future): rename this to just, 'SendTransaction(txn: MetaTransaction)' or 'SendTransaction(txn: SignedTransaction)', or something.. + * Project ID is only used by service and admin calls. Other clients must have projectID passed via the context + * TODO: rename return txnHash: string to metaTxnID: string + */ + sendMetaTxn(req: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise + + getMetaTxnNonce(req: GetMetaTxnNonceArgs, headers?: object, signal?: AbortSignal): Promise + + /** + * TODO: one day, make GetMetaTxnReceipt respond immediately with receipt or not + * and add WaitTransactionReceipt method, which will block and wait, similar to how GetMetaTxnReceipt + * is implemented now. + * For backwards compat, we can leave the current GetMetaTxnReceipt how it is, an deprecate it, and introduce + * new, GetTransactionReceipt and WaitTransactionReceipt methods + * we can also accept metaTxnId and txnHash .. so can take either or.. I wonder if ERC-4337 has any convention on this? + */ + getMetaTxnReceipt( + req: GetMetaTxnReceiptArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + simulate(req: SimulateArgs, headers?: object, signal?: AbortSignal): Promise + + simulateV3(req: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise + + /** + * TODO: deprecated, to be removed by https://github.com/0xsequence/stack/pull/356 at a later date + */ + updateMetaTxnGasLimits( + req: UpdateMetaTxnGasLimitsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + feeTokens(headers?: object, signal?: AbortSignal): Promise + + feeOptions(req: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise + + /** + * TODO: deprecated, to be removed by https://github.com/0xsequence/stack/pull/356 at a later date + */ + getMetaTxnNetworkFeeOptions( + req: GetMetaTxnNetworkFeeOptionsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + getMetaTransactions( + req: GetMetaTransactionsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + getTransactionCost( + req: GetTransactionCostArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Sent transactions from an account. If filter is omitted then it will return all transactions. + */ + sentTransactions(req: SentTransactionsArgs, headers?: object, signal?: AbortSignal): Promise + + /** + * Pending transactions waiting to be mined for an account. This endpoint is just a sugar of `SentTransactions` + * with the filter set to pending: true. + */ + pendingTransactions( + req: PendingTransactionsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Legacy Gas Tank + */ + getGasTank(req: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise + + addGasTank(req: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise + + updateGasTank(req: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise + + /** + * Legacy Gas Adjustment + */ + nextGasTankBalanceAdjustmentNonce( + req: NextGasTankBalanceAdjustmentNonceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + adjustGasTankBalance( + req: AdjustGasTankBalanceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + getGasTankBalanceAdjustment( + req: GetGasTankBalanceAdjustmentArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + listGasTankBalanceAdjustments( + req: ListGasTankBalanceAdjustmentsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Gas Sponsorship + */ + listGasSponsors(req: ListGasSponsorsArgs, headers?: object, signal?: AbortSignal): Promise + + getGasSponsor(req: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise + + addGasSponsor(req: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise + + updateGasSponsor(req: UpdateGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise + + removeGasSponsor(req: RemoveGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise + + /** + * Gas Sponsor Lookup + */ + addressGasSponsors( + req: AddressGasSponsorsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + /** + * Project Balance + */ + getProjectBalance( + req: GetProjectBalanceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise + + adjustProjectBalance( + req: AdjustProjectBalanceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise +} + +// +// Schema types +// + +export enum ETHTxnStatus { + UNKNOWN = 'UNKNOWN', + DROPPED = 'DROPPED', + QUEUED = 'QUEUED', + SENT = 'SENT', + SUCCEEDED = 'SUCCEEDED', + PARTIALLY_FAILED = 'PARTIALLY_FAILED', + FAILED = 'FAILED', + PENDING_PRECONDITION = 'PENDING_PRECONDITION', +} + +export enum TransferType { + SEND = 'SEND', + RECEIVE = 'RECEIVE', + BRIDGE_DEPOSIT = 'BRIDGE_DEPOSIT', + BRIDGE_WITHDRAW = 'BRIDGE_WITHDRAW', + BURN = 'BURN', + UNKNOWN = 'UNKNOWN', +} + +export enum SimulateStatus { + SKIPPED = 'SKIPPED', + SUCCEEDED = 'SUCCEEDED', + FAILED = 'FAILED', + ABORTED = 'ABORTED', + REVERTED = 'REVERTED', + NOT_ENOUGH_GAS = 'NOT_ENOUGH_GAS', +} + +export enum FeeTokenType { + UNKNOWN = 'UNKNOWN', + ERC20_TOKEN = 'ERC20_TOKEN', + ERC1155_TOKEN = 'ERC1155_TOKEN', +} + +export enum SortOrder { + DESC = 'DESC', + ASC = 'ASC', +} + +export interface Version { + webrpcVersion: string + schemaVersion: string + schemaHash: string + appVersion: string +} + +export interface RuntimeStatus { + healthOK: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + chainID: number + useEIP1559: boolean + senders: Array + checks: RuntimeChecks +} + +export interface SenderStatus { + index: number + address: string + etherBalance: number + active: boolean +} + +export interface RuntimeChecks {} + +export interface SequenceContext { + factory: string + mainModule: string + mainModuleUpgradable: string + guestModule: string + utils: string +} + +export interface GasTank { + id: number + chainId: number + name: string + currentBalance: number + unlimited: boolean + feeMarkupFactor: number + updatedAt: string + createdAt: string +} + +export interface GasTankBalanceAdjustment { + gasTankId: number + nonce: number + amount: number + totalBalance: number + balanceTimestamp: string + createdAt: string +} + +export interface GasSponsor { + id: number + gasTankId: number + projectId: number + chainId: number + address: string + name: string + active: boolean + updatedAt: string + createdAt: string + deletedAt: string +} + +export interface GasSponsorUsage { + name: string + id: number + totalGasUsed: number + totalTxnFees: number + totalTxnFeesUsd: number + avgGasPrice: number + totalTxns: number + startTime: string + endTime: string +} + +export interface MetaTxn { + walletAddress: string + contract: string + input: string +} + +export interface MetaTxnLog { + id: number + chainId: number + projectId: number + txnHash: string + txnNonce: string + metaTxnID?: string + txnStatus: ETHTxnStatus + txnRevertReason: string + requeues: number + queuedAt: string + sentAt: string + minedAt: string + target: string + input: string + txnArgs: { [key: string]: any } + txnReceipt?: { [key: string]: any } + walletAddress: string + metaTxnNonce: string + gasLimit: number + gasPrice: string + gasUsed: number + gasEstimated: number + gasFeeMarkup?: number + usdRate: string + creditsUsed: number + cost: string + isWhitelisted: boolean + gasSponsor?: number + gasTank?: number + updatedAt: string + createdAt: string +} + +export interface MetaTxnReceipt { + id: string + status: string + revertReason?: string + index: number + logs: Array + receipts: Array + blockNumber: string + txnHash: string + txnReceipt: string +} + +export interface MetaTxnReceiptLog { + address: string + topics: Array + data: string +} + +export interface Transactions { + chainID: string + transactions: Array + preconditions?: Array +} + +export interface Transaction { + delegateCall: boolean + revertOnError: boolean + gasLimit: string + target: string + value: string + data: string +} + +export interface TransactionPrecondition { + type: string + chainId: number + ownerAddress: string + tokenAddress: string + minAmount: bigint +} + +export interface TxnLogUser { + username: string +} + +export interface TxnLogTransfer { + transferType: TransferType + contractAddress: string + from: string + to: string + ids: Array + amounts: Array +} + +export interface SentTransactionsFilter { + pending?: boolean + failed?: boolean +} + +export interface SimulateResult { + executed: boolean + succeeded: boolean + result?: string + reason?: string + gasUsed: number + gasLimit: number +} + +export interface SimulateV3Result { + status: SimulateStatus + result?: string + error?: string + gasUsed: number + gasLimit: number +} + +export interface FeeOption { + token: FeeToken + to: string + value: string + gasLimit: number +} + +export interface FeeToken { + chainId: number + name: string + symbol: string + type: FeeTokenType + decimals?: number + logoURL: string + contractAddress?: string + tokenID?: string +} + +export interface Page { + pageSize?: number + page?: number + more?: boolean + totalRecords?: number + column?: string + before?: any + after?: any + sort?: Array +} + +export interface SortBy { + column: string + order: SortOrder +} + +export interface PingArgs {} + +export interface PingReturn { + status: boolean +} + +export interface VersionArgs {} + +export interface VersionReturn { + version: Version +} + +export interface RuntimeStatusArgs {} + +export interface RuntimeStatusReturn { + status: RuntimeStatus +} + +export interface GetSequenceContextArgs {} + +export interface GetSequenceContextReturn { + data: SequenceContext +} + +export interface GetChainIDArgs {} + +export interface GetChainIDReturn { + chainID: number +} + +export interface SendMetaTxnArgs { + call: MetaTxn + quote?: string + projectID?: number + preconditions?: Array +} + +export interface SendMetaTxnReturn { + status: boolean + txnHash: string +} + +export interface GetMetaTxnNonceArgs { + walletContractAddress: string + space?: string +} + +export interface GetMetaTxnNonceReturn { + nonce: string +} + +export interface GetMetaTxnReceiptArgs { + metaTxID: string +} + +export interface GetMetaTxnReceiptReturn { + receipt: MetaTxnReceipt +} + +export interface SimulateArgs { + wallet: string + transactions: string +} + +export interface SimulateReturn { + results: Array +} + +export interface SimulateV3Args { + wallet: string + calls: string +} + +export interface SimulateV3Return { + results: Array +} + +export interface UpdateMetaTxnGasLimitsArgs { + walletAddress: string + walletConfig: any + payload: string +} + +export interface UpdateMetaTxnGasLimitsReturn { + payload: string +} + +export interface FeeTokensArgs {} + +export interface FeeTokensReturn { + isFeeRequired: boolean + tokens: Array + paymentAddress: string +} + +export interface FeeOptionsArgs { + wallet: string + to: string + data: string + simulate?: boolean +} + +export interface FeeOptionsReturn { + options: Array + sponsored: boolean + quote?: string +} + +export interface GetMetaTxnNetworkFeeOptionsArgs { + walletConfig: any + payload: string +} + +export interface GetMetaTxnNetworkFeeOptionsReturn { + options: Array +} + +export interface GetMetaTransactionsArgs { + projectId: number + page?: Page +} + +export interface GetMetaTransactionsReturn { + page: Page + transactions: Array +} + +export interface GetTransactionCostArgs { + projectId: number + from: string + to: string +} + +export interface GetTransactionCostReturn { + cost: number +} + +export interface SentTransactionsArgs { + filter?: SentTransactionsFilter + page?: Page +} + +export interface SentTransactionsReturn { + page: Page + transactions: Array +} + +export interface PendingTransactionsArgs { + page?: Page +} + +export interface PendingTransactionsReturn { + page: Page + transactions: Array +} + +export interface GetGasTankArgs { + id: number +} + +export interface GetGasTankReturn { + gasTank: GasTank +} + +export interface AddGasTankArgs { + name: string + feeMarkupFactor: number + unlimited?: boolean +} + +export interface AddGasTankReturn { + status: boolean + gasTank: GasTank +} + +export interface UpdateGasTankArgs { + id: number + name?: string + feeMarkupFactor?: number + unlimited?: boolean +} + +export interface UpdateGasTankReturn { + status: boolean + gasTank: GasTank +} + +export interface NextGasTankBalanceAdjustmentNonceArgs { + id: number +} + +export interface NextGasTankBalanceAdjustmentNonceReturn { + nonce: number +} + +export interface AdjustGasTankBalanceArgs { + id: number + nonce: number + amount: number +} + +export interface AdjustGasTankBalanceReturn { + status: boolean + adjustment: GasTankBalanceAdjustment +} + +export interface GetGasTankBalanceAdjustmentArgs { + id: number + nonce: number +} + +export interface GetGasTankBalanceAdjustmentReturn { + adjustment: GasTankBalanceAdjustment +} + +export interface ListGasTankBalanceAdjustmentsArgs { + id: number + page?: Page +} + +export interface ListGasTankBalanceAdjustmentsReturn { + page: Page + adjustments: Array +} + +export interface ListGasSponsorsArgs { + projectId: number + page?: Page +} + +export interface ListGasSponsorsReturn { + page: Page + gasSponsors: Array +} + +export interface GetGasSponsorArgs { + projectId: number + id: number +} + +export interface GetGasSponsorReturn { + gasSponsor: GasSponsor +} + +export interface AddGasSponsorArgs { + projectId: number + address: string + name?: string + active?: boolean +} + +export interface AddGasSponsorReturn { + status: boolean + gasSponsor: GasSponsor +} + +export interface UpdateGasSponsorArgs { + projectId: number + id: number + name?: string + active?: boolean +} + +export interface UpdateGasSponsorReturn { + status: boolean + gasSponsor: GasSponsor +} + +export interface RemoveGasSponsorArgs { + projectId: number + id: number +} + +export interface RemoveGasSponsorReturn { + status: boolean +} + +export interface AddressGasSponsorsArgs { + address: string + page?: Page +} + +export interface AddressGasSponsorsReturn { + page: Page + gasSponsors: Array +} + +export interface GetProjectBalanceArgs { + projectId: number +} + +export interface GetProjectBalanceReturn { + balance: number +} + +export interface AdjustProjectBalanceArgs { + projectId: number + amount: number + identifier: string +} + +export interface AdjustProjectBalanceReturn { + balance: number +} + +// +// Client +// + +export class Relayer implements RelayerClient { + protected hostname: string + protected fetch: Fetch + protected path = '/rpc/Relayer/' + + constructor(hostname: string, fetch: Fetch) { + this.hostname = hostname.replace(/\/*$/, '') + this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) + } + + private url(name: string): string { + return this.hostname + this.path + name + } + + queryKey = { + ping: () => ['Relayer', 'ping'] as const, + version: () => ['Relayer', 'version'] as const, + runtimeStatus: () => ['Relayer', 'runtimeStatus'] as const, + getSequenceContext: () => ['Relayer', 'getSequenceContext'] as const, + getChainID: () => ['Relayer', 'getChainID'] as const, + sendMetaTxn: (req: SendMetaTxnArgs) => ['Relayer', 'sendMetaTxn', req] as const, + getMetaTxnNonce: (req: GetMetaTxnNonceArgs) => ['Relayer', 'getMetaTxnNonce', req] as const, + getMetaTxnReceipt: (req: GetMetaTxnReceiptArgs) => ['Relayer', 'getMetaTxnReceipt', req] as const, + simulate: (req: SimulateArgs) => ['Relayer', 'simulate', req] as const, + simulateV3: (req: SimulateV3Args) => ['Relayer', 'simulateV3', req] as const, + updateMetaTxnGasLimits: (req: UpdateMetaTxnGasLimitsArgs) => ['Relayer', 'updateMetaTxnGasLimits', req] as const, + feeTokens: () => ['Relayer', 'feeTokens'] as const, + feeOptions: (req: FeeOptionsArgs) => ['Relayer', 'feeOptions', req] as const, + getMetaTxnNetworkFeeOptions: (req: GetMetaTxnNetworkFeeOptionsArgs) => + ['Relayer', 'getMetaTxnNetworkFeeOptions', req] as const, + getMetaTransactions: (req: GetMetaTransactionsArgs) => ['Relayer', 'getMetaTransactions', req] as const, + getTransactionCost: (req: GetTransactionCostArgs) => ['Relayer', 'getTransactionCost', req] as const, + sentTransactions: (req: SentTransactionsArgs) => ['Relayer', 'sentTransactions', req] as const, + pendingTransactions: (req: PendingTransactionsArgs) => ['Relayer', 'pendingTransactions', req] as const, + getGasTank: (req: GetGasTankArgs) => ['Relayer', 'getGasTank', req] as const, + addGasTank: (req: AddGasTankArgs) => ['Relayer', 'addGasTank', req] as const, + updateGasTank: (req: UpdateGasTankArgs) => ['Relayer', 'updateGasTank', req] as const, + nextGasTankBalanceAdjustmentNonce: (req: NextGasTankBalanceAdjustmentNonceArgs) => + ['Relayer', 'nextGasTankBalanceAdjustmentNonce', req] as const, + adjustGasTankBalance: (req: AdjustGasTankBalanceArgs) => ['Relayer', 'adjustGasTankBalance', req] as const, + getGasTankBalanceAdjustment: (req: GetGasTankBalanceAdjustmentArgs) => + ['Relayer', 'getGasTankBalanceAdjustment', req] as const, + listGasTankBalanceAdjustments: (req: ListGasTankBalanceAdjustmentsArgs) => + ['Relayer', 'listGasTankBalanceAdjustments', req] as const, + listGasSponsors: (req: ListGasSponsorsArgs) => ['Relayer', 'listGasSponsors', req] as const, + getGasSponsor: (req: GetGasSponsorArgs) => ['Relayer', 'getGasSponsor', req] as const, + addGasSponsor: (req: AddGasSponsorArgs) => ['Relayer', 'addGasSponsor', req] as const, + updateGasSponsor: (req: UpdateGasSponsorArgs) => ['Relayer', 'updateGasSponsor', req] as const, + removeGasSponsor: (req: RemoveGasSponsorArgs) => ['Relayer', 'removeGasSponsor', req] as const, + addressGasSponsors: (req: AddressGasSponsorsArgs) => ['Relayer', 'addressGasSponsors', req] as const, + getProjectBalance: (req: GetProjectBalanceArgs) => ['Relayer', 'getProjectBalance', req] as const, + adjustProjectBalance: (req: AdjustProjectBalanceArgs) => ['Relayer', 'adjustProjectBalance', req] as const, + } + + ping = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PingReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + version = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'VersionReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'RuntimeStatusReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getSequenceContext = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetSequenceContext'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetSequenceContextReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getChainID = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetChainID'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetChainIDReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + sendMetaTxn = (req: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('SendMetaTxn'), + createHttpRequest(JsonEncode(req, 'SendMetaTxnArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'SendMetaTxnReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getMetaTxnNonce = ( + req: GetMetaTxnNonceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetMetaTxnNonce'), + createHttpRequest(JsonEncode(req, 'GetMetaTxnNonceArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetMetaTxnNonceReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getMetaTxnReceipt = ( + req: GetMetaTxnReceiptArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetMetaTxnReceipt'), + createHttpRequest(JsonEncode(req, 'GetMetaTxnReceiptArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetMetaTxnReceiptReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + simulate = (req: SimulateArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Simulate'), createHttpRequest(JsonEncode(req, 'SimulateArgs'), headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'SimulateReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + simulateV3 = (req: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('SimulateV3'), + createHttpRequest(JsonEncode(req, 'SimulateV3Args'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'SimulateV3Return') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + updateMetaTxnGasLimits = ( + req: UpdateMetaTxnGasLimitsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('UpdateMetaTxnGasLimits'), + createHttpRequest(JsonEncode(req, 'UpdateMetaTxnGasLimitsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UpdateMetaTxnGasLimitsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + feeTokens = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('FeeTokens'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'FeeTokensReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + feeOptions = (req: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('FeeOptions'), + createHttpRequest(JsonEncode(req, 'FeeOptionsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'FeeOptionsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getMetaTxnNetworkFeeOptions = ( + req: GetMetaTxnNetworkFeeOptionsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetMetaTxnNetworkFeeOptions'), + createHttpRequest(JsonEncode(req, 'GetMetaTxnNetworkFeeOptionsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetMetaTxnNetworkFeeOptionsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getMetaTransactions = ( + req: GetMetaTransactionsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetMetaTransactions'), + createHttpRequest(JsonEncode(req, 'GetMetaTransactionsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetMetaTransactionsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getTransactionCost = ( + req: GetTransactionCostArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetTransactionCost'), + createHttpRequest(JsonEncode(req, 'GetTransactionCostArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetTransactionCostReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + sentTransactions = ( + req: SentTransactionsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('SentTransactions'), + createHttpRequest(JsonEncode(req, 'SentTransactionsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'SentTransactionsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + pendingTransactions = ( + req: PendingTransactionsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('PendingTransactions'), + createHttpRequest(JsonEncode(req, 'PendingTransactionsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PendingTransactionsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getGasTank = (req: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('GetGasTank'), + createHttpRequest(JsonEncode(req, 'GetGasTankArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetGasTankReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + addGasTank = (req: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('AddGasTank'), + createHttpRequest(JsonEncode(req, 'AddGasTankArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'AddGasTankReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + updateGasTank = (req: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('UpdateGasTank'), + createHttpRequest(JsonEncode(req, 'UpdateGasTankArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UpdateGasTankReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + nextGasTankBalanceAdjustmentNonce = ( + req: NextGasTankBalanceAdjustmentNonceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('NextGasTankBalanceAdjustmentNonce'), + createHttpRequest(JsonEncode(req, 'NextGasTankBalanceAdjustmentNonceArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'NextGasTankBalanceAdjustmentNonceReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + adjustGasTankBalance = ( + req: AdjustGasTankBalanceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('AdjustGasTankBalance'), + createHttpRequest(JsonEncode(req, 'AdjustGasTankBalanceArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'AdjustGasTankBalanceReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getGasTankBalanceAdjustment = ( + req: GetGasTankBalanceAdjustmentArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetGasTankBalanceAdjustment'), + createHttpRequest(JsonEncode(req, 'GetGasTankBalanceAdjustmentArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetGasTankBalanceAdjustmentReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listGasTankBalanceAdjustments = ( + req: ListGasTankBalanceAdjustmentsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('ListGasTankBalanceAdjustments'), + createHttpRequest(JsonEncode(req, 'ListGasTankBalanceAdjustmentsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListGasTankBalanceAdjustmentsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listGasSponsors = ( + req: ListGasSponsorsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('ListGasSponsors'), + createHttpRequest(JsonEncode(req, 'ListGasSponsorsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListGasSponsorsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getGasSponsor = (req: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('GetGasSponsor'), + createHttpRequest(JsonEncode(req, 'GetGasSponsorArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetGasSponsorReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + addGasSponsor = (req: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('AddGasSponsor'), + createHttpRequest(JsonEncode(req, 'AddGasSponsorArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'AddGasSponsorReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + updateGasSponsor = ( + req: UpdateGasSponsorArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('UpdateGasSponsor'), + createHttpRequest(JsonEncode(req, 'UpdateGasSponsorArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'UpdateGasSponsorReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + removeGasSponsor = ( + req: RemoveGasSponsorArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('RemoveGasSponsor'), + createHttpRequest(JsonEncode(req, 'RemoveGasSponsorArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'RemoveGasSponsorReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + addressGasSponsors = ( + req: AddressGasSponsorsArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('AddressGasSponsors'), + createHttpRequest(JsonEncode(req, 'AddressGasSponsorsArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'AddressGasSponsorsReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getProjectBalance = ( + req: GetProjectBalanceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetProjectBalance'), + createHttpRequest(JsonEncode(req, 'GetProjectBalanceArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetProjectBalanceReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + adjustProjectBalance = ( + req: AdjustProjectBalanceArgs, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('AdjustProjectBalance'), + createHttpRequest(JsonEncode(req, 'AdjustProjectBalanceArgs'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'AdjustProjectBalanceReturn') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } +} + +const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { + const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } + return { method: 'POST', headers: reqHeaders, body, signal } +} + +const buildResponse = (res: Response): Promise => { + return res.text().then((text) => { + let data + try { + data = JSON.parse(text) + } catch (error) { + throw WebrpcBadResponseError.new({ + status: res.status, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, + }) + } + if (!res.ok) { + const code: number = typeof data.code === 'number' ? data.code : 0 + throw (webrpcErrorByCode[code] || WebrpcError).new(data) + } + return data + }) +} + +export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise + +// +// BigInt helpers +// + +const BIG_INT_FIELDS: { [typ: string]: (string | [string, string])[] } = { + SendMetaTxnArgs: [['preconditions', 'TransactionPrecondition[]']], + TransactionPrecondition: ['minAmount'], + Transactions: [['preconditions', 'TransactionPrecondition[]']], +} + +// Encode in-place: mutate provided object graph to serialize bigints to strings. +function encodeType(typ: string, obj: any): any { + if (obj == null || typeof obj !== 'object') return obj + const descs = BIG_INT_FIELDS[typ] || [] + if (!descs.length) return obj + for (const d of descs) { + if (Array.isArray(d)) { + const [fieldName, nestedType] = d + if (fieldName.endsWith('[]')) { + const base = fieldName.slice(0, -2) + const arr = obj[base] + if (Array.isArray(arr)) { + for (let i = 0; i < arr.length; i++) arr[i] = encodeType(nestedType, arr[i]) + } + } else if (obj[fieldName]) { + obj[fieldName] = encodeType(nestedType, obj[fieldName]) + } + continue + } + if (d.endsWith('[]')) { + const base = d.slice(0, -2) + const arr = obj[base] + if (Array.isArray(arr)) { + for (let i = 0; i < arr.length; i++) { + if (typeof arr[i] === 'bigint') arr[i] = arr[i].toString() + } + } + continue + } + if (typeof obj[d] === 'bigint') obj[d] = obj[d].toString() + } + return obj +} + +// Decode in-place: mutate object graph; throw if expected numeric string is invalid. +function decodeType(typ: string, obj: any): any { + if (obj == null || typeof obj !== 'object') return obj + const descs = BIG_INT_FIELDS[typ] || [] + if (!descs.length) return obj + for (const d of descs) { + if (Array.isArray(d)) { + const [fieldName, nestedType] = d + if (fieldName.endsWith('[]')) { + const base = fieldName.slice(0, -2) + const arr = obj[base] + if (Array.isArray(arr)) { + for (let i = 0; i < arr.length; i++) arr[i] = decodeType(nestedType, arr[i]) + } + } else if (obj[fieldName]) { + obj[fieldName] = decodeType(nestedType, obj[fieldName]) + } + continue + } + if (d.endsWith('[]')) { + const base = d.slice(0, -2) + const arr = obj[base] + if (Array.isArray(arr)) { + for (let i = 0; i < arr.length; i++) { + const v = arr[i] + if (typeof v === 'string') { + try { + arr[i] = BigInt(v) + } catch (e) { + throw WebrpcBadResponseError.new({ cause: `Invalid bigint value for ${base}[${i}]: ${v}` }) + } + } + } + } + continue + } + const v = obj[d] + if (typeof v === 'string') { + try { + obj[d] = BigInt(v) + } catch (e) { + throw WebrpcBadResponseError.new({ cause: `Invalid bigint value for ${d}: ${v}` }) + } + } + } + return obj +} + +// Encode object of given root type to JSON with BigInts converted to decimal strings. +export const JsonEncode = (obj: T, typ: string = ''): string => { + return JSON.stringify(encodeType(typ, obj)) +} + +// Decode data (JSON string or already-parsed object) and convert declared BigInt string fields back to BigInt. +export const JsonDecode = (data: string | any, typ: string = ''): T => { + let parsed: any = data + if (typeof data === 'string') { + try { + parsed = JSON.parse(data) + } catch (err) { + throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) + } + } + return decodeType(typ, parsed) as T +} + +// +// Errors +// + +type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } + +export class WebrpcError extends Error { + code: number + status: number + + constructor(error: WebrpcErrorParams = {}) { + super(error.message) + this.name = error.name || 'WebrpcEndpointError' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcError.prototype) + } + + static new(payload: any): WebrpcError { + return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) + } +} + +export class WebrpcEndpointError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcEndpoint' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcEndpointError.prototype) + } +} + +export class WebrpcRequestFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcRequestFailed' + this.code = typeof error.code === 'number' ? error.code : -1 + this.message = error.message || `request failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) + } +} + +export class WebrpcBadRouteError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRoute' + this.code = typeof error.code === 'number' ? error.code : -2 + this.message = error.message || `bad route` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) + } +} + +export class WebrpcBadMethodError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadMethod' + this.code = typeof error.code === 'number' ? error.code : -3 + this.message = error.message || `bad method` + this.status = typeof error.status === 'number' ? error.status : 405 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) + } +} + +export class WebrpcBadRequestError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRequest' + this.code = typeof error.code === 'number' ? error.code : -4 + this.message = error.message || `bad request` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) + } +} + +export class WebrpcBadResponseError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadResponse' + this.code = typeof error.code === 'number' ? error.code : -5 + this.message = error.message || `bad response` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) + } +} + +export class WebrpcServerPanicError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcServerPanic' + this.code = typeof error.code === 'number' ? error.code : -6 + this.message = error.message || `server panic` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) + } +} + +export class WebrpcInternalErrorError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcInternalError' + this.code = typeof error.code === 'number' ? error.code : -7 + this.message = error.message || `internal error` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) + } +} + +export class WebrpcClientAbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcClientAborted' + this.code = typeof error.code === 'number' ? error.code : -8 + this.message = error.message || `request aborted by client` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) + } +} + +export class WebrpcStreamLostError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamLost' + this.code = typeof error.code === 'number' ? error.code : -9 + this.message = error.message || `stream lost` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) + } +} + +export class WebrpcStreamFinishedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamFinished' + this.code = typeof error.code === 'number' ? error.code : -10 + this.message = error.message || `stream finished` + this.status = typeof error.status === 'number' ? error.status : 200 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) + } +} + +// +// Schema errors +// + +export class UnauthorizedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unauthorized' + this.code = typeof error.code === 'number' ? error.code : 1000 + this.message = error.message || `Unauthorized access` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnauthorizedError.prototype) + } +} + +export class PermissionDeniedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'PermissionDenied' + this.code = typeof error.code === 'number' ? error.code : 1001 + this.message = error.message || `Permission denied` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, PermissionDeniedError.prototype) + } +} + +export class SessionExpiredError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'SessionExpired' + this.code = typeof error.code === 'number' ? error.code : 1002 + this.message = error.message || `Session expired` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, SessionExpiredError.prototype) + } +} + +export class MethodNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MethodNotFound' + this.code = typeof error.code === 'number' ? error.code : 1003 + this.message = error.message || `Method not found` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MethodNotFoundError.prototype) + } +} + +export class RequestConflictError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RequestConflict' + this.code = typeof error.code === 'number' ? error.code : 1004 + this.message = error.message || `Conflict with target resource` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RequestConflictError.prototype) + } +} + +export class AbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Aborted' + this.code = typeof error.code === 'number' ? error.code : 1005 + this.message = error.message || `Request aborted` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AbortedError.prototype) + } +} + +export class GeoblockedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Geoblocked' + this.code = typeof error.code === 'number' ? error.code : 1006 + this.message = error.message || `Geoblocked region` + this.status = typeof error.status === 'number' ? error.status : 451 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, GeoblockedError.prototype) + } +} + +export class RateLimitedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RateLimited' + this.code = typeof error.code === 'number' ? error.code : 1007 + this.message = error.message || `Rate-limited. Please slow down.` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RateLimitedError.prototype) + } +} + +export class ProjectNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ProjectNotFound' + this.code = typeof error.code === 'number' ? error.code : 1008 + this.message = error.message || `Project not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, ProjectNotFoundError.prototype) + } +} + +export class AccessKeyNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AccessKeyNotFound' + this.code = typeof error.code === 'number' ? error.code : 1101 + this.message = error.message || `Access key not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) + } +} + +export class AccessKeyMismatchError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AccessKeyMismatch' + this.code = typeof error.code === 'number' ? error.code : 1102 + this.message = error.message || `Access key mismatch` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) + } +} + +export class InvalidOriginError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidOrigin' + this.code = typeof error.code === 'number' ? error.code : 1103 + this.message = error.message || `Invalid origin for Access Key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidOriginError.prototype) + } +} + +export class InvalidServiceError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidService' + this.code = typeof error.code === 'number' ? error.code : 1104 + this.message = error.message || `Service not enabled for Access key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidServiceError.prototype) + } +} + +export class UnauthorizedUserError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'UnauthorizedUser' + this.code = typeof error.code === 'number' ? error.code : 1105 + this.message = error.message || `Unauthorized user` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnauthorizedUserError.prototype) + } +} + +export class QuotaExceededError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QuotaExceeded' + this.code = typeof error.code === 'number' ? error.code : 1200 + this.message = error.message || `Quota request exceeded` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, QuotaExceededError.prototype) + } +} + +export class QuotaRateLimitError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QuotaRateLimit' + this.code = typeof error.code === 'number' ? error.code : 1201 + this.message = error.message || `Quota rate limit exceeded` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, QuotaRateLimitError.prototype) + } +} + +export class NoDefaultKeyError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NoDefaultKey' + this.code = typeof error.code === 'number' ? error.code : 1300 + this.message = error.message || `No default access key found` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, NoDefaultKeyError.prototype) + } +} + +export class MaxAccessKeysError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MaxAccessKeys' + this.code = typeof error.code === 'number' ? error.code : 1301 + this.message = error.message || `Access keys limit reached` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MaxAccessKeysError.prototype) + } +} + +export class AtLeastOneKeyError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AtLeastOneKey' + this.code = typeof error.code === 'number' ? error.code : 1302 + this.message = error.message || `You need at least one Access Key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) + } +} + +export class TimeoutError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Timeout' + this.code = typeof error.code === 'number' ? error.code : 1900 + this.message = error.message || `Request timed out` + this.status = typeof error.status === 'number' ? error.status : 408 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, TimeoutError.prototype) + } +} + +export class InvalidArgumentError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidArgument' + this.code = typeof error.code === 'number' ? error.code : 2001 + this.message = error.message || `Invalid argument` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidArgumentError.prototype) + } +} + +export class UnavailableError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unavailable' + this.code = typeof error.code === 'number' ? error.code : 2002 + this.message = error.message || `Unavailable resource` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnavailableError.prototype) + } +} + +export class QueryFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QueryFailed' + this.code = typeof error.code === 'number' ? error.code : 2003 + this.message = error.message || `Query failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, QueryFailedError.prototype) + } +} + +export class NotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NotFound' + this.code = typeof error.code === 'number' ? error.code : 3000 + this.message = error.message || `Resource not found` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, NotFoundError.prototype) + } +} + +export class InsufficientFeeError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InsufficientFee' + this.code = typeof error.code === 'number' ? error.code : 3004 + this.message = error.message || `Insufficient fee` + this.status = typeof error.status === 'number' ? error.status : 402 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InsufficientFeeError.prototype) + } +} + +export class NotEnoughBalanceError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NotEnoughBalance' + this.code = typeof error.code === 'number' ? error.code : 3005 + this.message = error.message || `Not enough balance` + this.status = typeof error.status === 'number' ? error.status : 402 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, NotEnoughBalanceError.prototype) + } +} + +export class SimulationFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'SimulationFailed' + this.code = typeof error.code === 'number' ? error.code : 3006 + this.message = error.message || `Simulation failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, SimulationFailedError.prototype) + } +} + +export enum errors { + WebrpcEndpoint = 'WebrpcEndpoint', + WebrpcRequestFailed = 'WebrpcRequestFailed', + WebrpcBadRoute = 'WebrpcBadRoute', + WebrpcBadMethod = 'WebrpcBadMethod', + WebrpcBadRequest = 'WebrpcBadRequest', + WebrpcBadResponse = 'WebrpcBadResponse', + WebrpcServerPanic = 'WebrpcServerPanic', + WebrpcInternalError = 'WebrpcInternalError', + WebrpcClientAborted = 'WebrpcClientAborted', + WebrpcStreamLost = 'WebrpcStreamLost', + WebrpcStreamFinished = 'WebrpcStreamFinished', + Unauthorized = 'Unauthorized', + PermissionDenied = 'PermissionDenied', + SessionExpired = 'SessionExpired', + MethodNotFound = 'MethodNotFound', + RequestConflict = 'RequestConflict', + Aborted = 'Aborted', + Geoblocked = 'Geoblocked', + RateLimited = 'RateLimited', + ProjectNotFound = 'ProjectNotFound', + AccessKeyNotFound = 'AccessKeyNotFound', + AccessKeyMismatch = 'AccessKeyMismatch', + InvalidOrigin = 'InvalidOrigin', + InvalidService = 'InvalidService', + UnauthorizedUser = 'UnauthorizedUser', + QuotaExceeded = 'QuotaExceeded', + QuotaRateLimit = 'QuotaRateLimit', + NoDefaultKey = 'NoDefaultKey', + MaxAccessKeys = 'MaxAccessKeys', + AtLeastOneKey = 'AtLeastOneKey', + Timeout = 'Timeout', + InvalidArgument = 'InvalidArgument', + Unavailable = 'Unavailable', + QueryFailed = 'QueryFailed', + NotFound = 'NotFound', + InsufficientFee = 'InsufficientFee', + NotEnoughBalance = 'NotEnoughBalance', + SimulationFailed = 'SimulationFailed', +} + +export enum WebrpcErrorCodes { + WebrpcEndpoint = 0, + WebrpcRequestFailed = -1, + WebrpcBadRoute = -2, + WebrpcBadMethod = -3, + WebrpcBadRequest = -4, + WebrpcBadResponse = -5, + WebrpcServerPanic = -6, + WebrpcInternalError = -7, + WebrpcClientAborted = -8, + WebrpcStreamLost = -9, + WebrpcStreamFinished = -10, + Unauthorized = 1000, + PermissionDenied = 1001, + SessionExpired = 1002, + MethodNotFound = 1003, + RequestConflict = 1004, + Aborted = 1005, + Geoblocked = 1006, + RateLimited = 1007, + ProjectNotFound = 1008, + AccessKeyNotFound = 1101, + AccessKeyMismatch = 1102, + InvalidOrigin = 1103, + InvalidService = 1104, + UnauthorizedUser = 1105, + QuotaExceeded = 1200, + QuotaRateLimit = 1201, + NoDefaultKey = 1300, + MaxAccessKeys = 1301, + AtLeastOneKey = 1302, + Timeout = 1900, + InvalidArgument = 2001, + Unavailable = 2002, + QueryFailed = 2003, + NotFound = 3000, + InsufficientFee = 3004, + NotEnoughBalance = 3005, + SimulationFailed = 3006, +} + +export const webrpcErrorByCode: { [code: number]: any } = { + [0]: WebrpcEndpointError, + [-1]: WebrpcRequestFailedError, + [-2]: WebrpcBadRouteError, + [-3]: WebrpcBadMethodError, + [-4]: WebrpcBadRequestError, + [-5]: WebrpcBadResponseError, + [-6]: WebrpcServerPanicError, + [-7]: WebrpcInternalErrorError, + [-8]: WebrpcClientAbortedError, + [-9]: WebrpcStreamLostError, + [-10]: WebrpcStreamFinishedError, + [1000]: UnauthorizedError, + [1001]: PermissionDeniedError, + [1002]: SessionExpiredError, + [1003]: MethodNotFoundError, + [1004]: RequestConflictError, + [1005]: AbortedError, + [1006]: GeoblockedError, + [1007]: RateLimitedError, + [1008]: ProjectNotFoundError, + [1101]: AccessKeyNotFoundError, + [1102]: AccessKeyMismatchError, + [1103]: InvalidOriginError, + [1104]: InvalidServiceError, + [1105]: UnauthorizedUserError, + [1200]: QuotaExceededError, + [1201]: QuotaRateLimitError, + [1300]: NoDefaultKeyError, + [1301]: MaxAccessKeysError, + [1302]: AtLeastOneKeyError, + [1900]: TimeoutError, + [2001]: InvalidArgumentError, + [2002]: UnavailableError, + [2003]: QueryFailedError, + [3000]: NotFoundError, + [3004]: InsufficientFeeError, + [3005]: NotEnoughBalanceError, + [3006]: SimulationFailedError, +} + +// +// Webrpc +// + +export const WebrpcHeader = 'Webrpc' + +export const WebrpcHeaderValue = 'webrpc@v0.30.2;gen-typescript@v0.22.2;sequence-relayer@v0.4.1' + +type WebrpcGenVersions = { + WebrpcGenVersion: string + codeGenName: string + codeGenVersion: string + schemaName: string + schemaVersion: string +} + +export function VersionFromHeader(headers: Headers): WebrpcGenVersions { + const headerValue = headers.get(WebrpcHeader) + if (!headerValue) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + } + } + + return parseWebrpcGenVersions(headerValue) +} + +function parseWebrpcGenVersions(header: string): WebrpcGenVersions { + const versions = header.split(';') + if (versions.length < 3) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + } + } + + const [_, WebrpcGenVersion] = versions[0]!.split('@') + const [codeGenName, codeGenVersion] = versions[1]!.split('@') + const [schemaName, schemaVersion] = versions[2]!.split('@') + + return { + WebrpcGenVersion: WebrpcGenVersion ?? '', + codeGenName: codeGenName ?? '', + codeGenVersion: codeGenVersion ?? '', + schemaName: schemaName ?? '', + schemaVersion: schemaVersion ?? '', + } +} diff --git a/packages/wallet/core/src/relayer/standard/abi.ts b/packages/services/relayer/src/relayer/standard/abi.ts similarity index 100% rename from packages/wallet/core/src/relayer/standard/abi.ts rename to packages/services/relayer/src/relayer/standard/abi.ts diff --git a/packages/wallet/core/src/relayer/standard/eip6963.ts b/packages/services/relayer/src/relayer/standard/eip6963.ts similarity index 85% rename from packages/wallet/core/src/relayer/standard/eip6963.ts rename to packages/services/relayer/src/relayer/standard/eip6963.ts index e1a2bd1746..9d48613633 100644 --- a/packages/wallet/core/src/relayer/standard/eip6963.ts +++ b/packages/services/relayer/src/relayer/standard/eip6963.ts @@ -1,9 +1,9 @@ import { createStore, EIP6963ProviderInfo, EIP6963ProviderDetail } from 'mipd' import { EIP1193ProviderAdapter, LocalRelayer } from './local.js' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../relayer.js' +import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' import { Address, Hex } from 'ox' import { Payload } from '@0xsequence/wallet-primitives' -import { IntentPrecondition } from './rpc/relayer.gen.js' +import { FeeToken, TransactionPrecondition } from '../rpc-relayer/relayer.gen.js' export class EIP6963Relayer implements Relayer { public readonly kind: 'relayer' = 'relayer' @@ -23,6 +23,10 @@ export class EIP6963Relayer implements Relayer { return this.relayer.isAvailable(wallet, chainId) } + feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + return this.relayer.feeTokens() + } + feeOptions( wallet: Address.Address, chainId: number, @@ -39,7 +43,7 @@ export class EIP6963Relayer implements Relayer { return this.relayer.status(opHash, chainId) } - async checkPrecondition(precondition: IntentPrecondition): Promise { + async checkPrecondition(precondition: TransactionPrecondition): Promise { return this.relayer.checkPrecondition(precondition) } } diff --git a/packages/wallet/core/src/relayer/standard/index.ts b/packages/services/relayer/src/relayer/standard/index.ts similarity index 77% rename from packages/wallet/core/src/relayer/standard/index.ts rename to packages/services/relayer/src/relayer/standard/index.ts index 12260aef4a..d04527fa03 100644 --- a/packages/wallet/core/src/relayer/standard/index.ts +++ b/packages/services/relayer/src/relayer/standard/index.ts @@ -1,5 +1,4 @@ export * from './local.js' export * from './pk-relayer.js' export * from './sequence.js' -export * as Rpc from './rpc/index.js' export * as EIP6963 from './eip6963.js' diff --git a/packages/wallet/core/src/relayer/standard/local.ts b/packages/services/relayer/src/relayer/standard/local.ts similarity index 96% rename from packages/wallet/core/src/relayer/standard/local.ts rename to packages/services/relayer/src/relayer/standard/local.ts index 0e4f30732a..14d697aa23 100644 --- a/packages/wallet/core/src/relayer/standard/local.ts +++ b/packages/services/relayer/src/relayer/standard/local.ts @@ -1,8 +1,8 @@ import { Constants, Payload } from '@0xsequence/wallet-primitives' import { EIP1193Provider } from 'mipd' import { AbiFunction, Address, Bytes, Hex, TransactionReceipt } from 'ox' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../relayer.js' -import { IntentPrecondition } from './rpc/relayer.gen.js' +import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' +import { FeeToken, TransactionPrecondition } from '../rpc-relayer/relayer.gen.js' import { decodePrecondition } from '../../preconditions/index.js' import { erc20BalanceOf, @@ -47,6 +47,12 @@ export class LocalRelayer implements Relayer { return new LocalRelayer(new EIP1193ProviderAdapter(provider)) } + feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + return Promise.resolve({ + isFeeRequired: false, + }) + } + feeOptions( wallet: Address.Address, chainId: number, @@ -74,7 +80,7 @@ export class LocalRelayer implements Relayer { data: Hex.Hex, chainId: number, quote?: FeeQuote, - preconditions?: IntentPrecondition[], + preconditions?: TransactionPrecondition[], checkInterval: number = 5000, ): Promise<{ opHash: Hex.Hex }> { // Helper function to check all preconditions @@ -162,7 +168,7 @@ export class LocalRelayer implements Relayer { : { status: 'failed', reason: 'failed' } } - async checkPrecondition(precondition: IntentPrecondition): Promise { + async checkPrecondition(precondition: TransactionPrecondition): Promise { const decoded = decodePrecondition(precondition) if (!decoded) { diff --git a/packages/wallet/core/src/relayer/standard/pk-relayer.ts b/packages/services/relayer/src/relayer/standard/pk-relayer.ts similarity index 96% rename from packages/wallet/core/src/relayer/standard/pk-relayer.ts rename to packages/services/relayer/src/relayer/standard/pk-relayer.ts index d680f67f77..37b2e5a08a 100644 --- a/packages/wallet/core/src/relayer/standard/pk-relayer.ts +++ b/packages/services/relayer/src/relayer/standard/pk-relayer.ts @@ -1,7 +1,8 @@ import { Payload, Precondition } from '@0xsequence/wallet-primitives' import { Address, Hex, Provider, Secp256k1, TransactionEnvelopeEip1559, TransactionReceipt } from 'ox' import { LocalRelayer } from './local.js' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../relayer.js' +import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' +import { FeeToken } from '../rpc-relayer/relayer.gen.js' export class PkRelayer implements Relayer { public readonly kind: 'relayer' = 'relayer' @@ -106,6 +107,10 @@ export class PkRelayer implements Relayer { return providerChainId === chainId } + feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + return this.relayer.feeTokens() + } + feeOptions( wallet: Address.Address, chainId: number, diff --git a/packages/wallet/core/src/relayer/standard/sequence.ts b/packages/services/relayer/src/relayer/standard/sequence.ts similarity index 82% rename from packages/wallet/core/src/relayer/standard/sequence.ts rename to packages/services/relayer/src/relayer/standard/sequence.ts index d99cd41cef..5c0bd16632 100644 --- a/packages/wallet/core/src/relayer/standard/sequence.ts +++ b/packages/services/relayer/src/relayer/standard/sequence.ts @@ -1,8 +1,7 @@ -import { ETHTxnStatus, IntentPrecondition, Relayer as Service } from '@0xsequence/relayer' +import { ETHTxnStatus, TransactionPrecondition, Relayer as Service, FeeToken } from '../rpc-relayer/relayer.gen.js' import { Payload } from '@0xsequence/wallet-primitives' import { AbiFunction, Address, Bytes, Hex } from 'ox' -import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../relayer.js' - +import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js' export class SequenceRelayer implements Relayer { public readonly kind: 'relayer' = 'relayer' public readonly type = 'sequence' @@ -18,6 +17,22 @@ export class SequenceRelayer implements Relayer { return true } + async feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + const { isFeeRequired, tokens, paymentAddress } = await this.service.feeTokens() + if (isFeeRequired) { + Address.assert(paymentAddress) + return { + isFeeRequired, + tokens, + paymentAddress, + } + } + // Not required + return { + isFeeRequired, + } + } + async feeOptions( wallet: Address.Address, _chainId: number, @@ -37,7 +52,7 @@ export class SequenceRelayer implements Relayer { } } - async checkPrecondition(precondition: IntentPrecondition): Promise { + async checkPrecondition(precondition: TransactionPrecondition): Promise { // TODO: implement return false } diff --git a/packages/services/relayer/src/rpc-relayer/index.ts b/packages/services/relayer/src/rpc-relayer/index.ts deleted file mode 100644 index 6e10f600bd..0000000000 --- a/packages/services/relayer/src/rpc-relayer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './relayer.gen.js' diff --git a/packages/services/relayer/src/rpc-relayer/relayer.gen.ts b/packages/services/relayer/src/rpc-relayer/relayer.gen.ts deleted file mode 100644 index 79ca492968..0000000000 --- a/packages/services/relayer/src/rpc-relayer/relayer.gen.ts +++ /dev/null @@ -1,1900 +0,0 @@ -/* eslint-disable */ -// sequence-relayer v0.4.1 62fe2b49d57c4a0d3960ac1176d48ecfffc7af5a -// -- -// Code generated by webrpc-gen@v0.26.0 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=relayer.ridl -target=typescript -client -out=./clients/relayer.gen.ts - -export const WebrpcHeader = "Webrpc" - -export const WebrpcHeaderValue = "webrpc@v0.26.0;gen-typescript@v0.17.0;sequence-relayer@v0.4.1" - -// WebRPC description and code-gen version -export const WebRPCVersion = "v1" - -// Schema version of your RIDL schema -export const WebRPCSchemaVersion = "v0.4.1" - -// Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = "62fe2b49d57c4a0d3960ac1176d48ecfffc7af5a" - -type WebrpcGenVersions = { - webrpcGenVersion: string; - codeGenName: string; - codeGenVersion: string; - schemaName: string; - schemaVersion: string; -}; - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader); - if (!headerValue) { - return { - webrpcGenVersion: "", - codeGenName: "", - codeGenVersion: "", - schemaName: "", - schemaVersion: "", - }; - } - - return parseWebrpcGenVersions(headerValue); -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(";"); - if (versions.length < 3) { - return { - webrpcGenVersion: "", - codeGenName: "", - codeGenVersion: "", - schemaName: "", - schemaVersion: "", - }; - } - - const [_, webrpcGenVersion] = versions[0]!.split("@"); - const [codeGenName, codeGenVersion] = versions[1]!.split("@"); - const [schemaName, schemaVersion] = versions[2]!.split("@"); - - return { - webrpcGenVersion: webrpcGenVersion ?? "", - codeGenName: codeGenName ?? "", - codeGenVersion: codeGenVersion ?? "", - schemaName: schemaName ?? "", - schemaVersion: schemaVersion ?? "", - }; -} - -// -// Types -// - - -export enum ETHTxnStatus { - UNKNOWN = 'UNKNOWN', - DROPPED = 'DROPPED', - QUEUED = 'QUEUED', - SENT = 'SENT', - SUCCEEDED = 'SUCCEEDED', - PARTIALLY_FAILED = 'PARTIALLY_FAILED', - FAILED = 'FAILED', - PENDING_PRECONDITION = 'PENDING_PRECONDITION' -} - -export enum TransferType { - SEND = 'SEND', - RECEIVE = 'RECEIVE', - BRIDGE_DEPOSIT = 'BRIDGE_DEPOSIT', - BRIDGE_WITHDRAW = 'BRIDGE_WITHDRAW', - BURN = 'BURN', - UNKNOWN = 'UNKNOWN' -} - -export enum SimulateStatus { - SKIPPED = 'SKIPPED', - SUCCEEDED = 'SUCCEEDED', - FAILED = 'FAILED', - ABORTED = 'ABORTED', - REVERTED = 'REVERTED', - NOT_ENOUGH_GAS = 'NOT_ENOUGH_GAS' -} - -export enum FeeTokenType { - UNKNOWN = 'UNKNOWN', - ERC20_TOKEN = 'ERC20_TOKEN', - ERC1155_TOKEN = 'ERC1155_TOKEN' -} - -export enum SortOrder { - DESC = 'DESC', - ASC = 'ASC' -} - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface RuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - useEIP1559: boolean - senders: Array - checks: RuntimeChecks -} - -export interface SenderStatus { - index: number - address: string - etherBalance: number - active: boolean -} - -export interface RuntimeChecks { -} - -export interface SequenceContext { - factory: string - mainModule: string - mainModuleUpgradable: string - guestModule: string - utils: string -} - -export interface GasTank { - id: number - chainId: number - name: string - currentBalance: number - unlimited: boolean - feeMarkupFactor: number - updatedAt: string - createdAt: string -} - -export interface GasTankBalanceAdjustment { - gasTankId: number - nonce: number - amount: number - totalBalance: number - balanceTimestamp: string - createdAt: string -} - -export interface GasSponsor { - id: number - gasTankId: number - projectId: number - chainId: number - address: string - name: string - active: boolean - updatedAt: string - createdAt: string - deletedAt: string -} - -export interface GasSponsorUsage { - name: string - id: number - totalGasUsed: number - totalTxnFees: number - totalTxnFeesUsd: number - avgGasPrice: number - totalTxns: number - startTime: string - endTime: string -} - -export interface MetaTxn { - walletAddress: string - contract: string - input: string -} - -export interface MetaTxnLog { - id: number - chainId: number - projectId: number - txnHash: string - txnNonce: string - metaTxnID?: string - txnStatus: ETHTxnStatus - txnRevertReason: string - requeues: number - queuedAt: string - sentAt: string - minedAt: string - target: string - input: string - txnArgs: {[key: string]: any} - txnReceipt?: {[key: string]: any} - walletAddress: string - metaTxnNonce: string - gasLimit: number - gasPrice: string - gasUsed: number - gasEstimated: number - gasFeeMarkup?: number - usdRate: string - creditsUsed: number - cost: string - isWhitelisted: boolean - gasSponsor?: number - gasTank?: number - updatedAt: string - createdAt: string -} - -export interface MetaTxnReceipt { - id: string - status: string - revertReason?: string - index: number - logs: Array - receipts: Array - blockNumber: string - txnHash: string - txnReceipt: string -} - -export interface MetaTxnReceiptLog { - address: string - topics: Array - data: string -} - -export interface IntentPrecondition { - type: string - chainId: string - data: any -} - -export interface IntentSolution { - transactions: Array -} - -export interface Transactions { - chainID: string - transactions: Array - preconditions?: Array -} - -export interface Transaction { - delegateCall: boolean - revertOnError: boolean - gasLimit: string - target: string - value: string - data: string -} - -export interface TxnLogUser { - username: string -} - -export interface TxnLogTransfer { - transferType: TransferType - contractAddress: string - from: string - to: string - ids: Array - amounts: Array -} - -export interface SentTransactionsFilter { - pending?: boolean - failed?: boolean -} - -export interface SimulateResult { - executed: boolean - succeeded: boolean - result?: string - reason?: string - gasUsed: number - gasLimit: number -} - -export interface SimulateV3Result { - status: SimulateStatus - result?: string - error?: string - gasUsed: number - gasLimit: number -} - -export interface FeeOption { - token: FeeToken - to: string - value: string - gasLimit: number -} - -export interface FeeToken { - chainId: number - name: string - symbol: string - type: FeeTokenType - decimals?: number - logoURL: string - contractAddress?: string - tokenID?: string -} - -export interface Page { - pageSize?: number - page?: number - more?: boolean - totalRecords?: number - column?: string - before?: any - after?: any - sort?: Array -} - -export interface SortBy { - column: string - order: SortOrder -} - -export interface Relayer { - ping(headers?: object, signal?: AbortSignal): Promise - version(headers?: object, signal?: AbortSignal): Promise - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - getSequenceContext(headers?: object, signal?: AbortSignal): Promise - getChainID(headers?: object, signal?: AbortSignal): Promise - /** - * - * Transactions - * - * TODO (future): rename this to just, 'SendTransaction(txn: MetaTransaction)' or 'SendTransaction(txn: SignedTransaction)', or something.. - * Project ID is only used by service and admin calls. Other clients must have projectID passed via the context - * TODO: rename return txnHash: string to metaTxnID: string - */ - sendMetaTxn(args: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise - getMetaTxnNonce(args: GetMetaTxnNonceArgs, headers?: object, signal?: AbortSignal): Promise - /** - * TODO: one day, make GetMetaTxnReceipt respond immediately with receipt or not - * and add WaitTransactionReceipt method, which will block and wait, similar to how GetMetaTxnReceipt - * is implemented now. - * For backwards compat, we can leave the current GetMetaTxnReceipt how it is, an deprecate it, and introduce - * new, GetTransactionReceipt and WaitTransactionReceipt methods - * we can also accept metaTxnId and txnHash .. so can take either or.. I wonder if ERC-4337 has any convention on this? - */ - getMetaTxnReceipt(args: GetMetaTxnReceiptArgs, headers?: object, signal?: AbortSignal): Promise - simulate(args: SimulateArgs, headers?: object, signal?: AbortSignal): Promise - simulateV3(args: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise - /** - * TODO: deprecated, to be removed by https://github.com/0xsequence/stack/pull/356 at a later date - */ - updateMetaTxnGasLimits(args: UpdateMetaTxnGasLimitsArgs, headers?: object, signal?: AbortSignal): Promise - feeTokens(headers?: object, signal?: AbortSignal): Promise - feeOptions(args: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * TODO: deprecated, to be removed by https://github.com/0xsequence/stack/pull/356 at a later date - */ - getMetaTxnNetworkFeeOptions(args: GetMetaTxnNetworkFeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise - getMetaTransactions(args: GetMetaTransactionsArgs, headers?: object, signal?: AbortSignal): Promise - getTransactionCost(args: GetTransactionCostArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Sent transactions from an account. If filter is omitted then it will return all transactions. - */ - sentTransactions(args: SentTransactionsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Pending transactions waiting to be mined for an account. This endpoint is just a sugar of `SentTransactions` - * with the filter set to pending: true. - */ - pendingTransactions(args: PendingTransactionsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Legacy Gas Tank - */ - getGasTank(args: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise - addGasTank(args: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise - updateGasTank(args: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Legacy Gas Adjustment - */ - nextGasTankBalanceAdjustmentNonce(args: NextGasTankBalanceAdjustmentNonceArgs, headers?: object, signal?: AbortSignal): Promise - adjustGasTankBalance(args: AdjustGasTankBalanceArgs, headers?: object, signal?: AbortSignal): Promise - getGasTankBalanceAdjustment(args: GetGasTankBalanceAdjustmentArgs, headers?: object, signal?: AbortSignal): Promise - listGasTankBalanceAdjustments(args: ListGasTankBalanceAdjustmentsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Gas Sponsorship - */ - listGasSponsors(args: ListGasSponsorsArgs, headers?: object, signal?: AbortSignal): Promise - getGasSponsor(args: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - addGasSponsor(args: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - updateGasSponsor(args: UpdateGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - removeGasSponsor(args: RemoveGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Gas Sponsor Lookup - */ - addressGasSponsors(args: AddressGasSponsorsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Project Balance - */ - getProjectBalance(args: GetProjectBalanceArgs, headers?: object, signal?: AbortSignal): Promise - adjustProjectBalance(args: AdjustProjectBalanceArgs, headers?: object, signal?: AbortSignal): Promise -} - -export interface PingArgs { -} - -export interface PingReturn { - status: boolean -} -export interface VersionArgs { -} - -export interface VersionReturn { - version: Version -} -export interface RuntimeStatusArgs { -} - -export interface RuntimeStatusReturn { - status: RuntimeStatus -} -export interface GetSequenceContextArgs { -} - -export interface GetSequenceContextReturn { - data: SequenceContext -} -export interface GetChainIDArgs { -} - -export interface GetChainIDReturn { - chainID: number -} -export interface SendMetaTxnArgs { - call: MetaTxn - quote?: string - projectID?: number - preconditions?: Array -} - -export interface SendMetaTxnReturn { - status: boolean - txnHash: string -} -export interface GetMetaTxnNonceArgs { - walletContractAddress: string - space?: string -} - -export interface GetMetaTxnNonceReturn { - nonce: string -} -export interface GetMetaTxnReceiptArgs { - metaTxID: string -} - -export interface GetMetaTxnReceiptReturn { - receipt: MetaTxnReceipt -} -export interface SimulateArgs { - wallet: string - transactions: string -} - -export interface SimulateReturn { - results: Array -} -export interface SimulateV3Args { - wallet: string - calls: string -} - -export interface SimulateV3Return { - results: Array -} -export interface UpdateMetaTxnGasLimitsArgs { - walletAddress: string - walletConfig: any - payload: string -} - -export interface UpdateMetaTxnGasLimitsReturn { - payload: string -} -export interface FeeTokensArgs { -} - -export interface FeeTokensReturn { - isFeeRequired: boolean - tokens: Array -} -export interface FeeOptionsArgs { - wallet: string - to: string - data: string - simulate?: boolean -} - -export interface FeeOptionsReturn { - options: Array - sponsored: boolean - quote?: string -} -export interface GetMetaTxnNetworkFeeOptionsArgs { - walletConfig: any - payload: string -} - -export interface GetMetaTxnNetworkFeeOptionsReturn { - options: Array -} -export interface GetMetaTransactionsArgs { - projectId: number - page?: Page -} - -export interface GetMetaTransactionsReturn { - page: Page - transactions: Array -} -export interface GetTransactionCostArgs { - projectId: number - from: string - to: string -} - -export interface GetTransactionCostReturn { - cost: number -} -export interface SentTransactionsArgs { - filter?: SentTransactionsFilter - page?: Page -} - -export interface SentTransactionsReturn { - page: Page - transactions: Array -} -export interface PendingTransactionsArgs { - page?: Page -} - -export interface PendingTransactionsReturn { - page: Page - transactions: Array -} -export interface GetGasTankArgs { - id: number -} - -export interface GetGasTankReturn { - gasTank: GasTank -} -export interface AddGasTankArgs { - name: string - feeMarkupFactor: number - unlimited?: boolean -} - -export interface AddGasTankReturn { - status: boolean - gasTank: GasTank -} -export interface UpdateGasTankArgs { - id: number - name?: string - feeMarkupFactor?: number - unlimited?: boolean -} - -export interface UpdateGasTankReturn { - status: boolean - gasTank: GasTank -} -export interface NextGasTankBalanceAdjustmentNonceArgs { - id: number -} - -export interface NextGasTankBalanceAdjustmentNonceReturn { - nonce: number -} -export interface AdjustGasTankBalanceArgs { - id: number - nonce: number - amount: number -} - -export interface AdjustGasTankBalanceReturn { - status: boolean - adjustment: GasTankBalanceAdjustment -} -export interface GetGasTankBalanceAdjustmentArgs { - id: number - nonce: number -} - -export interface GetGasTankBalanceAdjustmentReturn { - adjustment: GasTankBalanceAdjustment -} -export interface ListGasTankBalanceAdjustmentsArgs { - id: number - page?: Page -} - -export interface ListGasTankBalanceAdjustmentsReturn { - page: Page - adjustments: Array -} -export interface ListGasSponsorsArgs { - projectId: number - page?: Page -} - -export interface ListGasSponsorsReturn { - page: Page - gasSponsors: Array -} -export interface GetGasSponsorArgs { - projectId: number - id: number -} - -export interface GetGasSponsorReturn { - gasSponsor: GasSponsor -} -export interface AddGasSponsorArgs { - projectId: number - address: string - name?: string - active?: boolean -} - -export interface AddGasSponsorReturn { - status: boolean - gasSponsor: GasSponsor -} -export interface UpdateGasSponsorArgs { - projectId: number - id: number - name?: string - active?: boolean -} - -export interface UpdateGasSponsorReturn { - status: boolean - gasSponsor: GasSponsor -} -export interface RemoveGasSponsorArgs { - projectId: number - id: number -} - -export interface RemoveGasSponsorReturn { - status: boolean -} -export interface AddressGasSponsorsArgs { - address: string - page?: Page -} - -export interface AddressGasSponsorsReturn { - page: Page - gasSponsors: Array -} -export interface GetProjectBalanceArgs { - projectId: number -} - -export interface GetProjectBalanceReturn { - balance: number -} -export interface AdjustProjectBalanceArgs { - projectId: number - amount: number - identifier: string -} - -export interface AdjustProjectBalanceReturn { - balance: number -} - - - -// -// Client -// -export class Relayer implements Relayer { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Relayer/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('Ping'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('Version'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - version: (_data.version), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('RuntimeStatus'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getSequenceContext = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetSequenceContext'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - data: (_data.data), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getChainID = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetChainID'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - chainID: (_data.chainID), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sendMetaTxn = (args: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SendMetaTxn'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - txnHash: (_data.txnHash), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getMetaTxnNonce = (args: GetMetaTxnNonceArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetMetaTxnNonce'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - nonce: (_data.nonce), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getMetaTxnReceipt = (args: GetMetaTxnReceiptArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetMetaTxnReceipt'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - receipt: (_data.receipt), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - simulate = (args: SimulateArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('Simulate'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - results: >(_data.results), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - simulateV3 = (args: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SimulateV3'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - results: >(_data.results), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - updateMetaTxnGasLimits = (args: UpdateMetaTxnGasLimitsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('UpdateMetaTxnGasLimits'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - payload: (_data.payload), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - feeTokens = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('FeeTokens'), - createHTTPRequest({}, headers, signal) - ).then((res) => { - return buildResponse(res).then(_data => { - return { - isFeeRequired: (_data.isFeeRequired), - tokens: >(_data.tokens), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - feeOptions = (args: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('FeeOptions'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - options: >(_data.options), - sponsored: (_data.sponsored), - quote: (_data.quote), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getMetaTxnNetworkFeeOptions = (args: GetMetaTxnNetworkFeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetMetaTxnNetworkFeeOptions'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - options: >(_data.options), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getMetaTransactions = (args: GetMetaTransactionsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetMetaTransactions'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - transactions: >(_data.transactions), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getTransactionCost = (args: GetTransactionCostArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetTransactionCost'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - cost: (_data.cost), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - sentTransactions = (args: SentTransactionsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('SentTransactions'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - transactions: >(_data.transactions), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - pendingTransactions = (args: PendingTransactionsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('PendingTransactions'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - transactions: >(_data.transactions), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getGasTank = (args: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetGasTank'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - gasTank: (_data.gasTank), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - addGasTank = (args: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('AddGasTank'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - gasTank: (_data.gasTank), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - updateGasTank = (args: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('UpdateGasTank'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - gasTank: (_data.gasTank), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - nextGasTankBalanceAdjustmentNonce = (args: NextGasTankBalanceAdjustmentNonceArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('NextGasTankBalanceAdjustmentNonce'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - nonce: (_data.nonce), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - adjustGasTankBalance = (args: AdjustGasTankBalanceArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('AdjustGasTankBalance'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - adjustment: (_data.adjustment), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getGasTankBalanceAdjustment = (args: GetGasTankBalanceAdjustmentArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetGasTankBalanceAdjustment'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - adjustment: (_data.adjustment), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - listGasTankBalanceAdjustments = (args: ListGasTankBalanceAdjustmentsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('ListGasTankBalanceAdjustments'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - adjustments: >(_data.adjustments), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - listGasSponsors = (args: ListGasSponsorsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('ListGasSponsors'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - gasSponsors: >(_data.gasSponsors), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getGasSponsor = (args: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetGasSponsor'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - gasSponsor: (_data.gasSponsor), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - addGasSponsor = (args: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('AddGasSponsor'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - gasSponsor: (_data.gasSponsor), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - updateGasSponsor = (args: UpdateGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('UpdateGasSponsor'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - gasSponsor: (_data.gasSponsor), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - removeGasSponsor = (args: RemoveGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('RemoveGasSponsor'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - status: (_data.status), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - addressGasSponsors = (args: AddressGasSponsorsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('AddressGasSponsors'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - page: (_data.page), - gasSponsors: >(_data.gasSponsors), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - getProjectBalance = (args: GetProjectBalanceArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('GetProjectBalance'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - balance: (_data.balance), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - - adjustProjectBalance = (args: AdjustProjectBalanceArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch( - this.url('AdjustProjectBalance'), - createHTTPRequest(args, headers, signal)).then((res) => { - return buildResponse(res).then(_data => { - return { - balance: (_data.balance), - } - }) - }, (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }) - } - -} - - const createHTTPRequest = (body: object = {}, headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: {[key: string]: string} = { ...headers, 'Content-Type': 'application/json' } - reqHeaders[WebrpcHeader] = WebrpcHeaderValue - - return { - method: 'POST', - headers: reqHeaders, - body: JSON.stringify(body || {}), - signal - } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then(text => { - let data - try { - data = JSON.parse(text) - } catch(error) { - let message = '' - if (error instanceof Error) { - message = error.message - } - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${message}: response text: ${text}`}, - ) - } - if (!res.ok) { - const code: number = (typeof data.code === 'number') ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -// -// Errors -// - -export class WebrpcError extends Error { - name: string - code: number - message: string - status: number - cause?: string - - /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ - msg: string - - constructor(name: string, code: number, message: string, status: number, cause?: string) { - super(message) - this.name = name || 'WebrpcError' - this.code = typeof code === 'number' ? code : 0 - this.message = message || `endpoint error ${this.code}` - this.msg = this.message - this.status = typeof status === 'number' ? status : 0 - this.cause = cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) - } -} - -// Webrpc errors - -export class WebrpcEndpointError extends WebrpcError { - constructor( - name: string = 'WebrpcEndpoint', - code: number = 0, - message: string = `endpoint error`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = `request failed`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = `bad route`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = `bad method`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = `bad request`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = `bad response`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = `server panic`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = `internal error`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientDisconnectedError extends WebrpcError { - constructor( - name: string = 'WebrpcClientDisconnected', - code: number = -8, - message: string = `client disconnected`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = `stream lost`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = `stream finished`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - - -// Schema errors - -export class UnauthorizedError extends WebrpcError { - constructor( - name: string = 'Unauthorized', - code: number = 1000, - message: string = `Unauthorized access`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor( - name: string = 'PermissionDenied', - code: number = 1001, - message: string = `Permission denied`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor( - name: string = 'SessionExpired', - code: number = 1002, - message: string = `Session expired`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor( - name: string = 'MethodNotFound', - code: number = 1003, - message: string = `Method not found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor( - name: string = 'RequestConflict', - code: number = 1004, - message: string = `Conflict with target resource`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class AbortedError extends WebrpcError { - constructor( - name: string = 'Aborted', - code: number = 1005, - message: string = `Request aborted`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor( - name: string = 'Geoblocked', - code: number = 1006, - message: string = `Geoblocked region`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor( - name: string = 'RateLimited', - code: number = 1007, - message: string = `Rate-limited. Please slow down.`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor( - name: string = 'ProjectNotFound', - code: number = 1008, - message: string = `Project not found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class AccessKeyNotFoundError extends WebrpcError { - constructor( - name: string = 'AccessKeyNotFound', - code: number = 1101, - message: string = `Access key not found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) - } -} - -export class AccessKeyMismatchError extends WebrpcError { - constructor( - name: string = 'AccessKeyMismatch', - code: number = 1102, - message: string = `Access key mismatch`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) - } -} - -export class InvalidOriginError extends WebrpcError { - constructor( - name: string = 'InvalidOrigin', - code: number = 1103, - message: string = `Invalid origin for Access Key`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidOriginError.prototype) - } -} - -export class InvalidServiceError extends WebrpcError { - constructor( - name: string = 'InvalidService', - code: number = 1104, - message: string = `Service not enabled for Access key`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidServiceError.prototype) - } -} - -export class UnauthorizedUserError extends WebrpcError { - constructor( - name: string = 'UnauthorizedUser', - code: number = 1105, - message: string = `Unauthorized user`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedUserError.prototype) - } -} - -export class QuotaExceededError extends WebrpcError { - constructor( - name: string = 'QuotaExceeded', - code: number = 1200, - message: string = `Quota request exceeded`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QuotaExceededError.prototype) - } -} - -export class QuotaRateLimitError extends WebrpcError { - constructor( - name: string = 'QuotaRateLimit', - code: number = 1201, - message: string = `Quota rate limit exceeded`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QuotaRateLimitError.prototype) - } -} - -export class NoDefaultKeyError extends WebrpcError { - constructor( - name: string = 'NoDefaultKey', - code: number = 1300, - message: string = `No default access key found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NoDefaultKeyError.prototype) - } -} - -export class MaxAccessKeysError extends WebrpcError { - constructor( - name: string = 'MaxAccessKeys', - code: number = 1301, - message: string = `Access keys limit reached`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MaxAccessKeysError.prototype) - } -} - -export class AtLeastOneKeyError extends WebrpcError { - constructor( - name: string = 'AtLeastOneKey', - code: number = 1302, - message: string = `You need at least one Access Key`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor( - name: string = 'Timeout', - code: number = 1900, - message: string = `Request timed out`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor( - name: string = 'InvalidArgument', - code: number = 2001, - message: string = `Invalid argument`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class UnavailableError extends WebrpcError { - constructor( - name: string = 'Unavailable', - code: number = 2002, - message: string = `Unavailable resource`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnavailableError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor( - name: string = 'QueryFailed', - code: number = 2003, - message: string = `Query failed`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor( - name: string = 'NotFound', - code: number = 3000, - message: string = `Resource not found`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class InsufficientFeeError extends WebrpcError { - constructor( - name: string = 'InsufficientFee', - code: number = 3004, - message: string = `Insufficient fee`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InsufficientFeeError.prototype) - } -} - -export class NotEnoughBalanceError extends WebrpcError { - constructor( - name: string = 'NotEnoughBalance', - code: number = 3005, - message: string = `Not enough balance`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotEnoughBalanceError.prototype) - } -} - -export class SimulationFailedError extends WebrpcError { - constructor( - name: string = 'SimulationFailed', - code: number = 3006, - message: string = `Simulation failed`, - status: number = 0, - cause?: string - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SimulationFailedError.prototype) - } -} - - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - Aborted = 'Aborted', - Geoblocked = 'Geoblocked', - RateLimited = 'RateLimited', - ProjectNotFound = 'ProjectNotFound', - AccessKeyNotFound = 'AccessKeyNotFound', - AccessKeyMismatch = 'AccessKeyMismatch', - InvalidOrigin = 'InvalidOrigin', - InvalidService = 'InvalidService', - UnauthorizedUser = 'UnauthorizedUser', - QuotaExceeded = 'QuotaExceeded', - QuotaRateLimit = 'QuotaRateLimit', - NoDefaultKey = 'NoDefaultKey', - MaxAccessKeys = 'MaxAccessKeys', - AtLeastOneKey = 'AtLeastOneKey', - Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', - Unavailable = 'Unavailable', - QueryFailed = 'QueryFailed', - NotFound = 'NotFound', - InsufficientFee = 'InsufficientFee', - NotEnoughBalance = 'NotEnoughBalance', - SimulationFailed = 'SimulationFailed', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientDisconnected = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - Aborted = 1005, - Geoblocked = 1006, - RateLimited = 1007, - ProjectNotFound = 1008, - AccessKeyNotFound = 1101, - AccessKeyMismatch = 1102, - InvalidOrigin = 1103, - InvalidService = 1104, - UnauthorizedUser = 1105, - QuotaExceeded = 1200, - QuotaRateLimit = 1201, - NoDefaultKey = 1300, - MaxAccessKeys = 1301, - AtLeastOneKey = 1302, - Timeout = 1900, - InvalidArgument = 2001, - Unavailable = 2002, - QueryFailed = 2003, - NotFound = 3000, - InsufficientFee = 3004, - NotEnoughBalance = 3005, - SimulationFailed = 3006, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [1008]: ProjectNotFoundError, - [1101]: AccessKeyNotFoundError, - [1102]: AccessKeyMismatchError, - [1103]: InvalidOriginError, - [1104]: InvalidServiceError, - [1105]: UnauthorizedUserError, - [1200]: QuotaExceededError, - [1201]: QuotaRateLimitError, - [1300]: NoDefaultKeyError, - [1301]: MaxAccessKeysError, - [1302]: AtLeastOneKeyError, - [1900]: TimeoutError, - [2001]: InvalidArgumentError, - [2002]: UnavailableError, - [2003]: QueryFailedError, - [3000]: NotFoundError, - [3004]: InsufficientFeeError, - [3005]: NotEnoughBalanceError, - [3006]: SimulationFailedError, -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise - diff --git a/packages/wallet/core/test/preconditions/codec.test.ts b/packages/services/relayer/test/preconditions/codec.test.ts similarity index 69% rename from packages/wallet/core/test/preconditions/codec.test.ts rename to packages/services/relayer/test/preconditions/codec.test.ts index f67a016fa9..88d442510a 100644 --- a/packages/wallet/core/test/preconditions/codec.test.ts +++ b/packages/services/relayer/test/preconditions/codec.test.ts @@ -5,7 +5,7 @@ import { decodePrecondition, decodePreconditions, encodePrecondition, - IntentPrecondition, + TransactionPrecondition, } from '../../src/preconditions/codec.js' import { NativeBalancePrecondition, @@ -21,6 +21,8 @@ import { const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') const OPERATOR_ADDRESS = Address.from('0x9876543210987654321098765432109876543210') +const ARBITRUM_CHAIN_ID = 42161 +const NATIVE_TOKEN_ADDRESS = Address.from('0x0000000000000000000000000000000000000000') describe('Preconditions Codec', () => { // Mock console.warn to test error logging @@ -38,33 +40,13 @@ describe('Preconditions Codec', () => { expect(decodePrecondition(undefined as any)).toBeUndefined() }) - it('should decode native balance precondition with min and max', () => { - const intent: IntentPrecondition = { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - max: '2000000000000000000', - }), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(NativeBalancePrecondition) - - const precondition = result as NativeBalancePrecondition - expect(precondition.address).toBe(TEST_ADDRESS) - expect(precondition.min).toBe(1000000000000000000n) - expect(precondition.max).toBe(2000000000000000000n) - expect(precondition.type()).toBe('native-balance') - }) - it('should decode native balance precondition with only min', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000000000000000'), } const result = decodePrecondition(intent) @@ -75,32 +57,13 @@ describe('Preconditions Codec', () => { expect(precondition.max).toBeUndefined() }) - it('should decode native balance precondition with only max', () => { - const intent: IntentPrecondition = { - type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - max: '2000000000000000000', - }), - } - - const result = decodePrecondition(intent) - expect(result).toBeInstanceOf(NativeBalancePrecondition) - - const precondition = result as NativeBalancePrecondition - expect(precondition.min).toBeUndefined() - expect(precondition.max).toBe(2000000000000000000n) - }) - it('should decode ERC20 balance precondition', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - min: '1000000', - max: '2000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000'), } const result = decodePrecondition(intent) @@ -110,18 +73,16 @@ describe('Preconditions Codec', () => { expect(precondition.address).toBe(TEST_ADDRESS) expect(precondition.token).toBe(TOKEN_ADDRESS) expect(precondition.min).toBe(1000000n) - expect(precondition.max).toBe(2000000n) + expect(precondition.max).toBeUndefined() }) it('should decode ERC20 approval precondition', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'erc20-approval', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - operator: OPERATOR_ADDRESS, - min: '1000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000'), } const result = decodePrecondition(intent) @@ -130,19 +91,17 @@ describe('Preconditions Codec', () => { const precondition = result as Erc20ApprovalPrecondition expect(precondition.address).toBe(TEST_ADDRESS) expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.operator).toBe(OPERATOR_ADDRESS) + expect(precondition.operator).toBe(TEST_ADDRESS) expect(precondition.min).toBe(1000000n) }) it('should decode ERC721 ownership precondition', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'erc721-ownership', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - tokenId: '123', - owned: true, - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('0'), } const result = decodePrecondition(intent) @@ -151,36 +110,33 @@ describe('Preconditions Codec', () => { const precondition = result as Erc721OwnershipPrecondition expect(precondition.address).toBe(TEST_ADDRESS) expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(123n) + expect(precondition.tokenId).toBe(0n) expect(precondition.owned).toBe(true) }) it('should decode ERC721 ownership precondition without owned flag', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'erc721-ownership', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - tokenId: '123', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('0'), } const result = decodePrecondition(intent) expect(result).toBeInstanceOf(Erc721OwnershipPrecondition) const precondition = result as Erc721OwnershipPrecondition - expect(precondition.owned).toBeUndefined() + expect(precondition.owned).toBe(true) }) it('should decode ERC721 approval precondition', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'erc721-approval', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - tokenId: '123', - operator: OPERATOR_ADDRESS, - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('0'), } const result = decodePrecondition(intent) @@ -189,20 +145,17 @@ describe('Preconditions Codec', () => { const precondition = result as Erc721ApprovalPrecondition expect(precondition.address).toBe(TEST_ADDRESS) expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(123n) - expect(precondition.operator).toBe(OPERATOR_ADDRESS) + expect(precondition.tokenId).toBe(0n) + expect(precondition.operator).toBe(TEST_ADDRESS) }) it('should decode ERC1155 balance precondition', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'erc1155-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - tokenId: '123', - min: '1000000', - max: '2000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000'), } const result = decodePrecondition(intent) @@ -211,21 +164,18 @@ describe('Preconditions Codec', () => { const precondition = result as Erc1155BalancePrecondition expect(precondition.address).toBe(TEST_ADDRESS) expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(123n) + expect(precondition.tokenId).toBe(0n) expect(precondition.min).toBe(1000000n) - expect(precondition.max).toBe(2000000n) + expect(precondition.max).toBeUndefined() }) it('should decode ERC1155 approval precondition', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'erc1155-approval', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - tokenId: '123', - operator: OPERATOR_ADDRESS, - min: '1000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000'), } const result = decodePrecondition(intent) @@ -234,15 +184,18 @@ describe('Preconditions Codec', () => { const precondition = result as Erc1155ApprovalPrecondition expect(precondition.address).toBe(TEST_ADDRESS) expect(precondition.token).toBe(TOKEN_ADDRESS) - expect(precondition.tokenId).toBe(123n) - expect(precondition.operator).toBe(OPERATOR_ADDRESS) + expect(precondition.tokenId).toBe(0n) + expect(precondition.operator).toBe(TEST_ADDRESS) expect(precondition.min).toBe(1000000n) }) it('should return undefined for unknown precondition type', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'unknown-type', - data: JSON.stringify({ address: TEST_ADDRESS }), + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('0'), } const result = decodePrecondition(intent) @@ -250,36 +203,38 @@ describe('Preconditions Codec', () => { }) it('should return undefined and log warning for invalid JSON', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'native-balance', - data: 'invalid json', + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000000000000000'), } const result = decodePrecondition(intent) - expect(result).toBeUndefined() - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Failed to decode precondition')) + expect(result).toBeInstanceOf(NativeBalancePrecondition) }) it('should return undefined and log warning for invalid precondition', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'native-balance', - data: JSON.stringify({ - // Missing required address field - min: '1000000000000000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('2000000000000000000'), } const result = decodePrecondition(intent) - expect(result).toBeUndefined() - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Failed to decode precondition')) + expect(result).toBeInstanceOf(NativeBalancePrecondition) }) it('should handle malformed addresses gracefully', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'native-balance', - data: JSON.stringify({ - address: 'invalid-address', - }), + ownerAddress: 'invalid-address' as any, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000000000000000'), } const result = decodePrecondition(intent) @@ -288,12 +243,12 @@ describe('Preconditions Codec', () => { }) it('should handle malformed BigInt values gracefully', () => { - const intent: IntentPrecondition = { + const intent: TransactionPrecondition = { type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: 'not-a-number', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: 'not-a-number' as any, } const result = decodePrecondition(intent) @@ -302,38 +257,38 @@ describe('Preconditions Codec', () => { }) it('should return undefined and log warning for precondition that fails validation', () => { - const intent: IntentPrecondition = { + // Note: NativeBalancePrecondition validation only checks min > max if both are defined + // Since TransactionPrecondition doesn't have max, this test may not trigger validation error + // But we can test with a valid precondition that should pass + const intent: TransactionPrecondition = { type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '2000000000000000000', // min > max should fail validation - max: '1000000000000000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000000000000000'), } const result = decodePrecondition(intent) - expect(result).toBeUndefined() - expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Invalid precondition')) + expect(result).toBeInstanceOf(NativeBalancePrecondition) }) }) describe('decodePreconditions', () => { it('should decode multiple preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000000000000000'), }, { type: 'erc20-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - token: TOKEN_ADDRESS, - min: '1000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000'), }, ] @@ -344,21 +299,27 @@ describe('Preconditions Codec', () => { }) it('should filter out invalid preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', - data: JSON.stringify({ - address: TEST_ADDRESS, - min: '1000000000000000000', - }), + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000000000000000'), }, { type: 'invalid-type', - data: JSON.stringify({ address: TEST_ADDRESS }), + ownerAddress: TEST_ADDRESS, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('0'), }, { type: 'native-balance', - data: 'invalid json', + ownerAddress: 'invalid-address' as any, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('1000000000000000000'), }, ] @@ -505,15 +466,20 @@ describe('Preconditions Codec', () => { const original = new NativeBalancePrecondition(TEST_ADDRESS, 1000000000000000000n, 2000000000000000000n) const encoded = encodePrecondition(original) - const intent: IntentPrecondition = { + const data = JSON.parse(encoded) + const intent: TransactionPrecondition = { type: original.type(), - data: encoded, + ownerAddress: data.address, + tokenAddress: NATIVE_TOKEN_ADDRESS, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt(data.min), } const decoded = decodePrecondition(intent) as NativeBalancePrecondition expect(decoded.address).toBe(original.address) expect(decoded.min).toBe(original.min) - expect(decoded.max).toBe(original.max) + // Note: max is not preserved in TransactionPrecondition format + expect(decoded.max).toBeUndefined() expect(decoded.type()).toBe(original.type()) }) @@ -521,16 +487,21 @@ describe('Preconditions Codec', () => { const original = new Erc20BalancePrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 1000000n, 2000000n) const encoded = encodePrecondition(original) - const intent: IntentPrecondition = { + const data = JSON.parse(encoded) + const intent: TransactionPrecondition = { type: original.type(), - data: encoded, + ownerAddress: data.address, + tokenAddress: data.token, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt(data.min), } const decoded = decodePrecondition(intent) as Erc20BalancePrecondition expect(decoded.address).toBe(original.address) expect(decoded.token).toBe(original.token) expect(decoded.min).toBe(original.min) - expect(decoded.max).toBe(original.max) + // Note: max is not preserved in TransactionPrecondition format + expect(decoded.max).toBeUndefined() expect(decoded.type()).toBe(original.type()) }) @@ -538,16 +509,22 @@ describe('Preconditions Codec', () => { const original = new Erc721OwnershipPrecondition(TEST_ADDRESS, TOKEN_ADDRESS, 123n, true) const encoded = encodePrecondition(original) - const intent: IntentPrecondition = { + const data = JSON.parse(encoded) + const intent: TransactionPrecondition = { type: original.type(), - data: encoded, + ownerAddress: data.address, + tokenAddress: data.token, + chainId: ARBITRUM_CHAIN_ID, + minAmount: BigInt('0'), } const decoded = decodePrecondition(intent) as Erc721OwnershipPrecondition expect(decoded.address).toBe(original.address) expect(decoded.token).toBe(original.token) - expect(decoded.tokenId).toBe(original.tokenId) - expect(decoded.owned).toBe(original.owned) + // Note: tokenId is not preserved in TransactionPrecondition format (defaults to 0) + expect(decoded.tokenId).toBe(0n) + // Note: owned is hardcoded to true in decoder + expect(decoded.owned).toBe(true) expect(decoded.type()).toBe(original.type()) }) }) diff --git a/packages/wallet/core/test/preconditions.test.ts b/packages/services/relayer/test/preconditions/preconditions.test.ts similarity index 98% rename from packages/wallet/core/test/preconditions.test.ts rename to packages/services/relayer/test/preconditions/preconditions.test.ts index af067e95b7..e4975daaf9 100644 --- a/packages/wallet/core/test/preconditions.test.ts +++ b/packages/services/relayer/test/preconditions/preconditions.test.ts @@ -8,9 +8,9 @@ import { Erc721ApprovalPrecondition, Erc721OwnershipPrecondition, NativeBalancePrecondition, -} from '../src/preconditions/types' -import { LocalRelayer } from '../src/relayer/standard/local' -import { CAN_RUN_LIVE, RPC_URL } from './constants' +} from '../../src/preconditions/types.js' +import { LocalRelayer } from '../../src/standard/local.js' +import { CAN_RUN_LIVE, RPC_URL } from '../../../../wallet/core/test/constants' import { Network } from '@0xsequence/wallet-primitives' const ERC20_IMPLICIT_MINT_CONTRACT = '0x041E0CDC028050519C8e6485B2d9840caf63773F' diff --git a/packages/wallet/core/test/preconditions/selectors.test.ts b/packages/services/relayer/test/preconditions/selectors.test.ts similarity index 92% rename from packages/wallet/core/test/preconditions/selectors.test.ts rename to packages/services/relayer/test/preconditions/selectors.test.ts index 36fe6e5f5c..7fdc008add 100644 --- a/packages/wallet/core/test/preconditions/selectors.test.ts +++ b/packages/services/relayer/test/preconditions/selectors.test.ts @@ -7,7 +7,7 @@ import { extractNativeBalancePreconditions, extractERC20BalancePreconditions, } from '../../src/preconditions/selectors.js' -import { IntentPrecondition } from '../../src/preconditions/codec.js' +import { TransactionPrecondition } from '../../src/preconditions/codec.js' import { NativeBalancePrecondition, Erc20BalancePrecondition, @@ -22,7 +22,7 @@ const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') describe('Preconditions Selectors', () => { describe('extractChainID', () => { it('should extract chainID from valid precondition data', () => { - const precondition: IntentPrecondition = { + const precondition: TransactionPrecondition = { type: 'native-balance', data: JSON.stringify({ address: TEST_ADDRESS, @@ -36,7 +36,7 @@ describe('Preconditions Selectors', () => { }) it('should extract large chainID values', () => { - const precondition: IntentPrecondition = { + const precondition: TransactionPrecondition = { type: 'native-balance', data: JSON.stringify({ address: TEST_ADDRESS, @@ -49,7 +49,7 @@ describe('Preconditions Selectors', () => { }) it('should return undefined when chainID is not present', () => { - const precondition: IntentPrecondition = { + const precondition: TransactionPrecondition = { type: 'native-balance', data: JSON.stringify({ address: TEST_ADDRESS, @@ -62,7 +62,7 @@ describe('Preconditions Selectors', () => { }) it('should return undefined when chainID is falsy', () => { - const precondition: IntentPrecondition = { + const precondition: TransactionPrecondition = { type: 'native-balance', data: JSON.stringify({ address: TEST_ADDRESS, @@ -76,7 +76,7 @@ describe('Preconditions Selectors', () => { }) it('should return undefined when chainID is null', () => { - const precondition: IntentPrecondition = { + const precondition: TransactionPrecondition = { type: 'native-balance', data: JSON.stringify({ address: TEST_ADDRESS, @@ -95,7 +95,7 @@ describe('Preconditions Selectors', () => { }) it('should return undefined for invalid JSON', () => { - const precondition: IntentPrecondition = { + const precondition: TransactionPrecondition = { type: 'native-balance', data: 'invalid json', } @@ -105,7 +105,7 @@ describe('Preconditions Selectors', () => { }) it('should handle chainID with value 0', () => { - const precondition: IntentPrecondition = { + const precondition: TransactionPrecondition = { type: 'native-balance', data: JSON.stringify({ address: TEST_ADDRESS, @@ -120,7 +120,7 @@ describe('Preconditions Selectors', () => { describe('extractSupportedPreconditions', () => { it('should extract valid preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', data: JSON.stringify({ @@ -145,7 +145,7 @@ describe('Preconditions Selectors', () => { }) it('should filter out invalid preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', data: JSON.stringify({ @@ -179,7 +179,7 @@ describe('Preconditions Selectors', () => { }) it('should handle mixed valid and invalid preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', data: JSON.stringify({ @@ -210,7 +210,7 @@ describe('Preconditions Selectors', () => { describe('extractNativeBalancePreconditions', () => { it('should extract only native balance preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', data: JSON.stringify({ @@ -246,7 +246,7 @@ describe('Preconditions Selectors', () => { }) it('should return empty array when no native balance preconditions exist', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'erc20-balance', data: JSON.stringify({ @@ -280,7 +280,7 @@ describe('Preconditions Selectors', () => { }) it('should filter out invalid native balance preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', data: JSON.stringify({ @@ -310,7 +310,7 @@ describe('Preconditions Selectors', () => { describe('extractERC20BalancePreconditions', () => { it('should extract only ERC20 balance preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', data: JSON.stringify({ @@ -349,7 +349,7 @@ describe('Preconditions Selectors', () => { }) it('should return empty array when no ERC20 balance preconditions exist', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'native-balance', data: JSON.stringify({ @@ -382,7 +382,7 @@ describe('Preconditions Selectors', () => { }) it('should filter out invalid ERC20 balance preconditions', () => { - const intents: IntentPrecondition[] = [ + const intents: TransactionPrecondition[] = [ { type: 'erc20-balance', data: JSON.stringify({ diff --git a/packages/wallet/core/test/preconditions/types.test.ts b/packages/services/relayer/test/preconditions/types.test.ts similarity index 100% rename from packages/wallet/core/test/preconditions/types.test.ts rename to packages/services/relayer/test/preconditions/types.test.ts diff --git a/packages/wallet/core/test/relayer/relayer.test.ts b/packages/services/relayer/test/relayer/relayer.test.ts similarity index 86% rename from packages/wallet/core/test/relayer/relayer.test.ts rename to packages/services/relayer/test/relayer/relayer.test.ts index d0f4cba93f..adbadd236c 100644 --- a/packages/wallet/core/test/relayer/relayer.test.ts +++ b/packages/services/relayer/test/relayer/relayer.test.ts @@ -1,20 +1,7 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' import { Address, Hex } from 'ox' -import { Network, Payload, Precondition } from '@0xsequence/wallet-primitives' -import { - Relayer, - isRelayer, - FeeOption, - FeeQuote, - OperationStatus, - OperationUnknownStatus, - OperationQueuedStatus, - OperationPendingStatus, - OperationPendingPreconditionStatus, - OperationConfirmedStatus, - OperationFailedStatus, -} from '../../src/relayer/relayer.js' -import { FeeTokenType } from '../../src/relayer/standard/rpc/index.js' +import { Network, Payload } from '@0xsequence/wallet-primitives' +import { Relayer, RelayerGen } from '@0xsequence/relayer' // Test addresses and data const TEST_WALLET_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') @@ -24,20 +11,21 @@ const TEST_CHAIN_ID = Network.ChainId.MAINNET const TEST_OP_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') describe('Relayer', () => { - describe('isRelayer type guard', () => { + describe('Relayer.isRelayer type guard', () => { it('should return true for valid relayer objects', () => { - const mockRelayer: Relayer = { + const mockRelayer: Relayer.Relayer = { kind: 'relayer', type: 'test', id: 'test-relayer', isAvailable: vi.fn(), + feeTokens: vi.fn(), feeOptions: vi.fn(), relay: vi.fn(), status: vi.fn(), checkPrecondition: vi.fn(), } - expect(isRelayer(mockRelayer)).toBe(true) + expect(Relayer.isRelayer(mockRelayer)).toBe(true) }) it('should return false for objects missing required methods', () => { @@ -51,7 +39,7 @@ describe('Relayer', () => { status: vi.fn(), checkPrecondition: vi.fn(), } - expect(isRelayer(missing1)).toBe(false) + expect(Relayer.isRelayer(missing1)).toBe(false) // Missing feeOptions const missing2 = { @@ -63,7 +51,7 @@ describe('Relayer', () => { status: vi.fn(), checkPrecondition: vi.fn(), } - expect(isRelayer(missing2)).toBe(false) + expect(Relayer.isRelayer(missing2)).toBe(false) // Missing relay const missing3 = { @@ -75,7 +63,7 @@ describe('Relayer', () => { status: vi.fn(), checkPrecondition: vi.fn(), } - expect(isRelayer(missing3)).toBe(false) + expect(Relayer.isRelayer(missing3)).toBe(false) // Missing status const missing4 = { @@ -87,7 +75,7 @@ describe('Relayer', () => { relay: vi.fn(), checkPrecondition: vi.fn(), } - expect(isRelayer(missing4)).toBe(false) + expect(Relayer.isRelayer(missing4)).toBe(false) // Missing checkPrecondition const missing5 = { @@ -99,18 +87,18 @@ describe('Relayer', () => { relay: vi.fn(), status: vi.fn(), } - expect(isRelayer(missing5)).toBe(false) + expect(Relayer.isRelayer(missing5)).toBe(false) }) it('should return false for non-objects', () => { // These will throw due to the 'in' operator, so we need to test the actual behavior - expect(() => isRelayer(null)).toThrow() - expect(() => isRelayer(undefined)).toThrow() - expect(() => isRelayer('string')).toThrow() - expect(() => isRelayer(123)).toThrow() - expect(() => isRelayer(true)).toThrow() + expect(() => Relayer.isRelayer(null)).toThrow() + expect(() => Relayer.isRelayer(undefined)).toThrow() + expect(() => Relayer.isRelayer('string')).toThrow() + expect(() => Relayer.isRelayer(123)).toThrow() + expect(() => Relayer.isRelayer(true)).toThrow() // Arrays and objects should not throw, but should return false - expect(isRelayer([])).toBe(false) + expect(Relayer.isRelayer([])).toBe(false) }) it('should return false for objects with properties but wrong types', () => { @@ -126,20 +114,20 @@ describe('Relayer', () => { } // The current implementation only checks if properties exist, not their types // So this will actually return true since all required properties exist - expect(isRelayer(wrongTypes)).toBe(true) + expect(Relayer.isRelayer(wrongTypes)).toBe(true) }) }) describe('FeeOption interface', () => { it('should accept valid fee option objects', () => { - const feeOption: FeeOption = { + const feeOption: Relayer.FeeOption = { token: { chainId: Network.ChainId.MAINNET, name: 'Ethereum', symbol: 'ETH', decimals: 18, logoURL: 'https://example.com/eth.png', - type: 'NATIVE' as FeeTokenType, + type: 'NATIVE' as RelayerGen.FeeTokenType, contractAddress: undefined, }, to: TEST_TO_ADDRESS, @@ -156,7 +144,7 @@ describe('Relayer', () => { describe('FeeQuote interface', () => { it('should accept valid fee quote objects', () => { - const feeQuote: FeeQuote = { + const feeQuote: Relayer.FeeQuote = { _tag: 'FeeQuote', _quote: { someQuoteData: 'value' }, } @@ -168,7 +156,7 @@ describe('Relayer', () => { describe('OperationStatus types', () => { it('should accept OperationUnknownStatus', () => { - const status: OperationUnknownStatus = { + const status: Relayer.OperationUnknownStatus = { status: 'unknown', reason: 'Transaction not found', } @@ -178,7 +166,7 @@ describe('Relayer', () => { }) it('should accept OperationQueuedStatus', () => { - const status: OperationQueuedStatus = { + const status: Relayer.OperationQueuedStatus = { status: 'queued', reason: 'Transaction queued for processing', } @@ -188,7 +176,7 @@ describe('Relayer', () => { }) it('should accept OperationPendingStatus', () => { - const status: OperationPendingStatus = { + const status: Relayer.OperationPendingStatus = { status: 'pending', reason: 'Transaction pending confirmation', } @@ -198,7 +186,7 @@ describe('Relayer', () => { }) it('should accept OperationPendingPreconditionStatus', () => { - const status: OperationPendingPreconditionStatus = { + const status: Relayer.OperationPendingPreconditionStatus = { status: 'pending-precondition', reason: 'Waiting for preconditions to be met', } @@ -208,7 +196,7 @@ describe('Relayer', () => { }) it('should accept OperationConfirmedStatus', () => { - const status: OperationConfirmedStatus = { + const status: Relayer.OperationConfirmedStatus = { status: 'confirmed', transactionHash: TEST_OP_HASH, data: { @@ -231,7 +219,7 @@ describe('Relayer', () => { }) it('should accept OperationFailedStatus', () => { - const status: OperationFailedStatus = { + const status: Relayer.OperationFailedStatus = { status: 'failed', transactionHash: TEST_OP_HASH, reason: 'Transaction reverted', @@ -256,7 +244,7 @@ describe('Relayer', () => { }) it('should handle OperationStatus union type', () => { - const statuses: OperationStatus[] = [ + const statuses: Relayer.OperationStatus[] = [ { status: 'unknown' }, { status: 'queued' }, { status: 'pending' }, @@ -272,7 +260,7 @@ describe('Relayer', () => { }) describe('Relayer interface contract', () => { - let mockRelayer: Relayer + let mockRelayer: Relayer.Relayer beforeEach(() => { mockRelayer = { @@ -280,6 +268,7 @@ describe('Relayer', () => { type: 'mock', id: 'mock-relayer', isAvailable: vi.fn(), + feeTokens: vi.fn(), feeOptions: vi.fn(), relay: vi.fn(), status: vi.fn(), diff --git a/packages/services/userdata/CHANGELOG.md b/packages/services/userdata/CHANGELOG.md new file mode 100644 index 0000000000..b28ab5220c --- /dev/null +++ b/packages/services/userdata/CHANGELOG.md @@ -0,0 +1,13 @@ +# @0xsequence/userdata + +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 diff --git a/packages/services/userdata/README.md b/packages/services/userdata/README.md new file mode 100644 index 0000000000..2387b56e2e --- /dev/null +++ b/packages/services/userdata/README.md @@ -0,0 +1,3 @@ +# @0xsequence/userdata + +See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/services/userdata/package.json b/packages/services/userdata/package.json new file mode 100644 index 0000000000..3d2fd79e98 --- /dev/null +++ b/packages/services/userdata/package.json @@ -0,0 +1,28 @@ +{ + "name": "@0xsequence/userdata", + "version": "3.0.0-beta.6", + "description": "userdata sub-package for Sequence", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/userdata", + "author": "Sequence Platforms Inc.", + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "test": "echo", + "typecheck": "tsc --noEmit" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "devDependencies": { + "@repo/typescript-config": "workspace:^", + "@types/node": "^25.0.2", + "typescript": "^5.9.3" + } +} diff --git a/packages/services/userdata/src/index.ts b/packages/services/userdata/src/index.ts new file mode 100644 index 0000000000..af76930fc8 --- /dev/null +++ b/packages/services/userdata/src/index.ts @@ -0,0 +1,36 @@ +export * from './userdata.gen' + +import { UserData as UserdataRpc } from './userdata.gen' + +export class SequenceUserdataClient extends UserdataRpc { + constructor( + hostname: string, + public projectAccessKey?: string, + public jwtAuth?: string, + ) { + super(hostname.endsWith('/') ? hostname.slice(0, -1) : hostname, fetch) + this.fetch = this._fetch + } + + _fetch = (input: RequestInfo, init?: RequestInit): Promise => { + // automatically include jwt and access key auth header to requests + // if its been set on the api client + const headers: { [key: string]: any } = {} + + const jwtAuth = this.jwtAuth + const projectAccessKey = this.projectAccessKey + + if (jwtAuth && jwtAuth.length > 0) { + headers['Authorization'] = `BEARER ${jwtAuth}` + } + + if (projectAccessKey && projectAccessKey.length > 0) { + headers['X-Access-Key'] = projectAccessKey + } + + // before the request is made + init!.headers = { ...init!.headers, ...headers } + + return fetch(input, init) + } +} diff --git a/packages/services/userdata/src/userdata.gen.ts b/packages/services/userdata/src/userdata.gen.ts new file mode 100644 index 0000000000..a26fdb9950 --- /dev/null +++ b/packages/services/userdata/src/userdata.gen.ts @@ -0,0 +1,686 @@ +/* eslint-disable */ +// userdata v0.1.0 99a19ff0218eda6f5e544642d0fd72f66736bdaf +// -- +// Code generated by Webrpc-gen@v0.30.2 with typescript generator. DO NOT EDIT. +// +// webrpc-gen -schema=userdata.ridl -target=typescript -client -out=./clients/userdata.gen.ts + +// Webrpc description and code-gen version +export const WebrpcVersion = 'v1' + +// Schema version of your RIDL schema +export const WebrpcSchemaVersion = 'v0.1.0' + +// Schema hash generated from your RIDL schema +export const WebrpcSchemaHash = '99a19ff0218eda6f5e544642d0fd72f66736bdaf' + +// +// Client interface +// + +export interface UserDataClient { + getCapabilities(headers?: object, signal?: AbortSignal): Promise + + getAccessToken(req: GetAccessTokenRequest, headers?: object, signal?: AbortSignal): Promise + + getIdentityToken( + req: GetIdentityTokenRequest, + headers?: object, + signal?: AbortSignal, + ): Promise +} + +// +// Schema types +// + +export interface Wallet { + address: string + ecosystem: number +} + +export interface Signer { + address: string + kind: string + email?: string +} + +export interface WalletSigner { + walletAddress: string + signerAddress: string +} + +export interface Session { + walletAddress: string + sessionAddress: string + ipAddress: string + userAgent: string + originUrl: string + appUrl: string + createdAt: string +} + +export interface SessionProps { + address: string + appUrl: string +} + +export interface GetCapabilitiesRequest {} + +export interface GetCapabilitiesResponse { + supportedMethods: Array +} + +export interface GetAccessTokenRequest { + ethauthProof: string + chainId: string +} + +export interface GetAccessTokenResponse { + accessToken: string + refreshToken: string + expiresIn: number +} + +export interface GetIdentityTokenRequest { + claims: { [key: string]: any } +} + +export interface GetIdentityTokenResponse { + idToken: string +} + +// +// Client +// + +export class UserData implements UserDataClient { + protected hostname: string + protected fetch: Fetch + protected path = '/rpc/UserData/' + + constructor(hostname: string, fetch: Fetch) { + this.hostname = hostname.replace(/\/*$/, '') + this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) + } + + private url(name: string): string { + return this.hostname + this.path + name + } + + queryKey = { + getCapabilities: () => ['UserData', 'getCapabilities'] as const, + getAccessToken: (req: GetAccessTokenRequest) => ['UserData', 'getAccessToken', req] as const, + getIdentityToken: (req: GetIdentityTokenRequest) => ['UserData', 'getIdentityToken', req] as const, + } + + getCapabilities = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetCapabilities'), createHttpRequest('{}', headers, signal)).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetCapabilitiesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getAccessToken = ( + req: GetAccessTokenRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetAccessToken'), + createHttpRequest(JsonEncode(req, 'GetAccessTokenRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetAccessTokenResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + getIdentityToken = ( + req: GetIdentityTokenRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetIdentityToken'), + createHttpRequest(JsonEncode(req, 'GetIdentityTokenRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetIdentityTokenResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } +} + +const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { + const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } + return { method: 'POST', headers: reqHeaders, body, signal } +} + +const buildResponse = (res: Response): Promise => { + return res.text().then((text) => { + let data + try { + data = JSON.parse(text) + } catch (error) { + throw WebrpcBadResponseError.new({ + status: res.status, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}`, + }) + } + if (!res.ok) { + const code: number = typeof data.code === 'number' ? data.code : 0 + throw (webrpcErrorByCode[code] || WebrpcError).new(data) + } + return data + }) +} + +export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise + +export const JsonEncode = (obj: T, _typ: string = ''): string => { + return JSON.stringify(obj) +} + +export const JsonDecode = (data: string | any, _typ: string = ''): T => { + let parsed: any = data + if (typeof data === 'string') { + try { + parsed = JSON.parse(data) + } catch (err) { + throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) + } + } + return parsed as T +} + +// +// Errors +// + +type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } + +export class WebrpcError extends Error { + code: number + status: number + + constructor(error: WebrpcErrorParams = {}) { + super(error.message) + this.name = error.name || 'WebrpcEndpointError' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcError.prototype) + } + + static new(payload: any): WebrpcError { + return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) + } +} + +export class WebrpcEndpointError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcEndpoint' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcEndpointError.prototype) + } +} + +export class WebrpcRequestFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcRequestFailed' + this.code = typeof error.code === 'number' ? error.code : -1 + this.message = error.message || `request failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) + } +} + +export class WebrpcBadRouteError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRoute' + this.code = typeof error.code === 'number' ? error.code : -2 + this.message = error.message || `bad route` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) + } +} + +export class WebrpcBadMethodError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadMethod' + this.code = typeof error.code === 'number' ? error.code : -3 + this.message = error.message || `bad method` + this.status = typeof error.status === 'number' ? error.status : 405 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) + } +} + +export class WebrpcBadRequestError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRequest' + this.code = typeof error.code === 'number' ? error.code : -4 + this.message = error.message || `bad request` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) + } +} + +export class WebrpcBadResponseError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadResponse' + this.code = typeof error.code === 'number' ? error.code : -5 + this.message = error.message || `bad response` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) + } +} + +export class WebrpcServerPanicError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcServerPanic' + this.code = typeof error.code === 'number' ? error.code : -6 + this.message = error.message || `server panic` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) + } +} + +export class WebrpcInternalErrorError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcInternalError' + this.code = typeof error.code === 'number' ? error.code : -7 + this.message = error.message || `internal error` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) + } +} + +export class WebrpcClientAbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcClientAborted' + this.code = typeof error.code === 'number' ? error.code : -8 + this.message = error.message || `request aborted by client` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) + } +} + +export class WebrpcStreamLostError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamLost' + this.code = typeof error.code === 'number' ? error.code : -9 + this.message = error.message || `stream lost` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) + } +} + +export class WebrpcStreamFinishedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamFinished' + this.code = typeof error.code === 'number' ? error.code : -10 + this.message = error.message || `stream finished` + this.status = typeof error.status === 'number' ? error.status : 200 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) + } +} + +// +// Schema errors +// + +export class UnauthorizedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unauthorized' + this.code = typeof error.code === 'number' ? error.code : 1000 + this.message = error.message || `Unauthorized access` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnauthorizedError.prototype) + } +} + +export class PermissionDeniedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'PermissionDenied' + this.code = typeof error.code === 'number' ? error.code : 1001 + this.message = error.message || `Permission denied` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, PermissionDeniedError.prototype) + } +} + +export class SessionExpiredError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'SessionExpired' + this.code = typeof error.code === 'number' ? error.code : 1002 + this.message = error.message || `Session expired` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, SessionExpiredError.prototype) + } +} + +export class MethodNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MethodNotFound' + this.code = typeof error.code === 'number' ? error.code : 1003 + this.message = error.message || `Method not found` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MethodNotFoundError.prototype) + } +} + +export class RequestConflictError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RequestConflict' + this.code = typeof error.code === 'number' ? error.code : 1004 + this.message = error.message || `Conflict with target resource` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RequestConflictError.prototype) + } +} + +export class AbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Aborted' + this.code = typeof error.code === 'number' ? error.code : 1005 + this.message = error.message || `Request aborted` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AbortedError.prototype) + } +} + +export class GeoblockedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Geoblocked' + this.code = typeof error.code === 'number' ? error.code : 1006 + this.message = error.message || `Geoblocked region` + this.status = typeof error.status === 'number' ? error.status : 451 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, GeoblockedError.prototype) + } +} + +export class RateLimitedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RateLimited' + this.code = typeof error.code === 'number' ? error.code : 1007 + this.message = error.message || `Rate-limited. Please slow down.` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RateLimitedError.prototype) + } +} + +export class ProjectNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ProjectNotFound' + this.code = typeof error.code === 'number' ? error.code : 1008 + this.message = error.message || `Project not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, ProjectNotFoundError.prototype) + } +} + +export class InvalidArgumentError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidArgument' + this.code = typeof error.code === 'number' ? error.code : 2000 + this.message = error.message || `Invalid argument` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidArgumentError.prototype) + } +} + +export class UnavailableError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unavailable' + this.code = typeof error.code === 'number' ? error.code : 2002 + this.message = error.message || `Unavailable resource` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnavailableError.prototype) + } +} + +export class QueryFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QueryFailed' + this.code = typeof error.code === 'number' ? error.code : 2003 + this.message = error.message || `Query failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, QueryFailedError.prototype) + } +} + +export class NotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NotFound' + this.code = typeof error.code === 'number' ? error.code : 3000 + this.message = error.message || `Resource not found` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, NotFoundError.prototype) + } +} + +export class UnsupportedNetworkError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'UnsupportedNetwork' + this.code = typeof error.code === 'number' ? error.code : 3008 + this.message = error.message || `Unsupported network` + this.status = typeof error.status === 'number' ? error.status : 422 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnsupportedNetworkError.prototype) + } +} + +export enum errors { + WebrpcEndpoint = 'WebrpcEndpoint', + WebrpcRequestFailed = 'WebrpcRequestFailed', + WebrpcBadRoute = 'WebrpcBadRoute', + WebrpcBadMethod = 'WebrpcBadMethod', + WebrpcBadRequest = 'WebrpcBadRequest', + WebrpcBadResponse = 'WebrpcBadResponse', + WebrpcServerPanic = 'WebrpcServerPanic', + WebrpcInternalError = 'WebrpcInternalError', + WebrpcClientAborted = 'WebrpcClientAborted', + WebrpcStreamLost = 'WebrpcStreamLost', + WebrpcStreamFinished = 'WebrpcStreamFinished', + Unauthorized = 'Unauthorized', + PermissionDenied = 'PermissionDenied', + SessionExpired = 'SessionExpired', + MethodNotFound = 'MethodNotFound', + RequestConflict = 'RequestConflict', + Aborted = 'Aborted', + Geoblocked = 'Geoblocked', + RateLimited = 'RateLimited', + ProjectNotFound = 'ProjectNotFound', + InvalidArgument = 'InvalidArgument', + Unavailable = 'Unavailable', + QueryFailed = 'QueryFailed', + NotFound = 'NotFound', + UnsupportedNetwork = 'UnsupportedNetwork', +} + +export enum WebrpcErrorCodes { + WebrpcEndpoint = 0, + WebrpcRequestFailed = -1, + WebrpcBadRoute = -2, + WebrpcBadMethod = -3, + WebrpcBadRequest = -4, + WebrpcBadResponse = -5, + WebrpcServerPanic = -6, + WebrpcInternalError = -7, + WebrpcClientAborted = -8, + WebrpcStreamLost = -9, + WebrpcStreamFinished = -10, + Unauthorized = 1000, + PermissionDenied = 1001, + SessionExpired = 1002, + MethodNotFound = 1003, + RequestConflict = 1004, + Aborted = 1005, + Geoblocked = 1006, + RateLimited = 1007, + ProjectNotFound = 1008, + InvalidArgument = 2000, + Unavailable = 2002, + QueryFailed = 2003, + NotFound = 3000, + UnsupportedNetwork = 3008, +} + +export const webrpcErrorByCode: { [code: number]: any } = { + [0]: WebrpcEndpointError, + [-1]: WebrpcRequestFailedError, + [-2]: WebrpcBadRouteError, + [-3]: WebrpcBadMethodError, + [-4]: WebrpcBadRequestError, + [-5]: WebrpcBadResponseError, + [-6]: WebrpcServerPanicError, + [-7]: WebrpcInternalErrorError, + [-8]: WebrpcClientAbortedError, + [-9]: WebrpcStreamLostError, + [-10]: WebrpcStreamFinishedError, + [1000]: UnauthorizedError, + [1001]: PermissionDeniedError, + [1002]: SessionExpiredError, + [1003]: MethodNotFoundError, + [1004]: RequestConflictError, + [1005]: AbortedError, + [1006]: GeoblockedError, + [1007]: RateLimitedError, + [1008]: ProjectNotFoundError, + [2000]: InvalidArgumentError, + [2002]: UnavailableError, + [2003]: QueryFailedError, + [3000]: NotFoundError, + [3008]: UnsupportedNetworkError, +} + +// +// Webrpc +// + +export const WebrpcHeader = 'Webrpc' + +export const WebrpcHeaderValue = 'webrpc@v0.30.2;gen-typescript@v0.22.2;userdata@v0.1.0' + +type WebrpcGenVersions = { + WebrpcGenVersion: string + codeGenName: string + codeGenVersion: string + schemaName: string + schemaVersion: string +} + +export function VersionFromHeader(headers: Headers): WebrpcGenVersions { + const headerValue = headers.get(WebrpcHeader) + if (!headerValue) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + } + } + + return parseWebrpcGenVersions(headerValue) +} + +function parseWebrpcGenVersions(header: string): WebrpcGenVersions { + const versions = header.split(';') + if (versions.length < 3) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '', + } + } + + const [_, WebrpcGenVersion] = versions[0]!.split('@') + const [codeGenName, codeGenVersion] = versions[1]!.split('@') + const [schemaName, schemaVersion] = versions[2]!.split('@') + + return { + WebrpcGenVersion: WebrpcGenVersion ?? '', + codeGenName: codeGenName ?? '', + codeGenVersion: codeGenVersion ?? '', + schemaName: schemaName ?? '', + schemaVersion: schemaVersion ?? '', + } +} diff --git a/packages/services/userdata/tsconfig.json b/packages/services/userdata/tsconfig.json new file mode 100644 index 0000000000..fed9c77b49 --- /dev/null +++ b/packages/services/userdata/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@repo/typescript-config/base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "types": ["node"] + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/utils/abi/CHANGELOG.md b/packages/utils/abi/CHANGELOG.md index aa0333e535..92b0978a18 100644 --- a/packages/utils/abi/CHANGELOG.md +++ b/packages/utils/abi/CHANGELOG.md @@ -1,5 +1,41 @@ # @0xsequence/abi +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 + ## 2.3.8 ### Patch Changes @@ -1422,7 +1458,6 @@ - relayer: fix Relayer.wait() interface The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - timeout: the maximum time to wait for the transaction receipt - delay: the polling interval, i.e. the time to wait between requests - maxFails: the maximum number of hard failures to tolerate before giving up @@ -1761,7 +1796,6 @@ ### Minor Changes - major architectural changes in Sequence design - - only one API instance, API is no longer a per-chain service - separate per-chain indexer service, API no longer handles indexing - single contract metadata service, API no longer serves metadata diff --git a/packages/utils/abi/package.json b/packages/utils/abi/package.json index 738e1398cf..fdf2e38c2b 100644 --- a/packages/utils/abi/package.json +++ b/packages/utils/abi/package.json @@ -1,8 +1,8 @@ { "name": "@0xsequence/abi", - "version": "3.0.0", + "version": "3.0.0-beta.6", "description": "abi sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/abi", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/utils/abi", "author": "Sequence Platforms Inc.", "license": "Apache-2.0", "publishConfig": { @@ -16,13 +16,13 @@ }, "exports": { ".": { - "types": "./src/index.ts", + "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "typescript": "^5.8.3" + "@types/node": "^25.0.2", + "typescript": "^5.9.3" } } diff --git a/packages/wallet/core/CHANGELOG.md b/packages/wallet/core/CHANGELOG.md new file mode 100644 index 0000000000..0ff25b21aa --- /dev/null +++ b/packages/wallet/core/CHANGELOG.md @@ -0,0 +1,61 @@ +# @0xsequence/wallet-core + +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.6 + - @0xsequence/relayer@3.0.0-beta.6 + - @0xsequence/wallet-primitives@3.0.0-beta.6 + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.5 + - @0xsequence/relayer@3.0.0-beta.5 + - @0xsequence/wallet-primitives@3.0.0-beta.5 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.4 + - @0xsequence/relayer@3.0.0-beta.4 + - @0xsequence/wallet-primitives@3.0.0-beta.4 + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.3 + - @0xsequence/relayer@3.0.0-beta.3 + - @0xsequence/wallet-primitives@3.0.0-beta.3 + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.2 + - @0xsequence/relayer@3.0.0-beta.2 + - @0xsequence/wallet-primitives@3.0.0-beta.2 + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.1 + - @0xsequence/relayer@3.0.0-beta.1 + - @0xsequence/wallet-primitives@3.0.0-beta.1 diff --git a/packages/wallet/core/package.json b/packages/wallet/core/package.json index 00fbbf56e5..b7e7b463fe 100644 --- a/packages/wallet/core/package.json +++ b/packages/wallet/core/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-core", - "version": "0.0.0", + "version": "3.0.0-beta.6", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -23,19 +23,19 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "@vitest/coverage-v8": "^3.2.4", - "dotenv": "^16.5.0", - "fake-indexeddb": "^6.0.1", - "typescript": "^5.8.3", - "vitest": "^3.2.1" + "@types/node": "^25.0.2", + "@vitest/coverage-v8": "^4.0.15", + "dotenv": "^17.2.3", + "fake-indexeddb": "^6.2.5", + "typescript": "^5.9.3", + "vitest": "^4.0.15" }, "dependencies": { "@0xsequence/guard": "workspace:^", "@0xsequence/relayer": "workspace:^", "@0xsequence/wallet-primitives": "workspace:^", "mipd": "^0.0.7", - "ox": "^0.7.2", - "viem": "^2.30.6" + "ox": "^0.9.17", + "viem": "^2.40.3" } } diff --git a/packages/wallet/core/src/relayer/bundler.ts b/packages/wallet/core/src/bundler/bundler.ts similarity index 85% rename from packages/wallet/core/src/relayer/bundler.ts rename to packages/wallet/core/src/bundler/bundler.ts index 4468f086de..baa473b817 100644 --- a/packages/wallet/core/src/relayer/bundler.ts +++ b/packages/wallet/core/src/bundler/bundler.ts @@ -1,7 +1,7 @@ import { Payload } from '@0xsequence/wallet-primitives' import { Address, Hex } from 'ox' import { UserOperation } from 'ox/erc4337' -import { OperationStatus } from './relayer.js' +import { Relayer } from '@0xsequence/relayer' export interface Bundler { kind: 'bundler' @@ -13,7 +13,7 @@ export interface Bundler { payload: Payload.Calls4337_07, ): Promise<{ speed?: 'slow' | 'standard' | 'fast'; payload: Payload.Calls4337_07 }[]> relay(entrypoint: Address.Address, userOperation: UserOperation.RpcV07): Promise<{ opHash: Hex.Hex }> - status(opHash: Hex.Hex, chainId: number): Promise + status(opHash: Hex.Hex, chainId: number): Promise isAvailable(entrypoint: Address.Address, chainId: number): Promise } diff --git a/packages/wallet/core/src/relayer/bundlers/index.ts b/packages/wallet/core/src/bundler/bundlers/index.ts similarity index 100% rename from packages/wallet/core/src/relayer/bundlers/index.ts rename to packages/wallet/core/src/bundler/bundlers/index.ts diff --git a/packages/wallet/core/src/relayer/bundlers/pimlico.ts b/packages/wallet/core/src/bundler/bundlers/pimlico.ts similarity index 97% rename from packages/wallet/core/src/relayer/bundlers/pimlico.ts rename to packages/wallet/core/src/bundler/bundlers/pimlico.ts index 11ae0dd646..e2d95ec331 100644 --- a/packages/wallet/core/src/relayer/bundlers/pimlico.ts +++ b/packages/wallet/core/src/bundler/bundlers/pimlico.ts @@ -2,7 +2,7 @@ import { Payload } from '@0xsequence/wallet-primitives' import { Bundler } from '../bundler.js' import { Provider, Hex, Address, RpcTransport } from 'ox' import { UserOperation } from 'ox/erc4337' -import { OperationStatus } from '../relayer.js' +import { Relayer } from '@0xsequence/relayer' type FeePerGasPair = { maxFeePerGas: Hex.Hex | bigint @@ -103,7 +103,7 @@ export class PimlicoBundler implements Bundler { } } - async status(opHash: Hex.Hex, _chainId: number): Promise { + async status(opHash: Hex.Hex, _chainId: number): Promise { try { type PimlicoStatusResp = { status: 'not_found' | 'not_submitted' | 'submitted' | 'rejected' | 'included' | 'failed' | 'reverted' diff --git a/packages/wallet/core/src/relayer/index.ts b/packages/wallet/core/src/bundler/index.ts similarity index 67% rename from packages/wallet/core/src/relayer/index.ts rename to packages/wallet/core/src/bundler/index.ts index 5fb0b67240..53c531a9be 100644 --- a/packages/wallet/core/src/relayer/index.ts +++ b/packages/wallet/core/src/bundler/index.ts @@ -1,7 +1,5 @@ // Export the core interfaces and type guards -export * from './relayer.js' export * from './bundler.js' // Group and export implementations -export * as Standard from './standard/index.js' export * as Bundlers from './bundlers/index.js' diff --git a/packages/wallet/core/src/index.ts b/packages/wallet/core/src/index.ts index 1bc32aed48..b36e917cae 100644 --- a/packages/wallet/core/src/index.ts +++ b/packages/wallet/core/src/index.ts @@ -2,7 +2,12 @@ export * from './wallet.js' export * as Signers from './signers/index.js' export * as State from './state/index.js' -export * as Relayer from './relayer/index.js' +export * as Bundler from './bundler/index.js' export * as Envelope from './envelope.js' -export * as Preconditions from './preconditions/index.js' export * as Utils from './utils/index.js' +export { + type ExplicitSessionConfig, + type ExplicitSession, + type ImplicitSession, + type Session, +} from './utils/session/types.js' diff --git a/packages/wallet/core/src/relayer/standard/rpc/relayer.gen.ts b/packages/wallet/core/src/relayer/standard/rpc/relayer.gen.ts deleted file mode 100644 index a9e6b44405..0000000000 --- a/packages/wallet/core/src/relayer/standard/rpc/relayer.gen.ts +++ /dev/null @@ -1,2037 +0,0 @@ -/* eslint-disable */ -// sequence-relayer v0.4.1 62fe2b49d57c4a0d3960ac1176d48ecfffc7af5a -// -- -// Code generated by webrpc-gen@v0.26.0 with typescript generator. DO NOT EDIT. -// -// webrpc-gen -schema=relayer.ridl -target=typescript -client -out=./clients/relayer.gen.ts - -export const WebrpcHeader = 'Webrpc' - -export const WebrpcHeaderValue = 'webrpc@v0.26.0;gen-typescript@v0.17.0;sequence-relayer@v0.4.1' - -// WebRPC description and code-gen version -export const WebRPCVersion = 'v1' - -// Schema version of your RIDL schema -export const WebRPCSchemaVersion = 'v0.4.1' - -// Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = '62fe2b49d57c4a0d3960ac1176d48ecfffc7af5a' - -type WebrpcGenVersions = { - webrpcGenVersion: string - codeGenName: string - codeGenVersion: string - schemaName: string - schemaVersion: string -} - -export function VersionFromHeader(headers: Headers): WebrpcGenVersions { - const headerValue = headers.get(WebrpcHeader) - if (!headerValue) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - return parseWebrpcGenVersions(headerValue) -} - -function parseWebrpcGenVersions(header: string): WebrpcGenVersions { - const versions = header.split(';') - if (versions.length < 3) { - return { - webrpcGenVersion: '', - codeGenName: '', - codeGenVersion: '', - schemaName: '', - schemaVersion: '', - } - } - - const [_, webrpcGenVersion] = versions[0]!.split('@') - const [codeGenName, codeGenVersion] = versions[1]!.split('@') - const [schemaName, schemaVersion] = versions[2]!.split('@') - - return { - webrpcGenVersion: webrpcGenVersion ?? '', - codeGenName: codeGenName ?? '', - codeGenVersion: codeGenVersion ?? '', - schemaName: schemaName ?? '', - schemaVersion: schemaVersion ?? '', - } -} - -// -// Types -// - -export enum ETHTxnStatus { - UNKNOWN = 'UNKNOWN', - DROPPED = 'DROPPED', - QUEUED = 'QUEUED', - SENT = 'SENT', - SUCCEEDED = 'SUCCEEDED', - PARTIALLY_FAILED = 'PARTIALLY_FAILED', - FAILED = 'FAILED', - PENDING_PRECONDITION = 'PENDING_PRECONDITION', -} - -export enum TransferType { - SEND = 'SEND', - RECEIVE = 'RECEIVE', - BRIDGE_DEPOSIT = 'BRIDGE_DEPOSIT', - BRIDGE_WITHDRAW = 'BRIDGE_WITHDRAW', - BURN = 'BURN', - UNKNOWN = 'UNKNOWN', -} - -export enum SimulateStatus { - SKIPPED = 'SKIPPED', - SUCCEEDED = 'SUCCEEDED', - FAILED = 'FAILED', - ABORTED = 'ABORTED', - REVERTED = 'REVERTED', - NOT_ENOUGH_GAS = 'NOT_ENOUGH_GAS', -} - -export enum FeeTokenType { - UNKNOWN = 'UNKNOWN', - ERC20_TOKEN = 'ERC20_TOKEN', - ERC1155_TOKEN = 'ERC1155_TOKEN', -} - -export enum SortOrder { - DESC = 'DESC', - ASC = 'ASC', -} - -export interface Version { - webrpcVersion: string - schemaVersion: string - schemaHash: string - appVersion: string -} - -export interface RuntimeStatus { - healthOK: boolean - startTime: string - uptime: number - ver: string - branch: string - commitHash: string - chainID: number - useEIP1559: boolean - senders: Array - checks: RuntimeChecks -} - -export interface SenderStatus { - index: number - address: string - etherBalance: number - active: boolean -} - -export interface RuntimeChecks {} - -export interface SequenceContext { - factory: string - mainModule: string - mainModuleUpgradable: string - guestModule: string - utils: string -} - -export interface GasTank { - id: number - chainId: number - name: string - currentBalance: number - unlimited: boolean - feeMarkupFactor: number - updatedAt: string - createdAt: string -} - -export interface GasTankBalanceAdjustment { - gasTankId: number - nonce: number - amount: number - totalBalance: number - balanceTimestamp: string - createdAt: string -} - -export interface GasSponsor { - id: number - gasTankId: number - projectId: number - chainId: number - address: string - name: string - active: boolean - updatedAt: string - createdAt: string - deletedAt: string -} - -export interface GasSponsorUsage { - name: string - id: number - totalGasUsed: number - totalTxnFees: number - totalTxnFeesUsd: number - avgGasPrice: number - totalTxns: number - startTime: string - endTime: string -} - -export interface MetaTxn { - walletAddress: string - contract: string - input: string -} - -export interface MetaTxnLog { - id: number - chainId: number - projectId: number - txnHash: string - txnNonce: string - metaTxnID?: string - txnStatus: ETHTxnStatus - txnRevertReason: string - requeues: number - queuedAt: string - sentAt: string - minedAt: string - target: string - input: string - txnArgs: { [key: string]: any } - txnReceipt?: { [key: string]: any } - walletAddress: string - metaTxnNonce: string - gasLimit: number - gasPrice: string - gasUsed: number - gasEstimated: number - gasFeeMarkup?: number - usdRate: string - creditsUsed: number - cost: string - isWhitelisted: boolean - gasSponsor?: number - gasTank?: number - updatedAt: string - createdAt: string -} - -export interface MetaTxnReceipt { - id: string - status: string - revertReason?: string - index: number - logs: Array - receipts: Array - blockNumber: string - txnHash: string - txnReceipt: string -} - -export interface MetaTxnReceiptLog { - address: string - topics: Array - data: string -} - -export interface IntentPrecondition { - type: string - chainId: string - data: any -} - -export interface IntentSolution { - transactions: Array -} - -export interface Transactions { - chainID: string - transactions: Array - preconditions?: Array -} - -export interface Transaction { - delegateCall: boolean - revertOnError: boolean - gasLimit: string - target: string - value: string - data: string -} - -export interface TxnLogUser { - username: string -} - -export interface TxnLogTransfer { - transferType: TransferType - contractAddress: string - from: string - to: string - ids: Array - amounts: Array -} - -export interface SentTransactionsFilter { - pending?: boolean - failed?: boolean -} - -export interface SimulateResult { - executed: boolean - succeeded: boolean - result?: string - reason?: string - gasUsed: number - gasLimit: number -} - -export interface SimulateV3Result { - status: SimulateStatus - result?: string - error?: string - gasUsed: number - gasLimit: number -} - -export interface FeeOption { - token: FeeToken - to: string - value: string - gasLimit: number -} - -export interface FeeToken { - chainId: number - name: string - symbol: string - type: FeeTokenType - decimals?: number - logoURL: string - contractAddress?: string - tokenID?: string -} - -export interface Page { - pageSize?: number - page?: number - more?: boolean - totalRecords?: number - column?: string - before?: any - after?: any - sort?: Array -} - -export interface SortBy { - column: string - order: SortOrder -} - -export interface Relayer { - ping(headers?: object, signal?: AbortSignal): Promise - version(headers?: object, signal?: AbortSignal): Promise - runtimeStatus(headers?: object, signal?: AbortSignal): Promise - getSequenceContext(headers?: object, signal?: AbortSignal): Promise - getChainID(headers?: object, signal?: AbortSignal): Promise - /** - * - * Transactions - * - * TODO (future): rename this to just, 'SendTransaction(txn: MetaTransaction)' or 'SendTransaction(txn: SignedTransaction)', or something.. - * Project ID is only used by service and admin calls. Other clients must have projectID passed via the context - * TODO: rename return txnHash: string to metaTxnID: string - */ - sendMetaTxn(args: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise - getMetaTxnNonce(args: GetMetaTxnNonceArgs, headers?: object, signal?: AbortSignal): Promise - /** - * TODO: one day, make GetMetaTxnReceipt respond immediately with receipt or not - * and add WaitTransactionReceipt method, which will block and wait, similar to how GetMetaTxnReceipt - * is implemented now. - * For backwards compat, we can leave the current GetMetaTxnReceipt how it is, an deprecate it, and introduce - * new, GetTransactionReceipt and WaitTransactionReceipt methods - * we can also accept metaTxnId and txnHash .. so can take either or.. I wonder if ERC-4337 has any convention on this? - */ - getMetaTxnReceipt( - args: GetMetaTxnReceiptArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - simulate(args: SimulateArgs, headers?: object, signal?: AbortSignal): Promise - simulateV3(args: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise - /** - * TODO: deprecated, to be removed by https://github.com/0xsequence/stack/pull/356 at a later date - */ - updateMetaTxnGasLimits( - args: UpdateMetaTxnGasLimitsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - feeTokens(headers?: object, signal?: AbortSignal): Promise - feeOptions(args: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * TODO: deprecated, to be removed by https://github.com/0xsequence/stack/pull/356 at a later date - */ - getMetaTxnNetworkFeeOptions( - args: GetMetaTxnNetworkFeeOptionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getMetaTransactions( - args: GetMetaTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getTransactionCost( - args: GetTransactionCostArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Sent transactions from an account. If filter is omitted then it will return all transactions. - */ - sentTransactions(args: SentTransactionsArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Pending transactions waiting to be mined for an account. This endpoint is just a sugar of `SentTransactions` - * with the filter set to pending: true. - */ - pendingTransactions( - args: PendingTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Legacy Gas Tank - */ - getGasTank(args: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise - addGasTank(args: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise - updateGasTank(args: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Legacy Gas Adjustment - */ - nextGasTankBalanceAdjustmentNonce( - args: NextGasTankBalanceAdjustmentNonceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - adjustGasTankBalance( - args: AdjustGasTankBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - getGasTankBalanceAdjustment( - args: GetGasTankBalanceAdjustmentArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - listGasTankBalanceAdjustments( - args: ListGasTankBalanceAdjustmentsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Gas Sponsorship - */ - listGasSponsors(args: ListGasSponsorsArgs, headers?: object, signal?: AbortSignal): Promise - getGasSponsor(args: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - addGasSponsor(args: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - updateGasSponsor(args: UpdateGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - removeGasSponsor(args: RemoveGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise - /** - * Gas Sponsor Lookup - */ - addressGasSponsors( - args: AddressGasSponsorsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - /** - * Project Balance - */ - getProjectBalance( - args: GetProjectBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise - adjustProjectBalance( - args: AdjustProjectBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise -} - -export interface PingArgs {} - -export interface PingReturn { - status: boolean -} -export interface VersionArgs {} - -export interface VersionReturn { - version: Version -} -export interface RuntimeStatusArgs {} - -export interface RuntimeStatusReturn { - status: RuntimeStatus -} -export interface GetSequenceContextArgs {} - -export interface GetSequenceContextReturn { - data: SequenceContext -} -export interface GetChainIDArgs {} - -export interface GetChainIDReturn { - chainID: number -} -export interface SendMetaTxnArgs { - call: MetaTxn - quote?: string - projectID?: number - preconditions?: Array -} - -export interface SendMetaTxnReturn { - status: boolean - txnHash: string -} -export interface GetMetaTxnNonceArgs { - walletContractAddress: string - space?: string -} - -export interface GetMetaTxnNonceReturn { - nonce: string -} -export interface GetMetaTxnReceiptArgs { - metaTxID: string -} - -export interface GetMetaTxnReceiptReturn { - receipt: MetaTxnReceipt -} -export interface SimulateArgs { - wallet: string - transactions: string -} - -export interface SimulateReturn { - results: Array -} -export interface SimulateV3Args { - wallet: string - calls: string -} - -export interface SimulateV3Return { - results: Array -} -export interface UpdateMetaTxnGasLimitsArgs { - walletAddress: string - walletConfig: any - payload: string -} - -export interface UpdateMetaTxnGasLimitsReturn { - payload: string -} -export interface FeeTokensArgs {} - -export interface FeeTokensReturn { - isFeeRequired: boolean - tokens: Array -} -export interface FeeOptionsArgs { - wallet: string - to: string - data: string - simulate?: boolean -} - -export interface FeeOptionsReturn { - options: Array - sponsored: boolean - quote?: string -} -export interface GetMetaTxnNetworkFeeOptionsArgs { - walletConfig: any - payload: string -} - -export interface GetMetaTxnNetworkFeeOptionsReturn { - options: Array -} -export interface GetMetaTransactionsArgs { - projectId: number - page?: Page -} - -export interface GetMetaTransactionsReturn { - page: Page - transactions: Array -} -export interface GetTransactionCostArgs { - projectId: number - from: string - to: string -} - -export interface GetTransactionCostReturn { - cost: number -} -export interface SentTransactionsArgs { - filter?: SentTransactionsFilter - page?: Page -} - -export interface SentTransactionsReturn { - page: Page - transactions: Array -} -export interface PendingTransactionsArgs { - page?: Page -} - -export interface PendingTransactionsReturn { - page: Page - transactions: Array -} -export interface GetGasTankArgs { - id: number -} - -export interface GetGasTankReturn { - gasTank: GasTank -} -export interface AddGasTankArgs { - name: string - feeMarkupFactor: number - unlimited?: boolean -} - -export interface AddGasTankReturn { - status: boolean - gasTank: GasTank -} -export interface UpdateGasTankArgs { - id: number - name?: string - feeMarkupFactor?: number - unlimited?: boolean -} - -export interface UpdateGasTankReturn { - status: boolean - gasTank: GasTank -} -export interface NextGasTankBalanceAdjustmentNonceArgs { - id: number -} - -export interface NextGasTankBalanceAdjustmentNonceReturn { - nonce: number -} -export interface AdjustGasTankBalanceArgs { - id: number - nonce: number - amount: number -} - -export interface AdjustGasTankBalanceReturn { - status: boolean - adjustment: GasTankBalanceAdjustment -} -export interface GetGasTankBalanceAdjustmentArgs { - id: number - nonce: number -} - -export interface GetGasTankBalanceAdjustmentReturn { - adjustment: GasTankBalanceAdjustment -} -export interface ListGasTankBalanceAdjustmentsArgs { - id: number - page?: Page -} - -export interface ListGasTankBalanceAdjustmentsReturn { - page: Page - adjustments: Array -} -export interface ListGasSponsorsArgs { - projectId: number - page?: Page -} - -export interface ListGasSponsorsReturn { - page: Page - gasSponsors: Array -} -export interface GetGasSponsorArgs { - projectId: number - id: number -} - -export interface GetGasSponsorReturn { - gasSponsor: GasSponsor -} -export interface AddGasSponsorArgs { - projectId: number - address: string - name?: string - active?: boolean -} - -export interface AddGasSponsorReturn { - status: boolean - gasSponsor: GasSponsor -} -export interface UpdateGasSponsorArgs { - projectId: number - id: number - name?: string - active?: boolean -} - -export interface UpdateGasSponsorReturn { - status: boolean - gasSponsor: GasSponsor -} -export interface RemoveGasSponsorArgs { - projectId: number - id: number -} - -export interface RemoveGasSponsorReturn { - status: boolean -} -export interface AddressGasSponsorsArgs { - address: string - page?: Page -} - -export interface AddressGasSponsorsReturn { - page: Page - gasSponsors: Array -} -export interface GetProjectBalanceArgs { - projectId: number -} - -export interface GetProjectBalanceReturn { - balance: number -} -export interface AdjustProjectBalanceArgs { - projectId: number - amount: number - identifier: string -} - -export interface AdjustProjectBalanceReturn { - balance: number -} - -// -// Client -// -export class Relayer implements Relayer { - protected hostname: string - protected fetch: Fetch - protected path = '/rpc/Relayer/' - - constructor(hostname: string, fetch: Fetch) { - this.hostname = hostname.replace(/\/*$/, '') - this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) - } - - private url(name: string): string { - return this.hostname + this.path + name - } - - ping = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Ping'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - version = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Version'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - version: _data.version, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('RuntimeStatus'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getSequenceContext = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetSequenceContext'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - data: _data.data, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getChainID = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetChainID'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - chainID: _data.chainID, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - sendMetaTxn = (args: SendMetaTxnArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SendMetaTxn'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - txnHash: _data.txnHash, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getMetaTxnNonce = ( - args: GetMetaTxnNonceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMetaTxnNonce'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - nonce: _data.nonce, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getMetaTxnReceipt = ( - args: GetMetaTxnReceiptArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMetaTxnReceipt'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - receipt: _data.receipt, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - simulate = (args: SimulateArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('Simulate'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - results: >_data.results, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - simulateV3 = (args: SimulateV3Args, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('SimulateV3'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - results: >_data.results, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - updateMetaTxnGasLimits = ( - args: UpdateMetaTxnGasLimitsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateMetaTxnGasLimits'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - payload: _data.payload, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - feeTokens = (headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('FeeTokens'), createHTTPRequest({}, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - isFeeRequired: _data.isFeeRequired, - tokens: >_data.tokens, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - feeOptions = (args: FeeOptionsArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('FeeOptions'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - options: >_data.options, - sponsored: _data.sponsored, - quote: _data.quote, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getMetaTxnNetworkFeeOptions = ( - args: GetMetaTxnNetworkFeeOptionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMetaTxnNetworkFeeOptions'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - options: >_data.options, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getMetaTransactions = ( - args: GetMetaTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetMetaTransactions'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - page: _data.page, - transactions: >_data.transactions, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getTransactionCost = ( - args: GetTransactionCostArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetTransactionCost'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - cost: _data.cost, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - sentTransactions = ( - args: SentTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('SentTransactions'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - page: _data.page, - transactions: >_data.transactions, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - pendingTransactions = ( - args: PendingTransactionsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('PendingTransactions'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - page: _data.page, - transactions: >_data.transactions, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getGasTank = (args: GetGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetGasTank'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - gasTank: _data.gasTank, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - addGasTank = (args: AddGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('AddGasTank'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - gasTank: _data.gasTank, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - updateGasTank = (args: UpdateGasTankArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('UpdateGasTank'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - gasTank: _data.gasTank, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - nextGasTankBalanceAdjustmentNonce = ( - args: NextGasTankBalanceAdjustmentNonceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('NextGasTankBalanceAdjustmentNonce'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - nonce: _data.nonce, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - adjustGasTankBalance = ( - args: AdjustGasTankBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AdjustGasTankBalance'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - adjustment: _data.adjustment, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getGasTankBalanceAdjustment = ( - args: GetGasTankBalanceAdjustmentArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetGasTankBalanceAdjustment'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - adjustment: _data.adjustment, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - listGasTankBalanceAdjustments = ( - args: ListGasTankBalanceAdjustmentsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListGasTankBalanceAdjustments'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - page: _data.page, - adjustments: >_data.adjustments, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - listGasSponsors = ( - args: ListGasSponsorsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('ListGasSponsors'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - page: _data.page, - gasSponsors: >_data.gasSponsors, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getGasSponsor = (args: GetGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('GetGasSponsor'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - gasSponsor: _data.gasSponsor, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - addGasSponsor = (args: AddGasSponsorArgs, headers?: object, signal?: AbortSignal): Promise => { - return this.fetch(this.url('AddGasSponsor'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - gasSponsor: _data.gasSponsor, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - updateGasSponsor = ( - args: UpdateGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('UpdateGasSponsor'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - gasSponsor: _data.gasSponsor, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - removeGasSponsor = ( - args: RemoveGasSponsorArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('RemoveGasSponsor'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - status: _data.status, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - addressGasSponsors = ( - args: AddressGasSponsorsArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AddressGasSponsors'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - page: _data.page, - gasSponsors: >_data.gasSponsors, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - getProjectBalance = ( - args: GetProjectBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('GetProjectBalance'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - balance: _data.balance, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } - - adjustProjectBalance = ( - args: AdjustProjectBalanceArgs, - headers?: object, - signal?: AbortSignal, - ): Promise => { - return this.fetch(this.url('AdjustProjectBalance'), createHTTPRequest(args, headers, signal)).then( - (res) => { - return buildResponse(res).then((_data) => { - return { - balance: _data.balance, - } - }) - }, - (error) => { - throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error.message || ''}` }) - }, - ) - } -} - -const createHTTPRequest = (body: object = {}, headers: object = {}, signal: AbortSignal | null = null): object => { - const reqHeaders: { [key: string]: string } = { ...headers, 'Content-Type': 'application/json' } - reqHeaders[WebrpcHeader] = WebrpcHeaderValue - - return { - method: 'POST', - headers: reqHeaders, - body: JSON.stringify(body || {}), - signal, - } -} - -const buildResponse = (res: Response): Promise => { - return res.text().then((text) => { - let data - try { - data = JSON.parse(text) - } catch (error) { - let message = '' - if (error instanceof Error) { - message = error.message - } - throw WebrpcBadResponseError.new({ - status: res.status, - cause: `JSON.parse(): ${message}: response text: ${text}`, - }) - } - if (!res.ok) { - const code: number = typeof data.code === 'number' ? data.code : 0 - throw (webrpcErrorByCode[code] || WebrpcError).new(data) - } - return data - }) -} - -// -// Errors -// - -export class WebrpcError extends Error { - name: string - code: number - message: string - status: number - cause?: string - - /** @deprecated Use message instead of msg. Deprecated in webrpc v0.11.0. */ - msg: string - - constructor(name: string, code: number, message: string, status: number, cause?: string) { - super(message) - this.name = name || 'WebrpcError' - this.code = typeof code === 'number' ? code : 0 - this.message = message || `endpoint error ${this.code}` - this.msg = this.message - this.status = typeof status === 'number' ? status : 0 - this.cause = cause - Object.setPrototypeOf(this, WebrpcError.prototype) - } - - static new(payload: any): WebrpcError { - return new this(payload.error, payload.code, payload.message || payload.msg, payload.status, payload.cause) - } -} - -// Webrpc errors - -export class WebrpcEndpointError extends WebrpcError { - constructor( - name: string = 'WebrpcEndpoint', - code: number = 0, - message: string = `endpoint error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcEndpointError.prototype) - } -} - -export class WebrpcRequestFailedError extends WebrpcError { - constructor( - name: string = 'WebrpcRequestFailed', - code: number = -1, - message: string = `request failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) - } -} - -export class WebrpcBadRouteError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRoute', - code: number = -2, - message: string = `bad route`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) - } -} - -export class WebrpcBadMethodError extends WebrpcError { - constructor( - name: string = 'WebrpcBadMethod', - code: number = -3, - message: string = `bad method`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) - } -} - -export class WebrpcBadRequestError extends WebrpcError { - constructor( - name: string = 'WebrpcBadRequest', - code: number = -4, - message: string = `bad request`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) - } -} - -export class WebrpcBadResponseError extends WebrpcError { - constructor( - name: string = 'WebrpcBadResponse', - code: number = -5, - message: string = `bad response`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) - } -} - -export class WebrpcServerPanicError extends WebrpcError { - constructor( - name: string = 'WebrpcServerPanic', - code: number = -6, - message: string = `server panic`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) - } -} - -export class WebrpcInternalErrorError extends WebrpcError { - constructor( - name: string = 'WebrpcInternalError', - code: number = -7, - message: string = `internal error`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) - } -} - -export class WebrpcClientDisconnectedError extends WebrpcError { - constructor( - name: string = 'WebrpcClientDisconnected', - code: number = -8, - message: string = `client disconnected`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcClientDisconnectedError.prototype) - } -} - -export class WebrpcStreamLostError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamLost', - code: number = -9, - message: string = `stream lost`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) - } -} - -export class WebrpcStreamFinishedError extends WebrpcError { - constructor( - name: string = 'WebrpcStreamFinished', - code: number = -10, - message: string = `stream finished`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) - } -} - -// Schema errors - -export class UnauthorizedError extends WebrpcError { - constructor( - name: string = 'Unauthorized', - code: number = 1000, - message: string = `Unauthorized access`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedError.prototype) - } -} - -export class PermissionDeniedError extends WebrpcError { - constructor( - name: string = 'PermissionDenied', - code: number = 1001, - message: string = `Permission denied`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, PermissionDeniedError.prototype) - } -} - -export class SessionExpiredError extends WebrpcError { - constructor( - name: string = 'SessionExpired', - code: number = 1002, - message: string = `Session expired`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SessionExpiredError.prototype) - } -} - -export class MethodNotFoundError extends WebrpcError { - constructor( - name: string = 'MethodNotFound', - code: number = 1003, - message: string = `Method not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MethodNotFoundError.prototype) - } -} - -export class RequestConflictError extends WebrpcError { - constructor( - name: string = 'RequestConflict', - code: number = 1004, - message: string = `Conflict with target resource`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RequestConflictError.prototype) - } -} - -export class AbortedError extends WebrpcError { - constructor( - name: string = 'Aborted', - code: number = 1005, - message: string = `Request aborted`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AbortedError.prototype) - } -} - -export class GeoblockedError extends WebrpcError { - constructor( - name: string = 'Geoblocked', - code: number = 1006, - message: string = `Geoblocked region`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, GeoblockedError.prototype) - } -} - -export class RateLimitedError extends WebrpcError { - constructor( - name: string = 'RateLimited', - code: number = 1007, - message: string = `Rate-limited. Please slow down.`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, RateLimitedError.prototype) - } -} - -export class ProjectNotFoundError extends WebrpcError { - constructor( - name: string = 'ProjectNotFound', - code: number = 1008, - message: string = `Project not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, ProjectNotFoundError.prototype) - } -} - -export class AccessKeyNotFoundError extends WebrpcError { - constructor( - name: string = 'AccessKeyNotFound', - code: number = 1101, - message: string = `Access key not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) - } -} - -export class AccessKeyMismatchError extends WebrpcError { - constructor( - name: string = 'AccessKeyMismatch', - code: number = 1102, - message: string = `Access key mismatch`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) - } -} - -export class InvalidOriginError extends WebrpcError { - constructor( - name: string = 'InvalidOrigin', - code: number = 1103, - message: string = `Invalid origin for Access Key`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidOriginError.prototype) - } -} - -export class InvalidServiceError extends WebrpcError { - constructor( - name: string = 'InvalidService', - code: number = 1104, - message: string = `Service not enabled for Access key`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidServiceError.prototype) - } -} - -export class UnauthorizedUserError extends WebrpcError { - constructor( - name: string = 'UnauthorizedUser', - code: number = 1105, - message: string = `Unauthorized user`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnauthorizedUserError.prototype) - } -} - -export class QuotaExceededError extends WebrpcError { - constructor( - name: string = 'QuotaExceeded', - code: number = 1200, - message: string = `Quota request exceeded`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QuotaExceededError.prototype) - } -} - -export class QuotaRateLimitError extends WebrpcError { - constructor( - name: string = 'QuotaRateLimit', - code: number = 1201, - message: string = `Quota rate limit exceeded`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QuotaRateLimitError.prototype) - } -} - -export class NoDefaultKeyError extends WebrpcError { - constructor( - name: string = 'NoDefaultKey', - code: number = 1300, - message: string = `No default access key found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NoDefaultKeyError.prototype) - } -} - -export class MaxAccessKeysError extends WebrpcError { - constructor( - name: string = 'MaxAccessKeys', - code: number = 1301, - message: string = `Access keys limit reached`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, MaxAccessKeysError.prototype) - } -} - -export class AtLeastOneKeyError extends WebrpcError { - constructor( - name: string = 'AtLeastOneKey', - code: number = 1302, - message: string = `You need at least one Access Key`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) - } -} - -export class TimeoutError extends WebrpcError { - constructor( - name: string = 'Timeout', - code: number = 1900, - message: string = `Request timed out`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, TimeoutError.prototype) - } -} - -export class InvalidArgumentError extends WebrpcError { - constructor( - name: string = 'InvalidArgument', - code: number = 2001, - message: string = `Invalid argument`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InvalidArgumentError.prototype) - } -} - -export class UnavailableError extends WebrpcError { - constructor( - name: string = 'Unavailable', - code: number = 2002, - message: string = `Unavailable resource`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, UnavailableError.prototype) - } -} - -export class QueryFailedError extends WebrpcError { - constructor( - name: string = 'QueryFailed', - code: number = 2003, - message: string = `Query failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, QueryFailedError.prototype) - } -} - -export class NotFoundError extends WebrpcError { - constructor( - name: string = 'NotFound', - code: number = 3000, - message: string = `Resource not found`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotFoundError.prototype) - } -} - -export class InsufficientFeeError extends WebrpcError { - constructor( - name: string = 'InsufficientFee', - code: number = 3004, - message: string = `Insufficient fee`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, InsufficientFeeError.prototype) - } -} - -export class NotEnoughBalanceError extends WebrpcError { - constructor( - name: string = 'NotEnoughBalance', - code: number = 3005, - message: string = `Not enough balance`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, NotEnoughBalanceError.prototype) - } -} - -export class SimulationFailedError extends WebrpcError { - constructor( - name: string = 'SimulationFailed', - code: number = 3006, - message: string = `Simulation failed`, - status: number = 0, - cause?: string, - ) { - super(name, code, message, status, cause) - Object.setPrototypeOf(this, SimulationFailedError.prototype) - } -} - -export enum errors { - WebrpcEndpoint = 'WebrpcEndpoint', - WebrpcRequestFailed = 'WebrpcRequestFailed', - WebrpcBadRoute = 'WebrpcBadRoute', - WebrpcBadMethod = 'WebrpcBadMethod', - WebrpcBadRequest = 'WebrpcBadRequest', - WebrpcBadResponse = 'WebrpcBadResponse', - WebrpcServerPanic = 'WebrpcServerPanic', - WebrpcInternalError = 'WebrpcInternalError', - WebrpcClientDisconnected = 'WebrpcClientDisconnected', - WebrpcStreamLost = 'WebrpcStreamLost', - WebrpcStreamFinished = 'WebrpcStreamFinished', - Unauthorized = 'Unauthorized', - PermissionDenied = 'PermissionDenied', - SessionExpired = 'SessionExpired', - MethodNotFound = 'MethodNotFound', - RequestConflict = 'RequestConflict', - Aborted = 'Aborted', - Geoblocked = 'Geoblocked', - RateLimited = 'RateLimited', - ProjectNotFound = 'ProjectNotFound', - AccessKeyNotFound = 'AccessKeyNotFound', - AccessKeyMismatch = 'AccessKeyMismatch', - InvalidOrigin = 'InvalidOrigin', - InvalidService = 'InvalidService', - UnauthorizedUser = 'UnauthorizedUser', - QuotaExceeded = 'QuotaExceeded', - QuotaRateLimit = 'QuotaRateLimit', - NoDefaultKey = 'NoDefaultKey', - MaxAccessKeys = 'MaxAccessKeys', - AtLeastOneKey = 'AtLeastOneKey', - Timeout = 'Timeout', - InvalidArgument = 'InvalidArgument', - Unavailable = 'Unavailable', - QueryFailed = 'QueryFailed', - NotFound = 'NotFound', - InsufficientFee = 'InsufficientFee', - NotEnoughBalance = 'NotEnoughBalance', - SimulationFailed = 'SimulationFailed', -} - -export enum WebrpcErrorCodes { - WebrpcEndpoint = 0, - WebrpcRequestFailed = -1, - WebrpcBadRoute = -2, - WebrpcBadMethod = -3, - WebrpcBadRequest = -4, - WebrpcBadResponse = -5, - WebrpcServerPanic = -6, - WebrpcInternalError = -7, - WebrpcClientDisconnected = -8, - WebrpcStreamLost = -9, - WebrpcStreamFinished = -10, - Unauthorized = 1000, - PermissionDenied = 1001, - SessionExpired = 1002, - MethodNotFound = 1003, - RequestConflict = 1004, - Aborted = 1005, - Geoblocked = 1006, - RateLimited = 1007, - ProjectNotFound = 1008, - AccessKeyNotFound = 1101, - AccessKeyMismatch = 1102, - InvalidOrigin = 1103, - InvalidService = 1104, - UnauthorizedUser = 1105, - QuotaExceeded = 1200, - QuotaRateLimit = 1201, - NoDefaultKey = 1300, - MaxAccessKeys = 1301, - AtLeastOneKey = 1302, - Timeout = 1900, - InvalidArgument = 2001, - Unavailable = 2002, - QueryFailed = 2003, - NotFound = 3000, - InsufficientFee = 3004, - NotEnoughBalance = 3005, - SimulationFailed = 3006, -} - -export const webrpcErrorByCode: { [code: number]: any } = { - [0]: WebrpcEndpointError, - [-1]: WebrpcRequestFailedError, - [-2]: WebrpcBadRouteError, - [-3]: WebrpcBadMethodError, - [-4]: WebrpcBadRequestError, - [-5]: WebrpcBadResponseError, - [-6]: WebrpcServerPanicError, - [-7]: WebrpcInternalErrorError, - [-8]: WebrpcClientDisconnectedError, - [-9]: WebrpcStreamLostError, - [-10]: WebrpcStreamFinishedError, - [1000]: UnauthorizedError, - [1001]: PermissionDeniedError, - [1002]: SessionExpiredError, - [1003]: MethodNotFoundError, - [1004]: RequestConflictError, - [1005]: AbortedError, - [1006]: GeoblockedError, - [1007]: RateLimitedError, - [1008]: ProjectNotFoundError, - [1101]: AccessKeyNotFoundError, - [1102]: AccessKeyMismatchError, - [1103]: InvalidOriginError, - [1104]: InvalidServiceError, - [1105]: UnauthorizedUserError, - [1200]: QuotaExceededError, - [1201]: QuotaRateLimitError, - [1300]: NoDefaultKeyError, - [1301]: MaxAccessKeysError, - [1302]: AtLeastOneKeyError, - [1900]: TimeoutError, - [2001]: InvalidArgumentError, - [2002]: UnavailableError, - [2003]: QueryFailedError, - [3000]: NotFoundError, - [3004]: InsufficientFeeError, - [3005]: NotEnoughBalanceError, - [3006]: SimulationFailedError, -} - -export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise diff --git a/packages/wallet/core/src/signers/guard.ts b/packages/wallet/core/src/signers/guard.ts index 3a654edb46..6ee2f21302 100644 --- a/packages/wallet/core/src/signers/guard.ts +++ b/packages/wallet/core/src/signers/guard.ts @@ -3,9 +3,10 @@ import { Attestation, Payload } from '@0xsequence/wallet-primitives' import * as GuardService from '@0xsequence/guard' import * as Envelope from '../envelope.js' -type GuardToken = { - id: 'TOTP' | 'PIN' +export type GuardToken = { + id: 'TOTP' | 'PIN' | 'recovery' code: string + resetAuth?: boolean } export class Guard { @@ -36,7 +37,7 @@ export class Guard { digest, message, previousSignatures, - token ? { id: token.id, token: token.code } : undefined, + token ? { id: token.id, token: token.code, resetAuth: token.resetAuth } : undefined, ) return { address: this.guard.address, diff --git a/packages/wallet/core/src/signers/pk/encrypted.ts b/packages/wallet/core/src/signers/pk/encrypted.ts index becc2b41a2..c75bc57f93 100644 --- a/packages/wallet/core/src/signers/pk/encrypted.ts +++ b/packages/wallet/core/src/signers/pk/encrypted.ts @@ -2,8 +2,8 @@ import { Hex, Address, PublicKey, Secp256k1, Bytes } from 'ox' import { PkStore } from './index.js' export interface EncryptedData { - iv: Uint8Array - data: ArrayBuffer + iv: BufferSource + data: BufferSource keyPointer: string address: Address.Address publicKey: PublicKey.PublicKey diff --git a/packages/wallet/core/src/signers/session-manager.ts b/packages/wallet/core/src/signers/session-manager.ts index 3cd40ef00d..ef3d81b3a3 100644 --- a/packages/wallet/core/src/signers/session-manager.ts +++ b/packages/wallet/core/src/signers/session-manager.ts @@ -17,6 +17,7 @@ import { isExplicitSessionSigner, SessionSigner, SessionSignerInvalidReason, + isImplicitSessionSigner, UsageLimit, } from './session/index.js' @@ -132,15 +133,13 @@ export class SessionManager implements SapientSigner { async findSignersForCalls(wallet: Address.Address, chainId: number, calls: Payload.Call[]): Promise { // Only use signers that match the topology const topology = await this.topology - const identitySigner = SessionConfig.getIdentitySigner(topology) - if (!identitySigner) { - throw new Error('Identity signer not found') + const identitySigners = SessionConfig.getIdentitySigners(topology) + if (identitySigners.length === 0) { + throw new Error('Identity signers not found') } - const validImplicitSigners = this._implicitSigners.filter((signer) => signer.isValid(topology, chainId).isValid) - const validExplicitSigners = this._explicitSigners.filter((signer) => signer.isValid(topology, chainId).isValid) // Prioritize implicit signers - const availableSigners = [...validImplicitSigners, ...validExplicitSigners] + const availableSigners = [...this._implicitSigners, ...this._explicitSigners] if (availableSigners.length === 0) { throw new Error('No signers match the topology') } @@ -149,9 +148,18 @@ export class SessionManager implements SapientSigner { const signers: SessionSigner[] = [] for (const call of calls) { let supported = false + let expiredSupportedSigner: SessionSigner | undefined for (const signer of availableSigners) { try { supported = await signer.supportedCall(wallet, chainId, call, this.address, this._provider) + if (supported) { + // Check signer validity + const signerValidity = signer.isValid(topology, chainId) + if (signerValidity.invalidReason === 'Expired') { + expiredSupportedSigner = signer + } + supported = signerValidity.isValid + } } catch (error) { console.error('findSignersForCalls error', error) continue @@ -162,7 +170,12 @@ export class SessionManager implements SapientSigner { } } if (!supported) { - throw new Error('No signer supported for call') + if (expiredSupportedSigner) { + throw new Error(`Signer supporting call is expired: ${expiredSupportedSigner.address}`) + } + throw new Error( + `No signer supported for call. ` + `Call: to=${call.to}, data=${call.data}, value=${call.value}, `, + ) } } return signers @@ -255,6 +268,7 @@ export class SessionManager implements SapientSigner { const signers = await this.findSignersForCalls(wallet, chainId, payload.calls) if (signers.length !== payload.calls.length) { + // Unreachable. Throw in findSignersForCalls throw new Error('No signer supported for call') } const signatures = await Promise.all( @@ -291,9 +305,10 @@ export class SessionManager implements SapientSigner { } } - // Encode the signature + // Prepare encoding params const explicitSigners: Address.Address[] = [] const implicitSigners: Address.Address[] = [] + let identitySigner: Address.Address | undefined await Promise.all( signers.map(async (signer) => { const address = await signer.address @@ -301,17 +316,32 @@ export class SessionManager implements SapientSigner { if (!explicitSigners.find((a) => Address.isEqual(a, address))) { explicitSigners.push(address) } - } else { + } else if (isImplicitSessionSigner(signer)) { if (!implicitSigners.find((a) => Address.isEqual(a, address))) { implicitSigners.push(address) + if (!identitySigner) { + identitySigner = signer.identitySigner + } else if (!Address.isEqual(identitySigner, signer.identitySigner)) { + throw new Error('Multiple implicit signers with different identity signers') + } } } }), ) + if (!identitySigner) { + // Explicit signers only. Use any identity signer + const identitySigners = SessionConfig.getIdentitySigners(await this.topology) + if (identitySigners.length === 0) { + throw new Error('No identity signers found') + } + identitySigner = identitySigners[0]! + } - const encodedSignature = SessionSignature.encodeSessionCallSignatures( + // Perform encoding + const encodedSignature = SessionSignature.encodeSessionSignature( signatures, await this.topology, + identitySigner, explicitSigners, implicitSigners, ) diff --git a/packages/wallet/core/src/signers/session/explicit.ts b/packages/wallet/core/src/signers/session/explicit.ts index e373b04699..cd72b2256f 100644 --- a/packages/wallet/core/src/signers/session/explicit.ts +++ b/packages/wallet/core/src/signers/session/explicit.ts @@ -263,10 +263,7 @@ export class Explicit implements ExplicitSessionSigner { } // Sign it - const useDeprecatedHash = - Address.isEqual(sessionManagerAddress, Extensions.Dev1.sessions) || - Address.isEqual(sessionManagerAddress, Extensions.Dev2.sessions) - const callHash = SessionSignature.hashCallWithReplayProtection(payload, callIdx, chainId, useDeprecatedHash) + const callHash = SessionSignature.hashPayloadWithCallIdx(wallet, payload, callIdx, chainId, sessionManagerAddress) const sessionSignature = await this._privateKey.signDigest(Bytes.fromHex(callHash)) return { permissionIndex: BigInt(permissionIndex), diff --git a/packages/wallet/core/src/signers/session/implicit.ts b/packages/wallet/core/src/signers/session/implicit.ts index 973da2b09e..71b1128650 100644 --- a/packages/wallet/core/src/signers/session/implicit.ts +++ b/packages/wallet/core/src/signers/session/implicit.ts @@ -8,11 +8,11 @@ import { } from '@0xsequence/wallet-primitives' import { AbiFunction, Address, Bytes, Hex, Provider, Secp256k1, Signature } from 'ox' import { MemoryPkStore, PkStore } from '../pk/index.js' -import { SessionSigner, SessionSignerValidity } from './session.js' +import { ImplicitSessionSigner, SessionSignerValidity } from './session.js' export type AttestationParams = Omit -export class Implicit implements SessionSigner { +export class Implicit implements ImplicitSessionSigner { private readonly _privateKey: PkStore private readonly _identitySignature: SequenceSignature.RSY public readonly address: Address.Address @@ -43,13 +43,11 @@ export class Implicit implements SessionSigner { } isValid(sessionTopology: SessionConfig.SessionsTopology, _chainId: number): SessionSignerValidity { - const implicitSigner = SessionConfig.getIdentitySigner(sessionTopology) - if (!implicitSigner) { + const implicitSigners = SessionConfig.getIdentitySigners(sessionTopology) + const thisIdentitySigner = this.identitySigner + if (!implicitSigners.some((s) => Address.isEqual(s, thisIdentitySigner))) { return { isValid: false, invalidReason: 'Identity signer not found' } } - if (!Address.isEqual(implicitSigner, this.identitySigner)) { - return { isValid: false, invalidReason: 'Identity signer mismatch' } - } const blacklist = SessionConfig.getImplicitBlacklist(sessionTopology) if (blacklist?.some((b) => Address.isEqual(b, this.address))) { return { isValid: false, invalidReason: 'Blacklisted' } @@ -117,10 +115,7 @@ export class Implicit implements SessionSigner { if (!isSupported) { throw new Error('Unsupported call') } - const useDeprecatedHash = - Address.isEqual(sessionManagerAddress, Extensions.Dev1.sessions) || - Address.isEqual(sessionManagerAddress, Extensions.Dev2.sessions) - const callHash = SessionSignature.hashCallWithReplayProtection(payload, callIdx, chainId, useDeprecatedHash) + const callHash = SessionSignature.hashPayloadWithCallIdx(wallet, payload, callIdx, chainId, sessionManagerAddress) const sessionSignature = await this._privateKey.signDigest(Bytes.fromHex(callHash)) return { attestation: this._attestation, diff --git a/packages/wallet/core/src/signers/session/session.ts b/packages/wallet/core/src/signers/session/session.ts index 8cf44257fe..4bcc5bb771 100644 --- a/packages/wallet/core/src/signers/session/session.ts +++ b/packages/wallet/core/src/signers/session/session.ts @@ -57,6 +57,14 @@ export interface ExplicitSessionSigner extends SessionSigner { ) => Promise } +export interface ImplicitSessionSigner extends SessionSigner { + identitySigner: Address.Address +} + export function isExplicitSessionSigner(signer: SessionSigner): signer is ExplicitSessionSigner { return 'prepareIncrements' in signer } + +export function isImplicitSessionSigner(signer: SessionSigner): signer is ImplicitSessionSigner { + return 'identitySigner' in signer +} diff --git a/packages/wallet/core/src/state/local/index.ts b/packages/wallet/core/src/state/local/index.ts index 77e15da6c7..b3200c8445 100644 --- a/packages/wallet/core/src/state/local/index.ts +++ b/packages/wallet/core/src/state/local/index.ts @@ -66,7 +66,7 @@ export interface Store { export class Provider implements ProviderInterface { constructor( private readonly store: Store = new MemoryStore(), - public readonly extensions: Extensions.Extensions = Extensions.Rc3, + public readonly extensions: Extensions.Extensions = Extensions.Rc5, ) {} getConfiguration(imageHash: Hex.Hex): Promise { diff --git a/packages/wallet/core/src/state/sequence/index.ts b/packages/wallet/core/src/state/sequence/index.ts index 140b3f1ca8..3712f2aa55 100644 --- a/packages/wallet/core/src/state/sequence/index.ts +++ b/packages/wallet/core/src/state/sequence/index.ts @@ -14,7 +14,7 @@ import { Sessions, SignatureType } from './sessions.gen.js' export class Provider implements ProviderInterface { private readonly service: Sessions - constructor(host = 'https://v3-keymachine.sequence-dev.app') { + constructor(host = 'https://keymachine.sequence.app') { this.service = new Sessions(host, fetch) } @@ -364,9 +364,13 @@ export class Provider implements ProviderInterface { } } -const passkeySigners = [Extensions.Dev1.passkeys, Extensions.Dev2.passkeys, Extensions.Rc3.passkeys].map( - Address.checksum, -) +const passkeySigners = [ + Extensions.Dev1.passkeys, + Extensions.Dev2.passkeys, + Extensions.Rc3.passkeys, + Extensions.Rc4.passkeys, + Extensions.Rc5.passkeys, +].map(Address.checksum) const recoverSapientSignatureCompactSignature = 'function recoverSapientSignatureCompact(bytes32 _digest, bytes _signature) view returns (bytes32)' @@ -374,10 +378,14 @@ const recoverSapientSignatureCompactSignature = const recoverSapientSignatureCompactFunction = AbiFunction.from(recoverSapientSignatureCompactSignature) class PasskeySignatureValidator implements oxProvider.Provider { - request: oxProvider.Provider['request'] = (({ method, params }: { method: string; params: unknown }) => { - switch (method) { + request: oxProvider.Provider['request'] = (async (request) => { + switch (request.method) { case 'eth_call': - const transaction: TransactionRequest.Rpc = (params as any)[0] + if (!request.params || !Array.isArray(request.params) || request.params.length === 0) { + throw new Error('eth_call requires transaction parameters') + } + + const transaction: TransactionRequest.Rpc = request.params[0] if (!transaction.data?.startsWith(AbiFunction.getSelector(recoverSapientSignatureCompactFunction))) { throw new Error( @@ -400,15 +408,15 @@ class PasskeySignatureValidator implements oxProvider.Provider { } default: - throw new Error(`method ${method} not implemented`) + throw new Error(`method ${request.method} not implemented`) } - }) as any + }) as oxProvider.Provider['request'] - on(event: string) { + on: oxProvider.Provider['on'] = (event: string) => { throw new Error(`unable to listen for ${event}: not implemented`) } - removeListener(event: string) { + removeListener: oxProvider.Provider['removeListener'] = (event: string) => { throw new Error(`unable to remove listener for ${event}: not implemented`) } } diff --git a/packages/wallet/core/src/utils/session/permission-builder.ts b/packages/wallet/core/src/utils/session/permission-builder.ts index 08b279509a..c77580a188 100644 --- a/packages/wallet/core/src/utils/session/permission-builder.ts +++ b/packages/wallet/core/src/utils/session/permission-builder.ts @@ -59,8 +59,8 @@ export class PermissionBuilder { throw new Error(`cannot call exactCalldata() after calling allowAll() or adding rules`) } for (let offset = 0; offset < calldata.length; offset += 32) { - let value = calldata.slice(offset, offset + 32) - let mask = Permission.MASK.BYTES32 + let value: Bytes.Bytes = calldata.slice(offset, offset + 32) + let mask: Bytes.Bytes = Permission.MASK.BYTES32 if (value.length < 32) { mask = Bytes.fromHex(`0x${'ff'.repeat(value.length)}${'00'.repeat(32 - value.length)}`) value = Bytes.padRight(value, 32) diff --git a/packages/wallet/core/src/utils/session/types.ts b/packages/wallet/core/src/utils/session/types.ts new file mode 100644 index 0000000000..6bf1086d4d --- /dev/null +++ b/packages/wallet/core/src/utils/session/types.ts @@ -0,0 +1,33 @@ +import { Permission } from '@0xsequence/wallet-primitives' +import { Address } from 'ox' + +export type ExplicitSessionConfig = { + valueLimit: bigint + deadline: bigint + permissions: Permission.Permission[] + chainId: number +} + +// Complete session types - what the SDK returns after session creation +export type ImplicitSession = { + sessionAddress: Address.Address + type: 'implicit' +} + +export type ExplicitSession = { + sessionAddress: Address.Address + valueLimit: bigint + deadline: bigint + permissions: Permission.Permission[] + chainId: number + type: 'explicit' +} + +export type Session = { + type: 'explicit' | 'implicit' + sessionAddress: Address.Address + valueLimit?: bigint + deadline?: bigint + permissions?: Permission.Permission[] + chainId?: number +} diff --git a/packages/wallet/core/src/wallet.ts b/packages/wallet/core/src/wallet.ts index db4733e2be..05a02da09e 100644 --- a/packages/wallet/core/src/wallet.ts +++ b/packages/wallet/core/src/wallet.ts @@ -402,7 +402,7 @@ export class Wallet { factory, factoryData, }, - ...(await this.prepareBlankEnvelope(Number(chainId))), + ...(await this.prepareBlankEnvelope(Number(chainId), provider)), } } @@ -490,7 +490,7 @@ export class Wallet { nonce, calls, }, - ...(await this.prepareBlankEnvelope(Number(chainId))), + ...(await this.prepareBlankEnvelope(Number(chainId), provider)), } } @@ -597,8 +597,8 @@ export class Wallet { return encoded } - private async prepareBlankEnvelope(chainId: number) { - const status = await this.getStatus() + private async prepareBlankEnvelope(chainId: number, provider?: Provider.Provider) { + const status = await this.getStatus(provider) return { wallet: this.address, diff --git a/packages/wallet/core/test/constants.ts b/packages/wallet/core/test/constants.ts index ddbeb5ade4..493bf6bf21 100644 --- a/packages/wallet/core/test/constants.ts +++ b/packages/wallet/core/test/constants.ts @@ -4,7 +4,10 @@ import { Abi, AbiEvent, Address } from 'ox' const envFile = process.env.CI ? '.env.test' : '.env.test.local' dotenvConfig({ path: envFile }) -export const EMITTER_ADDRESS: Address.Address = '0xb7bE532959236170064cf099e1a3395aEf228F44' +// Requires https://example.com redirectUrl +export const EMITTER_ADDRESS1: Address.Address = '0xad90eB52BC180Bd9f66f50981E196f3E996278D3' +// Requires https://another-example.com redirectUrl +export const EMITTER_ADDRESS2: Address.Address = '0x4cb8d282365C7bee8C0d3Bf1B3ca5828e0Db553F' export const EMITTER_FUNCTIONS = Abi.from(['function explicitEmit()', 'function implicitEmit()']) export const EMITTER_EVENT_TOPICS = [ AbiEvent.encode(AbiEvent.from('event Explicit(address sender)')).topics[0], @@ -14,5 +17,3 @@ export const USDC_ADDRESS: Address.Address = '0xaf88d065e77c8cc2239327c5edb3a432 // Environment variables export const LOCAL_RPC_URL = process.env.LOCAL_RPC_URL || 'http://localhost:8545' -export const { RPC_URL, PRIVATE_KEY } = process.env -export const CAN_RUN_LIVE = !!RPC_URL && !!PRIVATE_KEY diff --git a/packages/wallet/core/test/relayer/bundler.test.ts b/packages/wallet/core/test/relayer/bundler.test.ts index cf5b3df469..bc565e1cc3 100644 --- a/packages/wallet/core/test/relayer/bundler.test.ts +++ b/packages/wallet/core/test/relayer/bundler.test.ts @@ -2,8 +2,8 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' import { Address, Hex } from 'ox' import { UserOperation } from 'ox/erc4337' import { Network, Payload } from '@0xsequence/wallet-primitives' -import { Bundler, isBundler } from '../../src/relayer/bundler.js' -import { OperationStatus } from '../../src/relayer/relayer.js' +import { Bundler, isBundler } from '../../src/bundler/index.js' +import { Relayer } from '@0xsequence/relayer' // Test addresses and data const TEST_WALLET_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') @@ -220,7 +220,7 @@ describe('Bundler', () => { }) it('should handle various operation statuses', async () => { - const statuses: OperationStatus[] = [ + const statuses: Relayer.OperationStatus[] = [ { status: 'unknown' }, { status: 'pending' }, { status: 'confirmed', transactionHash: TEST_OP_HASH }, diff --git a/packages/wallet/core/test/session-manager.test.ts b/packages/wallet/core/test/session-manager.test.ts index 098d7e9c66..93a69ed89d 100644 --- a/packages/wallet/core/test/session-manager.test.ts +++ b/packages/wallet/core/test/session-manager.test.ts @@ -1,12 +1,17 @@ +import { Extensions } from '@0xsequence/wallet-primitives' import { AbiEvent, AbiFunction, Address, Bytes, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' import { describe, expect, it } from 'vitest' - import { Attestation, GenericTree, Payload, Permission, SessionConfig } from '../../primitives/src/index.js' import { Envelope, Signers, State, Utils, Wallet } from '../src/index.js' - -import { EMITTER_FUNCTIONS, EMITTER_ADDRESS, EMITTER_EVENT_TOPICS, LOCAL_RPC_URL, USDC_ADDRESS } from './constants' -import { Extensions } from '@0xsequence/wallet-primitives' - +import { ExplicitSessionConfig } from '../src/utils/session/types.js' +import { + EMITTER_ADDRESS1, + EMITTER_ADDRESS2, + EMITTER_EVENT_TOPICS, + EMITTER_FUNCTIONS, + LOCAL_RPC_URL, + USDC_ADDRESS, +} from './constants' const { PermissionBuilder, ERC20PermissionBuilder } = Utils function randomAddress(): Address.Address { @@ -26,6 +31,14 @@ const ALL_EXTENSIONS = [ name: 'Rc3', ...Extensions.Rc3, }, + { + name: 'Rc4', + ...Extensions.Rc4, + }, + { + name: 'Rc5', + ...Extensions.Rc5, + }, ] // Handle the increment call being first or last depending on the session manager version @@ -45,10 +58,26 @@ for (const extension of ALL_EXTENSIONS) { describe(`SessionManager (${extension.name})`, () => { const timeout = 30000 - const identityPrivateKey = Secp256k1.randomPrivateKey() - const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) - - const stateProvider = new State.Local.Provider() + const createImplicitSigner = async (redirectUrl: string, signingKey: Hex.Hex) => { + const implicitPrivateKey = Secp256k1.randomPrivateKey() + const implicitAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: implicitPrivateKey })) + const attestation: Attestation.Attestation = { + approvedSigner: implicitAddress, + identityType: new Uint8Array(4), + issuerHash: new Uint8Array(32), + audienceHash: new Uint8Array(32), + applicationData: new Uint8Array(), + authData: { + redirectUrl, + issuedAt: BigInt(Math.floor(Date.now() / 1000)), + }, + } + const identitySignature = Secp256k1.sign({ + payload: Attestation.hash(attestation), + privateKey: signingKey, + }) + return new Signers.Session.Implicit(implicitPrivateKey, attestation, identitySignature, implicitAddress) + } it( 'should load from state', @@ -56,9 +85,14 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + let topology = SessionConfig.emptySessionsTopology(identityAddress) // Add random signer to the topology - const sessionPermission: Signers.Session.ExplicitParams = { + const sessionPermission: ExplicitSessionConfig = { chainId, valueLimit: 1000000000000000000n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now @@ -121,7 +155,7 @@ for (const extension of ALL_EXTENSIONS) { const actualImageHash = await sessionManager.imageHash expect(actualImageHash).toBe(imageHash) expect(SessionConfig.isCompleteSessionsTopology(actualTopology)).toBe(true) - expect(SessionConfig.getIdentitySigner(actualTopology)).toBe(identityAddress) + expect(SessionConfig.getIdentitySigners(actualTopology)).toStrictEqual([identityAddress]) expect(SessionConfig.getImplicitBlacklist(actualTopology)).toStrictEqual([randomBlacklistAddress]) const actualPermissions = SessionConfig.getSessionPermissions(actualTopology, randomSigner) expect(actualPermissions).toStrictEqual({ @@ -138,6 +172,11 @@ for (const extension of ALL_EXTENSIONS) { async () => { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create implicit signer const implicitPrivateKey = Secp256k1.randomPrivateKey() const implicitAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: implicitPrivateKey })) @@ -171,7 +210,11 @@ for (const extension of ALL_EXTENSIONS) { { threshold: 1n, checkpoint: 0n, - topology: { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, + topology: [ + { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, + // Include a random node leaf (bytes32) to prevent image hash collision + Hex.random(32), + ], }, { stateProvider, @@ -184,7 +227,7 @@ for (const extension of ALL_EXTENSIONS) { // Create a test transaction const call: Payload.Call = { - to: EMITTER_ADDRESS, + to: EMITTER_ADDRESS1, value: 0n, data: AbiFunction.encodeData(EMITTER_FUNCTIONS[1]), // Implicit emit gasLimit: 0n, @@ -215,17 +258,178 @@ for (const extension of ALL_EXTENSIONS) { timeout, ) + it( + 'should create and sign with a multiple implicit sessions', + async () => { + const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) + + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + + const implicitSigner1 = await createImplicitSigner('https://example.com', identityPrivateKey) + const implicitSigner2 = await createImplicitSigner('https://another-example.com', identityPrivateKey) + const topology = SessionConfig.emptySessionsTopology(identityAddress) + await stateProvider.saveTree(SessionConfig.sessionsTopologyToConfigurationTree(topology)) + const imageHash = GenericTree.hash(SessionConfig.sessionsTopologyToConfigurationTree(topology)) + const wallet = await Wallet.fromConfiguration( + { + threshold: 1n, + checkpoint: 0n, + topology: [ + { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, + // Include a random node leaf (bytes32) to prevent image hash collision + Hex.random(32), + ], + }, + { + stateProvider, + }, + ) + const sessionManager = new Signers.SessionManager(wallet, { + provider, + sessionManagerAddress: extension.sessions, + }) + .withImplicitSigner(implicitSigner1) + .withImplicitSigner(implicitSigner2) + + // Create a test transaction + const payload: Payload.Parented = { + type: 'call', + nonce: 0n, + space: 0n, + calls: [ + { + to: EMITTER_ADDRESS1, + value: 0n, + data: AbiFunction.encodeData(EMITTER_FUNCTIONS[1]), // Implicit emit + gasLimit: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + }, + { + to: EMITTER_ADDRESS2, + value: 0n, + data: AbiFunction.encodeData(EMITTER_FUNCTIONS[1]), // Implicit emit + gasLimit: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + }, + ], + parentWallets: [wallet.address], + } + + // Sign the transaction + const chainId = Number(await provider.request({ method: 'eth_chainId' })) + const signature = await sessionManager.signSapient(wallet.address, chainId, payload, imageHash) + + expect(signature.type).toBe('sapient') + expect(signature.address).toBe(sessionManager.address) + expect(signature.data).toBeDefined() + + // Check if the signature is valid + const isValid = await sessionManager.isValidSapientSignature(wallet.address, chainId, payload, signature) + expect(isValid).toBe(true) + }, + timeout, + ) + + it( + 'should fail to sign with a multiple implicit sessions with different identity signers', + async () => { + const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) + + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + + const identityPrivateKey2 = Secp256k1.randomPrivateKey() + const identityAddress2 = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey2 })) + + const implicitSigner1 = await createImplicitSigner('https://example.com', identityPrivateKey) + const implicitSigner2 = await createImplicitSigner('https://another-example.com', identityPrivateKey2) + let topology = SessionConfig.emptySessionsTopology(identityAddress) + topology = SessionConfig.addIdentitySigner(topology, identityAddress2) + await stateProvider.saveTree(SessionConfig.sessionsTopologyToConfigurationTree(topology)) + const imageHash = GenericTree.hash(SessionConfig.sessionsTopologyToConfigurationTree(topology)) + const wallet = await Wallet.fromConfiguration( + { + threshold: 1n, + checkpoint: 0n, + topology: [ + { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, + // Include a random node leaf (bytes32) to prevent image hash collision + Hex.random(32), + ], + }, + { + stateProvider, + }, + ) + const sessionManager = new Signers.SessionManager(wallet, { + provider, + sessionManagerAddress: extension.sessions, + }) + .withImplicitSigner(implicitSigner1) + .withImplicitSigner(implicitSigner2) + + // Create a test transaction + const payload: Payload.Parented = { + type: 'call', + nonce: 0n, + space: 0n, + calls: [ + { + to: EMITTER_ADDRESS1, + value: 0n, + data: AbiFunction.encodeData(EMITTER_FUNCTIONS[1]), // Implicit emit + gasLimit: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + }, + { + to: EMITTER_ADDRESS2, + value: 0n, + data: AbiFunction.encodeData(EMITTER_FUNCTIONS[1]), // Implicit emit + gasLimit: 0n, + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert', + }, + ], + parentWallets: [wallet.address], + } + + // Sign the transaction + const chainId = Number(await provider.request({ method: 'eth_chainId' })) + await expect(sessionManager.signSapient(wallet.address, chainId, payload, imageHash)).rejects.toThrow( + 'Multiple implicit signers with different identity signers', + ) + }, + timeout, + ) + const shouldCreateAndSignWithExplicitSession = async (useChainId: boolean) => { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() - const explicitPermissions: Signers.Session.ExplicitParams = { + const explicitPermissions: ExplicitSessionConfig = { chainId: useChainId ? chainId : 0, valueLimit: 1000000000000000000n, // 1 ETH deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now - permissions: [PermissionBuilder.for(EMITTER_ADDRESS).allowAll().build()], + permissions: [PermissionBuilder.for(EMITTER_ADDRESS1).allowAll().build()], } const explicitSigner = new Signers.Session.Explicit(explicitPrivateKey, explicitPermissions) // Create the topology and wallet @@ -239,7 +443,11 @@ for (const extension of ALL_EXTENSIONS) { { threshold: 1n, checkpoint: 0n, - topology: { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, + topology: [ + { type: 'sapient-signer', address: extension.sessions, weight: 1n, imageHash }, + // Include a random node leaf (bytes32) to prevent image hash collision + Hex.random(32), + ], }, { stateProvider, @@ -253,7 +461,7 @@ for (const extension of ALL_EXTENSIONS) { // Create a test transaction within permissions const call: Payload.Call = { - to: EMITTER_ADDRESS, + to: EMITTER_ADDRESS1, value: 0n, data: AbiFunction.encodeData(EMITTER_FUNCTIONS[0]), // Explicit emit gasLimit: 0n, @@ -302,13 +510,18 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = 0 + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() const explicitPermissions: Signers.Session.ExplicitParams = { chainId, valueLimit: 1000000000000000000n, // 1 ETH deadline: BigInt(Math.floor(Date.now() / 1000) - 3600), // 1 hour ago - permissions: [PermissionBuilder.for(EMITTER_ADDRESS).allowAll().build()], + permissions: [PermissionBuilder.for(EMITTER_ADDRESS1).allowAll().build()], } const explicitSigner = new Signers.Session.Explicit(explicitPrivateKey, explicitPermissions) // Create the topology and wallet @@ -337,7 +550,7 @@ for (const extension of ALL_EXTENSIONS) { // Create a test transaction within permissions const call: Payload.Call = { - to: EMITTER_ADDRESS, + to: EMITTER_ADDRESS1, value: 0n, data: AbiFunction.encodeData(EMITTER_FUNCTIONS[0]), // Explicit emit gasLimit: 0n, @@ -353,8 +566,8 @@ for (const extension of ALL_EXTENSIONS) { } // Sign the transaction - expect(sessionManager.signSapient(wallet.address, chainId, payload, imageHash)).rejects.toThrow( - 'No signers match the topology', + await expect(sessionManager.signSapient(wallet.address, chainId, payload, imageHash)).rejects.toThrow( + `Signer supporting call is expired: ${explicitSigner.address}`, ) }, timeout, @@ -393,10 +606,26 @@ for (const extension of ALL_EXTENSIONS) { transaction: { to: Address.Address; data: Hex.Hex }, expectedEventTopic?: Hex.Hex, ) => { + // Generate and use a random sender address to prevent race conditions + const senderAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) + await provider.request({ + method: 'anvil_setBalance', + params: [senderAddress, Hex.fromNumber(1000000000000000000n)], + }) + await provider.request({ + method: 'anvil_impersonateAccount', + params: [senderAddress], + }) + console.log('Simulating transaction', transaction) const txHash = await provider.request({ method: 'eth_sendTransaction', - params: [transaction], + params: [ + { + ...transaction, + from: senderAddress, + }, + ], }) console.log('Transaction hash:', txHash) @@ -430,6 +659,11 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create an implicit signer const implicitPrivateKey = Secp256k1.randomPrivateKey() const implicitAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: implicitPrivateKey })) @@ -480,7 +714,7 @@ for (const extension of ALL_EXTENSIONS) { }) const call: Payload.Call = { - to: EMITTER_ADDRESS, + to: EMITTER_ADDRESS1, value: 0n, data: AbiFunction.encodeData(EMITTER_FUNCTIONS[1]), // Implicit emit gasLimit: 0n, @@ -502,13 +736,18 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() - const sessionPermission: Signers.Session.ExplicitParams = { + const sessionPermission: ExplicitSessionConfig = { chainId, valueLimit: 1000000000000000000n, // 1 ETH deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now - permissions: [PermissionBuilder.for(EMITTER_ADDRESS).allowAll().build()], + permissions: [PermissionBuilder.for(EMITTER_ADDRESS1).allowAll().build()], } const explicitSigner = new Signers.Session.Explicit(explicitPrivateKey, sessionPermission) // Test manually building the session topology @@ -548,7 +787,7 @@ for (const extension of ALL_EXTENSIONS) { }) const call: Payload.Call = { - to: EMITTER_ADDRESS, + to: EMITTER_ADDRESS1, value: 0n, data: AbiFunction.encodeData(EMITTER_FUNCTIONS[0]), // Explicit emit gasLimit: 0n, @@ -570,13 +809,18 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() - const sessionPermission: Signers.Session.ExplicitParams = { + const sessionPermission: ExplicitSessionConfig = { chainId, valueLimit: 0n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now - permissions: [PermissionBuilder.for(EMITTER_ADDRESS).forFunction(EMITTER_FUNCTIONS[0]).onlyOnce().build()], + permissions: [PermissionBuilder.for(EMITTER_ADDRESS1).forFunction(EMITTER_FUNCTIONS[0]).onlyOnce().build()], } const explicitSigner = new Signers.Session.Explicit(explicitPrivateKey, sessionPermission) // Test manually building the session topology @@ -616,7 +860,7 @@ for (const extension of ALL_EXTENSIONS) { }) const call: Payload.Call = { - to: EMITTER_ADDRESS, + to: EMITTER_ADDRESS1, value: 0n, data: AbiFunction.encodeData(EMITTER_FUNCTIONS[0]), // Explicit emit gasLimit: 0n, @@ -657,11 +901,16 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() const explicitAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: explicitPrivateKey })) const approveAmount = 10000000n // 10 USDC - const sessionPermission: Signers.Session.ExplicitParams = { + const sessionPermission: ExplicitSessionConfig = { chainId, valueLimit: 0n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now @@ -755,10 +1004,15 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() const explicitAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: explicitPrivateKey })) - const sessionPermission: Signers.Session.ExplicitParams = { + const sessionPermission: ExplicitSessionConfig = { chainId, valueLimit: 1000000000000000000n, // 1 ETH deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now @@ -860,10 +1114,15 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() const explicitAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: explicitPrivateKey })) - const sessionPermission: Signers.Session.ExplicitParams = { + const sessionPermission: ExplicitSessionConfig = { chainId, valueLimit: 1000000000000000000n, // 1 ETH deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now @@ -979,10 +1238,15 @@ for (const extension of ALL_EXTENSIONS) { const provider = Provider.from(RpcTransport.fromHttp(LOCAL_RPC_URL)) const chainId = Number(await provider.request({ method: 'eth_chainId' })) + // Create unique identity and state provider for this test + const identityPrivateKey = Secp256k1.randomPrivateKey() + const identityAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: identityPrivateKey })) + const stateProvider = new State.Local.Provider() + // Create explicit signer const explicitPrivateKey = Secp256k1.randomPrivateKey() const explicitAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: explicitPrivateKey })) - const sessionPermission: Signers.Session.ExplicitParams = { + const sessionPermission: ExplicitSessionConfig = { chainId, valueLimit: 1000000000000000000n, // 1 ETH deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now diff --git a/packages/wallet/core/test/signers-session-implicit.test.ts b/packages/wallet/core/test/signers-session-implicit.test.ts index 7d6b0db994..ed66a50afc 100644 --- a/packages/wallet/core/test/signers-session-implicit.test.ts +++ b/packages/wallet/core/test/signers-session-implicit.test.ts @@ -103,7 +103,7 @@ describe('Implicit Session', () => { const result = implicitSigner.isValid(topology, 1) expect(result.isValid).toBe(false) - expect(result.invalidReason).toBe('Identity signer mismatch') + expect(result.invalidReason).toBe('Identity signer not found') }) it('should return true regardless of chainId', () => { @@ -223,7 +223,7 @@ describe('Implicit Session', () => { const result = implicitSigner.isValid(topology, 1) expect(result.isValid).toBe(false) - expect(result.invalidReason).toBe('Identity signer mismatch') + expect(result.invalidReason).toBe('Identity signer not found') }) it('should return false when attestation is issued in the future', () => { diff --git a/packages/wallet/core/vitest.config.ts b/packages/wallet/core/vitest.config.ts deleted file mode 100644 index 0b2f7c6c76..0000000000 --- a/packages/wallet/core/vitest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - poolOptions: { - singleThread: true, - }, - }, -}) diff --git a/packages/wallet/dapp-client/CHANGELOG.md b/packages/wallet/dapp-client/CHANGELOG.md new file mode 100644 index 0000000000..e7feb8989e --- /dev/null +++ b/packages/wallet/dapp-client/CHANGELOG.md @@ -0,0 +1,67 @@ +# @0xsequence/dapp-client + +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.6 + - @0xsequence/relayer@3.0.0-beta.6 + - @0xsequence/wallet-core@3.0.0-beta.6 + - @0xsequence/wallet-primitives@3.0.0-beta.6 + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.5 + - @0xsequence/relayer@3.0.0-beta.5 + - @0xsequence/wallet-core@3.0.0-beta.5 + - @0xsequence/wallet-primitives@3.0.0-beta.5 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.4 + - @0xsequence/relayer@3.0.0-beta.4 + - @0xsequence/wallet-core@3.0.0-beta.4 + - @0xsequence/wallet-primitives@3.0.0-beta.4 + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.3 + - @0xsequence/relayer@3.0.0-beta.3 + - @0xsequence/wallet-core@3.0.0-beta.3 + - @0xsequence/wallet-primitives@3.0.0-beta.3 + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.2 + - @0xsequence/relayer@3.0.0-beta.2 + - @0xsequence/wallet-core@3.0.0-beta.2 + - @0xsequence/wallet-primitives@3.0.0-beta.2 + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.1 + - @0xsequence/relayer@3.0.0-beta.1 + - @0xsequence/wallet-core@3.0.0-beta.1 + - @0xsequence/wallet-primitives@3.0.0-beta.1 diff --git a/packages/wallet/dapp-client/eslint.config.mjs b/packages/wallet/dapp-client/eslint.config.mjs index 19170f88ed..cecf89b031 100644 --- a/packages/wallet/dapp-client/eslint.config.mjs +++ b/packages/wallet/dapp-client/eslint.config.mjs @@ -1,4 +1,4 @@ -import { config } from "@repo/eslint-config/react-internal"; +import { config as baseConfig } from "@repo/eslint-config/base" /** @type {import("eslint").Linter.Config} */ -export default config; +export default baseConfig diff --git a/packages/wallet/dapp-client/package.json b/packages/wallet/dapp-client/package.json index b10524e429..40a6659c59 100644 --- a/packages/wallet/dapp-client/package.json +++ b/packages/wallet/dapp-client/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/dapp-client", - "version": "0.0.0", + "version": "3.0.0-beta.6", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -21,18 +21,19 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "@vitest/coverage-v8": "^3.2.4", - "dotenv": "^16.5.0", - "fake-indexeddb": "^6.0.1", - "happy-dom": "^20.0.2", - "typescript": "^5.8.3", - "vitest": "^3.2.1" + "@types/node": "^25.0.2", + "@vitest/coverage-v8": "^4.0.15", + "dotenv": "^17.2.3", + "fake-indexeddb": "^6.2.5", + "happy-dom": "^20.0.11", + "typescript": "^5.9.3", + "vitest": "^4.0.15" }, "dependencies": { + "@0xsequence/guard": "workspace:^", + "@0xsequence/relayer": "workspace:^", "@0xsequence/wallet-core": "workspace:^", "@0xsequence/wallet-primitives": "workspace:^", - "@0xsequence/guard": "workspace:^", - "ox": "^0.7.2" + "ox": "^0.9.17" } } diff --git a/packages/wallet/dapp-client/src/ChainSessionManager.ts b/packages/wallet/dapp-client/src/ChainSessionManager.ts index 76b62a3f3d..cd67c6b609 100644 --- a/packages/wallet/dapp-client/src/ChainSessionManager.ts +++ b/packages/wallet/dapp-client/src/ChainSessionManager.ts @@ -1,8 +1,18 @@ -import { Envelope, Relayer, Signers, State, Wallet } from '@0xsequence/wallet-core' -import { Attestation, Constants, Extensions, Payload, SessionConfig } from '@0xsequence/wallet-primitives' import * as Guard from '@0xsequence/guard' import { AbiFunction, Address, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' +import { + Envelope, + Signers, + State, + Wallet, + Attestation, + Constants, + Extensions, + Payload, + SessionConfig, +} from './index.js' + import { DappTransport } from './DappTransport.js' import { @@ -18,21 +28,25 @@ import { SequenceStorage } from './utils/storage.js' import { getRelayerUrl, getRpcUrl } from './utils/index.js' import { - AddExplicitSessionPayload, - CreateNewSessionPayload, - ConnectSuccessResponsePayload, + CreateNewSessionResponse, ExplicitSessionEventListener, - ModifySessionPayload, - ModifySessionSuccessResponsePayload, LoginMethod, RandomPrivateKeyFn, RequestActionType, - Session, Transaction, TransportMode, GuardConfig, + CreateNewSessionPayload, + ModifyExplicitSessionPayload, + SessionResponse, + AddExplicitSessionPayload, + FeeOption, + OperationFailedStatus, + OperationStatus, } from './types/index.js' import { CACHE_DB_NAME, VALUE_FORWARDER_ADDRESS } from './utils/constants.js' +import { ExplicitSession, ImplicitSession, ExplicitSessionConfig } from './index.js' +import { Relayer } from '@0xsequence/relayer' interface ChainSessionManagerEventMap { explicitSessionResponse: ExplicitSessionEventListener @@ -54,13 +68,14 @@ export class ChainSessionManager { [K in keyof ChainSessionManagerEventMap]?: Set } = {} - private sessions: Session[] = [] + private explicitSessions: ExplicitSession[] = [] + private implicitSession: ImplicitSession | null = null private walletAddress: Address.Address | null = null private sessionManager: Signers.SessionManager | null = null private wallet: Wallet | null = null private provider: Provider.Provider | null = null - private relayer: Relayer.Standard.Rpc.RpcRelayer + private relayer: Relayer.RpcRelayer private readonly chainId: number public transport: DappTransport | null = null private sequenceStorage: SequenceStorage @@ -99,7 +114,8 @@ export class ChainSessionManager { const rpcUrl = getRpcUrl(chainId, nodesUrl, projectAccessKey) this.chainId = chainId - if (canUseIndexedDb) { + const canUseIndexedDbInEnv = canUseIndexedDb && typeof indexedDB !== 'undefined' + if (canUseIndexedDbInEnv) { this.stateProvider = new State.Cached({ source: new State.Sequence.Provider(keyMachineUrl), cache: new State.Local.Provider(new State.Local.IndexedDbStore(CACHE_DB_NAME)), @@ -109,10 +125,12 @@ export class ChainSessionManager { } this.guard = guard this.provider = Provider.from(RpcTransport.fromHttp(rpcUrl)) - this.relayer = new Relayer.Standard.Rpc.RpcRelayer( + this.relayer = new Relayer.RpcRelayer( getRelayerUrl(chainId, relayerUrl), this.chainId, getRpcUrl(chainId, nodesUrl, projectAccessKey), + undefined, + projectAccessKey, ) this.transport = transport @@ -162,7 +180,7 @@ export class ChainSessionManager { * @throws {InitializationError} If initialization fails. */ async initialize(): Promise<{ - loginMethod: string | null + loginMethod: LoginMethod | null userEmail: string | null } | void> { if (this.isInitializing) return @@ -204,7 +222,7 @@ export class ChainSessionManager { stateProvider: this.stateProvider, }) this.sessionManager = new Signers.SessionManager(this.wallet, { - sessionManagerAddress: Extensions.Rc3.sessions, + sessionManagerAddress: Extensions.Rc5.sessions, provider: this.provider!, }) this.isInitialized = true @@ -219,7 +237,7 @@ export class ChainSessionManager { const implicitSession = await this.sequenceStorage.getImplicitSession() - if (implicitSession && implicitSession.chainId === this.chainId) { + if (implicitSession) { await this._initializeImplicitSessionInternal( implicitSession.pk, walletAddress, @@ -250,13 +268,14 @@ export class ChainSessionManager { /** * Initiates the creation of a new session by sending a request to the wallet. - * @param permissions (Optional) Permissions for an initial explicit session. + * @param origin The origin of the session. + * @param sessionConfig (Optional) Session configuration for an initial explicit session. * @param options (Optional) Additional options like preferred login method. * @throws {InitializationError} If a session already exists or the transport fails to initialize. */ async createNewSession( origin: string, - permissions?: Signers.Session.ExplicitParams, + sessionConfig?: ExplicitSessionConfig, options: { preferredLoginMethod?: LoginMethod email?: string @@ -267,23 +286,34 @@ export class ChainSessionManager { throw new InitializationError('A session already exists. Disconnect first.') } - const newPk = await this.randomPrivateKeyFn() - const newSignerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: newPk })) + const shouldCreateSession = !!sessionConfig || (options.includeImplicitSession ?? false) + + const newPk = shouldCreateSession ? await this.randomPrivateKeyFn() : null + const newSignerAddress = + shouldCreateSession && newPk ? Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: newPk })) : null + const completeSession = + shouldCreateSession && newSignerAddress + ? { + sessionAddress: newSignerAddress, + ...sessionConfig, + } + : undefined try { if (!this.transport) throw new InitializationError('Transport failed to initialize.') const payload: CreateNewSessionPayload = { - sessionAddress: newSignerAddress, origin, - permissions, + session: completeSession as ExplicitSession | undefined, includeImplicitSession: options.includeImplicitSession ?? false, preferredLoginMethod: options.preferredLoginMethod, email: options.preferredLoginMethod === 'email' ? options.email : undefined, } if (this.transport.mode === TransportMode.REDIRECT) { - await this.sequenceStorage.saveTempSessionPk(newPk) + if (shouldCreateSession && newPk) { + await this.sequenceStorage.saveTempSessionPk(newPk) + } await this.sequenceStorage.savePendingRequest({ chainId: this.chainId, action: RequestActionType.CREATE_NEW_SESSION, @@ -292,7 +322,7 @@ export class ChainSessionManager { await this.sequenceStorage.setPendingRedirectRequest(true) } - const connectResponse = await this.transport.sendRequest( + const connectResponse = await this.transport.sendRequest( RequestActionType.CREATE_NEW_SESSION, this.redirectUrl, payload, @@ -302,34 +332,44 @@ export class ChainSessionManager { const receivedAddress = Address.from(connectResponse.walletAddress) const { attestation, signature, userEmail, loginMethod, guard } = connectResponse - if (attestation && signature) { + if (shouldCreateSession) { await this._resetStateAndClearCredentials() + this.loginMethod = null + this.userEmail = null + this.initializeWithWallet(receivedAddress) - await this._initializeImplicitSessionInternal( - newPk, - receivedAddress, - attestation, - signature, - true, - loginMethod, - userEmail, - guard, - ) - } + if (attestation && signature && newPk) { + await this._initializeImplicitSessionInternal( + newPk, + receivedAddress, + attestation, + signature, + true, + loginMethod, + userEmail, + guard, + ) + } - if (permissions) { + if (sessionConfig && newPk) { + await this._initializeExplicitSessionInternal(newPk, loginMethod, userEmail, guard, true) + await this.sequenceStorage.saveExplicitSession({ + pk: newPk, + walletAddress: receivedAddress, + chainId: this.chainId, + guard, + loginMethod, + userEmail, + }) + } + } else { + await this._resetStateAndClearCredentials() this.initializeWithWallet(receivedAddress) - await this._initializeExplicitSessionInternal(newPk, loginMethod, userEmail, guard, true) - await this.sequenceStorage.saveExplicitSession({ - pk: newPk, - walletAddress: receivedAddress, - chainId: this.chainId, - guard, - loginMethod, - userEmail, - }) + this.loginMethod = loginMethod ?? null + this.userEmail = userEmail ?? null + this.guard = guard } if (this.transport.mode === TransportMode.POPUP) { @@ -344,11 +384,11 @@ export class ChainSessionManager { /** * Initiates the addition of a new explicit session by sending a request to the wallet. - * @param permissions The permissions for the new explicit session. + * @param explicitSessionConfig The explicit session configuration for the new explicit session. * @throws {InitializationError} If the manager is not initialized. * @throws {AddExplicitSessionError} If adding the session fails. */ - async addExplicitSession(permissions: Signers.Session.ExplicitParams): Promise { + async addExplicitSession(explicitSessionConfig: ExplicitSessionConfig): Promise { if (!this.walletAddress) { throw new InitializationError( 'Cannot add an explicit session without a wallet address. Initialize the manager with a wallet address first.', @@ -363,8 +403,7 @@ export class ChainSessionManager { const newSignerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: newPk })) const payload: AddExplicitSessionPayload = { - sessionAddress: newSignerAddress, - permissions, + session: { ...explicitSessionConfig, sessionAddress: newSignerAddress, type: 'explicit' }, } if (this.transport.mode === TransportMode.REDIRECT) { @@ -377,7 +416,7 @@ export class ChainSessionManager { await this.sequenceStorage.setPendingRedirectRequest(true) } - const response = await this.transport.sendRequest( + const response = await this.transport.sendRequest( RequestActionType.ADD_EXPLICIT_SESSION, this.redirectUrl, payload, @@ -407,6 +446,7 @@ export class ChainSessionManager { userEmail: response.userEmail, guard: response.guard, }) + await this.sequenceStorage.clearSessionlessConnection() } catch (err) { if (this.transport?.mode === TransportMode.POPUP) this.transport.closeWallet() throw new AddExplicitSessionError(`Adding explicit session failed: ${err}`) @@ -415,15 +455,11 @@ export class ChainSessionManager { /** * Initiates the modification of an existing explicit session by sending a request to the wallet. - * @param sessionAddress The address of the explicit session to modify. - * @param newPermissions The new permissions for the session. + * @param modifiedExplicitSession The modified explicit session. * @throws {InitializationError} If the manager is not initialized. * @throws {ModifyExplicitSessionError} If modifying the session fails. */ - async modifyExplicitSession( - sessionAddress: Address.Address, - newPermissions: Signers.Session.ExplicitParams, - ): Promise { + async modifyExplicitSession(modifiedExplicitSession: ExplicitSession): Promise { if (!this.walletAddress) { throw new InitializationError( 'Cannot modify an explicit session without a wallet address. Initialize the manager with a wallet address first.', @@ -433,15 +469,22 @@ export class ChainSessionManager { try { if (!this.transport) throw new InitializationError('Transport failed to initialize.') - const session = this.sessions.find((s) => Address.isEqual(s.address, sessionAddress)) - if (!session) { + if (!modifiedExplicitSession.sessionAddress) { + throw new ModifyExplicitSessionError('Session address is required.') + } + + const existingExplicitSession = this.explicitSessions.find((s) => + Address.isEqual(s.sessionAddress!, modifiedExplicitSession.sessionAddress!), + ) + if (!existingExplicitSession) { throw new ModifyExplicitSessionError('Session not found.') } - const payload: ModifySessionPayload = { + const payload: ModifyExplicitSessionPayload = { walletAddress: this.walletAddress, - sessionAddress: sessionAddress, - permissions: newPermissions, + session: { + ...modifiedExplicitSession, + }, } if (this.transport.mode === TransportMode.REDIRECT) { @@ -453,7 +496,7 @@ export class ChainSessionManager { await this.sequenceStorage.setPendingRedirectRequest(true) } - const response = await this.transport.sendRequest( + const response = await this.transport.sendRequest( RequestActionType.MODIFY_EXPLICIT_SESSION, this.redirectUrl, payload, @@ -462,12 +505,14 @@ export class ChainSessionManager { if ( !Address.isEqual(Address.from(response.walletAddress), this.walletAddress) && - !Address.isEqual(Address.from(response.sessionAddress), sessionAddress) + !Address.isEqual(Address.from(response.sessionAddress), modifiedExplicitSession.sessionAddress) ) { throw new ModifyExplicitSessionError('Wallet or session address mismatch.') } - session.permissions = newPermissions + existingExplicitSession.permissions = modifiedExplicitSession.permissions + existingExplicitSession.deadline = modifiedExplicitSession.deadline + existingExplicitSession.valueLimit = modifiedExplicitSession.valueLimit if (this.transport?.mode === TransportMode.POPUP) { this.transport?.closeWallet() @@ -484,29 +529,38 @@ export class ChainSessionManager { * @returns A promise resolving to true on success. */ private async _handleRedirectConnectionResponse(response: { - payload: ConnectSuccessResponsePayload + payload: CreateNewSessionResponse action: string }): Promise { - const tempPk = await this.sequenceStorage.getAndClearTempSessionPk() - if (!tempPk) { - throw new InitializationError('Failed to retrieve temporary session key after redirect.') - } - try { const connectResponse = response.payload const receivedAddress = Address.from(connectResponse.walletAddress) const { userEmail, loginMethod, guard } = connectResponse + const savedRequest = await this.sequenceStorage.peekPendingRequest() + const savedPayload = savedRequest?.payload as CreateNewSessionPayload | undefined + const explicitSessionRequested = (savedPayload?.session?.permissions?.length ?? 0) > 0 + const implicitSessionRequested = savedPayload?.includeImplicitSession ?? false + const needsTempPk = explicitSessionRequested || implicitSessionRequested + const tempPk = needsTempPk ? await this.sequenceStorage.getAndClearTempSessionPk() : null + + if (needsTempPk && !tempPk) { + throw new InitializationError('Failed to retrieve temporary session key after redirect.') + } if (response.action === RequestActionType.CREATE_NEW_SESSION) { const { attestation, signature } = connectResponse - const savedRequest = await this.sequenceStorage.peekPendingRequest() - const savedPayload = savedRequest?.payload as CreateNewSessionPayload | undefined await this._resetStateAndClearCredentials() + this.loginMethod = null + this.userEmail = null + this.initializeWithWallet(receivedAddress) - if (attestation && signature) { + if (implicitSessionRequested) { + if (!attestation || !signature || !tempPk) { + throw new InitializationError('Missing implicit session data in redirect response.') + } await this._initializeImplicitSessionInternal( tempPk, receivedAddress, @@ -519,7 +573,7 @@ export class ChainSessionManager { ) } - if (savedRequest && savedPayload && savedPayload.permissions) { + if (explicitSessionRequested && savedPayload?.session && tempPk) { await this._initializeExplicitSessionInternal(tempPk, loginMethod, userEmail, guard, true) await this.sequenceStorage.saveExplicitSession({ pk: tempPk, @@ -529,29 +583,42 @@ export class ChainSessionManager { userEmail, guard, }) + await this.sequenceStorage.clearSessionlessConnection() + } + + if (!explicitSessionRequested && !implicitSessionRequested) { + this.loginMethod = loginMethod ?? null + this.userEmail = userEmail ?? null + this.guard = guard } } else if (response.action === RequestActionType.ADD_EXPLICIT_SESSION) { if (!this.walletAddress || !Address.isEqual(receivedAddress, this.walletAddress)) { throw new InitializationError('Received an explicit session for a wallet that is not active.') } + const explicitSessionPk = tempPk ?? (await this.sequenceStorage.getAndClearTempSessionPk()) + if (!explicitSessionPk) { + throw new InitializationError('Failed to retrieve temporary session key for explicit session.') + } + await this._initializeExplicitSessionInternal( - tempPk, + explicitSessionPk, this.loginMethod ?? undefined, this.userEmail ?? undefined, this.guard ?? undefined, true, ) await this.sequenceStorage.saveExplicitSession({ - pk: tempPk, + pk: explicitSessionPk, walletAddress: receivedAddress, chainId: this.chainId, loginMethod: this.loginMethod ?? undefined, userEmail: this.userEmail ?? undefined, guard: this.guard ?? undefined, }) + await this.sequenceStorage.clearSessionlessConnection() - const newSignerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: tempPk })) + const newSignerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: explicitSessionPk })) this.emit('explicitSessionResponse', { action: RequestActionType.ADD_EXPLICIT_SESSION, @@ -560,6 +627,8 @@ export class ChainSessionManager { sessionAddress: newSignerAddress, }, }) + } else { + throw new WalletRedirectError(`Received unhandled redirect action: ${response.action}`) } this.isInitialized = true return true @@ -613,10 +682,10 @@ export class ChainSessionManager { ) this.sessionManager = this.sessionManager.withImplicitSigner(implicitSigner) - this.sessions.push({ - address: implicitSigner.address, - isImplicit: true, - }) + this.implicitSession = { + sessionAddress: implicitSigner.address, + type: 'implicit', + } this.walletAddress = address if (saveSession) @@ -662,7 +731,7 @@ export class ChainSessionManager { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const tempManager = new Signers.SessionManager(this.wallet, { - sessionManagerAddress: Extensions.Rc3.sessions, + sessionManagerAddress: Extensions.Rc5.sessions, provider: this.provider, }) const topology = await tempManager.getTopology() @@ -679,11 +748,13 @@ export class ChainSessionManager { const explicitSigner = new Signers.Session.Explicit(pk, permissions) this.sessionManager = this.sessionManager.withExplicitSigner(explicitSigner) - this.sessions.push({ - address: explicitSigner.address, - isImplicit: false, + this.explicitSessions.push({ + sessionAddress: explicitSigner.address, chainId: this.chainId, - permissions, + permissions: permissions.permissions, + valueLimit: permissions.valueLimit, + deadline: permissions.deadline, + type: 'explicit', }) if (guard && !this.guard) this.guard = guard @@ -700,6 +771,20 @@ export class ChainSessionManager { throw new InitializationError(`Explicit session init failed after ${maxRetries} attempts: ${lastError.message}`) } + private async _refreshExplicitSession(expiredSignerAddress: Address.Address): Promise { + if (!this.wallet || !this.sessionManager || !this.provider || !this.isInitialized) + throw new InitializationError('Session is not initialized.') + // Find current explicit session + const explicitSigner = this.explicitSessions.find((s) => Address.isEqual(s.sessionAddress, expiredSignerAddress)) + if (!explicitSigner) throw new ModifyExplicitSessionError('Explicit session not found.') + // Update the deadline + const newExplicitSession = { + ...explicitSigner, + deadline: BigInt(Math.floor(Date.now() / 1000)) + BigInt(24 * 60 * 60), + } + await this.modifyExplicitSession(newExplicitSession) + } + /** * Checks if the current session has permission to execute a set of transactions. * @param transactions The transactions to check permissions for. @@ -726,6 +811,19 @@ export class ChainSessionManager { await this.sessionManager.findSignersForCalls(this.wallet.address, this.chainId, calls) return true } catch (error) { + if (error instanceof Error && error.message.includes('Signer supporting call is expired')) { + // Extract the expired signer address from the message with address regex + const expiredSignerAddress = error.message.match(/(0x[0-9a-fA-F]{40})/)?.[1] + if (expiredSignerAddress) { + // Refresh the session + await this._refreshExplicitSession(Address.from(expiredSignerAddress)) + // Retry the permission check + return this.hasPermission(transactions) + } else { + // Could not parse error message. Rethrow as this shouldn't happen. + throw error + } + } // An error from findSignersForCalls indicates a permission failure. console.warn( `Permission check failed for chain ${this.chainId}:`, @@ -741,7 +839,7 @@ export class ChainSessionManager { * @returns A promise that resolves with an array of fee options. * @throws {FeeOptionError} If fetching fee options fails. */ - async getFeeOptions(calls: Transaction[]): Promise { + async getFeeOptions(calls: Transaction[]): Promise { const callsToSend = calls.map((tx) => ({ to: tx.to, value: tx.value, @@ -768,7 +866,7 @@ export class ChainSessionManager { * @throws {InitializationError} If the session is not initialized. * @throws {TransactionError} If the transaction fails at any stage. */ - async buildSignAndSendTransactions(transactions: Transaction[], feeOption?: Relayer.FeeOption): Promise { + async buildSignAndSendTransactions(transactions: Transaction[], feeOption?: FeeOption): Promise { if (!this.wallet || !this.sessionManager || !this.provider || !this.isInitialized) throw new InitializationError('Session is not initialized.') try { @@ -815,7 +913,7 @@ export class ChainSessionManager { if (status.status === 'confirmed') { return status.transactionHash } else { - const failedStatus = status as Relayer.OperationFailedStatus + const failedStatus = status as OperationFailedStatus const reason = failedStatus.reason || `unexpected status ${status.status}` throw new TransactionError(`Transaction failed: ${reason}`) } @@ -853,7 +951,7 @@ export class ChainSessionManager { ) { return this._handleRedirectConnectionResponse(response) } else if (response.action === RequestActionType.MODIFY_EXPLICIT_SESSION) { - const modifyResponse = response.payload as ModifySessionSuccessResponsePayload + const modifyResponse = response.payload as SessionResponse if (!Address.isEqual(Address.from(modifyResponse.walletAddress), this.walletAddress!)) { throw new ModifyExplicitSessionError('Wallet address mismatch on redirect response.') } @@ -880,12 +978,24 @@ export class ChainSessionManager { return this.walletAddress } + getGuard(): GuardConfig | undefined { + return this.guard + } + /** - * Gets the sessions (signers) managed by this instance. + * Gets the sessions (signers) managed by this session manager. * @returns An array of session objects. */ - getSessions(): Session[] { - return this.sessions + getExplicitSessions(): ExplicitSession[] { + return this.explicitSessions + } + + /** + * Gets the implicit session managed by this session manager. + * @returns An implicit session object or null if no implicit session is found. + */ + getImplicitSession(): ImplicitSession | null { + return this.implicitSession } /** @@ -955,7 +1065,7 @@ export class ChainSessionManager { * @param chainId The chain ID of the transaction. * @returns The final status of the transaction. */ - private async _waitForTransactionReceipt(opHash: `0x${string}`, chainId: number): Promise { + private async _waitForTransactionReceipt(opHash: `0x${string}`, chainId: number): Promise { try { while (true) { const currentStatus = await this.relayer.status(opHash, chainId) @@ -973,11 +1083,13 @@ export class ChainSessionManager { * @private Resets the internal state of the manager without clearing stored credentials. */ private _resetState(): void { - this.sessions = [] + this.explicitSessions = [] + this.implicitSession = null this.walletAddress = null this.wallet = null this.sessionManager = null this.isInitialized = false + this.guard = undefined } /** @@ -987,5 +1099,6 @@ export class ChainSessionManager { this._resetState() await this.sequenceStorage.clearImplicitSession() await this.sequenceStorage.clearExplicitSessions() + await this.sequenceStorage.clearSessionlessConnection() } } diff --git a/packages/wallet/dapp-client/src/DappClient.ts b/packages/wallet/dapp-client/src/DappClient.ts index 4bff6bdb4a..3c5a29cba9 100644 --- a/packages/wallet/dapp-client/src/DappClient.ts +++ b/packages/wallet/dapp-client/src/DappClient.ts @@ -1,21 +1,23 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Relayer, Signers } from '@0xsequence/wallet-core' import { Address, Hex } from 'ox' +import { type ExplicitSession, type ExplicitSessionConfig, type ImplicitSession, type Session } from './index.js' + import { ChainSessionManager } from './ChainSessionManager.js' import { DappTransport } from './DappTransport.js' -import { InitializationError, SigningError, TransactionError } from './utils/errors.js' -import { SequenceStorage, WebStorage } from './utils/storage.js' +import { ConnectionError, InitializationError, SigningError, TransactionError } from './utils/errors.js' +import { SequenceStorage, WebStorage, type SessionlessConnectionData } from './utils/storage.js' import { + CreateNewSessionResponse, DappClientExplicitSessionEventListener, DappClientWalletActionEventListener, + FeeOption, + GetFeeTokensResponse, GuardConfig, LoginMethod, RandomPrivateKeyFn, RequestActionType, SendWalletTransactionPayload, SequenceSessionStorage, - Session, SignMessagePayload, SignTypedDataPayload, Transaction, @@ -25,6 +27,8 @@ import { } from './types/index.js' import { TypedData } from 'ox/TypedData' import { KEYMACHINE_URL, NODES_URL, RELAYER_URL } from './utils/constants.js' +import { getRelayerUrl, getRpcUrl } from './utils/index.js' +import { Relayer } from '@0xsequence/relayer' export type DappClientEventListener = (data?: any) => void @@ -39,8 +43,6 @@ interface DappClientEventMap { * This client manages user sessions across multiple chains, handles connection * and disconnection, and provides methods for signing and sending transactions. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client} for more detailed documentation. - * * @example * // It is recommended to manage a singleton instance of this client. * const dappClient = new DappClient('http://localhost:5173'); @@ -58,7 +60,7 @@ interface DappClientEventMap { export class DappClient { public isInitialized = false - public loginMethod: string | null = null + public loginMethod: LoginMethod | null = null public userEmail: string | null = null public guard?: GuardConfig @@ -67,7 +69,8 @@ export class DappClient { private chainSessionManagers: Map = new Map() private walletUrl: string - private transport: DappTransport + private transport: DappTransport | null = null + private transportModeSetting: TransportMode private projectAccessKey: string private nodesUrl: string private relayerUrl: string @@ -82,10 +85,16 @@ export class DappClient { private isInitializing = false private walletAddress: Address.Address | null = null + private hasSessionlessConnection = false + private cachedSessionlessConnection: SessionlessConnectionData | null = null private eventListeners: { [K in keyof DappClientEventMap]?: Set } = {} + private get isBrowser(): boolean { + return typeof window !== 'undefined' && typeof document !== 'undefined' + } + /** * @param walletUrl The URL of the Wallet Webapp. * @param origin The origin of the dapp @@ -130,14 +139,8 @@ export class DappClient { canUseIndexedDb = true, } = options || {} - this.transport = new DappTransport( - walletUrl, - transportMode, - undefined, - sequenceSessionStorage, - redirectActionHandler, - ) this.walletUrl = walletUrl + this.transportModeSetting = transportMode this.projectAccessKey = projectAccessKey this.nodesUrl = options?.nodesUrl || NODES_URL this.relayerUrl = options?.relayerUrl || RELAYER_URL @@ -155,7 +158,7 @@ export class DappClient { * @returns The transport mode of the client. {@link TransportMode} */ public get transportMode(): TransportMode { - return this.transport.mode + return this.transport?.mode ?? this.transportModeSetting } /** @@ -164,8 +167,6 @@ export class DappClient { * @param listener The listener to call when the event occurs. * @returns A function to remove the listener. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/on} for more detailed documentation. - * * @example * useEffect(() => { * const handleWalletAction = (response) => { @@ -191,8 +192,6 @@ export class DappClient { * Retrieves the wallet address of the current session. * @returns The wallet address of the current session, or null if not initialized. {@link Address.Address} * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/get-wallet-address} for more detailed documentation. - * * @example * const dappClient = new DappClient('http://localhost:5173'); * await dappClient.initialize(); @@ -207,31 +206,64 @@ export class DappClient { } /** - * Retrieves a list of all active sessions (signers) associated with the current wallet. - * @returns An array of all the active sessions. {@link { address: Address.Address, isImplicit: boolean }[]} - * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/get-all-sessions} for more detailed documentation. + * Retrieves a list of all active explicit sessions (signers) associated with the current wallet. + * @returns An array of all the active explicit sessions. {@link ExplicitSession[]} * * @example * const dappClient = new DappClient('http://localhost:5173'); * await dappClient.initialize(); * * if (dappClient.isInitialized) { - * const sessions = dappClient.getAllSessions(); - * console.log('Sessions:', sessions); + * const explicitSessions = dappClient.getAllExplicitSessions(); + * console.log('Sessions:', explicitSessions); * } */ - public getAllSessions(): Session[] { - const allSessions = new Map() + public getAllExplicitSessions(): ExplicitSession[] { + const allExplicitSessions = new Map() Array.from(this.chainSessionManagers.values()).forEach((chainSessionManager) => { - chainSessionManager.getSessions().forEach((session) => { - const uniqueKey = `${session.address.toLowerCase()}-${session.isImplicit}` - if (!allSessions.has(uniqueKey)) { - allSessions.set(uniqueKey, session) + chainSessionManager.getExplicitSessions().forEach((session) => { + const uniqueKey = session.sessionAddress?.toLowerCase() + if (!allExplicitSessions.has(uniqueKey)) { + allExplicitSessions.set(uniqueKey, session) } }) }) - return Array.from(allSessions.values()) + return Array.from(allExplicitSessions.values()) + } + + /** + * Retrieves a list of all active implicit sessions (signers) associated with the current wallet. + * @note There can only be one implicit session per chain. + * @returns An array of all the active implicit sessions. {@link ImplicitSession[]} + * + * @example + * const dappClient = new DappClient('http://localhost:5173'); + * await dappClient.initialize(); + * + * if (dappClient.isInitialized) { + * const implicitSessions = dappClient.getAllImplicitSessions(); + * console.log('Sessions:', implicitSessions); + * } + */ + public getAllImplicitSessions(): ImplicitSession[] { + const allImplicitSessions = new Map() + Array.from(this.chainSessionManagers.values()).forEach((chainSessionManager) => { + const session = chainSessionManager.getImplicitSession() + if (!session) return + const uniqueKey = session?.sessionAddress?.toLowerCase() + if (uniqueKey && !allImplicitSessions.has(uniqueKey)) { + allImplicitSessions.set(uniqueKey, session) + } + }) + return Array.from(allImplicitSessions.values()) + } + + /** + * Gets all the sessions (explicit and implicit) managed by the client. + * @returns An array of session objects. {@link Session[]} + */ + public getAllSessions(): Session[] { + return [...this.getAllImplicitSessions(), ...this.getAllExplicitSessions()] } /** @@ -241,18 +273,42 @@ export class DappClient { private async _loadStateFromStorage(): Promise { const implicitSession = await this.sequenceStorage.getImplicitSession() - const explicitSessions = await this.sequenceStorage.getExplicitSessions() + const [explicitSessions, sessionlessConnection, sessionlessSnapshot] = await Promise.all([ + this.sequenceStorage.getExplicitSessions(), + this.sequenceStorage.getSessionlessConnection(), + this.sequenceStorage.getSessionlessConnectionSnapshot + ? this.sequenceStorage.getSessionlessConnectionSnapshot() + : Promise.resolve(null), + ]) + this.cachedSessionlessConnection = sessionlessSnapshot ?? null const chainIdsToInitialize = new Set([ ...(implicitSession?.chainId !== undefined ? [implicitSession.chainId] : []), ...explicitSessions.map((s) => s.chainId), ]) if (chainIdsToInitialize.size === 0) { - this.isInitialized = false - this.emit('sessionsUpdated') + if (sessionlessConnection) { + await this.applySessionlessConnectionState( + sessionlessConnection.walletAddress, + sessionlessConnection.loginMethod, + sessionlessConnection.userEmail, + sessionlessConnection.guard, + false, + ) + } else { + this.isInitialized = false + this.hasSessionlessConnection = false + this.walletAddress = null + this.loginMethod = null + this.userEmail = null + this.guard = undefined + this.emit('sessionsUpdated') + } return } + this.hasSessionlessConnection = false + const initPromises = Array.from(chainIdsToInitialize).map((chainId) => this.getChainSessionManager(chainId).initialize(), ) @@ -263,6 +319,11 @@ export class DappClient { this.loginMethod = result[0]?.loginMethod || null this.userEmail = result[0]?.userEmail || null this.guard = implicitSession?.guard || explicitSessions.find((s) => !!s.guard)?.guard + await this.sequenceStorage.clearSessionlessConnection() + if (this.sequenceStorage.clearSessionlessConnectionSnapshot) { + await this.sequenceStorage.clearSessionlessConnectionSnapshot() + } + this.cachedSessionlessConnection = null this.isInitialized = true this.emit('sessionsUpdated') @@ -280,8 +341,6 @@ export class DappClient { * * @returns A promise that resolves when initialization is complete. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/initialize} for more detailed documentation. - * * @example * const dappClient = new DappClient('http://localhost:5173'); * await dappClient.initialize(); @@ -318,6 +377,63 @@ export class DappClient { } } + /** + * Indicates if there is cached sessionless connection data that can be restored. + */ + public async hasRestorableSessionlessConnection(): Promise { + if (this.cachedSessionlessConnection) return true + this.cachedSessionlessConnection = this.sequenceStorage.getSessionlessConnectionSnapshot + ? await this.sequenceStorage.getSessionlessConnectionSnapshot() + : null + return this.cachedSessionlessConnection !== null + } + + /** + * Returns the cached sessionless connection metadata without altering client state. + * @returns The cached sessionless connection or null if none is available. + */ + public async getSessionlessConnectionInfo(): Promise { + if (!this.cachedSessionlessConnection) { + this.cachedSessionlessConnection = this.sequenceStorage.getSessionlessConnectionSnapshot + ? await this.sequenceStorage.getSessionlessConnectionSnapshot() + : null + } + if (!this.cachedSessionlessConnection) return null + return { + walletAddress: this.cachedSessionlessConnection.walletAddress, + loginMethod: this.cachedSessionlessConnection.loginMethod, + userEmail: this.cachedSessionlessConnection.userEmail, + guard: this.cachedSessionlessConnection.guard, + } + } + + /** + * Restores a sessionless connection that was previously persisted via {@link disconnect} or a connect flow. + * @returns A promise that resolves to true if a sessionless connection was applied. + */ + public async restoreSessionlessConnection(): Promise { + const sessionlessConnection = + this.cachedSessionlessConnection ?? + (this.sequenceStorage.getSessionlessConnectionSnapshot + ? await this.sequenceStorage.getSessionlessConnectionSnapshot() + : null) + if (!sessionlessConnection) { + return false + } + + await this.applySessionlessConnectionState( + sessionlessConnection.walletAddress, + sessionlessConnection.loginMethod, + sessionlessConnection.userEmail, + sessionlessConnection.guard, + ) + if (this.sequenceStorage.clearSessionlessConnectionSnapshot) { + await this.sequenceStorage.clearSessionlessConnectionSnapshot() + } + this.cachedSessionlessConnection = null + return true + } + /** * Handles the redirect response from the Wallet. * This is called automatically on `initialize()` for web environments but can be called manually @@ -328,7 +444,11 @@ export class DappClient { public async handleRedirectResponse(url?: string): Promise { const pendingRequest = await this.sequenceStorage.peekPendingRequest() - const response = await this.transport.getRedirectResponse(true, url) + if (!this.transport && this.transportMode === TransportMode.POPUP && !this.isBrowser) { + return + } + + const response = await this.ensureTransport().getRedirectResponse(true, url) if (!response) { return } @@ -352,8 +472,59 @@ export class DappClient { } this.emit('walletActionResponse', eventPayload) } else if (chainId !== undefined) { + if ('error' in response && response.error && action === RequestActionType.CREATE_NEW_SESSION) { + await this.sequenceStorage.setPendingRedirectRequest(false) + await this.sequenceStorage.getAndClearTempSessionPk() + await this.sequenceStorage.getAndClearPendingRequest() + + if (this.hasSessionlessConnection) { + const sessionlessConnection = await this.sequenceStorage.getSessionlessConnection() + if (sessionlessConnection) { + await this.applySessionlessConnectionState( + sessionlessConnection.walletAddress, + sessionlessConnection.loginMethod, + sessionlessConnection.userEmail, + sessionlessConnection.guard, + false, + ) + } else if (this.walletAddress) { + await this.applySessionlessConnectionState( + this.walletAddress, + this.loginMethod, + this.userEmail, + this.guard, + false, + ) + } + } + return + } + const chainSessionManager = this.getChainSessionManager(chainId) - await chainSessionManager.handleRedirectResponse(response) + if (!chainSessionManager.isInitialized && this.walletAddress) { + chainSessionManager.initializeWithWallet(this.walletAddress) + } + const handled = await chainSessionManager.handleRedirectResponse(response) + if (handled && action === RequestActionType.CREATE_NEW_SESSION) { + const hasImplicit = !!chainSessionManager.getImplicitSession() + const hasExplicit = chainSessionManager.getExplicitSessions().length > 0 + if (hasImplicit || hasExplicit) { + this.hasSessionlessConnection = false + await this._loadStateFromStorage() + } else if ('payload' in response && response.payload) { + const payload = response.payload as CreateNewSessionResponse + const walletAddress = chainSessionManager.getWalletAddress() ?? Address.from(payload.walletAddress) + await this.applySessionlessConnectionState( + walletAddress, + chainSessionManager.loginMethod, + chainSessionManager.userEmail, + chainSessionManager.getGuard(), + ) + } + } else if (handled && action === RequestActionType.ADD_EXPLICIT_SESSION) { + this.hasSessionlessConnection = false + await this._loadStateFromStorage() + } } else { throw new InitializationError(`Could not find a pending request context for the redirect action: ${action}`) } @@ -362,24 +533,28 @@ export class DappClient { /** * Initiates a connection with the wallet and creates a new session. * @param chainId The primary chain ID for the new session. - * @param permissions (Optional) Permissions to request for an initial explicit session. {@link Signers.Session.ExplicitParams} + * @param sessionConfig Session configuration {@link ExplicitSessionConfig} to request for an initial session. * @param options (Optional) Connection options, such as a preferred login method or email for social or email logins. * @throws If the connection process fails. {@link ConnectionError} * @throws If a session already exists. {@link InitializationError} * * @returns A promise that resolves when the connection is established. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/connect} for more detailed documentation. - * * @example - * const dappClient = new DappClient('http://localhost:5173'); - * await dappClient.connect(137, window.location.origin, undefined, { + * // Connect with an explicit session configuration + * const explicitSessionConfig: ExplicitSessionConfig = { + * valueLimit: 0n, + * deadline: BigInt(Date.now() + 1000 * 60 * 60), // 1 hour + * permissions: [...], + * chainId: 137 + * }; + * await dappClient.connect(137, explicitSessionConfig, { * preferredLoginMethod: 'google', * }); */ async connect( chainId: number, - permissions?: Signers.Session.ExplicitParams, + sessionConfig?: ExplicitSessionConfig, options: { preferredLoginMethod?: LoginMethod email?: string @@ -392,16 +567,114 @@ export class DappClient { try { const chainSessionManager = this.getChainSessionManager(chainId) - await chainSessionManager.createNewSession(this.origin, permissions, options) + const shouldCreateSession = !!sessionConfig || (options.includeImplicitSession ?? false) + this.hasSessionlessConnection = false + await chainSessionManager.createNewSession(this.origin, sessionConfig, options) // For popup mode, we need to manually update the state and emit an event. // For redirect mode, this code won't be reached; the page will navigate away. - if (this.transport.mode === TransportMode.POPUP) { - await this._loadStateFromStorage() + if (this.transportMode === TransportMode.POPUP) { + const hasImplicitSession = !!chainSessionManager.getImplicitSession() + const hasExplicitSessions = chainSessionManager.getExplicitSessions().length > 0 + if (shouldCreateSession && (hasImplicitSession || hasExplicitSessions)) { + await this._loadStateFromStorage() + } else { + const walletAddress = chainSessionManager.getWalletAddress() + if (!walletAddress) { + throw new InitializationError('Wallet address missing after connect.') + } + await this.applySessionlessConnectionState( + walletAddress, + chainSessionManager.loginMethod, + chainSessionManager.userEmail, + chainSessionManager.getGuard(), + ) + } } } catch (err) { await this.disconnect() - throw err + throw new ConnectionError(`Connection failed: ${err}`) + } + } + + /** + * Upgrades an existing sessionless connection by creating implicit and/or explicit sessions. + * @param chainId The chain ID to target for the new sessions. + * @param sessionConfig The explicit session configuration to request. {@link ExplicitSessionConfig} + * @param options Connection options such as preferred login method or email for social/email logins. + * @throws If no sessionless connection is available or the session upgrade fails. {@link InitializationError} + * @throws If neither an implicit nor explicit session is requested. {@link InitializationError} + * + * @returns A promise that resolves once the session upgrade completes. + */ + async upgradeSessionlessConnection( + chainId: number, + sessionConfig?: ExplicitSessionConfig, + options: { + preferredLoginMethod?: LoginMethod + email?: string + includeImplicitSession?: boolean + } = {}, + ): Promise { + if (!this.isInitialized || !this.hasSessionlessConnection || !this.walletAddress) { + throw new InitializationError('A sessionless connection is required before requesting new sessions.') + } + + const shouldCreateSession = !!sessionConfig || (options.includeImplicitSession ?? false) + if (!shouldCreateSession) { + throw new InitializationError( + 'Cannot upgrade a sessionless connection without requesting an implicit or explicit session.', + ) + } + + const sessionlessSnapshot = { + walletAddress: this.walletAddress, + loginMethod: this.loginMethod, + userEmail: this.userEmail, + guard: this.guard, + } + + try { + let chainSessionManager = this.chainSessionManagers.get(chainId) + if ( + chainSessionManager && + chainSessionManager.isInitialized && + !chainSessionManager.getImplicitSession() && + chainSessionManager.getExplicitSessions().length === 0 + ) { + this.chainSessionManagers.delete(chainId) + chainSessionManager = undefined + } + chainSessionManager = chainSessionManager ?? this.getChainSessionManager(chainId) + await chainSessionManager.createNewSession(this.origin, sessionConfig, options) + + if (this.transportMode === TransportMode.POPUP) { + const hasImplicitSession = !!chainSessionManager.getImplicitSession() + const hasExplicitSessions = chainSessionManager.getExplicitSessions().length > 0 + + if (shouldCreateSession && (hasImplicitSession || hasExplicitSessions)) { + await this._loadStateFromStorage() + } else { + const walletAddress = chainSessionManager.getWalletAddress() + if (!walletAddress) { + throw new InitializationError('Wallet address missing after connect.') + } + await this.applySessionlessConnectionState( + walletAddress, + chainSessionManager.loginMethod, + chainSessionManager.userEmail, + chainSessionManager.getGuard(), + ) + } + } + } catch (err) { + await this.applySessionlessConnectionState( + sessionlessSnapshot.walletAddress, + sessionlessSnapshot.loginMethod, + sessionlessSnapshot.userEmail, + sessionlessSnapshot.guard, + ) + throw new ConnectionError(`Connection failed: ${err}`) } } @@ -409,19 +682,16 @@ export class DappClient { * Adds a new explicit session for a given chain to an existing wallet. * @remarks * An `explicit session` is a session that can interact with any contract, subject to user-approved permissions. - * @param chainId The chain ID on which to add the explicit session. - * @param permissions The permissions to request for the new session. {@link Signers.Session.ExplicitParams} + * @param session The explicit session to add. {@link ExplicitSession} * * @throws If the session cannot be added. {@link AddExplicitSessionError} * @throws If the client or relevant chain is not initialized. {@link InitializationError} * * @returns A promise that resolves when the session is added. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/add-explicit-session} for more detailed documentation. - * * @example * ... - * import { Signers, Utils } from "@0xsequence/wallet-core"; + * import { ExplicitSession, Utils } from "@0xsequence/wallet-core"; * import { DappClient } from "@0xsequence/sessions"; * ... * @@ -433,75 +703,61 @@ export class DappClient { * * if (dappClient.isInitialized) { * // Allow Dapp (Session Signer) to transfer "amount" of USDC - * const permissions: Signers.Session.ExplicitParams = { + * const explicitSession: ExplicitSession = { * chainId: Number(chainId), * valueLimit: 0n, // Not allowed to transfer native tokens (ETH, etc) * deadline: BigInt(Date.now() + 1000 * 60 * 5000), // 5000 minutes from now * permissions: [Utils.ERC20PermissionBuilder.buildTransfer(USDC_ADDRESS, amount)] * }; - * await dappClient.addExplicitSession(1, permissions); + * await dappClient.addExplicitSession(explicitSession); * } */ - async addExplicitSession(chainId: number, permissions: Signers.Session.ExplicitParams): Promise { + async addExplicitSession(explicitSessionConfig: ExplicitSessionConfig): Promise { if (!this.isInitialized || !this.walletAddress) throw new InitializationError('Cannot add an explicit session without an existing wallet.') - const chainSessionManager = this.getChainSessionManager(chainId) + const chainSessionManager = this.getChainSessionManager(explicitSessionConfig.chainId) if (!chainSessionManager.isInitialized) { chainSessionManager.initializeWithWallet(this.walletAddress) } - await chainSessionManager.addExplicitSession(permissions) + await chainSessionManager.addExplicitSession(explicitSessionConfig) - if (this.transport.mode === TransportMode.POPUP) { + if (this.transportMode === TransportMode.POPUP) { await this._loadStateFromStorage() } } /** - * Modifies the permissions of an existing explicit session for a given chain and session address. - * @param chainId The chain ID on which the explicit session exists. - * @param sessionAddress The address of the explicit session to modify. {@link Address.Address} - * @param permissions The new permissions to set for the session. {@link Signers.Session.ExplicitParams} + * Modifies an explicit session for a given chain + * @param explicitSession The explicit session to modify. {@link ExplicitSession} * * @throws If the client or relevant chain is not initialized. {@link InitializationError} * @throws If something goes wrong while modifying the session. {@link ModifyExplicitSessionError} * * @returns A promise that resolves when the session permissions are updated. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/modify-explicit-session} for more detailed documentation. - * * @example * const dappClient = new DappClient('http://localhost:5173'); * await dappClient.initialize(); * * if (dappClient.isInitialized) { - * // The address of an existing explicit session (Grants the Dapp permission to transfer 100 USDC for the user) - * const sessionAddress = '0x...'; - * // We create a new permission object where we can increase the granted transfer amount limit - * const permissions: Signers.Session.ExplicitParams = { - * chainId: Number(chainId), - * valueLimit: 0n, - * deadline: BigInt(Date.now() + 1000 * 60 * 5000), - * permissions: [Utils.ERC20PermissionBuilder.buildTransfer(USDC_ADDRESS, amount)] - * }; - * await dappClient.modifyExplicitSession(1, sessionAddress, permissions); + * // Increase the deadline of the current session by 24 hours + * const currentExplicitSession = {...} + * const newExplicitSession = {...currentExplicitSession, deadline: currentExplicitSession.deadline + 24 * 60 * 60} + * await dappClient.modifyExplicitSession(newExplicitSession); * } */ - async modifyExplicitSession( - chainId: number, - sessionAddress: Address.Address, - permissions: Signers.Session.ExplicitParams, - ): Promise { + async modifyExplicitSession(explicitSession: ExplicitSession): Promise { if (!this.isInitialized || !this.walletAddress) throw new InitializationError('Cannot modify an explicit session without an existing wallet.') - const chainSessionManager = this.getChainSessionManager(chainId) + const chainSessionManager = this.getChainSessionManager(explicitSession.chainId) if (!chainSessionManager.isInitialized) { chainSessionManager.initializeWithWallet(this.walletAddress) } - await chainSessionManager.modifyExplicitSession(sessionAddress, permissions) + await chainSessionManager.modifyExplicitSession(explicitSession) - if (this.transport.mode === TransportMode.POPUP) { + if (this.transportMode === TransportMode.POPUP) { await this._loadStateFromStorage() } } @@ -513,9 +769,7 @@ export class DappClient { * @throws If the fee options cannot be fetched. {@link FeeOptionError} * @throws If the client or relevant chain is not initialized. {@link InitializationError} * - * @returns A promise that resolves with the fee options. {@link Relayer.FeeOption[]} - * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/get-fee-options} for more detailed documentation. + * @returns A promise that resolves with the fee options. {@link FeeOption[]} * * @example * const dappClient = new DappClient('http://localhost:5173'); @@ -535,14 +789,25 @@ export class DappClient { * const txHash = await dappClient.sendTransaction(1, transactions, feeOption); * } */ - async getFeeOptions(chainId: number, transactions: Transaction[]): Promise { - if (!this.isInitialized) throw new InitializationError('Not initialized') - const chainSessionManager = this.getChainSessionManager(chainId) - if (!chainSessionManager.isInitialized) - throw new InitializationError(`ChainSessionManager for chain ${chainId} is not initialized.`) + async getFeeOptions(chainId: number, transactions: Transaction[]): Promise { + const chainSessionManager = await this.getOrInitializeChainManager(chainId) return await chainSessionManager.getFeeOptions(transactions) } + /** + * Fetches fee tokens for a chain. + * @returns A promise that resolves with the fee tokens response. {@link GetFeeTokensResponse} + * @throws If the fee tokens cannot be fetched. {@link InitializationError} + */ + async getFeeTokens(chainId: number): Promise { + const relayer = new Relayer.RpcRelayer( + getRelayerUrl(chainId, this.relayerUrl), + chainId, + getRpcUrl(chainId, this.nodesUrl, this.projectAccessKey), + ) + return await relayer.feeTokens() + } + /** * Checks if the current session has permission to execute a set of transactions on a specific chain. * @param chainId The chain ID on which to check the permissions. @@ -550,25 +815,31 @@ export class DappClient { * @returns A promise that resolves to true if the session has permission, otherwise false. */ async hasPermission(chainId: number, transactions: Transaction[]): Promise { - const chainSessionManager = this.chainSessionManagers.get(chainId) - if (!chainSessionManager || !chainSessionManager.isInitialized) { + if (!this.isInitialized) { + return false + } + try { + const chainSessionManager = await this.getOrInitializeChainManager(chainId) + return await chainSessionManager.hasPermission(transactions) + } catch (error) { + console.warn( + `hasPermission check failed for chain ${chainId}:`, + error instanceof Error ? error.message : String(error), + ) return false } - return await chainSessionManager.hasPermission(transactions) } /** * Signs and sends a transaction using an available session signer. * @param chainId The chain ID on which to send the transaction. * @param transactions An array of transactions to be executed atomically in a single batch. {@link Transaction} - * @param feeOption (Optional) The selected fee option to sponsor the transaction. {@link Relayer.FeeOption} + * @param feeOption (Optional) The selected fee option to sponsor the transaction. {@link FeeOption} * @throws {TransactionError} If the transaction fails to send or confirm. * @throws {InitializationError} If the client or relevant chain is not initialized. * * @returns A promise that resolves with the transaction hash. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/send-transaction} for more detailed documentation. - * * @example * const dappClient = new DappClient('http://localhost:5173'); * await dappClient.initialize(); @@ -582,11 +853,8 @@ export class DappClient { * * const txHash = await dappClient.sendTransaction(1, [transaction]); */ - async sendTransaction(chainId: number, transactions: Transaction[], feeOption?: Relayer.FeeOption): Promise { - if (!this.isInitialized) throw new InitializationError('Not initialized') - const chainSessionManager = this.getChainSessionManager(chainId) - if (!chainSessionManager.isInitialized) - throw new InitializationError(`ChainSessionManager for chain ${chainId} is not initialized.`) + async sendTransaction(chainId: number, transactions: Transaction[], feeOption?: FeeOption): Promise { + const chainSessionManager = await this.getOrInitializeChainManager(chainId) return await chainSessionManager.buildSignAndSendTransactions(transactions, feeOption) } @@ -599,8 +867,6 @@ export class DappClient { * * @returns A promise that resolves when the signing process is initiated. The signature is delivered via the `walletActionResponse` event listener. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/sign-message} for more detailed documentation. - * * @example * const dappClient = new DappClient('http://localhost:5173'); * await dappClient.initialize(); @@ -633,8 +899,6 @@ export class DappClient { * * @returns A promise that resolves when the signing process is initiated. The signature is returned in the `walletActionResponse` event listener. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/sign-typed-data} for more detailed documentation. - * * @example * const dappClient = new DappClient('http://localhost:5173'); * await dappClient.initialize(); @@ -686,36 +950,59 @@ export class DappClient { /** * Disconnects the client, clearing all session data from browser storage. * @remarks This action does not revoke the sessions on-chain. Sessions remain active until they expire or are manually revoked by the user in their wallet. + * @param options Options to control the disconnection behavior. + * @param options.keepSessionlessConnection When true, retains the latest wallet metadata so it can be restored later as a sessionless connection. Defaults to true. * @returns A promise that resolves when disconnection is complete. * - * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/disconnect} for more detailed documentation. - * * @example * const dappClient = new DappClient('http://localhost:5173'); * await dappClient.initialize(); * * if (dappClient.isInitialized) { - * await dappClient.disconnect(); + * await dappClient.disconnect({ keepSessionlessConnection: true }); * } */ - async disconnect(): Promise { - const transportMode = this.transport.mode - - this.transport.destroy() - this.transport = new DappTransport( - this.walletUrl, - transportMode, - undefined, - this.sequenceSessionStorage, - this.redirectActionHandler, - ) + async disconnect(options?: { keepSessionlessConnection?: boolean }): Promise { + const keepSessionlessConnection = options?.keepSessionlessConnection ?? true + + const transportMode = this.transportMode + + if (this.transport) { + this.transport.destroy() + } + this.transport = null this.chainSessionManagers.clear() + const sessionlessSnapshot = + keepSessionlessConnection && this.walletAddress + ? { + walletAddress: this.walletAddress, + loginMethod: this.loginMethod ?? undefined, + userEmail: this.userEmail ?? undefined, + guard: this.guard, + } + : undefined + await this.sequenceStorage.clearAllData() + + if (sessionlessSnapshot) { + if (this.sequenceStorage.saveSessionlessConnectionSnapshot) { + await this.sequenceStorage.saveSessionlessConnectionSnapshot(sessionlessSnapshot) + } + this.cachedSessionlessConnection = sessionlessSnapshot + } else { + if (this.sequenceStorage.clearSessionlessConnectionSnapshot) { + await this.sequenceStorage.clearSessionlessConnectionSnapshot() + } + this.cachedSessionlessConnection = null + } + this.isInitialized = false this.walletAddress = null this.loginMethod = null this.userEmail = null + this.guard = undefined + this.hasSessionlessConnection = false this.emit('sessionsUpdated') } @@ -731,6 +1018,47 @@ export class DappClient { } } + private ensureTransport(): DappTransport { + if (!this.transport) { + if (this.transportModeSetting === TransportMode.POPUP && !this.isBrowser) { + throw new InitializationError('Popup transport requires a browser environment.') + } + this.transport = new DappTransport( + this.walletUrl, + this.transportModeSetting, + undefined, + this.sequenceSessionStorage, + this.redirectActionHandler, + ) + } + return this.transport + } + + private async applySessionlessConnectionState( + walletAddress: Address.Address, + loginMethod?: LoginMethod | null, + userEmail?: string | null, + guard?: GuardConfig, + persist: boolean = true, + ): Promise { + this.walletAddress = walletAddress + this.loginMethod = loginMethod ?? null + this.userEmail = userEmail ?? null + this.guard = guard + this.hasSessionlessConnection = true + this.isInitialized = true + this.cachedSessionlessConnection = null + this.emit('sessionsUpdated') + if (persist) { + await this.sequenceStorage.saveSessionlessConnection({ + walletAddress, + loginMethod: this.loginMethod ?? undefined, + userEmail: this.userEmail ?? undefined, + guard: this.guard, + }) + } + } + private async _requestWalletAction( action: (typeof RequestActionType)['SIGN_MESSAGE' | 'SIGN_TYPED_DATA' | 'SEND_WALLET_TRANSACTION'], payload: SignMessagePayload | SignTypedDataPayload | SendWalletTransactionPayload, @@ -743,17 +1071,18 @@ export class DappClient { try { const redirectUrl = this.origin + (this.redirectPath ? this.redirectPath : '') const path = action === RequestActionType.SEND_WALLET_TRANSACTION ? '/request/transaction' : '/request/sign' + const transport = this.ensureTransport() - if (this.transport.mode === TransportMode.REDIRECT) { + if (transport.mode === TransportMode.REDIRECT) { await this.sequenceStorage.savePendingRequest({ action, payload, chainId: chainId, }) await this.sequenceStorage.setPendingRedirectRequest(true) - await this.transport.sendRequest(action, redirectUrl, payload, { path }) + await transport.sendRequest(action, redirectUrl, payload, { path }) } else { - const response = await this.transport.sendRequest(action, redirectUrl, payload, { + const response = await transport.sendRequest(action, redirectUrl, payload, { path, }) this.emit('walletActionResponse', { action, response, chainId }) @@ -763,12 +1092,34 @@ export class DappClient { this.emit('walletActionResponse', { action, error, chainId }) throw error } finally { - if (this.transport.mode === TransportMode.POPUP) { + if (this.transportMode === TransportMode.POPUP && this.transport) { this.transport.closeWallet() } } } + /** + * @private Retrieves or creates and initializes a ChainSessionManager for a given chain ID. + * @param chainId The chain ID to get the ChainSessionManager for. + * @returns The initialized ChainSessionManager for the given chain ID. + */ + private async getOrInitializeChainManager(chainId: number): Promise { + if (!this.isInitialized || !this.walletAddress) { + throw new InitializationError('DappClient is not initialized.') + } + const manager = this.getChainSessionManager(chainId) + if (!manager.isInitialized) { + await manager.initialize() + } + if (!manager.isInitialized) { + throw new InitializationError(`ChainSessionManager for chain ${chainId} could not be initialized.`) + } + if (!manager.getImplicitSession() && manager.getExplicitSessions().length === 0) { + throw new InitializationError('No sessions are available for the requested action.') + } + return manager + } + /** * @private Retrieves or creates a ChainSessionManager for a given chain ID. * @param chainId The chain ID to get the ChainSessionManager for. @@ -777,9 +1128,10 @@ export class DappClient { private getChainSessionManager(chainId: number): ChainSessionManager { let chainSessionManager = this.chainSessionManagers.get(chainId) if (!chainSessionManager) { + const transport = this.ensureTransport() chainSessionManager = new ChainSessionManager( chainId, - this.transport, + transport, this.projectAccessKey, this.keymachineUrl, this.nodesUrl, diff --git a/packages/wallet/dapp-client/src/DappTransport.ts b/packages/wallet/dapp-client/src/DappTransport.ts index a01494695b..463c2874ad 100644 --- a/packages/wallet/dapp-client/src/DappTransport.ts +++ b/packages/wallet/dapp-client/src/DappTransport.ts @@ -12,6 +12,28 @@ import { WalletSize, } from './types/index.js' +const isBrowserEnvironment = typeof window !== 'undefined' && typeof document !== 'undefined' + +const base64Encode = (value: string) => { + if (typeof btoa !== 'undefined') { + return btoa(value) + } + if (typeof Buffer !== 'undefined') { + return Buffer.from(value, 'utf-8').toString('base64') + } + throw new Error('Base64 encoding is not supported in this environment.') +} + +const base64Decode = (value: string) => { + if (typeof atob !== 'undefined') { + return atob(value) + } + if (typeof Buffer !== 'undefined') { + return Buffer.from(value, 'base64').toString('utf-8') + } + throw new Error('Base64 decoding is not supported in this environment.') +} + enum ConnectionState { DISCONNECTED = 'DISCONNECTED', CONNECTING = 'CONNECTING', @@ -36,6 +58,7 @@ export class DappTransport { private readonly handshakeTimeoutMs: number private readonly sequenceSessionStorage: SequenceSessionStorage private readonly redirectActionHandler?: (url: string) => void + private readonly isBrowser: boolean public readonly walletOrigin: string @@ -46,6 +69,7 @@ export class DappTransport { sequenceSessionStorage?: SequenceSessionStorage, redirectActionHandler?: (url: string) => void, ) { + this.isBrowser = isBrowserEnvironment try { this.walletOrigin = new URL(walletUrl).origin } catch (e) { @@ -57,18 +81,26 @@ export class DappTransport { throw new Error('Invalid wallet origin derived from walletUrl.') } - if (sequenceSessionStorage) { - this.sequenceSessionStorage = sequenceSessionStorage - } else if (typeof window !== 'undefined' && window.sessionStorage) { - this.sequenceSessionStorage = window.sessionStorage - } else { - throw new Error('A storage implementation must be provided for non-browser environments.') - } + this.sequenceSessionStorage = + sequenceSessionStorage || + ({ + getItem: (key: string) => (this.isBrowser && window.sessionStorage ? window.sessionStorage.getItem(key) : null), + setItem: (key: string, value: string) => { + if (this.isBrowser && window.sessionStorage) { + window.sessionStorage.setItem(key, value) + } + }, + removeItem: (key: string) => { + if (this.isBrowser && window.sessionStorage) { + window.sessionStorage.removeItem(key) + } + }, + } satisfies SequenceSessionStorage) this.requestTimeoutMs = popupModeOptions.requestTimeoutMs ?? 300000 this.handshakeTimeoutMs = popupModeOptions.handshakeTimeoutMs ?? 15000 - if (this.mode === TransportMode.POPUP) { + if (this.mode === TransportMode.POPUP && this.isBrowser) { window.addEventListener('message', this.handleMessage) } @@ -91,13 +123,23 @@ export class DappTransport { payload?: TRequest, options: SendRequestOptions = {}, ): Promise { + if (!this.isBrowser && this.mode === TransportMode.POPUP) { + throw new Error( + 'Popup transport requires a browser environment. Use redirect mode or provide a redirect handler.', + ) + } + if (this.mode === TransportMode.REDIRECT) { const url = await this.getRequestRedirectUrl(action, payload, redirectUrl, options.path) if (this.redirectActionHandler) { this.redirectActionHandler(url) - } else { + } else if (this.isBrowser) { console.info('[DappTransport] No redirectActionHandler provided. Using window.location.href to navigate.') window.location.href = url + } else { + throw new Error( + 'Redirect navigation is not possible outside the browser without a redirectActionHandler. Provide a handler to perform navigation.', + ) } return new Promise(() => {}) } @@ -148,7 +190,7 @@ export class DappTransport { throw new Error('Could not save redirect state to storage. Redirect flow is unavailable.') } - const serializedPayload = btoa(JSON.stringify(payload || {}, jsonReplacers)) + const serializedPayload = base64Encode(JSON.stringify(payload || {}, jsonReplacers)) const fullWalletUrl = path ? `${this.walletUrl}${path}` : this.walletUrl const url = new URL(fullWalletUrl) url.searchParams.set('action', action) @@ -164,7 +206,12 @@ export class DappTransport { cleanState: boolean = true, url?: string, ): Promise<{ payload: TResponse; action: string } | { error: any; action: string } | null> { - const params = new URLSearchParams(url ? new URL(url).search : window.location.search) + if (!url && !this.isBrowser) { + throw new Error('A URL must be provided when handling redirect responses outside of a browser environment.') + } + + const search = url ? new URL(url).search : this.isBrowser ? window.location.search : '' + const params = new URLSearchParams(search) const responseId = params.get('id') if (!responseId) return null @@ -191,11 +238,9 @@ export class DappTransport { const responsePayloadB64 = params.get('payload') const responseErrorB64 = params.get('error') - const isBrowser = typeof window !== 'undefined' && window.history - if (cleanState) { await this.sequenceSessionStorage.removeItem(REDIRECT_REQUEST_KEY) - if (isBrowser && !url) { + if (this.isBrowser && !url && window.history) { const cleanUrl = new URL(window.location.href) ;['id', 'payload', 'error', 'mode'].forEach((p) => cleanUrl.searchParams.delete(p)) history.replaceState({}, document.title, cleanUrl.toString()) @@ -205,7 +250,7 @@ export class DappTransport { if (responseErrorB64) { try { return { - error: JSON.parse(atob(responseErrorB64), jsonRevivers), + error: JSON.parse(base64Decode(responseErrorB64), jsonRevivers), action: originalRequest.action, } } catch (e) { @@ -219,7 +264,7 @@ export class DappTransport { if (responsePayloadB64) { try { return { - payload: JSON.parse(atob(responsePayloadB64), jsonRevivers), + payload: JSON.parse(base64Decode(responsePayloadB64), jsonRevivers), action: originalRequest.action, } } catch (e) { @@ -240,6 +285,9 @@ export class DappTransport { if (this.mode === TransportMode.REDIRECT) { throw new Error("`openWallet` is not available in 'redirect' mode.") } + if (!this.isBrowser) { + throw new Error('Popup transport requires a browser environment.') + } if (this.connectionState !== ConnectionState.DISCONNECTED) { if (this.isWalletOpen) this.walletWindow?.focus() return this.readyPromise || Promise.resolve() @@ -314,12 +362,14 @@ export class DappTransport { } destroy(): void { - if (this.mode === TransportMode.POPUP) { + if (this.mode === TransportMode.POPUP && this.isBrowser) { window.removeEventListener('message', this.handleMessage) if (this.isWalletOpen) { this.walletWindow?.close() } this._resetConnection(new Error('Transport destroyed.'), 'Destroying transport...') + } else { + this._resetConnection(new Error('Transport destroyed.'), 'Destroying transport...') } } @@ -494,7 +544,7 @@ export class DappTransport { const requestsToClear = new Map(this.pendingRequests) this.pendingRequests.clear() requestsToClear.forEach((pending) => { - window.clearTimeout(pending.timer) + clearTimeout(pending.timer) const errorToSend = reason instanceof Error ? reason : new Error(`Operation failed: ${reason}`) pending.reject(errorToSend) }) @@ -503,11 +553,11 @@ export class DappTransport { private clearTimeouts(): void { if (this.handshakeTimeoutId !== undefined) { - window.clearTimeout(this.handshakeTimeoutId) + clearTimeout(this.handshakeTimeoutId) this.handshakeTimeoutId = undefined } if (this.closeCheckIntervalId !== undefined) { - window.clearInterval(this.closeCheckIntervalId) + clearInterval(this.closeCheckIntervalId) this.closeCheckIntervalId = undefined } } diff --git a/packages/wallet/dapp-client/src/index.ts b/packages/wallet/dapp-client/src/index.ts index c920a0ac5b..d16662c372 100644 --- a/packages/wallet/dapp-client/src/index.ts +++ b/packages/wallet/dapp-client/src/index.ts @@ -4,25 +4,25 @@ export type { LoginMethod, GuardConfig, Transaction, - SignatureSuccessResponse, - ChainSessionManagerEvent, + SignatureResponse, SequenceSessionStorage, RandomPrivateKeyFn, - Session, SignMessagePayload, + SessionResponse, AddExplicitSessionPayload, - AddExplicitSessionSuccessResponsePayload, CreateNewSessionPayload, + CreateNewSessionResponse, SignTypedDataPayload, - ConnectSuccessResponsePayload, - ModifySessionSuccessResponsePayload, - ModifySessionPayload, + ModifyExplicitSessionPayload, DappClientWalletActionEventListener, DappClientExplicitSessionEventListener, TransactionRequest, SendWalletTransactionPayload, - SendWalletTransactionSuccessResponse, + SendWalletTransactionResponse, WalletActionResponse, + GetFeeTokensResponse, + FeeToken, + FeeOption, } from './types/index.js' export { RequestActionType, TransportMode } from './types/index.js' export { @@ -39,10 +39,12 @@ export type { SequenceStorage, ExplicitSessionData, ImplicitSessionData, + SessionlessConnectionData, PendingRequestContext, PendingPayload, } from './utils/storage.js' export { WebStorage } from './utils/storage.js' -export { Permission, Extensions, SessionConfig } from '@0xsequence/wallet-primitives' -export { Signers, Wallet, Utils, Relayer } from '@0xsequence/wallet-core' +export { Attestation, Permission, Extensions, SessionConfig, Constants, Payload } from '@0xsequence/wallet-primitives' +export type { ExplicitSessionConfig, ExplicitSession, ImplicitSession, Session } from '@0xsequence/wallet-core' +export { Signers, Wallet, Utils, Envelope, State } from '@0xsequence/wallet-core' diff --git a/packages/wallet/dapp-client/src/types/index.ts b/packages/wallet/dapp-client/src/types/index.ts index ad251cab49..72e3dbe116 100644 --- a/packages/wallet/dapp-client/src/types/index.ts +++ b/packages/wallet/dapp-client/src/types/index.ts @@ -1,11 +1,17 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { Relayer } from '@0xsequence/relayer' +import { ExplicitSession } from '@0xsequence/wallet-core' import { Attestation, Payload } from '@0xsequence/wallet-primitives' -import { Signers } from '@0xsequence/wallet-core' import { Address, Hex } from 'ox' import type { TypedData } from 'ox/TypedData' // --- Public Interfaces and Constants --- +export type FeeToken = Relayer.FeeToken +export type FeeOption = Relayer.FeeOption +export type OperationFailedStatus = Relayer.OperationFailedStatus +export type OperationStatus = Relayer.OperationStatus + export const RequestActionType = { CREATE_NEW_SESSION: 'createNewSession', ADD_EXPLICIT_SESSION: 'addExplicitSession', @@ -25,25 +31,22 @@ export interface GuardConfig { // --- Payloads for Transport --- export interface CreateNewSessionPayload { - sessionAddress: Address.Address - origin: string - permissions?: Signers.Session.ExplicitParams + origin?: string + session?: ExplicitSession includeImplicitSession?: boolean preferredLoginMethod?: LoginMethod email?: string } export interface AddExplicitSessionPayload { - sessionAddress: Address.Address - permissions: Signers.Session.ExplicitParams + session: ExplicitSession preferredLoginMethod?: LoginMethod email?: string } -export interface ModifySessionPayload { +export interface ModifyExplicitSessionPayload { walletAddress: Address.Address - sessionAddress: Address.Address - permissions: Signers.Session.ExplicitParams + session: ExplicitSession } export interface SignMessagePayload { @@ -58,6 +61,12 @@ export interface SignTypedDataPayload { chainId: number } +export interface SendWalletTransactionPayload { + address: Address.Address + transactionRequest: TransactionRequest + chainId: number +} + export type TransactionRequest = { to: Address.Address value?: bigint @@ -65,13 +74,7 @@ export type TransactionRequest = { gasLimit?: bigint } -export interface SendWalletTransactionPayload { - address: Address.Address - transactionRequest: TransactionRequest - chainId: number -} - -export interface ConnectSuccessResponsePayload { +export interface CreateNewSessionResponse { walletAddress: string attestation?: Attestation.Attestation signature?: Hex.Hex @@ -80,28 +83,23 @@ export interface ConnectSuccessResponsePayload { guard?: GuardConfig } -export interface AddExplicitSessionSuccessResponsePayload { +export interface SignatureResponse { + signature: Hex.Hex walletAddress: string - sessionAddress: string } -export interface ModifySessionSuccessResponsePayload { +export interface SendWalletTransactionResponse { + transactionHash: Hex.Hex walletAddress: string - sessionAddress: string } -export interface SignatureSuccessResponse { - signature: Hex.Hex - walletAddress: string -} +export type WalletActionResponse = SignatureResponse | SendWalletTransactionResponse -export interface SendWalletTransactionSuccessResponse { - transactionHash: Hex.Hex +export interface SessionResponse { walletAddress: string + sessionAddress: string } -export type WalletActionResponse = SignatureSuccessResponse | SendWalletTransactionSuccessResponse - // --- Dapp-facing Types --- export type RandomPrivateKeyFn = () => Hex.Hex | Promise @@ -114,20 +112,11 @@ export type Transaction = // All other properties from Payload.Call, but optional Partial> -export type Session = { - address: Address.Address - isImplicit: boolean - permissions?: Signers.Session.ExplicitParams - chainId?: number -} - // --- Event Types --- -export type ChainSessionManagerEvent = 'sessionsUpdated' | 'explicitSessionResponse' - export type ExplicitSessionEventListener = (data: { action: (typeof RequestActionType)['ADD_EXPLICIT_SESSION' | 'MODIFY_EXPLICIT_SESSION'] - response?: AddExplicitSessionSuccessResponsePayload | ModifySessionSuccessResponsePayload + response?: SessionResponse error?: any }) => void @@ -143,7 +132,7 @@ export type DappClientWalletActionEventListener = (data: { export type DappClientExplicitSessionEventListener = (data: { action: (typeof RequestActionType)['ADD_EXPLICIT_SESSION' | 'MODIFY_EXPLICIT_SESSION'] - response?: AddExplicitSessionSuccessResponsePayload | ModifySessionSuccessResponsePayload + response?: SessionResponse error?: any chainId: number }) => void @@ -182,24 +171,6 @@ export interface TransportMessage { error?: any } -export interface BaseRequest { - type: string -} - -export interface MessageSignatureRequest extends BaseRequest { - type: 'message_signature' - message: string - address: Address.Address - chainId: number -} - -export interface TypedDataSignatureRequest extends BaseRequest { - type: 'typed_data_signature' - typedData: unknown - address: Address.Address - chainId: number -} - export const WalletSize = { width: 380, height: 600, @@ -211,8 +182,13 @@ export interface PendingRequest { timer: number action: string } - export interface SendRequestOptions { timeout?: number path?: string } + +export type GetFeeTokensResponse = { + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address +} diff --git a/packages/wallet/dapp-client/src/utils/constants.ts b/packages/wallet/dapp-client/src/utils/constants.ts index c1eec3ceff..7d382d41c3 100644 --- a/packages/wallet/dapp-client/src/utils/constants.ts +++ b/packages/wallet/dapp-client/src/utils/constants.ts @@ -1,5 +1,5 @@ export const CACHE_DB_NAME = 'sequence-cache' export const NODES_URL = 'https://nodes.sequence.app/{network}' -export const RELAYER_URL = 'https://dev-{network}-relayer.sequence.app' -export const KEYMACHINE_URL = 'https://v3-keymachine.sequence-dev.app' +export const RELAYER_URL = 'https://{network}-relayer.sequence.app' +export const KEYMACHINE_URL = 'https://keymachine.sequence.app' export const VALUE_FORWARDER_ADDRESS = '0xABAAd93EeE2a569cF0632f39B10A9f5D734777ca' diff --git a/packages/wallet/dapp-client/src/utils/storage.ts b/packages/wallet/dapp-client/src/utils/storage.ts index a554f86fe6..8928d354e5 100644 --- a/packages/wallet/dapp-client/src/utils/storage.ts +++ b/packages/wallet/dapp-client/src/utils/storage.ts @@ -1,17 +1,22 @@ -import { Attestation } from '@0xsequence/wallet-primitives' import { Address, Hex } from 'ox' import { jsonReplacers, jsonRevivers } from './index.js' import { - AddExplicitSessionPayload, - CreateNewSessionPayload, - ModifySessionPayload, LoginMethod, SignMessagePayload, SignTypedDataPayload, GuardConfig, SendWalletTransactionPayload, + ModifyExplicitSessionPayload, + CreateNewSessionPayload, + AddExplicitSessionPayload, } from '../types/index.js' +import { Attestation } from '../index.js' + +const isBrowser = typeof window !== 'undefined' +const hasSessionStorage = isBrowser && typeof sessionStorage !== 'undefined' +const hasIndexedDb = typeof indexedDB !== 'undefined' + export interface ExplicitSessionData { pk: Hex.Hex walletAddress: Address.Address @@ -32,10 +37,17 @@ export interface ImplicitSessionData { guard?: GuardConfig } +export interface SessionlessConnectionData { + walletAddress: Address.Address + loginMethod?: LoginMethod + userEmail?: string + guard?: GuardConfig +} + export type PendingPayload = | CreateNewSessionPayload | AddExplicitSessionPayload - | ModifySessionPayload + | ModifyExplicitSessionPayload | SignMessagePayload | SignTypedDataPayload | SendWalletTransactionPayload @@ -65,6 +77,14 @@ export interface SequenceStorage { getImplicitSession(): Promise clearImplicitSession(): Promise + saveSessionlessConnection(sessionData: SessionlessConnectionData): Promise + getSessionlessConnection(): Promise + clearSessionlessConnection(): Promise + + saveSessionlessConnectionSnapshot?(sessionData: SessionlessConnectionData): Promise + getSessionlessConnectionSnapshot?(): Promise + clearSessionlessConnectionSnapshot?(): Promise + clearAllData(): Promise } @@ -73,13 +93,20 @@ const DB_VERSION = 1 const STORE_NAME = 'userKeys' const IMPLICIT_SESSIONS_IDB_KEY = 'SequenceImplicitSession' const EXPLICIT_SESSIONS_IDB_KEY = 'SequenceExplicitSession' +const SESSIONLESS_CONNECTION_IDB_KEY = 'SequenceSessionlessConnection' +const SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY = 'SequenceSessionlessConnectionSnapshot' const PENDING_REDIRECT_REQUEST_KEY = 'SequencePendingRedirect' const TEMP_SESSION_PK_KEY = 'SequencePendingTempSessionPk' const PENDING_REQUEST_CONTEXT_KEY = 'SequencePendingRequestContext' export class WebStorage implements SequenceStorage { + private inMemoryDb = new Map() + private openDB(): Promise { + if (!hasIndexedDb) { + return Promise.reject(new Error('IndexedDB is not available in this environment.')) + } return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION) request.onerror = (event) => reject(`IndexedDB error: ${(event.target as IDBRequest).error}`) @@ -94,6 +121,9 @@ export class WebStorage implements SequenceStorage { } private async getIDBItem(key: IDBValidKey): Promise { + if (!hasIndexedDb) { + return this.inMemoryDb.get(key) as T | undefined + } const db = await this.openDB() return new Promise((resolve, reject) => { const request = db.transaction(STORE_NAME, 'readonly').objectStore(STORE_NAME).get(key) @@ -103,6 +133,10 @@ export class WebStorage implements SequenceStorage { } private async setIDBItem(key: IDBValidKey, value: unknown): Promise { + if (!hasIndexedDb) { + this.inMemoryDb.set(key, value) + return + } const db = await this.openDB() return new Promise((resolve, reject) => { const request = db.transaction(STORE_NAME, 'readwrite').objectStore(STORE_NAME).put(value, key) @@ -112,6 +146,10 @@ export class WebStorage implements SequenceStorage { } private async deleteIDBItem(key: IDBValidKey): Promise { + if (!hasIndexedDb) { + this.inMemoryDb.delete(key) + return + } const db = await this.openDB() return new Promise((resolve, reject) => { const request = db.transaction(STORE_NAME, 'readwrite').objectStore(STORE_NAME).delete(key) @@ -122,11 +160,9 @@ export class WebStorage implements SequenceStorage { async setPendingRedirectRequest(isPending: boolean): Promise { try { - if (isPending) { - sessionStorage.setItem(PENDING_REDIRECT_REQUEST_KEY, 'true') - } else { - sessionStorage.removeItem(PENDING_REDIRECT_REQUEST_KEY) - } + if (!hasSessionStorage) return + if (isPending) sessionStorage.setItem(PENDING_REDIRECT_REQUEST_KEY, 'true') + else sessionStorage.removeItem(PENDING_REDIRECT_REQUEST_KEY) } catch (error) { console.error('Failed to set pending redirect flag:', error) } @@ -134,6 +170,7 @@ export class WebStorage implements SequenceStorage { async isRedirectRequestPending(): Promise { try { + if (!hasSessionStorage) return false return sessionStorage.getItem(PENDING_REDIRECT_REQUEST_KEY) === 'true' } catch (error) { console.error('Failed to check pending redirect flag:', error) @@ -143,6 +180,7 @@ export class WebStorage implements SequenceStorage { async saveTempSessionPk(pk: Hex.Hex): Promise { try { + if (!hasSessionStorage) return sessionStorage.setItem(TEMP_SESSION_PK_KEY, pk) } catch (error) { console.error('Failed to save temp session PK:', error) @@ -151,6 +189,7 @@ export class WebStorage implements SequenceStorage { async getAndClearTempSessionPk(): Promise { try { + if (!hasSessionStorage) return null const pk = sessionStorage.getItem(TEMP_SESSION_PK_KEY) sessionStorage.removeItem(TEMP_SESSION_PK_KEY) return pk as Hex.Hex | null @@ -162,6 +201,7 @@ export class WebStorage implements SequenceStorage { async savePendingRequest(context: PendingRequestContext): Promise { try { + if (!hasSessionStorage) return sessionStorage.setItem(PENDING_REQUEST_CONTEXT_KEY, JSON.stringify(context, jsonReplacers)) } catch (error) { console.error('Failed to save pending request context:', error) @@ -170,6 +210,7 @@ export class WebStorage implements SequenceStorage { async getAndClearPendingRequest(): Promise { try { + if (!hasSessionStorage) return null const context = sessionStorage.getItem(PENDING_REQUEST_CONTEXT_KEY) if (!context) return null sessionStorage.removeItem(PENDING_REQUEST_CONTEXT_KEY) @@ -182,6 +223,7 @@ export class WebStorage implements SequenceStorage { async peekPendingRequest(): Promise { try { + if (!hasSessionStorage) return null const context = sessionStorage.getItem(PENDING_REQUEST_CONTEXT_KEY) if (!context) return null return JSON.parse(context, jsonRevivers) @@ -254,16 +296,74 @@ export class WebStorage implements SequenceStorage { } } + async saveSessionlessConnection(sessionData: SessionlessConnectionData): Promise { + try { + await this.setIDBItem(SESSIONLESS_CONNECTION_IDB_KEY, sessionData) + } catch (error) { + console.error('Failed to save sessionless connection:', error) + throw error + } + } + + async getSessionlessConnection(): Promise { + try { + return (await this.getIDBItem(SESSIONLESS_CONNECTION_IDB_KEY)) ?? null + } catch (error) { + console.error('Failed to retrieve sessionless connection:', error) + return null + } + } + + async clearSessionlessConnection(): Promise { + try { + await this.deleteIDBItem(SESSIONLESS_CONNECTION_IDB_KEY) + } catch (error) { + console.error('Failed to clear sessionless connection:', error) + throw error + } + } + + async saveSessionlessConnectionSnapshot(sessionData: SessionlessConnectionData): Promise { + try { + await this.setIDBItem(SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY, sessionData) + } catch (error) { + console.error('Failed to save sessionless connection snapshot:', error) + throw error + } + } + + async getSessionlessConnectionSnapshot(): Promise { + try { + return (await this.getIDBItem(SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY)) ?? null + } catch (error) { + console.error('Failed to retrieve sessionless connection snapshot:', error) + return null + } + } + + async clearSessionlessConnectionSnapshot(): Promise { + try { + await this.deleteIDBItem(SESSIONLESS_CONNECTION_SNAPSHOT_IDB_KEY) + } catch (error) { + console.error('Failed to clear sessionless connection snapshot:', error) + throw error + } + } + async clearAllData(): Promise { try { // Clear all session storage items - sessionStorage.removeItem(PENDING_REDIRECT_REQUEST_KEY) - sessionStorage.removeItem(TEMP_SESSION_PK_KEY) - sessionStorage.removeItem(PENDING_REQUEST_CONTEXT_KEY) + if (hasSessionStorage) { + sessionStorage.removeItem(PENDING_REDIRECT_REQUEST_KEY) + sessionStorage.removeItem(TEMP_SESSION_PK_KEY) + sessionStorage.removeItem(PENDING_REQUEST_CONTEXT_KEY) + } // Clear all IndexedDB items await this.clearExplicitSessions() await this.clearImplicitSession() + await this.clearSessionlessConnection() + await this.clearSessionlessConnectionSnapshot() } catch (error) { console.error('Failed to clear all data:', error) throw error diff --git a/packages/wallet/primitives-cli/eslint.config.mjs b/packages/wallet/primitives-cli/eslint.config.mjs index 19170f88ed..cecf89b031 100644 --- a/packages/wallet/primitives-cli/eslint.config.mjs +++ b/packages/wallet/primitives-cli/eslint.config.mjs @@ -1,4 +1,4 @@ -import { config } from "@repo/eslint-config/react-internal"; +import { config as baseConfig } from "@repo/eslint-config/base" /** @type {import("eslint").Linter.Config} */ -export default config; +export default baseConfig diff --git a/packages/wallet/primitives-cli/package.json b/packages/wallet/primitives-cli/package.json index b01f2eb983..0a8c978d41 100644 --- a/packages/wallet/primitives-cli/package.json +++ b/packages/wallet/primitives-cli/package.json @@ -1,6 +1,8 @@ { "name": "@0xsequence/wallet-primitives-cli", "type": "module", + "bin": "./dist/index.js", + "private": true, "scripts": { "build": "tsc", "build:esbuild": "esbuild src/index.ts --bundle --platform=node --target=node16 --outfile=dist/index.js", @@ -19,18 +21,18 @@ } }, "devDependencies": { - "@repo/eslint-config": "workspace:*", + "@repo/eslint-config": "workspace:^", "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "@types/yargs": "^17.0.33", - "concurrently": "^8.2.2", - "esbuild": "^0.25.5", - "nodemon": "^3.1.10", - "typescript": "^5.8.3" + "@types/node": "^25.0.2", + "@types/yargs": "^17.0.35", + "concurrently": "^9.2.1", + "esbuild": "^0.27.1", + "nodemon": "^3.1.11", + "typescript": "^5.9.3" }, "dependencies": { "@0xsequence/wallet-primitives": "workspace:^", - "ox": "^0.7.2", - "yargs": "^17.7.2" + "ox": "^0.9.17", + "yargs": "^18.0.0" } } diff --git a/packages/wallet/primitives-cli/src/subcommands/address.ts b/packages/wallet/primitives-cli/src/subcommands/address.ts index b418f14066..062349efca 100644 --- a/packages/wallet/primitives-cli/src/subcommands/address.ts +++ b/packages/wallet/primitives-cli/src/subcommands/address.ts @@ -45,7 +45,7 @@ const addressCommand: CommandModule = { .option('creationCode', { type: 'string', description: 'Creation code (optional)', - default: Context.Rc3.creationCode, + default: Context.Rc5.creationCode, }) }, async (argv) => { diff --git a/packages/wallet/primitives-cli/src/subcommands/server.ts b/packages/wallet/primitives-cli/src/subcommands/server.ts index 790aeee005..29c5e1118b 100644 --- a/packages/wallet/primitives-cli/src/subcommands/server.ts +++ b/packages/wallet/primitives-cli/src/subcommands/server.ts @@ -137,10 +137,11 @@ const rpcMethods: Record Promise> = { return result }, async session_encodeCallSignatures(params) { - const { sessionTopology, callSignatures, explicitSigners, implicitSigners } = params + const { sessionTopology, callSignatures, explicitSigners, implicitSigners, identitySigner } = params const result = await session.doEncodeSessionCallSignatures( JSON.stringify(sessionTopology), callSignatures.map(JSON.stringify), + identitySigner, explicitSigners, implicitSigners, ) diff --git a/packages/wallet/primitives-cli/src/subcommands/session.ts b/packages/wallet/primitives-cli/src/subcommands/session.ts index 3d343d1abe..2672721c61 100644 --- a/packages/wallet/primitives-cli/src/subcommands/session.ts +++ b/packages/wallet/primitives-cli/src/subcommands/session.ts @@ -19,14 +19,24 @@ export async function doEncodeTopology(sessionTopologyInput: string): Promise { const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) const callSignatures = callSignaturesInput.map((s) => SessionSignature.sessionCallSignatureFromJson(s)) - const encoded = SessionSignature.encodeSessionCallSignatures( + // Use first identity signer if not provided + if (!identitySigner) { + const identitySigners = SessionConfig.getIdentitySigners(sessionTopology) + if (identitySigners.length === 0) { + throw new Error('No identity signers found') + } + identitySigner = identitySigners[0]! + } + const encoded = SessionSignature.encodeSessionSignature( callSignatures, sessionTopology, + identitySigner as `0x${string}`, explicitSigners as `0x${string}`[], implicitSigners as `0x${string}`[], ) @@ -90,6 +100,13 @@ const sessionCommand: CommandModule = { description: 'The call signatures', demandOption: true, }) + .option('identity-signer', { + type: 'string', + description: 'The identity signer', + demandOption: false, + default: undefined, + alias: 'id', + }) .option('explicit-signers', { type: 'string', array: true, @@ -112,6 +129,7 @@ const sessionCommand: CommandModule = { await doEncodeSessionCallSignatures( args.sessionTopology, args.callSignatures, + args.identitySigner, args.explicitSigners, args.implicitSigners, ), diff --git a/packages/wallet/primitives/CHANGELOG.md b/packages/wallet/primitives/CHANGELOG.md new file mode 100644 index 0000000000..c98111672f --- /dev/null +++ b/packages/wallet/primitives/CHANGELOG.md @@ -0,0 +1,37 @@ +# @0xsequence/wallet-primitives + +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 diff --git a/packages/wallet/primitives/eslint.config.mjs b/packages/wallet/primitives/eslint.config.mjs index 19170f88ed..cecf89b031 100644 --- a/packages/wallet/primitives/eslint.config.mjs +++ b/packages/wallet/primitives/eslint.config.mjs @@ -1,4 +1,4 @@ -import { config } from "@repo/eslint-config/react-internal"; +import { config as baseConfig } from "@repo/eslint-config/base" /** @type {import("eslint").Linter.Config} */ -export default config; +export default baseConfig diff --git a/packages/wallet/primitives/package.json b/packages/wallet/primitives/package.json index ead0a78231..08e2b41bae 100644 --- a/packages/wallet/primitives/package.json +++ b/packages/wallet/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-primitives", - "version": "0.0.0", + "version": "3.0.0-beta.6", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -23,11 +23,11 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@vitest/coverage-v8": "^3.2.4", - "typescript": "^5.8.3", - "vitest": "^3.2.1" + "@vitest/coverage-v8": "^4.0.15", + "typescript": "^5.9.3", + "vitest": "^4.0.15" }, "dependencies": { - "ox": "^0.7.2" + "ox": "^0.9.17" } } diff --git a/packages/wallet/primitives/src/config.ts b/packages/wallet/primitives/src/config.ts index c193ce3588..27bb7f0324 100644 --- a/packages/wallet/primitives/src/config.ts +++ b/packages/wallet/primitives/src/config.ts @@ -281,6 +281,16 @@ export function flatLeavesToTopology(leaves: Leaf[]): Topology { ] } +export function topologyToFlatLeaves(topology: Topology): Leaf[] { + if (isNode(topology)) { + return [...topologyToFlatLeaves(topology[0]), ...topologyToFlatLeaves(topology[1])] + } + if (isNestedLeaf(topology)) { + return [...topologyToFlatLeaves(topology.tree)] + } + return [topology] +} + export function configToJson(config: Config): string { return JSON.stringify({ threshold: config.threshold.toString(), @@ -617,3 +627,39 @@ function mergeLeaf(a: Leaf, b: Leaf): Leaf { throw new Error('Topology mismatch: incompatible leaf types') } + +export function replaceAddress( + topology: Topology, + targetAddress: Address.Address, + replacementAddress: Address.Address, +): Topology { + // 1. Handle Branches/Nodes (Recursion) + if (isNode(topology)) { + return [ + replaceAddress(topology[0], targetAddress, replacementAddress), + replaceAddress(topology[1], targetAddress, replacementAddress), + ] + } + + // 2. Handle Nested Leaves (Recursion) + if (isNestedLeaf(topology)) { + return { + ...topology, + tree: replaceAddress(topology.tree, targetAddress, replacementAddress), + } + } + + // 3. Handle Leaves (Replacement) + if (isSignerLeaf(topology) || isSapientSignerLeaf(topology)) { + // If this leaf holds the placeholder address, swap it + if (Address.isEqual(topology.address, targetAddress)) { + return { + ...topology, + address: replacementAddress, + } + } + } + + // 4. Return other leaf types unchanged (Subdigest, NodeLeaf, etc.) + return topology +} diff --git a/packages/wallet/primitives/src/constants.ts b/packages/wallet/primitives/src/constants.ts index 46185a49dd..763f94389e 100644 --- a/packages/wallet/primitives/src/constants.ts +++ b/packages/wallet/primitives/src/constants.ts @@ -1,8 +1,9 @@ import { Abi } from 'ox' export const ZeroAddress = '0x0000000000000000000000000000000000000000' as const +export const PlaceholderAddress = '0xffff0000ffff0000ffff0000ffff0000ffff0000' as const -export const DefaultGuestAddress = '0x0000000000601fcA38f0cCA649453F6739436d6C' as const +export const DefaultGuestAddress = '0x0000000000006Ac72ed1d192fa28f0058D3F8806' as const // ERC1271 export const IS_VALID_SIGNATURE = Abi.from([ diff --git a/packages/wallet/primitives/src/context.ts b/packages/wallet/primitives/src/context.ts index 001b9a5f19..fa70f8e3ac 100644 --- a/packages/wallet/primitives/src/context.ts +++ b/packages/wallet/primitives/src/context.ts @@ -59,6 +59,44 @@ export const Rc3_4337: Context = { }, } +export const Rc4: Context = { + factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', + stage1: '0x0000000000003DF093bc4257E6dCE45D937EF161', + stage2: '0x10bE1Abf3cD0918bb1079ECc6b8220c177F34088', + creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', +} + +export const Rc4_4337: Context = { + factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', + stage1: '0x0000000000003add039FF84b064B7347Fc23C444', + stage2: '0x4B3E5735665057A0A15eE448A7293bC01e3b4De9', + creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', + capabilities: { + erc4337: { + entrypoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', + }, + }, +} + +export const Rc5: Context = { + factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', + stage1: '0x0000000000001f3C39d61698ab21131a12134454', + stage2: '0xD0ae8eF93b7DA4eabb32Ec4d81b7a501DCa04D4C', + creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', +} + +export const Rc5_4337: Context = { + factory: '0x00000000000018A77519fcCCa060c2537c9D6d3F', + stage1: '0x0000000000009caFdeDb6f64Bf5F31a22124B2a8', + stage2: '0xcBca3328a731deffE6Ce4c2fb51b585c3c37FB92', + creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', + capabilities: { + erc4337: { + entrypoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', + }, + }, +} + export type KnownContext = Context & { name: string development: boolean @@ -70,6 +108,10 @@ export const KnownContexts: KnownContext[] = [ { name: 'Dev2_4337', development: true, ...Dev2_4337 }, { name: 'Rc3', development: true, ...Rc3 }, { name: 'Rc3_4337', development: true, ...Rc3_4337 }, + { name: 'Rc4', development: false, ...Rc4 }, + { name: 'Rc4_4337', development: false, ...Rc4_4337 }, + { name: 'Rc5', development: false, ...Rc5 }, + { name: 'Rc5_4337', development: false, ...Rc5_4337 }, ] export function isKnownContext(context: Context): context is KnownContext { diff --git a/packages/wallet/primitives/src/erc-6492.ts b/packages/wallet/primitives/src/erc-6492.ts index a07de019cf..868350edff 100644 --- a/packages/wallet/primitives/src/erc-6492.ts +++ b/packages/wallet/primitives/src/erc-6492.ts @@ -1,5 +1,5 @@ import { AbiFunction, AbiParameters, Address, Bytes, Hex, Provider } from 'ox' -import { WrappedSignature } from 'ox/erc6492' +import { SignatureErc6492 } from 'ox/erc6492' import { DEPLOY } from './constants.js' import { Context } from './context.js' @@ -29,7 +29,7 @@ export function wrap( [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], [to, Hex.from(data), Hex.from(signature)], ), - WrappedSignature.magicBytes, + SignatureErc6492.magicBytes, ) switch (typeof signature) { @@ -46,12 +46,12 @@ export function decode( switch (typeof signature) { case 'object': if ( - Bytes.toHex(signature.subarray(-WrappedSignature.magicBytes.slice(2).length / 2)) === - WrappedSignature.magicBytes + Bytes.toHex(signature.subarray(-SignatureErc6492.magicBytes.slice(2).length / 2)) === + SignatureErc6492.magicBytes ) { const [to, data, decoded] = AbiParameters.decode( [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], - signature.subarray(0, -WrappedSignature.magicBytes.slice(2).length / 2), + signature.subarray(0, -SignatureErc6492.magicBytes.slice(2).length / 2), ) return { signature: Hex.toBytes(decoded) as T, erc6492: { to, data: Hex.toBytes(data) as T } } } else { @@ -59,11 +59,11 @@ export function decode( } case 'string': - if (signature.endsWith(WrappedSignature.magicBytes.slice(2))) { + if (signature.endsWith(SignatureErc6492.magicBytes.slice(2))) { try { const [to, data, decoded] = AbiParameters.decode( [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], - signature.slice(0, -WrappedSignature.magicBytes.slice(2).length) as Hex.Hex, + signature.slice(0, -SignatureErc6492.magicBytes.slice(2).length) as Hex.Hex, ) return { signature: decoded as T, erc6492: { to, data: data as T } } } catch { diff --git a/packages/wallet/primitives/src/extensions/index.ts b/packages/wallet/primitives/src/extensions/index.ts index 3d7582cbc2..2ff8ac16b2 100644 --- a/packages/wallet/primitives/src/extensions/index.ts +++ b/packages/wallet/primitives/src/extensions/index.ts @@ -24,5 +24,17 @@ export const Rc3: Extensions = { sessions: '0x0000000000CC58810c33F3a0D78aA1Ed80FaDcD8', } +export const Rc4: Extensions = { + passkeys: '0x0000000000005204F3711851EAD52CC9c241499a', + recovery: '0x000000000001FC499c3E177DD56Febb0A4bc15b7', + sessions: '0x00000000000030Bcc832F7d657f50D6Be35C92b3', +} + +export const Rc5: Extensions = { + passkeys: '0x0000000000005204F3711851EAD52CC9c241499a', + recovery: '0x000000000000AB36D17eB1150116371520565205', + sessions: '0x00000000000030Bcc832F7d657f50D6Be35C92b3', +} + export * as Passkeys from './passkeys.js' export * as Recovery from './recovery.js' diff --git a/packages/wallet/primitives/src/network.ts b/packages/wallet/primitives/src/network.ts index f63417f9ec..b0ef2bbde1 100644 --- a/packages/wallet/primitives/src/network.ts +++ b/packages/wallet/primitives/src/network.ts @@ -102,14 +102,6 @@ export const ChainId = { IMMUTABLE_ZKEVM: 13371, IMMUTABLE_ZKEVM_TESTNET: 13473, - // The Root Network - ROOT_NETWORK: 7668, - ROOT_NETWORK_PORCINI: 7672, - - // LAOS - LAOS: 6283, - LAOS_SIGMA_TESTNET: 62850, - // ETHERLINK ETHERLINK: 42793, ETHERLINK_TESTNET: 128123, @@ -119,6 +111,7 @@ export const ChainId = { MOONBASE_ALPHA: 1287, // MONAD + MONAD: 143, MONAD_TESTNET: 10143, // SOMNIA @@ -133,6 +126,9 @@ export const ChainId = { // SANDBOX SANDBOX_TESTNET: 6252, + + // ARC + ARC_TESTNET: 5042002, } as const export type ChainId = (typeof ChainId)[keyof typeof ChainId] @@ -751,74 +747,6 @@ export const ALL: Network[] = [ decimals: 18, }, }, - { - chainId: ChainId.ROOT_NETWORK, - type: NetworkType.MAINNET, - name: 'rootnet', - title: 'The Root Network', - rpcUrl: getRpcUrl('rootnet'), - logoUrl: getLogoUrl(ChainId.ROOT_NETWORK), - blockExplorer: { - name: 'The Root Network Explorer', - url: 'https://rootscan.io/', - }, - nativeCurrency: { - symbol: 'XRP', - name: 'XRP', - decimals: 18, - }, - }, - { - chainId: ChainId.ROOT_NETWORK_PORCINI, - type: NetworkType.TESTNET, - name: 'rootnet-porcini', - title: 'The Root Network Porcini Testnet', - rpcUrl: getRpcUrl('rootnet-porcini'), - logoUrl: getLogoUrl(ChainId.ROOT_NETWORK_PORCINI), - blockExplorer: { - name: 'The Root Network Porcini Testnet Explorer', - url: 'https://porcini.rootscan.io/', - }, - nativeCurrency: { - symbol: 'XRP', - name: 'XRP', - decimals: 18, - }, - }, - { - chainId: ChainId.LAOS, - type: NetworkType.MAINNET, - name: 'laos', - title: 'LAOS', - rpcUrl: getRpcUrl('laos'), - logoUrl: getLogoUrl(ChainId.LAOS), - blockExplorer: { - name: 'LAOS Explorer', - url: 'https://blockscout.laos.laosfoundation.io/', - }, - nativeCurrency: { - symbol: 'LAOS', - name: 'LAOS', - decimals: 18, - }, - }, - { - chainId: ChainId.LAOS_SIGMA_TESTNET, - type: NetworkType.TESTNET, - name: 'laos-sigma-testnet', - title: 'LAOS Sigma Testnet', - rpcUrl: getRpcUrl('laos-sigma-testnet'), - logoUrl: getLogoUrl(ChainId.LAOS_SIGMA_TESTNET), - blockExplorer: { - name: 'LAOS Sigma Testnet Explorer', - url: 'https://sigma.explorer.laosnetwork.io/', - }, - nativeCurrency: { - symbol: 'SIGMA', - name: 'SIGMA', - decimals: 18, - }, - }, { chainId: ChainId.MOONBEAM, type: NetworkType.MAINNET, @@ -887,6 +815,23 @@ export const ALL: Network[] = [ decimals: 18, }, }, + { + chainId: ChainId.MONAD, + type: NetworkType.MAINNET, + name: 'monad', + title: 'Monad', + rpcUrl: getRpcUrl('monad'), + logoUrl: getLogoUrl(ChainId.MONAD), + blockExplorer: { + name: 'Monad Explorer', + url: 'https://mainnet-beta.monvision.io/', + }, + nativeCurrency: { + symbol: 'MON', + name: 'MON', + decimals: 18, + }, + }, { chainId: ChainId.MONAD_TESTNET, type: NetworkType.TESTNET, @@ -994,6 +939,24 @@ export const ALL: Network[] = [ decimals: 18, }, }, + + { + chainId: ChainId.ARC_TESTNET, + type: NetworkType.TESTNET, + name: 'arc-testnet', + title: 'Arc Testnet', + rpcUrl: getRpcUrl('arc-testnet'), + logoUrl: getLogoUrl(ChainId.ARC_TESTNET), + blockExplorer: { + name: 'Arc Testnet Explorer', + url: 'https://1jr2dw1zdqvyes8u.blockscout.com/', + }, + nativeCurrency: { + symbol: 'USDC', + name: 'USDC', + decimals: 6, + }, + }, ] function getRpcUrl(networkName: string): string { diff --git a/packages/wallet/primitives/src/payload.ts b/packages/wallet/primitives/src/payload.ts index c8f05ee8d0..1359abdbe9 100644 --- a/packages/wallet/primitives/src/payload.ts +++ b/packages/wallet/primitives/src/payload.ts @@ -184,6 +184,10 @@ export function isCalls4337_07(payload: Payload): payload is Calls4337_07 { return payload.type === 'call_4337_07' } +export function isParented(payload: Payload): payload is Parented { + return 'parentWallets' in payload +} + export function toRecovery(payload: T): Recovery { if (isRecovery(payload)) { return payload diff --git a/packages/wallet/primitives/src/permission.ts b/packages/wallet/primitives/src/permission.ts index 773a176e59..c2909696d8 100644 --- a/packages/wallet/primitives/src/permission.ts +++ b/packages/wallet/primitives/src/permission.ts @@ -25,7 +25,7 @@ export type SessionPermissions = { chainId: number valueLimit: bigint deadline: bigint // uint64 - permissions: [Permission, ...Permission[]] + permissions: Permission[] } export const MAX_PERMISSIONS_COUNT = 2 ** 7 - 1 @@ -127,7 +127,7 @@ export function decodeSessionPermissions(bytes: Bytes.Bytes): SessionPermissions chainId, valueLimit, deadline, - permissions: permissions as [Permission, ...Permission[]], + permissions: permissions, } } diff --git a/packages/wallet/primitives/src/session-config.ts b/packages/wallet/primitives/src/session-config.ts index d65563c048..38ba2056ca 100644 --- a/packages/wallet/primitives/src/session-config.ts +++ b/packages/wallet/primitives/src/session-config.ts @@ -35,8 +35,10 @@ export type SessionLeaf = SessionPermissionsLeaf | ImplicitBlacklistLeaf | Ident export type SessionBranch = [SessionsTopology, SessionsTopology, ...SessionsTopology[]] export type SessionsTopology = SessionBranch | SessionLeaf | SessionNode +const SESSIONS_NODE_SIZE_BYTES = 32 + function isSessionsNode(topology: any): topology is SessionNode { - return Hex.validate(topology) && Hex.size(topology) === 32 + return Hex.validate(topology) && Hex.size(topology) === SESSIONS_NODE_SIZE_BYTES } function isImplicitBlacklist(topology: any): topology is ImplicitBlacklistLeaf { @@ -65,7 +67,8 @@ export function isSessionsTopology(topology: any): topology is SessionsTopology /** * Checks if the topology is complete. - * A complete topology has exactly one identity signer and one blacklist. + * A complete topology has at least one identity signer and one blacklist. + * When performing encoding, exactly one identity signer is required. Others must be hashed into nodes. * @param topology The topology to check * @returns True if the topology is complete */ @@ -74,9 +77,9 @@ export function isCompleteSessionsTopology(topology: any): topology is SessionsT if (!isSessionsTopology(topology)) { return false } - // Check the topology contains exactly one identity signer and one blacklist + // Check the topology contains at least one identity signer and exactly one blacklist const { identitySignerCount, blacklistCount } = checkIsCompleteSessionsBranch(topology) - return identitySignerCount === 1 && blacklistCount === 1 + return identitySignerCount >= 1 && blacklistCount === 1 } function checkIsCompleteSessionsBranch(topology: SessionsTopology): { @@ -102,28 +105,22 @@ function checkIsCompleteSessionsBranch(topology: SessionsTopology): { } /** - * Gets the identity signer from the topology. + * Gets the identity signers from the topology. * @param topology The topology to get the identity signer from - * @returns The identity signer or null if it's not present + * @returns The identity signers */ -export function getIdentitySigner(topology: SessionsTopology): Address.Address | null { +export function getIdentitySigners(topology: SessionsTopology): Address.Address[] { if (isIdentitySignerLeaf(topology)) { - // Got it - return topology.identitySigner + // Got one + return [topology.identitySigner] } if (isSessionsBranch(topology)) { // Check branches - const results = topology.map(getIdentitySigner).filter((t) => t !== null) - if (results.length > 1) { - throw new Error('Multiple identity signers') - } - if (results.length === 1) { - return results[0]! - } + return topology.map(getIdentitySigners).flat() } - return null + return [] } /** @@ -164,7 +161,10 @@ export function getImplicitBlacklistLeaf(topology: SessionsTopology): ImplicitBl return null } -export function getSessionPermissions(topology: SessionsTopology, address: Address.Address): SessionPermissions | null { +export function getSessionPermissions( + topology: SessionsTopology, + address: Address.Address, +): SessionPermissionsLeaf | null { if (isSessionPermissions(topology)) { if (Address.isEqual(topology.signer, address)) { return topology @@ -344,6 +344,90 @@ export function encodeSessionsTopology(topology: SessionsTopology): Bytes.Bytes throw new Error('Invalid topology') } +export function decodeSessionsTopology(bytes: Bytes.Bytes): SessionsTopology { + const { topology } = decodeSessionTopologyPointer(bytes) + return topology +} + +function decodeSessionTopologyPointer(bytes: Bytes.Bytes): { + topology: SessionsTopology + pointer: number +} { + if (bytes.length === 0) { + throw new Error('Empty topology bytes') + } + + const flagByte = bytes[0]! + const flag = (flagByte & 0xf0) >> 4 + const sizeSize = flagByte & 0x0f + + if (flag === SESSIONS_FLAG_BRANCH) { + // Branch + if (sizeSize === 0 || sizeSize > 15) { + throw new Error('Invalid branch size') + } + + let offset = 1 + const encodedLength = Bytes.toNumber(bytes.slice(offset, offset + sizeSize)) + offset += sizeSize + + const encodedBranches = bytes.slice(offset, offset + encodedLength) + const branches: SessionsTopology[] = [] + + let branchOffset = 0 + while (branchOffset < encodedBranches.length) { + const { topology: branchTopology, pointer: branchPointer } = decodeSessionTopologyPointer( + encodedBranches.slice(branchOffset), + ) + branches.push(branchTopology) + branchOffset += branchPointer + } + + return { topology: branches as SessionsTopology, pointer: offset + encodedLength } + } else if (flag === SESSIONS_FLAG_PERMISSIONS) { + // Permissions + const sessionPermissions = decodeSessionPermissions(bytes.slice(1)) + const nodeLength = 1 + encodeSessionPermissions(sessionPermissions).length + return { topology: { type: 'session-permissions', ...sessionPermissions }, pointer: nodeLength } + } else if (flag === SESSIONS_FLAG_NODE) { + // Node + const nodeLength = SESSIONS_NODE_SIZE_BYTES + 1 + if (bytes.length < nodeLength) { + throw new Error('Invalid node length') + } + return { topology: Hex.fromBytes(bytes.slice(1, nodeLength)), pointer: nodeLength } + } else if (flag === SESSIONS_FLAG_BLACKLIST) { + // Blacklist + let offset = 1 + let blacklistLength = sizeSize + if (sizeSize === 0x0f) { + // Size is encoded in the next 2 bytes + blacklistLength = Bytes.toNumber(bytes.slice(offset, offset + 2)) + offset += 2 + } + + const blacklist: Address.Address[] = [] + for (let i = 0; i < blacklistLength; i++) { + const addressBytes = bytes.slice(offset + i * 20, offset + (i + 1) * 20) + blacklist.push(Address.from(Hex.fromBytes(addressBytes))) + } + + return { topology: { type: 'implicit-blacklist', blacklist }, pointer: offset + blacklistLength * 20 } + } else if (flag === SESSIONS_FLAG_IDENTITY_SIGNER) { + // Identity signer + const nodeLength = 21 // Flag + address + if (bytes.length < nodeLength) { + throw new Error('Invalid identity signer length') + } + return { + topology: { type: 'identity-signer', identitySigner: Address.from(Hex.fromBytes(bytes.slice(1, nodeLength))) }, + pointer: nodeLength, + } + } else { + throw new Error(`Invalid topology flag: ${flag}`) + } +} + // JSON export function sessionsTopologyToJson(topology: SessionsTopology): string { @@ -416,28 +500,39 @@ function sessionsTopologyFromParsed(parsed: any): SessionsTopology { // Operations -/** - * Removes all explicit sessions (permissions leaf nodes) that match the given signer from the topology. - * Returns the updated topology or null if it becomes empty (for nesting). - * If the signer is not found, the topology is returned unchanged. - */ -export function removeExplicitSession( - topology: SessionsTopology, - signerAddress: `0x${string}`, -): SessionsTopology | null { - if (isSessionPermissions(topology)) { - if (Address.isEqual(topology.signer, signerAddress)) { +function removeLeaf(topology: SessionsTopology, leaf: SessionLeaf | SessionNode): SessionsTopology | null { + if (isSessionsLeaf(topology) && isSessionsLeaf(leaf)) { + if (topology.type === leaf.type) { + if (isSessionPermissions(topology) && isSessionPermissions(leaf)) { + if (Address.isEqual(topology.signer, leaf.signer)) { + return null + } + } else if (isImplicitBlacklist(topology) && isImplicitBlacklist(leaf)) { + // Remove blacklist items in leaf from topology + const newBlacklist = topology.blacklist.filter((b) => !leaf.blacklist.includes(b)) + if (newBlacklist.length === 0) { + return null + } + return { type: 'implicit-blacklist', blacklist: newBlacklist } + } else if (isIdentitySignerLeaf(topology) && isIdentitySignerLeaf(leaf)) { + // Remove identity signer from topology + if (Address.isEqual(topology.identitySigner, leaf.identitySigner)) { + return null + } + } + } + } else if (isSessionsNode(topology) && isSessionsNode(leaf)) { + if (Hex.isEqual(topology, leaf)) { + // Match, remove the node return null } - // Return the leaf unchanged - return topology } // If it's a branch, recurse on each child: if (isSessionsBranch(topology)) { const newChildren: SessionsTopology[] = [] for (const child of topology) { - const updatedChild = removeExplicitSession(child, signerAddress) + const updatedChild = removeLeaf(child, leaf) if (updatedChild != null) { newChildren.push(updatedChild) } @@ -461,6 +556,29 @@ export function removeExplicitSession( return topology } +/** + * Removes all explicit sessions (permissions leaf nodes) that match the given signer from the topology. + * Returns the updated topology or null if it becomes empty (for nesting). + * If the signer is not found, the topology is returned unchanged. + */ +export function removeExplicitSession( + topology: SessionsTopology, + signerAddress: `0x${string}`, +): SessionsTopology | null { + const explicitLeaf = getSessionPermissions(topology, signerAddress) + if (!explicitLeaf) { + // Not found, return unchanged + return topology + } + const removed = removeLeaf(topology, explicitLeaf) + if (!removed) { + // Empty, return null + return null + } + // Balance it + return balanceSessionsTopology(removed) +} + export function addExplicitSession( topology: SessionsTopology, sessionPermissions: SessionPermissions, @@ -474,6 +592,33 @@ export function addExplicitSession( return balanceSessionsTopology(merged) } +export function removeIdentitySigner( + topology: SessionsTopology, + identitySigner: Address.Address, +): SessionsTopology | null { + const identityLeaf: IdentitySignerLeaf = { + type: 'identity-signer', + identitySigner, + } + // Remove the old identity signer and balance + const removed = removeLeaf(topology, identityLeaf) + if (!removed) { + // Empty, return null + return null + } + return balanceSessionsTopology(removed) +} + +export function addIdentitySigner(topology: SessionsTopology, identitySigner: Address.Address): SessionsTopology { + // Find the session in the topology + if (getIdentitySigners(topology).some((s) => Address.isEqual(s, identitySigner))) { + throw new Error('Identity signer already exists') + } + // Merge and balance + const merged = mergeSessionsTopologies(topology, { type: 'identity-signer', identitySigner }) + return balanceSessionsTopology(merged) +} + /** * Merges two topologies into a new branch of [a, b]. */ @@ -521,17 +666,9 @@ function buildBalancedSessionsTopology(items: (SessionLeaf | SessionNode)[]): Se /** * Balances the topology by flattening and rebuilding as a balanced binary tree. - * This does not make a binary tree as the blacklist and identity signer are included at the top level. */ export function balanceSessionsTopology(topology: SessionsTopology): SessionsTopology { - const flattened = flattenSessionsTopology(topology) - const blacklist = flattened.find((l) => isImplicitBlacklist(l)) - const identitySigner = flattened.find((l) => isIdentitySignerLeaf(l)) - const leaves = flattened.filter((l) => isSessionPermissions(l)) - if (!blacklist || !identitySigner) { - throw new Error('No blacklist or identity signer') - } - return buildBalancedSessionsTopology([blacklist, identitySigner, ...leaves]) + return buildBalancedSessionsTopology(flattenSessionsTopology(topology)) } /** @@ -596,9 +733,10 @@ export function minimiseSessionsTopology( topology: SessionsTopology, explicitSigners: Address.Address[] = [], implicitSigners: Address.Address[] = [], + identitySigner?: Address.Address, ): SessionsTopology { if (isSessionsBranch(topology)) { - const branches = topology.map((b) => minimiseSessionsTopology(b, explicitSigners, implicitSigners)) + const branches = topology.map((b) => minimiseSessionsTopology(b, explicitSigners, implicitSigners, identitySigner)) // If all branches are nodes, the branch can be a node too if (branches.every((b) => isSessionsNode(b))) { return Hash.keccak256(Bytes.concat(...branches.map((b) => Hex.toBytes(b))), { as: 'Hex' }) @@ -621,7 +759,11 @@ export function minimiseSessionsTopology( return topology } if (isIdentitySignerLeaf(topology)) { - // Never roll up the identity signer + if (identitySigner && !Address.isEqual(topology.identitySigner, identitySigner)) { + // Not the identity signer we're looking for, so roll it up + return GenericTree.hash(encodeLeafToGeneric(topology)) + } + // Return this identity signer leaf return topology } if (isSessionsNode(topology)) { @@ -667,15 +809,18 @@ export function removeFromImplicitBlacklist(topology: SessionsTopology, address: /** * Generate an empty sessions topology with the given identity signer. No session permission and an empty blacklist */ -export function emptySessionsTopology(identitySigner: Address.Address): SessionsTopology { - return [ +export function emptySessionsTopology( + identitySigner: Address.Address | [Address.Address, ...Address.Address[]], +): SessionsTopology { + if (!Array.isArray(identitySigner)) { + return emptySessionsTopology([identitySigner]) + } + const flattenedTopology: SessionLeaf[] = [ { type: 'implicit-blacklist', blacklist: [], }, - { - type: 'identity-signer', - identitySigner, - }, + ...identitySigner.map((signer): IdentitySignerLeaf => ({ type: 'identity-signer', identitySigner: signer })), ] + return buildBalancedSessionsTopology(flattenedTopology) } diff --git a/packages/wallet/primitives/src/session-signature.ts b/packages/wallet/primitives/src/session-signature.ts index 8b9306d7fc..c3f67ca241 100644 --- a/packages/wallet/primitives/src/session-signature.ts +++ b/packages/wallet/primitives/src/session-signature.ts @@ -1,18 +1,19 @@ import { Address, Bytes, Hash, Hex } from 'ox' -import { Attestation, encode, encodeForJson, fromParsed, toJson } from './attestation.js' +import { Attestation, Extensions, Payload } from './index.js' import { MAX_PERMISSIONS_COUNT } from './permission.js' import { + decodeSessionsTopology, encodeSessionsTopology, + getIdentitySigners, isCompleteSessionsTopology, minimiseSessionsTopology, SessionsTopology, } from './session-config.js' import { RSY } from './signature.js' -import { minBytesFor, packRSY } from './utils.js' -import { Payload } from './index.js' +import { minBytesFor, packRSY, unpackRSY } from './utils.js' export type ImplicitSessionCallSignature = { - attestation: Attestation + attestation: Attestation.Attestation identitySignature: RSY sessionSignature: RSY } @@ -45,7 +46,7 @@ export function sessionCallSignatureToJson(callSignature: SessionCallSignature): export function encodeSessionCallSignatureForJson(callSignature: SessionCallSignature): any { if (isImplicitSessionCallSignature(callSignature)) { return { - attestation: encodeForJson(callSignature.attestation), + attestation: Attestation.encodeForJson(callSignature.attestation), identitySignature: rsyToRsvStr(callSignature.identitySignature), sessionSignature: rsyToRsvStr(callSignature.sessionSignature), } @@ -67,7 +68,7 @@ export function sessionCallSignatureFromJson(json: string): SessionCallSignature export function sessionCallSignatureFromParsed(decoded: any): SessionCallSignature { if (decoded.attestation) { return { - attestation: fromParsed(decoded.attestation), + attestation: Attestation.fromParsed(decoded.attestation), identitySignature: rsyFromRsvStr(decoded.identitySignature), sessionSignature: rsyFromRsvStr(decoded.sessionSignature), } @@ -103,9 +104,19 @@ function rsyFromRsvStr(sigStr: string): RSY { // Usage -export function encodeSessionCallSignatures( +/** + * Encodes a list of session call signatures into a bytes array for contract validation. + * @param callSignatures The list of session call signatures to encode. + * @param topology The complete session topology. + * @param explicitSigners The list of explicit signers to encode. Others will be hashed into nodes. + * @param implicitSigners The list of implicit signers to encode. Others will be hashed into nodes. + * @param identitySigner The identity signer to encode. Others will be hashed into nodes. + * @returns The encoded session call signatures. + */ +export function encodeSessionSignature( callSignatures: SessionCallSignature[], topology: SessionsTopology, + identitySigner: Address.Address, explicitSigners: Address.Address[] = [], implicitSigners: Address.Address[] = [], ): Bytes.Bytes { @@ -117,8 +128,14 @@ export function encodeSessionCallSignatures( throw new Error('Incomplete topology') } + // Check the topology contains the identity signer + const identitySigners = getIdentitySigners(topology) + if (!identitySigners.some((s) => Address.isEqual(s, identitySigner))) { + throw new Error('Identity signer not found') + } + // Optimise the configuration tree by rolling unused signers into nodes. - topology = minimiseSessionsTopology(topology, explicitSigners, implicitSigners) + topology = minimiseSessionsTopology(topology, explicitSigners, implicitSigners, identitySigner) // Session topology const encodedTopology = encodeSessionsTopology(topology) @@ -134,10 +151,12 @@ export function encodeSessionCallSignatures( // Map each call signature to its attestation index callSignatures.filter(isImplicitSessionCallSignature).forEach((callSig) => { if (callSig.attestation) { - const attestationStr = toJson(callSig.attestation) + const attestationStr = Attestation.toJson(callSig.attestation) if (!attestationMap.has(attestationStr)) { attestationMap.set(attestationStr, encodedAttestations.length) - encodedAttestations.push(Bytes.concat(encode(callSig.attestation), packRSY(callSig.identitySignature))) + encodedAttestations.push( + Bytes.concat(Attestation.encode(callSig.attestation), packRSY(callSig.identitySignature)), + ) } } }) @@ -152,7 +171,7 @@ export function encodeSessionCallSignatures( for (const callSignature of callSignatures) { if (isImplicitSessionCallSignature(callSignature)) { // Implicit - const attestationStr = toJson(callSignature.attestation) + const attestationStr = Attestation.toJson(callSignature.attestation) const attestationIndex = attestationMap.get(attestationStr) if (attestationIndex === undefined) { // Unreachable @@ -176,24 +195,126 @@ export function encodeSessionCallSignatures( return Bytes.concat(...parts) } -// Helper +export function decodeSessionSignature(encodedSignatures: Bytes.Bytes): { + topology: SessionsTopology + callSignatures: SessionCallSignature[] +} { + let offset = 0 + + // Parse session topology length (3 bytes) + const topologyLength = Bytes.toNumber(encodedSignatures.slice(offset, offset + 3)) + offset += 3 + + // Parse session topology + const topologyBytes = encodedSignatures.slice(offset, offset + topologyLength) + offset += topologyLength + const topology = decodeSessionsTopology(topologyBytes) + + // Parse attestations count (1 byte) + const attestationsCount = Bytes.toNumber(encodedSignatures.slice(offset, offset + 1)) + offset += 1 + + // Parse attestations and identity signatures + const attestations: Attestation.Attestation[] = [] + const identitySignatures: RSY[] = [] + + for (let i = 0; i < attestationsCount; i++) { + // Parse attestation + const attestation = Attestation.decode(encodedSignatures.slice(offset)) + offset += Attestation.encode(attestation).length + attestations.push(attestation) -export function hashCallWithReplayProtection( - payload: Payload.Calls, + // Parse identity signature (64 bytes) + const identitySignature = unpackRSY(encodedSignatures.slice(offset, offset + 64)) + offset += 64 + identitySignatures.push(identitySignature) + } + + // Parse call signatures + const callSignatures: SessionCallSignature[] = [] + + while (offset < encodedSignatures.length) { + // Parse flag byte + const flagByte = encodedSignatures[offset]! + offset += 1 + + // Parse session signature (64 bytes) + const sessionSignature = unpackRSY(encodedSignatures.slice(offset, offset + 64)) + offset += 64 + + // Check if implicit (MSB set) or explicit + if ((flagByte & 0x80) !== 0) { + // Implicit call signature + const attestationIndex = flagByte & 0x7f + if (attestationIndex >= attestations.length) { + throw new Error('Invalid attestation index') + } + + callSignatures.push({ + attestation: attestations[attestationIndex]!, + identitySignature: identitySignatures[attestationIndex]!, + sessionSignature, + }) + } else { + // Explicit call signature + const permissionIndex = flagByte + callSignatures.push({ + permissionIndex: BigInt(permissionIndex), + sessionSignature, + }) + } + } + + return { + topology, + callSignatures, + } +} + +// Call encoding + +/** + * Hashes a call with replay protection parameters. + * @param payload The payload to hash. + * @param callIdx The index of the call to hash. + * @param chainId The chain ID. Use 0 when noChainId enabled. + * @param sessionManagerAddress The session manager address to compile the hash for. Only required to support deprecated hash encodings for Dev1, Dev2 and Rc3. + * @returns The hash of the call with replay protection parameters for sessions. + */ +export function hashPayloadWithCallIdx( + wallet: Address.Address, + payload: Payload.Calls & Payload.Parent, callIdx: number, chainId: number, - skipCallIdx: boolean = false, // Deprecated. Dev1 and Dev2 support + sessionManagerAddress?: Address.Address, ): Hex.Hex { - const call = payload.calls[callIdx]! - return Hex.fromBytes( - Hash.keccak256( - Bytes.concat( - Bytes.fromNumber(chainId, { size: 32 }), - Bytes.fromNumber(payload.space, { size: 32 }), - Bytes.fromNumber(payload.nonce, { size: 32 }), - skipCallIdx ? Bytes.from([]) : Bytes.fromNumber(callIdx, { size: 32 }), - Bytes.fromHex(Payload.hashCall(call)), + // Support deprecated hashes for Dev1, Dev2 and Rc3 + const deprecatedHashing = + sessionManagerAddress && + (Address.isEqual(sessionManagerAddress, Extensions.Dev1.sessions) || + Address.isEqual(sessionManagerAddress, Extensions.Dev2.sessions) || + Address.isEqual(sessionManagerAddress, Extensions.Rc3.sessions)) + if (deprecatedHashing) { + const call = payload.calls[callIdx]! + const ignoreCallIdx = !Address.isEqual(sessionManagerAddress, Extensions.Rc3.sessions) + return Hex.fromBytes( + Hash.keccak256( + Bytes.concat( + Bytes.fromNumber(chainId, { size: 32 }), + Bytes.fromNumber(payload.space, { size: 32 }), + Bytes.fromNumber(payload.nonce, { size: 32 }), + ignoreCallIdx ? Bytes.from([]) : Bytes.fromNumber(callIdx, { size: 32 }), + Bytes.fromHex(Payload.hashCall(call)), + ), ), - ), - ) + ) + } + // Current hashing scheme uses entire payload hash and call index (without last parent) + const parentWallets = payload.parentWallets + if (payload.parentWallets && payload.parentWallets.length > 0) { + payload.parentWallets.pop() + } + const payloadHash = Payload.hash(wallet, chainId, payload) + payload.parentWallets = parentWallets + return Hex.fromBytes(Hash.keccak256(Bytes.concat(payloadHash, Bytes.fromNumber(callIdx, { size: 32 })))) } diff --git a/packages/wallet/primitives/test/address.test.ts b/packages/wallet/primitives/test/address.test.ts index c53ada1418..38ac16ccc4 100644 --- a/packages/wallet/primitives/test/address.test.ts +++ b/packages/wallet/primitives/test/address.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest' import { Address, Bytes, Hash, Hex } from 'ox' import { from } from '../src/address.js' -import { Context, Dev1, Dev2, Rc3 } from '../src/context.js' +import { Context, Dev1, Dev2, Rc3, Rc4, Rc5 } from '../src/context.js' import { Config, hashConfiguration } from '../src/config.js' describe('Address', () => { @@ -121,6 +121,32 @@ describe('Address', () => { expect(address).not.toBe(dev2Address) }) + it('should work with Rc4 context', () => { + const { stage2, ...rc4Context } = Rc4 + const address = from(sampleConfig, rc4Context) + + expect(() => Address.assert(address)).not.toThrow() + expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) + + // Should be different from Dev2 + const { stage2: _, ...dev2Context } = Dev2 + const dev2Address = from(sampleConfig, dev2Context) + expect(address).not.toBe(dev2Address) + }) + + it('should work with Rc5 context', () => { + const { stage2, ...rc5Context } = Rc5 + const address = from(sampleConfig, rc5Context) + + expect(() => Address.assert(address)).not.toThrow() + expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) + + // Should be different from Dev2 + const { stage2: _, ...dev2Context } = Dev2 + const dev2Address = from(sampleConfig, dev2Context) + expect(address).not.toBe(dev2Address) + }) + it('should handle complex topology configurations', () => { const complexConfig: Config = { threshold: 2n, diff --git a/packages/wallet/primitives/test/config.test.ts b/packages/wallet/primitives/test/config.test.ts index 7df3a58070..4dbaa0f843 100644 --- a/packages/wallet/primitives/test/config.test.ts +++ b/packages/wallet/primitives/test/config.test.ts @@ -33,11 +33,13 @@ import { maximumDepth, evaluateConfigurationSafety, normalizeSignerSignature, + replaceAddress, } from '../src/config.js' describe('Config', () => { const testAddress1 = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' + const replacementAddress = '0x1111111111111111111111111111111111111111' const testImageHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' @@ -316,6 +318,67 @@ describe('Config', () => { }) }) + describe('replaceAddress', () => { + it('should replace signer leaf addresses', () => { + const signerLeaf: SignerLeaf = { ...sampleSignerLeaf } + + const result = replaceAddress(signerLeaf, testAddress1, replacementAddress) + + expect(result).toEqual({ ...sampleSignerLeaf, address: replacementAddress }) + expect(result).not.toBe(signerLeaf) + expect(signerLeaf.address).toBe(testAddress1) + }) + + it('should replace sapient signer leaf addresses', () => { + const sapientLeaf: SapientSignerLeaf = { ...sampleSapientSignerLeaf } + + const result = replaceAddress(sapientLeaf, testAddress2, replacementAddress) + + expect(result).toEqual({ ...sampleSapientSignerLeaf, address: replacementAddress }) + expect(result).not.toBe(sapientLeaf) + expect(sapientLeaf.address).toBe(testAddress2) + }) + + it('should recurse through nodes and nested leaves', () => { + const nestedSapient: SapientSignerLeaf = { ...sampleSapientSignerLeaf, address: testAddress1 } + const nestedLeaf: NestedLeaf = { + type: 'nested', + tree: [nestedSapient, sampleSubdigestLeaf], + weight: 5n, + threshold: 1n, + } + const topology: Topology = [{ ...sampleSignerLeaf }, nestedLeaf] + + const result = replaceAddress(topology, testAddress1, replacementAddress) + + expect(result).toEqual([ + { ...sampleSignerLeaf, address: replacementAddress }, + { + ...nestedLeaf, + tree: [{ ...nestedSapient, address: replacementAddress }, sampleSubdigestLeaf], + }, + ]) + expect(nestedSapient.address).toBe(testAddress1) + expect((nestedLeaf.tree as Node)[1]).toBe(sampleSubdigestLeaf) + }) + + it('should return the original topology when no address matches', () => { + const sapientLeaf: SapientSignerLeaf = { ...sampleSapientSignerLeaf } + + const result = replaceAddress(sapientLeaf, replacementAddress, testAddress1) + + expect(result).toBe(sapientLeaf) + }) + + it('should leave non-signer leaves unchanged', () => { + expect(replaceAddress(sampleSubdigestLeaf, testAddress1, replacementAddress)).toBe(sampleSubdigestLeaf) + expect(replaceAddress(sampleAnyAddressSubdigestLeaf, testAddress1, replacementAddress)).toBe( + sampleAnyAddressSubdigestLeaf, + ) + expect(replaceAddress(sampleNodeLeaf, testAddress1, replacementAddress)).toBe(sampleNodeLeaf) + }) + }) + describe('getWeight', () => { it('should return correct weight for signer leaf with canSign true', () => { const result = getWeight(sampleSignerLeaf, () => true) diff --git a/packages/wallet/primitives/test/erc-6492.test.ts b/packages/wallet/primitives/test/erc-6492.test.ts index dcb7688ee0..7398f58db9 100644 --- a/packages/wallet/primitives/test/erc-6492.test.ts +++ b/packages/wallet/primitives/test/erc-6492.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest' import { Address, Bytes, Hex, Provider } from 'ox' -import { WrappedSignature } from 'ox/erc6492' +import { SignatureErc6492 } from 'ox/erc6492' import { deploy, wrap, decode, isValid } from '../src/erc-6492.js' import { Context } from '../src/context.js' @@ -82,7 +82,7 @@ describe('ERC-6492', () => { expect(result.startsWith('0x')).toBe(true) // Should end with the magic bytes - expect(result.endsWith(WrappedSignature.magicBytes.slice(2))).toBe(true) + expect(result.endsWith(SignatureErc6492.magicBytes.slice(2))).toBe(true) // Should contain the original signature data somewhere expect(result.length).toBeGreaterThan(testSignature.length) @@ -96,7 +96,7 @@ describe('ERC-6492', () => { // Convert to hex to check magic bytes const resultHex = Bytes.toHex(result) - expect(resultHex.endsWith(WrappedSignature.magicBytes.slice(2))).toBe(true) + expect(resultHex.endsWith(SignatureErc6492.magicBytes.slice(2))).toBe(true) }) it('should return same type as input signature', () => { @@ -133,7 +133,7 @@ describe('ERC-6492', () => { // The wrapped signature should contain encoded: address, bytes (data), bytes (signature) expect(result.length).toBeGreaterThan(testSignature.length + deployData.data.length) expect(result).toContain(testAddress.slice(2)) // Address without 0x - expect(result.endsWith(WrappedSignature.magicBytes.slice(2))).toBe(true) + expect(result.endsWith(SignatureErc6492.magicBytes.slice(2))).toBe(true) }) }) @@ -209,7 +209,7 @@ describe('ERC-6492', () => { it('should handle malformed wrapped signature gracefully', () => { // Create a signature that ends with magic bytes but has invalid encoding - const malformedSig = ('0x1234' + WrappedSignature.magicBytes.slice(2)) as Hex.Hex + const malformedSig = ('0x1234' + SignatureErc6492.magicBytes.slice(2)) as Hex.Hex const result = decode(malformedSig) // Should return original signature when decoding fails @@ -391,7 +391,7 @@ describe('ERC-6492', () => { // 2. Wrap signature with deploy data const wrappedSig = wrap(testSignature, deployCall) - expect(wrappedSig.endsWith(WrappedSignature.magicBytes.slice(2))).toBe(true) + expect(wrappedSig.endsWith(SignatureErc6492.magicBytes.slice(2))).toBe(true) // 3. Decode wrapped signature const decoded = decode(wrappedSig) @@ -457,7 +457,7 @@ describe('ERC-6492', () => { it('should handle signatures that accidentally contain magic bytes', () => { // Create a signature that contains the magic bytes but isn't wrapped - const magicInSignature = (testSignature + WrappedSignature.magicBytes.slice(2) + '1234') as Hex.Hex + const magicInSignature = (testSignature + SignatureErc6492.magicBytes.slice(2) + '1234') as Hex.Hex const result = decode(magicInSignature) // Should try to decode, but if it fails, should return original diff --git a/packages/wallet/primitives/test/session-config.test.ts b/packages/wallet/primitives/test/session-config.test.ts index 7d092c15c0..6e20d55974 100644 --- a/packages/wallet/primitives/test/session-config.test.ts +++ b/packages/wallet/primitives/test/session-config.test.ts @@ -1,60 +1,60 @@ +import { Address, Bytes } from 'ox' import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hex } from 'ox' +import { ChainId } from '../src/network.js' +import { ParameterOperation, Permission, SessionPermissions } from '../src/permission.js' import { - SESSIONS_FLAG_PERMISSIONS, - SESSIONS_FLAG_NODE, - SESSIONS_FLAG_BRANCH, + IdentitySignerLeaf, + ImplicitBlacklistLeaf, SESSIONS_FLAG_BLACKLIST, + SESSIONS_FLAG_BRANCH, SESSIONS_FLAG_IDENTITY_SIGNER, - ImplicitBlacklistLeaf, - IdentitySignerLeaf, - SessionPermissionsLeaf, - SessionNode, - SessionLeaf, + SESSIONS_FLAG_NODE, + SESSIONS_FLAG_PERMISSIONS, SessionBranch, + SessionNode, + SessionPermissionsLeaf, SessionsTopology, - isSessionsTopology, - isCompleteSessionsTopology, - getIdentitySigner, + addExplicitSession, + addToImplicitBlacklist, + balanceSessionsTopology, + cleanSessionsTopology, + configurationTreeToSessionsTopology, + decodeLeafFromBytes, + decodeSessionsTopology, + emptySessionsTopology, + encodeLeafToGeneric, + encodeSessionsTopology, + getExplicitSigners, + getIdentitySigners, getImplicitBlacklist, getImplicitBlacklistLeaf, getSessionPermissions, - getExplicitSigners, - encodeLeafToGeneric, - decodeLeafFromBytes, - sessionsTopologyToConfigurationTree, - configurationTreeToSessionsTopology, - encodeSessionsTopology, - sessionsTopologyToJson, - sessionsTopologyFromJson, - removeExplicitSession, - addExplicitSession, + isCompleteSessionsTopology, + isSessionsTopology, mergeSessionsTopologies, - balanceSessionsTopology, - cleanSessionsTopology, minimiseSessionsTopology, - addToImplicitBlacklist, + removeExplicitSession, removeFromImplicitBlacklist, - emptySessionsTopology, + sessionsTopologyFromJson, + sessionsTopologyToConfigurationTree, + sessionsTopologyToJson, } from '../src/session-config.js' -import { SessionPermissions } from '../src/permission.js' -import { ChainId } from '../src/network.js' describe('Session Config', () => { // Test data - const testAddress1 = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address - const testAddress3 = '0xa0b86a33e6f8b5f56e64c9e1a1b8c6a9cc4b9a9e' as Address.Address - const testNode = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as SessionNode + const testAddress1: Address.Address = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' + const testAddress2: Address.Address = '0x8ba1f109551bd432803012645aac136c776056c0' + const testAddress3: Address.Address = '0xa0b86a33e6f8b5f56e64c9e1a1b8c6a9cc4b9a9e' + const testNode: SessionNode = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - const samplePermission = { + const samplePermission: Permission = { target: testAddress3, rules: [ { cumulative: false, - operation: 0, // EQUAL - value: Bytes.fromHex('0x'), + operation: ParameterOperation.EQUAL, + value: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), offset: 0n, mask: Bytes.fromHex('0xffffffff00000000000000000000000000000000000000000000000000000000'), }, @@ -152,9 +152,9 @@ describe('Session Config', () => { expect(isCompleteSessionsTopology(duplicateBlacklist)).toBe(false) }) - it('should return false for topology with multiple identity signers', () => { + it('should return true for topology with multiple identity signers', () => { const duplicateIdentity = [sampleBlacklistLeaf, sampleIdentitySignerLeaf, sampleIdentitySignerLeaf] - expect(isCompleteSessionsTopology(duplicateIdentity)).toBe(false) + expect(isCompleteSessionsTopology(duplicateIdentity)).toBe(true) }) it('should return false for invalid topology', () => { @@ -165,29 +165,29 @@ describe('Session Config', () => { }) describe('Topology Queries', () => { - describe('getIdentitySigner', () => { + describe('getIdentitySigners', () => { it('should return identity signer from identity signer leaf', () => { - const result = getIdentitySigner(sampleIdentitySignerLeaf) - expect(result).toBe(testAddress1) + const result = getIdentitySigners(sampleIdentitySignerLeaf) + expect(result).toEqual([testAddress1]) }) it('should return identity signer from branch', () => { - const result = getIdentitySigner(sampleCompleteTopology) - expect(result).toBe(testAddress1) + const result = getIdentitySigners(sampleCompleteTopology) + expect(result).toEqual([testAddress1]) }) - it('should return null when no identity signer present', () => { - const result = getIdentitySigner(sampleSessionPermissionsLeaf) - expect(result).toBe(null) + it('should return empty array when no identity signer present', () => { + const result = getIdentitySigners(sampleSessionPermissionsLeaf) + expect(result).toEqual([]) }) - it('should throw for multiple identity signers', () => { + it('should return multiple identity signers', () => { const multipleIdentity = [ sampleIdentitySignerLeaf, sampleIdentitySignerLeaf, sampleBlacklistLeaf, ] as SessionBranch - expect(() => getIdentitySigner(multipleIdentity)).toThrow('Multiple identity signers') + expect(getIdentitySigners(multipleIdentity)).toEqual([testAddress1, testAddress1]) }) }) @@ -400,12 +400,14 @@ describe('Session Config', () => { }) }) - describe('Sessions Topology Encoding', () => { + describe('Sessions Topology Encoding and Decoding', () => { describe('encodeSessionsTopology', () => { it('should encode session permissions leaf', () => { const result = encodeSessionsTopology(sampleSessionPermissionsLeaf) expect(result).toBeInstanceOf(Uint8Array) expect(result[0] >> 4).toBe(SESSIONS_FLAG_PERMISSIONS) + const decoded = decodeSessionsTopology(result) + expect(decoded).toEqual(sampleSessionPermissionsLeaf) }) it('should encode session node', () => { @@ -413,12 +415,34 @@ describe('Session Config', () => { expect(result).toBeInstanceOf(Uint8Array) expect(result[0] >> 4).toBe(SESSIONS_FLAG_NODE) expect(result.length).toBe(33) // 1 flag byte + 32 hash bytes + const decoded = decodeSessionsTopology(result) + expect(decoded).toEqual(testNode) }) it('should encode blacklist leaf', () => { const result = encodeSessionsTopology(sampleBlacklistLeaf) expect(result).toBeInstanceOf(Uint8Array) expect(result[0] >> 4).toBe(SESSIONS_FLAG_BLACKLIST) + const decoded = decodeSessionsTopology(result) + expect(decoded).toEqual(sampleBlacklistLeaf) + }) + + it('should encode large blacklist leaf', () => { + const blacklistCount = 1000 + const largeBlacklist: ImplicitBlacklistLeaf = { + type: 'implicit-blacklist', + blacklist: Array(blacklistCount).fill(testAddress1), + } + const result = encodeSessionsTopology(largeBlacklist) + expect(result).toBeInstanceOf(Uint8Array) + expect(result[0]).toBe((SESSIONS_FLAG_BLACKLIST << 4) | 0x0f) // Encoded large size flag + expect(Bytes.toNumber(result.slice(1, 3))).toBe(blacklistCount) + expect(result.slice(3)).toEqual( + Bytes.concat(...largeBlacklist.blacklist.map((b) => Bytes.padLeft(Bytes.fromHex(b), 20))), + ) + expect(result.length).toBe(3 + blacklistCount * 20) + const decoded = decodeSessionsTopology(result) + expect(decoded).toEqual(largeBlacklist) }) it('should encode identity signer leaf', () => { @@ -426,12 +450,16 @@ describe('Session Config', () => { expect(result).toBeInstanceOf(Uint8Array) expect(result[0] >> 4).toBe(SESSIONS_FLAG_IDENTITY_SIGNER) expect(result.length).toBe(21) // 1 flag byte + 20 address bytes + const decoded = decodeSessionsTopology(result) + expect(decoded).toEqual(sampleIdentitySignerLeaf) }) it('should encode session branch', () => { const result = encodeSessionsTopology(sampleBranch) expect(result).toBeInstanceOf(Uint8Array) expect(result[0] >> 4).toBe(SESSIONS_FLAG_BRANCH) + const decoded = decodeSessionsTopology(result) + expect(decoded).toEqual(sampleBranch) }) it('should handle large blacklist with extended encoding', () => { @@ -442,6 +470,15 @@ describe('Session Config', () => { const result = encodeSessionsTopology(largeBlacklist) expect(result).toBeInstanceOf(Uint8Array) expect(result[0] & 0x0f).toBe(0x0f) // Extended encoding flag + const decoded = decodeSessionsTopology(result) + expect(decoded).toEqual(largeBlacklist) + }) + + it('should handle complete topology', () => { + const result = encodeSessionsTopology(sampleCompleteTopology) + expect(result).toBeInstanceOf(Uint8Array) + const decoded = decodeSessionsTopology(result) + expect(decoded).toEqual(sampleCompleteTopology) }) it('should throw for blacklist too large', () => { @@ -619,13 +656,9 @@ describe('Session Config', () => { expect(isSessionsTopology(result)).toBe(true) const blacklist = getImplicitBlacklist(result) - const identitySigner = getIdentitySigner(result) + const identitySigners = getIdentitySigners(result) expect(blacklist).toBeTruthy() - expect(identitySigner).toBeTruthy() - }) - - it('should throw when missing blacklist or identity signer', () => { - expect(() => balanceSessionsTopology(sampleSessionPermissionsLeaf)).toThrow('No blacklist or identity signer') + expect(identitySigners).toBeTruthy() }) }) @@ -730,6 +763,137 @@ describe('Session Config', () => { it('should throw for invalid topology', () => { expect(() => minimiseSessionsTopology({} as any, [], [])).toThrow('Invalid topology') }) + + it('should minimize topology with multiple identity signers but keep only the specified one', () => { + // Create multiple identity signer leaves + const identitySigner1: IdentitySignerLeaf = { + type: 'identity-signer', + identitySigner: testAddress1, + } + const identitySigner2: IdentitySignerLeaf = { + type: 'identity-signer', + identitySigner: testAddress2, + } + const identitySigner3: IdentitySignerLeaf = { + type: 'identity-signer', + identitySigner: testAddress3, + } + + // Create topology with multiple identity signers + const topologyWithMultipleIdentitySigners: SessionBranch = [ + sampleBlacklistLeaf, + identitySigner1, + identitySigner2, + identitySigner3, + sampleSessionPermissionsLeaf, + ] + + // Minimize with only testAddress2 as the identity signer + const result = minimiseSessionsTopology( + topologyWithMultipleIdentitySigners, + [], // no explicit signers + [], // no implicit signers + testAddress2, // only keep this identity signer + ) + + expect(isSessionsTopology(result)).toBe(true) + + // Get all identity signers from the result + const identitySigners = getIdentitySigners(result) + + // Should only contain the specified identity signer + expect(identitySigners).toEqual([testAddress2]) + expect(identitySigners).not.toContain(testAddress1) + expect(identitySigners).not.toContain(testAddress3) + + // Verify the result is still a valid topology + expect(isSessionsTopology(result)).toBe(true) + }) + + it('should minimize deeply nested topology with multiple identity signers but keep only the specified one', () => { + // Create multiple identity signer leaves + const identitySigner1: IdentitySignerLeaf = { + type: 'identity-signer', + identitySigner: testAddress1, + } + const identitySigner2: IdentitySignerLeaf = { + type: 'identity-signer', + identitySigner: testAddress2, + } + const identitySigner3: IdentitySignerLeaf = { + type: 'identity-signer', + identitySigner: testAddress3, + } + + // Create additional session permissions for nesting + const sessionPermissions2: SessionPermissionsLeaf = { + type: 'session-permissions', + signer: testAddress2, + chainId: ChainId.MAINNET, + valueLimit: 500000000000000000n, + deadline: BigInt(Math.floor(Date.now() / 1000) + 1800), + permissions: [samplePermission], + } + + const sessionPermissions3: SessionPermissionsLeaf = { + type: 'session-permissions', + signer: testAddress3, + chainId: ChainId.MAINNET, + valueLimit: 750000000000000000n, + deadline: BigInt(Math.floor(Date.now() / 1000) + 2700), + permissions: [samplePermission], + } + + // Create a deeply nested topology structure + // Level 1: Main branch + // Level 2: Nested branches containing different combinations + const deeplyNestedTopology: SessionBranch = [ + // First nested branch: blacklist + identity signer 1 + [ + sampleBlacklistLeaf, + identitySigner1, + sampleSessionPermissionsLeaf, // testAddress1 session + ], + // Second nested branch: identity signer 2 + session permissions 2 + [ + identitySigner2, + sessionPermissions2, // testAddress2 session + ], + // Third nested branch: identity signer 3 + session permissions 3 + [ + identitySigner3, + sessionPermissions3, // testAddress3 session + ], + ] + + // Minimize with only testAddress2 as the identity signer + const result = minimiseSessionsTopology( + deeplyNestedTopology, + [], // no explicit signers + [], // no implicit signers + testAddress2, // only keep this identity signer + ) + + expect(isSessionsTopology(result)).toBe(true) + + // Get all identity signers from the result + const identitySigners = getIdentitySigners(result) + + // Should only contain the specified identity signer + expect(identitySigners).toEqual([testAddress2]) + expect(identitySigners).not.toContain(testAddress1) + expect(identitySigners).not.toContain(testAddress3) + + // Verify the result is still a valid topology + expect(isSessionsTopology(result)).toBe(true) + + // Verify that the nested structure is properly minimized + // The result should be a branch with hashed nodes and the preserved identity signer + if (Array.isArray(result)) { + // Should have some components (hashed nodes and the preserved identity signer) + expect(result.length).toBeGreaterThan(0) + } + }) }) describe('addToImplicitBlacklist', () => { @@ -796,8 +960,23 @@ describe('Session Config', () => { expect(isCompleteSessionsTopology(result)).toBe(true) - const identitySigner = getIdentitySigner(result) - expect(identitySigner).toBe(testAddress1) + const identitySigners = getIdentitySigners(result) + expect(identitySigners).toEqual([testAddress1]) + + const blacklist = getImplicitBlacklist(result) + expect(blacklist).toEqual([]) + + const explicitSigners = getExplicitSigners(result) + expect(explicitSigners).toEqual([]) + }) + + it('should create empty topology with multiple identity signers', () => { + const result = emptySessionsTopology([testAddress1, testAddress2]) + + expect(isCompleteSessionsTopology(result)).toBe(true) + + const identitySigners = getIdentitySigners(result) + expect(identitySigners).toEqual([testAddress1, testAddress2]) const blacklist = getImplicitBlacklist(result) expect(blacklist).toEqual([]) @@ -837,8 +1016,8 @@ describe('Session Config', () => { expect(isSessionsTopology(nestedTopology)).toBe(true) expect(isCompleteSessionsTopology(nestedTopology)).toBe(true) - const identitySigner = getIdentitySigner(nestedTopology) - expect(identitySigner).toBe(testAddress1) + const identitySigners = getIdentitySigners(nestedTopology) + expect(identitySigners).toEqual([testAddress1]) const blacklist = getImplicitBlacklist(nestedTopology) expect(blacklist).toContain(testAddress2) @@ -902,7 +1081,7 @@ describe('Session Config', () => { expect(isCompleteSessionsTopology(deserialized)).toBe(true) // Verify data integrity - expect(getIdentitySigner(deserialized)).toBe(testAddress1) + expect(getIdentitySigners(deserialized)).toEqual([testAddress1]) expect(getImplicitBlacklist(deserialized)).toContain(testAddress3) expect(getSessionPermissions(deserialized, testAddress2)).toBeTruthy() }) diff --git a/packages/wallet/primitives/test/session-signature.test.ts b/packages/wallet/primitives/test/session-signature.test.ts index 887ca6b047..a1fd0fe233 100644 --- a/packages/wallet/primitives/test/session-signature.test.ts +++ b/packages/wallet/primitives/test/session-signature.test.ts @@ -1,29 +1,32 @@ +import { Address, Bytes, Hex } from 'ox' import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Attestation } from '../src/attestation.js' +import { ChainId } from '../src/network.js' +import * as Payload from '../src/payload.js' +import { ParameterOperation } from '../src/permission.js' +import { minimiseSessionsTopology, SessionsTopology } from '../src/session-config.js' import { - ImplicitSessionCallSignature, + decodeSessionSignature, + encodeSessionCallSignatureForJson, + encodeSessionSignature, ExplicitSessionCallSignature, - SessionCallSignature, - isImplicitSessionCallSignature, + hashPayloadWithCallIdx, + ImplicitSessionCallSignature, isExplicitSessionCallSignature, - sessionCallSignatureToJson, - encodeSessionCallSignatureForJson, + isImplicitSessionCallSignature, + SessionCallSignature, sessionCallSignatureFromJson, sessionCallSignatureFromParsed, - encodeSessionCallSignatures, - hashCallWithReplayProtection, + sessionCallSignatureToJson, } from '../src/session-signature.js' import { RSY } from '../src/signature.js' -import { Attestation } from '../src/attestation.js' -import { SessionsTopology } from '../src/session-config.js' -import * as Payload from '../src/payload.js' -import { ChainId } from '../src/network.js' +import { Extensions } from '../src/index.js' describe('Session Signature', () => { // Test data - const testAddress1 = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address + const testAddress1: Address.Address = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' + const testAddress2: Address.Address = '0x8ba1f109551bd432803012645aac136c776056c0' const testChainId = ChainId.MAINNET const testSpace = 0n const testNonce = 1n @@ -102,7 +105,7 @@ describe('Session Signature', () => { rules: [ { cumulative: false, - operation: 0, + operation: ParameterOperation.EQUAL, value: Bytes.fromHex('0x'), offset: 0n, mask: Bytes.fromHex('0xffffffff00000000000000000000000000000000000000000000000000000000'), @@ -276,31 +279,50 @@ describe('Session Signature', () => { }) }) - describe('Signature Encoding', () => { - describe('encodeSessionCallSignatures', () => { + describe('Signature Encoding and Decoding', () => { + describe('encode / decodeSessionCallSignatures', () => { it('should encode single explicit session call signature', () => { const callSignatures = [sampleExplicitSignature] - const result = encodeSessionCallSignatures(callSignatures, completeTopology) + const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBeGreaterThan(0) + + const decoded = decodeSessionSignature(result) + expect(decoded.callSignatures.length).toBe(1) + const callSignature = decoded.callSignatures[0]! + if (!isExplicitSessionCallSignature(callSignature)) { + throw new Error('Call signature is not explicit') + } + expect(callSignature.permissionIndex).toBe(callSignatures[0]!.permissionIndex) + // The topology gets minimized during encoding, so we expect the minimized version + const minimizedTopology = minimiseSessionsTopology(completeTopology, [], [], testAddress1) + expect(decoded.topology).toEqual(minimizedTopology) }) // Skip implicit signature tests that cause encoding issues it.skip('should encode single implicit session call signature', () => { const callSignatures = [sampleImplicitSignature] - const result = encodeSessionCallSignatures(callSignatures, completeTopology) + const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBeGreaterThan(0) + + const decoded = decodeSessionSignature(result) + expect(decoded.callSignatures).toEqual(callSignatures) + expect(decoded.topology).toEqual(completeTopology) }) it.skip('should encode multiple mixed session call signatures', () => { const callSignatures = [sampleImplicitSignature, sampleExplicitSignature] - const result = encodeSessionCallSignatures(callSignatures, completeTopology) + const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBeGreaterThan(0) + + const decoded = decodeSessionSignature(result) + expect(decoded.callSignatures).toEqual(callSignatures) + expect(decoded.topology).toEqual(completeTopology) }) it.skip('should encode multiple implicit signatures with same attestation', () => { @@ -311,10 +333,15 @@ describe('Session Signature', () => { sessionSignature: sampleRSY2, // Different session signature }, ] - const result = encodeSessionCallSignatures(callSignatures, completeTopology) + const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBeGreaterThan(0) + + const decoded = decodeSessionSignature(result) + expect(decoded.callSignatures).toEqual(callSignatures) + const minimizedTopology = minimiseSessionsTopology(completeTopology, [], [], testAddress1) + expect(decoded.topology).toEqual(minimizedTopology) }) it('should throw for incomplete topology', () => { @@ -335,8 +362,8 @@ describe('Session Signature', () => { rules: [ { cumulative: false, - operation: 0, - value: Bytes.fromHex('0x'), + operation: ParameterOperation.EQUAL, + value: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), offset: 0n, mask: Bytes.fromHex('0xffffffff00000000000000000000000000000000000000000000000000000000'), }, @@ -347,7 +374,7 @@ describe('Session Signature', () => { // Missing identity signer, but has 2 elements for valid SessionBranch ] - expect(() => encodeSessionCallSignatures([sampleExplicitSignature], incompleteTopology)).toThrow( + expect(() => encodeSessionSignature([sampleExplicitSignature], incompleteTopology, testAddress1)).toThrow( 'Incomplete topology', ) }) @@ -358,68 +385,85 @@ describe('Session Signature', () => { sessionSignature: sampleRSY, } - expect(() => encodeSessionCallSignatures([largeIndexSignature], completeTopology)).toThrow( + expect(() => encodeSessionSignature([largeIndexSignature], completeTopology, testAddress1)).toThrow( 'Permission index is too large', ) }) - it('should throw for too many attestations (simplified)', () => { - // Just test that we can create many explicit signatures instead - const callSignatures: ExplicitSessionCallSignature[] = Array(10) - .fill(null) - .map((_, i) => ({ - permissionIndex: BigInt(i), - sessionSignature: sampleRSY, - })) - - const result = encodeSessionCallSignatures(callSignatures, completeTopology) - expect(result).toBeInstanceOf(Uint8Array) - }) - it('should handle explicit signers parameter', () => { const callSignatures = [sampleExplicitSignature] - const result = encodeSessionCallSignatures(callSignatures, completeTopology, [testAddress1]) + const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBeGreaterThan(0) + + const decoded = decodeSessionSignature(result) + expect(decoded.callSignatures.length).toBe(1) + const callSignature = decoded.callSignatures[0]! + if (!isExplicitSessionCallSignature(callSignature)) { + throw new Error('Call signature is not explicit') + } + expect(callSignature.permissionIndex).toBe(callSignatures[0]!.permissionIndex) + // The topology gets minimized during encoding, so we expect the minimized version + const minimizedTopology = minimiseSessionsTopology(completeTopology, [], [], testAddress1) + expect(decoded.topology).toEqual(minimizedTopology) }) it('should handle implicit signers parameter', () => { const callSignatures = [sampleExplicitSignature] - const result = encodeSessionCallSignatures(callSignatures, completeTopology, [], [testAddress2]) + const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1, [], [testAddress2]) expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBeGreaterThan(0) + + const decoded = decodeSessionSignature(result) + expect(decoded.callSignatures.length).toBe(1) + const callSignature = decoded.callSignatures[0]! + if (!isExplicitSessionCallSignature(callSignature)) { + throw new Error('Call signature is not explicit') + } + expect(callSignature.permissionIndex).toBe(callSignatures[0]!.permissionIndex) + // The topology gets minimized during encoding, so we expect the minimized version + const minimizedTopology = minimiseSessionsTopology(completeTopology, [], [testAddress2], testAddress1) + expect(decoded.topology).toEqual(minimizedTopology) }) it('should throw for invalid call signature type', () => { const invalidSignature = {} as any - expect(() => encodeSessionCallSignatures([invalidSignature], completeTopology)).toThrow( + expect(() => encodeSessionSignature([invalidSignature], completeTopology, testAddress1)).toThrow( 'Invalid call signature', ) }) + + it('should throw for identity signer not found', () => { + const callSignatures = [sampleExplicitSignature] + expect(() => + encodeSessionSignature(callSignatures, completeTopology, testAddress2, [], [testAddress2]), + ).toThrow('Identity signer not found') + }) }) }) describe('Helper Functions', () => { - describe('hashCallWithReplayProtection', () => { + describe('hashPayloadWithCallIdx', () => { it('should hash call with replay protection parameters', () => { - const result = hashCallWithReplayProtection(samplePayload, 0, testChainId) + const result = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) expect(result).toMatch(/^0x[0-9a-f]{64}$/) // 32-byte hex string expect(Hex.size(result)).toBe(32) }) it('should produce different hashes for different chain IDs', () => { - const hash1 = hashCallWithReplayProtection(samplePayload, 0, ChainId.MAINNET) - const hash2 = hashCallWithReplayProtection(samplePayload, 0, ChainId.POLYGON) + const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, ChainId.MAINNET) + const hash2 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, ChainId.POLYGON) expect(hash1).not.toBe(hash2) }) it('should produce different hashes for different spaces', () => { - const hash1 = hashCallWithReplayProtection(samplePayload, 0, testChainId) - const hash2 = hashCallWithReplayProtection( + const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) + const hash2 = hashPayloadWithCallIdx( + testAddress1, { ...samplePayload, space: samplePayload.space + 1n }, 0, testChainId, @@ -429,8 +473,9 @@ describe('Session Signature', () => { }) it('should produce different hashes for different nonces', () => { - const hash1 = hashCallWithReplayProtection(samplePayload, 0, testChainId) - const hash2 = hashCallWithReplayProtection( + const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) + const hash2 = hashPayloadWithCallIdx( + testAddress1, { ...samplePayload, nonce: samplePayload.nonce + 1n }, 0, testChainId, @@ -446,17 +491,51 @@ describe('Session Signature', () => { } const payload2 = { ...samplePayload, calls: [call2] } - const hash1 = hashCallWithReplayProtection(samplePayload, 0, testChainId) - const hash2 = hashCallWithReplayProtection(payload2, 0, testChainId) + const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) + const hash2 = hashPayloadWithCallIdx(testAddress1, payload2, 0, testChainId) + + expect(hash1).not.toBe(hash2) + }) + + it('should produce different hashes for different wallets', () => { + const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } + + const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId) + const hash2 = hashPayloadWithCallIdx(testAddress2, payload, 0, testChainId) + + expect(hash1).not.toBe(hash2) + }) + + it('should NOT produce different hashes for different wallets when using deprecated hash encoding for Dev1 and Dev2', () => { + // This is ONLY for backward compatibility with Dev1 and Dev2 + // This is exploitable and should not be used in practice + const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } + + const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId, Extensions.Dev1.sessions) + const hash2 = hashPayloadWithCallIdx(testAddress2, payload, 0, testChainId, Extensions.Dev2.sessions) + + expect(hash1).toBe(hash2) + }) + + it('should produce different hashes for different wallets when using deprecated hash encoding for Dev1/2, Rc3 and latest', () => { + // This is ONLY for backward compatibility with Rc3 + // This is exploitable and should not be used in practice + const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } + + const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId, Extensions.Dev1.sessions) + const hash2 = hashPayloadWithCallIdx(testAddress2, payload, 0, testChainId, Extensions.Rc3.sessions) + const hash3 = hashPayloadWithCallIdx(testAddress2, payload, 0, testChainId) expect(hash1).not.toBe(hash2) + expect(hash1).not.toBe(hash3) + expect(hash2).not.toBe(hash3) }) it('should produce different hashes for same call at different index', () => { const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } - const hash1 = hashCallWithReplayProtection(payload, 0, testChainId) - const hash2 = hashCallWithReplayProtection(payload, 1, testChainId) + const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId) + const hash2 = hashPayloadWithCallIdx(testAddress1, payload, 1, testChainId) expect(hash1).not.toBe(hash2) }) @@ -466,15 +545,15 @@ describe('Session Signature', () => { // This is exploitable and should not be used in practice const payload = { ...samplePayload, calls: [sampleCall, sampleCall] } - const hash1 = hashCallWithReplayProtection(payload, 0, testChainId, true) - const hash2 = hashCallWithReplayProtection(payload, 1, testChainId, true) + const hash1 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId, Extensions.Dev1.sessions) + const hash2 = hashPayloadWithCallIdx(testAddress1, payload, 1, testChainId, Extensions.Dev1.sessions) expect(hash1).toBe(hash2) }) it('should be deterministic', () => { - const hash1 = hashCallWithReplayProtection(samplePayload, 0, testChainId) - const hash2 = hashCallWithReplayProtection(samplePayload, 0, testChainId) + const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) + const hash2 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) expect(hash1).toBe(hash2) }) @@ -484,7 +563,8 @@ describe('Session Signature', () => { const largeSpace = 2n ** 16n const largeNonce = 2n ** 24n - const result = hashCallWithReplayProtection( + const result = hashPayloadWithCallIdx( + testAddress1, { ...samplePayload, space: largeSpace, nonce: largeNonce }, 0, largeChainId, @@ -493,7 +573,7 @@ describe('Session Signature', () => { }) it('should handle zero values', () => { - const result = hashCallWithReplayProtection({ ...samplePayload, space: 0n, nonce: 0n }, 0, 0) + const result = hashPayloadWithCallIdx(testAddress1, { ...samplePayload, space: 0n, nonce: 0n }, 0, 0) expect(result).toMatch(/^0x[0-9a-f]{64}$/) }) @@ -504,7 +584,7 @@ describe('Session Signature', () => { } const payload = { ...samplePayload, calls: [callWithEmptyData] } - const result = hashCallWithReplayProtection(payload, 0, testChainId) + const result = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId) expect(result).toMatch(/^0x[0-9a-f]{64}$/) }) @@ -515,8 +595,8 @@ describe('Session Signature', () => { } const payload = { ...samplePayload, calls: [delegateCall] } - const hash1 = hashCallWithReplayProtection(samplePayload, 0, testChainId) - const hash2 = hashCallWithReplayProtection(payload, 0, testChainId) + const hash1 = hashPayloadWithCallIdx(testAddress1, samplePayload, 0, testChainId) + const hash2 = hashPayloadWithCallIdx(testAddress1, payload, 0, testChainId) expect(hash1).not.toBe(hash2) }) @@ -525,7 +605,7 @@ describe('Session Signature', () => { describe('Edge Cases and Error Handling', () => { it('should handle empty call signatures array', () => { - const result = encodeSessionCallSignatures([], completeTopology) + const result = encodeSessionSignature([], completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBeGreaterThan(0) // Should still contain topology }) @@ -536,7 +616,7 @@ describe('Session Signature', () => { sessionSignature: sampleRSY, } - const result = encodeSessionCallSignatures([maxIndexSignature], completeTopology) + const result = encodeSessionSignature([maxIndexSignature], completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) }) @@ -546,7 +626,7 @@ describe('Session Signature', () => { sessionSignature: sampleRSY, } - const result = encodeSessionCallSignatures([zeroIndexSignature], completeTopology) + const result = encodeSessionSignature([zeroIndexSignature], completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) }) @@ -579,9 +659,9 @@ describe('Session Signature', () => { it.skip('should handle attestation with minimal data', () => { const minimalAttestation: Attestation = { approvedSigner: testAddress1, - identityType: Bytes.fromHex('0x00'), - issuerHash: Bytes.fromHex(('0x' + '00'.repeat(32)) as Hex.Hex), - audienceHash: Bytes.fromHex(('0x' + '00'.repeat(32)) as Hex.Hex), + identityType: Bytes.fromHex('0x00000000'), + issuerHash: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), + audienceHash: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), applicationData: Bytes.fromArray([]), authData: { redirectUrl: '', @@ -595,7 +675,7 @@ describe('Session Signature', () => { sessionSignature: sampleRSY2, } - const result = encodeSessionCallSignatures([minimalImplicitSignature], completeTopology) + const result = encodeSessionSignature([minimalImplicitSignature], completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) }) @@ -624,8 +704,8 @@ describe('Session Signature', () => { rules: [ { cumulative: false, - operation: 0, - value: Bytes.fromHex('0x'), + operation: ParameterOperation.EQUAL, + value: Bytes.fromHex('0x0000000000000000000000000000000000000000000000000000000000000000'), offset: 0n, mask: Bytes.fromHex('0xffffffff00000000000000000000000000000000000000000000000000000000'), }, @@ -639,7 +719,7 @@ describe('Session Signature', () => { // This test may not actually trigger the error since creating a 3-byte overflow is complex // We'll test that the function works with a large but valid topology - const result = encodeSessionCallSignatures(callSignatures, largeTopology) + const result = encodeSessionSignature(callSignatures, largeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) }) @@ -662,7 +742,7 @@ describe('Session Signature', () => { const callSignatures: ExplicitSessionCallSignature[] = [invalidExplicitSignature] expect(() => { - encodeSessionCallSignatures(callSignatures, completeTopology) + encodeSessionSignature(callSignatures, completeTopology, testAddress1) }).toThrow() // Should throw due to permission index validation }) }) @@ -678,7 +758,7 @@ describe('Session Signature', () => { ] // Encode - const encoded = encodeSessionCallSignatures(callSignatures, completeTopology, [testAddress1]) + const encoded = encodeSessionSignature(callSignatures, completeTopology, testAddress1) expect(encoded).toBeInstanceOf(Uint8Array) // Test encoding for each signature @@ -693,12 +773,15 @@ describe('Session Signature', () => { const calls: Payload.Call[] = [ sampleCall, { ...sampleCall, to: testAddress2 }, + { ...sampleCall, to: testAddress2 }, // Repeat call { ...sampleCall, value: 500000000000000000n }, ] const payload = { ...samplePayload, calls: calls } // Generate hashes for each call - const hashes = calls.map((call) => hashCallWithReplayProtection(payload, calls.indexOf(call), testChainId)) + const hashes = calls.map((call) => + hashPayloadWithCallIdx(testAddress1, payload, calls.indexOf(call), testChainId), + ) // All hashes should be valid and different for (let i = 0; i < hashes.length; i++) { @@ -726,7 +809,7 @@ describe('Session Signature', () => { }, ] - const result = encodeSessionCallSignatures(callSignatures, completeTopology) + const result = encodeSessionSignature(callSignatures, completeTopology, testAddress1) expect(result).toBeInstanceOf(Uint8Array) expect(result.length).toBeGreaterThan(0) }) diff --git a/packages/wallet/wdk/.env.test b/packages/wallet/wdk/.env.test deleted file mode 100644 index 84a53e8c03..0000000000 --- a/packages/wallet/wdk/.env.test +++ /dev/null @@ -1,5 +0,0 @@ -PRIVATE_KEY= -# When using an RPC, use cors-anywhere. docker run -d -p 8080:8080 redocly/cors-anywhere. http://localhost:8080/https... -RPC_URL= -RELAYER_PK= - diff --git a/packages/wallet/wdk/CHANGELOG.md b/packages/wallet/wdk/CHANGELOG.md new file mode 100644 index 0000000000..6f2094f859 --- /dev/null +++ b/packages/wallet/wdk/CHANGELOG.md @@ -0,0 +1,73 @@ +# @0xsequence/wallet-wdk + +## 3.0.0-beta.6 + +### Patch Changes + +- Fix signer 404 error, minor fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.6 + - @0xsequence/identity-instrument@3.0.0-beta.6 + - @0xsequence/relayer@3.0.0-beta.6 + - @0xsequence/wallet-core@3.0.0-beta.6 + - @0xsequence/wallet-primitives@3.0.0-beta.6 + +## 3.0.0-beta.5 + +### Patch Changes + +- Beta release for v3 +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.5 + - @0xsequence/identity-instrument@3.0.0-beta.5 + - @0xsequence/relayer@3.0.0-beta.5 + - @0xsequence/wallet-core@3.0.0-beta.5 + - @0xsequence/wallet-primitives@3.0.0-beta.5 + +## 3.0.0-beta.4 + +### Patch Changes + +- RC5 upgrade +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.4 + - @0xsequence/identity-instrument@3.0.0-beta.4 + - @0xsequence/relayer@3.0.0-beta.4 + - @0xsequence/wallet-core@3.0.0-beta.4 + - @0xsequence/wallet-primitives@3.0.0-beta.4 + +## 3.0.0-beta.3 + +### Patch Changes + +- 3.0.0-beta.3 with fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.3 + - @0xsequence/identity-instrument@3.0.0-beta.3 + - @0xsequence/relayer@3.0.0-beta.3 + - @0xsequence/wallet-core@3.0.0-beta.3 + - @0xsequence/wallet-primitives@3.0.0-beta.3 + +## 3.0.0-beta.2 + +### Patch Changes + +- 3.0.0-beta.2 with identity instrument updates +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.2 + - @0xsequence/identity-instrument@3.0.0-beta.2 + - @0xsequence/relayer@3.0.0-beta.2 + - @0xsequence/wallet-core@3.0.0-beta.2 + - @0xsequence/wallet-primitives@3.0.0-beta.2 + +## 3.0.0-beta.1 + +### Patch Changes + +- 3.0.0-beta.1 +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.1 + - @0xsequence/identity-instrument@3.0.0-beta.1 + - @0xsequence/relayer@3.0.0-beta.1 + - @0xsequence/wallet-core@3.0.0-beta.1 + - @0xsequence/wallet-primitives@3.0.0-beta.1 diff --git a/packages/wallet/wdk/package.json b/packages/wallet/wdk/package.json index 05e0a91710..069ea2491a 100644 --- a/packages/wallet/wdk/package.json +++ b/packages/wallet/wdk/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-wdk", - "version": "0.0.0", + "version": "3.0.0-beta.6", "license": "Apache-2.0", "type": "module", "publishConfig": { @@ -10,8 +10,9 @@ "scripts": { "build": "tsc", "dev": "tsc --watch", - "test": "vitest run", + "test": "vitest run && npm run test:ssr", "test:coverage": "vitest run --coverage", + "test:ssr": "node test/test-ssr-safety.mjs", "typecheck": "tsc --noEmit", "clean": "rimraf dist" }, @@ -23,23 +24,24 @@ }, "devDependencies": { "@repo/typescript-config": "workspace:^", - "@types/node": "^22.15.29", - "@vitest/coverage-v8": "^3.2.4", - "dotenv": "^16.5.0", - "fake-indexeddb": "^6.0.1", - "happy-dom": "^20.0.2", - "typescript": "^5.8.3", - "vitest": "^3.2.1" + "@types/node": "^25.0.2", + "@vitest/coverage-v8": "^4.0.15", + "dotenv": "^17.2.3", + "fake-indexeddb": "^6.2.5", + "happy-dom": "^20.0.11", + "typescript": "^5.9.3", + "vitest": "^4.0.15" }, "dependencies": { "@0xsequence/guard": "workspace:^", "@0xsequence/identity-instrument": "workspace:^", + "@0xsequence/relayer": "workspace:^", "@0xsequence/tee-verifier": "^0.1.2", "@0xsequence/wallet-core": "workspace:^", "@0xsequence/wallet-primitives": "workspace:^", - "idb": "^7.1.1", + "idb": "^8.0.3", "jwt-decode": "^4.0.0", - "ox": "^0.7.2", - "uuid": "^11.1.0" + "ox": "^0.9.17", + "uuid": "^13.0.0" } } diff --git a/packages/wallet/wdk/src/dbs/auth-commitments.ts b/packages/wallet/wdk/src/dbs/auth-commitments.ts index dcb97e5664..a3f360639a 100644 --- a/packages/wallet/wdk/src/dbs/auth-commitments.ts +++ b/packages/wallet/wdk/src/dbs/auth-commitments.ts @@ -5,7 +5,7 @@ const TABLE_NAME = 'auth-commitments' export type AuthCommitment = { id: string - kind: 'google-pkce' | 'apple' + kind: 'google-pkce' | 'apple' | `custom-${string}` metadata: { [key: string]: string } verifier?: string challenge?: string diff --git a/packages/wallet/wdk/src/identity/signer.ts b/packages/wallet/wdk/src/identity/signer.ts index 6204f12854..fdc53ca417 100644 --- a/packages/wallet/wdk/src/identity/signer.ts +++ b/packages/wallet/wdk/src/identity/signer.ts @@ -17,7 +17,7 @@ export function toIdentityAuthKey(authKey: AuthKey): Identity.AuthKey { hash: 'SHA-256', }, authKey.privateKey, - digest, + new Uint8Array(digest), ) return Hex.fromBytes(new Uint8Array(authKeySignature)) }, diff --git a/packages/wallet/wdk/src/sequence/guards.ts b/packages/wallet/wdk/src/sequence/guards.ts index c46f670904..a005a16190 100644 --- a/packages/wallet/wdk/src/sequence/guards.ts +++ b/packages/wallet/wdk/src/sequence/guards.ts @@ -1,8 +1,8 @@ -import { Address, Secp256k1 } from 'ox' +import { Address, Bytes } from 'ox' import { Shared } from './manager.js' import * as Guard from '@0xsequence/guard' import { Signers } from '@0xsequence/wallet-core' -import { Config } from '@0xsequence/wallet-primitives' +import { Config, Constants } from '@0xsequence/wallet-primitives' export type GuardRole = 'wallet' | 'sessions' @@ -28,17 +28,28 @@ export class Guards { return undefined } - topology(role: GuardRole): Config.NestedLeaf | undefined { + topology(role: GuardRole): Config.Topology | undefined { const guardAddress = this.shared.sequence.guardAddresses[role] if (!guardAddress) { return undefined } - return { - type: 'nested', - weight: 1n, - threshold: 1n, - tree: { ...this.shared.sequence.defaultGuardTopology, address: guardAddress }, + const topology = Config.replaceAddress( + this.shared.sequence.defaultGuardTopology, + Constants.PlaceholderAddress, + guardAddress, + ) + + // If the imageHash did not change it means the replacement failed + if ( + Bytes.isEqual( + Config.hashConfiguration(topology), + Config.hashConfiguration(this.shared.sequence.defaultGuardTopology), + ) + ) { + throw new Error(`Guard address replacement failed for role ${role}`) } + + return topology } } diff --git a/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts b/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts index 6029f41141..0b4706c0e9 100644 --- a/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts +++ b/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts @@ -8,15 +8,16 @@ import { AuthCodeHandler } from './authcode.js' export class AuthCodePkceHandler extends AuthCodeHandler implements Handler { constructor( - signupKind: 'google-pkce', + signupKind: 'google-pkce' | `custom-${string}`, issuer: string, + oauthUrl: string, audience: string, nitro: Identity.IdentityInstrument, signatures: Signatures, commitments: Db.AuthCommitments, authKeys: Db.AuthKeys, ) { - super(signupKind, issuer, audience, nitro, signatures, commitments, authKeys) + super(signupKind, issuer, oauthUrl, audience, nitro, signatures, commitments, authKeys) } public async commitAuth(target: string, isSignUp: boolean, state?: string, signer?: string) { @@ -50,8 +51,7 @@ export class AuthCodePkceHandler extends AuthCodeHandler implements Handler { state, }) - const oauthUrl = this.oauthUrl() - return `${oauthUrl}?${searchParams.toString()}` + return `${this.oauthUrl}?${searchParams.toString()}` } public async completeAuth( diff --git a/packages/wallet/wdk/src/sequence/handlers/authcode.ts b/packages/wallet/wdk/src/sequence/handlers/authcode.ts index bb8a1b315d..f73f9ec5d1 100644 --- a/packages/wallet/wdk/src/sequence/handlers/authcode.ts +++ b/packages/wallet/wdk/src/sequence/handlers/authcode.ts @@ -11,8 +11,9 @@ export class AuthCodeHandler extends IdentityHandler implements Handler { protected redirectUri: string = '' constructor( - public readonly signupKind: 'apple' | 'google-pkce', + public readonly signupKind: 'apple' | 'google-pkce' | `custom-${string}`, public readonly issuer: string, + protected readonly oauthUrl: string, public readonly audience: string, nitro: Identity.IdentityInstrument, signatures: Signatures, @@ -48,12 +49,11 @@ export class AuthCodeHandler extends IdentityHandler implements Handler { client_id: this.audience, redirect_uri: this.redirectUri, response_type: 'code', - scope: 'openid', + scope: 'openid profile email', state, }) - const oauthUrl = this.oauthUrl() - return `${oauthUrl}?${searchParams.toString()}` + return `${this.oauthUrl}?${searchParams.toString()}` } public async completeAuth( @@ -100,15 +100,4 @@ export class AuthCodeHandler extends IdentityHandler implements Handler { }, } } - - protected oauthUrl() { - switch (this.issuer) { - case 'https://accounts.google.com': - return 'https://accounts.google.com/o/oauth2/v2/auth' - case 'https://appleid.apple.com': - return 'https://appleid.apple.com/auth/authorize' - default: - throw new Error('unsupported-issuer') - } - } } diff --git a/packages/wallet/wdk/src/sequence/handlers/guard.ts b/packages/wallet/wdk/src/sequence/handlers/guard.ts index c9a0a7b706..bbe8c26989 100644 --- a/packages/wallet/wdk/src/sequence/handlers/guard.ts +++ b/packages/wallet/wdk/src/sequence/handlers/guard.ts @@ -1,25 +1,30 @@ import { Address, Hex } from 'ox' import * as Guard from '@0xsequence/guard' +import { Signers } from '@0xsequence/wallet-core' import { Handler } from './handler.js' import { BaseSignatureRequest, SignerUnavailable, SignerReady, SignerActionable, Kinds } from '../types/index.js' import { Signatures } from '../signatures.js' -import { GuardRole, Guards } from '../guards.js' +import { Guards } from '../guards.js' + +type RespondFn = (token: Signers.GuardToken) => Promise + +export type PromptCodeHandler = ( + request: BaseSignatureRequest, + codeType: 'TOTP' | 'PIN', + respond: RespondFn, +) => Promise export class GuardHandler implements Handler { kind = Kinds.Guard - private onPromptCode: - | undefined - | ((codeType: 'TOTP' | 'PIN', respond: (code: string) => Promise) => Promise) + private onPromptCode: undefined | PromptCodeHandler constructor( private readonly signatures: Signatures, private readonly guards: Guards, ) {} - public registerUI( - onPromptCode: (codeType: 'TOTP' | 'PIN', respond: (code: string) => Promise) => Promise, - ) { + public registerUI(onPromptCode: PromptCodeHandler) { this.onPromptCode = onPromptCode return () => { this.onPromptCode = undefined @@ -90,17 +95,13 @@ export class GuardHandler implements Handler { resolve(true) } catch (e) { if (e instanceof Guard.AuthRequiredError) { - const respond = async (code: string) => { - try { - const signature = await guard.signEnvelope(request.envelope, { id: e.id, code }) - await this.signatures.addSignature(request.id, signature) - resolve(true) - } catch (e) { - reject(e) - } + const respond: RespondFn = async (token) => { + const signature = await guard.signEnvelope(request.envelope, token) + await this.signatures.addSignature(request.id, signature) + resolve(true) } - await onPromptCode(e.id, respond) + await onPromptCode(request, e.id, respond) } else { reject(e) } diff --git a/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts b/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts index e24a32900a..143edc0cdd 100644 --- a/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts +++ b/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts @@ -7,15 +7,17 @@ import { SignerReady, SignerUnavailable, BaseSignatureRequest, SignerActionable type RespondFn = (mnemonic: string) => Promise +export type PromptMnemonicHandler = (respond: RespondFn) => Promise + export class MnemonicHandler implements Handler { kind = Kinds.LoginMnemonic - private onPromptMnemonic: undefined | ((respond: RespondFn) => Promise) + private onPromptMnemonic: undefined | PromptMnemonicHandler private readySigners = new Map() constructor(private readonly signatures: Signatures) {} - public registerUI(onPromptMnemonic: (respond: RespondFn) => Promise) { + public registerUI(onPromptMnemonic: PromptMnemonicHandler) { this.onPromptMnemonic = onPromptMnemonic return () => { this.onPromptMnemonic = undefined @@ -93,7 +95,7 @@ export class MnemonicHandler implements Handler { message: 'enter-mnemonic', handle: () => new Promise(async (resolve, reject) => { - const respond = async (mnemonic: string) => { + const respond: RespondFn = async (mnemonic) => { const signer = MnemonicHandler.toSigner(mnemonic) if (!signer) { return reject('invalid-mnemonic') diff --git a/packages/wallet/wdk/src/sequence/handlers/otp.ts b/packages/wallet/wdk/src/sequence/handlers/otp.ts index c6cb53aa8f..f037189cbf 100644 --- a/packages/wallet/wdk/src/sequence/handlers/otp.ts +++ b/packages/wallet/wdk/src/sequence/handlers/otp.ts @@ -11,16 +11,18 @@ import { AnswerIncorrectError, ChallengeExpiredError, TooManyAttemptsError } fro type RespondFn = (otp: string) => Promise +export type PromptOtpHandler = (recipient: string, respond: RespondFn) => Promise + export class OtpHandler extends IdentityHandler implements Handler { kind = Kinds.LoginEmailOtp - private onPromptOtp: undefined | ((recipient: string, respond: RespondFn) => Promise) + private onPromptOtp: undefined | PromptOtpHandler constructor(nitro: Identity.IdentityInstrument, signatures: Signatures, authKeys: Db.AuthKeys) { super(nitro, authKeys, signatures, Identity.IdentityType.Email) } - public registerUI(onPromptOtp: (recipient: string, respond: RespondFn) => Promise) { + public registerUI(onPromptOtp: PromptOtpHandler) { this.onPromptOtp = onPromptOtp return () => { this.onPromptOtp = undefined @@ -91,13 +93,13 @@ export class OtpHandler extends IdentityHandler implements Handler { private handleAuth( challenge: Identity.OtpChallenge, - onPromptOtp: (recipient: string, respond: RespondFn) => Promise, + onPromptOtp: PromptOtpHandler, ): Promise<{ signer: Signers.Signer & Signers.Witnessable; email: string }> { return new Promise(async (resolve, reject) => { try { const { loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge) - const respond = async (otp: string) => { + const respond: RespondFn = async (otp) => { try { const { signer, email: returnedEmail } = await this.nitroCompleteAuth( challenge.withAnswer(codeChallenge, otp), diff --git a/packages/wallet/wdk/src/sequence/manager.ts b/packages/wallet/wdk/src/sequence/manager.ts index 0dbe84ef86..ba27116cf3 100644 --- a/packages/wallet/wdk/src/sequence/manager.ts +++ b/packages/wallet/wdk/src/sequence/manager.ts @@ -1,5 +1,5 @@ -import { Signers as CoreSigners, Relayer, State } from '@0xsequence/wallet-core' - +import { Bundler, Signers as CoreSigners, State } from '@0xsequence/wallet-core' +import { Relayer } from '@0xsequence/relayer' import { IdentityInstrument } from '@0xsequence/identity-instrument' import { createAttestationVerifyingFetch } from '@0xsequence/tee-verifier' import { Config, Constants, Context, Extensions, Network } from '@0xsequence/wallet-primitives' @@ -27,14 +27,17 @@ import { Signers } from './signers.js' import { Transactions, TransactionsInterface } from './transactions.js' import { Kinds } from './types/signer.js' import { Wallets, WalletsInterface } from './wallets.js' -import { GuardHandler } from './handlers/guard.js' +import { GuardHandler, PromptCodeHandler } from './handlers/guard.js' import { PasskeyCredential } from '../dbs/index.js' +import { PromptMnemonicHandler } from './handlers/mnemonic.js' +import { PromptOtpHandler } from './handlers/otp.js' export type ManagerOptions = { verbose?: boolean extensions?: Extensions.Extensions context?: Context.Context + context4337?: Context.Context guest?: Address.Address encryptedPksDb?: CoreSigners.Pk.Encrypted.EncryptedPksDb @@ -52,11 +55,14 @@ export type ManagerOptions = { stateProvider?: State.Provider networks?: Network.Network[] relayers?: Relayer.Relayer[] | (() => Relayer.Relayer[]) - bundlers?: Relayer.Bundler[] + bundlers?: Bundler.Bundler[] guardUrl?: string guardAddresses?: Record - defaultGuardTopology?: Config.SignerLeaf + nonWitnessableSigners?: Address.Address[] + + // The default guard topology MUST have a placeholder address for the guard address + defaultGuardTopology?: Config.Topology defaultRecoverySettings?: RecoverySettings // EIP-6963 support @@ -79,15 +85,22 @@ export type ManagerOptions = { enabled: boolean clientId: string } + customProviders?: { + kind: `custom-${string}` + authMethod: 'id-token' | 'authcode' | 'authcode-pkce' + issuer: string + oauthUrl: string + clientId: string + }[] } } export const ManagerOptionsDefaults = { verbose: false, - extensions: Extensions.Rc3, - context: Context.Rc3, - context4337: Context.Rc3_4337, + extensions: Extensions.Rc5, + context: Context.Rc5, + context4337: Context.Rc5_4337, guest: Constants.DefaultGuestAddress, encryptedPksDb: new CoreSigners.Pk.Encrypted.EncryptedPksDb(), @@ -104,24 +117,42 @@ export const ManagerOptionsDefaults = { stateProvider: new State.Sequence.Provider(), networks: Network.ALL, - relayers: () => [Relayer.Standard.LocalRelayer.createFromWindow(window)].filter((r) => r !== undefined), + relayers: () => { + if (typeof window !== 'undefined') { + return [Relayer.LocalRelayer.createFromWindow(window)].filter((r) => r !== undefined) + } + return [] + }, bundlers: [], - guardUrl: 'https://dev-guard.sequence.app', + nonWitnessableSigners: [] as Address.Address[], + + guardUrl: 'https://guard.sequence.app', guardAddresses: { - wallet: '0xa2e70CeaB3Eb145F32d110383B75B330fA4e288a', - sessions: '0x18002Fc09deF9A47437cc64e270843dE094f5984', - } as Record, // TODO: change to the actual guard address + wallet: '0x26f3D30F41FA897309Ae804A2AFf15CEb1dA5742', + sessions: '0xF6Bc87F5F2edAdb66737E32D37b46423901dfEF1', + } as Record, defaultGuardTopology: { - // TODO: Move this somewhere else - type: 'signer', - address: '0x0000000000000000000000000000000000000000', // will be replaced by the actual guard address + type: 'nested', weight: 1n, - } as Config.SignerLeaf, + threshold: 1n, + tree: [ + { + type: 'signer', + address: Constants.PlaceholderAddress, + weight: 1n, + }, + { + type: 'signer', + // Sequence dev multisig, as recovery guard signer + address: '0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4', + weight: 1n, + }, + ], + } as Config.NestedLeaf, defaultSessionsTopology: { - // TODO: Move this somewhere else type: 'sapient-signer', weight: 1n, } as Omit, @@ -134,9 +165,8 @@ export const ManagerOptionsDefaults = { multiInjectedProviderDiscovery: true, identity: { - // TODO: change to prod url once deployed - url: 'https://dev-identity.sequence-dev.app', - fetch: window.fetch, + url: 'https://identity.sequence.app', + fetch: typeof window !== 'undefined' ? window.fetch : undefined, verifyAttestation: true, email: { enabled: false, @@ -157,11 +187,41 @@ export const CreateWalletOptionsDefaults = { } export function applyManagerOptionsDefaults(options?: ManagerOptions) { - return { + const merged = { ...ManagerOptionsDefaults, ...options, identity: { ...ManagerOptionsDefaults.identity, ...options?.identity }, } + + // Merge and normalize non-witnessable signers. + // We always include the sessions extension address for the active extensions set. + const nonWitnessable = new Set() + for (const address of ManagerOptionsDefaults.nonWitnessableSigners ?? []) { + nonWitnessable.add(address.toLowerCase()) + } + for (const address of options?.nonWitnessableSigners ?? []) { + nonWitnessable.add(address.toLowerCase()) + } + nonWitnessable.add(merged.extensions.sessions.toLowerCase()) + + // Include static signer leaves from the guard topology (e.g. recovery guard signer), + // but ignore the placeholder address that is later replaced per-role. + if (merged.defaultGuardTopology) { + const guardTopologySigners = Config.getSigners(merged.defaultGuardTopology) + for (const signer of guardTopologySigners.signers) { + if (Address.isEqual(signer, Constants.PlaceholderAddress)) { + continue + } + nonWitnessable.add(signer.toLowerCase()) + } + for (const signer of guardTopologySigners.sapientSigners) { + nonWitnessable.add(signer.address.toLowerCase()) + } + } + + merged.nonWitnessableSigners = Array.from(nonWitnessable) as Address.Address[] + + return merged } export type RecoverySettings = { @@ -193,9 +253,11 @@ export type Sequence = { readonly networks: Network.Network[] readonly relayers: Relayer.Relayer[] - readonly bundlers: Relayer.Bundler[] + readonly bundlers: Bundler.Bundler[] - readonly defaultGuardTopology: Config.SignerLeaf + readonly nonWitnessableSigners: ReadonlySet + + readonly defaultGuardTopology: Config.Topology readonly defaultRecoverySettings: RecoverySettings readonly guardUrl: string @@ -358,7 +420,7 @@ export class Manager { // Add EIP-6963 relayers if enabled if (ops.multiInjectedProviderDiscovery) { try { - relayers.push(...Relayer.Standard.EIP6963.getRelayers()) + relayers.push(...Relayer.EIP6963.getRelayers()) } catch (error) { console.warn('Failed to initialize EIP-6963 relayers:', error) } @@ -382,6 +444,10 @@ export class Manager { relayers, bundlers: ops.bundlers, + nonWitnessableSigners: new Set( + (ops.nonWitnessableSigners ?? []).map((address) => address.toLowerCase() as Address.Address), + ), + defaultGuardTopology: ops.defaultGuardTopology, defaultRecoverySettings: ops.defaultRecoverySettings, @@ -466,6 +532,7 @@ export class Manager { new AuthCodePkceHandler( 'google-pkce', 'https://accounts.google.com', + 'https://accounts.google.com/o/oauth2/v2/auth', ops.identity.google.clientId, identityInstrument, modules.signatures, @@ -480,6 +547,7 @@ export class Manager { new AuthCodeHandler( 'apple', 'https://appleid.apple.com', + 'https://appleid.apple.com/auth/authorize', ops.identity.apple.clientId, identityInstrument, modules.signatures, @@ -488,6 +556,46 @@ export class Manager { ), ) } + if (ops.identity.customProviders?.length) { + for (const provider of ops.identity.customProviders) { + switch (provider.authMethod) { + case 'id-token': + throw new Error('id-token is not supported yet') + case 'authcode': + shared.handlers.set( + provider.kind, + new AuthCodeHandler( + provider.kind, + provider.issuer, + provider.oauthUrl, + provider.clientId, + identityInstrument, + modules.signatures, + shared.databases.authCommitments, + shared.databases.authKeys, + ), + ) + break + case 'authcode-pkce': + shared.handlers.set( + provider.kind, + new AuthCodePkceHandler( + provider.kind, + provider.issuer, + provider.oauthUrl, + provider.clientId, + identityInstrument, + modules.signatures, + shared.databases.authCommitments, + shared.databases.authKeys, + ), + ) + break + default: + throw new Error('unsupported auth method') + } + } + } shared.modules = modules this.shared = shared @@ -500,18 +608,16 @@ export class Manager { } } - public registerMnemonicUI(onPromptMnemonic: (respond: (mnemonic: string) => Promise) => Promise) { + public registerMnemonicUI(onPromptMnemonic: PromptMnemonicHandler) { return this.mnemonicHandler.registerUI(onPromptMnemonic) } - public registerOtpUI(onPromptOtp: (recipient: string, respond: (otp: string) => Promise) => Promise) { + public registerOtpUI(onPromptOtp: PromptOtpHandler) { return this.otpHandler?.registerUI(onPromptOtp) || (() => {}) } - public registerGuardUI( - onPromptOtp: (codeType: 'TOTP' | 'PIN', respond: (otp: string) => Promise) => Promise, - ) { - return this.guardHandler?.registerUI(onPromptOtp) || (() => {}) + public registerGuardUI(onPromptCode: PromptCodeHandler) { + return this.guardHandler?.registerUI(onPromptCode) || (() => {}) } public async setRedirectPrefix(prefix: string) { diff --git a/packages/wallet/wdk/src/sequence/sessions.ts b/packages/wallet/wdk/src/sequence/sessions.ts index c47bf47cb1..70c2d85ed3 100644 --- a/packages/wallet/wdk/src/sequence/sessions.ts +++ b/packages/wallet/wdk/src/sequence/sessions.ts @@ -1,25 +1,22 @@ -import { Signers as CoreSigners, Envelope } from '@0xsequence/wallet-core' +import { IdentityType } from '@0xsequence/identity-instrument' +import { Envelope, type ExplicitSession } from '@0xsequence/wallet-core' import { Attestation, Config, - Constants, GenericTree, Payload, Signature as SequenceSignature, SessionConfig, } from '@0xsequence/wallet-primitives' import { Address, Bytes, Hash, Hex } from 'ox' -import { IdentityType } from '@0xsequence/identity-instrument' import { AuthCodePkceHandler } from './handlers/authcode-pkce.js' import { IdentityHandler, identityTypeToHex } from './handlers/identity.js' +import { Handler } from './handlers/index.js' import { ManagerOptionsDefaults, Shared } from './manager.js' +import { Kinds, Module } from './types/index.js' +import { AuthorizeImplicitSessionArgs } from './types/sessions.js' import { Actions } from './types/signature-request.js' -export type AuthorizeImplicitSessionArgs = { - target: string - applicationData?: Hex.Hex -} - export interface SessionsInterface { /** * Retrieves the raw, detailed session topology for a given wallet. @@ -91,16 +88,11 @@ export interface SessionsInterface { * completed using the `complete` method. * * @param walletAddress The address of the wallet to modify. - * @param sessionAddress The address of the key to be added as a session signer. * @param permissions The set of rules and limits that will govern this session key's capabilities. * @returns A promise that resolves to a `requestId` for the configuration update signature request. * @see {complete} to finalize the update after it has been signed. */ - addExplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, - permissions: CoreSigners.Session.ExplicitParams, - ): Promise + addExplicitSession(walletAddress: Address.Address, explicitSession: ExplicitSession): Promise /** * Initiates an on-chain configuration update to modify an existing "explicit session". @@ -112,7 +104,6 @@ export interface SessionsInterface { * Like adding a session, this requires a signed configuration update. * * @param walletAddress The address of the wallet to modify. - * @param sessionAddress The address of the session signer to modify. * @param permissions The new, complete set of rules and limits for this session key. * @param origin Optional string to identify the source of the request. * @returns A promise that resolves to a `requestId` for the configuration update. @@ -120,8 +111,7 @@ export interface SessionsInterface { */ modifyExplicitSession( walletAddress: Address.Address, - sessionAddress: Address.Address, - permissions: CoreSigners.Session.ExplicitParams, + explicitSession: ExplicitSession, origin?: string, ): Promise @@ -186,17 +176,24 @@ export class Sessions implements SessionsInterface { constructor(private readonly shared: Shared) {} async getTopology(walletAddress: Address.Address, fixMissing = false): Promise { - const { loginTopology, modules } = await this.shared.modules.wallets.getConfigurationParts(walletAddress) + const { loginTopology, devicesTopology, modules } = + await this.shared.modules.wallets.getConfigurationParts(walletAddress) const managerModule = modules.find((m) => Address.isEqual(m.sapientLeaf.address, this.shared.sequence.extensions.sessions), ) if (!managerModule) { if (fixMissing) { // Create the default session manager leaf - if (!Config.isSignerLeaf(loginTopology) && !Config.isSapientSignerLeaf(loginTopology)) { - throw new Error('Login topology is not a signer leaf') + const authorizedSigners = [...Config.topologyToFlatLeaves([devicesTopology, loginTopology])].filter( + Config.isSignerLeaf, + ) + if (authorizedSigners.length === 0) { + throw new Error('No signer leaves found') + } + let sessionsTopology = SessionConfig.emptySessionsTopology(authorizedSigners[0]!.address) + for (let i = 1; i < authorizedSigners.length; i++) { + sessionsTopology = SessionConfig.addIdentitySigner(sessionsTopology, authorizedSigners[i]!.address) } - const sessionsTopology = SessionConfig.emptySessionsTopology(loginTopology.address) const sessionsConfigTree = SessionConfig.sessionsTopologyToConfigurationTree(sessionsTopology) this.shared.sequence.stateProvider.saveTree(sessionsConfigTree) const imageHash = GenericTree.hash(sessionsConfigTree) @@ -221,23 +218,142 @@ export class Sessions implements SessionsInterface { return SessionConfig.configurationTreeToSessionsTopology(tree) } + private async updateSessionModule( + modules: Module[], + transformer: (topology: SessionConfig.SessionsTopology) => SessionConfig.SessionsTopology, + ) { + const ext = this.shared.sequence.extensions.sessions + const idx = modules.findIndex((m) => Address.isEqual(m.sapientLeaf.address, ext)) + if (idx === -1) { + return + } + + const sessionModule = modules[idx] + if (!sessionModule) { + throw new Error('session-module-not-found') + } + + const genericTree = await this.shared.sequence.stateProvider.getTree(sessionModule.sapientLeaf.imageHash) + if (!genericTree) { + throw new Error('session-module-tree-not-found') + } + + const topology = SessionConfig.configurationTreeToSessionsTopology(genericTree) + const nextTopology = transformer(topology) + const nextTree = SessionConfig.sessionsTopologyToConfigurationTree(nextTopology) + await this.shared.sequence.stateProvider.saveTree(nextTree) + if (!modules[idx]) { + throw new Error('session-module-not-found-(unreachable)') + } + + modules[idx].sapientLeaf.imageHash = GenericTree.hash(nextTree) + } + + hasSessionModule(modules: Module[]): boolean { + return modules.some((m) => Address.isEqual(m.sapientLeaf.address, this.shared.sequence.extensions.sessions)) + } + + async initSessionModule(modules: Module[], identitySigners: Address.Address[], guardTopology?: Config.Topology) { + if (this.hasSessionModule(modules)) { + throw new Error('session-module-already-initialized') + } + + if (identitySigners.length === 0) { + throw new Error('No identity signers provided') + } + + // Calculate image hash with the identity signers + const sessionsTopology = SessionConfig.emptySessionsTopology( + identitySigners as [Address.Address, ...Address.Address[]], + ) + // Store this tree in the state provider + const sessionsConfigTree = SessionConfig.sessionsTopologyToConfigurationTree(sessionsTopology) + this.shared.sequence.stateProvider.saveTree(sessionsConfigTree) + // Prepare the configuration leaf + const sessionsImageHash = GenericTree.hash(sessionsConfigTree) + const signer = { + ...ManagerOptionsDefaults.defaultSessionsTopology, + address: this.shared.sequence.extensions.sessions, + imageHash: sessionsImageHash, + } + modules.push({ + sapientLeaf: signer, + weight: 255n, + guardLeaf: guardTopology, + }) + } + + async addIdentitySignerToModules(modules: Module[], address: Address.Address) { + if (!this.hasSessionModule(modules)) { + throw new Error('session-module-not-enabled') + } + + await this.updateSessionModule(modules, (topology) => { + const existingSigners = SessionConfig.getIdentitySigners(topology) + if (existingSigners?.some((s) => Address.isEqual(s, address))) { + return topology + } + + return SessionConfig.addIdentitySigner(topology, address) + }) + } + + async removeIdentitySignerFromModules(modules: Module[], address: Address.Address) { + if (!this.hasSessionModule(modules)) { + throw new Error('session-module-not-enabled') + } + + await this.updateSessionModule(modules, (topology) => { + const newTopology = SessionConfig.removeIdentitySigner(topology, address) + if (!newTopology) { + // Can't remove the last identity signer + throw new Error('Cannot remove the last identity signer') + } + return newTopology + }) + } + async prepareAuthorizeImplicitSession( walletAddress: Address.Address, sessionAddress: Address.Address, args: AuthorizeImplicitSessionArgs, ): Promise { const topology = await this.getTopology(walletAddress) - const identitySignerAddress = SessionConfig.getIdentitySigner(topology) - if (!identitySignerAddress) { - throw new Error('No identity signer address found') + const identitySigners = SessionConfig.getIdentitySigners(topology) + if (identitySigners.length === 0) { + throw new Error('No identity signers found') } - const identityKind = await this.shared.modules.signers.kindOf(walletAddress, identitySignerAddress) - if (!identityKind) { - throw new Error('No identity handler kind found') + let handler: Handler | undefined + let identitySignerAddress: Address.Address | undefined + for (const identitySigner of identitySigners) { + const identityKind = await this.shared.modules.signers.kindOf(walletAddress, identitySigner) + if (!identityKind) { + console.warn('No identity handler kind found for', identitySigner) + continue + } + if (identityKind === Kinds.LoginPasskey) { + console.warn('Implicit sessions do not support passkeys', identitySigner) + continue + } + const iHandler = this.shared.handlers.get(identityKind) + if (!iHandler) { + continue + } + if (identityKind === Kinds.LocalDevice) { + const hasLocalDevice = await this.shared.modules.devices.has(identitySigner) + if (!hasLocalDevice) { + console.warn('Identity signer not on this device, skipping', identitySigner) + continue + } + } + + handler = iHandler + identitySignerAddress = identitySigner + break } - const handler = this.shared.handlers.get(identityKind) - if (!handler) { - throw new Error('No identity handler found') + + if (!handler || !identitySignerAddress) { + throw new Error('No identity handler or address found') } // Create the attestation to sign @@ -326,33 +442,31 @@ export class Sessions implements SessionsInterface { async addExplicitSession( walletAddress: Address.Address, - sessionAddress: Address.Address, - permissions: CoreSigners.Session.ExplicitParams, + explicitSession: ExplicitSession, origin?: string, ): Promise { const topology = await this.getTopology(walletAddress, true) const newTopology = SessionConfig.addExplicitSession(topology, { - ...permissions, - signer: sessionAddress, + ...explicitSession, + signer: explicitSession.sessionAddress, }) return this.prepareSessionUpdate(walletAddress, newTopology, origin) } async modifyExplicitSession( walletAddress: Address.Address, - sessionAddress: Address.Address, - permissions: CoreSigners.Session.ExplicitParams, + explicitSession: ExplicitSession, origin?: string, ): Promise { // This will add the session manager if it's missing const topology = await this.getTopology(walletAddress, true) - const intermediateTopology = SessionConfig.removeExplicitSession(topology, sessionAddress) + const intermediateTopology = SessionConfig.removeExplicitSession(topology, explicitSession.sessionAddress) if (!intermediateTopology) { throw new Error('Incomplete session topology') } const newTopology = SessionConfig.addExplicitSession(intermediateTopology, { - ...permissions, - signer: sessionAddress, + ...explicitSession, + signer: explicitSession.sessionAddress, }) return this.prepareSessionUpdate(walletAddress, newTopology, origin) } diff --git a/packages/wallet/wdk/src/sequence/signers.ts b/packages/wallet/wdk/src/sequence/signers.ts index 051e032705..c2002d2d9a 100644 --- a/packages/wallet/wdk/src/sequence/signers.ts +++ b/packages/wallet/wdk/src/sequence/signers.ts @@ -8,7 +8,11 @@ export function isWitnessExtraSignerKind(extra: any): extra is WitnessExtraSigne } function toKnownKind(kind: string): Kind { - if (Object.values(Kinds).includes(kind as Kind)) { + if (kind.startsWith('custom-')) { + return kind as Kind + } + + if (Object.values(Kinds).includes(kind as (typeof Kinds)[keyof typeof Kinds])) { return kind as Kind } @@ -44,6 +48,19 @@ export class Signers { return Kinds.Guard } + // Passkeys are a sapient signer module: the address alone identifies the kind. + // Metadata (credential id, public key, etc.) is loaded later by the PasskeysHandler + // via the witness payload, so we can skip the witness probe here. + if (Address.isEqual(this.shared.sequence.extensions.passkeys, address)) { + return Kinds.LoginPasskey + } + + // Some signers are known to never publish a witness record (e.g. module signers). + // Skip probing the Sessions/Witness endpoint for them. + if (this.shared.sequence.nonWitnessableSigners.has(address.toLowerCase() as Address.Address)) { + return undefined + } + // We need to use the state provider (and witness) this will tell us the kind of signer // NOTICE: This looks expensive, but this operation should be cached by the state provider const witness = await (imageHash diff --git a/packages/wallet/wdk/src/sequence/transactions.ts b/packages/wallet/wdk/src/sequence/transactions.ts index e16a4bee47..824cb00f69 100644 --- a/packages/wallet/wdk/src/sequence/transactions.ts +++ b/packages/wallet/wdk/src/sequence/transactions.ts @@ -1,4 +1,5 @@ -import { Envelope, Relayer, Wallet } from '@0xsequence/wallet-core' +import { Envelope, Wallet, Bundler } from '@0xsequence/wallet-core' +import { Relayer } from '@0xsequence/relayer' import { Constants, Payload } from '@0xsequence/wallet-primitives' import { Abi, AbiFunction, Address, Hex, Provider, RpcTransport } from 'ox' import { v7 as uuidv7 } from 'uuid' @@ -179,11 +180,13 @@ export class Transactions implements TransactionsInterface { } if (tx.status === 'relayed') { - let relayer: Relayer.Relayer | Relayer.Bundler | undefined = this.shared.sequence.relayers.find( + let relayer: Relayer.Relayer | Bundler.Bundler | undefined = this.shared.sequence.relayers.find( (relayer) => relayer.id === tx.relayerId, ) if (!relayer) { - const bundler = this.shared.sequence.bundlers.find((bundler) => bundler.id === tx.relayerId) + const bundler: Bundler.Bundler | undefined = this.shared.sequence.bundlers.find( + (bundler) => bundler.id === tx.relayerId, + ) if (!bundler) { console.warn('relayer or bundler not found', tx.id, tx.relayerId) continue @@ -347,7 +350,7 @@ export class Transactions implements TransactionsInterface { const feeOptions = await relayer.feeOptions(tx.wallet, tx.envelope.chainId, tx.envelope.payload.calls) if (feeOptions.options.length === 0) { - const { name, icon } = relayer instanceof Relayer.Standard.EIP6963.EIP6963Relayer ? relayer.info : {} + const { name, icon } = relayer instanceof Relayer.EIP6963.EIP6963Relayer ? relayer.info : {} return [ { @@ -361,7 +364,7 @@ export class Transactions implements TransactionsInterface { ] } - return feeOptions.options.map((feeOption) => ({ + return feeOptions.options.map((feeOption: Relayer.FeeOption) => ({ kind: 'standard', id: uuidv7(), feeOption, @@ -378,7 +381,7 @@ export class Transactions implements TransactionsInterface { } return Promise.all( - this.shared.sequence.bundlers.map(async (bundler): Promise => { + this.shared.sequence.bundlers.map(async (bundler: Bundler.Bundler): Promise => { const ifAvailable = await bundler.isAvailable(entrypoint, tx.envelope.chainId) if (!ifAvailable) { return [] @@ -580,7 +583,7 @@ export class Transactions implements TransactionsInterface { await this.shared.modules.signatures.complete(signature.id) } else if (isERC4337RelayerOption(tx.relayerOption)) { - if (!Relayer.isBundler(relayer)) { + if (!Bundler.isBundler(relayer)) { throw new Error(`Relayer ${tx.relayerOption.relayerId} is not a bundler`) } diff --git a/packages/wallet/wdk/src/sequence/types/module.ts b/packages/wallet/wdk/src/sequence/types/module.ts index df254a6482..014a97ae69 100644 --- a/packages/wallet/wdk/src/sequence/types/module.ts +++ b/packages/wallet/wdk/src/sequence/types/module.ts @@ -3,5 +3,5 @@ import { Config } from '@0xsequence/wallet-primitives' export type Module = { weight: bigint sapientLeaf: Config.SapientSignerLeaf - guardLeaf?: Config.NestedLeaf + guardLeaf?: Config.Topology } diff --git a/packages/wallet/wdk/src/sequence/types/sessions.ts b/packages/wallet/wdk/src/sequence/types/sessions.ts new file mode 100644 index 0000000000..1efef2490a --- /dev/null +++ b/packages/wallet/wdk/src/sequence/types/sessions.ts @@ -0,0 +1,6 @@ +import { Hex } from 'ox' + +export type AuthorizeImplicitSessionArgs = { + target: string + applicationData?: Hex.Hex +} diff --git a/packages/wallet/wdk/src/sequence/types/signer.ts b/packages/wallet/wdk/src/sequence/types/signer.ts index ac4b5b51f0..30a2a50731 100644 --- a/packages/wallet/wdk/src/sequence/types/signer.ts +++ b/packages/wallet/wdk/src/sequence/types/signer.ts @@ -12,7 +12,7 @@ export const Kinds = { Unknown: 'unknown', } as const -export type Kind = (typeof Kinds)[keyof typeof Kinds] +export type Kind = (typeof Kinds)[keyof typeof Kinds] | `custom-${string}` export type WitnessExtraSignerKind = { signerKind: string diff --git a/packages/wallet/wdk/src/sequence/types/transaction-request.ts b/packages/wallet/wdk/src/sequence/types/transaction-request.ts index 289bf5b645..51160a0499 100644 --- a/packages/wallet/wdk/src/sequence/types/transaction-request.ts +++ b/packages/wallet/wdk/src/sequence/types/transaction-request.ts @@ -1,6 +1,7 @@ -import { Envelope, Relayer } from '@0xsequence/wallet-core' +import { Envelope } from '@0xsequence/wallet-core' import { Payload } from '@0xsequence/wallet-primitives' import { Address, Hex } from 'ox' +import { Relayer } from '@0xsequence/relayer' export type TransactionRequest = { to: Address.Address diff --git a/packages/wallet/wdk/src/sequence/wallets.ts b/packages/wallet/wdk/src/sequence/wallets.ts index 1e8ac963a4..fd0320cc43 100644 --- a/packages/wallet/wdk/src/sequence/wallets.ts +++ b/packages/wallet/wdk/src/sequence/wallets.ts @@ -14,7 +14,7 @@ import { PasskeysHandler } from './handlers/passkeys.js' import { GuardRole } from './guards.js' export type StartSignUpWithRedirectArgs = { - kind: 'google-pkce' | 'apple' + kind: 'google-pkce' | 'apple' | `custom-${string}` target: string metadata: { [key: string]: string } } @@ -55,7 +55,7 @@ export type CompleteRedirectArgs = CommonSignupArgs & { } export type AuthCodeSignupArgs = CommonSignupArgs & { - kind: 'google-pkce' | 'apple' + kind: 'google-pkce' | 'apple' | `custom-${string}` commitment: AuthCommitment code: string target: string @@ -434,7 +434,7 @@ function toConfig( loginTopology: Config.Topology, devicesTopology: Config.Topology, modules: Module[], - guardTopology?: Config.NestedLeaf, + guardTopology?: Config.Topology, ): Config.Config { if (!guardTopology) { return { @@ -467,7 +467,7 @@ function toModulesTopology(modules: Module[]): Config.Topology { return { type: 'nested', weight: module.weight, - threshold: module.sapientLeaf.weight + module.guardLeaf.weight, + threshold: module.sapientLeaf.weight + Config.getWeight(module.guardLeaf, () => true).maxWeight, tree: [module.sapientLeaf, module.guardLeaf], } as Config.NestedLeaf } else { @@ -491,8 +491,7 @@ function fromModulesTopology(topology: Config.Topology): Module[] { } else if ( Config.isNestedLeaf(topology) && Config.isNode(topology.tree) && - Config.isSapientSignerLeaf(topology.tree[0]) && - Config.isNestedLeaf(topology.tree[1]) + Config.isSapientSignerLeaf(topology.tree[0]) ) { modules.push({ sapientLeaf: topology.tree[0], @@ -513,7 +512,7 @@ function fromConfig(config: Config.Config): { loginTopology: Config.Topology devicesTopology: Config.Topology modules: Module[] - guardTopology?: Config.NestedLeaf + guardTopology?: Config.Topology } { if (config.threshold === 1n) { if (Config.isNode(config.topology) && Config.isNode(config.topology[0])) { @@ -530,7 +529,7 @@ function fromConfig(config: Config.Config): { Config.isNode(config.topology) && Config.isNode(config.topology[0]) && Config.isNode(config.topology[0][0]) && - Config.isNestedLeaf(config.topology[0][1]) + Config.isTopology(config.topology[0][1]) ) { return { loginTopology: config.topology[0][0][0], @@ -693,10 +692,30 @@ export class Wallets implements WalletsInterface { } } } + + if (args.kind.startsWith('custom-')) { + // TODO: support other custom auth methods (e.g. id-token) + const handler = this.shared.handlers.get(args.kind) as AuthCodeHandler + if (!handler) { + throw new Error('handler-not-registered') + } + + const [signer, metadata] = await handler.completeAuth(args.commitment, args.code) + return { + signer, + extra: { + signerKind: args.kind, + }, + loginEmail: metadata.email, + } + } + + throw new Error('invalid-signup-kind') } async startSignUpWithRedirect(args: StartSignUpWithRedirectArgs) { - const handler = this.shared.handlers.get('login-' + args.kind) as AuthCodeHandler + const kind = args.kind.startsWith('custom-') ? args.kind : 'login-' + args.kind + const handler = this.shared.handlers.get(kind) as AuthCodeHandler if (!handler) { throw new Error('handler-not-registered') } @@ -721,7 +740,8 @@ export class Wallets implements WalletsInterface { use4337: args.use4337, }) } else { - const handler = this.shared.handlers.get('login-' + commitment.kind) as AuthCodeHandler + const kind = commitment.kind.startsWith('custom-') ? commitment.kind : 'login-' + commitment.kind + const handler = this.shared.handlers.get(kind) as AuthCodeHandler if (!handler) { throw new Error('handler-not-registered') } @@ -817,30 +837,12 @@ export class Wallets implements WalletsInterface { let modules: Module[] = [] if (!args.noSessionManager) { - // Calculate image hash with the identity signer - const sessionsTopology = SessionConfig.emptySessionsTopology(loginSignerAddress) - // Store this tree in the state provider - const sessionsConfigTree = SessionConfig.sessionsTopologyToConfigurationTree(sessionsTopology) - this.shared.sequence.stateProvider.saveTree(sessionsConfigTree) - // Prepare the configuration leaf - const sessionsImageHash = GenericTree.hash(sessionsConfigTree) - const signer = { - ...ManagerOptionsDefaults.defaultSessionsTopology, - address: this.shared.sequence.extensions.sessions, - imageHash: sessionsImageHash, - } - if (sessionsGuardTopology) { - modules.push({ - sapientLeaf: signer, - weight: 255n, - guardLeaf: sessionsGuardTopology, - }) - } else { - modules.push({ - sapientLeaf: signer, - weight: 255n, - }) + const identitySigners = [device.address] + if (!Signers.isSapientSigner(loginSigner.signer)) { + // Add non sapient login signer to the identity signers + identitySigners.unshift(loginSignerAddress) } + await this.shared.modules.sessions.initSessionModule(modules, identitySigners, sessionsGuardTopology) } if (!args.noRecovery) { @@ -1008,6 +1010,10 @@ export class Wallets implements WalletsInterface { await this.shared.modules.recovery.addRecoverySignerToModules(modules, device.address) } + if (this.shared.modules.sessions.hasSessionModule(modules)) { + await this.shared.modules.sessions.addIdentitySignerToModules(modules, device.address) + } + const walletEntryToUpdate: Wallet = { ...(existingWallet as Wallet), address: args.wallet, @@ -1173,12 +1179,6 @@ export class Wallets implements WalletsInterface { throw new Error('wallet-not-found') } - // Prevent starting logout if already logging out or not ready - if (walletEntry.status !== 'ready') { - console.warn(`Logout called on wallet ${wallet} with status ${walletEntry.status}. Aborting.`) - throw new Error(`Wallet is not in 'ready' state for logout (current: ${walletEntry.status})`) - } - if (options?.skipRemoveDevice) { await Promise.all([ this.shared.databases.manager.del(wallet), @@ -1187,6 +1187,12 @@ export class Wallets implements WalletsInterface { return undefined as any } + // Prevent starting logout if already logging out or not ready + if (walletEntry.status !== 'ready') { + console.warn(`Logout called on wallet ${wallet} with status ${walletEntry.status}. Aborting.`) + throw new Error(`Wallet is not in 'ready' state for logout (current: ${walletEntry.status})`) + } + const device = await this.shared.modules.devices.get(walletEntry.device) if (!device) { throw new Error('device-not-found') @@ -1376,6 +1382,11 @@ export class Wallets implements WalletsInterface { await this.shared.modules.recovery.removeRecoverySignerFromModules(modules, deviceToRemove) } + // Remove the device from the session module's topology as well. + if (this.shared.modules.sessions.hasSessionModule(modules)) { + await this.shared.modules.sessions.removeIdentitySignerFromModules(modules, deviceToRemove) + } + // Request the configuration update. const requestId = await this.requestConfigurationUpdate( wallet, diff --git a/packages/wallet/wdk/test/authcode-pkce.test.ts b/packages/wallet/wdk/test/authcode-pkce.test.ts index dc6ecfc732..c69e66d710 100644 --- a/packages/wallet/wdk/test/authcode-pkce.test.ts +++ b/packages/wallet/wdk/test/authcode-pkce.test.ts @@ -1,10 +1,10 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { Address, Hex, Bytes } from 'ox' import * as Identity from '@0xsequence/identity-instrument' -import { AuthCodePkceHandler } from '../src/sequence/handlers/authcode-pkce' -import { Signatures } from '../src/sequence/signatures' -import * as Db from '../src/dbs' -import { IdentitySigner } from '../src/identity/signer' +import { AuthCodePkceHandler } from '../src/sequence/handlers/authcode-pkce.js' +import { Signatures } from '../src/sequence/signatures.js' +import * as Db from '../src/dbs/index.js' +import { IdentitySigner } from '../src/identity/signer.js' describe('AuthCodePkceHandler', () => { let handler: AuthCodePkceHandler @@ -56,6 +56,7 @@ describe('AuthCodePkceHandler', () => { handler = new AuthCodePkceHandler( 'google-pkce', 'https://accounts.google.com', + 'https://accounts.google.com/o/oauth2/v2/auth', 'test-google-client-id', mockNitroInstrument, mockSignatures, @@ -81,8 +82,6 @@ describe('AuthCodePkceHandler', () => { email: 'user@example.com', } }) - - vi.spyOn(handler as any, 'oauthUrl').mockReturnValue('https://accounts.google.com/oauth/authorize') }) afterEach(() => { @@ -116,7 +115,7 @@ describe('AuthCodePkceHandler', () => { }) // Verify OAuth URL is constructed correctly - expect(result).toMatch(/^https:\/\/accounts\.google\.com\/oauth\/authorize\?/) + expect(result).toMatch(/^https:\/\/accounts\.google\.com\/o\/oauth2\/v2\/auth\?/) expect(result).toContain('code_challenge=mock-challenge-hash') expect(result).toContain('code_challenge_method=S256') expect(result).toContain('client_id=test-google-client-id') @@ -335,10 +334,6 @@ describe('AuthCodePkceHandler', () => { const newRedirectUri = 'https://newdomain.com/callback' handler.setRedirectUri(newRedirectUri) - // Verify redirect URI is used in OAuth URL construction - const mockUrl = 'https://accounts.google.com/oauth/authorize' - vi.spyOn(handler as any, 'oauthUrl').mockReturnValue(mockUrl) - return handler.commitAuth('https://example.com/success', true).then((result) => { expect(result).toContain(`redirect_uri=${encodeURIComponent(newRedirectUri)}`) }) @@ -346,8 +341,9 @@ describe('AuthCodePkceHandler', () => { it('Should work with different issuer and audience configurations', () => { const customHandler = new AuthCodePkceHandler( - 'google-pkce', + 'custom-provider', 'https://custom-issuer.com', + 'https://custom-issuer.com/o/oauth2/v2/auth', 'custom-client-id', mockNitroInstrument, mockSignatures, @@ -357,7 +353,7 @@ describe('AuthCodePkceHandler', () => { expect(customHandler['issuer']).toBe('https://custom-issuer.com') expect(customHandler['audience']).toBe('custom-client-id') - expect(customHandler.signupKind).toBe('google-pkce') + expect(customHandler.signupKind).toBe('custom-provider') }) }) }) diff --git a/packages/wallet/wdk/test/authcode.test.ts b/packages/wallet/wdk/test/authcode.test.ts index 06e4372af0..4874e475be 100644 --- a/packages/wallet/wdk/test/authcode.test.ts +++ b/packages/wallet/wdk/test/authcode.test.ts @@ -2,11 +2,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { Address, Hex, Bytes } from 'ox' import { Network, Payload } from '@0xsequence/wallet-primitives' import { IdentityInstrument, IdentityType, KeyType, AuthCodeChallenge } from '@0xsequence/identity-instrument' -import { AuthCodeHandler } from '../src/sequence/handlers/authcode' -import { Signatures } from '../src/sequence/signatures' -import * as Db from '../src/dbs' -import { IdentitySigner } from '../src/identity/signer' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request' +import { AuthCodeHandler } from '../src/sequence/handlers/authcode.js' +import { Signatures } from '../src/sequence/signatures.js' +import * as Db from '../src/dbs/index.js' +import { IdentitySigner } from '../src/identity/signer.js' +import { BaseSignatureRequest } from '../src/sequence/types/signature-request.js' // Mock the global crypto API const mockCryptoSubtle = { @@ -129,6 +129,7 @@ describe('AuthCodeHandler', () => { authCodeHandler = new AuthCodeHandler( 'google-pkce', 'https://accounts.google.com', + 'https://accounts.google.com/o/oauth2/v2/auth', 'test-audience', mockIdentityInstrument, mockSignatures, @@ -148,6 +149,7 @@ describe('AuthCodeHandler', () => { const handler = new AuthCodeHandler( 'google-pkce', 'https://accounts.google.com', + 'https://accounts.google.com/o/oauth2/v2/auth', 'google-client-id', mockIdentityInstrument, mockSignatures, @@ -165,6 +167,7 @@ describe('AuthCodeHandler', () => { const handler = new AuthCodeHandler( 'apple', 'https://appleid.apple.com', + 'https://appleid.apple.com/auth/authorize', 'apple-client-id', mockIdentityInstrument, mockSignatures, @@ -189,6 +192,7 @@ describe('AuthCodeHandler', () => { const googleHandler = new AuthCodeHandler( 'google-pkce', 'https://accounts.google.com', + 'https://accounts.google.com/o/oauth2/v2/auth', 'test-audience', mockIdentityInstrument, mockSignatures, @@ -203,6 +207,7 @@ describe('AuthCodeHandler', () => { const appleHandler = new AuthCodeHandler( 'apple', 'https://appleid.apple.com', + 'https://appleid.apple.com/auth/authorize', 'test-audience', mockIdentityInstrument, mockSignatures, @@ -249,7 +254,7 @@ describe('AuthCodeHandler', () => { // Verify commitment was saved expect(mockAuthCommitmentsSet).toHaveBeenCalledOnce() - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(commitmentCall.kind).toBe('google-pkce') expect(commitmentCall.signer).toBe(signer) @@ -274,7 +279,7 @@ describe('AuthCodeHandler', () => { const result = await authCodeHandler.commitAuth('/target', false, customState) // Verify commitment uses custom state - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(commitmentCall.id).toBe(customState) expect(result).toContain(`state=${customState}`) }) @@ -282,7 +287,7 @@ describe('AuthCodeHandler', () => { it('Should generate random state when not provided', async () => { const result = await authCodeHandler.commitAuth('/target', false) - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(commitmentCall.id).toBeDefined() expect(typeof commitmentCall.id).toBe('string') expect(commitmentCall.id.startsWith('0x')).toBe(true) @@ -293,6 +298,7 @@ describe('AuthCodeHandler', () => { const appleHandler = new AuthCodeHandler( 'apple', 'https://appleid.apple.com', + 'https://appleid.apple.com/auth/authorize', 'apple-client-id', mockIdentityInstrument, mockSignatures, @@ -310,7 +316,7 @@ describe('AuthCodeHandler', () => { it('Should create commitment without signer', async () => { const result = await authCodeHandler.commitAuth('/target', true) - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(commitmentCall.signer).toBeUndefined() expect(commitmentCall.isSignUp).toBe(true) }) @@ -342,12 +348,12 @@ describe('AuthCodeHandler', () => { // Verify commitVerifier was called expect(mockCommitVerifier).toHaveBeenCalledOnce() - const commitVerifierCall = mockCommitVerifier.mock.calls[0] + const commitVerifierCall = mockCommitVerifier.mock.calls[0]! expect(commitVerifierCall[1]).toBeInstanceOf(AuthCodeChallenge) // Verify completeAuth was called expect(mockCompleteAuth).toHaveBeenCalledOnce() - const completeAuthCall = mockCompleteAuth.mock.calls[0] + const completeAuthCall = mockCompleteAuth.mock.calls[0]! expect(completeAuthCall[1]).toBeInstanceOf(AuthCodeChallenge) // Verify results @@ -484,20 +490,21 @@ describe('AuthCodeHandler', () => { expect(window.location.href).toContain('https://accounts.google.com/o/oauth2/v2/auth') expect(mockAuthCommitmentsSet).toHaveBeenCalledOnce() - const commitmentCall = mockAuthCommitmentsSet.mock.calls[0][0] + const commitmentCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(commitmentCall.target).toBe(window.location.pathname) expect(commitmentCall.isSignUp).toBe(false) expect(commitmentCall.signer).toBe(testWallet) }) }) - // === OAUTH URL METHOD === + // === OAUTH URL PROPERTY === - describe('oauthUrl()', () => { + describe('oauthUrl', () => { it('Should return Google OAuth URL for Google issuer', () => { const googleHandler = new AuthCodeHandler( 'google-pkce', 'https://accounts.google.com', + 'https://accounts.google.com/o/oauth2/v2/auth', 'test-audience', mockIdentityInstrument, mockSignatures, @@ -505,7 +512,7 @@ describe('AuthCodeHandler', () => { mockAuthKeys, ) - const url = googleHandler['oauthUrl']() + const url = googleHandler['oauthUrl'] expect(url).toBe('https://accounts.google.com/o/oauth2/v2/auth') }) @@ -513,6 +520,7 @@ describe('AuthCodeHandler', () => { const appleHandler = new AuthCodeHandler( 'apple', 'https://appleid.apple.com', + 'https://appleid.apple.com/auth/authorize', 'test-audience', mockIdentityInstrument, mockSignatures, @@ -520,23 +528,9 @@ describe('AuthCodeHandler', () => { mockAuthKeys, ) - const url = appleHandler['oauthUrl']() + const url = appleHandler['oauthUrl'] expect(url).toBe('https://appleid.apple.com/auth/authorize') }) - - it('Should throw error for unsupported issuer', () => { - const unsupportedHandler = new AuthCodeHandler( - 'google-pkce', - 'https://unsupported.provider.com', - 'test-audience', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - - expect(() => unsupportedHandler['oauthUrl']()).toThrow('unsupported-issuer') - }) }) // === INHERITED METHODS FROM IDENTITYHANDLER === @@ -711,52 +705,20 @@ describe('AuthCodeHandler', () => { expect(metadata.email).toBe('test@example.com') }) - it('Should handle different OAuth providers correctly', async () => { - const providers = [ - { - signupKind: 'google-pkce' as const, - issuer: 'https://accounts.google.com', - expectedUrl: 'https://accounts.google.com/o/oauth2/v2/auth', - }, - { - signupKind: 'apple' as const, - issuer: 'https://appleid.apple.com', - expectedUrl: 'https://appleid.apple.com/auth/authorize', - }, - ] - - for (const provider of providers) { - const handler = new AuthCodeHandler( - provider.signupKind, - provider.issuer, - 'test-audience', - mockIdentityInstrument, - mockSignatures, - mockAuthCommitments, - mockAuthKeys, - ) - handler.setRedirectUri('https://example.com/callback') - - const url = await handler.commitAuth('/target', false) - expect(url).toContain(provider.expectedUrl) - expect(handler.kind).toBe(`login-${provider.signupKind}`) - } - }) - it('Should handle signup vs login flows correctly', async () => { authCodeHandler.setRedirectUri('https://example.com/callback') // Test signup flow await authCodeHandler.commitAuth('/signup-target', true, 'signup-state') - const signupCall = mockAuthCommitmentsSet.mock.calls[0][0] + const signupCall = mockAuthCommitmentsSet.mock.calls[0]![0]! expect(signupCall.isSignUp).toBe(true) expect(signupCall.target).toBe('/signup-target') // Test login flow await authCodeHandler.commitAuth('/login-target', false, 'login-state') - const loginCall = mockAuthCommitmentsSet.mock.calls[1][0] + const loginCall = mockAuthCommitmentsSet.mock.calls[1]![0]! expect(loginCall.isSignUp).toBe(false) expect(loginCall.target).toBe('/login-target') }) diff --git a/packages/wallet/wdk/test/constants.ts b/packages/wallet/wdk/test/constants.ts index 4ace92092c..01dff3b508 100644 --- a/packages/wallet/wdk/test/constants.ts +++ b/packages/wallet/wdk/test/constants.ts @@ -1,9 +1,10 @@ import { config as dotenvConfig } from 'dotenv' import { Abi, Address, Provider, RpcTransport } from 'ox' -import { Manager, ManagerOptions, ManagerOptionsDefaults } from '../src/sequence' -import { mockEthereum } from './setup' -import { Signers as CoreSigners, State, Relayer } from '@0xsequence/wallet-core' -import * as Db from '../src/dbs' +import { Manager, ManagerOptions, ManagerOptionsDefaults } from '../src/sequence/index.js' +import { mockEthereum } from './setup.js' +import { Signers as CoreSigners, State, Bundler } from '@0xsequence/wallet-core' +import { Relayer } from '@0xsequence/relayer' +import * as Db from '../src/dbs/index.js' import { Network } from '@0xsequence/wallet-primitives' const envFile = process.env.CI ? '.env.test' : '.env.test.local' @@ -13,8 +14,6 @@ export const EMITTER_ADDRESS: Address.Address = '0xb7bE532959236170064cf099e1a33 export const EMITTER_ABI = Abi.from(['function explicitEmit()', 'function implicitEmit()']) // Environment variables -export const { RPC_URL, PRIVATE_KEY } = process.env -export const CAN_RUN_LIVE = !!RPC_URL && !!PRIVATE_KEY export const LOCAL_RPC_URL = process.env.LOCAL_RPC_URL || 'http://localhost:8545' let testIdCounter = 0 @@ -83,16 +82,16 @@ export function newRemoteManager( : `_testrun_${testIdCounter}` let relayers: Relayer.Relayer[] = [] - let bundlers: Relayer.Bundler[] = [] + let bundlers: Bundler.Bundler[] = [] if (remoteManagerOptions.network.relayerPk) { const provider = Provider.from(RpcTransport.fromHttp(remoteManagerOptions.network.rpcUrl)) - relayers.push(new Relayer.Standard.PkRelayer(remoteManagerOptions.network.relayerPk as `0x${string}`, provider)) + relayers.push(new Relayer.PkRelayer(remoteManagerOptions.network.relayerPk as `0x${string}`, provider)) } if (remoteManagerOptions.network.bundlerUrl) { bundlers.push( - new Relayer.Bundlers.PimlicoBundler( + new Bundler.Bundlers.PimlicoBundler( remoteManagerOptions.network.bundlerUrl, Provider.from(RpcTransport.fromHttp(remoteManagerOptions.network.rpcUrl)), ), diff --git a/packages/wallet/wdk/test/guard.test.ts b/packages/wallet/wdk/test/guard.test.ts index 136a072e45..8614de6c2c 100644 --- a/packages/wallet/wdk/test/guard.test.ts +++ b/packages/wallet/wdk/test/guard.test.ts @@ -1,11 +1,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Manager } from '../src/sequence' -import { GuardHandler } from '../src/sequence/handlers/guard' +import { Manager } from '../src/sequence/index.js' +import { GuardHandler } from '../src/sequence/handlers/guard.js' import { Address, Bytes, Hex, TypedData } from 'ox' -import { Network, Payload } from '@0xsequence/wallet-primitives' -import { Kinds } from '../src/sequence/types/signer' -import { newManager } from './constants' -import { GuardRole, Guards } from '../src/sequence/guards' +import { Config, Constants, Network, Payload } from '@0xsequence/wallet-primitives' +import { Kinds } from '../src/sequence/types/signer.js' +import { newManager } from './constants.js' +import { GuardRole, Guards } from '../src/sequence/guards.js' // Mock fetch globally for guard API calls const mockFetch = vi.fn() @@ -163,7 +163,7 @@ describe('GuardHandler', () => { expect(result).toBe(true) expect(mockAddSignature).toHaveBeenCalledOnce() - const [requestId, signatureData] = mockAddSignature.mock.calls[0] + const [requestId, signatureData] = mockAddSignature.mock.calls[0]! expect(requestId).toBe('test-request-id') expect(signatureData.address).toBe(guards.getByRole('wallet').address) expect(signatureData.signature).toBeDefined() @@ -223,7 +223,7 @@ describe('GuardHandler', () => { const mockAddSignature = vi.fn() signatures.addSignature = mockAddSignature - const mockCallback = vi.fn().mockImplementation(async (codeType, respond) => { + const mockCallback = vi.fn().mockImplementation(async (request, codeType, respond) => { expect(codeType).toBe('TOTP') await respond('123456') }) @@ -247,7 +247,7 @@ describe('GuardHandler', () => { expect(mockCallback).toHaveBeenCalledOnce() expect(mockAddSignature).toHaveBeenCalledOnce() - const [requestId, signatureData] = mockAddSignature.mock.calls[0] + const [requestId, signatureData] = mockAddSignature.mock.calls[0]! expect(requestId).toBe('test-request-id') expect(signatureData.address).toBe(guards.getByRole('wallet').address) expect(signatureData.signature).toBeDefined() @@ -300,7 +300,7 @@ describe('GuardHandler', () => { signatures: [], }) - expect(mockFetch.mock.calls[0][0]).toContain(customGuardUrl) + expect(mockFetch.mock.calls[0]![0]).toContain(customGuardUrl) await customManager.stop() }) @@ -315,4 +315,60 @@ describe('GuardHandler', () => { expect(sharedConfig.guardAddresses).toBeDefined() }) }) + + describe('Guard Topology', () => { + it('should replace the placeholder guard address', () => { + const guardAddress = (manager as any).shared.sequence.guardAddresses.wallet + const defaultTopology = (manager as any).shared.sequence.defaultGuardTopology + + const topology = guards.topology('wallet') + + expect(topology).toBeDefined() + expect(Config.findSignerLeaf(topology!, guardAddress)).toBeDefined() + expect(Config.findSignerLeaf(topology!, Constants.PlaceholderAddress as Address.Address)).toBeUndefined() + expect(Config.findSignerLeaf(defaultTopology, guardAddress)).toBeUndefined() + expect(Config.hashConfiguration(topology!)).not.toEqual(Config.hashConfiguration(defaultTopology)) + }) + + it('should throw when the placeholder is missing in the default topology', async () => { + const customManager = newManager( + { + defaultGuardTopology: { + type: 'signer', + address: '0x0000000000000000000000000000000000000001', + weight: 1n, + }, + }, + undefined, + `guard_topology_${Date.now()}`, + ) + + const customGuards = (customManager as any).shared.modules.guards as Guards + + try { + expect(() => customGuards.topology('wallet')).toThrow('Guard address replacement failed for role wallet') + } finally { + await customManager.stop() + } + }) + + it('should return undefined when no guard address is set for a role', async () => { + const defaultWalletGuard = (manager as any).shared.sequence.guardAddresses.wallet + const customManager = newManager( + { + guardAddresses: { + wallet: defaultWalletGuard, + } as any, + }, + undefined, + `guard_missing_${Date.now()}`, + ) + + const customGuards = (customManager as any).shared.modules.guards as Guards + + expect(customGuards.topology('sessions')).toBeUndefined() + + await customManager.stop() + }) + }) }) diff --git a/packages/wallet/wdk/test/identity-auth-dbs.test.ts b/packages/wallet/wdk/test/identity-auth-dbs.test.ts index 43f6e0b5ba..eccc8b885d 100644 --- a/packages/wallet/wdk/test/identity-auth-dbs.test.ts +++ b/packages/wallet/wdk/test/identity-auth-dbs.test.ts @@ -1,9 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Manager } from '../src/sequence' -import { Address, Hex, Bytes } from 'ox' -import { IdentityInstrument } from '@0xsequence/identity-instrument' -import * as Db from '../src/dbs' -import { LOCAL_RPC_URL } from './constants' +import { Manager } from '../src/sequence/index.js' +import * as Db from '../src/dbs/index.js' +import { LOCAL_RPC_URL } from './constants.js' import { State } from '@0xsequence/wallet-core' import { Network } from '@0xsequence/wallet-primitives' diff --git a/packages/wallet/wdk/test/identity-signer.test.ts b/packages/wallet/wdk/test/identity-signer.test.ts index 5eb9c42b47..9d62f5719a 100644 --- a/packages/wallet/wdk/test/identity-signer.test.ts +++ b/packages/wallet/wdk/test/identity-signer.test.ts @@ -1,10 +1,10 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { Address, Hex, Bytes } from 'ox' +import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest' +import { Address, Bytes, Hex } from 'ox' import { Network, Payload } from '@0xsequence/wallet-primitives' import { IdentityInstrument, KeyType } from '@0xsequence/identity-instrument' import { State } from '@0xsequence/wallet-core' -import { IdentitySigner, toIdentityAuthKey } from '../src/identity/signer' -import { AuthKey } from '../src/dbs/auth-keys' +import { IdentitySigner, toIdentityAuthKey } from '../src/identity/signer.js' +import { AuthKey } from '../src/dbs/auth-keys.js' // Mock the global crypto API const mockCryptoSubtle = { @@ -31,7 +31,7 @@ describe('Identity Signer', () => { let testAuthKey: AuthKey let testWallet: Address.Address let mockStateWriter: State.Writer - let mockSignFn: ReturnType + let mockSignFn: Mock beforeEach(() => { vi.clearAllMocks() @@ -179,7 +179,7 @@ describe('Identity Signer', () => { // Verify that identityInstrument.sign was called with correct parameters expect(mockSignFn).toHaveBeenCalledOnce() - const [authKeyArg, digestArg] = mockSignFn.mock.calls[0] + const [authKeyArg, digestArg] = mockSignFn.mock.calls[0]! expect(authKeyArg.address).toBe(testAuthKey.address) expect(authKeyArg.signer).toBe(testAuthKey.identitySigner) expect(digestArg).toBeDefined() @@ -196,7 +196,7 @@ describe('Identity Signer', () => { expect(mockSignFn).toHaveBeenCalledOnce() // The digest should be different for different chainIds - const [, digestArg] = mockSignFn.mock.calls[0] + const [, digestArg] = mockSignFn.mock.calls[0]! expect(digestArg).toBeDefined() }) @@ -255,7 +255,7 @@ describe('Identity Signer', () => { } expect(mockSignFn).toHaveBeenCalledOnce() - const [authKeyArg, digestArg] = mockSignFn.mock.calls[0] + const [authKeyArg, digestArg] = mockSignFn.mock.calls[0]! expect(authKeyArg.address).toBe(testAuthKey.address) expect(digestArg).toBe(digest) }) @@ -295,7 +295,7 @@ describe('Identity Signer', () => { it('Should handle malformed signature from identity instrument', async () => { const digest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - mockSignFn.mockResolvedValueOnce('invalid-signature') + mockSignFn.mockResolvedValueOnce('invalid-signature' as any) await expect(identitySigner.signDigest(digest)).rejects.toThrow() // Should throw when Signature.fromHex fails }) @@ -317,7 +317,7 @@ describe('Identity Signer', () => { // Verify witness was saved expect(mockSaveWitnesses).toHaveBeenCalledOnce() - const [wallet, chainId, payload, witness] = mockSaveWitnesses.mock.calls[0] + const [wallet, chainId, payload, witness] = mockSaveWitnesses.mock.calls[0]! expect(wallet).toBe(testWallet) expect(chainId).toBe(0) // Witness signatures use chainId 0 @@ -338,7 +338,7 @@ describe('Identity Signer', () => { await identitySigner.witness(mockStateWriter, testWallet) // Extract the payload that was signed - const [, , payload] = mockSaveWitnesses.mock.calls[0] + const [, , payload] = mockSaveWitnesses.mock.calls[0]! // Parse the message content to verify consent structure const messageHex = payload.message @@ -367,7 +367,7 @@ describe('Identity Signer', () => { await identitySigner.witness(mockStateWriter, testWallet, extraData) // Extract and verify extra data was included - const [, , payload] = mockSaveWitnesses.mock.calls[0] + const [, , payload] = mockSaveWitnesses.mock.calls[0]! const messageString = Hex.toString(payload.message) const consentData = JSON.parse(messageString) @@ -435,7 +435,7 @@ describe('Identity Signer', () => { expect(mockSaveWitnesses).toHaveBeenCalledOnce() // Verify witness payload includes extra context - const [, , witnessPayload] = mockSaveWitnesses.mock.calls[0] + const [, , witnessPayload] = mockSaveWitnesses.mock.calls[0]! const witnessMessage = JSON.parse(Hex.toString(witnessPayload.message)) expect(witnessMessage.signatureId).toBe('sig-123') expect(witnessMessage.purpose).toBe('authentication') @@ -469,8 +469,8 @@ describe('Identity Signer', () => { expect(mockSignFn).toHaveBeenCalledTimes(2) // Verify different payloads produce different hashes - const [, messageDigest] = mockSignFn.mock.calls[0] - const [, transactionDigest] = mockSignFn.mock.calls[1] + const [, messageDigest] = mockSignFn.mock.calls[0]! + const [, transactionDigest] = mockSignFn.mock.calls[1]! expect(messageDigest).not.toEqual(transactionDigest) }) }) @@ -500,7 +500,7 @@ describe('Identity Signer', () => { it('Should handle malformed hex signatures', async () => { const digest = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') - mockSignFn.mockResolvedValueOnce('not-a-hex-string') + mockSignFn.mockResolvedValueOnce('not-a-hex-string' as any) await expect(identitySigner.signDigest(digest)).rejects.toThrow() }) diff --git a/packages/wallet/wdk/test/messages.test.ts b/packages/wallet/wdk/test/messages.test.ts index 9420ca0004..32d68ffe5e 100644 --- a/packages/wallet/wdk/test/messages.test.ts +++ b/packages/wallet/wdk/test/messages.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest' -import { Manager, SignerActionable } from '../src/sequence' +import { Manager, SignerActionable } from '../src/sequence/index.js' import { Mnemonic } from 'ox' -import { newManager } from './constants' +import { newManager } from './constants.js' import { Network } from '@0xsequence/wallet-primitives' describe('Messages', () => { @@ -41,12 +41,13 @@ describe('Messages', () => { // Verify message appears in list const messages = await manager.messages.list() expect(messages.length).toBe(1) - expect(messages[0].wallet).toBe(wallet) - expect(messages[0].message).toBe(testMessage) - expect(messages[0].status).toBe('requested') - expect(messages[0].signatureId).toBe(signatureId) - expect(messages[0].source).toBe('unknown') - expect(messages[0].id).toBeDefined() + const message = messages[0]! + expect(message.wallet).toBe(wallet) + expect(message.message).toBe(testMessage) + expect(message.status).toBe('requested') + expect(message.signatureId).toBe(signatureId) + expect(message.source).toBe('unknown') + expect(message.id).toBeDefined() }) it('Should create message request with custom source', async () => { @@ -63,8 +64,11 @@ describe('Messages', () => { const messages = await manager.messages.list() expect(messages.length).toBe(1) - expect(messages[0].source).toBe(customSource) - expect(messages[0].message).toBe(testMessage) + + const message = messages[0]! + + expect(message.source).toBe(customSource) + expect(message.message).toBe(testMessage) }) it('Should get message by ID', async () => { @@ -79,7 +83,7 @@ describe('Messages', () => { const messages = await manager.messages.list() expect(messages.length).toBe(1) - const messageId = messages[0].id + const messageId = messages[0]!.id // Get by message ID const retrievedMessage = await manager.messages.get(messageId) @@ -269,7 +273,7 @@ describe('Messages', () => { const signatureId = await manager.messages.request(wallet!, testMessage) const messages = await manager.messages.list() - const messageId = messages[0].id + const messageId = messages[0]!.id let updateCallCount = 0 let lastMessage: any @@ -315,7 +319,7 @@ describe('Messages', () => { await manager.messages.request(wallet!, testMessage) const messages = await manager.messages.list() - const messageId = messages[0].id + const messageId = messages[0]!.id let callCount = 0 let receivedMessage: any diff --git a/packages/wallet/wdk/test/otp.test.ts b/packages/wallet/wdk/test/otp.test.ts index 1ad4502270..f3ae452094 100644 --- a/packages/wallet/wdk/test/otp.test.ts +++ b/packages/wallet/wdk/test/otp.test.ts @@ -1,13 +1,13 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest' import { Address, Hex } from 'ox' import { Network, Payload } from '@0xsequence/wallet-primitives' import { IdentityInstrument, IdentityType, KeyType, OtpChallenge } from '@0xsequence/identity-instrument' -import { OtpHandler } from '../src/sequence/handlers/otp' -import { Signatures } from '../src/sequence/signatures' -import * as Db from '../src/dbs' -import { IdentitySigner } from '../src/identity/signer' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request' -import { Kinds } from '../src/sequence/types/signer' +import { OtpHandler, PromptOtpHandler } from '../src/sequence/handlers/otp.js' +import { Signatures } from '../src/sequence/signatures.js' +import * as Db from '../src/dbs/index.js' +import { IdentitySigner } from '../src/identity/signer.js' +import { BaseSignatureRequest } from '../src/sequence/types/signature-request.js' +import { Kinds } from '../src/sequence/types/signer.js' // Mock the global crypto API const mockCryptoSubtle = { @@ -69,7 +69,7 @@ describe('OtpHandler', () => { let otpHandler: OtpHandler let testWallet: Address.Address let testRequest: BaseSignatureRequest - let mockPromptOtp: ReturnType + let mockPromptOtp: Mock beforeEach(() => { vi.clearAllMocks() diff --git a/packages/wallet/wdk/test/passkeys.test.ts b/packages/wallet/wdk/test/passkeys.test.ts index 857258c233..199265822b 100644 --- a/packages/wallet/wdk/test/passkeys.test.ts +++ b/packages/wallet/wdk/test/passkeys.test.ts @@ -3,10 +3,10 @@ import { Address, Hex } from 'ox' import { Network, Payload } from '@0xsequence/wallet-primitives' import { Signers, State } from '@0xsequence/wallet-core' import { Extensions } from '@0xsequence/wallet-primitives' -import { PasskeysHandler } from '../src/sequence/handlers/passkeys' -import { Signatures } from '../src/sequence/signatures' -import { BaseSignatureRequest } from '../src/sequence/types/signature-request' -import { Kinds } from '../src/sequence/types/signer' +import { PasskeysHandler } from '../src/sequence/handlers/passkeys.js' +import { Signatures } from '../src/sequence/signatures.js' +import { BaseSignatureRequest } from '../src/sequence/types/signature-request.js' +import { Kinds } from '../src/sequence/types/signer.js' // Mock dependencies with proper vi.fn() types const mockAddSignature = vi.fn() diff --git a/packages/wallet/wdk/test/recovery.test.ts b/packages/wallet/wdk/test/recovery.test.ts index 9401260abf..3ca6da0752 100644 --- a/packages/wallet/wdk/test/recovery.test.ts +++ b/packages/wallet/wdk/test/recovery.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest' -import { Manager, QueuedRecoveryPayload, SignerReady, TransactionDefined } from '../src/sequence' +import { QueuedRecoveryPayload, SignerReady, TransactionDefined } from '../src/sequence/index.js' import { Bytes, Hex, Mnemonic, Provider, RpcTransport } from 'ox' import { Network, Payload } from '@0xsequence/wallet-primitives' -import { LOCAL_RPC_URL, newManager } from './constants' +import { LOCAL_RPC_URL, newManager } from './constants.js' describe('Recovery', () => { it('Should execute a recovery', async () => { @@ -159,7 +159,7 @@ describe('Recovery', () => { expect(tx.status).toBe('defined') expect((tx as TransactionDefined).relayerOptions.length).toBe(1) - const localRelayer = (tx as TransactionDefined).relayerOptions[0] + const localRelayer = (tx as TransactionDefined).relayerOptions[0]! expect(localRelayer).toBeDefined() expect(localRelayer.relayerId).toBe('local') @@ -332,7 +332,7 @@ describe('Recovery', () => { expect(Array.isArray(fetchedPayloads)).toBeTruthy() expect(fetchedPayloads.length).toBe(1) - const fetchedPayload = fetchedPayloads[0] + const fetchedPayload = fetchedPayloads[0]! expect(fetchedPayload).toBeDefined() expect(fetchedPayload.wallet).toBe(wallet) expect(fetchedPayload.chainId).toBe(Network.ChainId.ARBITRUM) @@ -390,8 +390,8 @@ describe('Recovery', () => { expect(updatedPayloads.length).toBe(fetchedPayloads2.length) if (updatedPayloads.length > 0 && fetchedPayloads.length > 0) { - const updated = updatedPayloads[0] - const fetched = fetchedPayloads[0] + const updated = updatedPayloads[0]! + const fetched = fetchedPayloads[0]! expect(updated.id).toBe(fetched.id) expect(updated.wallet).toBe(fetched.wallet) diff --git a/packages/wallet/wdk/test/sessions.test.ts b/packages/wallet/wdk/test/sessions.test.ts index bd1500e5d3..98a762d3e3 100644 --- a/packages/wallet/wdk/test/sessions.test.ts +++ b/packages/wallet/wdk/test/sessions.test.ts @@ -1,59 +1,93 @@ -import { AbiFunction, Address, Bytes, Hex, Mnemonic, Provider, RpcTransport } from 'ox' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { Signers as CoreSigners, Wallet as CoreWallet, Envelope, Relayer, State } from '../../core/src/index.js' -import { Attestation, Constants, Extensions, Network, Payload, Permission } from '../../primitives/src/index.js' +import { AbiFunction, Address, Bytes, Hex, Mnemonic, Provider, RpcTransport, Secp256k1 } from 'ox' +import { beforeEach, describe, expect, it } from 'vitest' +import { Signers as CoreSigners, Wallet as CoreWallet, Envelope, State } from '../../core/src/index.js' +import { ExplicitSession } from '../../core/src/utils/session/types.js' +import { Context, Extensions, Network, Payload, Permission } from '../../primitives/src/index.js' import { Sequence } from '../src/index.js' -import { CAN_RUN_LIVE, EMITTER_ABI, EMITTER_ADDRESS, PRIVATE_KEY, RPC_URL } from './constants' - -describe('Sessions (via Manager)', () => { - // Shared components - let provider: Provider.Provider - let chainId: number - let stateProvider: State.Provider - - // Wallet webapp components - let wdk: { - identitySignerAddress: Address.Address - manager: Sequence.Manager - } - - // Dapp components - let dapp: { - pkStore: CoreSigners.Pk.Encrypted.EncryptedPksDb - wallet: CoreWallet - sessionManager: CoreSigners.SessionManager - } - - const setupExplicitSession = async ( - sessionAddress: Address.Address, - permissions: Permission.SessionPermissions, - isModify = false, - ) => { - let requestId: string - if (isModify) { - requestId = await wdk.manager.sessions.modifyExplicitSession(dapp.wallet.address, sessionAddress, permissions) - } else { - requestId = await wdk.manager.sessions.addExplicitSession(dapp.wallet.address, sessionAddress, permissions) +import { EMITTER_ABI, EMITTER_ADDRESS, LOCAL_RPC_URL } from './constants.js' + +const ALL_EXTENSIONS: { + name: string + extensions: Extensions.Extensions + context: Context.Context + context4337?: Context.Context +}[] = [ + { + name: 'Dev1', + extensions: Extensions.Dev1, + context: Context.Dev1, + }, + { + name: 'Dev2', + extensions: Extensions.Dev2, + context: Context.Dev2, + context4337: Context.Dev2_4337, + }, + { + name: 'Rc3', + extensions: Extensions.Rc3, + context: Context.Rc3, + context4337: Context.Rc3_4337, + }, + { + name: 'Rc4', + extensions: Extensions.Rc4, + context: Context.Rc4, + context4337: Context.Rc4_4337, + }, + { + name: 'Rc5', + extensions: Extensions.Rc5, + context: Context.Rc5, + context4337: Context.Rc5_4337, + }, +] + +for (const extension of ALL_EXTENSIONS) { + describe(`Sessions (via Manager ${extension.name})`, () => { + // Shared components + let provider: Provider.Provider + let chainId: number + let stateProvider: State.Provider + + // Wallet webapp components + let wdk: { + identitySignerAddress: Address.Address + manager: Sequence.Manager } - // Sign and complete the request - const sigRequest = await wdk.manager.signatures.get(requestId) - const identitySigner = sigRequest.signers.find((s) => Address.isEqual(s.address, wdk.identitySignerAddress)) - if (!identitySigner || (identitySigner.status !== 'actionable' && identitySigner.status !== 'ready')) { - throw new Error(`Identity signer not found or not ready/actionable: ${identitySigner?.status}`) + // Dapp components + let dapp: { + pkStore: CoreSigners.Pk.Encrypted.EncryptedPksDb + wallet: CoreWallet + sessionManager: CoreSigners.SessionManager } - const handled = await identitySigner.handle() - if (!handled) { - throw new Error('Failed to handle identity signer') + + const setupExplicitSession = async (explicitSession: ExplicitSession, isModify = false) => { + let requestId: string + if (isModify) { + requestId = await wdk.manager.sessions.modifyExplicitSession(dapp.wallet.address, explicitSession) + } else { + requestId = await wdk.manager.sessions.addExplicitSession(dapp.wallet.address, explicitSession) + } + + // Sign and complete the request + const sigRequest = await wdk.manager.signatures.get(requestId) + const identitySigner = sigRequest.signers.find((s) => Address.isEqual(s.address, wdk.identitySignerAddress)) + if (!identitySigner || (identitySigner.status !== 'actionable' && identitySigner.status !== 'ready')) { + throw new Error(`Identity signer not found or not ready/actionable: ${identitySigner?.status}`) + } + const handled = await identitySigner.handle() + if (!handled) { + throw new Error('Failed to handle identity signer') + } + await wdk.manager.sessions.complete(requestId) } - await wdk.manager.sessions.complete(requestId) - } - beforeEach(async () => { - // Create provider or use arbitrum sepolia - if (RPC_URL) { + beforeEach(async () => { + // Create provider using LOCAL_RPC_URL provider = Provider.from( - RpcTransport.fromHttp(RPC_URL, { + RpcTransport.fromHttp(LOCAL_RPC_URL, { fetchOptions: { headers: { 'x-requested-with': 'XMLHttpRequest', @@ -62,129 +96,136 @@ describe('Sessions (via Manager)', () => { }), ) chainId = Number(await provider.request({ method: 'eth_chainId' })) - } else { - provider = vi.mocked({ - request: vi.fn(), - on: vi.fn(), - removeListener: vi.fn(), - }) - chainId = Network.ChainId.MAINNET - } - // Create state provider - stateProvider = new State.Local.Provider() - - // Create manager - const opts = Sequence.applyManagerOptionsDefaults({ - stateProvider, - relayers: [], // No relayers needed for testing - networks: [ - { - chainId, - type: Network.NetworkType.MAINNET, - rpcUrl: RPC_URL ?? 'XXX', - name: 'XXX', - blockExplorer: { url: 'XXX' }, - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18, + // Create state provider + stateProvider = new State.Local.Provider() + + // Create manager + const opts = Sequence.applyManagerOptionsDefaults({ + stateProvider, + extensions: extension.extensions, + context: extension.context, + context4337: extension.context4337 ?? extension.context, + relayers: [], // No relayers needed for testing + networks: [ + { + chainId, + type: Network.NetworkType.MAINNET, + rpcUrl: LOCAL_RPC_URL, + name: 'XXX', + blockExplorer: { url: 'XXX' }, + nativeCurrency: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + }, }, - }, - ], - }) + ], + }) - // Create manager - const manager = new Sequence.Manager(opts) - - // Use a mnemonic to create the wallet - const identitySignerMnemonic = Mnemonic.random(Mnemonic.english) - const identitySignerPk = Mnemonic.toPrivateKey(identitySignerMnemonic, { as: 'Hex' }) - const identitySignerAddress = new CoreSigners.Pk.Pk(identitySignerPk).address - const walletAddress = await manager.wallets.signUp({ - kind: 'mnemonic', - mnemonic: identitySignerMnemonic, - noGuard: true, - noSessionManager: false, - }) - if (!walletAddress) { - throw new Error('Failed to create wallet') - } + // Create manager + const manager = new Sequence.Manager(opts) - // Initialize the wdk components - wdk = { - identitySignerAddress, - manager, - } - manager.registerMnemonicUI(async (respond) => { - await respond(identitySignerMnemonic) - }) + // Use a mnemonic to create the wallet + const identitySignerMnemonic = Mnemonic.random(Mnemonic.english) + const identitySignerPk = Mnemonic.toPrivateKey(identitySignerMnemonic, { as: 'Hex' }) + const identitySignerAddress = new CoreSigners.Pk.Pk(identitySignerPk).address + const walletAddress = await manager.wallets.signUp({ + kind: 'mnemonic', + mnemonic: identitySignerMnemonic, + noGuard: true, + noSessionManager: false, + }) + if (!walletAddress) { + throw new Error('Failed to create wallet') + } + + // Initialize the wdk components + wdk = { + identitySignerAddress, + manager, + } + manager.registerMnemonicUI(async (respond) => { + await respond(identitySignerMnemonic) + }) - // Create the pk store and pk - const pkStore = new CoreSigners.Pk.Encrypted.EncryptedPksDb() + // Create the pk store and pk + const pkStore = new CoreSigners.Pk.Encrypted.EncryptedPksDb() - // Create wallet in core - const coreWallet = new CoreWallet(walletAddress, { - guest: opts.guest, - // Share the state provider with wdk. In practice this will be the key machine. - stateProvider, + // Create wallet in core + const coreWallet = new CoreWallet(walletAddress, { + guest: opts.guest, + // Share the state provider with wdk. In practice this will be the key machine. + stateProvider, + }) + + dapp = { + pkStore, + wallet: coreWallet, + sessionManager: new CoreSigners.SessionManager(coreWallet, { + provider, + sessionManagerAddress: extension.extensions.sessions, + }), + } }) - dapp = { - pkStore, - wallet: coreWallet, - sessionManager: new CoreSigners.SessionManager(coreWallet, { - provider, - sessionManagerAddress: Extensions.Rc3.sessions, - }), - } - }) + const signAndSend = async (call: Payload.Call) => { + const envelope = await dapp.wallet.prepareTransaction(provider, [call], { noConfigUpdate: true }) + const parentedEnvelope: Payload.Parented = { + ...envelope.payload, + parentWallets: [dapp.wallet.address], + } - const signAndSend = async (call: Payload.Call) => { - const envelope = await dapp.wallet.prepareTransaction(provider, [call], { noConfigUpdate: true }) - const parentedEnvelope: Payload.Parented = { - ...envelope.payload, - parentWallets: [dapp.wallet.address], - } + // Sign the envelope + const sessionImageHash = await dapp.sessionManager.imageHash + if (!sessionImageHash) { + throw new Error('Session image hash not found') + } + const signature = await dapp.sessionManager.signSapient( + dapp.wallet.address, + chainId ?? 1n, + parentedEnvelope, + sessionImageHash, + ) + const sapientSignature: Envelope.SapientSignature = { + imageHash: sessionImageHash, + signature, + } + const signedEnvelope = Envelope.toSigned(envelope, [sapientSignature]) - // Sign the envelope - const sessionImageHash = await dapp.sessionManager.imageHash - if (!sessionImageHash) { - throw new Error('Session image hash not found') - } - const signature = await dapp.sessionManager.signSapient( - dapp.wallet.address, - chainId ?? 1n, - parentedEnvelope, - sessionImageHash, - ) - const sapientSignature: Envelope.SapientSignature = { - imageHash: sessionImageHash, - signature, - } - const signedEnvelope = Envelope.toSigned(envelope, [sapientSignature]) - - // Build the transaction - const transaction = await dapp.wallet.buildTransaction(provider, signedEnvelope) - console.log('tx', transaction) - - // Send the transaction - if (CAN_RUN_LIVE && PRIVATE_KEY) { - // Load the sender - const senderPk = Hex.from(PRIVATE_KEY as `0x${string}`) - const pkRelayer = new Relayer.Standard.PkRelayer(senderPk, provider) - const tx = await pkRelayer.relay(transaction.to, transaction.data, chainId, undefined) - console.log('Transaction sent', tx) + // Build the transaction + const transaction = await dapp.wallet.buildTransaction(provider, signedEnvelope) + console.log('tx', transaction) + + // Generate and use a random sender address to prevent race conditions + const senderAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) + await provider.request({ + method: 'anvil_setBalance', + params: [senderAddress, Hex.fromNumber(1000000000000000000n)], + }) + await provider.request({ + method: 'anvil_impersonateAccount', + params: [senderAddress], + }) + + // Send the transaction + const txHash = await provider.request({ + method: 'eth_sendTransaction', + params: [ + { + ...transaction, + from: senderAddress, + }, + ], + }) + console.log('Transaction sent', txHash) await new Promise((resolve) => setTimeout(resolve, 3000)) - const receipt = await provider.request({ method: 'eth_getTransactionReceipt', params: [tx.opHash] }) + const receipt = await provider.request({ method: 'eth_getTransactionReceipt', params: [txHash] }) console.log('Transaction receipt', receipt) - return tx.opHash + return txHash } - } - it( - 'should add the session manager leaf when not present', - async () => { + it('should add the session manager leaf when not present', { timeout: 60000 }, async () => { // Recreate the wallet specifically for this test const identitySignerMnemonic = Mnemonic.random(Mnemonic.english) const identitySignerPk = Mnemonic.toPrivateKey(identitySignerMnemonic, { as: 'Hex' }) @@ -213,7 +254,7 @@ describe('Sessions (via Manager)', () => { dapp.wallet = coreWallet dapp.sessionManager = new CoreSigners.SessionManager(coreWallet, { provider, - sessionManagerAddress: Extensions.Rc3.sessions, + sessionManagerAddress: extension.extensions.sessions, }) // At this point the wallet should NOT have a session topology @@ -225,8 +266,9 @@ describe('Sessions (via Manager)', () => { if (!s) { throw new Error('Failed to create pk store') } - const permission: Permission.SessionPermissions = { - signer: e.address, + const explicitSession: ExplicitSession = { + type: 'explicit', + sessionAddress: e.address, chainId, valueLimit: 0n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now @@ -237,11 +279,11 @@ describe('Sessions (via Manager)', () => { }, ], } - const explicitSigner = new CoreSigners.Session.Explicit(s, permission) + const explicitSigner = new CoreSigners.Session.Explicit(s, explicitSession) // Add to manager dapp.sessionManager = dapp.sessionManager.withExplicitSigner(explicitSigner) - await setupExplicitSession(explicitSigner.address, permission) + await setupExplicitSession(explicitSession) // Create a call payload const call: Payload.Call = { @@ -254,44 +296,20 @@ describe('Sessions (via Manager)', () => { behaviorOnError: 'revert', } - if (!RPC_URL) { - // Configure mock provider - ;(provider as any).request.mockImplementation(({ method, params }) => { - if (method === 'eth_chainId') { - return Promise.resolve(chainId.toString()) - } - if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) { - // Undeployed wallet - return Promise.resolve('0x') - } - if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.READ_NONCE, [0n])) { - // Nonce is 0 - return Promise.resolve('0x00') - } - if (method === 'eth_call' && params[0].data?.startsWith(AbiFunction.getSelector(Constants.GET_LIMIT_USAGE))) { - // Return 0 for usage limit (no usage yet) - return Promise.resolve('0x0000000000000000000000000000000000000000000000000000000000000000') - } - }) - } - // Sign and send the transaction await signAndSend(call) - }, - PRIVATE_KEY || RPC_URL ? { timeout: 60000 } : undefined, - ) + }) - it( - 'should create and sign with an explicit session', - async () => { + it('should create and sign with an explicit session', { timeout: 60000 }, async () => { // Create the explicit session signer const e = await dapp.pkStore.generateAndStore() const s = await dapp.pkStore.getEncryptedPkStore(e.address) if (!s) { throw new Error('Failed to create pk store') } - const permission: Permission.SessionPermissions = { - signer: e.address, + const explicitSession: ExplicitSession = { + type: 'explicit', + sessionAddress: e.address, chainId, valueLimit: 0n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now @@ -311,11 +329,11 @@ describe('Sessions (via Manager)', () => { }, ], } - const explicitSigner = new CoreSigners.Session.Explicit(s, permission) + const explicitSigner = new CoreSigners.Session.Explicit(s, explicitSession) // Add to manager dapp.sessionManager = dapp.sessionManager.withExplicitSigner(explicitSigner) - await setupExplicitSession(explicitSigner.address, permission) + await setupExplicitSession(explicitSession) // Create a call payload const call: Payload.Call = { @@ -328,36 +346,11 @@ describe('Sessions (via Manager)', () => { behaviorOnError: 'revert', } - if (!RPC_URL) { - // Configure mock provider - ;(provider as any).request.mockImplementation(({ method, params }) => { - if (method === 'eth_chainId') { - return Promise.resolve(chainId.toString()) - } - if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) { - // Undeployed wallet - return Promise.resolve('0x') - } - if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.READ_NONCE, [0n])) { - // Nonce is 0 - return Promise.resolve('0x00') - } - if (method === 'eth_call' && params[0].data?.startsWith(AbiFunction.getSelector(Constants.GET_LIMIT_USAGE))) { - // Return 0 for usage limit (no usage yet) - return Promise.resolve('0x0000000000000000000000000000000000000000000000000000000000000000') - } - }) - } - // Sign and send the transaction await signAndSend(call) - }, - PRIVATE_KEY || RPC_URL ? { timeout: 60000 } : undefined, - ) + }) - it( - 'should modify an explicit session permission', - async () => { + it('should modify an explicit session permission', { timeout: 60000 }, async () => { // First we create the explicit sessions signer const e = await dapp.pkStore.generateAndStore() const s = await dapp.pkStore.getEncryptedPkStore(e.address) @@ -365,8 +358,9 @@ describe('Sessions (via Manager)', () => { throw new Error('Failed to create pk store') } // Create the initial permissions - let permission: Permission.SessionPermissions = { - signer: e.address, + let explicitSession: ExplicitSession = { + type: 'explicit', + sessionAddress: e.address, chainId, valueLimit: 0n, deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now @@ -386,11 +380,11 @@ describe('Sessions (via Manager)', () => { }, ], } - const explicitSigner = new CoreSigners.Session.Explicit(s, permission) + const explicitSigner = new CoreSigners.Session.Explicit(s, explicitSession) // Add to manager dapp.sessionManager = dapp.sessionManager.withExplicitSigner(explicitSigner) - await setupExplicitSession(explicitSigner.address, permission) + await setupExplicitSession(explicitSession) // Create a call payload const call: Payload.Call = { @@ -403,46 +397,21 @@ describe('Sessions (via Manager)', () => { behaviorOnError: 'revert', } - if (!RPC_URL) { - // Configure mock provider - ;(provider as any).request.mockImplementation(({ method, params }) => { - if (method === 'eth_chainId') { - return Promise.resolve(chainId.toString()) - } - if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) { - // Undeployed wallet - return Promise.resolve('0x') - } - if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.READ_NONCE, [0n])) { - // Nonce is 0 - return Promise.resolve('0x00') - } - if (method === 'eth_call' && params[0].data?.startsWith(AbiFunction.getSelector(Constants.GET_LIMIT_USAGE))) { - // Return 0 for usage limit (no usage yet) - return Promise.resolve('0x0000000000000000000000000000000000000000000000000000000000000000') - } - }) - } - // Sign and send the transaction await signAndSend(call) // Now we modify the permissions target contract to zero address // This should cause any session call to the EMITTER_ADDRESS contract to fail - permission.permissions[0].target = '0x0000000000000000000000000000000000000000' + explicitSession.permissions[0]!.target = '0x0000000000000000000000000000000000000000' - await setupExplicitSession(explicitSigner.address, permission, true) + await setupExplicitSession(explicitSession, true) // Sign and send the transaction // Should fail with 'No signer supported for call' await expect(signAndSend(call)).rejects.toThrow('No signer supported for call') - }, - PRIVATE_KEY || RPC_URL ? { timeout: 60000 } : undefined, - ) + }) - it( - 'should create and sign with an implicit session', - async () => { + it('should create and sign with an implicit session', { timeout: 60000 }, async () => { // Create the implicit session signer const e = await dapp.pkStore.generateAndStore() const s = await dapp.pkStore.getEncryptedPkStore(e.address) @@ -490,37 +459,8 @@ describe('Sessions (via Manager)', () => { behaviorOnError: 'revert', } - if (!RPC_URL) { - // Configure mock provider - ;(provider as any).request.mockImplementation(({ method, params }) => { - if (method === 'eth_chainId') { - return Promise.resolve(chainId.toString()) - } - if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) { - // Undeployed wallet - return Promise.resolve('0x') - } - if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.READ_NONCE, [0n])) { - // Nonce is 0 - return Promise.resolve('0x00') - } - if ( - method === 'eth_call' && - Address.isEqual(params[0].from, dapp.sessionManager.address) && - Address.isEqual(params[0].to, call.to) - ) { - // Implicit request simulation result - const expectedResult = Bytes.toHex( - Attestation.generateImplicitRequestMagic(attestation, dapp.wallet.address), - ) - return Promise.resolve(expectedResult) - } - }) - } - // Sign and send the transaction await signAndSend(call) - }, - PRIVATE_KEY || RPC_URL ? { timeout: 60000 } : undefined, - ) -}) + }) + }) +} diff --git a/packages/wallet/wdk/test/setup.ts b/packages/wallet/wdk/test/setup.ts index 70482040c0..4aa336a55a 100644 --- a/packages/wallet/wdk/test/setup.ts +++ b/packages/wallet/wdk/test/setup.ts @@ -14,7 +14,7 @@ import { } from 'fake-indexeddb' import { Provider, RpcTransport } from 'ox' import { vi } from 'vitest' -import { LOCAL_RPC_URL } from './constants' +import { LOCAL_RPC_URL } from './constants.js' // Add IndexedDB support to the test environment using fake-indexeddb global.indexedDB = indexedDB diff --git a/packages/wallet/wdk/test/signers-kindof.test.ts b/packages/wallet/wdk/test/signers-kindof.test.ts new file mode 100644 index 0000000000..4e5f83aad5 --- /dev/null +++ b/packages/wallet/wdk/test/signers-kindof.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it, vi } from 'vitest' + +import { Kinds } from '../src/sequence/index.js' +import { newManager } from './constants.js' + +describe('Signers.kindOf', () => { + it('does not probe Sessions/Witness for non-witnessable signers', async () => { + const getWitnessFor = vi.fn().mockResolvedValue(undefined) + const getWitnessForSapient = vi.fn().mockResolvedValue(undefined) + + const manager = newManager({ + stateProvider: { + getWitnessFor, + getWitnessForSapient, + } as any, + }) + + const signers = (manager as any).shared.modules.signers + const extensions = (manager as any).shared.sequence.extensions + + const wallet = '0x1111111111111111111111111111111111111111' + const imageHash = ('0x' + '00'.repeat(32)) as `0x${string}` + + // Sessions extension signer (sapient leaf) never publishes a witness. + await signers.kindOf(wallet, extensions.sessions, imageHash) + + // Passkeys module is a known sapient signer kind. + expect(await signers.kindOf(wallet, extensions.passkeys, imageHash)).toBe(Kinds.LoginPasskey) + + // Sequence dev multisig (default guard topology leaf) never publishes a witness. + await signers.kindOf(wallet, '0x007a47e6BF40C1e0ed5c01aE42fDC75879140bc4') + + expect(getWitnessFor).not.toHaveBeenCalled() + expect(getWitnessForSapient).not.toHaveBeenCalled() + + // Unknown signers still rely on a witness probe. + await signers.kindOf(wallet, '0x2222222222222222222222222222222222222222') + expect(getWitnessFor).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/wallet/wdk/test/test-ssr-safety.mjs b/packages/wallet/wdk/test/test-ssr-safety.mjs new file mode 100644 index 0000000000..55ef7723db --- /dev/null +++ b/packages/wallet/wdk/test/test-ssr-safety.mjs @@ -0,0 +1,308 @@ +#!/usr/bin/env node +/** + * Comprehensive SSR Safety Test (Runtime Execution) + * + * This script tests that the entire wdk package can be imported and used in a Node.js + * environment (SSR context) without throwing errors about missing window. + * + * It executes the code at runtime to catch any SSR issues. + * + * Run with: node test-ssr-comprehensive.mjs + */ + +import { readFile } from 'fs/promises' +import { fileURLToPath } from 'url' +import { dirname, join } from 'path' +import { createRequire } from 'module' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) +const require = createRequire(import.meta.url) + +console.log('Testing SSR safety with runtime execution...\n') + +// Ensure we're in a Node.js environment (no window) +if (typeof window !== 'undefined') { + console.error('ERROR: window is defined! This should not happen in Node.js.') + process.exit(1) +} + +console.log('✓ window is undefined (as expected in Node.js)\n') + +const errors = [] +const warnings = [] + +// Read package.json to get package name and exports +let packageJson +try { + const packageJsonPath = join(__dirname, '..', 'package.json') + packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8')) +} catch (err) { + console.error('Failed to read package.json:', err.message) + process.exit(1) +} + +// Test 1: Import main module via package name +console.log('='.repeat(60)) +console.log('Test 1: Importing package via package name') +console.log('='.repeat(60)) + +let wdk +try { + // Use the package name from package.json + const packageName = packageJson.name + console.log(`Importing ${packageName}...`) + + // Try to resolve the package + const packagePath = require.resolve(packageName) + console.log(` Package resolved to: ${packagePath}`) + + // Import the package + wdk = await import(packageName) + console.log('✓ Successfully imported package') + console.log(' Top-level exports:', Object.keys(wdk)) + +} catch (error) { + // Check if it's an SSR-related error + if (error.message.includes('window is not defined') || + error.message.includes('window') || + error.message.includes('document is not defined') || + error.message.includes('document') || + error.message.includes('localStorage') || + error.message.includes('sessionStorage')) { + errors.push(`SSR ERROR: Package accesses browser globals at module load time: ${error.message}`) + if (error.stack) { + console.error('\nError stack:') + console.error(error.stack) + } + } else { + errors.push(`Failed to import package: ${error.message}`) + if (error.stack) { + console.error('Stack:', error.stack) + } + } + + // Don't exit immediately - let the summary show the error + if (errors.length > 0) { + // Skip remaining tests if import failed + wdk = null + } +} + +// Test 2: Recursively access and test all exports +console.log('\n' + '='.repeat(60)) +console.log('Test 2: Accessing and testing all exports') +console.log('='.repeat(60)) + +if (!wdk) { + console.log('Skipping - package import failed') +} else { + async function testExports(obj, path = '', depth = 0) { + if (depth > 5) return // Prevent infinite recursion + + for (const [key, value] of Object.entries(obj)) { + const currentPath = path ? `${path}.${key}` : key + + try { + // Skip if it's a circular reference or already tested + if (value === null || value === undefined) { + continue + } + + // Test accessing the value (this executes any getters) + const accessed = value + + // Test different types + if (typeof accessed === 'function') { + // Try to get function properties + try { + const props = Object.getOwnPropertyNames(accessed) + if (props.length > 0 && depth < 3) { + // Test static properties on functions + for (const prop of props.slice(0, 3)) { + try { + const propValue = accessed[prop] + if (typeof propValue === 'object' && propValue !== null && depth < 2) { + await testExports(propValue, `${currentPath}.${prop}`, depth + 1) + } + } catch (err) { + if (err.message.includes('window') || err.message.includes('document')) { + errors.push(`${currentPath}.${prop}: ${err.message}`) + } + } + } + } + } catch (err) { + if (err.message.includes('window') || err.message.includes('document')) { + errors.push(`${currentPath}: ${err.message}`) + } + } + } else if (typeof accessed === 'object' && accessed !== null) { + // Test object properties + if (Array.isArray(accessed)) { + // Test array elements + for (let i = 0; i < Math.min(accessed.length, 3); i++) { + try { + const item = accessed[i] + if (typeof item === 'object' && item !== null && depth < 3) { + await testExports(item, `${currentPath}[${i}]`, depth + 1) + } + } catch (err) { + if (err.message.includes('window') || err.message.includes('document')) { + errors.push(`${currentPath}[${i}]: ${err.message}`) + } + } + } + } else { + // Test object properties recursively + await testExports(accessed, currentPath, depth + 1) + } + } + + } catch (error) { + // Check if it's an SSR-related error + if (error.message.includes('window is not defined') || + error.message.includes('window') || + error.message.includes('document is not defined') || + error.message.includes('document') || + error.message.includes('localStorage') || + error.message.includes('sessionStorage')) { + errors.push(`${currentPath}: ${error.message}`) + } else { + // Other errors are warnings (might be expected, like missing dependencies) + warnings.push(`${currentPath}: ${error.message}`) + } + } + } +} + + // Test all top-level exports + console.log('Testing all exports recursively...') + await testExports(wdk) +} + +// Test 3: Try to access specific critical exports and use them +console.log('\n' + '='.repeat(60)) +console.log('Test 3: Testing critical exports with actual usage') +console.log('='.repeat(60)) + +if (!wdk) { + console.log('Skipping - package import failed') +} else { + // Test ManagerOptionsDefaults + try { + if (wdk.Sequence?.ManagerOptionsDefaults) { + console.log('Testing ManagerOptionsDefaults...') + const defaults = wdk.Sequence.ManagerOptionsDefaults + + // Access all properties + Object.keys(defaults).forEach(key => { + try { + const value = defaults[key] + console.log(` ✓ ${key}: ${typeof value}`) + + // If it's a function, try calling it + if (typeof value === 'function' && key === 'relayers') { + const result = value() + console.log(` Called ${key}(), returned:`, Array.isArray(result) ? `${result.length} items` : typeof result) + } + } catch (err) { + if (err.message.includes('window') || err.message.includes('document')) { + errors.push(`ManagerOptionsDefaults.${key}: ${err.message}`) + } + } + }) + } +} catch (err) { + if (err.message.includes('window') || err.message.includes('document')) { + errors.push(`ManagerOptionsDefaults: ${err.message}`) + } +} + +// Test applyManagerOptionsDefaults function +try { + if (wdk.Sequence?.applyManagerOptionsDefaults) { + console.log('Testing applyManagerOptionsDefaults...') + const result = wdk.Sequence.applyManagerOptionsDefaults() + console.log(' ✓ Function executed successfully') + console.log(' Result keys:', Object.keys(result).slice(0, 5).join(', '), '...') + } +} catch (err) { + if (err.message.includes('window') || err.message.includes('document')) { + errors.push(`applyManagerOptionsDefaults: ${err.message}`) + } + } +} + +// Test 4: Try importing sub-modules that might be imported separately +console.log('\n' + '='.repeat(60)) +console.log('Test 4: Testing sub-module imports') +console.log('='.repeat(60)) + +if (!wdk) { + console.log('Skipping - package import failed') +} else { + // Get the package path and try importing from dist + try { + const packagePath = require.resolve(packageJson.name) + const packageDir = dirname(packagePath) + + // Try to import from the exports field if available + if (packageJson.exports) { + for (const [exportPath, exportConfig] of Object.entries(packageJson.exports)) { + if (exportPath === '.') { + const modulePath = exportConfig.default || exportConfig.types + if (modulePath) { + try { + const fullPath = join(packageDir, '..', modulePath) + console.log(`Testing import from ${exportPath}...`) + const subModule = await import(fullPath) + console.log(` ✓ Imported successfully`) + + // Test accessing exports + const subExports = Object.keys(subModule) + if (subExports.length > 0) { + console.log(` Exports: ${subExports.slice(0, 5).join(', ')}${subExports.length > 5 ? '...' : ''}`) + } + } catch (err) { + if (err.message.includes('window') || err.message.includes('document')) { + errors.push(`Import ${exportPath}: ${err.message}`) + } else if (!err.message.includes('Cannot find module')) { + warnings.push(`Import ${exportPath}: ${err.message}`) + } + } + } + } + } + } + } catch (err) { + warnings.push(`Could not test sub-modules: ${err.message}`) + } +} + +// Summary +console.log('\n' + '='.repeat(60)) +console.log('Test Summary') +console.log('='.repeat(60)) + +if (errors.length === 0) { + console.log('\n✅ All SSR Safety Tests PASSED!') + console.log('The package can be safely imported and used in a Node.js/SSR environment.') + if (warnings.length > 0) { + console.log(`\n⚠️ ${warnings.length} warning(s) (non-SSR related):`) + warnings.slice(0, 5).forEach(warn => console.log(` - ${warn}`)) + if (warnings.length > 5) { + console.log(` ... and ${warnings.length - 5} more`) + } + } + process.exit(0) +} else { + console.log('\n❌ ERRORS FOUND:') + errors.forEach(err => console.log(` - ${err}`)) + console.log('\n❌ SSR Safety Test FAILED!') + if (warnings.length > 0) { + console.log(`\n⚠️ ${warnings.length} warning(s):`) + warnings.slice(0, 5).forEach(warn => console.log(` - ${warn}`)) + } + process.exit(1) +} diff --git a/packages/wallet/wdk/test/transactions.test.ts b/packages/wallet/wdk/test/transactions.test.ts index 91bffa56ac..910511e7a9 100644 --- a/packages/wallet/wdk/test/transactions.test.ts +++ b/packages/wallet/wdk/test/transactions.test.ts @@ -1,7 +1,13 @@ import { afterEach, describe, expect, it, vi } from 'vitest' -import { Manager, SignerActionable, Transaction, TransactionDefined, TransactionRelayed } from '../src/sequence' +import { + Manager, + SignerActionable, + Transaction, + TransactionDefined, + TransactionRelayed, +} from '../src/sequence/index.js' import { Address, Hex, Mnemonic, Provider, RpcTransport } from 'ox' -import { LOCAL_RPC_URL, newManager } from './constants' +import { LOCAL_RPC_URL, newManager } from './constants.js' import { Payload, Network } from '@0xsequence/wallet-primitives' describe('Transactions', () => { @@ -48,9 +54,9 @@ describe('Transactions', () => { } expect(tx.relayerOptions.length).toBe(1) - expect(tx.relayerOptions[0].id).toBeDefined() + expect(tx.relayerOptions[0]!.id).toBeDefined() - const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0]!.id) expect(sigId).toBeDefined() tx = await manager.transactions.get(txId!) @@ -161,9 +167,9 @@ describe('Transactions', () => { } expect(tx.relayerOptions.length).toBe(1) - expect(tx.relayerOptions[0].id).toBeDefined() + expect(tx.relayerOptions[0]!.id).toBeDefined() - const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0]!.id) expect(sigId).toBeDefined() tx = await manager.transactions.get(txId!) @@ -230,11 +236,12 @@ describe('Transactions', () => { expect(calledTimes).toBe(1) expect(transactions.length).toBe(1) - expect(transactions[0].status).toBe('requested') - expect(transactions[0].wallet).toBe(wallet!) - expect(transactions[0].requests.length).toBe(1) - expect(transactions[0].requests[0].to).toEqual(to) - expect(transactions[0].requests[0].value).toEqual(9n) + const tx = transactions[0]! + expect(tx.status).toBe('requested') + expect(tx.wallet).toBe(wallet!) + expect(tx.requests.length).toBe(1) + expect(tx.requests[0]!.to).toEqual(to) + expect(tx.requests[0]!.value).toEqual(9n) }) it('Should call onTransactionUpdate when a transaction is defined, relayer selected and relayed', async () => { @@ -273,12 +280,12 @@ describe('Transactions', () => { expect(tx!.status).toBe('defined') expect(tx!.wallet).toBe(wallet!) expect(tx!.requests.length).toBe(1) - expect(tx!.requests[0].to).toEqual(to) - expect(tx!.requests[0].value).toBeUndefined() - expect(tx!.requests[0].gasLimit).toBeUndefined() - expect(tx!.requests[0].data).toBeUndefined() + expect(tx!.requests[0]!.to).toEqual(to) + expect(tx!.requests[0]!.value).toBeUndefined() + expect(tx!.requests[0]!.gasLimit).toBeUndefined() + expect(tx!.requests[0]!.data).toBeUndefined() - const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) expect(sigId).toBeDefined() while (calledTimes < 2) { @@ -374,7 +381,7 @@ describe('Transactions', () => { expect(tx).toBeDefined() expect(tx!.status).toBe('defined') - const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) expect(sigId).toBeDefined() await manager.transactions.delete(txId!) @@ -433,12 +440,12 @@ describe('Transactions', () => { // The first call should be to the random address // and the second one should be a call to self - const call1 = (tx.envelope.payload as Payload.Calls).calls[0] - const call2 = (tx.envelope.payload as Payload.Calls).calls[1] + const call1 = (tx.envelope.payload as Payload.Calls).calls[0]! + const call2 = (tx.envelope.payload as Payload.Calls).calls[1]! expect(call1.to).toEqual(randomAddress) expect(call2.to).toEqual(wallet) - const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId!, (tx as TransactionDefined).relayerOptions[0]!.id) expect(sigId).toBeDefined() tx = await manager.transactions.get(txId!) @@ -540,9 +547,10 @@ describe('Transactions', () => { // Should now have one transaction expect(transactionsList.length).toBe(1) - expect(transactionsList[0].id).toBe(txId) - expect(transactionsList[0].status).toBe('requested') - expect(transactionsList[0].wallet).toBe(wallet) + const tx = transactionsList[0]! + expect(tx.id).toBe(txId) + expect(tx.status).toBe('requested') + expect(tx.wallet).toBe(wallet) unsubscribe() }) @@ -577,7 +585,7 @@ describe('Transactions', () => { expect(callCount).toBe(1) expect(receivedTransactions.length).toBe(1) - expect(receivedTransactions[0].id).toBe(txId) + expect(receivedTransactions[0]!.id).toBe(txId) unsubscribe() }) @@ -697,8 +705,8 @@ describe('Transactions', () => { const tx = await manager.transactions.get(txId) expect(tx.status).toBe('defined') - expect(tx.envelope.payload.calls[0].gasLimit).toBe(50000n) - expect(tx.envelope.payload.calls[1].gasLimit).toBe(75000n) + expect(tx.envelope.payload.calls[0]!.gasLimit).toBe(50000n) + expect(tx.envelope.payload.calls[1]!.gasLimit).toBe(75000n) }) it('Should throw error when defining transaction not in requested state', async () => { @@ -780,8 +788,8 @@ describe('Transactions', () => { expect(tx.status).toBe('requested') expect(tx.source).toBe('test-dapp') expect(tx.envelope.payload.space).toBe(customSpace) - expect(tx.requests[0].data).toBe('0x1234') - expect(tx.requests[0].gasLimit).toBe(21000n) + expect(tx.requests[0]!.data).toBe('0x1234') + expect(tx.requests[0]!.gasLimit).toBe(21000n) }) it('Should throw error for unknown network', async () => { @@ -820,12 +828,12 @@ describe('Transactions', () => { const tx = await manager.transactions.get(txId) expect(tx.status).toBe('requested') - expect(tx.envelope.payload.calls[0].value).toBe(0n) - expect(tx.envelope.payload.calls[0].data).toBe('0x') - expect(tx.envelope.payload.calls[0].gasLimit).toBe(0n) - expect(tx.envelope.payload.calls[0].delegateCall).toBe(false) - expect(tx.envelope.payload.calls[0].onlyFallback).toBe(false) - expect(tx.envelope.payload.calls[0].behaviorOnError).toBe('revert') + expect(tx.envelope.payload.calls[0]!.value).toBe(0n) + expect(tx.envelope.payload.calls[0]!.data).toBe('0x') + expect(tx.envelope.payload.calls[0]!.gasLimit).toBe(0n) + expect(tx.envelope.payload.calls[0]!.delegateCall).toBe(false) + expect(tx.envelope.payload.calls[0]!.onlyFallback).toBe(false) + expect(tx.envelope.payload.calls[0]!.behaviorOnError).toBe('revert') }) it('Should handle relay with signature ID instead of transaction ID', async () => { @@ -856,7 +864,7 @@ describe('Transactions', () => { throw new Error('Transaction not defined') } - const sigId = await manager.transactions.selectRelayer(txId, tx.relayerOptions[0].id) + const sigId = await manager.transactions.selectRelayer(txId, tx.relayerOptions[0]!.id) // Sign the transaction const sigRequest = await manager.signatures.get(sigId) diff --git a/packages/wallet/wdk/test/wallets.test.ts b/packages/wallet/wdk/test/wallets.test.ts index 45db676131..99e71d73d6 100644 --- a/packages/wallet/wdk/test/wallets.test.ts +++ b/packages/wallet/wdk/test/wallets.test.ts @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it } from 'vitest' -import { Manager, SignerActionable, SignerReady } from '../src/sequence' +import { Manager, SignerActionable, SignerReady } from '../src/sequence/index.js' import { Mnemonic, Address } from 'ox' -import { newManager } from './constants' -import { Network } from '@0xsequence/wallet-primitives' +import { newManager } from './constants.js' +import { Config, Constants, Network } from '@0xsequence/wallet-primitives' describe('Wallets', () => { let manager: Manager | undefined @@ -65,7 +65,7 @@ describe('Wallets', () => { const walletsAfterFirst = await manager.wallets.list() expect(walletsAfterFirst.length).toBe(1) - expect(walletsAfterFirst[0].address).toBe(wallet1) + expect(walletsAfterFirst[0]!.address).toBe(wallet1) }) // === WALLET SELECTOR REGISTRATION === @@ -283,14 +283,14 @@ describe('Wallets', () => { expect(config.devices).toBeDefined() expect(config.devices.length).toBe(1) - expect(config.devices[0].kind).toBe('local-device') - expect(config.devices[0].address).toBeDefined() + expect(config.devices[0]!.kind).toBe('local-device') + expect(config.devices[0]!.address).toBeDefined() expect(config.login).toBeDefined() expect(config.login.length).toBe(1) - expect(config.login[0].kind).toBe('login-mnemonic') + expect(config.login[0]!.kind).toBe('login-mnemonic') - expect(config.guard).not.toBeDefined() // No guard for noGuard: true + expect(config.walletGuard).not.toBeDefined() // No guard for noGuard: true expect(config.raw).toBeDefined() expect(config.raw.loginTopology).toBeDefined() @@ -298,6 +298,99 @@ describe('Wallets', () => { expect(config.raw.modules).toBeDefined() }) + it('Should include guard configuration when enabled', async () => { + manager = newManager(undefined, undefined, `guard_enabled_${Date.now()}`) + const guardAddress = (manager as any).shared.sequence.guardAddresses.wallet + const sessionsGuardAddress = (manager as any).shared.sequence.guardAddresses.sessions + const sessionsModuleAddress = (manager as any).shared.sequence.extensions.sessions + + const wallet = await manager.wallets.signUp({ + mnemonic: Mnemonic.random(Mnemonic.english), + kind: 'mnemonic', + noGuard: false, + }) + + const config = await manager.wallets.getConfiguration(wallet!) + + expect(config.walletGuard?.address).toBe(guardAddress) + expect(config.raw.guardTopology).toBeDefined() + expect(Config.findSignerLeaf(config.raw.guardTopology!, guardAddress)).toBeDefined() + expect( + Config.findSignerLeaf(config.raw.guardTopology!, Constants.PlaceholderAddress as Address.Address), + ).toBeUndefined() + + const sessionsModule = config.raw.modules.find((m: any) => + Address.isEqual(m.sapientLeaf.address, sessionsModuleAddress), + ) + expect(sessionsModule?.guardLeaf).toBeDefined() + expect(Config.findSignerLeaf(sessionsModule!.guardLeaf!, sessionsGuardAddress)).toBeDefined() + expect( + Config.findSignerLeaf(sessionsModule!.guardLeaf!, Constants.PlaceholderAddress as Address.Address), + ).toBeUndefined() + + expect(config.moduleGuards.get(sessionsModuleAddress as Address.Address)?.address).toBe(sessionsGuardAddress) + }) + + it('Should support non-nested guard topologies', async () => { + manager = newManager( + { + defaultGuardTopology: { + type: 'signer', + address: Constants.PlaceholderAddress, + weight: 1n, + }, + }, + undefined, + `flat_guard_${Date.now()}`, + ) + + const guardAddress = (manager as any).shared.sequence.guardAddresses.wallet + const sessionsGuardAddress = (manager as any).shared.sequence.guardAddresses.sessions + const wallet = await manager.wallets.signUp({ + mnemonic: Mnemonic.random(Mnemonic.english), + kind: 'mnemonic', + noGuard: false, + }) + + const config = await manager.wallets.getConfiguration(wallet!) + + expect(config.walletGuard?.address).toBe(guardAddress) + expect(config.raw.guardTopology).toBeDefined() + expect(Config.findSignerLeaf(config.raw.guardTopology!, guardAddress)).toBeDefined() + expect( + Config.findSignerLeaf(config.raw.guardTopology!, Constants.PlaceholderAddress as Address.Address), + ).toBeUndefined() + + const sessionsModuleAddress = (manager as any).shared.sequence.extensions.sessions + const sessionsModule = config.raw.modules.find((m: any) => + Address.isEqual(m.sapientLeaf.address, sessionsModuleAddress), + ) + expect(sessionsModule?.guardLeaf).toBeDefined() + expect(Config.findSignerLeaf(sessionsModule!.guardLeaf!, sessionsGuardAddress)).toBeDefined() + }) + + it('Should fail signup when default guard topology lacks placeholder address', async () => { + manager = newManager( + { + defaultGuardTopology: { + type: 'signer', + address: '0x0000000000000000000000000000000000000001', + weight: 1n, + }, + }, + undefined, + `guard_missing_placeholder_${Date.now()}`, + ) + + await expect( + manager.wallets.signUp({ + mnemonic: Mnemonic.random(Mnemonic.english), + kind: 'mnemonic', + noGuard: false, + }), + ).rejects.toThrow('Guard address replacement failed for role wallet') + }) + // === ERROR HANDLING === it('Should throw error when trying to get configuration for non-existent wallet', async () => { @@ -317,7 +410,7 @@ describe('Wallets', () => { const mnemonic = Mnemonic.random(Mnemonic.english) await manager.wallets.signUp({ mnemonic, kind: 'mnemonic', noGuard: true }) - await manager.wallets.logout(await manager.wallets.list().then((w) => w[0].address), { skipRemoveDevice: true }) + await manager.wallets.logout(await manager.wallets.list().then((w) => w[0]!.address), { skipRemoveDevice: true }) const invalidSelector = async () => 'invalid-result' as any manager.wallets.registerWalletSelector(invalidSelector) @@ -337,7 +430,7 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) + expect(wallets[0]!.address).toBe(wallet!) const requestId = await manager.wallets.logout(wallet!) expect(requestId).toBeDefined() @@ -377,16 +470,16 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('ready') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('ready') const requestId = await manager.wallets.logout(wallet!) expect(requestId).toBeDefined() const wallets2 = await manager.wallets.list() expect(wallets2.length).toBe(1) - expect(wallets2[0].address).toBe(wallet!) - expect(wallets2[0].status).toBe('logging-out') + expect(wallets2[0]!.address).toBe(wallet!) + expect(wallets2[0]!.status).toBe('logging-out') const request = await manager.signatures.get(requestId) expect(request).toBeDefined() @@ -422,8 +515,8 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('logging-in') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('logging-in') let signRequests = 0 const unregistedUI = manager.registerMnemonicUI(async (respond) => { @@ -450,8 +543,8 @@ describe('Wallets', () => { expect((await manager.signatures.get(requestId1!))?.status).toBe('completed') const wallets2 = await manager.wallets.list() expect(wallets2.length).toBe(1) - expect(wallets2[0].address).toBe(wallet!) - expect(wallets2[0].status).toBe('ready') + expect(wallets2[0]!.address).toBe(wallet!) + expect(wallets2[0]!.status).toBe('ready') // The wallet should have 2 device keys and 2 recovery keys const config = await manager.wallets.getConfiguration(wallet!) @@ -471,7 +564,7 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) + expect(wallets[0]!.address).toBe(wallet!) const requestId = await manager.wallets.logout(wallet!) expect(requestId).toBeDefined() @@ -520,7 +613,7 @@ describe('Wallets', () => { expect((await manager.signatures.get(requestId2!))?.status).toBe('completed') const wallets3 = await manager.wallets.list() expect(wallets3.length).toBe(1) - expect(wallets3[0].address).toBe(wallet!) + expect(wallets3[0]!.address).toBe(wallet!) // The wallet should have a single device key and a single recovery key const config = await manager.wallets.getConfiguration(wallet!) @@ -529,10 +622,10 @@ describe('Wallets', () => { expect(recovery?.length).toBe(1) // The kind of the device key should be 'local-device' - expect(config.devices[0].kind).toBe('local-device') + expect(config.devices[0]!.kind).toBe('local-device') // The kind of the recovery key should be 'local-recovery' - expect(recovery?.[0].kind).toBe('local-device') + expect(recovery?.[0]!.kind).toBe('local-device') }) it('Should fail to logout from a non-existent wallet', async () => { @@ -583,8 +676,8 @@ describe('Wallets', () => { const wallets = await manager.wallets.list() expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('logging-in') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('logging-in') const request = await manager.signatures.get(requestId!) expect(request).toBeDefined() @@ -606,8 +699,8 @@ describe('Wallets', () => { expect((await manager.signatures.get(requestId!))?.status).toBe('completed') const wallets2 = await manager.wallets.list() expect(wallets2.length).toBe(1) - expect(wallets2[0].address).toBe(wallet!) - expect(wallets2[0].status).toBe('ready') + expect(wallets2[0]!.address).toBe(wallet!) + expect(wallets2[0]!.status).toBe('ready') }) it('Should trigger an update when a wallet is logged in', async () => { @@ -622,8 +715,8 @@ describe('Wallets', () => { unregisterCallback = manager.wallets.onWalletsUpdate((wallets) => { callbackCalls++ expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('ready') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('ready') resolve() }) }) @@ -684,8 +777,8 @@ describe('Wallets', () => { unregisterCallback = manager.wallets.onWalletsUpdate((wallets) => { callbackCalls++ expect(wallets.length).toBe(1) - expect(wallets[0].address).toBe(wallet!) - expect(wallets[0].status).toBe('logging-out') + expect(wallets[0]!.address).toBe(wallet!) + expect(wallets[0]!.status).toBe('logging-out') resolve() }) }) @@ -707,9 +800,9 @@ describe('Wallets', () => { const devices = await manager.wallets.listDevices(wallet!) expect(devices.length).toBe(1) - expect(devices[0].address).not.toBe(wallet) - expect(devices[0].isLocal).toBe(true) expect(devices[0]).toBeDefined() + expect(devices[0]!.address).not.toBe(wallet) + expect(devices[0]!.isLocal).toBe(true) }) it('Should list all active devices for a wallet, including a new remote device', async () => { @@ -727,8 +820,8 @@ describe('Wallets', () => { // Verify initial state from Device 1's perspective const devices1 = await managerDevice1.wallets.listDevices(wallet!) expect(devices1.length).toBe(1) - expect(devices1[0].isLocal).toBe(true) - const device1Address = devices1[0].address + expect(devices1[0]!.isLocal).toBe(true) + const device1Address = devices1[0]!.address // Wallet logs in on device 2 const managerDevice2 = newManager(undefined, undefined, 'device-2') @@ -845,8 +938,8 @@ describe('Wallets', () => { const finalDevices = await managerDevice1.wallets.listDevices(wallet!) console.log('Final devices', finalDevices) expect(finalDevices.length).toBe(1) - expect(finalDevices[0].isLocal).toBe(true) - expect(finalDevices[0].address).not.toBe(device2Address) + expect(finalDevices[0]!.isLocal).toBe(true) + expect(finalDevices[0]!.address).not.toBe(device2Address) await managerDevice1.stop() await managerDevice2.stop() @@ -863,7 +956,7 @@ describe('Wallets', () => { const devices = await manager.wallets.listDevices(wallet!) expect(devices.length).toBe(1) - const localDeviceAddress = devices[0].address + const localDeviceAddress = devices[0]!.address const remoteLogoutPromise = manager.wallets.remoteLogout(wallet!, localDeviceAddress) diff --git a/packages/wallet/wdk/vitest.config.ts b/packages/wallet/wdk/vitest.config.ts index 813dc499da..9c2092c0c4 100644 --- a/packages/wallet/wdk/vitest.config.ts +++ b/packages/wallet/wdk/vitest.config.ts @@ -1,4 +1,3 @@ -import { BrowserNavigationCrossOriginPolicyEnum } from 'happy-dom' import { defineConfig } from 'vitest/config' export default defineConfig({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 450d5be82a..fdbb822b82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,96 +4,108 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + ox: ^0.9.17 + importers: .: devDependencies: '@changesets/cli': - specifier: ^2.29.4 - version: 2.29.5 + specifier: ^2.29.8 + version: 2.29.8(@types/node@25.0.2) lefthook: - specifier: ^1.11.13 - version: 1.12.2 + specifier: ^2.0.12 + version: 2.0.12 prettier: - specifier: ^3.5.3 - version: 3.6.2 + specifier: ^3.7.4 + version: 3.7.4 rimraf: - specifier: ^6.0.1 - version: 6.0.1 + specifier: ^6.1.2 + version: 6.1.2 + syncpack: + specifier: ^13.0.4 + version: 13.0.4(typescript@5.9.3) turbo: - specifier: ^2.5.4 - version: 2.5.5 + specifier: ^2.6.3 + version: 2.6.3 typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 extras/docs: dependencies: '@repo/ui': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/ui next: - specifier: ^15.4.7 - version: 15.4.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^15.5.9 + version: 15.5.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: - specifier: ^19.1.0 - version: 19.1.0 + specifier: ^19.2.3 + version: 19.2.3 react-dom: - specifier: ^19.1.0 - version: 19.1.0(react@19.1.0) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) devDependencies: '@repo/eslint-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/eslint-config '@repo/typescript-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/typescript-config '@types/node': - specifier: ^20.17.57 - version: 20.19.9 + specifier: ^25.0.2 + version: 25.0.2 '@types/react': - specifier: 18.3.1 - version: 18.3.1 + specifier: ^19.2.7 + version: 19.2.7 '@types/react-dom': - specifier: 18.3.0 - version: 18.3.0 + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) + eslint: + specifier: ^9.39.2 + version: 9.39.2 typescript: - specifier: 5.5.4 - version: 5.5.4 + specifier: ^5.9.3 + version: 5.9.3 extras/web: dependencies: '@repo/ui': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/ui next: - specifier: ^15.4.7 - version: 15.4.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: ^15.5.9 + version: 15.5.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: - specifier: ^19.1.0 - version: 19.1.0 + specifier: ^19.2.3 + version: 19.2.3 react-dom: - specifier: ^19.1.0 - version: 19.1.0(react@19.1.0) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) devDependencies: '@repo/eslint-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/eslint-config '@repo/typescript-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../repo/typescript-config '@types/node': - specifier: ^20.17.57 - version: 20.19.9 + specifier: ^25.0.2 + version: 25.0.2 '@types/react': - specifier: 18.3.1 - version: 18.3.1 + specifier: ^19.2.7 + version: 19.2.7 '@types/react-dom': - specifier: 18.3.0 - version: 18.3.0 + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) + eslint: + specifier: ^9.39.2 + version: 9.39.2 typescript: - specifier: 5.5.4 - version: 5.5.4 + specifier: ^5.9.3 + version: 5.9.3 packages/services/api: devDependencies: @@ -101,11 +113,11 @@ importers: specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 packages/services/builder: devDependencies: @@ -113,30 +125,30 @@ importers: specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 packages/services/guard: dependencies: ox: - specifier: ^0.7.2 - version: 0.7.2(typescript@5.8.3) + specifier: ^0.9.17 + version: 0.9.17(typescript@5.9.3)(zod@4.2.0) devDependencies: '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 vitest: - specifier: ^3.2.1 - version: 3.2.4(@types/node@22.16.5)(happy-dom@20.0.2) + specifier: ^4.0.15 + version: 4.0.15(@types/node@25.0.2)(happy-dom@20.0.11) packages/services/identity-instrument: dependencies: @@ -147,21 +159,21 @@ importers: specifier: ^4.0.0 version: 4.0.0 ox: - specifier: ^0.7.2 - version: 0.7.2(typescript@5.8.3) + specifier: ^0.9.17 + version: 0.9.17(typescript@5.9.3)(zod@4.2.0) devDependencies: '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 vitest: - specifier: ^3.2.1 - version: 3.2.4(@types/node@22.16.5)(happy-dom@20.0.2) + specifier: ^4.0.15 + version: 4.0.15(@types/node@25.0.2)(happy-dom@20.0.11) packages/services/indexer: devDependencies: @@ -169,11 +181,11 @@ importers: specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 packages/services/marketplace: devDependencies: @@ -181,11 +193,11 @@ importers: specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 packages/services/metadata: devDependencies: @@ -193,23 +205,51 @@ importers: specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 packages/services/relayer: + dependencies: + '@0xsequence/wallet-primitives': + specifier: workspace:^ + version: link:../../wallet/primitives + mipd: + specifier: ^0.0.7 + version: 0.0.7(typescript@5.9.3) + ox: + specifier: ^0.9.17 + version: 0.9.17(typescript@5.9.3)(zod@4.2.0) + viem: + specifier: ^2.40.3 + version: 2.42.1(typescript@5.9.3)(zod@4.2.0) devDependencies: '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.15 + version: 4.0.15(@types/node@25.0.2)(happy-dom@20.0.11) + + packages/services/userdata: + devDependencies: + '@repo/typescript-config': + specifier: workspace:^ + version: link:../../../repo/typescript-config + '@types/node': + specifier: ^25.0.2 + version: 25.0.2 + typescript: + specifier: ^5.9.3 + version: 5.9.3 packages/utils/abi: devDependencies: @@ -217,11 +257,11 @@ importers: specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 packages/wallet/core: dependencies: @@ -236,41 +276,44 @@ importers: version: link:../primitives mipd: specifier: ^0.0.7 - version: 0.0.7(typescript@5.8.3) + version: 0.0.7(typescript@5.9.3) ox: - specifier: ^0.7.2 - version: 0.7.2(typescript@5.8.3) + specifier: ^0.9.17 + version: 0.9.17(typescript@5.9.3)(zod@4.2.0) viem: - specifier: ^2.30.6 - version: 2.33.0(typescript@5.8.3) + specifier: ^2.40.3 + version: 2.42.1(typescript@5.9.3)(zod@4.2.0) devDependencies: '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 '@vitest/coverage-v8': - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/node@22.16.5)(happy-dom@20.0.2)) + specifier: ^4.0.15 + version: 4.0.15(vitest@4.0.15(@types/node@25.0.2)(happy-dom@20.0.11)) dotenv: - specifier: ^16.5.0 - version: 16.6.1 + specifier: ^17.2.3 + version: 17.2.3 fake-indexeddb: - specifier: ^6.0.1 - version: 6.0.1 + specifier: ^6.2.5 + version: 6.2.5 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 vitest: - specifier: ^3.2.1 - version: 3.2.4(@types/node@22.16.5)(happy-dom@20.0.2) + specifier: ^4.0.15 + version: 4.0.15(@types/node@25.0.2)(happy-dom@20.0.11) packages/wallet/dapp-client: dependencies: '@0xsequence/guard': specifier: workspace:^ version: link:../../services/guard + '@0xsequence/relayer': + specifier: workspace:^ + version: link:../../services/relayer '@0xsequence/wallet-core': specifier: workspace:^ version: link:../core @@ -278,52 +321,52 @@ importers: specifier: workspace:^ version: link:../primitives ox: - specifier: ^0.7.2 - version: 0.7.2(typescript@5.8.3) + specifier: ^0.9.17 + version: 0.9.17(typescript@5.9.3)(zod@4.2.0) devDependencies: '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 '@vitest/coverage-v8': - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/node@22.16.5)(happy-dom@20.0.2)) + specifier: ^4.0.15 + version: 4.0.15(vitest@4.0.15(@types/node@25.0.2)(happy-dom@20.0.11)) dotenv: - specifier: ^16.5.0 - version: 16.6.1 + specifier: ^17.2.3 + version: 17.2.3 fake-indexeddb: - specifier: ^6.0.1 - version: 6.0.1 + specifier: ^6.2.5 + version: 6.2.5 happy-dom: - specifier: ^20.0.2 - version: 20.0.2 + specifier: ^20.0.11 + version: 20.0.11 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 vitest: - specifier: ^3.2.1 - version: 3.2.4(@types/node@22.16.5)(happy-dom@20.0.2) + specifier: ^4.0.15 + version: 4.0.15(@types/node@25.0.2)(happy-dom@20.0.11) packages/wallet/primitives: dependencies: ox: - specifier: ^0.7.2 - version: 0.7.2(typescript@5.8.3) + specifier: ^0.9.17 + version: 0.9.17(typescript@5.9.3)(zod@4.2.0) devDependencies: '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@vitest/coverage-v8': - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/node@22.16.5)(happy-dom@20.0.2)) + specifier: ^4.0.15 + version: 4.0.15(vitest@4.0.15(@types/node@25.0.2)(happy-dom@20.0.11)) typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 vitest: - specifier: ^3.2.1 - version: 3.2.4(@types/node@22.16.5)(happy-dom@20.0.2) + specifier: ^4.0.15 + version: 4.0.15(@types/node@25.0.2)(happy-dom@20.0.11) packages/wallet/primitives-cli: dependencies: @@ -331,36 +374,36 @@ importers: specifier: workspace:^ version: link:../primitives ox: - specifier: ^0.7.2 - version: 0.7.2(typescript@5.8.3) + specifier: ^0.9.17 + version: 0.9.17(typescript@5.9.3)(zod@4.2.0) yargs: - specifier: ^17.7.2 - version: 17.7.2 + specifier: ^18.0.0 + version: 18.0.0 devDependencies: '@repo/eslint-config': - specifier: workspace:* + specifier: workspace:^ version: link:../../../repo/eslint-config '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 '@types/yargs': - specifier: ^17.0.33 - version: 17.0.33 + specifier: ^17.0.35 + version: 17.0.35 concurrently: - specifier: ^8.2.2 - version: 8.2.2 + specifier: ^9.2.1 + version: 9.2.1 esbuild: - specifier: ^0.25.5 - version: 0.25.8 + specifier: ^0.27.1 + version: 0.27.1 nodemon: - specifier: ^3.1.10 - version: 3.1.10 + specifier: ^3.1.11 + version: 3.1.11 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 packages/wallet/wdk: dependencies: @@ -370,6 +413,9 @@ importers: '@0xsequence/identity-instrument': specifier: workspace:^ version: link:../../services/identity-instrument + '@0xsequence/relayer': + specifier: workspace:^ + version: link:../../services/relayer '@0xsequence/tee-verifier': specifier: ^0.1.2 version: 0.1.2 @@ -380,155 +426,201 @@ importers: specifier: workspace:^ version: link:../primitives idb: - specifier: ^7.1.1 - version: 7.1.1 + specifier: ^8.0.3 + version: 8.0.3 jwt-decode: specifier: ^4.0.0 version: 4.0.0 ox: - specifier: ^0.7.2 - version: 0.7.2(typescript@5.8.3) + specifier: ^0.9.17 + version: 0.9.17(typescript@5.9.3)(zod@4.2.0) uuid: - specifier: ^11.1.0 - version: 11.1.0 + specifier: ^13.0.0 + version: 13.0.0 devDependencies: '@repo/typescript-config': specifier: workspace:^ version: link:../../../repo/typescript-config '@types/node': - specifier: ^22.15.29 - version: 22.16.5 + specifier: ^25.0.2 + version: 25.0.2 '@vitest/coverage-v8': - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/node@22.16.5)(happy-dom@20.0.2)) + specifier: ^4.0.15 + version: 4.0.15(vitest@4.0.15(@types/node@25.0.2)(happy-dom@20.0.11)) dotenv: - specifier: ^16.5.0 - version: 16.6.1 + specifier: ^17.2.3 + version: 17.2.3 fake-indexeddb: - specifier: ^6.0.1 - version: 6.0.1 + specifier: ^6.2.5 + version: 6.2.5 happy-dom: - specifier: ^20.0.2 - version: 20.0.2 + specifier: ^20.0.11 + version: 20.0.11 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 vitest: - specifier: ^3.2.1 - version: 3.2.4(@types/node@22.16.5)(happy-dom@20.0.2) + specifier: ^4.0.15 + version: 4.0.15(@types/node@25.0.2)(happy-dom@20.0.11) repo/eslint-config: devDependencies: '@eslint/js': - specifier: ^9.28.0 - version: 9.31.0 + specifier: ^9.39.2 + version: 9.39.2 '@next/eslint-plugin-next': - specifier: ^15.3.3 - version: 15.4.2 + specifier: ^15.5.9 + version: 15.5.9 eslint: - specifier: ^9.28.0 - version: 9.31.0 + specifier: ^9.39.2 + version: 9.39.2 eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.2(eslint@9.31.0) + specifier: ^10.1.8 + version: 10.1.8(eslint@9.39.2) eslint-plugin-only-warn: specifier: ^1.1.0 version: 1.1.0 eslint-plugin-react: specifier: ^7.37.5 - version: 7.37.5(eslint@9.31.0) + version: 7.37.5(eslint@9.39.2) eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.31.0) + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.2) eslint-plugin-turbo: - specifier: ^2.5.4 - version: 2.5.5(eslint@9.31.0)(turbo@2.5.5) + specifier: ^2.6.3 + version: 2.6.3(eslint@9.39.2)(turbo@2.6.3) globals: - specifier: ^15.15.0 - version: 15.15.0 + specifier: ^16.5.0 + version: 16.5.0 typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.33.1 - version: 8.38.0(eslint@9.31.0)(typescript@5.8.3) + specifier: ^8.49.0 + version: 8.50.0(eslint@9.39.2)(typescript@5.9.3) repo/typescript-config: {} repo/ui: dependencies: react: - specifier: ^19.1.0 - version: 19.1.0 + specifier: ^19.2.3 + version: 19.2.3 react-dom: - specifier: ^19.1.0 - version: 19.1.0(react@19.1.0) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) devDependencies: '@repo/eslint-config': - specifier: workspace:* + specifier: workspace:^ version: link:../eslint-config '@repo/typescript-config': - specifier: workspace:* + specifier: workspace:^ version: link:../typescript-config '@turbo/gen': specifier: ^1.13.4 - version: 1.13.4(@types/node@20.19.9)(typescript@5.5.4) + version: 1.13.4(@types/node@25.0.2)(typescript@5.9.3) '@types/node': - specifier: ^20.17.57 - version: 20.19.9 + specifier: ^25.0.2 + version: 25.0.2 '@types/react': - specifier: 18.3.0 - version: 18.3.0 + specifier: ^19.2.7 + version: 19.2.7 '@types/react-dom': - specifier: 18.3.1 - version: 18.3.1 + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) typescript: - specifier: 5.5.4 - version: 5.5.4 + specifier: ^5.9.3 + version: 5.9.3 packages: '@0xsequence/tee-verifier@0.1.2': resolution: {integrity: sha512-7sKr8/T4newknx6LAukjlRI3siGiGhBnZohz2Z3jX0zb0EBQdKUq0L//A7CPSckHFPxTg/QvQU2v8e9x9GfkDw==} - '@adraffy/ens-normalize@1.11.0': - resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==} + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.0': - resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime-corejs3@7.28.0': - resolution: {integrity: sha512-nlIXnSqLcBij8K8TtkxbBJgfzfvi75V1pAKSM7dUXejGw12vJAqez74jZrHTsJ3Z+Aczc5Q/6JgNjKRMsVU44g==} + '@babel/runtime-corejs3@7.28.4': + resolution: {integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.27.6': - resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.1': - resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@changesets/apply-release-plan@7.0.12': - resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==} + '@changesets/apply-release-plan@7.0.14': + resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} '@changesets/assemble-release-plan@6.0.9': resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} @@ -536,12 +628,12 @@ packages: '@changesets/changelog-git@0.2.1': resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - '@changesets/cli@2.29.5': - resolution: {integrity: sha512-0j0cPq3fgxt2dPdFsg4XvO+6L66RC0pZybT9F4dG5TBrLA3jA/1pNkdTXH9IBBVHkgsKrNKenI3n1mPyPlIydg==} + '@changesets/cli@2.29.8': + resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==} hasBin: true - '@changesets/config@3.1.1': - resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} + '@changesets/config@3.1.2': + resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} @@ -549,8 +641,8 @@ packages: '@changesets/get-dependents-graph@2.1.3': resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} - '@changesets/get-release-plan@4.0.13': - resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} + '@changesets/get-release-plan@4.0.14': + resolution: {integrity: sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} @@ -561,14 +653,14 @@ packages: '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - '@changesets/parse@0.4.1': - resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} + '@changesets/parse@0.4.2': + resolution: {integrity: sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==} '@changesets/pre@2.0.2': resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - '@changesets/read@0.6.5': - resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} + '@changesets/read@0.6.6': + resolution: {integrity: sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==} '@changesets/should-skip-package@0.1.2': resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} @@ -586,219 +678,215 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} - '@esbuild/aix-ppc64@0.25.8': - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.8': - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.8': - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.8': - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.8': - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.8': - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.8': - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.8': - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.8': - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.8': - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.8': - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.8': - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.8': - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.8': - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.8': - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.8': - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.8': - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.8': - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.8': - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.8': - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.8': - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.8': - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.8': - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.8': - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.8': - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.8': - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.3.0': - resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.1': - resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.31.0': - resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.4': - resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} @@ -807,128 +895,148 @@ packages: resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.34.4': - resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.4': - resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.3': - resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.3': - resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.3': - resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.2.3': - resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.3': - resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.3': - resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.2.3': - resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.3': - resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.3': - resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.34.4': - resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.34.4': - resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.34.4': - resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - '@img/sharp-linux-s390x@0.34.4': - resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.34.4': - resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.4': - resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.4': - resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.34.4': - resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.4': - resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.4': - resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.4': - resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -937,26 +1045,21 @@ packages: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@jridgewell/gen-mapping@0.3.12': - resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.4': - resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.29': - resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -967,56 +1070,56 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@next/env@15.4.7': - resolution: {integrity: sha512-PrBIpO8oljZGTOe9HH0miix1w5MUiGJ/q83Jge03mHEE0E3pyqzAy2+l5G6aJDbXoobmxPJTVhbCuwlLtjSHwg==} + '@next/env@15.5.9': + resolution: {integrity: sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==} - '@next/eslint-plugin-next@15.4.2': - resolution: {integrity: sha512-k0rjdWjXBY6tAOty1ckrMETE6Mx66d85NsgcAIdDp7/cXOsTJ93ywmbg3uUcpxX5TUHFEcCWI5mb8nPhwCe9jg==} + '@next/eslint-plugin-next@15.5.9': + resolution: {integrity: sha512-kUzXx0iFiXw27cQAViE1yKWnz/nF8JzRmwgMRTMh8qMY90crNsdXJRh2e+R0vBpFR3kk1yvAR7wev7+fCCb79Q==} - '@next/swc-darwin-arm64@15.4.7': - resolution: {integrity: sha512-2Dkb+VUTp9kHHkSqtws4fDl2Oxms29HcZBwFIda1X7Ztudzy7M6XF9HDS2dq85TmdN47VpuhjE+i6wgnIboVzQ==} + '@next/swc-darwin-arm64@15.5.7': + resolution: {integrity: sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.4.7': - resolution: {integrity: sha512-qaMnEozKdWezlmh1OGDVFueFv2z9lWTcLvt7e39QA3YOvZHNpN2rLs/IQLwZaUiw2jSvxW07LxMCWtOqsWFNQg==} + '@next/swc-darwin-x64@15.5.7': + resolution: {integrity: sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.4.7': - resolution: {integrity: sha512-ny7lODPE7a15Qms8LZiN9wjNWIeI+iAZOFDOnv2pcHStncUr7cr9lD5XF81mdhrBXLUP9yT9RzlmSWKIazWoDw==} + '@next/swc-linux-arm64-gnu@15.5.7': + resolution: {integrity: sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.4.7': - resolution: {integrity: sha512-4SaCjlFR/2hGJqZLLWycccy1t+wBrE/vyJWnYaZJhUVHccpGLG5q0C+Xkw4iRzUIkE+/dr90MJRUym3s1+vO8A==} + '@next/swc-linux-arm64-musl@15.5.7': + resolution: {integrity: sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.4.7': - resolution: {integrity: sha512-2uNXjxvONyRidg00VwvlTYDwC9EgCGNzPAPYbttIATZRxmOZ3hllk/YYESzHZb65eyZfBR5g9xgCZjRAl9YYGg==} + '@next/swc-linux-x64-gnu@15.5.7': + resolution: {integrity: sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.4.7': - resolution: {integrity: sha512-ceNbPjsFgLscYNGKSu4I6LYaadq2B8tcK116nVuInpHHdAWLWSwVK6CHNvCi0wVS9+TTArIFKJGsEyVD1H+4Kg==} + '@next/swc-linux-x64-musl@15.5.7': + resolution: {integrity: sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.4.7': - resolution: {integrity: sha512-pZyxmY1iHlZJ04LUL7Css8bNvsYAMYOY9JRwFA3HZgpaNKsJSowD09Vg2R9734GxAcLJc2KDQHSCR91uD6/AAw==} + '@next/swc-win32-arm64-msvc@15.5.7': + resolution: {integrity: sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.4.7': - resolution: {integrity: sha512-HjuwPJ7BeRzgl3KrjKqD2iDng0eQIpIReyhpF5r4yeAHFwWRuAhfW92rWv/r3qeQHEwHsLRzFDvMqRjyM5DI6A==} + '@next/swc-win32-x64-msvc@15.5.7': + resolution: {integrity: sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1025,13 +1128,13 @@ packages: resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.9.2': - resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==} + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.9.4': - resolution: {integrity: sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==} - engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} @@ -1049,107 +1152,113 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@rollup/rollup-android-arm-eabi@4.45.1': - resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} + '@rollup/rollup-android-arm-eabi@4.53.4': + resolution: {integrity: sha512-PWU3Y92H4DD0bOqorEPp1Y0tbzwAurFmIYpjcObv5axGVOtcTlB0b2UKMd2echo08MgN7jO8WQZSSysvfisFSQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.45.1': - resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==} + '@rollup/rollup-android-arm64@4.53.4': + resolution: {integrity: sha512-Gw0/DuVm3rGsqhMGYkSOXXIx20cC3kTlivZeuaGt4gEgILivykNyBWxeUV5Cf2tDA2nPLah26vq3emlRrWVbng==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.45.1': - resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==} + '@rollup/rollup-darwin-arm64@4.53.4': + resolution: {integrity: sha512-+w06QvXsgzKwdVg5qRLZpTHh1bigHZIqoIUPtiqh05ZiJVUQ6ymOxaPkXTvRPRLH88575ZCRSRM3PwIoNma01Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.45.1': - resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==} + '@rollup/rollup-darwin-x64@4.53.4': + resolution: {integrity: sha512-EB4Na9G2GsrRNRNFPuxfwvDRDUwQEzJPpiK1vo2zMVhEeufZ1k7J1bKnT0JYDfnPC7RNZ2H5YNQhW6/p2QKATw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.45.1': - resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==} + '@rollup/rollup-freebsd-arm64@4.53.4': + resolution: {integrity: sha512-bldA8XEqPcs6OYdknoTMaGhjytnwQ0NClSPpWpmufOuGPN5dDmvIa32FygC2gneKK4A1oSx86V1l55hyUWUYFQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.45.1': - resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==} + '@rollup/rollup-freebsd-x64@4.53.4': + resolution: {integrity: sha512-3T8GPjH6mixCd0YPn0bXtcuSXi1Lj+15Ujw2CEb7dd24j9thcKscCf88IV7n76WaAdorOzAgSSbuVRg4C8V8Qw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.45.1': - resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.4': + resolution: {integrity: sha512-UPMMNeC4LXW7ZSHxeP3Edv09aLsFUMaD1TSVW6n1CWMECnUIJMFFB7+XC2lZTdPtvB36tYC0cJWc86mzSsaviw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.45.1': - resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} + '@rollup/rollup-linux-arm-musleabihf@4.53.4': + resolution: {integrity: sha512-H8uwlV0otHs5Q7WAMSoyvjV9DJPiy5nJ/xnHolY0QptLPjaSsuX7tw+SPIfiYH6cnVx3fe4EWFafo6gH6ekZKA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.45.1': - resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} + '@rollup/rollup-linux-arm64-gnu@4.53.4': + resolution: {integrity: sha512-BLRwSRwICXz0TXkbIbqJ1ibK+/dSBpTJqDClF61GWIrxTXZWQE78ROeIhgl5MjVs4B4gSLPCFeD4xML9vbzvCQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.45.1': - resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} + '@rollup/rollup-linux-arm64-musl@4.53.4': + resolution: {integrity: sha512-6bySEjOTbmVcPJAywjpGLckK793A0TJWSbIa0sVwtVGfe/Nz6gOWHOwkshUIAp9j7wg2WKcA4Snu7Y1nUZyQew==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.45.1': - resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} + '@rollup/rollup-linux-loong64-gnu@4.53.4': + resolution: {integrity: sha512-U0ow3bXYJZ5MIbchVusxEycBw7bO6C2u5UvD31i5IMTrnt2p4Fh4ZbHSdc/31TScIJQYHwxbj05BpevB3201ug==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': - resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} + '@rollup/rollup-linux-ppc64-gnu@4.53.4': + resolution: {integrity: sha512-iujDk07ZNwGLVn0YIWM80SFN039bHZHCdCCuX9nyx3Jsa2d9V/0Y32F+YadzwbvDxhSeVo9zefkoPnXEImnM5w==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.45.1': - resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} + '@rollup/rollup-linux-riscv64-gnu@4.53.4': + resolution: {integrity: sha512-MUtAktiOUSu+AXBpx1fkuG/Bi5rhlorGs3lw5QeJ2X3ziEGAq7vFNdWVde6XGaVqi0LGSvugwjoxSNJfHFTC0g==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.45.1': - resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} + '@rollup/rollup-linux-riscv64-musl@4.53.4': + resolution: {integrity: sha512-btm35eAbDfPtcFEgaXCI5l3c2WXyzwiE8pArhd66SDtoLWmgK5/M7CUxmUglkwtniPzwvWioBKKl6IXLbPf2sQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.45.1': - resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} + '@rollup/rollup-linux-s390x-gnu@4.53.4': + resolution: {integrity: sha512-uJlhKE9ccUTCUlK+HUz/80cVtx2RayadC5ldDrrDUFaJK0SNb8/cCmC9RhBhIWuZ71Nqj4Uoa9+xljKWRogdhA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.45.1': - resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} + '@rollup/rollup-linux-x64-gnu@4.53.4': + resolution: {integrity: sha512-jjEMkzvASQBbzzlzf4os7nzSBd/cvPrpqXCUOqoeCh1dQ4BP3RZCJk8XBeik4MUln3m+8LeTJcY54C/u8wb3DQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.45.1': - resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} + '@rollup/rollup-linux-x64-musl@4.53.4': + resolution: {integrity: sha512-lu90KG06NNH19shC5rBPkrh6mrTpq5kviFylPBXQVpdEu0yzb0mDgyxLr6XdcGdBIQTH/UAhDJnL+APZTBu1aQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.45.1': - resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} + '@rollup/rollup-openharmony-arm64@4.53.4': + resolution: {integrity: sha512-dFDcmLwsUzhAm/dn0+dMOQZoONVYBtgik0VuY/d5IJUUb787L3Ko/ibvTvddqhb3RaB7vFEozYevHN4ox22R/w==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.4': + resolution: {integrity: sha512-WvUpUAWmUxZKtRnQWpRKnLW2DEO8HB/l8z6oFFMNuHndMzFTJEXzaYJ5ZAmzNw0L21QQJZsUQFt2oPf3ykAD/w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.45.1': - resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==} + '@rollup/rollup-win32-ia32-msvc@4.53.4': + resolution: {integrity: sha512-JGbeF2/FDU0x2OLySw/jgvkwWUo05BSiJK0dtuI4LyuXbz3wKiC1xHhLB1Tqm5VU6ZZDmAorj45r/IgWNWku5g==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.45.1': - resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==} + '@rollup/rollup-win32-x64-gnu@4.53.4': + resolution: {integrity: sha512-zuuC7AyxLWLubP+mlUwEyR8M1ixW1ERNPHJfXm8x7eQNP4Pzkd7hS3qBuKBR70VRiQ04Kw8FNfRMF5TNxuZq2g==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.4': + resolution: {integrity: sha512-Sbx45u/Lbb5RyptSbX7/3deP+/lzEmZ0BTSHxwxN/IMOZDZf8S0AGo0hJD5n/LQssxb5Z3B4og4P2X6Dd8acCA==} cpu: [x64] os: [win32] @@ -1162,14 +1271,21 @@ packages: '@scure/bip39@1.6.0': resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} '@tsconfig/node12@1.0.11': resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} @@ -1188,8 +1304,8 @@ packages: resolution: {integrity: sha512-3uYg2b5TWCiupetbDFMbBFMHl33xQTvp5DNg0fZSYal73Z9AlFH9yWabHWMYw6ywmwM1evkYRpTVA2n7GgqT5A==} hasBin: true - '@types/chai@5.2.2': - resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -1213,29 +1329,19 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@20.19.21': - resolution: {integrity: sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA==} - - '@types/node@20.19.9': - resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==} - - '@types/node@22.16.5': - resolution: {integrity: sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==} - - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + '@types/node@20.19.27': + resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} - '@types/react-dom@18.3.0': - resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/node@25.0.2': + resolution: {integrity: sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==} - '@types/react-dom@18.3.1': - resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} - - '@types/react@18.3.0': - resolution: {integrity: sha512-DiUcKjzE6soLyln8NNZmyhcQjVv+WsUIFSqetMN0p8927OztKT4VTfFTqsbAi5oAGIcgOmOajlfBqyptDDjZRw==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 - '@types/react@18.3.1': - resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} '@types/through@0.0.33': resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} @@ -1249,111 +1355,122 @@ packages: '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.33': - resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.38.0': - resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==} + '@typescript-eslint/eslint-plugin@8.50.0': + resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.38.0 + '@typescript-eslint/parser': ^8.50.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.38.0': - resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==} + '@typescript-eslint/parser@8.50.0': + resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.38.0': - resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==} + '@typescript-eslint/project-service@8.50.0': + resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.38.0': - resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==} + '@typescript-eslint/scope-manager@8.50.0': + resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.38.0': - resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==} + '@typescript-eslint/tsconfig-utils@8.50.0': + resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.38.0': - resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==} + '@typescript-eslint/type-utils@8.50.0': + resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.38.0': - resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==} + '@typescript-eslint/types@8.50.0': + resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.38.0': - resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==} + '@typescript-eslint/typescript-estree@8.50.0': + resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.38.0': - resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==} + '@typescript-eslint/utils@8.50.0': + resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.38.0': - resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==} + '@typescript-eslint/visitor-keys@8.50.0': + resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitest/coverage-v8@3.2.4': - resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} + '@vitest/coverage-v8@4.0.15': + resolution: {integrity: sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==} peerDependencies: - '@vitest/browser': 3.2.4 - vitest: 3.2.4 + '@vitest/browser': 4.0.15 + vitest: 4.0.15 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@3.2.4': - resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.0.15': + resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} - '@vitest/mocker@3.2.4': - resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + '@vitest/mocker@4.0.15': + resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0-0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@4.0.15': + resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==} - '@vitest/runner@3.2.4': - resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/runner@4.0.15': + resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==} - '@vitest/snapshot@3.2.4': - resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/snapshot@4.0.15': + resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==} - '@vitest/spy@3.2.4': - resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@4.0.15': + resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==} - '@vitest/utils@3.2.4': - resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.0.15': + resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==} + + abitype@1.1.0: + resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true - abitype@1.0.8: - resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} + abitype@1.2.2: + resolution: {integrity: sha512-4DOIMWscIB3j8hboLAUjLZCE8TMLdgecBpHFumfU4PdO/C1SBCVx4Nu1wPYXaL2iK8B0Jk3tiwnDLCpUtm3fZg==} peerDependencies: typescript: '>=5.0.4' - zod: ^3 >=3.22.0 + zod: ^3.22.0 || ^4.0.0 peerDependenciesMeta: typescript: optional: true @@ -1397,8 +1514,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@3.2.1: @@ -1409,8 +1526,8 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} anymatch@3.1.3: @@ -1458,8 +1575,8 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - asn1js@3.0.6: - resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} + asn1js@3.0.7: + resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} engines: {node: '>=12.0.0'} assertion-error@2.0.1: @@ -1470,8 +1587,8 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} - ast-v8-to-istanbul@0.3.3: - resolution: {integrity: sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==} + ast-v8-to-istanbul@0.3.8: + resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} @@ -1487,6 +1604,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.9.7: + resolution: {integrity: sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==} + hasBin: true + basic-ftp@5.0.5: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} @@ -1512,6 +1633,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -1519,10 +1645,6 @@ packages: resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} engines: {node: '>=6.0.0'} - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1542,17 +1664,21 @@ packages: camel-case@3.0.0: resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} - caniuse-lite@1.0.30001743: - resolution: {integrity: sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==} + caniuse-lite@1.0.30001760: + resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} cbor2@1.12.0: resolution: {integrity: sha512-3Cco8XQhi27DogSp9Ri6LYNZLi/TBY/JVnDe+mj06NkBjW/ZYOtekaEU4wZ4xcRMNrFkDv8KNtOAqHyDfz3lYg==} engines: {node: '>=18.7'} - chai@5.2.1: - resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} engines: {node: '>=18'} + chalk-template@1.1.2: + resolution: {integrity: sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==} + engines: {node: '>=14.16'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1565,15 +1691,18 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + change-case@3.1.0: resolution: {integrity: sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==} chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} - engines: {node: '>= 16'} + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} @@ -1591,6 +1720,10 @@ packages: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + cli-spinners@2.9.2: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} @@ -1606,6 +1739,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} + clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -1627,19 +1764,35 @@ packages: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@8.2.2: - resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} - engines: {node: ^14.13.0 || >=16.0.0} + concurrently@9.2.1: + resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} + engines: {node: '>=18'} hasBin: true constant-case@2.0.0: resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==} - core-js-pure@3.44.0: - resolution: {integrity: sha512-gvMQAGB4dfVUxpYD0k3Fq8J+n5bB6Ytl15lqlZrOIXFzxOhtPaObfkQGHtMRdyjIf7z2IeNULwi1jEwyS+ltKQ==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + core-js-pure@3.47.0: + resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -1648,8 +1801,8 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} data-uri-to-buffer@6.0.2: resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} @@ -1667,12 +1820,8 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} - - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1680,10 +1829,6 @@ packages: supports-color: optional: true - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -1714,8 +1859,8 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - detect-libc@2.1.0: - resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} diff@4.0.2: @@ -1737,29 +1882,39 @@ packages: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + effect@3.19.12: + resolution: {integrity: sha512-7F9RGTrCTC3D7nh9Zw+3VlJWwZgo5k33KA+476BAaD0rKIXKZsY/jQ+ipyhR/Avo239Fi6GqAVFs1mqM1IJ7yg==} + + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -1770,8 +1925,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} engines: {node: '>= 0.4'} es-module-lexer@1.7.0: @@ -1793,8 +1948,8 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.25.8: - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} engines: {node: '>=18'} hasBin: true @@ -1815,8 +1970,8 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-prettier@9.1.2: - resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' @@ -1825,9 +1980,9 @@ packages: resolution: {integrity: sha512-2tktqUAT+Q3hCAU0iSf4xAN1k9zOpjK5WO8104mB0rT/dGhOa09582HN5HlbxNbPRZ0THV7nLGvzugcNOSjzfA==} engines: {node: '>=6'} - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 @@ -1837,8 +1992,8 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-turbo@2.5.5: - resolution: {integrity: sha512-IlN65X6W7rgK88u5xl1xC+7FIGKA7eyaca0yxZQ9CBNV6keAaqtjZQLw8ZfXdv7T+MzTLYkYOeOHAv8yCRUx4Q==} + eslint-plugin-turbo@2.6.3: + resolution: {integrity: sha512-91WZ+suhT/pk+qNS0/rqT43xLUlUblsa3a8jKmAStGhkJCmR2uX0oWo/e0Edb+It8MdnteXuYpCkvsK4Vw8FtA==} peerDependencies: eslint: '>6.6.0' turbo: '>2.0.0' @@ -1855,8 +2010,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.31.0: - resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1900,8 +2055,8 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} extendable-error@0.1.7: @@ -1911,10 +2066,14 @@ packages: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} - fake-indexeddb@6.0.1: - resolution: {integrity: sha512-He2AjQGHe46svIFq5+L2Nx/eHDTI1oKgoevBP+TthnjymXiKkeJQ3+ITeWey99Y5+2OaPFbI1qEsx/5RsGtWnQ==} + fake-indexeddb@6.2.5: + resolution: {integrity: sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==} engines: {node: '>=18'} + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1935,8 +2094,9 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fdir@6.4.6: - resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -1974,10 +2134,6 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -2008,10 +2164,22 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -2040,14 +2208,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - - glob@11.0.3: - resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + glob@13.0.0: + resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} engines: {node: 20 || >=22} - hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -2057,8 +2220,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} globalthis@1.0.4: @@ -2073,6 +2236,10 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + globby@14.1.0: + resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} + engines: {node: '>=18'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -2084,16 +2251,13 @@ packages: resolution: {integrity: sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==} engines: {node: '>=10'} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} hasBin: true - happy-dom@20.0.2: - resolution: {integrity: sha512-pYOyu624+6HDbY+qkjILpQGnpvZOusItCk+rvF5/V+6NkcgTKnbOldpIy22tBnxoaLtlM9nXgoqAcW29/B7CIw==} + happy-dom@20.0.11: + resolution: {integrity: sha512-QsCdAUHAmiDeKeaNojb1OHOPF7NjcWPBR7obdu3NwH2a/oyQaLg5d0aaCy/9My6CdPChYF07dvz5chaXBGaD4g==} engines: {node: '>=20.0.0'} has-bigints@1.1.0: @@ -2130,6 +2294,16 @@ packages: header-case@1.0.1: resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + hosted-git-info@8.1.0: + resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} + engines: {node: ^18.17.0 || >=20.5.0} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -2141,8 +2315,8 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - human-id@4.1.1: - resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} + human-id@4.1.3: + resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true human-signals@2.1.0: @@ -2153,8 +2327,12 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - idb@7.1.1: - resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + iconv-lite@0.7.1: + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + engines: {node: '>=0.10.0'} + + idb@8.0.3: + resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2196,22 +2374,25 @@ packages: resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} engines: {node: '>=8.0.0'} - inquirer@8.2.6: - resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + inquirer@8.2.7: + resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} engines: {node: '>=12.0.0'} internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -2256,8 +2437,8 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} is-glob@4.0.3: @@ -2268,6 +2449,10 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + is-lower-case@1.1.3: resolution: {integrity: sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==} @@ -2331,6 +2516,14 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + is-upper-case@1.1.2: resolution: {integrity: sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==} @@ -2377,37 +2570,32 @@ packages: resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} engines: {node: '>=10'} - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jackspeak@4.1.1: - resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} - engines: {node: 20 || >=22} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} hasBin: true - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -2415,17 +2603,28 @@ packages: json-canonicalize@2.0.0: resolution: {integrity: sha512-yyrnK/mEm6Na3ChbJUWueXdapueW0p380RUyTW87XGb1ww8l8hU0pRrGC3vSWHe9CxrbPHX2fGUOZpNiHR0IIg==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} @@ -2438,64 +2637,71 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - lefthook-darwin-arm64@1.12.2: - resolution: {integrity: sha512-fTxeI9tEskrHjc3QyEO+AG7impBXY2Ed8V5aiRc3fw9POfYtVh9b5jRx90fjk2+ld5hf+Z1DsyyLq/vOHDFskQ==} + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + lefthook-darwin-arm64@2.0.12: + resolution: {integrity: sha512-tuBz1sNLien+nKKb8BDopKjS6EnbXU8rQzhMVBY+bnVfsTiYDfbBr4wo/IzA5TcwoTL/b5somCJhljEw6DvSyg==} cpu: [arm64] os: [darwin] - lefthook-darwin-x64@1.12.2: - resolution: {integrity: sha512-T1dCDKAAfdHgYZ8qtrS02SJSHoR52RFcrGArFNll9Mu4ZSV19Sp8BO+kTwDUOcLYdcPGNaqOp9PkRBQGZWQC7g==} + lefthook-darwin-x64@2.0.12: + resolution: {integrity: sha512-FnuUMPPRMJyTEPXg6PotSrFJ8qf8FDLhhD1zLh74D+9Cye5j9n3lcrCQEjXubPT8du/GZLxMBjjffRbcZ8eYDA==} cpu: [x64] os: [darwin] - lefthook-freebsd-arm64@1.12.2: - resolution: {integrity: sha512-2n9z7Q4BKeMBoB9cuEdv0UBQH82Z4GgBQpCrfjCtyzpDnYQwrH8Tkrlnlko4qPh9MM6nLLGIYMKsA5nltzo8Cg==} + lefthook-freebsd-arm64@2.0.12: + resolution: {integrity: sha512-DXElB0qR5e6a8cXkFNYakhwCieypbfh6Y4QG39pzMnLsG03g/nhe093o6owfiUZ4mUFyDM6+0xmy0steOooF2g==} cpu: [arm64] os: [freebsd] - lefthook-freebsd-x64@1.12.2: - resolution: {integrity: sha512-1hNY/irY+/3kjRzKoJYxG+m3BYI8QxopJUK1PQnknGo1Wy5u302SdX+tR7pnpz6JM5chrNw4ozSbKKOvdZ5VEw==} + lefthook-freebsd-x64@2.0.12: + resolution: {integrity: sha512-iJN1ZxFeaDi4Fi3b9jcW9wgyNl19LOv2NaVOaAi/tG6mlIn196cmSdXkOA3+943ZbqbdfV9I+bBcIKwneXDA3Q==} cpu: [x64] os: [freebsd] - lefthook-linux-arm64@1.12.2: - resolution: {integrity: sha512-1W4swYIVRkxq/LFTuuK4oVpd6NtTKY4E3VY2Uq2JDkIOJV46+8qGBF+C/QA9K3O9chLffgN7c+i+NhIuGiZ/Vw==} + lefthook-linux-arm64@2.0.12: + resolution: {integrity: sha512-byvmO4Iri6P0COwM8c3lGgeCV3Q0hh1XJpRfrcZDr4Wslq9O63t6J3T6i87oOtY+UjC9pXLl6xGk6hlUcHZ3BQ==} cpu: [arm64] os: [linux] - lefthook-linux-x64@1.12.2: - resolution: {integrity: sha512-J6VGuMfhq5iCsg1Pv7xULbuXC63gP5LaikT0PhkyBNMi3HQneZFDJ8k/sp0Ue9HkQv6QfWIo3/FgB9gz38MCFw==} + lefthook-linux-x64@2.0.12: + resolution: {integrity: sha512-KBaiinmf336rA+/dmYs7H7TTeAOByB0CyLA7k8IecTCuaiuKr6ez7ktSjht19poa5G+V0mts4GgEGcx6HViR0w==} cpu: [x64] os: [linux] - lefthook-openbsd-arm64@1.12.2: - resolution: {integrity: sha512-wncDRW3ml24DaOyH22KINumjvCohswbQqbxyH2GORRCykSnE859cTjOrRIchTKBIARF7PSeGPUtS7EK0+oDbaw==} + lefthook-openbsd-arm64@2.0.12: + resolution: {integrity: sha512-1QBMXX1UW5rtgC4TB52OKWB7Rz/kCBRB+bKKLT/gDD79aPzLgJANTitQQzgFNIWoa7aM9UvzvIAJzOo6FcFIbg==} cpu: [arm64] os: [openbsd] - lefthook-openbsd-x64@1.12.2: - resolution: {integrity: sha512-2jDOkCHNnc/oK/vR62hAf3vZb1EQ6Md2GjIlgZ/V7A3ztOsM8QZ5IxwYN3D1UOIR5ZnwMBy7PtmTJC/HJrig5w==} + lefthook-openbsd-x64@2.0.12: + resolution: {integrity: sha512-zPcvUzs65GexRA37UHmaZqWuEGSU/zpBaPIY98MybXzzcJfCIf+O0oUQe2riMllwYGvNW0B1y3NOYRziDNe/vA==} cpu: [x64] os: [openbsd] - lefthook-windows-arm64@1.12.2: - resolution: {integrity: sha512-ZMH/q6UNSidhHEG/1QoqIl1n4yPTBWuVmKx5bONtKHicoz4QCQ+QEiNjKsG5OO4C62nfyHGThmweCzZVUQECJw==} + lefthook-windows-arm64@2.0.12: + resolution: {integrity: sha512-kgwxguS2GssoHM4SMTp+ArD/Gjg9q5MinD6iI5vSFpuJygD13ZWiXQQfESMHq9y/v1XkD0BdHTJej49dx8P+Vw==} cpu: [arm64] os: [win32] - lefthook-windows-x64@1.12.2: - resolution: {integrity: sha512-TqT2jIPcTQ9uwaw+v+DTmvnUHM/p7bbsSrPoPX+fRXSGLzFjyiY+12C9dObSwfCQq6rT70xqQJ9AmftJQsa5/Q==} + lefthook-windows-x64@2.0.12: + resolution: {integrity: sha512-Tf/VtSOtF3rBTc9dzRWROa+HuhqaiIV+Xp+1gzlx5+uCueLM0m87Rz6yd4IN5mL7TrDaNkiRXI3FvjCp0dUE4Q==} cpu: [x64] os: [win32] - lefthook@1.12.2: - resolution: {integrity: sha512-2CeTu5NcmoT9YnqsHTq/TF36MlqlzHzhivGx3DrXHwcff4TdvrkIwUTA56huM3Nlo5ODAF/0hlPzaKLmNHCBnQ==} + lefthook@2.0.12: + resolution: {integrity: sha512-I2FdA9cdnq1icwlNz4RADs7exuqe47q1N9+p2LmcP/WfchWh16mvTB82OAD7w7zK9GxblS9GpF7pASaOSl4c7A==} hasBin: true levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2525,13 +2731,14 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - loupe@3.1.4: - resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} - lower-case-first@1.0.2: resolution: {integrity: sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==} @@ -2541,19 +2748,22 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magicast@0.3.5: - resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} @@ -2581,8 +2791,12 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - minimatch@10.0.3: - resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} minimatch@3.1.2: @@ -2636,8 +2850,8 @@ packages: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} - next@15.4.7: - resolution: {integrity: sha512-OcqRugwF7n7mC8OSYjvsZhhG1AYSvulor1EIUsIkbbEbf1qoE5EbH36Swj8WhF4cHqmDgkiam3z1c1W0J1Wifg==} + next@15.5.9: + resolution: {integrity: sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -2664,8 +2878,11 @@ packages: resolution: {integrity: sha512-Cov028YhBZ5aB7MdMWJEmwyBig43aGL5WT4vdoB28Oitau1zZAcHUn8Sgfk9HM33TqhtLJ9PlM/O0Mv+QpV/4Q==} engines: {node: '>=8.9.4'} - nodemon@3.1.10: - resolution: {integrity: sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + nodemon@3.1.11: + resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==} engines: {node: '>=10'} hasBin: true @@ -2673,6 +2890,10 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + npm-package-arg@12.0.2: + resolution: {integrity: sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==} + engines: {node: ^18.17.0 || >=20.5.0} + npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -2705,6 +2926,9 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -2712,6 +2936,10 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2724,6 +2952,10 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} + engines: {node: '>=18'} + os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -2735,16 +2967,8 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - ox@0.7.2: - resolution: {integrity: sha512-JJHxdef3KqwQ8xW9H+UVLWaQiCUh/DeFN/Ii65XwbHTvSuXZveihTV2gs9hhLw5XEWPBrcH7sQBM0rqG7/QurQ==} - peerDependencies: - typescript: '>=5.4.0' - peerDependenciesMeta: - typescript: - optional: true - - ox@0.8.1: - resolution: {integrity: sha512-e+z5epnzV+Zuz91YYujecW8cF01mzmrUtWotJ0oEPym/G82uccs7q0WDHTYL3eiONbTUEvcZrptAKLgTBD3u2A==} + ox@0.9.17: + resolution: {integrity: sha512-rKAnhzhRU3Xh3hiko+i1ZxywZ55eWQzeS/Q4HRKLx2PqfHOolisZHErSsJVipGlmQKHW5qwOED/GighEw9dbLg==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: @@ -2804,6 +3028,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + pascal-case@2.0.1: resolution: {integrity: sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==} @@ -2825,25 +3053,21 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-scurry@2.0.0: - resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} engines: {node: 20 || >=22} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2859,9 +3083,9 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pkijs@3.2.5: - resolution: {integrity: sha512-WX0la7n7CbnguuaIQoT4Fc0IJckPDOUldzOwlZ0nwpOcySS+Six/tXBdc0RX17J5o1To0SAr3xDJjDLsOfDFQA==} - engines: {node: '>=12.0.0'} + pkijs@3.3.3: + resolution: {integrity: sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==} + engines: {node: '>=16.0.0'} possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} @@ -2884,11 +3108,19 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -2906,15 +3138,18 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pvtsutils@1.3.6: resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} - pvutils@1.1.3: - resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} - engines: {node: '>=6.0.0'} + pvutils@1.1.5: + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + engines: {node: '>=16.0.0'} - quansync@0.2.10: - resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2923,22 +3158,26 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@19.1.0: - resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: - react: ^19.1.0 + react: ^19.2.3 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react@19.1.0: - resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} + read-yaml-file@2.1.0: + resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} + engines: {node: '>=10.13'} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -2974,8 +3213,8 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true @@ -2987,6 +3226,10 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -2996,13 +3239,13 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rimraf@6.0.1: - resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + rimraf@6.1.2: + resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==} engines: {node: 20 || >=22} hasBin: true - rollup@4.45.1: - resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} + rollup@4.53.4: + resolution: {integrity: sha512-YpXaaArg0MvrnJpvduEDYIp7uGOqKXbH9NsHGQ6SxKCOsNAjZF018MmxefFUulVP2KLtiGw1UvZbr+/ekjvlDg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3038,15 +3281,15 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true @@ -3065,8 +3308,8 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - sharp@0.34.4: - resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -3111,10 +3354,17 @@ packages: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -3126,8 +3376,8 @@ packages: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} - socks@2.8.6: - resolution: {integrity: sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==} + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} source-map-js@1.2.1: @@ -3138,23 +3388,21 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - spawn-command@0.0.2: - resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.9.0: - resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -3164,9 +3412,9 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} @@ -3194,14 +3442,18 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -3214,9 +3466,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strip-literal@3.0.0: - resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} - styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -3249,43 +3498,41 @@ packages: swap-case@1.1.2: resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} + syncpack@13.0.4: + resolution: {integrity: sha512-kJ9VlRxNCsBD5pJAE29oXeBYbPLhEySQmK4HdpsLv81I6fcDDW17xeJqMwiU3H7/woAVsbgq25DJNS8BeiN5+w==} + engines: {node: '>=18.18.0'} + hasBin: true + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} - test-exclude@7.0.1: - resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} - engines: {node: '>=18'} - through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tightrope@0.2.0: + resolution: {integrity: sha512-Kw36UHxJEELq2VUqdaSGR2/8cAsPgMtvX8uGVU6Jk26O66PhXec0A5ZnRYs47btbtwPDpXXF66+Fo3vimCM9aQ==} + engines: {node: '>=16'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} tinygradient@1.1.5: resolution: {integrity: sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} - engines: {node: '>=14.0.0'} - - tinyspy@4.0.3: - resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} title-case@2.1.1: @@ -3327,44 +3574,47 @@ packages: '@swc/wasm': optional: true + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - turbo-darwin-64@2.5.5: - resolution: {integrity: sha512-RYnTz49u4F5tDD2SUwwtlynABNBAfbyT2uU/brJcyh5k6lDLyNfYKdKmqd3K2ls4AaiALWrFKVSBsiVwhdFNzQ==} + turbo-darwin-64@2.6.3: + resolution: {integrity: sha512-BlJJDc1CQ7SK5Y5qnl7AzpkvKSnpkfPmnA+HeU/sgny3oHZckPV2776ebO2M33CYDSor7+8HQwaodY++IINhYg==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.5.5: - resolution: {integrity: sha512-Tk+ZeSNdBobZiMw9aFypQt0DlLsWSFWu1ymqsAdJLuPoAH05qCfYtRxE1pJuYHcJB5pqI+/HOxtJoQ40726Btw==} + turbo-darwin-arm64@2.6.3: + resolution: {integrity: sha512-MwVt7rBKiOK7zdYerenfCRTypefw4kZCue35IJga9CH1+S50+KTiCkT6LBqo0hHeoH2iKuI0ldTF2a0aB72z3w==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.5.5: - resolution: {integrity: sha512-2/XvMGykD7VgsvWesZZYIIVXMlgBcQy+ZAryjugoTcvJv8TZzSU/B1nShcA7IAjZ0q7OsZ45uP2cOb8EgKT30w==} + turbo-linux-64@2.6.3: + resolution: {integrity: sha512-cqpcw+dXxbnPtNnzeeSyWprjmuFVpHJqKcs7Jym5oXlu/ZcovEASUIUZVN3OGEM6Y/OTyyw0z09tOHNt5yBAVg==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.5.5: - resolution: {integrity: sha512-DW+8CjCjybu0d7TFm9dovTTVg1VRnlkZ1rceO4zqsaLrit3DgHnN4to4uwyuf9s2V/BwS3IYcRy+HG9BL596Iw==} + turbo-linux-arm64@2.6.3: + resolution: {integrity: sha512-MterpZQmjXyr4uM7zOgFSFL3oRdNKeflY7nsjxJb2TklsYqiu3Z9pQ4zRVFFH8n0mLGna7MbQMZuKoWqqHb45w==} cpu: [arm64] os: [linux] - turbo-windows-64@2.5.5: - resolution: {integrity: sha512-q5p1BOy8ChtSZfULuF1BhFMYIx6bevXu4fJ+TE/hyNfyHJIfjl90Z6jWdqAlyaFLmn99X/uw+7d6T/Y/dr5JwQ==} + turbo-windows-64@2.6.3: + resolution: {integrity: sha512-biDU70v9dLwnBdLf+daoDlNJVvqOOP8YEjqNipBHzgclbQlXbsi6Gqqelp5er81Qo3BiRgmTNx79oaZQTPb07Q==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.5.5: - resolution: {integrity: sha512-AXbF1KmpHUq3PKQwddMGoKMYhHsy5t1YBQO8HZ04HLMR0rWv9adYlQ8kaeQJTko1Ay1anOBFTqaxfVOOsu7+1Q==} + turbo-windows-arm64@2.6.3: + resolution: {integrity: sha512-dDHVKpSeukah3VsI/xMEKeTnV9V9cjlpFSUs4bmsUiLu3Yv2ENlgVEZv65wxbeE0bh0jjpmElDT+P1KaCxArQQ==} cpu: [arm64] os: [win32] - turbo@2.5.5: - resolution: {integrity: sha512-eZ7wI6KjtT1eBqCnh2JPXWNUAxtoxxfi6VdBdZFvil0ychCOTxbm7YLRBi1JSt7U3c+u3CLxpoPxLdvr/Npr3A==} + turbo@2.6.3: + resolution: {integrity: sha512-bf6YKUv11l5Xfcmg76PyWoy/e2vbkkxFNBGJSnfdSXQC33ZiUfutYh6IXidc5MhsnrFkWfdNNLyaRk+kHMLlwA==} hasBin: true type-check@0.4.0: @@ -3391,20 +3641,15 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.38.0: - resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==} + typescript-eslint@8.50.0: + resolution: {integrity: sha512-Q1/6yNUmCpH94fbgMUMg2/BSAr/6U7GBk61kZTv1/asghQOWOjTlp9K8mixS5NcJmm2creY+UFfGeW/+OcA64A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - typescript@5.5.4: - resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} - engines: {node: '>=14.17'} - hasBin: true - - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true @@ -3423,6 +3668,13 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -3431,6 +3683,12 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + update-check@1.5.4: resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} @@ -3446,8 +3704,8 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true v8-compile-cache-lib@3.0.1: @@ -3457,21 +3715,20 @@ packages: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - viem@2.33.0: - resolution: {integrity: sha512-SxBM3CmeU+LWLlBclV9MPdbuFV8mQEl0NeRc9iyYU4a7Xb5sr5oku3s/bRGTPpEP+1hCAHYpM09/ui3/dQ6EsA==} + validate-npm-package-name@6.0.2: + resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + viem@2.42.1: + resolution: {integrity: sha512-NzT/f54jT+b0Um6pYzN/uAGMLg+3twhricAzXS+XH8pVIREzPEh7P25rlhPQnLYiPWzQd9mrFcvnm73Sc8bx+A==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: typescript: optional: true - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - - vite@7.0.5: - resolution: {integrity: sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==} + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3510,26 +3767,32 @@ packages: yaml: optional: true - vitest@3.2.4: - resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vitest@4.0.15: + resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.2.4 - '@vitest/ui': 3.2.4 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.15 + '@vitest/browser-preview': 4.0.15 + '@vitest/browser-webdriverio': 4.0.15 + '@vitest/ui': 4.0.15 happy-dom: '*' jsdom: '*' peerDependenciesMeta: '@edge-runtime/vm': optional: true - '@types/debug': + '@opentelemetry/api': optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': optional: true '@vitest/ui': optional: true @@ -3586,15 +3849,15 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3609,14 +3872,25 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -3625,44 +3899,135 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.2.0: + resolution: {integrity: sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==} + snapshots: '@0xsequence/tee-verifier@0.1.2': dependencies: cbor2: 1.12.0 - pkijs: 3.2.5 + pkijs: 3.3.3 + + '@adraffy/ens-normalize@1.11.1': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 - '@adraffy/ens-normalize@1.11.0': {} + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color - '@ampproject/remapping@2.3.0': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 - '@babel/parser@7.28.0': + '@babel/runtime-corejs3@7.28.4': dependencies: - '@babel/types': 7.28.1 + core-js-pure: 3.47.0 + + '@babel/runtime@7.28.4': {} - '@babel/runtime-corejs3@7.28.0': + '@babel/template@7.27.2': dependencies: - core-js-pure: 3.44.0 + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 - '@babel/runtime@7.27.6': {} + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color - '@babel/types@7.28.1': + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 '@bcoe/v8-coverage@1.0.2': {} - '@changesets/apply-release-plan@7.0.12': + '@changesets/apply-release-plan@7.0.14': dependencies: - '@changesets/config': 3.1.1 + '@changesets/config': 3.1.2 '@changesets/get-version-range-type': 0.4.0 '@changesets/git': 3.0.4 '@changesets/should-skip-package': 0.1.2 @@ -3674,7 +4039,7 @@ snapshots: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.7.2 + semver: 7.7.3 '@changesets/assemble-release-plan@6.0.9': dependencies: @@ -3683,44 +4048,46 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 - semver: 7.7.2 + semver: 7.7.3 '@changesets/changelog-git@0.2.1': dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.29.5': + '@changesets/cli@2.29.8(@types/node@25.0.2)': dependencies: - '@changesets/apply-release-plan': 7.0.12 + '@changesets/apply-release-plan': 7.0.14 '@changesets/assemble-release-plan': 6.0.9 '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.1 + '@changesets/config': 3.1.2 '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.13 + '@changesets/get-release-plan': 4.0.14 '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 + '@changesets/read': 0.6.6 '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@25.0.2) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 enquirer: 2.4.1 - external-editor: 3.1.0 fs-extra: 7.0.1 mri: 1.2.0 p-limit: 2.3.0 package-manager-detector: 0.2.11 picocolors: 1.1.1 resolve-from: 5.0.0 - semver: 7.7.2 + semver: 7.7.3 spawndamnit: 3.0.1 term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' - '@changesets/config@3.1.1': + '@changesets/config@3.1.2': dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 @@ -3739,14 +4106,14 @@ snapshots: '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 picocolors: 1.1.1 - semver: 7.7.2 + semver: 7.7.3 - '@changesets/get-release-plan@4.0.13': + '@changesets/get-release-plan@4.0.14': dependencies: '@changesets/assemble-release-plan': 6.0.9 - '@changesets/config': 3.1.1 + '@changesets/config': 3.1.2 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 + '@changesets/read': 0.6.6 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 @@ -3764,10 +4131,10 @@ snapshots: dependencies: picocolors: 1.1.1 - '@changesets/parse@0.4.1': + '@changesets/parse@0.4.2': dependencies: '@changesets/types': 6.1.0 - js-yaml: 3.14.1 + js-yaml: 4.1.1 '@changesets/pre@2.0.2': dependencies: @@ -3776,11 +4143,11 @@ snapshots: '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - '@changesets/read@0.6.5': + '@changesets/read@0.6.6': dependencies: '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.1 + '@changesets/parse': 0.4.2 '@changesets/types': 6.1.0 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -3799,333 +4166,340 @@ snapshots: dependencies: '@changesets/types': 6.1.0 fs-extra: 7.0.1 - human-id: 4.1.1 + human-id: 4.1.3 prettier: 2.8.8 '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@emnapi/runtime@1.5.0': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.8': + '@esbuild/aix-ppc64@0.27.1': optional: true - '@esbuild/android-arm64@0.25.8': + '@esbuild/android-arm64@0.27.1': optional: true - '@esbuild/android-arm@0.25.8': + '@esbuild/android-arm@0.27.1': optional: true - '@esbuild/android-x64@0.25.8': + '@esbuild/android-x64@0.27.1': optional: true - '@esbuild/darwin-arm64@0.25.8': + '@esbuild/darwin-arm64@0.27.1': optional: true - '@esbuild/darwin-x64@0.25.8': + '@esbuild/darwin-x64@0.27.1': optional: true - '@esbuild/freebsd-arm64@0.25.8': + '@esbuild/freebsd-arm64@0.27.1': optional: true - '@esbuild/freebsd-x64@0.25.8': + '@esbuild/freebsd-x64@0.27.1': optional: true - '@esbuild/linux-arm64@0.25.8': + '@esbuild/linux-arm64@0.27.1': optional: true - '@esbuild/linux-arm@0.25.8': + '@esbuild/linux-arm@0.27.1': optional: true - '@esbuild/linux-ia32@0.25.8': + '@esbuild/linux-ia32@0.27.1': optional: true - '@esbuild/linux-loong64@0.25.8': + '@esbuild/linux-loong64@0.27.1': optional: true - '@esbuild/linux-mips64el@0.25.8': + '@esbuild/linux-mips64el@0.27.1': optional: true - '@esbuild/linux-ppc64@0.25.8': + '@esbuild/linux-ppc64@0.27.1': optional: true - '@esbuild/linux-riscv64@0.25.8': + '@esbuild/linux-riscv64@0.27.1': optional: true - '@esbuild/linux-s390x@0.25.8': + '@esbuild/linux-s390x@0.27.1': optional: true - '@esbuild/linux-x64@0.25.8': + '@esbuild/linux-x64@0.27.1': optional: true - '@esbuild/netbsd-arm64@0.25.8': + '@esbuild/netbsd-arm64@0.27.1': optional: true - '@esbuild/netbsd-x64@0.25.8': + '@esbuild/netbsd-x64@0.27.1': optional: true - '@esbuild/openbsd-arm64@0.25.8': + '@esbuild/openbsd-arm64@0.27.1': optional: true - '@esbuild/openbsd-x64@0.25.8': + '@esbuild/openbsd-x64@0.27.1': optional: true - '@esbuild/openharmony-arm64@0.25.8': + '@esbuild/openharmony-arm64@0.27.1': optional: true - '@esbuild/sunos-x64@0.25.8': + '@esbuild/sunos-x64@0.27.1': optional: true - '@esbuild/win32-arm64@0.25.8': + '@esbuild/win32-arm64@0.27.1': optional: true - '@esbuild/win32-ia32@0.25.8': + '@esbuild/win32-ia32@0.27.1': optional: true - '@esbuild/win32-x64@0.25.8': + '@esbuild/win32-x64@0.27.1': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': dependencies: - eslint: 9.31.0 + eslint: 9.39.2 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.0': + '@eslint/config-array@0.21.1': dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.1(supports-color@5.5.0) + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.0': {} + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 - '@eslint/core@0.15.1': + '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.31.0': {} + '@eslint/js@9.39.2': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.3.4': + '@eslint/plugin-kit@0.4.1': dependencies: - '@eslint/core': 0.15.1 + '@eslint/core': 0.17.0 levn: 0.4.1 '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.6': + '@humanfs/node@0.16.7': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.3': {} '@img/colour@1.0.0': optional: true - '@img/sharp-darwin-arm64@0.34.4': + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - '@img/sharp-darwin-x64@0.34.4': + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - '@img/sharp-libvips-darwin-arm64@1.2.3': + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - '@img/sharp-libvips-darwin-x64@1.2.3': + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.3': + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm@1.2.3': + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.3': + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.3': + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.3': + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.3': + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - '@img/sharp-linux-arm64@0.34.4': + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - '@img/sharp-linux-arm@0.34.4': + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - '@img/sharp-linux-ppc64@0.34.4': + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - '@img/sharp-linux-s390x@0.34.4': + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - '@img/sharp-linux-x64@0.34.4': + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.4': + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.4': + '@img/sharp-linuxmusl-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 optional: true - '@img/sharp-wasm32@0.34.4': + '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.5.0 + '@emnapi/runtime': 1.7.1 optional: true - '@img/sharp-win32-arm64@0.34.4': + '@img/sharp-win32-arm64@0.34.5': optional: true - '@img/sharp-win32-ia32@0.34.4': + '@img/sharp-win32-ia32@0.34.5': optional: true - '@img/sharp-win32-x64@0.34.4': + '@img/sharp-win32-x64@0.34.5': optional: true + '@inquirer/external-editor@1.0.3(@types/node@25.0.2)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.1 + optionalDependencies: + '@types/node': 25.0.2 + '@isaacs/balanced-match@4.0.1': {} '@isaacs/brace-expansion@5.0.0': dependencies: '@isaacs/balanced-match': 4.0.1 - '@isaacs/cliui@8.0.2': + '@jridgewell/gen-mapping@0.3.13': dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@istanbuljs/schema@0.1.3': {} + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/gen-mapping@0.3.12': + '@jridgewell/remapping@2.3.5': dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.4': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.29': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@manypkg/find-root@1.1.0': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 '@manypkg/get-packages@1.1.3': dependencies: - '@babel/runtime': 7.27.6 + '@babel/runtime': 7.28.4 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 globby: 11.1.0 read-yaml-file: 1.1.0 - '@next/env@15.4.7': {} + '@next/env@15.5.9': {} - '@next/eslint-plugin-next@15.4.2': + '@next/eslint-plugin-next@15.5.9': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@15.4.7': + '@next/swc-darwin-arm64@15.5.7': optional: true - '@next/swc-darwin-x64@15.4.7': + '@next/swc-darwin-x64@15.5.7': optional: true - '@next/swc-linux-arm64-gnu@15.4.7': + '@next/swc-linux-arm64-gnu@15.5.7': optional: true - '@next/swc-linux-arm64-musl@15.4.7': + '@next/swc-linux-arm64-musl@15.5.7': optional: true - '@next/swc-linux-x64-gnu@15.4.7': + '@next/swc-linux-x64-gnu@15.5.7': optional: true - '@next/swc-linux-x64-musl@15.4.7': + '@next/swc-linux-x64-musl@15.5.7': optional: true - '@next/swc-win32-arm64-msvc@15.4.7': + '@next/swc-win32-arm64-msvc@15.5.7': optional: true - '@next/swc-win32-x64-msvc@15.4.7': + '@next/swc-win32-x64-msvc@15.5.7': optional: true '@noble/ciphers@1.3.0': {} - '@noble/curves@1.9.2': + '@noble/curves@1.9.1': dependencies: '@noble/hashes': 1.8.0 - '@noble/curves@1.9.4': - dependencies: - '@noble/hashes': 1.8.0 + '@noble/hashes@1.4.0': {} '@noble/hashes@1.8.0': {} @@ -4141,74 +4515,77 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@pkgjs/parseargs@0.11.0': + '@rollup/rollup-android-arm-eabi@4.53.4': + optional: true + + '@rollup/rollup-android-arm64@4.53.4': optional: true - '@rollup/rollup-android-arm-eabi@4.45.1': + '@rollup/rollup-darwin-arm64@4.53.4': optional: true - '@rollup/rollup-android-arm64@4.45.1': + '@rollup/rollup-darwin-x64@4.53.4': optional: true - '@rollup/rollup-darwin-arm64@4.45.1': + '@rollup/rollup-freebsd-arm64@4.53.4': optional: true - '@rollup/rollup-darwin-x64@4.45.1': + '@rollup/rollup-freebsd-x64@4.53.4': optional: true - '@rollup/rollup-freebsd-arm64@4.45.1': + '@rollup/rollup-linux-arm-gnueabihf@4.53.4': optional: true - '@rollup/rollup-freebsd-x64@4.45.1': + '@rollup/rollup-linux-arm-musleabihf@4.53.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.45.1': + '@rollup/rollup-linux-arm64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.45.1': + '@rollup/rollup-linux-arm64-musl@4.53.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.45.1': + '@rollup/rollup-linux-loong64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.45.1': + '@rollup/rollup-linux-ppc64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.45.1': + '@rollup/rollup-linux-riscv64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': + '@rollup/rollup-linux-riscv64-musl@4.53.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.45.1': + '@rollup/rollup-linux-s390x-gnu@4.53.4': optional: true - '@rollup/rollup-linux-riscv64-musl@4.45.1': + '@rollup/rollup-linux-x64-gnu@4.53.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.45.1': + '@rollup/rollup-linux-x64-musl@4.53.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.45.1': + '@rollup/rollup-openharmony-arm64@4.53.4': optional: true - '@rollup/rollup-linux-x64-musl@4.45.1': + '@rollup/rollup-win32-arm64-msvc@4.53.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.45.1': + '@rollup/rollup-win32-ia32-msvc@4.53.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.45.1': + '@rollup/rollup-win32-x64-gnu@4.53.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.45.1': + '@rollup/rollup-win32-x64-msvc@4.53.4': optional: true '@scure/base@1.2.6': {} '@scure/bip32@1.7.0': dependencies: - '@noble/curves': 1.9.4 + '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 @@ -4217,13 +4594,17 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 + '@sindresorhus/merge-streams@2.3.0': {} + + '@standard-schema/spec@1.0.0': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 '@tootallnate/quickjs-emscripten@0.23.0': {} - '@tsconfig/node10@1.0.11': {} + '@tsconfig/node10@1.0.12': {} '@tsconfig/node12@1.0.11': {} @@ -4231,17 +4612,17 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@turbo/gen@1.13.4(@types/node@20.19.9)(typescript@5.5.4)': + '@turbo/gen@1.13.4(@types/node@25.0.2)(typescript@5.9.3)': dependencies: - '@turbo/workspaces': 1.13.4 + '@turbo/workspaces': 1.13.4(@types/node@25.0.2) chalk: 2.4.2 commander: 10.0.1 fs-extra: 10.1.0 - inquirer: 8.2.6 + inquirer: 8.2.7(@types/node@25.0.2) minimatch: 9.0.5 node-plop: 0.26.3 proxy-agent: 6.5.0 - ts-node: 10.9.2(@types/node@20.19.9)(typescript@5.5.4) + ts-node: 10.9.2(@types/node@25.0.2)(typescript@5.9.3) update-check: 1.5.4 validate-npm-package-name: 5.0.1 transitivePeerDependencies: @@ -4251,7 +4632,7 @@ snapshots: - supports-color - typescript - '@turbo/workspaces@1.13.4': + '@turbo/workspaces@1.13.4(@types/node@25.0.2)': dependencies: chalk: 2.4.2 commander: 10.0.1 @@ -4259,16 +4640,19 @@ snapshots: fast-glob: 3.3.3 fs-extra: 10.1.0 gradient-string: 2.0.2 - inquirer: 8.2.6 - js-yaml: 4.1.0 + inquirer: 8.2.7(@types/node@25.0.2) + js-yaml: 4.1.1 ora: 4.1.1 rimraf: 3.0.2 - semver: 7.7.2 + semver: 7.7.3 update-check: 1.5.4 + transitivePeerDependencies: + - '@types/node' - '@types/chai@5.2.2': + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 '@types/deep-eql@4.0.2': {} @@ -4277,7 +4661,7 @@ snapshots: '@types/glob@7.2.0': dependencies: '@types/minimatch': 6.0.0 - '@types/node': 22.16.5 + '@types/node': 25.0.2 '@types/inquirer@6.5.0': dependencies: @@ -4292,41 +4676,25 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@20.19.21': - dependencies: - undici-types: 6.21.0 - - '@types/node@20.19.9': - dependencies: - undici-types: 6.21.0 - - '@types/node@22.16.5': + '@types/node@20.19.27': dependencies: undici-types: 6.21.0 - '@types/prop-types@15.7.15': {} - - '@types/react-dom@18.3.0': - dependencies: - '@types/react': 18.3.1 - - '@types/react-dom@18.3.1': + '@types/node@25.0.2': dependencies: - '@types/react': 18.3.1 + undici-types: 7.16.0 - '@types/react@18.3.0': + '@types/react-dom@19.2.3(@types/react@19.2.7)': dependencies: - '@types/prop-types': 15.7.15 - csstype: 3.1.3 + '@types/react': 19.2.7 - '@types/react@18.3.1': + '@types/react@19.2.7': dependencies: - '@types/prop-types': 15.7.15 - csstype: 3.1.3 + csstype: 3.2.3 '@types/through@0.0.33': dependencies: - '@types/node': 22.16.5 + '@types/node': 25.0.2 '@types/tinycolor2@1.4.6': {} @@ -4334,167 +4702,166 @@ snapshots: '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.33': + '@types/yargs@17.0.35': dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.38.0(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.38.0 - '@typescript-eslint/type-utils': 8.38.0(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.38.0(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.38.0 - eslint: 9.31.0 - graphemer: 1.4.0 + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.38.0(eslint@9.31.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.38.0 - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.38.0 - debug: 4.4.1(supports-color@5.5.0) - eslint: 9.31.0 - typescript: 5.8.3 + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + debug: 4.4.3(supports-color@5.5.0) + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.38.0(typescript@5.8.3)': + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3) - '@typescript-eslint/types': 8.38.0 - debug: 4.4.1(supports-color@5.5.0) - typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + debug: 4.4.3(supports-color@5.5.0) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.38.0': + '@typescript-eslint/scope-manager@8.50.0': dependencies: - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/visitor-keys': 8.38.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 - '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': dependencies: - typescript: 5.8.3 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.38.0(eslint@9.31.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.38.0(eslint@9.31.0)(typescript@5.8.3) - debug: 4.4.1(supports-color@5.5.0) - eslint: 9.31.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.3(supports-color@5.5.0) + eslint: 9.39.2 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.38.0': {} + '@typescript-eslint/types@8.50.0': {} - '@typescript-eslint/typescript-estree@8.38.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.38.0(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3) - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/visitor-keys': 8.38.0 - debug: 4.4.1(supports-color@5.5.0) - fast-glob: 3.3.3 - is-glob: 4.0.3 + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 + debug: 4.4.3(supports-color@5.5.0) minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.38.0(eslint@9.31.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) - '@typescript-eslint/scope-manager': 8.38.0 - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) - eslint: 9.31.0 - typescript: 5.8.3 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.38.0': + '@typescript-eslint/visitor-keys@8.50.0': dependencies: - '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/types': 8.50.0 eslint-visitor-keys: 4.2.1 - '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@22.16.5)(happy-dom@20.0.2))': + '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@types/node@25.0.2)(happy-dom@20.0.11))': dependencies: - '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - ast-v8-to-istanbul: 0.3.3 - debug: 4.4.1(supports-color@5.5.0) + '@vitest/utils': 4.0.15 + ast-v8-to-istanbul: 0.3.8 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.1.7 - magic-string: 0.30.17 - magicast: 0.3.5 - std-env: 3.9.0 - test-exclude: 7.0.1 - tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@22.16.5)(happy-dom@20.0.2) + istanbul-reports: 3.2.0 + magicast: 0.5.1 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.15(@types/node@25.0.2)(happy-dom@20.0.11) transitivePeerDependencies: - supports-color - '@vitest/expect@3.2.4': + '@vitest/expect@4.0.15': dependencies: - '@types/chai': 5.2.2 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.2.1 - tinyrainbow: 2.0.0 + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + chai: 6.2.1 + tinyrainbow: 3.0.3 - '@vitest/mocker@3.2.4(vite@7.0.5(@types/node@22.16.5))': + '@vitest/mocker@4.0.15(vite@7.3.0(@types/node@25.0.2))': dependencies: - '@vitest/spy': 3.2.4 + '@vitest/spy': 4.0.15 estree-walker: 3.0.3 - magic-string: 0.30.17 + magic-string: 0.30.21 optionalDependencies: - vite: 7.0.5(@types/node@22.16.5) + vite: 7.3.0(@types/node@25.0.2) - '@vitest/pretty-format@3.2.4': + '@vitest/pretty-format@4.0.15': dependencies: - tinyrainbow: 2.0.0 + tinyrainbow: 3.0.3 - '@vitest/runner@3.2.4': + '@vitest/runner@4.0.15': dependencies: - '@vitest/utils': 3.2.4 + '@vitest/utils': 4.0.15 pathe: 2.0.3 - strip-literal: 3.0.0 - '@vitest/snapshot@3.2.4': + '@vitest/snapshot@4.0.15': dependencies: - '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.17 + '@vitest/pretty-format': 4.0.15 + magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@3.2.4': - dependencies: - tinyspy: 4.0.3 + '@vitest/spy@4.0.15': {} - '@vitest/utils@3.2.4': + '@vitest/utils@4.0.15': dependencies: - '@vitest/pretty-format': 3.2.4 - loupe: 3.1.4 - tinyrainbow: 2.0.0 + '@vitest/pretty-format': 4.0.15 + tinyrainbow: 3.0.3 + + abitype@1.1.0(typescript@5.9.3)(zod@4.2.0): + optionalDependencies: + typescript: 5.9.3 + zod: 4.2.0 - abitype@1.0.8(typescript@5.8.3): + abitype@1.2.2(typescript@5.9.3)(zod@4.2.0): optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.3 + zod: 4.2.0 acorn-jsx@5.3.2(acorn@8.15.0): dependencies: @@ -4528,7 +4895,7 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@3.2.1: dependencies: @@ -4538,7 +4905,7 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} anymatch@3.1.3: dependencies: @@ -4563,7 +4930,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 @@ -4575,7 +4942,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -4584,21 +4951,21 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 @@ -4607,15 +4974,15 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 - asn1js@3.0.6: + asn1js@3.0.7: dependencies: pvtsutils: 1.3.6 - pvutils: 1.1.3 + pvutils: 1.1.5 tslib: 2.8.1 assertion-error@2.0.1: {} @@ -4624,9 +4991,9 @@ snapshots: dependencies: tslib: 2.8.1 - ast-v8-to-istanbul@0.3.3: + ast-v8-to-istanbul@0.3.8: dependencies: - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 js-tokens: 9.0.1 @@ -4640,6 +5007,8 @@ snapshots: base64-js@1.5.1: {} + baseline-browser-mapping@2.9.7: {} + basic-ftp@5.0.5: {} better-path-resolve@1.0.0: @@ -4667,6 +5036,14 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.7 + caniuse-lite: 1.0.30001760 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.2(browserslist@4.28.1) + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -4674,8 +5051,6 @@ snapshots: bytestreamjs@2.0.1: {} - cac@6.7.14: {} - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -4700,17 +5075,15 @@ snapshots: no-case: 2.3.2 upper-case: 1.1.3 - caniuse-lite@1.0.30001743: {} + caniuse-lite@1.0.30001760: {} cbor2@1.12.0: {} - chai@5.2.1: + chai@6.2.1: {} + + chalk-template@1.1.2: dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.1.4 - pathval: 2.0.1 + chalk: 5.6.2 chalk@2.4.2: dependencies: @@ -4728,6 +5101,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + change-case@3.1.0: dependencies: camel-case: 3.0.0 @@ -4751,7 +5126,7 @@ snapshots: chardet@0.7.0: {} - check-error@2.1.1: {} + chardet@2.1.1: {} chokidar@3.6.0: dependencies: @@ -4773,6 +5148,10 @@ snapshots: dependencies: restore-cursor: 3.1.0 + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + cli-spinners@2.9.2: {} cli-width@3.0.0: {} @@ -4785,6 +5164,12 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + cliui@9.0.1: + dependencies: + string-width: 7.2.0 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + clone@1.0.4: {} color-convert@1.9.3: @@ -4801,16 +5186,15 @@ snapshots: commander@10.0.1: {} + commander@13.1.0: {} + concat-map@0.0.1: {} - concurrently@8.2.2: + concurrently@9.2.1: dependencies: chalk: 4.1.2 - date-fns: 2.30.0 - lodash: 4.17.21 rxjs: 7.8.2 shell-quote: 1.8.3 - spawn-command: 0.0.2 supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 @@ -4820,7 +5204,18 @@ snapshots: snake-case: 2.1.0 upper-case: 1.1.3 - core-js-pure@3.44.0: {} + convert-source-map@2.0.0: {} + + core-js-pure@3.47.0: {} + + cosmiconfig@9.0.0(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 create-require@1.1.1: {} @@ -4830,7 +5225,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - csstype@3.1.3: {} + csstype@3.2.3: {} data-uri-to-buffer@6.0.2: {} @@ -4852,18 +5247,12 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 - date-fns@2.30.0: - dependencies: - '@babel/runtime': 7.27.6 - - debug@4.4.1(supports-color@5.5.0): + debug@4.4.3(supports-color@5.5.0): dependencies: ms: 2.1.3 optionalDependencies: supports-color: 5.5.0 - deep-eql@5.0.2: {} - deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -4903,7 +5292,7 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.1.0: + detect-libc@2.1.2: optional: true diff@4.0.2: {} @@ -4922,7 +5311,7 @@ snapshots: dotenv@16.0.3: {} - dotenv@16.6.1: {} + dotenv@17.2.3: {} dunder-proto@1.0.1: dependencies: @@ -4930,18 +5319,29 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - eastasianwidth@0.2.0: {} + effect@3.19.12: + dependencies: + '@standard-schema/spec': 1.0.0 + fast-check: 3.23.2 + + electron-to-chromium@1.5.267: {} - emoji-regex@8.0.0: {} + emoji-regex@10.6.0: {} - emoji-regex@9.2.2: {} + emoji-regex@8.0.0: {} enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - es-abstract@1.24.0: + env-paths@2.2.1: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -5002,12 +5402,12 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.2.1: + es-iterator-helpers@1.2.2: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -5044,34 +5444,34 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.25.8: + esbuild@0.27.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.8 - '@esbuild/android-arm': 0.25.8 - '@esbuild/android-arm64': 0.25.8 - '@esbuild/android-x64': 0.25.8 - '@esbuild/darwin-arm64': 0.25.8 - '@esbuild/darwin-x64': 0.25.8 - '@esbuild/freebsd-arm64': 0.25.8 - '@esbuild/freebsd-x64': 0.25.8 - '@esbuild/linux-arm': 0.25.8 - '@esbuild/linux-arm64': 0.25.8 - '@esbuild/linux-ia32': 0.25.8 - '@esbuild/linux-loong64': 0.25.8 - '@esbuild/linux-mips64el': 0.25.8 - '@esbuild/linux-ppc64': 0.25.8 - '@esbuild/linux-riscv64': 0.25.8 - '@esbuild/linux-s390x': 0.25.8 - '@esbuild/linux-x64': 0.25.8 - '@esbuild/netbsd-arm64': 0.25.8 - '@esbuild/netbsd-x64': 0.25.8 - '@esbuild/openbsd-arm64': 0.25.8 - '@esbuild/openbsd-x64': 0.25.8 - '@esbuild/openharmony-arm64': 0.25.8 - '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 escalade@3.2.0: {} @@ -5087,25 +5487,32 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@9.1.2(eslint@9.31.0): + eslint-config-prettier@10.1.8(eslint@9.39.2): dependencies: - eslint: 9.31.0 + eslint: 9.39.2 eslint-plugin-only-warn@1.1.0: {} - eslint-plugin-react-hooks@5.2.0(eslint@9.31.0): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.2): dependencies: - eslint: 9.31.0 + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + eslint: 9.39.2 + hermes-parser: 0.25.1 + zod: 4.2.0 + zod-validation-error: 4.0.2(zod@4.2.0) + transitivePeerDependencies: + - supports-color - eslint-plugin-react@7.37.5(eslint@9.31.0): + eslint-plugin-react@7.37.5(eslint@9.39.2): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.31.0 + es-iterator-helpers: 1.2.2 + eslint: 9.39.2 estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -5119,11 +5526,11 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.5.5(eslint@9.31.0)(turbo@2.5.5): + eslint-plugin-turbo@2.6.3(eslint@9.39.2)(turbo@2.6.3): dependencies: dotenv: 16.0.3 - eslint: 9.31.0 - turbo: 2.5.5 + eslint: 9.39.2 + turbo: 2.6.3 eslint-scope@8.4.0: dependencies: @@ -5134,25 +5541,24 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.31.0: - dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.0 - '@eslint/core': 0.15.1 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.31.0 - '@eslint/plugin-kit': 0.3.4 - '@humanfs/node': 0.16.6 + eslint@9.39.2: + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -5212,7 +5618,7 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - expect-type@1.2.2: {} + expect-type@1.3.0: {} extendable-error@0.1.7: {} @@ -5222,7 +5628,11 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 - fake-indexeddb@6.0.1: {} + fake-indexeddb@6.2.5: {} + + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 fast-deep-equal@3.1.3: {} @@ -5250,7 +5660,7 @@ snapshots: dependencies: reusify: 1.1.0 - fdir@6.4.6(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -5287,15 +5697,10 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.1.0 + jsonfile: 6.2.0 universalify: 2.0.1 fs-extra@7.0.1: @@ -5328,8 +5733,14 @@ snapshots: functions-have-names@1.2.3: {} + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-east-asian-width@1.4.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5360,7 +5771,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -5372,23 +5783,11 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - glob@11.0.3: + glob@13.0.0: dependencies: - foreground-child: 3.3.1 - jackspeak: 4.1.1 - minimatch: 10.0.3 + minimatch: 10.1.1 minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 2.0.0 + path-scurry: 2.0.1 glob@7.2.3: dependencies: @@ -5401,7 +5800,7 @@ snapshots: globals@14.0.0: {} - globals@15.15.0: {} + globals@16.5.0: {} globalthis@1.0.4: dependencies: @@ -5428,6 +5827,15 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + globby@14.1.0: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -5437,8 +5845,6 @@ snapshots: chalk: 4.1.2 tinygradient: 1.1.5 - graphemer@1.4.0: {} - handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -5448,9 +5854,9 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 - happy-dom@20.0.2: + happy-dom@20.0.11: dependencies: - '@types/node': 20.19.21 + '@types/node': 20.19.27 '@types/whatwg-mimetype': 3.0.2 whatwg-mimetype: 3.0.0 @@ -5483,23 +5889,33 @@ snapshots: no-case: 2.3.2 upper-case: 1.1.3 + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + hosted-git-info@8.1.0: + dependencies: + lru-cache: 10.4.3 + html-escaper@2.0.2: {} http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color - human-id@4.1.1: {} + human-id@4.1.3: {} human-signals@2.1.0: {} @@ -5507,7 +5923,11 @@ snapshots: dependencies: safer-buffer: 2.1.2 - idb@7.1.1: {} + iconv-lite@0.7.1: + dependencies: + safer-buffer: 2.1.2 + + idb@8.0.3: {} ieee754@1.2.1: {} @@ -5551,13 +5971,13 @@ snapshots: strip-ansi: 6.0.1 through: 2.3.8 - inquirer@8.2.6: + inquirer@8.2.7(@types/node@25.0.2): dependencies: + '@inquirer/external-editor': 1.0.3(@types/node@25.0.2) ansi-escapes: 4.3.2 chalk: 4.1.2 cli-cursor: 3.1.0 cli-width: 3.0.0 - external-editor: 3.1.0 figures: 3.2.0 lodash: 4.17.21 mute-stream: 0.0.8 @@ -5568,6 +5988,8 @@ snapshots: strip-ansi: 6.0.1 through: 2.3.8 wrap-ansi: 6.2.0 + transitivePeerDependencies: + - '@types/node' internal-slot@1.1.0: dependencies: @@ -5575,10 +5997,7 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - ip-address@9.0.5: - dependencies: - jsbn: 1.1.0 - sprintf-js: 1.1.3 + ip-address@10.1.0: {} is-array-buffer@3.0.5: dependencies: @@ -5586,6 +6005,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} + is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -5632,9 +6053,10 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.0: + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 + generator-function: 2.0.1 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -5645,6 +6067,8 @@ snapshots: is-interactive@1.0.0: {} + is-interactive@2.0.0: {} + is-lower-case@1.1.3: dependencies: lower-case: 1.1.4 @@ -5700,6 +6124,10 @@ snapshots: is-unicode-supported@0.1.0: {} + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + is-upper-case@1.1.2: dependencies: upper-case: 1.1.3 @@ -5723,9 +6151,9 @@ snapshots: isexe@2.0.0: {} - isows@1.0.7(ws@8.18.2): + isows@1.0.7(ws@8.18.3): dependencies: - ws: 8.18.2 + ws: 8.18.3 istanbul-lib-coverage@3.2.2: {} @@ -5737,13 +6165,13 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: - '@jridgewell/trace-mapping': 0.3.29 - debug: 4.4.1(supports-color@5.5.0) + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color - istanbul-reports@3.1.7: + istanbul-reports@3.2.0: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 @@ -5757,44 +6185,40 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jackspeak@4.1.1: - dependencies: - '@isaacs/cliui': 8.0.2 - js-tokens@4.0.0: {} js-tokens@9.0.1: {} - js-yaml@3.14.1: + js-yaml@3.14.2: dependencies: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.1.0: + js-yaml@4.1.1: dependencies: argparse: 2.0.1 - jsbn@1.1.0: {} + jsesc@3.1.0: {} json-buffer@3.0.1: {} json-canonicalize@2.0.0: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - jsonfile@6.1.0: + jsonfile@6.2.0: dependencies: universalify: 2.0.1 optionalDependencies: @@ -5813,54 +6237,58 @@ snapshots: dependencies: json-buffer: 3.0.1 - lefthook-darwin-arm64@1.12.2: + kleur@3.0.3: {} + + lefthook-darwin-arm64@2.0.12: optional: true - lefthook-darwin-x64@1.12.2: + lefthook-darwin-x64@2.0.12: optional: true - lefthook-freebsd-arm64@1.12.2: + lefthook-freebsd-arm64@2.0.12: optional: true - lefthook-freebsd-x64@1.12.2: + lefthook-freebsd-x64@2.0.12: optional: true - lefthook-linux-arm64@1.12.2: + lefthook-linux-arm64@2.0.12: optional: true - lefthook-linux-x64@1.12.2: + lefthook-linux-x64@2.0.12: optional: true - lefthook-openbsd-arm64@1.12.2: + lefthook-openbsd-arm64@2.0.12: optional: true - lefthook-openbsd-x64@1.12.2: + lefthook-openbsd-x64@2.0.12: optional: true - lefthook-windows-arm64@1.12.2: + lefthook-windows-arm64@2.0.12: optional: true - lefthook-windows-x64@1.12.2: + lefthook-windows-x64@2.0.12: optional: true - lefthook@1.12.2: + lefthook@2.0.12: optionalDependencies: - lefthook-darwin-arm64: 1.12.2 - lefthook-darwin-x64: 1.12.2 - lefthook-freebsd-arm64: 1.12.2 - lefthook-freebsd-x64: 1.12.2 - lefthook-linux-arm64: 1.12.2 - lefthook-linux-x64: 1.12.2 - lefthook-openbsd-arm64: 1.12.2 - lefthook-openbsd-x64: 1.12.2 - lefthook-windows-arm64: 1.12.2 - lefthook-windows-x64: 1.12.2 + lefthook-darwin-arm64: 2.0.12 + lefthook-darwin-x64: 2.0.12 + lefthook-freebsd-arm64: 2.0.12 + lefthook-freebsd-x64: 2.0.12 + lefthook-linux-arm64: 2.0.12 + lefthook-linux-x64: 2.0.12 + lefthook-openbsd-arm64: 2.0.12 + lefthook-openbsd-x64: 2.0.12 + lefthook-windows-arm64: 2.0.12 + lefthook-windows-x64: 2.0.12 levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 + lines-and-columns@1.2.4: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -5886,12 +6314,15 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + log-symbols@6.0.0: + dependencies: + chalk: 5.6.2 + is-unicode-supported: 1.3.0 + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - loupe@3.1.4: {} - lower-case-first@1.0.2: dependencies: lower-case: 1.1.4 @@ -5900,23 +6331,27 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.1.0: {} + lru-cache@11.2.4: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 lru-cache@7.18.3: {} - magic-string@0.30.17: + magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 - magicast@0.3.5: + magicast@0.5.1: dependencies: - '@babel/parser': 7.28.0 - '@babel/types': 7.28.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 source-map-js: 1.2.1 make-dir@4.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 make-error@1.3.6: {} @@ -5933,7 +6368,9 @@ snapshots: mimic-fn@2.1.0: {} - minimatch@10.0.3: + mimic-function@5.0.1: {} + + minimatch@10.1.1: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -5949,9 +6386,9 @@ snapshots: minipass@7.1.2: {} - mipd@0.0.7(typescript@5.8.3): + mipd@0.0.7(typescript@5.9.3): optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.3 mkdirp@0.5.6: dependencies: @@ -5971,25 +6408,25 @@ snapshots: netmask@2.0.2: {} - next@15.4.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.5.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: - '@next/env': 15.4.7 + '@next/env': 15.5.9 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001743 + caniuse-lite: 1.0.30001760 postcss: 8.4.31 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(react@19.1.0) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + styled-jsx: 5.1.6(react@19.2.3) optionalDependencies: - '@next/swc-darwin-arm64': 15.4.7 - '@next/swc-darwin-x64': 15.4.7 - '@next/swc-linux-arm64-gnu': 15.4.7 - '@next/swc-linux-arm64-musl': 15.4.7 - '@next/swc-linux-x64-gnu': 15.4.7 - '@next/swc-linux-x64-musl': 15.4.7 - '@next/swc-win32-arm64-msvc': 15.4.7 - '@next/swc-win32-x64-msvc': 15.4.7 - sharp: 0.34.4 + '@next/swc-darwin-arm64': 15.5.7 + '@next/swc-darwin-x64': 15.5.7 + '@next/swc-linux-arm64-gnu': 15.5.7 + '@next/swc-linux-arm64-musl': 15.5.7 + '@next/swc-linux-x64-gnu': 15.5.7 + '@next/swc-linux-x64-musl': 15.5.7 + '@next/swc-win32-arm64-msvc': 15.5.7 + '@next/swc-win32-x64-msvc': 15.5.7 + sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -6000,7 +6437,7 @@ snapshots: node-plop@0.26.3: dependencies: - '@babel/runtime-corejs3': 7.28.0 + '@babel/runtime-corejs3': 7.28.4 '@types/inquirer': 6.5.0 change-case: 3.1.0 del: 5.1.0 @@ -6010,16 +6447,18 @@ snapshots: isbinaryfile: 4.0.10 lodash.get: 4.4.2 mkdirp: 0.5.6 - resolve: 1.22.10 + resolve: 1.22.11 - nodemon@3.1.10: + node-releases@2.0.27: {} + + nodemon@3.1.11: dependencies: chokidar: 3.6.0 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) ignore-by-default: 1.0.1 minimatch: 3.1.2 pstree.remy: 1.1.8 - semver: 7.7.2 + semver: 7.7.3 simple-update-notifier: 2.0.0 supports-color: 5.5.0 touch: 3.1.1 @@ -6027,6 +6466,13 @@ snapshots: normalize-path@3.0.0: {} + npm-package-arg@12.0.2: + dependencies: + hosted-git-info: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.3 + validate-npm-package-name: 6.0.2 + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -6057,7 +6503,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.values@1.2.1: @@ -6067,6 +6513,8 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + obug@2.1.1: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -6075,6 +6523,10 @@ snapshots: dependencies: mimic-fn: 2.1.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -6107,6 +6559,18 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + ora@8.2.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.2 + os-tmpdir@1.0.2: {} outdent@0.5.0: {} @@ -6117,33 +6581,18 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - ox@0.7.2(typescript@5.8.3): - dependencies: - '@adraffy/ens-normalize': 1.11.0 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.4 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.8.3) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - zod - - ox@0.8.1(typescript@5.8.3): + ox@0.9.17(typescript@5.9.3)(zod@4.2.0): dependencies: - '@adraffy/ens-normalize': 1.11.0 + '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.2 + '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.8.3) + abitype: 1.2.2(typescript@5.9.3)(zod@4.2.0) eventemitter3: 5.0.1 optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - zod @@ -6179,7 +6628,7 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) get-uri: 6.0.5 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -6197,7 +6646,7 @@ snapshots: package-manager-detector@0.2.11: dependencies: - quansync: 0.2.10 + quansync: 0.2.11 param-case@2.1.1: dependencies: @@ -6207,6 +6656,13 @@ snapshots: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + pascal-case@2.0.1: dependencies: camel-case: 3.0.0 @@ -6224,21 +6680,16 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - path-scurry@2.0.0: + path-scurry@2.0.1: dependencies: - lru-cache: 11.1.0 + lru-cache: 11.2.4 minipass: 7.1.2 path-type@4.0.0: {} - pathe@2.0.3: {} + path-type@6.0.0: {} - pathval@2.0.1: {} + pathe@2.0.3: {} picocolors@1.1.1: {} @@ -6248,13 +6699,13 @@ snapshots: pify@4.0.1: {} - pkijs@3.2.5: + pkijs@3.3.3: dependencies: - '@noble/hashes': 1.8.0 - asn1js: 3.0.6 + '@noble/hashes': 1.4.0 + asn1js: 3.0.7 bytestreamjs: 2.0.1 pvtsutils: 1.3.6 - pvutils: 1.1.3 + pvutils: 1.1.5 tslib: 2.8.1 possible-typed-array-names@1.1.0: {} @@ -6275,7 +6726,14 @@ snapshots: prettier@2.8.8: {} - prettier@3.6.2: {} + prettier@3.7.4: {} + + proc-log@5.0.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 prop-types@15.8.1: dependencies: @@ -6286,7 +6744,7 @@ snapshots: proxy-agent@6.5.0: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -6302,13 +6760,15 @@ snapshots: punycode@2.3.1: {} + pure-rand@6.1.0: {} + pvtsutils@1.3.6: dependencies: tslib: 2.8.1 - pvutils@1.1.3: {} + pvutils@1.1.5: {} - quansync@0.2.10: {} + quansync@0.2.11: {} queue-microtask@1.2.3: {} @@ -6319,22 +6779,27 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-dom@19.1.0(react@19.1.0): + react-dom@19.2.3(react@19.2.3): dependencies: - react: 19.1.0 - scheduler: 0.26.0 + react: 19.2.3 + scheduler: 0.27.0 react-is@16.13.1: {} - react@19.1.0: {} + react@19.2.3: {} read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 - js-yaml: 3.14.1 + js-yaml: 3.14.2 pify: 4.0.1 strip-bom: 3.0.0 + read-yaml-file@2.1.0: + dependencies: + js-yaml: 4.1.1 + strip-bom: 4.0.0 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -6349,7 +6814,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -6380,7 +6845,7 @@ snapshots: resolve-from@5.0.0: {} - resolve@1.22.10: + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 @@ -6397,41 +6862,48 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + reusify@1.1.0: {} rimraf@3.0.2: dependencies: glob: 7.2.3 - rimraf@6.0.1: + rimraf@6.1.2: dependencies: - glob: 11.0.3 + glob: 13.0.0 package-json-from-dist: 1.0.1 - rollup@4.45.1: + rollup@4.53.4: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.45.1 - '@rollup/rollup-android-arm64': 4.45.1 - '@rollup/rollup-darwin-arm64': 4.45.1 - '@rollup/rollup-darwin-x64': 4.45.1 - '@rollup/rollup-freebsd-arm64': 4.45.1 - '@rollup/rollup-freebsd-x64': 4.45.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.45.1 - '@rollup/rollup-linux-arm-musleabihf': 4.45.1 - '@rollup/rollup-linux-arm64-gnu': 4.45.1 - '@rollup/rollup-linux-arm64-musl': 4.45.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.45.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1 - '@rollup/rollup-linux-riscv64-gnu': 4.45.1 - '@rollup/rollup-linux-riscv64-musl': 4.45.1 - '@rollup/rollup-linux-s390x-gnu': 4.45.1 - '@rollup/rollup-linux-x64-gnu': 4.45.1 - '@rollup/rollup-linux-x64-musl': 4.45.1 - '@rollup/rollup-win32-arm64-msvc': 4.45.1 - '@rollup/rollup-win32-ia32-msvc': 4.45.1 - '@rollup/rollup-win32-x64-msvc': 4.45.1 + '@rollup/rollup-android-arm-eabi': 4.53.4 + '@rollup/rollup-android-arm64': 4.53.4 + '@rollup/rollup-darwin-arm64': 4.53.4 + '@rollup/rollup-darwin-x64': 4.53.4 + '@rollup/rollup-freebsd-arm64': 4.53.4 + '@rollup/rollup-freebsd-x64': 4.53.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.4 + '@rollup/rollup-linux-arm-musleabihf': 4.53.4 + '@rollup/rollup-linux-arm64-gnu': 4.53.4 + '@rollup/rollup-linux-arm64-musl': 4.53.4 + '@rollup/rollup-linux-loong64-gnu': 4.53.4 + '@rollup/rollup-linux-ppc64-gnu': 4.53.4 + '@rollup/rollup-linux-riscv64-gnu': 4.53.4 + '@rollup/rollup-linux-riscv64-musl': 4.53.4 + '@rollup/rollup-linux-s390x-gnu': 4.53.4 + '@rollup/rollup-linux-x64-gnu': 4.53.4 + '@rollup/rollup-linux-x64-musl': 4.53.4 + '@rollup/rollup-openharmony-arm64': 4.53.4 + '@rollup/rollup-win32-arm64-msvc': 4.53.4 + '@rollup/rollup-win32-ia32-msvc': 4.53.4 + '@rollup/rollup-win32-x64-gnu': 4.53.4 + '@rollup/rollup-win32-x64-msvc': 4.53.4 fsevents: 2.3.3 run-async@2.4.1: {} @@ -6471,11 +6943,11 @@ snapshots: safer-buffer@2.1.2: {} - scheduler@0.26.0: {} + scheduler@0.27.0: {} semver@6.3.1: {} - semver@7.7.2: {} + semver@7.7.3: {} sentence-case@2.1.1: dependencies: @@ -6504,34 +6976,36 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 - sharp@0.34.4: + sharp@0.34.5: dependencies: '@img/colour': 1.0.0 - detect-libc: 2.1.0 - semver: 7.7.2 + detect-libc: 2.1.2 + semver: 7.7.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.4 - '@img/sharp-darwin-x64': 0.34.4 - '@img/sharp-libvips-darwin-arm64': 1.2.3 - '@img/sharp-libvips-darwin-x64': 1.2.3 - '@img/sharp-libvips-linux-arm': 1.2.3 - '@img/sharp-libvips-linux-arm64': 1.2.3 - '@img/sharp-libvips-linux-ppc64': 1.2.3 - '@img/sharp-libvips-linux-s390x': 1.2.3 - '@img/sharp-libvips-linux-x64': 1.2.3 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 - '@img/sharp-libvips-linuxmusl-x64': 1.2.3 - '@img/sharp-linux-arm': 0.34.4 - '@img/sharp-linux-arm64': 0.34.4 - '@img/sharp-linux-ppc64': 0.34.4 - '@img/sharp-linux-s390x': 0.34.4 - '@img/sharp-linux-x64': 0.34.4 - '@img/sharp-linuxmusl-arm64': 0.34.4 - '@img/sharp-linuxmusl-x64': 0.34.4 - '@img/sharp-wasm32': 0.34.4 - '@img/sharp-win32-arm64': 0.34.4 - '@img/sharp-win32-ia32': 0.34.4 - '@img/sharp-win32-x64': 0.34.4 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 optional: true shebang-command@2.0.0: @@ -6578,10 +7052,14 @@ snapshots: simple-update-notifier@2.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 + + sisteransi@1.0.5: {} slash@3.0.0: {} + slash@5.1.0: {} + smart-buffer@4.2.0: {} snake-case@2.1.0: @@ -6591,22 +7069,20 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 - debug: 4.4.1(supports-color@5.5.0) - socks: 2.8.6 + debug: 4.4.3(supports-color@5.5.0) + socks: 2.8.7 transitivePeerDependencies: - supports-color - socks@2.8.6: + socks@2.8.7: dependencies: - ip-address: 9.0.5 + ip-address: 10.1.0 smart-buffer: 4.2.0 source-map-js@1.2.1: {} source-map@0.6.1: {} - spawn-command@0.0.2: {} - spawndamnit@3.0.1: dependencies: cross-spawn: 7.0.6 @@ -6614,11 +7090,11 @@ snapshots: sprintf-js@1.0.3: {} - sprintf-js@1.1.3: {} - stackback@0.0.2: {} - std-env@3.9.0: {} + std-env@3.10.0: {} + + stdin-discarder@0.2.2: {} stop-iteration-iterator@1.1.0: dependencies: @@ -6631,18 +7107,18 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@5.1.2: + string-width@7.2.0: dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + emoji-regex: 10.6.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -6656,7 +7132,7 @@ snapshots: string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.trim@1.2.10: dependencies: @@ -6664,7 +7140,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -6689,26 +7165,24 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 strip-bom@3.0.0: {} + strip-bom@4.0.0: {} + strip-final-newline@2.0.0: {} strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} - strip-literal@3.0.0: - dependencies: - js-tokens: 9.0.1 - - styled-jsx@5.1.6(react@19.1.0): + styled-jsx@5.1.6(react@19.2.3): dependencies: client-only: 0.0.1 - react: 19.1.0 + react: 19.2.3 supports-color@5.5.0: dependencies: @@ -6729,25 +7203,43 @@ snapshots: lower-case: 1.1.4 upper-case: 1.1.3 - term-size@2.2.1: {} - - test-exclude@7.0.1: + syncpack@13.0.4(typescript@5.9.3): dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 10.4.5 + chalk: 5.6.2 + chalk-template: 1.1.2 + commander: 13.1.0 + cosmiconfig: 9.0.0(typescript@5.9.3) + effect: 3.19.12 + enquirer: 2.4.1 + fast-check: 3.23.2 + globby: 14.1.0 + jsonc-parser: 3.3.1 minimatch: 9.0.5 + npm-package-arg: 12.0.2 + ora: 8.2.0 + prompts: 2.4.2 + read-yaml-file: 2.1.0 + semver: 7.7.3 + tightrope: 0.2.0 + ts-toolbelt: 9.6.0 + transitivePeerDependencies: + - typescript + + term-size@2.2.1: {} through@2.3.8: {} + tightrope@0.2.0: {} + tinybench@2.9.0: {} tinycolor2@1.6.0: {} - tinyexec@0.3.2: {} + tinyexec@1.0.2: {} - tinyglobby@0.2.14: + tinyglobby@0.2.15: dependencies: - fdir: 6.4.6(picomatch@4.0.3) + fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 tinygradient@1.1.5: @@ -6755,11 +7247,7 @@ snapshots: '@types/tinycolor2': 1.4.6 tinycolor2: 1.6.0 - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - - tinyspy@4.0.3: {} + tinyrainbow@3.0.3: {} title-case@2.1.1: dependencies: @@ -6778,58 +7266,60 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.1.0(typescript@5.8.3): + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: - typescript: 5.8.3 + typescript: 5.9.3 - ts-node@10.9.2(@types/node@20.19.9)(typescript@5.5.4): + ts-node@10.9.2(@types/node@25.0.2)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 + '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.19.9 + '@types/node': 25.0.2 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.5.4 + typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + ts-toolbelt@9.6.0: {} + tslib@1.14.1: {} tslib@2.8.1: {} - turbo-darwin-64@2.5.5: + turbo-darwin-64@2.6.3: optional: true - turbo-darwin-arm64@2.5.5: + turbo-darwin-arm64@2.6.3: optional: true - turbo-linux-64@2.5.5: + turbo-linux-64@2.6.3: optional: true - turbo-linux-arm64@2.5.5: + turbo-linux-arm64@2.6.3: optional: true - turbo-windows-64@2.5.5: + turbo-windows-64@2.6.3: optional: true - turbo-windows-arm64@2.5.5: + turbo-windows-arm64@2.6.3: optional: true - turbo@2.5.5: + turbo@2.6.3: optionalDependencies: - turbo-darwin-64: 2.5.5 - turbo-darwin-arm64: 2.5.5 - turbo-linux-64: 2.5.5 - turbo-linux-arm64: 2.5.5 - turbo-windows-64: 2.5.5 - turbo-windows-arm64: 2.5.5 + turbo-darwin-64: 2.6.3 + turbo-darwin-arm64: 2.6.3 + turbo-linux-64: 2.6.3 + turbo-linux-arm64: 2.6.3 + turbo-windows-64: 2.6.3 + turbo-windows-arm64: 2.6.3 type-check@0.4.0: dependencies: @@ -6870,20 +7360,18 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.38.0(eslint@9.31.0)(typescript@5.8.3): + typescript-eslint@8.50.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0)(typescript@5.8.3))(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.38.0(eslint@9.31.0)(typescript@5.8.3) - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.38.0(eslint@9.31.0)(typescript@5.8.3) - eslint: 9.31.0 - typescript: 5.8.3 + '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - typescript@5.5.4: {} - - typescript@5.8.3: {} + typescript@5.9.3: {} uglify-js@3.19.3: optional: true @@ -6899,10 +7387,20 @@ snapshots: undici-types@6.21.0: {} + undici-types@7.16.0: {} + + unicorn-magic@0.3.0: {} + universalify@0.1.2: {} universalify@2.0.1: {} + update-browserslist-db@1.2.2(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + update-check@1.5.4: dependencies: registry-auth-token: 3.3.2 @@ -6920,90 +7418,68 @@ snapshots: util-deprecate@1.0.2: {} - uuid@11.1.0: {} + uuid@13.0.0: {} v8-compile-cache-lib@3.0.1: {} validate-npm-package-name@5.0.1: {} - viem@2.33.0(typescript@5.8.3): + validate-npm-package-name@6.0.2: {} + + viem@2.42.1(typescript@5.9.3)(zod@4.2.0): dependencies: - '@noble/curves': 1.9.2 + '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.8.3) - isows: 1.0.7(ws@8.18.2) - ox: 0.8.1(typescript@5.8.3) - ws: 8.18.2 + abitype: 1.1.0(typescript@5.9.3)(zod@4.2.0) + isows: 1.0.7(ws@8.18.3) + ox: 0.9.17(typescript@5.9.3)(zod@4.2.0) + ws: 8.18.3 optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - vite-node@3.2.4(@types/node@22.16.5): + vite@7.3.0(@types/node@25.0.2): dependencies: - cac: 6.7.14 - debug: 4.4.1(supports-color@5.5.0) - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.0.5(@types/node@22.16.5) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite@7.0.5(@types/node@22.16.5): - dependencies: - esbuild: 0.25.8 - fdir: 6.4.6(picomatch@4.0.3) + esbuild: 0.27.1 + fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.45.1 - tinyglobby: 0.2.14 + rollup: 4.53.4 + tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 22.16.5 + '@types/node': 25.0.2 fsevents: 2.3.3 - vitest@3.2.4(@types/node@22.16.5)(happy-dom@20.0.2): - dependencies: - '@types/chai': 5.2.2 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.0.5(@types/node@22.16.5)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.2.1 - debug: 4.4.1(supports-color@5.5.0) - expect-type: 1.2.2 - magic-string: 0.30.17 + vitest@4.0.15(@types/node@25.0.2)(happy-dom@20.0.11): + dependencies: + '@vitest/expect': 4.0.15 + '@vitest/mocker': 4.0.15(vite@7.3.0(@types/node@25.0.2)) + '@vitest/pretty-format': 4.0.15 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.9.0 + std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.14 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.0.5(@types/node@22.16.5) - vite-node: 3.2.4(@types/node@22.16.5) + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.0(@types/node@25.0.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.16.5 - happy-dom: 20.0.2 + '@types/node': 25.0.2 + happy-dom: 20.0.11 transitivePeerDependencies: - jiti - less @@ -7013,7 +7489,6 @@ snapshots: - sass-embedded - stylus - sugarss - - supports-color - terser - tsx - yaml @@ -7040,7 +7515,7 @@ snapshots: is-async-function: 2.1.1 is-date-object: 1.1.0 is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 + is-generator-function: 1.1.2 is-regex: 1.2.1 is-weakref: 1.1.1 isarray: 2.0.5 @@ -7090,20 +7565,24 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@8.1.0: + wrap-ansi@9.0.2: dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 wrappy@1.0.2: {} - ws@8.18.2: {} + ws@8.18.3: {} y18n@5.0.8: {} + yallist@3.1.1: {} + yargs-parser@21.1.1: {} + yargs-parser@22.0.0: {} + yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -7114,6 +7593,21 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yargs@18.0.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 7.2.0 + y18n: 5.0.8 + yargs-parser: 22.0.0 + yn@3.1.1: {} yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.2.0): + dependencies: + zod: 4.2.0 + + zod@4.2.0: {} diff --git a/repo/eslint-config/CHANGELOG.md b/repo/eslint-config/CHANGELOG.md new file mode 100644 index 0000000000..ddc6085a88 --- /dev/null +++ b/repo/eslint-config/CHANGELOG.md @@ -0,0 +1,13 @@ +# @repo/eslint-config + +## 0.0.1-beta.1 + +### Patch Changes + +- Beta release for v3 + +## 0.0.1-beta.0 + +### Patch Changes + +- 3.0.0-beta.3 with fixes diff --git a/repo/eslint-config/package.json b/repo/eslint-config/package.json index 8e609c8f56..5625bc6999 100644 --- a/repo/eslint-config/package.json +++ b/repo/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@repo/eslint-config", - "version": "0.0.0", + "version": "0.0.1-beta.1", "type": "module", "private": true, "exports": { @@ -9,16 +9,16 @@ "./react-internal": "./react-internal.js" }, "devDependencies": { - "@eslint/js": "^9.28.0", - "@next/eslint-plugin-next": "^15.3.3", - "eslint": "^9.28.0", - "eslint-config-prettier": "^9.1.0", + "@eslint/js": "^9.39.2", + "@next/eslint-plugin-next": "^15.5.9", + "eslint": "^9.39.2", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-only-warn": "^1.1.0", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-turbo": "^2.5.4", - "globals": "^15.15.0", - "typescript": "^5.8.3", - "typescript-eslint": "^8.33.1" + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-turbo": "^2.6.3", + "globals": "^16.5.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.49.0" } } diff --git a/repo/typescript-config/CHANGELOG.md b/repo/typescript-config/CHANGELOG.md new file mode 100644 index 0000000000..611dc70b2d --- /dev/null +++ b/repo/typescript-config/CHANGELOG.md @@ -0,0 +1,13 @@ +# @repo/typescript-config + +## 0.0.1-beta.1 + +### Patch Changes + +- Beta release for v3 + +## 0.0.1-beta.0 + +### Patch Changes + +- 3.0.0-beta.3 with fixes diff --git a/repo/typescript-config/package.json b/repo/typescript-config/package.json index 27c0e60436..cb34e9260e 100644 --- a/repo/typescript-config/package.json +++ b/repo/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@repo/typescript-config", - "version": "0.0.0", + "version": "0.0.1-beta.1", "private": true, "license": "MIT", "publishConfig": { diff --git a/repo/ui/CHANGELOG.md b/repo/ui/CHANGELOG.md new file mode 100644 index 0000000000..8994e02948 --- /dev/null +++ b/repo/ui/CHANGELOG.md @@ -0,0 +1,13 @@ +# @repo/ui + +## 0.0.1-beta.1 + +### Patch Changes + +- Beta release for v3 + +## 0.0.1-beta.0 + +### Patch Changes + +- 3.0.0-beta.3 with fixes diff --git a/repo/ui/package.json b/repo/ui/package.json index 51af743edf..6b380862e7 100644 --- a/repo/ui/package.json +++ b/repo/ui/package.json @@ -1,6 +1,6 @@ { "name": "@repo/ui", - "version": "0.0.0", + "version": "0.0.1-beta.1", "private": true, "exports": { "./button": "./src/button.tsx", @@ -13,16 +13,16 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "@repo/eslint-config": "workspace:*", - "@repo/typescript-config": "workspace:*", + "@repo/eslint-config": "workspace:^", + "@repo/typescript-config": "workspace:^", "@turbo/gen": "^1.13.4", - "@types/node": "^20.17.57", - "@types/react": "18.3.0", - "@types/react-dom": "18.3.1", - "typescript": "5.5.4" + "@types/node": "^25.0.2", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "typescript": "^5.9.3" }, "dependencies": { - "react": "^19.1.0", - "react-dom": "^19.1.0" + "react": "^19.2.3", + "react-dom": "^19.2.3" } } diff --git a/turbo.json b/turbo.json index 563d48c609..fcf0462701 100644 --- a/turbo.json +++ b/turbo.json @@ -25,7 +25,8 @@ "repo/**/*.ts", "repo/**/*.tsx", "test/**/*.ts", - "test/**/*.tsx" + "test/**/*.tsx", + "test/**/*.mjs" ], "outputs": [] }, diff --git a/v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSsequence.jszSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.BLOB b/v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSsequence.jszSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.BLOB new file mode 100644 index 0000000000000000000000000000000000000000..0b31b75844b0bd45b4038cb39465714c21e05f3b GIT binary patch literal 1584680 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

cLJKuqA+$WdqhoEnhCG&mnhq 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.w|9${|*hGAa-avd=5+o^5 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 rJ13%eU2ZKBgXCW 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

edW)of6fG)$hJnypa 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 i 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 z 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 r%b}7=MxI178DhA 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 literal 0 HcmV?d00001 diff --git a/v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSsequence.jszSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.MAP b/v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSsequence.jszSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.MAP new file mode 100644 index 0000000000..7404543529 --- /dev/null +++ b/v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSsequence.jszSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.MAP @@ -0,0 +1 @@ +{"/project/sequence.js/node_modules/.pnpm/@preconstruct+cli@2.8.7/node_modules/@preconstruct/cli/cli/dist/preconstruct-cli-cli.cjs.js":["8315344ea3ec2213ef0e4aaa37b41d90c77999cb",0,32424],"/project/sequence.js/node_modules/.pnpm/meow@7.1.1/node_modules/meow/index.js":["4a866f54ed797369e8e33d78cc06f5834ee7b7be",32424,34544],"/project/sequence.js/node_modules/.pnpm/minimist-options@4.1.0/node_modules/minimist-options/index.js":["0f0bff7fe31e97f7ea78636b1aca17e841e5f8e3",34544,36360],"/project/sequence.js/node_modules/.pnpm/is-plain-obj@1.1.0/node_modules/is-plain-obj/index.js":["16523ba84e3229bcd98780b380a4f4e323b8bf8b",36360,37088],"/project/sequence.js/node_modules/.pnpm/arrify@1.0.1/node_modules/arrify/index.js":["d91a789bdf6d6e7ea9014b0b1bacfef0d7621f47",37088,37760],"/project/sequence.js/node_modules/.pnpm/kind-of@6.0.3/node_modules/kind-of/index.js":["7de61d4d1e16e0c9b04660b8904d9488727a5d02",37760,39152],"/project/sequence.js/node_modules/.pnpm/yargs-parser@18.1.3/node_modules/yargs-parser/index.js":["ace83036108f3a4eb87218d76219768fee3520ed",39152,41760],"/project/sequence.js/node_modules/.pnpm/camelcase@5.3.1/node_modules/camelcase/index.js":["1d930d28c959c4323750d39d13464d7321c98058",41760,42536],"/project/sequence.js/node_modules/.pnpm/decamelize@1.2.0/node_modules/decamelize/index.js":["b2b327355fce3e54d2a2c542beb41ae803c33285",42536,43216],"/project/sequence.js/node_modules/.pnpm/yargs-parser@18.1.3/node_modules/yargs-parser/lib/tokenize-arg-string.js":["d6ba12849ef19788b046e6c733d39dc89ecca903",43216,43912],"/project/sequence.js/node_modules/.pnpm/camelcase-keys@6.2.2/node_modules/camelcase-keys/index.js":["5a26b7fde6dad796f3bd7397e7791eac3ab87aaf",43912,45152],"/project/sequence.js/node_modules/.pnpm/map-obj@4.3.0/node_modules/map-obj/index.js":["ac674b6338e4f5478f897777dfa0426f7ef08372",45152,46200],"/project/sequence.js/node_modules/.pnpm/quick-lru@4.0.1/node_modules/quick-lru/index.js":["e3a201d5356b92357346467daabe90da2e16a7db",46200,48056],"/project/sequence.js/node_modules/.pnpm/decamelize-keys@1.1.1/node_modules/decamelize-keys/index.js":["f14f033c88b7ad237428f9adfb5b0529f7ae1e64",48056,48904],"/project/sequence.js/node_modules/.pnpm/map-obj@1.0.1/node_modules/map-obj/index.js":["fa95683437abe12eed3217e281bddf5f4938d72b",48904,49576],"/project/sequence.js/node_modules/.pnpm/trim-newlines@3.0.1/node_modules/trim-newlines/index.js":["33ad59bfabca72daaaac5ce86b9a1b3a86ff862b",49576,50520],"/project/sequence.js/node_modules/.pnpm/redent@3.0.0/node_modules/redent/index.js":["d4bc13cc14753873aaaa2b18b42dac80e01a01b3",50520,51368],"/project/sequence.js/node_modules/.pnpm/strip-indent@3.0.0/node_modules/strip-indent/index.js":["354e99b30d73ccc0488c92c9b47a76d602a1a430",51368,52144],"/project/sequence.js/node_modules/.pnpm/min-indent@1.0.1/node_modules/min-indent/index.js":["37d5dc3ecc8998cb6d75b3783a762af9c1efff36",52144,52824],"/project/sequence.js/node_modules/.pnpm/indent-string@4.0.0/node_modules/indent-string/index.js":["9c4d9c2acd8fcff21a0b3be049caeb37e0db31bd",52824,53504],"/project/sequence.js/node_modules/.pnpm/read-pkg-up@7.0.1/node_modules/read-pkg-up/index.js":["df41751fdcc969bd93baf4c7d33321339af674d0",53504,54504],"/project/sequence.js/node_modules/.pnpm/find-up@4.1.0/node_modules/find-up/index.js":["94bdb702d37e75813308dfb1dd22d476b4505e46",54504,55672],"/project/sequence.js/node_modules/.pnpm/locate-path@5.0.0/node_modules/locate-path/index.js":["62506e005bad63217618acd7919ebb78fa2e210a",55672,57232],"/project/sequence.js/node_modules/.pnpm/p-locate@4.1.0/node_modules/p-locate/index.js":["dd5418f2eae9be067ba9b5041b19468c555b62fe",57232,58416],"/project/sequence.js/node_modules/.pnpm/p-limit@2.3.0/node_modules/p-limit/index.js":["a51ebba93599019f457cb181d15f65a823dd62ac",58416,59160],"/project/sequence.js/node_modules/.pnpm/p-try@2.2.0/node_modules/p-try/index.js":["885a5f2ed7229ecacb44d35396cd886fcec4e22a",59160,59816],"/project/sequence.js/node_modules/.pnpm/path-exists@4.0.0/node_modules/path-exists/index.js":["e67614299feaa6105ac0b8acff41fbad72d12215",59816,60832],"/project/sequence.js/node_modules/.pnpm/read-pkg@5.2.0/node_modules/read-pkg/index.js":["9036a71c8ec79c27e349e125db9cffc0c3ec08a3",60832,62032],"/project/sequence.js/node_modules/.pnpm/parse-json@5.2.0/node_modules/parse-json/index.js":["e858089e91151c1352634775bc2fdc50b3727f57",62032,63272],"/project/sequence.js/node_modules/.pnpm/error-ex@1.3.2/node_modules/error-ex/index.js":["2c9f804ca561680284eca33ab39c3de463428d9d",63272,64376],"/project/sequence.js/node_modules/.pnpm/is-arrayish@0.2.1/node_modules/is-arrayish/index.js":["6f9d636aff36876efe7d1cefbbdee98bfcc28c98",64376,65024],"/project/sequence.js/node_modules/.pnpm/json-parse-even-better-errors@2.3.1/node_modules/json-parse-even-better-errors/index.js":["d871279956a6953ab4aba74301e6e0dff6e5d0cf",65024,67120],"/project/sequence.js/node_modules/.pnpm/lines-and-columns@1.2.4/node_modules/lines-and-columns/build/index.js":["dfadef3f4dd9e0a529bd280d63072311d4003ae7",67120,68464],"/project/sequence.js/node_modules/.pnpm/@babel+code-frame@7.24.7/node_modules/@babel/code-frame/lib/index.js":["8f8903e03150fc590880556976c3e13bc778b741",68464,70472],"/project/sequence.js/node_modules/.pnpm/@babel+highlight@7.24.7/node_modules/@babel/highlight/lib/index.js":["5f222c37d8d679535861ff6eb8459fbebc371987",70472,73352],"/project/sequence.js/node_modules/.pnpm/js-tokens@4.0.0/node_modules/js-tokens/index.js":["0e440af0eefc12535ba7b998ca49db43f60bcda4",73352,74768],"/project/sequence.js/node_modules/.pnpm/@babel+helper-validator-identifier@7.24.7/node_modules/@babel/helper-validator-identifier/lib/index.js":["5813ad3628d945e124c7d95d0c68ba3a7243d270",74768,77312],"/project/sequence.js/node_modules/.pnpm/@babel+helper-validator-identifier@7.24.7/node_modules/@babel/helper-validator-identifier/lib/identifier.js":["cab8bd9b8c93b5cd09b51bdc0044debaaeff08b3",77312,88664],"/project/sequence.js/node_modules/.pnpm/@babel+helper-validator-identifier@7.24.7/node_modules/@babel/helper-validator-identifier/lib/keyword.js":["aaef241789a33ed11de7ca3608a030b92049763a",88664,91024],"/project/sequence.js/node_modules/.pnpm/picocolors@1.0.1/node_modules/picocolors/picocolors.js":["6ac3387abbd5230220acd0af2f617e74c57b243a",91024,92464],"/project/sequence.js/node_modules/.pnpm/hard-rejection@2.1.0/node_modules/hard-rejection/index.js":["fd2e90cbb49c0a3c0e0f70813f19994b587c2abe",92464,93256],"/project/sequence.js/node_modules/.pnpm/normalize-package-data@2.5.0/node_modules/normalize-package-data/lib/normalize.js":["e35e7e40659151ebe4d397135dfc7ae3e9aa8170",93256,94808],"/project/sequence.js/node_modules/.pnpm/normalize-package-data@2.5.0/node_modules/normalize-package-data/lib/fixer.js":["f7eebdfb540e7a39e1c006112140675045e2dfb0",94808,100184],"/project/sequence.js/node_modules/.pnpm/semver@5.7.2/node_modules/semver/semver.js":["4b85b5f963842409ab87e7714887d2d7cdd4c727",100184,114288],"/project/sequence.js/node_modules/.pnpm/validate-npm-package-license@3.0.4/node_modules/validate-npm-package-license/index.js":["f3b4c4411d93c2314997bcb06791fb22a39b0ce2",114288,115664],"/project/sequence.js/node_modules/.pnpm/spdx-expression-parse@3.0.1/node_modules/spdx-expression-parse/index.js":["7d9b17e093b4706955e0b8991ed0d48f5739a40f",115664,116488],"/project/sequence.js/node_modules/.pnpm/spdx-expression-parse@3.0.1/node_modules/spdx-expression-parse/scan.js":["6ce4f78704adba89f90f4420365a306cb1bbeee8",116488,117552],"/project/sequence.js/node_modules/.pnpm/spdx-expression-parse@3.0.1/node_modules/spdx-expression-parse/parse.js":["f86a533bd13ff24966957e11bdb506ee7a91dbaf",117552,118368],"/project/sequence.js/node_modules/.pnpm/spdx-correct@3.2.0/node_modules/spdx-correct/index.js":["bd8e631a674fd29f75548547da756954bb8253a7",118368,125976],"/project/sequence.js/node_modules/.pnpm/hosted-git-info@2.8.9/node_modules/hosted-git-info/index.js":["7d405138838d32a0444c13eb2a74beefb2a658b4",125976,127816],"/project/sequence.js/node_modules/.pnpm/hosted-git-info@2.8.9/node_modules/hosted-git-info/git-host-info.js":["f03c8e42c32672adb05b93be109daeb2585a6658",127816,132232],"/project/sequence.js/node_modules/.pnpm/hosted-git-info@2.8.9/node_modules/hosted-git-info/git-host.js":["1e81232714cc00492d5bb5a49e621d6b1d754dd4",132232,134872],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/index.js":["db23b5f633134f5f81380633d9b4666384e98591",134872,135680],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/lib/async.js":["5041f4330e1a476744145fb2469beeae1ac79d99",135680,138368],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/lib/homedir.js":["7c804908ed10d1aa264b880ea6e5c34b883f52ac",138368,139056],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/lib/caller.js":["a57f670bf054692c44ba92d17dd78274eda5d73b",139056,139760],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/lib/node-modules-paths.js":["9b3f1ddafeccbfacdbf36d84b1cb8d36ac631ed2",139760,140744],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/lib/normalize-options.js":["7e53322b0515bdbb435ab7e9623a7f5014ab2883",140744,141432],"/project/sequence.js/node_modules/.pnpm/is-core-module@2.15.0/node_modules/is-core-module/index.js":["8be68d231ccea32f9d3f5a7a2120864147079829",141432,142496],"/project/sequence.js/node_modules/.pnpm/hasown@2.0.2/node_modules/hasown/index.js":["668857f7a7f36a0f7b80c8b6992b543819ce3a82",142496,143232],"/project/sequence.js/node_modules/.pnpm/function-bind@1.1.2/node_modules/function-bind/index.js":["f761bc101bf315e6124f737d1691aaa77e507253",143232,143888],"/project/sequence.js/node_modules/.pnpm/function-bind@1.1.2/node_modules/function-bind/implementation.js":["42321136a108882e353520fff3411fbfcb798b5b",143888,145088],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/lib/core.js":["4f76a34a44fc8a71f77bc442edea6048f7f03cde",145088,145904],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/lib/is-core.js":["4d6318c847f3bc91dd1b0f647bfb98d5958d81aa",145904,146640],"/project/sequence.js/node_modules/.pnpm/resolve@1.22.8/node_modules/resolve/lib/sync.js":["f9aafa50a2ea04a14e085f1698ad17295cef2619",146640,148768],"/project/sequence.js/node_modules/.pnpm/normalize-package-data@2.5.0/node_modules/normalize-package-data/lib/extract_description.js":["1461754e50fbc41a07c9901c0134dd0fcd5d23c5",148768,149472],"/project/sequence.js/node_modules/.pnpm/normalize-package-data@2.5.0/node_modules/normalize-package-data/lib/make_warning.js":["9ab5d64a118d79b9aff4fe138d1357a1642c7d1c",149472,150400],"/project/sequence.js/node_modules/.pnpm/enquirer@2.4.1/node_modules/enquirer/index.js":["d12024026266df904fee11fb9f81da0d9e538ead",150400,153632],"/project/sequence.js/node_modules/.pnpm/enquirer@2.4.1/node_modules/enquirer/lib/utils.js":["c7ba15889c419cc86f9e8bd5713aeb7c10a6a779",153632,157960],"/project/sequence.js/node_modules/.pnpm/ansi-colors@4.1.3/node_modules/ansi-colors/index.js":["64b9e2dd7033a9c659dcbf02316ae560157e9f3c",157960,159024],"/project/sequence.js/node_modules/.pnpm/ansi-colors@4.1.3/node_modules/ansi-colors/symbols.js":["47c2659ee9c473f4453bc9ed9a715c06f790520d",159024,162120],"/project/sequence.js/node_modules/.pnpm/enquirer@2.4.1/node_modules/enquirer/lib/prompts/index.js":["07b26cee45ce2905f6031ecdbbe1ca34819e6284",162120,164408],"/project/sequence.js/node_modules/.pnpm/p-limit@3.1.0/node_modules/p-limit/index.js":["f4e1896d228ab6e93828fc6b0af02b151e100464",164408,165136],"/project/sequence.js/node_modules/.pnpm/yocto-queue@0.1.0/node_modules/yocto-queue/index.js":["ec0be6ffda71356067a94dd911e42b1ae0fd1db9",165136,166928],"/project/sequence.js/node_modules/.pnpm/dataloader@2.2.2/node_modules/dataloader/index.js":["89b95844305a89d1c23e9067a2709b2a9ef0991f",166928,169104],"/project/sequence.js/node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/index.js":["8c580aa7500d9969056a236df949fefc3db41905",169104,173640],"/project/sequence.js/node_modules/.pnpm/ansi-styles@4.3.0/node_modules/ansi-styles/index.js":["5f96df8b074e4854c03db87ef309eb6c741f4618",173640,175096],"/project/sequence.js/node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/index.js":["3773691818b7b49ed40595e91be318a3a98d7795",175096,177008],"/project/sequence.js/node_modules/.pnpm/has-flag@4.0.0/node_modules/has-flag/index.js":["74ec980a20fb60d8774b46096a70b7ab9246d743",177008,177680],"/project/sequence.js/node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/util.js":["4cb1e8afae9bc10f49a678034e835399ab9c0bcd",177680,178472],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/index.js":["e459e2a093427ef6b495bf02eccd044848688322",178472,181296],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/managers/tasks.js":["bcc3b169966c3e7ece6bf4b54745016b214fa448",181296,182920],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/utils/index.js":["752a08e3c614cbc9ecbb1b79c75780efea89f946",182920,184072],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/utils/array.js":["c2a902949583d0f9f812600271cde83086d8c7e7",184072,184912],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/utils/errno.js":["265f68611c7a5e7ac568213260b2faf0f69464bc",184912,185680],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/utils/fs.js":["fde03242c8fbced0e1fffde9176db3248e76c9c7",185680,186752],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/utils/path.js":["95a433bd9329f461bb6323ef1b3712bf54f131df",186752,188928],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/utils/pattern.js":["52d53793ea40d1ba27e85c1edc26815836dd436c",188928,192888],"/project/sequence.js/node_modules/.pnpm/glob-parent@5.1.2/node_modules/glob-parent/index.js":["adc400be934bd3d9b969c3accac4bb34a566d723",192888,194232],"/project/sequence.js/node_modules/.pnpm/is-glob@4.0.3/node_modules/is-glob/index.js":["9d88806c840a6ff6661e8b62a2e4ecef20ac1bc1",194232,195216],"/project/sequence.js/node_modules/.pnpm/is-extglob@2.1.1/node_modules/is-extglob/index.js":["c9ca9e3ebf4c37b326c24dc95fb9e6983b3dc1fd",195216,195864],"/project/sequence.js/node_modules/.pnpm/micromatch@4.0.7/node_modules/micromatch/index.js":["ee8a80b6774f0ca72a8109fff9eab9fedb457918",195864,198480],"/project/sequence.js/node_modules/.pnpm/braces@3.0.3/node_modules/braces/index.js":["16c230770b5d4e51d6e73d9e72d593e76cc09521",198480,199920],"/project/sequence.js/node_modules/.pnpm/braces@3.0.3/node_modules/braces/lib/stringify.js":["4b3d950417ae2f39a7487e583e85f529236ef422",199920,200680],"/project/sequence.js/node_modules/.pnpm/braces@3.0.3/node_modules/braces/lib/utils.js":["335ac09154d707fd39d341ddaffa377c47124261",200680,202296],"/project/sequence.js/node_modules/.pnpm/braces@3.0.3/node_modules/braces/lib/compile.js":["c0c55adea947384d778a24f4f08283d506aac1eb",202296,203088],"/project/sequence.js/node_modules/.pnpm/fill-range@7.1.1/node_modules/fill-range/index.js":["f90521fa85611e26457ceaf685f459a993381d1e",203088,205296],"/project/sequence.js/node_modules/.pnpm/to-regex-range@5.0.1/node_modules/to-regex-range/index.js":["b522adef7fd77cfdda9929f8773fc7d635439dcb",205296,207336],"/project/sequence.js/node_modules/.pnpm/is-number@7.0.0/node_modules/is-number/index.js":["1ec86d46940af464d8385e8d06a6ea34956cc757",207336,208008],"/project/sequence.js/node_modules/.pnpm/braces@3.0.3/node_modules/braces/lib/expand.js":["1ba7f6969f32a48741ae584d3a6e7b1fe98a0b63",208008,208952],"/project/sequence.js/node_modules/.pnpm/braces@3.0.3/node_modules/braces/lib/parse.js":["550e4c01b40e2a9b6bcbfbfb4ecc2fc660a1147f",208952,210576],"/project/sequence.js/node_modules/.pnpm/braces@3.0.3/node_modules/braces/lib/constants.js":["421df8cc8911fd1bc575cce481980b22e8d6b4f2",210576,212768],"/project/sequence.js/node_modules/.pnpm/picomatch@2.3.1/node_modules/picomatch/index.js":["e318b5985ff7d918da503a0e9aefb106a72c963b",212768,213376],"/project/sequence.js/node_modules/.pnpm/picomatch@2.3.1/node_modules/picomatch/lib/picomatch.js":["f45463f2c399358b722530b870722abb104b5091",213376,215352],"/project/sequence.js/node_modules/.pnpm/picomatch@2.3.1/node_modules/picomatch/lib/scan.js":["b706e51f6ce0d3cd2be9f6351547c1dfcc6f3520",215352,217136],"/project/sequence.js/node_modules/.pnpm/picomatch@2.3.1/node_modules/picomatch/lib/utils.js":["a38e121509c23f130931e3df7e3fdba25ae5dc39",217136,219488],"/project/sequence.js/node_modules/.pnpm/picomatch@2.3.1/node_modules/picomatch/lib/constants.js":["fe77299a32f26e8f1c63ac2a07614f4ed7261590",219488,225336],"/project/sequence.js/node_modules/.pnpm/picomatch@2.3.1/node_modules/picomatch/lib/parse.js":["b470522cc3965d7bab5ed135e1d1b4567da5489f",225336,226776],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/utils/stream.js":["83ed77291806827d1bdb16227833311083a5da09",226776,227672],"/project/sequence.js/node_modules/.pnpm/merge2@1.4.1/node_modules/merge2/index.js":["ad80c35745c87c842494078cfa8da55cae9d1201",227672,228680],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/utils/string.js":["21f718c2bbef24ca3602de2cb9b1f5c5080dbcfd",228680,229512],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/async.js":["540f1475cf17095a5590b87f623b4e7efc24cb00",229512,230792],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/readers/async.js":["a63fed54c57c8dea0aa270a756daa7f24a625082",230792,232128],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/index.js":["9a7b966c96d03e673ca4885659549a3686af2a3d",232128,233536],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/providers/async.js":["ca32b381c4bc54aea5a25767bc02ac66b49f977b",233536,234872],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/readers/async.js":["febbb01e24817ccb351997edf2888e7cf9e5656d",234872,237184],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/index.js":["71d2b5d9e9abd1d47d1e54b5c30815d3dd16b760",237184,238400],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/providers/async.js":["f9ba13340d69804ead7f16041ca7886e58f11b52",238400,240032],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/index.js":["048bb47c8f91cfd03154635590113a5e77aa5d56",240032,241240],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/providers/async.js":["69cbbe90cba363ba429223faa83438e1ec56ff71",241240,242200],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/providers/sync.js":["9ab973411ddd81c618800061019951e6b7d8dc76",242200,242968],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/settings.js":["b48a6628dc2be47a2701a2fbfced76e60f9fe413",242968,244120],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/adapters/fs.js":["fb2ccdc3491b88733ed6e8f3c1571efac4bf2922",244120,245208],"/project/sequence.js/node_modules/.pnpm/run-parallel@1.2.0/node_modules/run-parallel/index.js":["9e68d778658beb112f789fc35d3ea91122cb1c3d",245208,246088],"/project/sequence.js/node_modules/.pnpm/queue-microtask@1.2.3/node_modules/queue-microtask/index.js":["8f6d501ff6a7e34f852b3e9f04cc7cb16821baff",246088,246944],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/constants.js":["d62034ef78787932d063a3e5e7f807bfb68ed480",246944,248136],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/utils/index.js":["cf075300b99854c1007854f6a2832a47315faac7",248136,248880],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/utils/fs.js":["fde03242c8fbced0e1fffde9176db3248e76c9c7",248880,249976],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/providers/common.js":["f2fb9050a1cd10db8f4ceefae11c4e5495974d90",249976,250760],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/providers/sync.js":["fb31af2eb7ad22baffed26e90052b5ba3920aa20",250760,252040],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/settings.js":["e3045ee354afbe74ad821238dc41619daef57dc7",252040,253328],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/adapters/fs.js":["6cabb8bce0417d8cc98e1f5f770298927896331b",253328,254504],"/project/sequence.js/node_modules/.pnpm/fastq@1.17.1/node_modules/fastq/queue.js":["0e3aa14b503d0c504d2bb1384d2cbfc7a5dfaf40",254504,255880],"/project/sequence.js/node_modules/.pnpm/reusify@1.0.4/node_modules/reusify/reusify.js":["253f9c72d80869e32a2f49bc1b458dbaf453bb84",255880,256560],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/readers/common.js":["85a38c3201a80b4dd04a63cc44de7f7e4fdd62b5",256560,257632],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/readers/reader.js":["5430f71fe5a493cceeba149498a96837af343c5b",257632,258688],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/providers/stream.js":["26f2b34c349013452c47b069062d0621a93317d7",258688,259920],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/providers/sync.js":["302b2038047a50fba013ae099d123c374b91100f",259920,261080],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/readers/sync.js":["7062a53bb5549ff8246065cff5b58e3097fd2c2e",261080,262896],"/project/sequence.js/node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/settings.js":["d71706774d784235174fcc85a1dd889df9f29f43",262896,264112],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/readers/reader.js":["fb048d626e8d852a631a12e4fb2ab03a74b9fa8f",264112,265568],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/readers/stream.js":["ffce4582bc9f79455950a7b9db8094ed69bd3d97",265568,267160],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/provider.js":["e15e416490d6d4ccf4db4eb0bf841a19c9015166",267160,268792],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/filters/deep.js":["c2545d906765459ab10d8617935ad32ef14b1564",268792,270816],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/matchers/partial.js":["277c888be29835cf26d3f3f3959bf2b9d754fa2c",270816,271936],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/matchers/matcher.js":["85b823e0a7460cacaee1b40fec036df06ca2c325",271936,273288],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/filters/entry.js":["7186a847bc5bcacfae9bc54e1032a1be4c73eb7f",273288,275128],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/filters/error.js":["aafc75d8e19c67e324aaa3038648476d91d78fca",275128,276376],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/transformers/entry.js":["f22d8cfaf9fa87dc75f3ac0d6f6a9c228436af5d",276376,277632],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/stream.js":["92fdbe24b22a3c5e0df8ee784c8b57daef062501",277632,278984],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/providers/sync.js":["0b0718406005523d486497e1d3fa41a048d4b7d3",278984,280264],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/readers/sync.js":["0267cf7fe5e9a3c15f87400f5ef46a59baa42cb8",280264,281776],"/project/sequence.js/node_modules/.pnpm/fast-glob@3.3.2/node_modules/fast-glob/out/settings.js":["ba6870a40cacb77f648125f1ec379224546891c1",281776,283504],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/index.js":["b7111abcfec63f3c14d78fa1ab083daadb2a70d0",283504,284776],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/fs/index.js":["5362fe9fd2c8d632b37b2602ded1a0926a273f6b",284776,287088],"/project/sequence.js/node_modules/.pnpm/universalify@2.0.1/node_modules/universalify/index.js":["b89986d9f8a85f0ed20f038ad79a69fcb68a8288",287088,287952],"/project/sequence.js/node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/graceful-fs.js":["55efc5a24c26495d0341c7884f0de5eb36520efa",287952,291936],"/project/sequence.js/node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js":["38f3028ea7d9ec6b57f56ef32128499522c87a7f",291936,294064],"/project/sequence.js/node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/legacy-streams.js":["f4a3583d4c3e8b0c407ab8406bdafb02b4055b7f",294064,294872],"/project/sequence.js/node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/clone.js":["c912f366fe0025ea74e0e76e58277147dc0a3167",294872,295640],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/copy-sync/index.js":["3cf1fca7a7d2c1aba4a508a38e31c344d7255108",295640,296320],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/copy-sync/copy-sync.js":["3577d8fb71b1143769781f886e5e91a28458591c",296320,298760],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/mkdirs/index.js":["b0d9fcf6032f19505d113a6ea1ba769600751f28",298760,299792],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/mkdirs/make-dir.js":["66271453d9f0bdaadba03b78c7c95a862c52e970",299792,301224],"/project/sequence.js/node_modules/.pnpm/at-least-node@1.0.0/node_modules/at-least-node/index.js":["9c002b0e9446eff8a384d0a4b1c3494bb49a1e1f",301224,301904],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/util/utimes.js":["dad744f8edf8218685028574c168f77f9f1d75a8",301904,302768],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/util/stat.js":["38d324d1535d3303f3eb12af0eccb268194d267e",302768,304640],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/copy/index.js":["f685df7a46989c967bf917a5632a587298e22e40",304640,305392],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/copy/copy.js":["7715091e1e3173e3f5375092f035992cba09b39e",305392,308216],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/path-exists/index.js":["6e6491c1ab3389433d1b39a33b3ac8760649a2c8",308216,309160],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/empty/index.js":["72cfa757c416b468768d363ece8e71b66527577e",309160,310432],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/remove/index.js":["43a7630664db987ce37fc634b7474b6b9428ab4e",310432,311248],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/remove/rimraf.js":["9aaf8a271693de5fad3f942d7ca303e10be07c40",311248,313000],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/ensure/index.js":["68e93d6534353e9665f5d954de79edb27297b68f",313000,314264],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/ensure/file.js":["80c2a847a193ab5a9732746b6f5953cb50593f33",314264,315384],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/ensure/link.js":["87a056a34d6f9e0afcc150c235c6b972afc8cf7f",315384,316592],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/ensure/symlink.js":["dfc14c66722e60dc6879221877aecf9481d96b91",316592,318144],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/ensure/symlink-paths.js":["50fbb32d40b697a96fde72b07259933a9a72411a",318144,319160],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/ensure/symlink-type.js":["60686b2062037afe9661f006a43e124441409353",319160,320032],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/json/index.js":["047cedb67b8047183dfded82b56969c688dc5008",320032,321248],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/json/jsonfile.js":["6631d5dab8ea65a104dd9113357b4f0a2ada6fcc",321248,322176],"/project/sequence.js/node_modules/.pnpm/jsonfile@6.1.0/node_modules/jsonfile/index.js":["91c9c57af8bd81ee9a62a5b25797ea32883c15b0",322176,323648],"/project/sequence.js/node_modules/.pnpm/jsonfile@6.1.0/node_modules/jsonfile/utils.js":["9fcb3c2e8d7b909360c945cd568cc32fe7cf4596",323648,324408],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/json/output-json.js":["7a5ab88d3e20934904d7bffb20995b852442c0bb",324408,325264],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/output/index.js":["1b0879db53a00bbfeddcfdc0c190901387bab7bd",325264,326432],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/json/output-json-sync.js":["2f46f4bf9814aea91b0f6763c5d0f07e0ea9de05",326432,327296],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/move-sync/index.js":["ac40f3d0062886869329d8c31810935ad7c34ff5",327296,327976],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/move-sync/move-sync.js":["a391900c2c6e74e81d81c4414d9ef2bea7dd8ad9",327976,329352],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/move/index.js":["482e376c2d37368c3c202905b93429f3d46c9914",329352,330104],"/project/sequence.js/node_modules/.pnpm/fs-extra@9.1.0/node_modules/fs-extra/lib/move/move.js":["5f4c2db7135a3cfc04d9711474173fa4fb606c6a",330104,331544],"/project/sequence.js/node_modules/.pnpm/@babel+runtime@7.25.0/node_modules/@babel/runtime/helpers/objectSpread2.js":["f47c3a639c64b919d1099838ce6bd488211fd0a0",331544,332480],"/project/sequence.js/node_modules/.pnpm/@babel+runtime@7.25.0/node_modules/@babel/runtime/helpers/defineProperty.js":["1033ba95ff4616a295e4f849b2b7a34b931ae33f",332480,333320],"/project/sequence.js/node_modules/.pnpm/@babel+runtime@7.25.0/node_modules/@babel/runtime/helpers/toPropertyKey.js":["a2413a0eef7b66a7d1da8caa67567dfc3ba042b0",333320,334240],"/project/sequence.js/node_modules/.pnpm/@babel+runtime@7.25.0/node_modules/@babel/runtime/helpers/typeof.js":["ea88e31e0d68da436c784278c7b42f3ba928f5d7",334240,335064],"/project/sequence.js/node_modules/.pnpm/@babel+runtime@7.25.0/node_modules/@babel/runtime/helpers/toPrimitive.js":["002a87e71e7536e0c7f85c3bf97498ba4b956cdc",335064,335888],"/project/sequence.js/node_modules/.pnpm/detect-indent@6.1.0/node_modules/detect-indent/index.js":["a1c18640e5fee89596e01848e6e06f45cd34a884",335888,337216],"/project/sequence.js/node_modules/.pnpm/normalize-path@3.0.0/node_modules/normalize-path/index.js":["7af46f52994266092fb6890723ef7e1b059d1d20",337216,337904],"/project/sequence.js/node_modules/.pnpm/parse-glob@3.0.4/node_modules/parse-glob/index.js":["80de4342abc02723990d3849ef8f5710a3bfc2a0",337904,339296],"/project/sequence.js/node_modules/.pnpm/is-glob@2.0.1/node_modules/is-glob/index.js":["b3dcfb08edc51fa11b2f6a028e432f7f81c67ca8",339296,340024],"/project/sequence.js/node_modules/.pnpm/is-extglob@1.0.0/node_modules/is-extglob/index.js":["f2caa8e7efa77712cf91d0f349830dc563f1c9fa",340024,340672],"/project/sequence.js/node_modules/.pnpm/glob-base@0.3.0/node_modules/glob-base/index.js":["2f15588ede319f3f78ecb5b8be65c80f8c1570cf",340672,341568],"/project/sequence.js/node_modules/.pnpm/glob-parent@6.0.2/node_modules/glob-parent/index.js":["b59c2ce1188362fdc692963d4b287b2f3d79f90f",341568,342888],"/project/sequence.js/node_modules/.pnpm/is-dotfile@1.0.3/node_modules/is-dotfile/index.js":["01bfbcba70e3ab9c171f1053cdf5a833221fc990",342888,343568],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/index.js":["e38dd5a234ca8c66d1a751b65a97806e7f54f247",343568,345640],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/external.js":["f242d6d87258054f8b05e805f089ed43342608e3",345640,347440],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/errors.js":["d4b049d999fe64a12780c2b7cfb11ce599044c8b",347440,348528],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/locales/en.js":["060c7208a1d1040484e5710467a1f6e89d0a6524",348528,349424],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/helpers/util.js":["ce2fae02571c61f6db07908470ae58060d8ea65a",349424,352576],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/ZodError.js":["0ea1242578863570620d771906985661d97db604",352576,355352],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/helpers/parseUtil.js":["764a5b48d07d7288bdaab33628d4eaa59af14d2f",355352,358080],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/helpers/typeAliases.js":["b66edae489b6e4147ce7e1ec65a107e297219771",358080,358728],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/types.js":["6820bcddccd12fecf7dbf4071c3bc96890b90efd",358728,403600],"/project/sequence.js/node_modules/.pnpm/zod@3.23.8/node_modules/zod/lib/helpers/errorUtil.js":["5dd3884a3b2637d4dac94d46dbd9342b99cb953d",403600,404696],"/project/sequence.js/node_modules/.pnpm/npm-packlist@2.2.2/node_modules/npm-packlist/index.js":["d123f52a824837376e369e3aec7507647a184883",404696,408624],"/project/sequence.js/node_modules/.pnpm/npm-bundled@1.1.2/node_modules/npm-bundled/index.js":["88712a69de7c4e83aa2d9430dc2214dfd10be5c0",408624,411936],"/project/sequence.js/node_modules/.pnpm/npm-normalize-package-bin@1.0.1/node_modules/npm-normalize-package-bin/index.js":["936296ddebc6348bed8f93e0063aa9b81e594a50",411936,413096],"/project/sequence.js/node_modules/.pnpm/ignore-walk@3.0.4/node_modules/ignore-walk/index.js":["44909c6d209ab59cd7a00c297c8f907baccb841f",413096,416352],"/project/sequence.js/node_modules/.pnpm/minimatch@3.1.2/node_modules/minimatch/minimatch.js":["f21a6b3c6d1d71bb65e4e6e0af1bf1baba3a207e",416352,420736],"/project/sequence.js/node_modules/.pnpm/brace-expansion@1.1.11/node_modules/brace-expansion/index.js":["a2f937621d39c20ce582f697c3e4273d1e14b2e0",420736,422784],"/project/sequence.js/node_modules/.pnpm/concat-map@0.0.1/node_modules/concat-map/index.js":["a3063f014cc693b320dbd64de3243a79247c1e05",422784,423568],"/project/sequence.js/node_modules/.pnpm/balanced-match@1.0.2/node_modules/balanced-match/index.js":["12161cfaa33be93568ec9a6fd3d9c357991a6a76",423568,424408],"/project/sequence.js/node_modules/.pnpm/glob@7.2.3/node_modules/glob/glob.js":["7b624669f35601648f8300b45c3b3861bd9c7ef6",424408,429480],"/project/sequence.js/node_modules/.pnpm/fs.realpath@1.0.0/node_modules/fs.realpath/index.js":["9b5cdf4ef79264959ed0a23e4c35efbe6d64b0df",429480,430944],"/project/sequence.js/node_modules/.pnpm/fs.realpath@1.0.0/node_modules/fs.realpath/old.js":["d2d656e98e4d0735902068408824f8d08aaea84c",430944,432792],"/project/sequence.js/node_modules/.pnpm/inherits@2.0.4/node_modules/inherits/inherits.js":["222da288a07d8f65b2aed9b88815948cfe0b42d9",432792,433576],"/project/sequence.js/node_modules/.pnpm/path-is-absolute@1.0.1/node_modules/path-is-absolute/index.js":["6de38a82f68960de2bd07fd9114541f02bee2f62",433576,434400],"/project/sequence.js/node_modules/.pnpm/glob@7.2.3/node_modules/glob/sync.js":["82b1c855e4bfca820ecbed219649cd174b0c2f62",434400,437480],"/project/sequence.js/node_modules/.pnpm/glob@7.2.3/node_modules/glob/common.js":["4890b7b6c34bc659a38802851951da90baad085d",437480,439232],"/project/sequence.js/node_modules/.pnpm/inflight@1.0.6/node_modules/inflight/inflight.js":["84aed0b47c15de35a85a5aa6c641342ba4dd5a88",439232,440264],"/project/sequence.js/node_modules/.pnpm/wrappy@1.0.2/node_modules/wrappy/wrappy.js":["7d5c1c908664b3df4a9b72400a126652ba0dd905",440264,440992],"/project/sequence.js/node_modules/.pnpm/once@1.4.0/node_modules/once/once.js":["f78c8cb8d754261b59d03e867f329c2ffdefae45",440992,441976],"/project/sequence.js/node_modules/.pnpm/fast-deep-equal@2.0.1/node_modules/fast-deep-equal/index.js":["7544a59317225a41d7c3b02605e87459a251ea54",441976,442832],"/project/sequence.js/node_modules/.pnpm/resolve-from@5.0.0/node_modules/resolve-from/index.js":["12204537847d8c5d27e3dbeb024c2def138bb3ae",442832,443880],"/project/sequence.js/node_modules/.pnpm/rollup@2.79.1/node_modules/rollup/dist/rollup.js":["528be32fbed11ca77d2ddc3fa5a4b83153c1a6f4",443880,445104],"/project/sequence.js/node_modules/.pnpm/rollup@2.79.1/node_modules/rollup/dist/shared/rollup.js":["1b070c5ac10253c28c8b66a3b89713db723a347e",445104,741848],"/project/sequence.js/node_modules/.pnpm/@rollup+plugin-node-resolve@11.2.1_rollup@2.79.1/node_modules/@rollup/plugin-node-resolve/dist/cjs/index.js":["4c7176cdf5a30cd0a7d1266fae5deac3a0b55858",741848,748488],"/project/sequence.js/node_modules/.pnpm/builtin-modules@3.3.0/node_modules/builtin-modules/index.js":["78b46fe2850f2a721658e7ee0494e2ac9967d969",748488,749528],"/project/sequence.js/node_modules/.pnpm/deepmerge@4.3.1/node_modules/deepmerge/dist/cjs.js":["30a374f658500f10d214d0c1b0aab81773cb6582",749528,751720],"/project/sequence.js/node_modules/.pnpm/is-module@1.0.0/node_modules/is-module/index.js":["eea7f2fb1af6df299384691d877d0cfd20060e1a",751720,752784],"/project/sequence.js/node_modules/.pnpm/@rollup+pluginutils@3.1.0_rollup@2.79.1/node_modules/@rollup/pluginutils/dist/cjs/index.js":["3f1a4e804542765b8ed26ef9c84451ad737a0e46",752784,758000],"/project/sequence.js/node_modules/.pnpm/@rollup+plugin-alias@3.1.9_rollup@2.79.1/node_modules/@rollup/plugin-alias/dist/index.js":["03c72c8b0000a9ae55e2629c7d348df1ec55d116",758000,758984],"/project/sequence.js/node_modules/.pnpm/@rollup+plugin-commonjs@15.1.0_rollup@2.79.1/node_modules/@rollup/plugin-commonjs/dist/index.js":["daa1dd2236b14ac67f24c4cac4d7ac04396b4fee",758984,771680],"/project/sequence.js/node_modules/.pnpm/commondir@1.0.1/node_modules/commondir/index.js":["62f48e7310292100f457fd315c1eaeabfb741a72",771680,772448],"/project/sequence.js/node_modules/.pnpm/estree-walker@2.0.2/node_modules/estree-walker/dist/umd/estree-walker.js":["12880a2841f321975ae59f2789d482753951bd1d",772448,775088],"/project/sequence.js/node_modules/.pnpm/magic-string@0.25.9/node_modules/magic-string/dist/magic-string.cjs.js":["f50cbc79c4a1f869984025ed5328e2da919fb6ba",775088,783208],"/project/sequence.js/node_modules/.pnpm/sourcemap-codec@1.4.8/node_modules/sourcemap-codec/dist/sourcemap-codec.umd.js":["dfa9b54ce6838473151cd191a4904db99063bcce",783208,784408],"/project/sequence.js/node_modules/.pnpm/is-reference@1.2.1/node_modules/is-reference/dist/is-reference.js":["568fec88af0aeead7c9719447c3968e506d4672b",784408,785672],"/project/sequence.js/node_modules/.pnpm/@rollup+plugin-replace@2.4.2_rollup@2.79.1/node_modules/@rollup/plugin-replace/dist/rollup-plugin-replace.cjs.js":["604d14be2deb4b6497cb252240415fac3753ec33",785672,787176],"/project/sequence.js/node_modules/.pnpm/magic-string@0.30.11/node_modules/magic-string/dist/magic-string.cjs.js":["a1b82214ad58aa225c3d8372a563a70c6df1753b",787176,797160],"/project/sequence.js/node_modules/.pnpm/@jridgewell+sourcemap-codec@1.5.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js":["f8c6b12bc867c6c3b76c14cd7bdab4e9c509048b",797160,801576],"/project/sequence.js/node_modules/.pnpm/@rollup+plugin-json@4.1.0_rollup@2.79.1/node_modules/@rollup/plugin-json/dist/index.js":["3adfd5d9369691483a6c1ef2087de488f2462a52",801576,802416],"/project/sequence.js/node_modules/.pnpm/jest-worker@26.6.2/node_modules/jest-worker/build/index.js":["69de41b40fe0bcb567f075c46d5c9e6221e71bbb",802416,804760],"/project/sequence.js/node_modules/.pnpm/jest-worker@26.6.2/node_modules/jest-worker/build/Farm.js":["5984ef8e7cd217ce38501bda08d5631a7e5a1d74",804760,806568],"/project/sequence.js/node_modules/.pnpm/jest-worker@26.6.2/node_modules/jest-worker/build/types.js":["6ac3d2e94b3e62de1635e2962750f77fbafc997f",806568,807664],"/project/sequence.js/node_modules/.pnpm/jest-worker@26.6.2/node_modules/jest-worker/build/WorkerPool.js":["5db5da39a2f1711c5bc5e13217a23a68d5b06860",807664,809080],"/project/sequence.js/node_modules/.pnpm/jest-worker@26.6.2/node_modules/jest-worker/build/base/BaseWorkerPool.js":["f17a88f0a0756a0e478bb4d1050f056c268639b5",809080,811496],"/project/sequence.js/node_modules/.pnpm/jest-worker@26.6.2/node_modules/jest-worker/build/workers/messageParent.js":["af373cbfef8bc3a2960cfdfcf21b056f591b75c8",811496,812512],"/project/sequence.js/node_modules/.pnpm/ci-info@3.9.0/node_modules/ci-info/index.js":["306147110a81e4d70b7952dedf1e7f2721b72866",812512,814096],"/project/sequence.js/node_modules/.pnpm/quick-lru@5.1.1/node_modules/quick-lru/index.js":["94a42747caa09e0af0d52f35c6ba68c78b9a6018",814096,815952],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/index.js":["c01f38060f8c1eea0a62ee127afc3a7601029818",815952,820192],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/internal/re.js":["4847405c77f0465eb4baebe1385b155e72b57f6a",820192,827392],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/internal/constants.js":["819a733e61b6014ca6feeb6a570304612afe2b52",827392,828624],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/internal/debug.js":["d6166e7a8eda16340619cb02ee09c19a422b8333",828624,829536],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/classes/semver.js":["209950c633d84021324a834a14dbcbf0fb3202f4",829536,831624],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/internal/parse-options.js":["19a8ad4d2c32f4386402bd9eb235df80c73a8f75",831624,832480],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/internal/identifiers.js":["510c174c5bfc993023542e3b4f699cd18e2e0559",832480,833352],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/parse.js":["1bfe17569d11f23f9a539340cee18bba0e3f4f0a",833352,834096],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/valid.js":["edc5b800b8f302ac7ce238a419a02810cdeed8f2",834096,834824],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/clean.js":["92466e73dbb620c7b0c58b16e8d39a6d0ff22bc5",834824,835552],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/inc.js":["5814d4948ca724f91f2b61213c011bf8034f112f",835552,836296],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/diff.js":["a52f6ea79cf0224fda0d44968159b8dc13e36d7c",836296,837032],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/major.js":["5e2668d635ca6c7bde9bc1b7f763f26674e83c11",837032,837776],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/minor.js":["4bc0bc3ec293449f5fea1cbcfe976c8d2a26cce5",837776,838520],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/patch.js":["a78ef0c69e82d2a7b1f4f697e620aef6ad1de458",838520,839264],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/prerelease.js":["40a72fe55e64efcc0a5c6b859a0378ab030837db",839264,840000],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/compare.js":["33ec903e117ba1fe05cddedb86a9601d94e193a7",840000,840728],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/rcompare.js":["51f8192667aa9e1320e7fe0616b583039e8042c0",840728,841448],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/compare-loose.js":["1c581d61f0ab057af7fed4ad01c66d0998d1aa03",841448,842184],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/compare-build.js":["97ac51143c3f5c2255ba09c0ec0f952a2aecd8d1",842184,842944],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/sort.js":["b7fc2bc365d5f6f9e2ad842441755e7b8b19de5c",842944,843688],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/rsort.js":["1e99dcf8aa9518558b2a6945302273ac7b8d69bc",843688,844432],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/gt.js":["d5e2d5e6294e56ee0a42e92e3a89d8cf294cb833",844432,845144],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/lt.js":["fad43ee11cd4b18e2fbaf50593ae540f27365a87",845144,845856],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/eq.js":["498639a97e5682386b94c24096f133db4fd163d0",845856,846568],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/neq.js":["dfa93428b2368cff5aefd91d812bed067cb31ad6",846568,847288],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/gte.js":["b9c50a385c8e3877108a001fb8548b122a155193",847288,848008],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/lte.js":["bd1875ed01c16e0bf753352e775cfc3d993cc228",848008,848728],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/cmp.js":["50a23a530aac08f1545e15bf6441bf031282789e",848728,849792],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/coerce.js":["c64737eb38e2f78a361af16155116dc84c2af368",849792,850728],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/classes/comparator.js":["bbb95e311cc51af3911406848972f6cc50761d8f",850728,852712],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/classes/range.js":["49ea81742058369f806770d7d1b1a73192f0ca75",852712,856504],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/internal/lrucache.js":["26ae88faff2fd3ef9fbda59267979c98a1fea511",856504,857648],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/functions/satisfies.js":["47a3e3141433768a2ca6a03841c842d15cf419c2",857648,858392],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/to-comparators.js":["4d609454b2e81450d85be8f56109af8ba6b61b92",858392,859144],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/max-satisfying.js":["4dcef246781158eef12758041375d1bce437a383",859144,859976],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/min-satisfying.js":["2155aea4b92343159e1b803f878a47297ca9aa66",859976,860808],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/min-version.js":["04dab32f676a52ee4c81f440eb1b5d6c7511afa5",860808,861712],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/valid.js":["7a1c6afbe83e28264a384b43ab8f6765f7649114",861712,862448],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/outside.js":["c6f8b84ebd967e5479159e2f876f3ba27530eb97",862448,863808],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/gtr.js":["4f69fb02e28923fe7126531d80862dc85bf94c19",863808,864536],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/ltr.js":["d4948b6f660390895f8ac0cfe4cad97bc1f15190",864536,865248],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/intersects.js":["3763224a30a86582b56a4cdf1ebaa97b5038e1c8",865248,865992],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/simplify.js":["bc651999d477c5698289adcd2ed8773cf7a2da11",865992,866848],"/project/sequence.js/node_modules/.pnpm/semver@7.6.3/node_modules/semver/ranges/subset.js":["94dce217bb98598dad72f194de19c5e2f3246d7b",866848,868384],"/project/sequence.js/node_modules/.pnpm/ms@2.1.3/node_modules/ms/index.js":["ea869663486f513cc4d1ca8312ed52a165c417fa",868384,869520],"/project/sequence.js/node_modules/.pnpm/@babel+helpers@7.25.0/node_modules/@babel/helpers/lib/index.js":["357f5f097a45815db5a715e876269d5bfabd7dac",869520,871352],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/index.js":["b4a694814012bf73e5e969685037c929ba16bfe1",871352,892992],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/react/isReactComponent.js":["95f5d5dc4b45810514357c0380914dc58241ba24",892992,893848],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/buildMatchMemberExpression.js":["e5d43e99dde5f498e5a3a92e71f2e952b501aac3",893848,894792],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/matchesPattern.js":["8454cf4b6cbd50a08bbc3c1de9ab5c16de493b00",894792,895680],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/generated/index.js":["5397377964d899fec62d8c34ffefadcf2fc440db",895680,926856],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/utils/shallowEqual.js":["10edc2f8f2dc3288ac96e3945e4ec1c0488f6487",926856,927640],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/utils/deprecationWarning.js":["afa3da502a334e2761cdd333fd39352dc94fc835",927640,928640],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/react/isCompatTag.js":["ff2f399e3c4be990b0da48d9bcba447ac6d461a2",928640,929440],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/builders/react/buildChildren.js":["9d4954881254318ce49c25a46c0e273cc92ed501",929440,930488],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/utils/react/cleanJSXElementLiteralChild.js":["6144987d113b19878587b0e55dbc45742bc61377",930488,931520],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/builders/generated/index.js":["09d118993950e33889672839e3812c3efd3524e0",931520,961360],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/builders/validateNode.js":["9f3d5f96da42cbf270e2324816aa2715fa7b8859",961360,962344],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/validate.js":["23bcdbb590e36bbf25fc5cddd90dd95806ca36e6",962344,963408],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/index.js":["75473cc26234f822479cf98daa1deb187f85bf7e",963408,967408],"/project/sequence.js/node_modules/.pnpm/to-fast-properties@2.0.0/node_modules/to-fast-properties/index.js":["6308ff82e9bca6a67e7aa25111f0105f1fee89fc",967408,968224],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/core.js":["0c388d4068048171798d941e2e7fb357320b05ff",968224,1020112],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/is.js":["ca3a7002a5b3266c519975795feb3df111781548",1020112,1021232],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isType.js":["c35549269452b8a2efed8d388de69e86d02446ad",1021232,1022096],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isPlaceholderType.js":["29b3523f38b94f9a08d792cc9719bac32db78d27",1022096,1023000],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isValidIdentifier.js":["2b54d4a54bd63122fc8ce0a01eb531972c85eebc",1023000,1023944],"/project/sequence.js/node_modules/.pnpm/@babel+helper-string-parser@7.24.8/node_modules/@babel/helper-string-parser/lib/index.js":["3208fae30d742f71f09711bad362b9f09c5a79a6",1023944,1026144],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/constants/index.js":["49f82c5373bcdc59fc1fe32267648bc6c5d9b0c2",1026144,1030376],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/utils.js":["94640118c885a5ce4b4172f1de7970677860a744",1030376,1034072],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/flow.js":["616514b741caeea6bb56cdcc38754dd10c9d1d72",1034072,1053376],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/jsx.js":["75150a8a7b5f295714f618445a43b5be5134b3df",1053376,1058928],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/misc.js":["81f57e1e1fb15e0918d7a46502025a4ab0d1f9d4",1058928,1060504],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/placeholders.js":["b2e696e21aeae96071880f487af8883f6e8be92d",1060504,1062232],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/experimental.js":["d79101cd151187ddfd0dd80c38f777759d01ae5d",1062232,1067752],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/typescript.js":["79cde51962b6d573285de195a91c6954d794ae8b",1067752,1086864],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/definitions/deprecated-aliases.js":["f29794e441858c3e2f2b1085aecb4c756d77d711",1086864,1087744],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/asserts/assertNode.js":["f1638bb583fe74ab8bb9a1bc6f454b491a348274",1087744,1088624],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isNode.js":["17f73842d38e0825ffa9f577e40f179503ee031b",1088624,1089488],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/asserts/generated/index.js":["148205bd999ad2039159ece59f6c8ac33a33606a",1089488,1122048],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js":["8769a998687162bc7550dcfe98a9e81f2d74a459",1122048,1123008],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js":["3c7a20f8b066ef7f858dcf14d6afcf1698ee141c",1123008,1124048],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/modifications/flow/removeTypeDuplicates.js":["f70e4d934e2b3b696ef00b1e34fee8ff252458e6",1124048,1125072],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/builders/typescript/createTSUnionType.js":["ac99b6bbdb3ee5879edb6ba5dbb012f987ef690b",1125072,1126216],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/modifications/typescript/removeTypeDuplicates.js":["54976f3b36b1e7db28188129aa2098282846f807",1126216,1127248],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/builders/generated/uppercase.js":["01146dc1a091c09905f4d4bd51bd9befcfc6013b",1127248,1183224],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/builders/productions.js":["5bf863371b472963ae72c1e64ac1b8630367ac2b",1183224,1184112],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/clone/cloneNode.js":["f15454de0a71e53a1e42faa07ef39ba7f8747f67",1184112,1185616],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/clone/clone.js":["00c128835dc3ef60c50001bc0e1372f29b797cd4",1185616,1186472],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/clone/cloneDeep.js":["0edb67fa97425f2d4079c8c64e097d0ddd5eb0cc",1186472,1187344],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/clone/cloneDeepWithoutLoc.js":["e7719f2ecf0c0657c5b6068c09af3c3bf0592068",1187344,1188248],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/clone/cloneWithoutLoc.js":["f63ebfbdab6d34badb74fd376c38f5cc8930423c",1188248,1189136],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/comments/addComment.js":["16f2d359874230847f36cb97742de44472f84594",1189136,1190016],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/comments/addComments.js":["c62e5ffd8cb9350cf9bc0589ca476d009ac85f9a",1190016,1190808],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/comments/inheritInnerComments.js":["d040738ed2802ba9d61a8394fe6c9ee65d734a76",1190808,1191720],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/utils/inherit.js":["5896fd13c8a775e0155b7185805c9727329f6730",1191720,1192488],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/comments/inheritLeadingComments.js":["f87077b689c318c639537f4c42698ed278e4f829",1192488,1193400],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/comments/inheritsComments.js":["42f6cbab43c95c7d831a62a991f7ae449cd9b5d8",1193400,1194528],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/comments/inheritTrailingComments.js":["5bd6c9b811fbe4893a2fbef744b7374e2c1e84c7",1194528,1195440],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/comments/removeComments.js":["a6564e4d0b4893afb3d965c187c6ee3c9990ee77",1195440,1196328],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/constants/generated/index.js":["150c5b7b3b23479f3e251f6341bd26ee2c70b185",1196328,1201792],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/ensureBlock.js":["9546e15b033129e9d14e953a1e431f1f9bf88824",1201792,1202672],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/toBlock.js":["e42f617eb9a3d06d9e088fa1e41225b663c2bff9",1202672,1203640],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/toBindingIdentifierName.js":["26959a098e207bbb2842e79e7bccdbabe8323408",1203640,1204560],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/toIdentifier.js":["bb776190e90daa84973bc9941adcdd8193bd1e5e",1204560,1205632],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/toComputedKey.js":["9eecc51376934654679e0386cd244a8dda5f0baf",1205632,1206624],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/toExpression.js":["c6891bc3f4882d86e3e6dbff896c85ce44923cb4",1206624,1207528],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/toKeyAlias.js":["5fbdc0ac8dc95b5eabec0be50d71e6c0865ce35c",1207528,1208792],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/modifications/removePropertiesDeep.js":["23860ef1d6c45e8f71b8228a1f377668e8c96f1b",1208792,1209824],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/traverse/traverseFast.js":["67af8be359598f6a3aafc7637358f7046033aeca",1209824,1210720],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/modifications/removeProperties.js":["73107880504998df7f8c5b3bd7a278383e84c8bc",1210720,1211960],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/toStatement.js":["574ede0cdc26b05072b0646e7d66221fa1da6558",1211960,1212960],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/valueToNode.js":["8b5b35bc076f64bd9672753d23da7ec67b106fd8",1212960,1214288],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/modifications/appendToMemberExpression.js":["27439fd3e557f640b2f818d7361fc0c12f46cb43",1214288,1215216],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/modifications/inherits.js":["746ddefd49e0fbafb0f53d49ac490d9e5a67eccf",1215216,1216200],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/modifications/prependToMemberExpression.js":["5a713459848abc5691a7b9f14ad0112175c90ae5",1216200,1217216],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/retrievers/getAssignmentIdentifiers.js":["56001c75c4a7d7d9734e6f5410d5feef3c61a3c0",1217216,1218032],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js":["09124ac3269656d268bb5bb4745670c8961541ff",1218032,1221448],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/retrievers/getOuterBindingIdentifiers.js":["d0c30354a56b626a2a1533a68dd36f03d75f35d1",1221448,1222424],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/retrievers/getFunctionName.js":["0fb9488c606146a2e06e65a956f17c5ed503955b",1222424,1223496],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/traverse/traverse.js":["7992ab182de5d6e2635aa63d27e20ae6119fd085",1223496,1224440],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isBinding.js":["4184b924d4bf2122e4d64d4661db34225e6ce581",1224440,1225352],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isBlockScoped.js":["551efbe73814fbdf9b09af1a6dbd9edab8ca12c9",1225352,1226320],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isLet.js":["3a0c0986cdca1871a5c5ed9260abcc71f3622c6d",1226320,1227272],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isImmutable.js":["87ce8fd906f5fe6fea21757f3b96e2dbf10d45b9",1227272,1228240],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isNodesEquivalent.js":["8de1678d5fea7521128e49da8471adb7ac189ad8",1228240,1229152],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isReferenced.js":["8759a5583bcae62a90768a3418d106c78e08f2fe",1229152,1229944],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isScope.js":["db0b7d08fe2f223e5bb846f2713a608d9c4d3204",1229944,1230808],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isSpecifierDefault.js":["8c8652248a736fc000db5f45ec1d1a0570acb952",1230808,1231720],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isValidES3Identifier.js":["49d11809665072371ec9b0d5fe83f5214001eb6f",1231720,1233200],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/validators/isVar.js":["a8ac19b347058fbb51e9c0b9b35a838cd9c6ef14",1233200,1234152],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/toSequenceExpression.js":["9384f95b71f9748d64908bdc9b4b675c12d14104",1234152,1235096],"/project/sequence.js/node_modules/.pnpm/@babel+types@7.25.2/node_modules/@babel/types/lib/converters/gatherSequenceExpressions.js":["ba5ecf63baf8dfbe57529a29490ad381b7c8625b",1235096,1236424],"/project/sequence.js/node_modules/.pnpm/@babel+helpers@7.25.0/node_modules/@babel/helpers/lib/helpers-generated.js":["bdfe68621914607bd09d0c2a3dc4e7a2d74a3b9f",1236424,1350008],"/project/sequence.js/node_modules/.pnpm/@babel+template@7.25.0/node_modules/@babel/template/lib/index.js":["9a3926ea29ec8d546e723aa8a1353306e0bd0ad5",1350008,1351328],"/project/sequence.js/node_modules/.pnpm/@babel+template@7.25.0/node_modules/@babel/template/lib/formatters.js":["f9b9d9a37cbf61be7616b0656d92b8b79fa52b1f",1351328,1353448],"/project/sequence.js/node_modules/.pnpm/@babel+template@7.25.0/node_modules/@babel/template/lib/builder.js":["f23864c5185e593ed3929af741bf8a1ee43b4603",1353448,1354712],"/project/sequence.js/node_modules/.pnpm/@babel+template@7.25.0/node_modules/@babel/template/lib/options.js":["a2154aa8b4523eac2c1c71ebfcba32308afcd31f",1354712,1355976],"/project/sequence.js/node_modules/.pnpm/@babel+template@7.25.0/node_modules/@babel/template/lib/string.js":["d4bd2d379e5c5247bb0e825aa911836b07a7ad72",1355976,1357000],"/project/sequence.js/node_modules/.pnpm/@babel+template@7.25.0/node_modules/@babel/template/lib/parse.js":["1a725f4ba5a1fe7a4fcd95195b3d01be192b747d",1357000,1358904],"/project/sequence.js/node_modules/.pnpm/@babel+parser@7.25.3/node_modules/@babel/parser/lib/index.js":["d150d62841d0f12e7f4b42f5291013c9b1ebde79",1358904,1501792],"/project/sequence.js/node_modules/.pnpm/@babel+template@7.25.0/node_modules/@babel/template/lib/populate.js":["77692699c5cb74c059e0c9c786e2ffb59a68f434",1501792,1503200],"/project/sequence.js/node_modules/.pnpm/@babel+template@7.25.0/node_modules/@babel/template/lib/literal.js":["9f9a7059025c507617ea6303f9106ee2e53ddcb9",1503200,1504392],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/index.js":["c98c5ea2b11bbdb4bccf13ff70509990fad9919f",1504392,1505784],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/source-map.js":["5b5a15c3b2d5fec4b5c62457f3016e8aa33a46f0",1505784,1507336],"/project/sequence.js/node_modules/.pnpm/@jridgewell+gen-mapping@0.3.5/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.umd.js":["f59a96193c4d0050b224b915ec09303a0b363013",1507336,1511344],"/project/sequence.js/node_modules/.pnpm/@jridgewell+set-array@1.2.1/node_modules/@jridgewell/set-array/dist/set-array.umd.js":["20be9f5c3d323c9aca346a00e62f8d7e4a1fefa0",1511344,1513408],"/project/sequence.js/node_modules/.pnpm/@jridgewell+trace-mapping@0.3.25/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js":["d14969c9417dabccd90411b4770dc55726dea41d",1513408,1519576],"/project/sequence.js/node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js":["3d56b7861cb317124e76dc7f2caac7d4c8cf3a86",1519576,1522248],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/printer.js":["87b6083bdeb088244d146ec7094b137c7d19ff47",1522248,1529232],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/buffer.js":["1e5f3acd7d2127345524d342f8aeecb542146a50",1529232,1532440],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/node/index.js":["84c5abeb85944041673e6fa57c883a880e59e2d0",1532440,1534840],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/node/whitespace.js":["6432f37adcbacc354300b84a879ef6e0e504341a",1534840,1538560],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/node/parentheses.js":["616888cbcfd6f0e0b898aff092aae10647710d25",1538560,1544136],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/index.js":["19e79ce1eedfd872c35b815d444f6288c4050140",1544136,1547024],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/template-literals.js":["9069eae70537e6d8145bdcd443a7322a884edbcb",1547024,1548016],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/expressions.js":["7c7d6368f96052f5613036cd7504ffdb1fbc5c67",1548016,1551560],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/statements.js":["bba4e7700a33a7c482fb2de6620782ce25e03a34",1551560,1554504],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/classes.js":["95b2bae54b2cda46b6bcfcf2bfc9f455f045fcdd",1554504,1556208],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/methods.js":["6f0304fea2d5eaa3884b09293ee074a50e0ecd45",1556208,1557984],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/modules.js":["437038a7e68f202f78dd78b70156899cf75e6ff5",1557984,1560488],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/types.js":["09778cd4447014d0bb8f4d6053d455da8b990f3a",1560488,1563440],"/project/sequence.js/node_modules/.pnpm/jsesc@2.5.2/node_modules/jsesc/jsesc.js":["1ddb9ef6ced291147886096f09527cb1980a5572",1563440,1565640],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/flow.js":["5d7383206f35f019e6364dbd6930405141c3c74a",1565640,1573640],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/base.js":["51d18e9c693ca992d7d0a779deac61c493cb0e32",1573640,1575120],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/jsx.js":["85bf426cf47cadb0aebb50eb9f23b2c7e1b24d7c",1575120,1577248],"/project/sequence.js/node_modules/.pnpm/@babel+generator@7.25.0/node_modules/@babel/generator/lib/generators/typescript.js":["9a4ed7457f46c3c1159be1b9847355edbe87c70c",1577248,1584680]} \ No newline at end of file diff --git a/v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSworkspacezSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.BLOB b/v8-compile-cache-0/x64/11.3.244.8-node.19/zSprojectzSworkspacezSnode_moduleszS.pnpmzS@preconstruct+cli@2.8.7zSnode_moduleszS@preconstructzSclizSbin.js.BLOB new file mode 100644 index 0000000000000000000000000000000000000000..5e6ea312b8f18c714d6daec2d4489bf74de8be23 GIT binary patch literal 1583672 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�k^YMrpgSG-0QKZLLd(2 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ξ=Dm=DK23iH{x=i}<| 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%0AbRTePw~2@ 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

O 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

Jv>$!H@7qWJx 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

}&(ID0)5?9LF_lL@v*X~wB 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 + + + + + Create Wagmi + + +

+ + + diff --git a/wagmi-project/package.json b/wagmi-project/package.json new file mode 100644 index 0000000000..fb48d8e6a1 --- /dev/null +++ b/wagmi-project/package.json @@ -0,0 +1,29 @@ +{ + "name": "wagmi-project", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "biome check .", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/react-query": "5.64.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "viem": "^2.x", + "wagmi": "~0.x.x" + }, + "devDependencies": { + "@biomejs/biome": "^1.8.0", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.2.1", + "@wagmi/cli": "~0.x.x", + "buffer": "^6.0.3", + "typescript": "^5.4.5", + "vite": "^5.2.11" + } +} diff --git a/wagmi-project/src/App.tsx b/wagmi-project/src/App.tsx new file mode 100644 index 0000000000..1cf0716bd0 --- /dev/null +++ b/wagmi-project/src/App.tsx @@ -0,0 +1,46 @@ +import { useAccount, useConnect, useDisconnect } from 'wagmi' + +function App() { + const account = useAccount() + const { connectors, connect, status, error } = useConnect() + const { disconnect } = useDisconnect() + + return ( + <> +
+

Account

+ +
+ status: {account.status} +
+ addresses: {account.addresses?.map(addr =>
{addr}
)} +
+ chainId: {account.chainId} +
+ + {account.status === 'connected' && ( + + )} +
+ +
+

Connect

+ {connectors.map((connector) => ( + + ))} +
{status}
+
{error?.message}
+
+ + ) +} + +export default App diff --git a/wagmi-project/src/index.css b/wagmi-project/src/index.css new file mode 100644 index 0000000000..0733a7ee6b --- /dev/null +++ b/wagmi-project/src/index.css @@ -0,0 +1,21 @@ +:root { + background-color: #181818; + color: rgba(255, 255, 255, 0.87); + color-scheme: light dark; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-synthesis: none; + font-weight: 400; + line-height: 1.5; + text-rendering: optimizeLegibility; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +@media (prefers-color-scheme: light) { + :root { + background-color: #f8f8f8; + color: #181818; + } +} diff --git a/wagmi-project/src/main.tsx b/wagmi-project/src/main.tsx new file mode 100644 index 0000000000..d999e1a932 --- /dev/null +++ b/wagmi-project/src/main.tsx @@ -0,0 +1,24 @@ +import { Buffer } from 'buffer' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import React from 'react' +import ReactDOM from 'react-dom/client' +import { WagmiProvider } from 'wagmi' + +import App from './App.tsx' +import { config } from './wagmi.ts' + +import './index.css' + +globalThis.Buffer = Buffer + +const queryClient = new QueryClient() + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + + + , +) diff --git a/wagmi-project/src/vite-env.d.ts b/wagmi-project/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/wagmi-project/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/wagmi-project/src/wagmi.ts b/wagmi-project/src/wagmi.ts new file mode 100644 index 0000000000..43cf231934 --- /dev/null +++ b/wagmi-project/src/wagmi.ts @@ -0,0 +1,22 @@ +import { http, createConfig } from 'wagmi' +import { mainnet, sepolia } from 'wagmi/chains' +import { coinbaseWallet, injected, walletConnect } from 'wagmi/connectors' + +export const config = createConfig({ + chains: [mainnet, sepolia], + connectors: [ + injected(), + coinbaseWallet(), + walletConnect({ projectId: import.meta.env.VITE_WC_PROJECT_ID }), + ], + transports: { + [mainnet.id]: http(), + [sepolia.id]: http(), + }, +}) + +declare module 'wagmi' { + interface Register { + config: typeof config + } +} diff --git a/wagmi-project/tsconfig.json b/wagmi-project/tsconfig.json new file mode 100644 index 0000000000..a7fc6fbf23 --- /dev/null +++ b/wagmi-project/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/wagmi-project/tsconfig.node.json b/wagmi-project/tsconfig.node.json new file mode 100644 index 0000000000..42872c59f5 --- /dev/null +++ b/wagmi-project/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/wagmi-project/vite.config.ts b/wagmi-project/vite.config.ts new file mode 100644 index 0000000000..36f7f4e1bc --- /dev/null +++ b/wagmi-project/vite.config.ts @@ -0,0 +1,7 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) From 4788bf24383f85d86560f57c2848b0b7fade6a66 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 22 Dec 2025 02:26:11 +0700 Subject: [PATCH 02/23] Update wagmi-project/src/main.tsx Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- wagmi-project/src/main.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wagmi-project/src/main.tsx b/wagmi-project/src/main.tsx index d999e1a932..c22a9a81f9 100644 --- a/wagmi-project/src/main.tsx +++ b/wagmi-project/src/main.tsx @@ -9,7 +9,9 @@ import { config } from './wagmi.ts' import './index.css' -globalThis.Buffer = Buffer +if (!('Buffer' in globalThis)) { + ;(globalThis as any).Buffer = Buffer +} const queryClient = new QueryClient() From e9a1ced6b2efb825c552217e8fcc8ce35b4b2420 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 22 Dec 2025 02:28:12 +0700 Subject: [PATCH 03/23] Update wagmi-project/package.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- wagmi-project/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wagmi-project/package.json b/wagmi-project/package.json index fb48d8e6a1..b722de11fd 100644 --- a/wagmi-project/package.json +++ b/wagmi-project/package.json @@ -14,7 +14,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "viem": "^2.x", - "wagmi": "~0.x.x" + "wagmi": "^2.0.0" }, "devDependencies": { "@biomejs/biome": "^1.8.0", From 531c3f9f923a68eabcc0f3c93efeae92e39cb0ab Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 22 Dec 2025 02:28:39 +0700 Subject: [PATCH 04/23] Update wagmi-project/package.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --- wagmi-project/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wagmi-project/package.json b/wagmi-project/package.json index b722de11fd..b3573d0142 100644 --- a/wagmi-project/package.json +++ b/wagmi-project/package.json @@ -21,7 +21,7 @@ "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.2.1", - "@wagmi/cli": "~0.x.x", + "@wagmi/cli": "^2.0.0", "buffer": "^6.0.3", "typescript": "^5.4.5", "vite": "^5.2.11" From 8710ac85e0e945763bc2a02cfdd208e022d61deb Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Mon, 29 Dec 2025 17:13:10 +0700 Subject: [PATCH 05/23] 0xsequence/sequence.js/master (#166) * Pin foundry to v1.5.0 instead of nightly (0xsequence#947) (#134) * Bump next from 15.5.7 to 15.5.9 (#944) Bumps [next](https://github.com/vercel/next.js) from 15.5.7 to 15.5.9. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.5.7...v15.5.9) --- updated-dependencies: - dependency-name: next dependency-version: 15.5.9 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Pin foundry to v1.5.0 instead of nightly (#947) * Include repo and extras in syncpack config to ensure deps are synced (#945) * Include repo and extras in syncpack config to ensure deps are synced across all * Updating support deps * Updating deps * Updating pnpm lock * Fixing type errors within wdk tests * Short circuit 404s (#949) * skip witness on signers that don't support it * add passkey to test * 3.0.0-beta.6 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Taylan Pince Co-authored-by: Corban Riley Co-authored-by: Agusx1211 * Update packages/wallet/wdk/test/wallets.test.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> * Update packages/wallet/wdk/test/wallets.test.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Taylan Pince Co-authored-by: Corban Riley Co-authored-by: Agusx1211 Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> From 81c62baff3bdab20a1bf2f6918ee68128ecd4659 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Sat, 17 Jan 2026 20:28:49 +0700 Subject: [PATCH 06/23] Update indexer client (#207) * [AUTOMATED] Update: proto/clients/indexer*.gen.ts * [AUTOMATED] Update: proto/clients/indexer*.gen.ts * [AUTOMATED] Update: proto/clients/indexer*.gen.ts * [AUTOMATED] Update: proto/clients/indexer*.gen.ts * [AUTOMATED] Update: proto/clients/indexer*.gen.ts * [AUTOMATED] Update: proto/clients/indexer*.gen.ts * [AUTOMATED] Update: proto/clients/indexer*.gen.ts * [AUTOMATED] Update: proto/clients/indexer*.gen.ts --------- Co-authored-by: marino39 <722509+marino39@users.noreply.github.com> Co-authored-by: pkieltyka <18831+pkieltyka@users.noreply.github.com> Co-authored-by: xiam <385670+xiam@users.noreply.github.com> --- packages/indexer/src/indexer.gen.ts | 2763 +++++++++++++++++++++++++ packages/indexer/src/indexergw.gen.ts | 1785 ++++++++++++++++ 2 files changed, 4548 insertions(+) create mode 100644 packages/indexer/src/indexer.gen.ts create mode 100644 packages/indexer/src/indexergw.gen.ts diff --git a/packages/indexer/src/indexer.gen.ts b/packages/indexer/src/indexer.gen.ts new file mode 100644 index 0000000000..0fc4a52a15 --- /dev/null +++ b/packages/indexer/src/indexer.gen.ts @@ -0,0 +1,2763 @@ +/* eslint-disable */ +// sequence-indexer v0.4.0 cfbb0160fed6f7e2208a73cad454f3cddace9c7a +// -- +// Code generated by Webrpc-gen@v0.31.2 with typescript generator. DO NOT EDIT. +// +// webrpc-gen -schema=merged.gen.json -service=Indexer -target=typescript -client -out=./clients/indexer.gen.ts + +// Webrpc description and code-gen version +export const WebrpcVersion = 'v1' + +// Schema version of your RIDL schema +export const WebrpcSchemaVersion = 'v0.4.0' + +// Schema hash generated from your RIDL schema +export const WebrpcSchemaHash = 'cfbb0160fed6f7e2208a73cad454f3cddace9c7a' + +// +// Client interface +// + +export interface IndexerClient { + addWebhookListener(req: AddWebhookListenerRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * Fetches a single receipt and then will stop the subscription + */ + fetchTransactionReceipt( + req: FetchTransactionReceiptRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * Fetches a single receipt with filter and then will stop the subscription + */ + fetchTransactionReceiptWithFilter( + req: FetchTransactionReceiptWithFilterRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * Webhooks + */ + getAllWebhookListeners( + req: GetAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * Get balance update aggregate values -- useful for syncing balance details of a contract, ie. from Skyweaver. + * Also consider using SubscribeBalanceUpdates or SubscribeEvents as other alternatives. + */ + getBalanceUpdates(req: GetBalanceUpdatesRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * Get the chain ID of the indexer + */ + getChainID(headers?: object, signal?: AbortSignal): Promise + + /** + * Queries an ethereum node for the latest and confirm ETH balances + * DEPRECATED: use GetNativeTokenBalance instead + * + * @deprecated GetNativeTokenBalance + */ + getEtherBalance(req: GetEtherBalanceRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetMarketplaceOrders queries marketplace orders with filtering and pagination. + * + * Retrieves buy orders (offers) and sell orders (listings) from a specific marketplace + * and collection with comprehensive filtering options. + * + * Parameters: + * marketplaceContractAddress: Target marketplace contract (required) + * collectionAddress: NFT collection contract (required) + * filter: MarketplaceOrderFilter with options: + * - isListing: true=listings, false=offers, omit=both + * - userAddresses: Include specific users + * - currencyAddresses: Filter by currencies (empty=all) + * - orderIds: Filter by specific order ids (empty=all) + * - tokenIds: Filter by specific tokens (empty=all) + * - excludeUserAddresses: Exclude specific users + * - blockNumberGt: Orders greater than block number + * - createdAtAfter: Orders after timestamp + * - orderStatuses: Filter by status (OPEN, CLOSED, CANCELLED) + * - returnExpired: Include expired orders + * page: Pagination control (optional) + * + * Returns: Updated pagination info and array of matching orders + */ + getMarketplaceOrders( + req: GetMarketplaceOrdersRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetMarketplaceTopOrders finds the most competitive orders for specific tokens. + * + * Optimized for price discovery, returns the best available orders for each token. + * Useful for displaying current market prices and finding trading opportunities. + * + * Parameters: + * marketplaceContractAddress: Target marketplace contract (required) + * collectionAddress: NFT collection contract (required) + * filter: MarketplaceTopOrdersFilter with options: + * - currencyAddresses: Consider specific currencies (empty=all) + * - tokenIds: Target token IDs (required, non-empty) + * - isListing: true=listings/sell orders, false=offers/buy orders + * - priceSort: ASC=lowest first, DESC=highest first + * - excludeUser: Hide orders from specific user + * + * Returns: Array of top-priced active orders, sorted by priceSort preference + */ + getMarketplaceTopOrders( + req: GetMarketplaceTopOrdersRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetNativeTokenBalance queries an ethereum node for the latest native token account balance. + * The native token is the token of the chain the indexer is connected to, for example, ETH on Ethereum + * and POL on Polygon. + */ + getNativeTokenBalance( + req: GetNativeTokenBalanceRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetTokenBalances returns a balance summary/details for a specific account. By default + * if accountAddress is left empty, it will use the account from the jwt session. + * + * Also, if contractAddress is undefined, then it will list all current user coins/collectibles. + * But, if contractAddress is provided, then it will return the token balances for the contract, this is + * only useful for 1155, but for other tokens, it can act as a filter for the single balance. + * + * DEPRECATED: use GetTokenBalancesSummary / GetTokenBalancesDetails + * + * @deprecated GetTokenBalancesSummary + */ + getTokenBalances(req: GetTokenBalancesRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetTokenBalancesByContract returns a balances for a specific accounts and + * contracts. The collection ERC721 & ERC1155 tokens are represented as + * individual balances. + * + * If `filter` is not provided, it will error out as it requires at least + * contract address. + * + * If `filter.contractStatus` is not provided, it will include verified only + * tokens. + */ + getTokenBalancesByContract( + req: GetTokenBalancesByContractRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetTokenBalancesDetails returns a detailed balance summary for a specific + * accounts. The collection ERC721 & ERC1155 tokens are represented as + * individual balances. + * + * If `filter` is not provided, it will use the filter with account from the + * jwt session. + * + * If `filter.contractStatus` is not provided, it will include verified only + * tokens. + */ + getTokenBalancesDetails( + req: GetTokenBalancesDetailsRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetTokenBalancesSummary returns a summary of token balances for a specific + * accounts. The collection ERC721 & ERC1155 tokens are represented as a + * single aggregated balance. + * + * If `filter` is not provided, it will use the filter with account from the + * jwt session. + * + * If `filter.contractStatus` is not provided, it will include verified only + * tokens. + */ + getTokenBalancesSummary( + req: GetTokenBalancesSummaryRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetTokenIDRanges returns the range of tokenIDs for a token collection contract. + * This is useful for ERC-721 and ERC-1155 contracts to get the range of valid tokenIDs. It is similar to + * GetTokenIDs, but returns the range of tokenIDs instead of the list of tokenIDs, which is more efficient + * for large collections and very easy to the caller to expand the range into a list of tokenIDs. + * + * NOTE: this method will only return up to 15,000 ranges, if there are more ranges, it will return + * a boolean to indicate there are more ranges beyond the first 15,000. Therefore, if `moreRanges` is + * false then you have all the ranges, but if true, you need to make a follow up call to fetch the next + * page of ranges. + * + * As an example, if a NFT collection of 100,000 tokens uses ids from 1,2,3,...,100_000 then this endpoint + * will return just a single range from [1,100_000], but if there are gaps between the sequence, then + * those will be broken into separate range entries. + */ + getTokenIDRanges(req: GetTokenIDRangesRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetTokenIDs returns the list of each individual token id for a token collection contract. + * This is useful for ERC-721 and ERC-1155 contracts to get the list of valid tokenIDs. + */ + getTokenIDs(req: GetTokenIDsRequest, headers?: object, signal?: AbortSignal): Promise + + getTokenPrice(req: GetTokenPriceRequest, headers?: object, signal?: AbortSignal): Promise + + getTokenPrices(req: GetTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetTokenSupplies returns the set of tokenIDs used by a contract address, supporting ERC-20, ERC-721, and ERC-1155 + * contracts, and their respective supply as well. + */ + getTokenSupplies(req: GetTokenSuppliesRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetTokenSuppliesMap returns the token supplies of ERC-20 and ERC-1155 tokens as requested in the `tokenMap` + * represented as a map of contractAddress :: []tokenIDs. + * + * For an ERC-20 specify tokenIDs as an empty array or [0], for example, { '0xdef': [] } or { '0xdef': [0] } + * For ERC-1155 pass the array of tokens are strings, ie. { '0xabc': ['1', '2', '3'] } + */ + getTokenSuppliesMap( + req: GetTokenSuppliesMapRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * History of mined transactions for the account which includes a list of token transfers (sent/recieved) + * and sent transactions from a Sequence wallet + */ + getTransactionHistory( + req: GetTransactionHistoryRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + getWebhookListener(req: GetWebhookListenerRequest, headers?: object, signal?: AbortSignal): Promise + + listTokenPrices(req: ListTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise + + pauseAllWebhookListeners( + req: PauseAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * Ping the indexer + */ + ping(headers?: object, signal?: AbortSignal): Promise + + removeAllWebhookListeners( + req: RemoveAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + removeWebhookListener( + req: RemoveWebhookListenerRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + resumeAllWebhookListeners( + req: ResumeAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * Get the current runtime health status of the indexer + */ + runtimeStatus(headers?: object, signal?: AbortSignal): Promise + + /** + * SubscribeBalanceUpdates listens to balance updates for a specific contract address + */ + subscribeBalanceUpdates( + req: SubscribeBalanceUpdatesRequest, + options: WebrpcStreamOptions + ): WebrpcStreamController + + /** + * SubscribeEvents listens to events on-chain based on the filter criteria + * + * TODO: some additional options can be passed such as block, reorg true, etc. + * or stay behind, etc. + */ + subscribeEvents(req: SubscribeEventsRequest, options: WebrpcStreamOptions): WebrpcStreamController + + /** + * Listen to transaction receipts on-chain based on the filter criteria + */ + subscribeReceipts( + req: SubscribeReceiptsRequest, + options: WebrpcStreamOptions + ): WebrpcStreamController + + /** + * Re-sync an incorrect token balance with the correct on-chain balance + * NOTE: this method is almost never used, but we've marked it internal in case + * we ever want to use it again. This method was written a very long time ago in + * scenarios when the indexer had little bugs, but now its solid. + */ + syncBalance(req: SyncBalanceRequest, headers?: object, signal?: AbortSignal): Promise + + toggleWebhookListener( + req: ToggleWebhookListenerRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + updateWebhookListener( + req: UpdateWebhookListenerRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * Get the current version of the indexer + */ + version(headers?: object, signal?: AbortSignal): Promise +} + +// +// Schema types +// + +export interface Asset { + id: number + collectionId: number + tokenId?: string + url?: string + metadataField: string + name?: string + filesize?: number + mimeType?: string + width?: number + height?: number + updatedAt?: string +} + +export interface BloomStats { + hitRatio: string + falsePositivesPercent: string + hitCount: number + missCount: number + falsePositives: number +} + +export interface BloomStatus { + enabled: boolean + initialized: boolean + bloomInitElapsedTime: string + stats: BloomStats +} + +export interface Bond { + pebble: PebbleMetrics + estimatedDiskUsagePerTable: any + estimatedDiskUsageTotal: string +} + +export interface ChainInfo { + chainId: number + chainName: string +} + +export interface ContractInfo { + chainId: number + address: string + source: string + name: string + type: string + symbol: string + decimals?: number + logoURI: string + deployed: boolean + bytecodeHash: string + extensions: ContractInfoExtensions + updatedAt: string + queuedAt?: string + status: ResourceStatus +} + +export interface ContractInfoExtensionBridgeInfo { + tokenAddress: string +} + +export interface ContractInfoExtensionIndexingInfo { + useOnChainBalance: boolean +} + +export interface ContractInfoExtensions { + link?: string + description?: string + categories?: Array + bridgeInfo?: { [key: string]: ContractInfoExtensionBridgeInfo } + indexingInfo?: ContractInfoExtensionIndexingInfo + ogImage?: string + ogName?: string + originChainId?: number + originAddress?: string + blacklist?: boolean + verified?: boolean + verifiedBy?: string + featured?: boolean + featureIndex?: number +} + +export enum ContractType { + UNKNOWN = 'UNKNOWN', + NATIVE = 'NATIVE', + ERC20 = 'ERC20', + ERC721 = 'ERC721', + ERC1155 = 'ERC1155', + SEQUENCE_WALLET = 'SEQUENCE_WALLET', + ERC20_BRIDGE = 'ERC20_BRIDGE', + ERC721_BRIDGE = 'ERC721_BRIDGE', + ERC1155_BRIDGE = 'ERC1155_BRIDGE', + SEQ_MARKETPLACE = 'SEQ_MARKETPLACE', + ERC6909 = 'ERC6909' +} + +export enum ContractVerificationStatus { + VERIFIED = 'VERIFIED', + UNVERIFIED = 'UNVERIFIED', + ALL = 'ALL' +} + +export interface DiskUsage { + humanReadable: string + used: number + size: number + percent: number + dirs: { [key: string]: string } +} + +export interface EtherBalance { + accountAddress: string + balanceWei: string +} + +export interface EventDecoded { + topicHash: string + eventSig: string + types: Array + names: Array + values: Array +} + +export interface EventFilter { + events?: Array + contractAddresses?: Array + accounts?: Array + tokenIDs?: Array +} + +export interface EventLog { + id: number + uid: string + type: EventLogType + blockNumber: number + blockHash: string + parentBlockHash: string + contractAddress: string + contractType: ContractType + txnHash: string + txnIndex: number + txnLogIndex: number + logDataType: EventLogDataType + ts: string + txnInfo?: TxnInfo + rawLog?: { [key: string]: any } + event?: EventDecoded +} + +export enum EventLogDataType { + EVENT = 'EVENT', + TOKEN_TRANSFER = 'TOKEN_TRANSFER', + NATIVE_TOKEN_TRANSFER = 'NATIVE_TOKEN_TRANSFER', + SEQUENCE_TXN = 'SEQUENCE_TXN' +} + +export enum EventLogType { + UNKNOWN = 'UNKNOWN', + BLOCK_ADDED = 'BLOCK_ADDED', + BLOCK_REMOVED = 'BLOCK_REMOVED' +} + +export interface GatewayBackendResponseTime { + percentiles: { [key: string]: number } + average: number +} + +export interface GatewayBackendRuntimeStatus { + name: string + chainId: number + responseTime: GatewayBackendResponseTime +} + +export interface GatewayEtherBalance { + chainId: number + errorReason?: string + result: EtherBalance +} + +export interface GatewayNativeTokenBalance { + chainId: number + errorReason?: string + result: NativeTokenBalance +} + +export interface GatewayNativeTokenBalances { + chainId: number + errorReason?: string + results: Array +} + +export interface GatewayPrice { + chainID: number + errorReason?: string + results: Array +} + +export interface GatewayRuntimeStatus { + healthOK: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + backends: Array +} + +export interface GatewayTokenBalance { + chainId: number + errorReason?: string + results: Array +} + +export interface GatewayTokenPriceQuery { + chainID: number + queries: Array +} + +export interface GatewayTransaction { + chainId: number + errorReason?: string + results: Array +} + +export interface IndexState { + chainId: string + lastBlockNum: number + lastBlockHash: string +} + +export interface IndexedBlock { + blockNumber: number + blockShortHash: string +} + +export interface IndexerMetrics { + blocksPerSecond: number + eventsPerSecond: number +} + +export interface MarketplaceOrder { + orderId: string + tokenContract: string + tokenId: string + isListing: boolean + quantity: string + quantityRemaining: string + currencyAddress: string + pricePerToken: string + expiry: string + orderStatus: OrderStatus + createdBy: string + blockNumber: number + orderbookContractAddress: string + createdAt: number +} + +export interface MarketplaceOrderFilter { + isListing?: boolean + userAddresses?: Array + currencyAddresses: Array + orderIds: Array + tokenIds: Array + excludeUserAddresses?: Array + blockNumberGt: number + createdAtAfter: number + orderStatuses: Array + returnExpired: boolean +} + +export interface MarketplaceTopOrdersFilter { + currencyAddresses: Array + tokenIds: Array + isListing: boolean + priceSort: SortOrder + excludeUser?: string +} + +export interface MetadataOptions { + verifiedOnly?: boolean + unverifiedOnly?: boolean + includeContracts?: Array +} + +export interface NativeTokenBalance { + accountAddress: string + chainId: number + name: string + symbol: string + balance: string + balanceUSD: string + priceUSD: string + priceUpdatedAt?: string + errorReason?: string +} + +export enum NetworkType { + MAINNETS = 'MAINNETS', + TESTNETS = 'TESTNETS', + ALL = 'ALL' +} + +export enum OrderStatus { + OPEN = 'OPEN', + CLOSED = 'CLOSED', + CANCELLED = 'CANCELLED' +} + +export interface Page { + page?: number + column?: string + before?: any + after?: any + sort?: Array + pageSize?: number + more?: boolean +} + +export interface PebbleMetrics { + compactionCount: number + compactionEstimatedDebt: number + compactionInProgressBytes: number + compactionNumInProgress: number + compactionMarkedFiles: number +} + +export interface Price { + contractAddress: string + tokenID?: string + priceUSD: string + updatedAt?: string +} + +export enum ResourceStatus { + NOT_AVAILABLE = 'NOT_AVAILABLE', + REFRESHING = 'REFRESHING', + AVAILABLE = 'AVAILABLE' +} + +export interface RuntimeChecks { + running: boolean + runnables: any + cgoEnabled: boolean + quotaControlEnabled: boolean + syncMode: string + percentIndexed: number + lastBlockNum: number + lastBlockNumWithState: number + bloomStatus: BloomStatus + bond: Bond + diskUsage: DiskUsage + metrics: IndexerMetrics +} + +export interface RuntimeStatus { + healthOK: boolean + indexerEnabled: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + chainID: number + checks: RuntimeChecks +} + +export interface SortBy { + column: string + order: SortOrder +} + +export enum SortOrder { + DESC = 'DESC', + ASC = 'ASC' +} + +export interface TokenBalance { + contractType: ContractType + contractAddress: string + accountAddress: string + tokenID?: string + balance: string + balanceUSD: string + priceUSD: string + priceUpdatedAt?: string + blockHash: string + blockNumber: number + chainId: number + uniqueCollectibles: string + isSummary: boolean + contractInfo?: ContractInfo + tokenMetadata?: TokenMetadata +} + +export interface TokenBalanceFilter { + contractAddress: string + sinceBlockNumber: number +} + +export interface TokenBalancesByContractFilter { + contractAddresses: Array + accountAddresses?: Array + contractStatus?: ContractVerificationStatus +} + +export interface TokenBalancesFilter { + accountAddresses: Array + contractStatus?: ContractVerificationStatus + contractTypes?: Array + contractWhitelist?: Array + contractBlacklist?: Array + omitNativeBalances: boolean + omitPrices?: boolean +} + +export interface TokenHistory { + blockNumber: number + blockHash: string + contractAddress: string + contractType: ContractType + fromAddress: string + toAddress: string + txnHash: string + txnIndex: number + txnLogIndex: number + tokenIDs: string + amounts: string + ts: string +} + +export interface TokenIDRange { + start: string + end: string +} + +export interface TokenMetadata { + chainId?: number + contractAddress?: string + tokenId: string + source: string + name: string + description?: string + image?: string + video?: string + audio?: string + properties?: { [key: string]: any } + attributes: Array<{ [key: string]: any }> + image_data?: string + external_url?: string + background_color?: string + animation_url?: string + decimals?: number + updatedAt?: string + assets?: Array + status: ResourceStatus + queuedAt?: string + lastFetched?: string +} + +export interface TokenPriceQuery { + contractAddress: string + tokenID?: string +} + +export interface TokenSupply { + tokenID: string + supply: string + chainId: number + contractInfo?: ContractInfo + tokenMetadata?: TokenMetadata +} + +export interface Transaction { + txnHash: string + blockNumber: number + blockHash: string + chainId: number + metaTxnID?: string + transfers?: Array + timestamp: string +} + +export interface TransactionFilter { + txnHash?: string + from?: string + to?: string + contractAddress?: string + event?: string +} + +export interface TransactionHistoryFilter { + accountAddress?: string + contractAddress?: string + accountAddresses?: Array + contractAddresses?: Array + transactionHashes?: Array + metaTransactionIDs?: Array + fromBlock?: number + toBlock?: number + tokenID?: string + omitPrices?: boolean +} + +export interface TransactionLog { + contractAddress: string + topics: Array + data: string + index: number +} + +export interface TransactionReceipt { + txnHash: string + txnStatus: TransactionStatus + txnIndex: number + txnType: TransactionType + blockHash: string + blockNumber: number + gasUsed: number + effectiveGasPrice: string + from: string + to: string + logs: Array + final: boolean + reorged: boolean +} + +export enum TransactionStatus { + FAILED = 'FAILED', + SUCCESSFUL = 'SUCCESSFUL' +} + +export enum TransactionType { + LegacyTxnType = 'LegacyTxnType', + AccessListTxnType = 'AccessListTxnType', + DynamicFeeTxnType = 'DynamicFeeTxnType' +} + +export interface TxnInfo { + from: string + to: string + value: string +} + +export interface TxnTransfer { + transferType: TxnTransferType + contractAddress: string + contractType: ContractType + from: string + to: string + tokenIds?: Array + amounts: Array + logIndex: number + amountsUSD?: Array + pricesUSD?: Array + contractInfo?: ContractInfo + tokenMetadata?: { [key: string]: TokenMetadata } +} + +export enum TxnTransferType { + UNKNOWN = 'UNKNOWN', + SEND = 'SEND', + RECEIVE = 'RECEIVE' +} + +export interface Version { + webrpcVersion: string + schemaVersion: string + schemaHash: string + appVersion: string +} + +export interface WALWriterRuntimeStatus { + healthOK: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + chainID: number + percentWALWritten: number +} + +export interface WebhookListener { + id: number + projectID: number + url: string + filters: EventFilter + name: string + updatedAt: string + active: boolean +} + +export interface AddWebhookListenerRequest { + url: string + filters: EventFilter + projectId?: number +} + +export interface AddWebhookListenerResponse { + status: boolean + listener: WebhookListener +} + +export interface FetchTransactionReceiptRequest { + txnHash: string + maxBlockWait?: number +} + +export interface FetchTransactionReceiptResponse { + receipt: TransactionReceipt +} + +export interface FetchTransactionReceiptWithFilterRequest { + filter: TransactionFilter + maxBlockWait?: number +} + +export interface FetchTransactionReceiptWithFilterResponse { + receipt: TransactionReceipt +} + +export interface GetAllWebhookListenersRequest { + projectId?: number +} + +export interface GetAllWebhookListenersResponse { + listeners: Array +} + +export interface GetBalanceUpdatesRequest { + contractAddress: string + lastBlockNumber: number + lastBlockHash?: string + page?: Page +} + +export interface GetBalanceUpdatesResponse { + page: Page + balances: Array +} + +export interface GetChainIDRequest {} + +export interface GetChainIDResponse { + chainID: number +} + +export interface GetEtherBalanceRequest { + accountAddress?: string +} + +export interface GetEtherBalanceResponse { + balance: EtherBalance +} + +export interface GetMarketplaceOrdersRequest { + marketplaceContractAddress: string + collectionAddress: string + filter?: MarketplaceOrderFilter + page?: Page +} + +export interface GetMarketplaceOrdersResponse { + page?: Page + orders: Array +} + +export interface GetMarketplaceTopOrdersRequest { + marketplaceContractAddress: string + collectionAddress: string + filter: MarketplaceTopOrdersFilter +} + +export interface GetMarketplaceTopOrdersResponse { + orders: Array +} + +export interface GetNativeTokenBalanceRequest { + accountAddress?: string + omitPrices?: boolean +} + +export interface GetNativeTokenBalanceResponse { + balance: NativeTokenBalance +} + +export interface GetTokenBalancesRequest { + accountAddress?: string + contractAddress?: string + tokenID?: string + includeMetadata?: boolean + metadataOptions?: MetadataOptions + includeCollectionTokens?: boolean + page?: Page +} + +export interface GetTokenBalancesResponse { + page: Page + balances: Array +} + +export interface GetTokenBalancesByContractRequest { + filter: TokenBalancesByContractFilter + omitMetadata?: boolean + page?: Page +} + +export interface GetTokenBalancesByContractResponse { + page: Page + balances: Array +} + +export interface GetTokenBalancesDetailsRequest { + filter: TokenBalancesFilter + omitMetadata?: boolean + page?: Page +} + +export interface GetTokenBalancesDetailsResponse { + page: Page + nativeBalances: Array + balances: Array +} + +export interface GetTokenBalancesSummaryRequest { + filter: TokenBalancesFilter + omitMetadata?: boolean + page?: Page +} + +export interface GetTokenBalancesSummaryResponse { + page: Page + nativeBalances: Array + balances: Array +} + +export interface GetTokenIDRangesRequest { + contractAddress: string + lastTokenID?: string +} + +export interface GetTokenIDRangesResponse { + contractType: ContractType + tokenIDRanges: Array + moreRanges: boolean +} + +export interface GetTokenIDsRequest { + contractAddress: string + page?: Page +} + +export interface GetTokenIDsResponse { + page: Page + contractType: ContractType + tokenIDs: Array +} + +export interface GetTokenPriceRequest { + q: TokenPriceQuery +} + +export interface GetTokenPriceResponse { + price: Price +} + +export interface GetTokenPricesRequest { + q: Array +} + +export interface GetTokenPricesResponse { + prices: Array +} + +export interface GetTokenSuppliesRequest { + contractAddress: string + includeMetadata?: boolean + page?: Page +} + +export interface GetTokenSuppliesResponse { + page: Page + contractType: ContractType + tokenIDs: Array +} + +export interface GetTokenSuppliesMapRequest { + tokenMap: { [key: string]: Array } + includeMetadata?: boolean +} + +export interface GetTokenSuppliesMapResponse { + supplies: { [key: string]: Array } +} + +export interface GetTransactionHistoryRequest { + filter: TransactionHistoryFilter + page?: Page + includeMetadata?: boolean + metadataOptions?: MetadataOptions +} + +export interface GetTransactionHistoryResponse { + page: Page + transactions: Array +} + +export interface GetWebhookListenerRequest { + id: number + projectId?: number +} + +export interface GetWebhookListenerResponse { + listener: WebhookListener +} + +export interface ListTokenPricesRequest { + page?: Page +} + +export interface ListTokenPricesResponse { + page: Page + prices: Array +} + +export interface PauseAllWebhookListenersRequest { + projectId?: number +} + +export interface PauseAllWebhookListenersResponse { + status: boolean +} + +export interface PingRequest {} + +export interface PingResponse { + status: boolean +} + +export interface RemoveAllWebhookListenersRequest { + projectId?: number +} + +export interface RemoveAllWebhookListenersResponse { + status: boolean +} + +export interface RemoveWebhookListenerRequest { + id: number + projectId?: number +} + +export interface RemoveWebhookListenerResponse { + status: boolean +} + +export interface ResumeAllWebhookListenersRequest { + projectId?: number +} + +export interface ResumeAllWebhookListenersResponse { + status: boolean +} + +export interface RuntimeStatusRequest {} + +export interface RuntimeStatusResponse { + status: RuntimeStatus +} + +export interface SubscribeBalanceUpdatesRequest { + contractAddress: string +} + +export interface SubscribeBalanceUpdatesResponse { + balance: TokenBalance +} + +export interface SubscribeEventsRequest { + filter: EventFilter +} + +export interface SubscribeEventsResponse { + log: EventLog +} + +export interface SubscribeReceiptsRequest { + filter: TransactionFilter +} + +export interface SubscribeReceiptsResponse { + receipt: TransactionReceipt +} + +export interface SyncBalanceRequest { + accountAddress: string + contractAddress: string + tokenID?: string +} + +export interface SyncBalanceResponse {} + +export interface ToggleWebhookListenerRequest { + id: number + projectId?: number +} + +export interface ToggleWebhookListenerResponse { + webhookListener: WebhookListener +} + +export interface UpdateWebhookListenerRequest { + listener: WebhookListener + projectId?: number +} + +export interface UpdateWebhookListenerResponse { + status: boolean +} + +export interface VersionRequest {} + +export interface VersionResponse { + version: Version +} + +// +// Client +// + +export class Indexer implements IndexerClient { + protected hostname: string + protected fetch: Fetch + protected path = '/rpc/Indexer/' + + constructor(hostname: string, fetch: Fetch) { + this.hostname = hostname.replace(/\/*$/, '') + this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) + } + + private url(name: string): string { + return this.hostname + this.path + name + } + + queryKey = { + addWebhookListener: (req: AddWebhookListenerRequest) => ['Indexer', 'addWebhookListener', req] as const, + fetchTransactionReceipt: (req: FetchTransactionReceiptRequest) => ['Indexer', 'fetchTransactionReceipt', req] as const, + fetchTransactionReceiptWithFilter: (req: FetchTransactionReceiptWithFilterRequest) => + ['Indexer', 'fetchTransactionReceiptWithFilter', req] as const, + getAllWebhookListeners: (req: GetAllWebhookListenersRequest) => ['Indexer', 'getAllWebhookListeners', req] as const, + getBalanceUpdates: (req: GetBalanceUpdatesRequest) => ['Indexer', 'getBalanceUpdates', req] as const, + getChainID: () => ['Indexer', 'getChainID'] as const, + getEtherBalance: (req: GetEtherBalanceRequest) => ['Indexer', 'getEtherBalance', req] as const, + getMarketplaceOrders: (req: GetMarketplaceOrdersRequest) => ['Indexer', 'getMarketplaceOrders', req] as const, + getMarketplaceTopOrders: (req: GetMarketplaceTopOrdersRequest) => ['Indexer', 'getMarketplaceTopOrders', req] as const, + getNativeTokenBalance: (req: GetNativeTokenBalanceRequest) => ['Indexer', 'getNativeTokenBalance', req] as const, + getTokenBalances: (req: GetTokenBalancesRequest) => ['Indexer', 'getTokenBalances', req] as const, + getTokenBalancesByContract: (req: GetTokenBalancesByContractRequest) => + ['Indexer', 'getTokenBalancesByContract', req] as const, + getTokenBalancesDetails: (req: GetTokenBalancesDetailsRequest) => ['Indexer', 'getTokenBalancesDetails', req] as const, + getTokenBalancesSummary: (req: GetTokenBalancesSummaryRequest) => ['Indexer', 'getTokenBalancesSummary', req] as const, + getTokenIDRanges: (req: GetTokenIDRangesRequest) => ['Indexer', 'getTokenIDRanges', req] as const, + getTokenIDs: (req: GetTokenIDsRequest) => ['Indexer', 'getTokenIDs', req] as const, + getTokenPrice: (req: GetTokenPriceRequest) => ['Indexer', 'getTokenPrice', req] as const, + getTokenPrices: (req: GetTokenPricesRequest) => ['Indexer', 'getTokenPrices', req] as const, + getTokenSupplies: (req: GetTokenSuppliesRequest) => ['Indexer', 'getTokenSupplies', req] as const, + getTokenSuppliesMap: (req: GetTokenSuppliesMapRequest) => ['Indexer', 'getTokenSuppliesMap', req] as const, + getTransactionHistory: (req: GetTransactionHistoryRequest) => ['Indexer', 'getTransactionHistory', req] as const, + getWebhookListener: (req: GetWebhookListenerRequest) => ['Indexer', 'getWebhookListener', req] as const, + listTokenPrices: (req: ListTokenPricesRequest) => ['Indexer', 'listTokenPrices', req] as const, + pauseAllWebhookListeners: (req: PauseAllWebhookListenersRequest) => ['Indexer', 'pauseAllWebhookListeners', req] as const, + ping: () => ['Indexer', 'ping'] as const, + removeAllWebhookListeners: (req: RemoveAllWebhookListenersRequest) => ['Indexer', 'removeAllWebhookListeners', req] as const, + removeWebhookListener: (req: RemoveWebhookListenerRequest) => ['Indexer', 'removeWebhookListener', req] as const, + resumeAllWebhookListeners: (req: ResumeAllWebhookListenersRequest) => ['Indexer', 'resumeAllWebhookListeners', req] as const, + runtimeStatus: () => ['Indexer', 'runtimeStatus'] as const, + subscribeBalanceUpdates: (req: SubscribeBalanceUpdatesRequest) => ['Indexer', 'subscribeBalanceUpdates', req] as const, + subscribeEvents: (req: SubscribeEventsRequest) => ['Indexer', 'subscribeEvents', req] as const, + subscribeReceipts: (req: SubscribeReceiptsRequest) => ['Indexer', 'subscribeReceipts', req] as const, + syncBalance: (req: SyncBalanceRequest) => ['Indexer', 'syncBalance', req] as const, + toggleWebhookListener: (req: ToggleWebhookListenerRequest) => ['Indexer', 'toggleWebhookListener', req] as const, + updateWebhookListener: (req: UpdateWebhookListenerRequest) => ['Indexer', 'updateWebhookListener', req] as const, + version: () => ['Indexer', 'version'] as const + } + + addWebhookListener = ( + req: AddWebhookListenerRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('AddWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'AddWebhookListenerResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + fetchTransactionReceipt = ( + req: FetchTransactionReceiptRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('FetchTransactionReceipt'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'FetchTransactionReceiptResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + fetchTransactionReceiptWithFilter = ( + req: FetchTransactionReceiptWithFilterRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('FetchTransactionReceiptWithFilter'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'FetchTransactionReceiptWithFilterResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getAllWebhookListeners = ( + req: GetAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetAllWebhookListenersResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getBalanceUpdates = ( + req: GetBalanceUpdatesRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetBalanceUpdates'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetBalanceUpdatesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getChainID = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetChainID'), createHttpRequest('{}', headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetChainIDResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getEtherBalance = (req: GetEtherBalanceRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetEtherBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetEtherBalanceResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getMarketplaceOrders = ( + req: GetMarketplaceOrdersRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetMarketplaceOrders'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetMarketplaceOrdersResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getMarketplaceTopOrders = ( + req: GetMarketplaceTopOrdersRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetMarketplaceTopOrders'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetMarketplaceTopOrdersResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getNativeTokenBalance = ( + req: GetNativeTokenBalanceRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetNativeTokenBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetNativeTokenBalanceResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenBalances = ( + req: GetTokenBalancesRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenBalances'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenBalancesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenBalancesByContract = ( + req: GetTokenBalancesByContractRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenBalancesByContract'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenBalancesByContractResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenBalancesDetails = ( + req: GetTokenBalancesDetailsRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenBalancesDetails'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenBalancesDetailsResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenBalancesSummary = ( + req: GetTokenBalancesSummaryRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenBalancesSummary'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenBalancesSummaryResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenIDRanges = ( + req: GetTokenIDRangesRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenIDRanges'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenIDRangesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenIDs = (req: GetTokenIDsRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetTokenIDs'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenIDsResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenPrice = (req: GetTokenPriceRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetTokenPrice'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenPriceResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenPrices = (req: GetTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenPricesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenSupplies = ( + req: GetTokenSuppliesRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenSupplies'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenSuppliesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenSuppliesMap = ( + req: GetTokenSuppliesMapRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenSuppliesMap'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenSuppliesMapResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTransactionHistory = ( + req: GetTransactionHistoryRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTransactionHistory'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTransactionHistoryResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getWebhookListener = ( + req: GetWebhookListenerRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetWebhookListenerResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + listTokenPrices = (req: ListTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('ListTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'ListTokenPricesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + pauseAllWebhookListeners = ( + req: PauseAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('PauseAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'PauseAllWebhookListenersResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + ping = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'PingResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + removeAllWebhookListeners = ( + req: RemoveAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('RemoveAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'RemoveAllWebhookListenersResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + removeWebhookListener = ( + req: RemoveWebhookListenerRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('RemoveWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'RemoveWebhookListenerResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + resumeAllWebhookListeners = ( + req: ResumeAllWebhookListenersRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('ResumeAllWebhookListeners'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'ResumeAllWebhookListenersResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'RuntimeStatusResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + subscribeBalanceUpdates = ( + req: SubscribeBalanceUpdatesRequest, + options: WebrpcStreamOptions + ): WebrpcStreamController => { + const abortController = new AbortController() + const abortSignal = abortController.signal + + if (options.signal) { + abortSignal.addEventListener('abort', () => abortController.abort(options.signal?.reason), { + signal: options.signal + }) + } + + const _fetch = () => + this.fetch(this.url('SubscribeBalanceUpdates'), createHttpRequest(JsonEncode(req), options.headers, abortSignal)).then( + async res => { + await sseResponse(res, options, _fetch) + }, + error => { + options.onError(error, _fetch) + } + ) + + const resp = _fetch() + return { + abort: abortController.abort.bind(abortController), + closed: resp + } + } + subscribeEvents = ( + req: SubscribeEventsRequest, + options: WebrpcStreamOptions + ): WebrpcStreamController => { + const abortController = new AbortController() + const abortSignal = abortController.signal + + if (options.signal) { + abortSignal.addEventListener('abort', () => abortController.abort(options.signal?.reason), { + signal: options.signal + }) + } + + const _fetch = () => + this.fetch(this.url('SubscribeEvents'), createHttpRequest(JsonEncode(req), options.headers, abortSignal)).then( + async res => { + await sseResponse(res, options, _fetch) + }, + error => { + options.onError(error, _fetch) + } + ) + + const resp = _fetch() + return { + abort: abortController.abort.bind(abortController), + closed: resp + } + } + subscribeReceipts = ( + req: SubscribeReceiptsRequest, + options: WebrpcStreamOptions + ): WebrpcStreamController => { + const abortController = new AbortController() + const abortSignal = abortController.signal + + if (options.signal) { + abortSignal.addEventListener('abort', () => abortController.abort(options.signal?.reason), { + signal: options.signal + }) + } + + const _fetch = () => + this.fetch(this.url('SubscribeReceipts'), createHttpRequest(JsonEncode(req), options.headers, abortSignal)).then( + async res => { + await sseResponse(res, options, _fetch) + }, + error => { + options.onError(error, _fetch) + } + ) + + const resp = _fetch() + return { + abort: abortController.abort.bind(abortController), + closed: resp + } + } + syncBalance = (req: SyncBalanceRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('SyncBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'SyncBalanceResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + toggleWebhookListener = ( + req: ToggleWebhookListenerRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('ToggleWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'ToggleWebhookListenerResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + updateWebhookListener = ( + req: UpdateWebhookListenerRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('UpdateWebhookListener'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'UpdateWebhookListenerResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + version = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'VersionResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } +} + +const sseResponse = async (res: Response, options: WebrpcStreamOptions, retryFetch: () => Promise) => { + const { onMessage, onOpen, onClose, onError } = options + + if (!res.ok) { + try { + await buildResponse(res) + } catch (error) { + // @ts-ignore + onError(error, retryFetch) + } + return + } + + if (!res.body) { + onError( + WebrpcBadResponseError.new({ + status: res.status, + cause: 'Invalid response, missing body' + }), + retryFetch + ) + return + } + + onOpen && onOpen() + + const reader = res.body.getReader() + const decoder = new TextDecoder() + let buffer = '' + let lastReadTime = Date.now() + const timeout = (10 + 1) * 1000 + let timeoutError = false + const intervalId = setInterval(() => { + if (Date.now() - lastReadTime > timeout) { + timeoutError = true + clearInterval(intervalId) + reader.releaseLock() + } + }, timeout) + + while (true) { + let value + let done + try { + ;({ value, done } = await reader.read()) + if (timeoutError) throw new Error('Timeout, no data or heartbeat received') + lastReadTime = Date.now() + buffer += decoder.decode(value, { stream: true }) + } catch (error) { + if (error instanceof DOMException && error.name === 'AbortError') { + onError( + WebrpcClientAbortedError.new({ + message: 'AbortError', + cause: `AbortError: ${error instanceof Error ? error.message : String(error)}` + }), + () => { + throw new Error('Abort signal cannot be used to reconnect') + } + ) + } else { + onError( + WebrpcStreamLostError.new({ + cause: `reader.read(): ${error instanceof Error ? error.message : String(error)}` + }), + retryFetch + ) + } + return + } + + let lines = buffer.split('\n') + for (let i = 0; i < lines.length - 1; i++) { + const line = lines[i] + if (line?.length === 0) { + continue + } + let data: any + try { + data = JSON.parse(line) + if (data.hasOwnProperty('webrpcError')) { + const error = data.webrpcError + const code: number = typeof error.code === 'number' ? error.code : 0 + onError((webrpcErrorByCode[code] || WebrpcError).new(error), retryFetch) + return + } + } catch (error) { + if (error instanceof Error && error.message === 'Abort signal cannot be used to reconnect') { + throw error + } + onError( + WebrpcBadResponseError.new({ + status: res.status, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}` + }), + retryFetch + ) + } + onMessage(data) + } + + if (!done) { + const lastLine = lines[lines.length - 1] + buffer = lastLine || '' + continue + } + + onClose && onClose() + return + } +} + +const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { + const reqHeaders: { [key: string]: string } = { + ...headers, + 'Content-Type': 'application/json', + [WebrpcHeader]: WebrpcHeaderValue + } + return { method: 'POST', headers: reqHeaders, body, signal } +} + +const buildResponse = (res: Response): Promise => { + return res.text().then(text => { + let data + try { + data = JSON.parse(text) + } catch (error) { + throw WebrpcBadResponseError.new({ + status: res.status, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}` + }) + } + if (!res.ok) { + const code: number = typeof data.code === 'number' ? data.code : 0 + throw (webrpcErrorByCode[code] || WebrpcError).new(data) + } + return data + }) +} + +export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise + +export interface WebrpcStreamOptions extends WebrpcOptions { + onMessage: (message: T) => void + onError: (error: WebrpcError, reconnect: () => void) => void + onOpen?: () => void + onClose?: () => void +} + +export interface WebrpcOptions { + headers?: HeadersInit + signal?: AbortSignal +} + +export interface WebrpcStreamController { + abort: (reason?: any) => void + closed: Promise +} + +export const JsonEncode = (obj: T): string => { + return JSON.stringify(obj) +} + +export const JsonDecode = (data: string | any, _typ: string = ''): T => { + let parsed: any = data + if (typeof data === 'string') { + try { + parsed = JSON.parse(data) + } catch (err) { + throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) + } + } + return parsed as T +} + +// +// Errors +// + +type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } + +export class WebrpcError extends Error { + code: number + status: number + + constructor(error: WebrpcErrorParams = {}) { + super(error.message) + this.name = error.name || 'WebrpcEndpointError' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcError.prototype) + } + + static new(payload: any): WebrpcError { + return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) + } +} + +export class WebrpcEndpointError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcEndpoint' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcEndpointError.prototype) + } +} + +export class WebrpcRequestFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcRequestFailed' + this.code = typeof error.code === 'number' ? error.code : -1 + this.message = error.message || `request failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) + } +} + +export class WebrpcBadRouteError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRoute' + this.code = typeof error.code === 'number' ? error.code : -2 + this.message = error.message || `bad route` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) + } +} + +export class WebrpcBadMethodError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadMethod' + this.code = typeof error.code === 'number' ? error.code : -3 + this.message = error.message || `bad method` + this.status = typeof error.status === 'number' ? error.status : 405 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) + } +} + +export class WebrpcBadRequestError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRequest' + this.code = typeof error.code === 'number' ? error.code : -4 + this.message = error.message || `bad request` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) + } +} + +export class WebrpcBadResponseError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadResponse' + this.code = typeof error.code === 'number' ? error.code : -5 + this.message = error.message || `bad response` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) + } +} + +export class WebrpcServerPanicError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcServerPanic' + this.code = typeof error.code === 'number' ? error.code : -6 + this.message = error.message || `server panic` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) + } +} + +export class WebrpcInternalErrorError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcInternalError' + this.code = typeof error.code === 'number' ? error.code : -7 + this.message = error.message || `internal error` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) + } +} + +export class WebrpcClientAbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcClientAborted' + this.code = typeof error.code === 'number' ? error.code : -8 + this.message = error.message || `request aborted by client` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) + } +} + +export class WebrpcStreamLostError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamLost' + this.code = typeof error.code === 'number' ? error.code : -9 + this.message = error.message || `stream lost` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) + } +} + +export class WebrpcStreamFinishedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamFinished' + this.code = typeof error.code === 'number' ? error.code : -10 + this.message = error.message || `stream finished` + this.status = typeof error.status === 'number' ? error.status : 200 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) + } +} + +// +// Schema errors +// + +export class AbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Aborted' + this.code = typeof error.code === 'number' ? error.code : 1005 + this.message = error.message || `Request aborted` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AbortedError.prototype) + } +} + +export class AccessKeyMismatchError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AccessKeyMismatch' + this.code = typeof error.code === 'number' ? error.code : 1102 + this.message = error.message || `Access key mismatch` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) + } +} + +export class AccessKeyNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AccessKeyNotFound' + this.code = typeof error.code === 'number' ? error.code : 1101 + this.message = error.message || `Access key not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) + } +} + +export class AtLeastOneKeyError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AtLeastOneKey' + this.code = typeof error.code === 'number' ? error.code : 1302 + this.message = error.message || `You need at least one Access Key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) + } +} + +export class GeoblockedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Geoblocked' + this.code = typeof error.code === 'number' ? error.code : 1006 + this.message = error.message || `Geoblocked region` + this.status = typeof error.status === 'number' ? error.status : 451 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, GeoblockedError.prototype) + } +} + +export class InvalidArgumentError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidArgument' + this.code = typeof error.code === 'number' ? error.code : 2001 + this.message = error.message || `Invalid argument` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidArgumentError.prototype) + } +} + +export class InvalidOriginError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidOrigin' + this.code = typeof error.code === 'number' ? error.code : 1103 + this.message = error.message || `Invalid origin for Access Key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidOriginError.prototype) + } +} + +export class InvalidServiceError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidService' + this.code = typeof error.code === 'number' ? error.code : 1104 + this.message = error.message || `Service not enabled for Access key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidServiceError.prototype) + } +} + +export class MaxAccessKeysError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MaxAccessKeys' + this.code = typeof error.code === 'number' ? error.code : 1301 + this.message = error.message || `Access keys limit reached` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MaxAccessKeysError.prototype) + } +} + +export class MetadataCallFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MetadataCallFailed' + this.code = typeof error.code === 'number' ? error.code : 3003 + this.message = error.message || `Metadata service call failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MetadataCallFailedError.prototype) + } +} + +export class MethodNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MethodNotFound' + this.code = typeof error.code === 'number' ? error.code : 1003 + this.message = error.message || `Method not found` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MethodNotFoundError.prototype) + } +} + +export class NoDefaultKeyError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NoDefaultKey' + this.code = typeof error.code === 'number' ? error.code : 1300 + this.message = error.message || `No default access key found` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, NoDefaultKeyError.prototype) + } +} + +export class NotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NotFound' + this.code = typeof error.code === 'number' ? error.code : 3000 + this.message = error.message || `Resource not found` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, NotFoundError.prototype) + } +} + +export class PermissionDeniedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'PermissionDenied' + this.code = typeof error.code === 'number' ? error.code : 1001 + this.message = error.message || `Permission denied` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, PermissionDeniedError.prototype) + } +} + +export class ProjectNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ProjectNotFound' + this.code = typeof error.code === 'number' ? error.code : 1100 + this.message = error.message || `Project not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, ProjectNotFoundError.prototype) + } +} + +export class QueryFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QueryFailed' + this.code = typeof error.code === 'number' ? error.code : 2003 + this.message = error.message || `Query failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, QueryFailedError.prototype) + } +} + +export class QuotaExceededError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QuotaExceeded' + this.code = typeof error.code === 'number' ? error.code : 1200 + this.message = error.message || `Quota exceeded` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, QuotaExceededError.prototype) + } +} + +export class RateLimitError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RateLimit' + this.code = typeof error.code === 'number' ? error.code : 1201 + this.message = error.message || `Rate limit exceeded` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RateLimitError.prototype) + } +} + +export class RateLimitedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RateLimited' + this.code = typeof error.code === 'number' ? error.code : 1007 + this.message = error.message || `Rate-limited. Please slow down.` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RateLimitedError.prototype) + } +} + +export class RequestConflictError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RequestConflict' + this.code = typeof error.code === 'number' ? error.code : 1004 + this.message = error.message || `Conflict with target resource` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RequestConflictError.prototype) + } +} + +export class ResourceExhaustedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ResourceExhausted' + this.code = typeof error.code === 'number' ? error.code : 2004 + this.message = error.message || `Resource exhausted` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, ResourceExhaustedError.prototype) + } +} + +export class SessionExpiredError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'SessionExpired' + this.code = typeof error.code === 'number' ? error.code : 1002 + this.message = error.message || `Session expired` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, SessionExpiredError.prototype) + } +} + +export class TimeoutError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Timeout' + this.code = typeof error.code === 'number' ? error.code : 1900 + this.message = error.message || `Request timed out` + this.status = typeof error.status === 'number' ? error.status : 408 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, TimeoutError.prototype) + } +} + +export class UnauthorizedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unauthorized' + this.code = typeof error.code === 'number' ? error.code : 1000 + this.message = error.message || `Unauthorized access` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnauthorizedError.prototype) + } +} + +export class UnauthorizedUserError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'UnauthorizedUser' + this.code = typeof error.code === 'number' ? error.code : 1105 + this.message = error.message || `Unauthorized user` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnauthorizedUserError.prototype) + } +} + +export class UnavailableError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unavailable' + this.code = typeof error.code === 'number' ? error.code : 2002 + this.message = error.message || `Unavailable resource` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnavailableError.prototype) + } +} + +export enum errors { + WebrpcEndpoint = 'WebrpcEndpoint', + WebrpcRequestFailed = 'WebrpcRequestFailed', + WebrpcBadRoute = 'WebrpcBadRoute', + WebrpcBadMethod = 'WebrpcBadMethod', + WebrpcBadRequest = 'WebrpcBadRequest', + WebrpcBadResponse = 'WebrpcBadResponse', + WebrpcServerPanic = 'WebrpcServerPanic', + WebrpcInternalError = 'WebrpcInternalError', + WebrpcClientAborted = 'WebrpcClientAborted', + WebrpcStreamLost = 'WebrpcStreamLost', + WebrpcStreamFinished = 'WebrpcStreamFinished', + Aborted = 'Aborted', + AccessKeyMismatch = 'AccessKeyMismatch', + AccessKeyNotFound = 'AccessKeyNotFound', + AtLeastOneKey = 'AtLeastOneKey', + Geoblocked = 'Geoblocked', + InvalidArgument = 'InvalidArgument', + InvalidOrigin = 'InvalidOrigin', + InvalidService = 'InvalidService', + MaxAccessKeys = 'MaxAccessKeys', + MetadataCallFailed = 'MetadataCallFailed', + MethodNotFound = 'MethodNotFound', + NoDefaultKey = 'NoDefaultKey', + NotFound = 'NotFound', + PermissionDenied = 'PermissionDenied', + ProjectNotFound = 'ProjectNotFound', + QueryFailed = 'QueryFailed', + QuotaExceeded = 'QuotaExceeded', + RateLimit = 'RateLimit', + RateLimited = 'RateLimited', + RequestConflict = 'RequestConflict', + ResourceExhausted = 'ResourceExhausted', + SessionExpired = 'SessionExpired', + Timeout = 'Timeout', + Unauthorized = 'Unauthorized', + UnauthorizedUser = 'UnauthorizedUser', + Unavailable = 'Unavailable' +} + +export enum WebrpcErrorCodes { + WebrpcEndpoint = 0, + WebrpcRequestFailed = -1, + WebrpcBadRoute = -2, + WebrpcBadMethod = -3, + WebrpcBadRequest = -4, + WebrpcBadResponse = -5, + WebrpcServerPanic = -6, + WebrpcInternalError = -7, + WebrpcClientAborted = -8, + WebrpcStreamLost = -9, + WebrpcStreamFinished = -10, + Aborted = 1005, + AccessKeyMismatch = 1102, + AccessKeyNotFound = 1101, + AtLeastOneKey = 1302, + Geoblocked = 1006, + InvalidArgument = 2001, + InvalidOrigin = 1103, + InvalidService = 1104, + MaxAccessKeys = 1301, + MetadataCallFailed = 3003, + MethodNotFound = 1003, + NoDefaultKey = 1300, + NotFound = 3000, + PermissionDenied = 1001, + ProjectNotFound = 1100, + QueryFailed = 2003, + QuotaExceeded = 1200, + RateLimit = 1201, + RateLimited = 1007, + RequestConflict = 1004, + ResourceExhausted = 2004, + SessionExpired = 1002, + Timeout = 1900, + Unauthorized = 1000, + UnauthorizedUser = 1105, + Unavailable = 2002 +} + +export const webrpcErrorByCode: { [code: number]: any } = { + [0]: WebrpcEndpointError, + [-1]: WebrpcRequestFailedError, + [-2]: WebrpcBadRouteError, + [-3]: WebrpcBadMethodError, + [-4]: WebrpcBadRequestError, + [-5]: WebrpcBadResponseError, + [-6]: WebrpcServerPanicError, + [-7]: WebrpcInternalErrorError, + [-8]: WebrpcClientAbortedError, + [-9]: WebrpcStreamLostError, + [-10]: WebrpcStreamFinishedError, + [1005]: AbortedError, + [1102]: AccessKeyMismatchError, + [1101]: AccessKeyNotFoundError, + [1302]: AtLeastOneKeyError, + [1006]: GeoblockedError, + [2001]: InvalidArgumentError, + [1103]: InvalidOriginError, + [1104]: InvalidServiceError, + [1301]: MaxAccessKeysError, + [3003]: MetadataCallFailedError, + [1003]: MethodNotFoundError, + [1300]: NoDefaultKeyError, + [3000]: NotFoundError, + [1001]: PermissionDeniedError, + [1100]: ProjectNotFoundError, + [2003]: QueryFailedError, + [1200]: QuotaExceededError, + [1201]: RateLimitError, + [1007]: RateLimitedError, + [1004]: RequestConflictError, + [2004]: ResourceExhaustedError, + [1002]: SessionExpiredError, + [1900]: TimeoutError, + [1000]: UnauthorizedError, + [1105]: UnauthorizedUserError, + [2002]: UnavailableError +} + +// +// Webrpc +// + +export const WebrpcHeader = 'Webrpc' + +export const WebrpcHeaderValue = 'webrpc@v0.31.2;gen-typescript@v0.23.1;sequence-indexer@v0.4.0' + +type WebrpcGenVersions = { + WebrpcGenVersion: string + codeGenName: string + codeGenVersion: string + schemaName: string + schemaVersion: string +} + +export function VersionFromHeader(headers: Headers): WebrpcGenVersions { + const headerValue = headers.get(WebrpcHeader) + if (!headerValue) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '' + } + } + + return parseWebrpcGenVersions(headerValue) +} + +function parseWebrpcGenVersions(header: string): WebrpcGenVersions { + const versions = header.split(';') + if (versions.length < 3) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '' + } + } + + const [_, WebrpcGenVersion] = versions[0]!.split('@') + const [codeGenName, codeGenVersion] = versions[1]!.split('@') + const [schemaName, schemaVersion] = versions[2]!.split('@') + + return { + WebrpcGenVersion: WebrpcGenVersion ?? '', + codeGenName: codeGenName ?? '', + codeGenVersion: codeGenVersion ?? '', + schemaName: schemaName ?? '', + schemaVersion: schemaVersion ?? '' + } +} diff --git a/packages/indexer/src/indexergw.gen.ts b/packages/indexer/src/indexergw.gen.ts new file mode 100644 index 0000000000..ce59f412a8 --- /dev/null +++ b/packages/indexer/src/indexergw.gen.ts @@ -0,0 +1,1785 @@ +/* eslint-disable */ +// sequence-indexer v0.4.0 3309c3d48de1790b7d12c5769c1e6bc59eb9831c +// -- +// Code generated by Webrpc-gen@v0.31.2 with typescript generator. DO NOT EDIT. +// +// webrpc-gen -schema=merged.gen.json -service=IndexerGateway -target=typescript -client -out=./clients/indexergw.gen.ts + +// Webrpc description and code-gen version +export const WebrpcVersion = 'v1' + +// Schema version of your RIDL schema +export const WebrpcSchemaVersion = 'v0.4.0' + +// Schema hash generated from your RIDL schema +export const WebrpcSchemaHash = '3309c3d48de1790b7d12c5769c1e6bc59eb9831c' + +// +// Client interface +// + +export interface IndexerGatewayClient { + /** + * GetTokenBalances returns a balance summary/details for an specific account + * on all indexer nodes. By default if accountAddress is left empty, it will + * use the account from the jwt session. + */ + getBalanceUpdates(req: GetBalanceUpdatesRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetChains returns a list of chains with their ID and name + */ + getChains(req: GetChainsRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetNativeTokenBalance queries indexer nodes for the latest native token + * account balance. + */ + getNativeTokenBalance( + req: GetNativeTokenBalanceRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetTokenBalances returns a balance summary/details for a specific account + * on all indexer nodes. By default if accountAddress is left empty, it will + * use the account from the jwt session. + * + * @deprecated Use GetTokenBalancesSummary or GetTokenBalancesDetails instead. + */ + getTokenBalances(req: GetTokenBalancesRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetTokenBalancesByContract returns a balances for specific accounts and + * contracts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + * represented as individual balances. + */ + getTokenBalancesByContract( + req: GetTokenBalancesByContractRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetTokenBalancesDetails returns a detailed balance summary for the given + * accounts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + * represented as individual balances. + */ + getTokenBalancesDetails( + req: GetTokenBalancesDetailsRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * GetTokenBalancesSummary returns a summary of token balances for the given + * accounts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + * represented as a single aggregated balance. + */ + getTokenBalancesSummary( + req: GetTokenBalancesSummaryRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + getTokenPrice(req: GetTokenPriceRequest, headers?: object, signal?: AbortSignal): Promise + + getTokenPrices(req: GetTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise + + /** + * GetTransactionHistory returns the history of mined transactions for the + * given account on all indexer nodes, which includes a list of token transfer + * (sent/received) , and sent transactions from a Sequence wallet. + */ + getTransactionHistory( + req: GetTransactionHistoryRequest, + headers?: object, + signal?: AbortSignal + ): Promise + + /** + * Ping the indexer + */ + ping(headers?: object, signal?: AbortSignal): Promise + + /** + * Get the current runtime health status of the indexer gatewya + */ + runtimeStatus(headers?: object, signal?: AbortSignal): Promise + + /** + * Get the current version of the indexer + */ + version(headers?: object, signal?: AbortSignal): Promise +} + +// +// Schema types +// + +export interface Asset { + id: number + collectionId: number + tokenId?: string + url?: string + metadataField: string + name?: string + filesize?: number + mimeType?: string + width?: number + height?: number + updatedAt?: string +} + +export interface BloomStats { + hitRatio: string + falsePositivesPercent: string + hitCount: number + missCount: number + falsePositives: number +} + +export interface BloomStatus { + enabled: boolean + initialized: boolean + bloomInitElapsedTime: string + stats: BloomStats +} + +export interface Bond { + pebble: PebbleMetrics + estimatedDiskUsagePerTable: any + estimatedDiskUsageTotal: string +} + +export interface ChainInfo { + chainId: number + chainName: string +} + +export interface ContractInfo { + chainId: number + address: string + source: string + name: string + type: string + symbol: string + decimals?: number + logoURI: string + deployed: boolean + bytecodeHash: string + extensions: ContractInfoExtensions + updatedAt: string + queuedAt?: string + status: ResourceStatus +} + +export interface ContractInfoExtensionBridgeInfo { + tokenAddress: string +} + +export interface ContractInfoExtensionIndexingInfo { + useOnChainBalance: boolean +} + +export interface ContractInfoExtensions { + link?: string + description?: string + categories?: Array + bridgeInfo?: { [key: string]: ContractInfoExtensionBridgeInfo } + indexingInfo?: ContractInfoExtensionIndexingInfo + ogImage?: string + ogName?: string + originChainId?: number + originAddress?: string + blacklist?: boolean + verified?: boolean + verifiedBy?: string + featured?: boolean + featureIndex?: number +} + +export enum ContractType { + UNKNOWN = 'UNKNOWN', + NATIVE = 'NATIVE', + ERC20 = 'ERC20', + ERC721 = 'ERC721', + ERC1155 = 'ERC1155', + SEQUENCE_WALLET = 'SEQUENCE_WALLET', + ERC20_BRIDGE = 'ERC20_BRIDGE', + ERC721_BRIDGE = 'ERC721_BRIDGE', + ERC1155_BRIDGE = 'ERC1155_BRIDGE', + SEQ_MARKETPLACE = 'SEQ_MARKETPLACE', + ERC6909 = 'ERC6909' +} + +export enum ContractVerificationStatus { + VERIFIED = 'VERIFIED', + UNVERIFIED = 'UNVERIFIED', + ALL = 'ALL' +} + +export interface DiskUsage { + humanReadable: string + used: number + size: number + percent: number + dirs: { [key: string]: string } +} + +export interface EtherBalance { + accountAddress: string + balanceWei: string +} + +export interface EventDecoded { + topicHash: string + eventSig: string + types: Array + names: Array + values: Array +} + +export interface EventFilter { + events?: Array + contractAddresses?: Array + accounts?: Array + tokenIDs?: Array +} + +export interface EventLog { + id: number + uid: string + type: EventLogType + blockNumber: number + blockHash: string + parentBlockHash: string + contractAddress: string + contractType: ContractType + txnHash: string + txnIndex: number + txnLogIndex: number + logDataType: EventLogDataType + ts: string + txnInfo?: TxnInfo + rawLog?: { [key: string]: any } + event?: EventDecoded +} + +export enum EventLogDataType { + EVENT = 'EVENT', + TOKEN_TRANSFER = 'TOKEN_TRANSFER', + NATIVE_TOKEN_TRANSFER = 'NATIVE_TOKEN_TRANSFER', + SEQUENCE_TXN = 'SEQUENCE_TXN' +} + +export enum EventLogType { + UNKNOWN = 'UNKNOWN', + BLOCK_ADDED = 'BLOCK_ADDED', + BLOCK_REMOVED = 'BLOCK_REMOVED' +} + +export interface GatewayBackendResponseTime { + percentiles: { [key: string]: number } + average: number +} + +export interface GatewayBackendRuntimeStatus { + name: string + chainId: number + responseTime: GatewayBackendResponseTime +} + +export interface GatewayEtherBalance { + chainId: number + errorReason?: string + result: EtherBalance +} + +export interface GatewayNativeTokenBalance { + chainId: number + errorReason?: string + result: NativeTokenBalance +} + +export interface GatewayNativeTokenBalances { + chainId: number + errorReason?: string + results: Array +} + +export interface GatewayPrice { + chainID: number + errorReason?: string + results: Array +} + +export interface GatewayRuntimeStatus { + healthOK: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + backends: Array +} + +export interface GatewayTokenBalance { + chainId: number + errorReason?: string + results: Array +} + +export interface GatewayTokenPriceQuery { + chainID: number + queries: Array +} + +export interface GatewayTransaction { + chainId: number + errorReason?: string + results: Array +} + +export interface IndexState { + chainId: string + lastBlockNum: number + lastBlockHash: string +} + +export interface IndexedBlock { + blockNumber: number + blockShortHash: string +} + +export interface IndexerMetrics { + blocksPerSecond: number + eventsPerSecond: number +} + +export interface MarketplaceOrder { + orderId: string + tokenContract: string + tokenId: string + isListing: boolean + quantity: string + quantityRemaining: string + currencyAddress: string + pricePerToken: string + expiry: string + orderStatus: OrderStatus + createdBy: string + blockNumber: number + orderbookContractAddress: string + createdAt: number +} + +export interface MarketplaceOrderFilter { + isListing?: boolean + userAddresses?: Array + currencyAddresses: Array + orderIds: Array + tokenIds: Array + excludeUserAddresses?: Array + blockNumberGt: number + createdAtAfter: number + orderStatuses: Array + returnExpired: boolean +} + +export interface MarketplaceTopOrdersFilter { + currencyAddresses: Array + tokenIds: Array + isListing: boolean + priceSort: SortOrder + excludeUser?: string +} + +export interface MetadataOptions { + verifiedOnly?: boolean + unverifiedOnly?: boolean + includeContracts?: Array +} + +export interface NativeTokenBalance { + accountAddress: string + chainId: number + name: string + symbol: string + balance: string + balanceUSD: string + priceUSD: string + priceUpdatedAt?: string + errorReason?: string +} + +export enum NetworkType { + MAINNETS = 'MAINNETS', + TESTNETS = 'TESTNETS', + ALL = 'ALL' +} + +export enum OrderStatus { + OPEN = 'OPEN', + CLOSED = 'CLOSED', + CANCELLED = 'CANCELLED' +} + +export interface Page { + page?: number + column?: string + before?: any + after?: any + sort?: Array + pageSize?: number + more?: boolean +} + +export interface PebbleMetrics { + compactionCount: number + compactionEstimatedDebt: number + compactionInProgressBytes: number + compactionNumInProgress: number + compactionMarkedFiles: number +} + +export interface Price { + contractAddress: string + tokenID?: string + priceUSD: string + updatedAt?: string +} + +export enum ResourceStatus { + NOT_AVAILABLE = 'NOT_AVAILABLE', + REFRESHING = 'REFRESHING', + AVAILABLE = 'AVAILABLE' +} + +export interface RuntimeChecks { + running: boolean + runnables: any + cgoEnabled: boolean + quotaControlEnabled: boolean + syncMode: string + percentIndexed: number + lastBlockNum: number + lastBlockNumWithState: number + bloomStatus: BloomStatus + bond: Bond + diskUsage: DiskUsage + metrics: IndexerMetrics +} + +export interface RuntimeStatus { + healthOK: boolean + indexerEnabled: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + chainID: number + checks: RuntimeChecks +} + +export interface SortBy { + column: string + order: SortOrder +} + +export enum SortOrder { + DESC = 'DESC', + ASC = 'ASC' +} + +export interface TokenBalance { + contractType: ContractType + contractAddress: string + accountAddress: string + tokenID?: string + balance: string + balanceUSD: string + priceUSD: string + priceUpdatedAt?: string + blockHash: string + blockNumber: number + chainId: number + uniqueCollectibles: string + isSummary: boolean + contractInfo?: ContractInfo + tokenMetadata?: TokenMetadata +} + +export interface TokenBalanceFilter { + contractAddress: string + sinceBlockNumber: number +} + +export interface TokenBalancesByContractFilter { + contractAddresses: Array + accountAddresses?: Array + contractStatus?: ContractVerificationStatus +} + +export interface TokenBalancesFilter { + accountAddresses: Array + contractStatus?: ContractVerificationStatus + contractTypes?: Array + contractWhitelist?: Array + contractBlacklist?: Array + omitNativeBalances: boolean + omitPrices?: boolean +} + +export interface TokenHistory { + blockNumber: number + blockHash: string + contractAddress: string + contractType: ContractType + fromAddress: string + toAddress: string + txnHash: string + txnIndex: number + txnLogIndex: number + tokenIDs: string + amounts: string + ts: string +} + +export interface TokenIDRange { + start: string + end: string +} + +export interface TokenMetadata { + chainId?: number + contractAddress?: string + tokenId: string + source: string + name: string + description?: string + image?: string + video?: string + audio?: string + properties?: { [key: string]: any } + attributes: Array<{ [key: string]: any }> + image_data?: string + external_url?: string + background_color?: string + animation_url?: string + decimals?: number + updatedAt?: string + assets?: Array + status: ResourceStatus + queuedAt?: string + lastFetched?: string +} + +export interface TokenPriceQuery { + contractAddress: string + tokenID?: string +} + +export interface TokenSupply { + tokenID: string + supply: string + chainId: number + contractInfo?: ContractInfo + tokenMetadata?: TokenMetadata +} + +export interface Transaction { + txnHash: string + blockNumber: number + blockHash: string + chainId: number + metaTxnID?: string + transfers?: Array + timestamp: string +} + +export interface TransactionFilter { + txnHash?: string + from?: string + to?: string + contractAddress?: string + event?: string +} + +export interface TransactionHistoryFilter { + accountAddress?: string + contractAddress?: string + accountAddresses?: Array + contractAddresses?: Array + transactionHashes?: Array + metaTransactionIDs?: Array + fromBlock?: number + toBlock?: number + tokenID?: string + omitPrices?: boolean +} + +export interface TransactionLog { + contractAddress: string + topics: Array + data: string + index: number +} + +export interface TransactionReceipt { + txnHash: string + txnStatus: TransactionStatus + txnIndex: number + txnType: TransactionType + blockHash: string + blockNumber: number + gasUsed: number + effectiveGasPrice: string + from: string + to: string + logs: Array + final: boolean + reorged: boolean +} + +export enum TransactionStatus { + FAILED = 'FAILED', + SUCCESSFUL = 'SUCCESSFUL' +} + +export enum TransactionType { + LegacyTxnType = 'LegacyTxnType', + AccessListTxnType = 'AccessListTxnType', + DynamicFeeTxnType = 'DynamicFeeTxnType' +} + +export interface TxnInfo { + from: string + to: string + value: string +} + +export interface TxnTransfer { + transferType: TxnTransferType + contractAddress: string + contractType: ContractType + from: string + to: string + tokenIds?: Array + amounts: Array + logIndex: number + amountsUSD?: Array + pricesUSD?: Array + contractInfo?: ContractInfo + tokenMetadata?: { [key: string]: TokenMetadata } +} + +export enum TxnTransferType { + UNKNOWN = 'UNKNOWN', + SEND = 'SEND', + RECEIVE = 'RECEIVE' +} + +export interface Version { + webrpcVersion: string + schemaVersion: string + schemaHash: string + appVersion: string +} + +export interface WALWriterRuntimeStatus { + healthOK: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string + chainID: number + percentWALWritten: number +} + +export interface WebhookListener { + id: number + projectID: number + url: string + filters: EventFilter + name: string + updatedAt: string + active: boolean +} + +export interface GetBalanceUpdatesRequest { + chainIds?: Array + networks?: Array + networkType?: NetworkType + contractAddress: string + lastBlockNumber: number + lastBlockHash?: string + page?: Page +} + +export interface GetBalanceUpdatesResponse { + page: Page + balances: Array +} + +export interface GetChainsRequest { + networkType?: NetworkType +} + +export interface GetChainsResponse { + chains: Array +} + +export interface GetNativeTokenBalanceRequest { + chainIds?: Array + networks?: Array + networkType?: NetworkType + accountAddress?: string + omitPrices?: boolean +} + +export interface GetNativeTokenBalanceResponse { + balances: Array +} + +export interface GetTokenBalancesRequest { + chainIds?: Array + networks?: Array + networkType?: NetworkType + accountAddress?: string + contractAddress?: string + tokenID?: string + includeMetadata?: boolean + metadataOptions?: MetadataOptions + includeCollectionTokens?: boolean + page?: Page +} + +export interface GetTokenBalancesResponse { + page: Page + balances: Array +} + +export interface GetTokenBalancesByContractRequest { + chainIds?: Array + networks?: Array + networkType?: NetworkType + filter: TokenBalancesByContractFilter + omitMetadata?: boolean + page?: Page +} + +export interface GetTokenBalancesByContractResponse { + page: Page + balances: Array +} + +export interface GetTokenBalancesDetailsRequest { + chainIds?: Array + networks?: Array + networkType?: NetworkType + filter: TokenBalancesFilter + omitMetadata?: boolean + page?: Page +} + +export interface GetTokenBalancesDetailsResponse { + page: Page + nativeBalances: Array + balances: Array +} + +export interface GetTokenBalancesSummaryRequest { + chainIds?: Array + networks?: Array + networkType?: NetworkType + filter: TokenBalancesFilter + omitMetadata?: boolean + page?: Page +} + +export interface GetTokenBalancesSummaryResponse { + page: Page + nativeBalances: Array + balances: Array +} + +export interface GetTokenPriceRequest { + q: GatewayTokenPriceQuery +} + +export interface GetTokenPriceResponse { + price: GatewayPrice +} + +export interface GetTokenPricesRequest { + q: Array +} + +export interface GetTokenPricesResponse { + prices: Array +} + +export interface GetTransactionHistoryRequest { + chainIds?: Array + networks?: Array + networkType?: NetworkType + filter: TransactionHistoryFilter + includeMetadata?: boolean + metadataOptions?: MetadataOptions + page?: Page +} + +export interface GetTransactionHistoryResponse { + page: Page + transactions: Array +} + +export interface PingRequest {} + +export interface PingResponse { + status: boolean +} + +export interface RuntimeStatusRequest {} + +export interface RuntimeStatusResponse { + status: GatewayRuntimeStatus +} + +export interface VersionRequest {} + +export interface VersionResponse { + version: Version +} + +// +// Client +// + +export class IndexerGateway implements IndexerGatewayClient { + protected hostname: string + protected fetch: Fetch + protected path = '/rpc/IndexerGateway/' + + constructor(hostname: string, fetch: Fetch) { + this.hostname = hostname.replace(/\/*$/, '') + this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) + } + + private url(name: string): string { + return this.hostname + this.path + name + } + + queryKey = { + getBalanceUpdates: (req: GetBalanceUpdatesRequest) => ['IndexerGateway', 'getBalanceUpdates', req] as const, + getChains: (req: GetChainsRequest) => ['IndexerGateway', 'getChains', req] as const, + getNativeTokenBalance: (req: GetNativeTokenBalanceRequest) => ['IndexerGateway', 'getNativeTokenBalance', req] as const, + getTokenBalances: (req: GetTokenBalancesRequest) => ['IndexerGateway', 'getTokenBalances', req] as const, + getTokenBalancesByContract: (req: GetTokenBalancesByContractRequest) => + ['IndexerGateway', 'getTokenBalancesByContract', req] as const, + getTokenBalancesDetails: (req: GetTokenBalancesDetailsRequest) => ['IndexerGateway', 'getTokenBalancesDetails', req] as const, + getTokenBalancesSummary: (req: GetTokenBalancesSummaryRequest) => ['IndexerGateway', 'getTokenBalancesSummary', req] as const, + getTokenPrice: (req: GetTokenPriceRequest) => ['IndexerGateway', 'getTokenPrice', req] as const, + getTokenPrices: (req: GetTokenPricesRequest) => ['IndexerGateway', 'getTokenPrices', req] as const, + getTransactionHistory: (req: GetTransactionHistoryRequest) => ['IndexerGateway', 'getTransactionHistory', req] as const, + ping: () => ['IndexerGateway', 'ping'] as const, + runtimeStatus: () => ['IndexerGateway', 'runtimeStatus'] as const, + version: () => ['IndexerGateway', 'version'] as const + } + + getBalanceUpdates = ( + req: GetBalanceUpdatesRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetBalanceUpdates'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetBalanceUpdatesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getChains = (req: GetChainsRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetChains'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetChainsResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getNativeTokenBalance = ( + req: GetNativeTokenBalanceRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetNativeTokenBalance'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetNativeTokenBalanceResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenBalances = ( + req: GetTokenBalancesRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenBalances'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenBalancesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenBalancesByContract = ( + req: GetTokenBalancesByContractRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenBalancesByContract'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenBalancesByContractResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenBalancesDetails = ( + req: GetTokenBalancesDetailsRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenBalancesDetails'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenBalancesDetailsResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenBalancesSummary = ( + req: GetTokenBalancesSummaryRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTokenBalancesSummary'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenBalancesSummaryResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenPrice = (req: GetTokenPriceRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetTokenPrice'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenPriceResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTokenPrices = (req: GetTokenPricesRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('GetTokenPrices'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTokenPricesResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + getTransactionHistory = ( + req: GetTransactionHistoryRequest, + headers?: object, + signal?: AbortSignal + ): Promise => { + return this.fetch(this.url('GetTransactionHistory'), createHttpRequest(JsonEncode(req), headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'GetTransactionHistoryResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + ping = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Ping'), createHttpRequest('{}', headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'PingResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + runtimeStatus = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('RuntimeStatus'), createHttpRequest('{}', headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'RuntimeStatusResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } + + version = (headers?: object, signal?: AbortSignal): Promise => { + return this.fetch(this.url('Version'), createHttpRequest('{}', headers, signal)).then( + res => { + return buildResponse(res).then(_data => { + return JsonDecode(_data, 'VersionResponse') + }) + }, + error => { + throw WebrpcRequestFailedError.new({ cause: `fetch(): ${error instanceof Error ? error.message : String(error)}` }) + } + ) + } +} + +const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { + const reqHeaders: { [key: string]: string } = { + ...headers, + 'Content-Type': 'application/json', + [WebrpcHeader]: WebrpcHeaderValue + } + return { method: 'POST', headers: reqHeaders, body, signal } +} + +const buildResponse = (res: Response): Promise => { + return res.text().then(text => { + let data + try { + data = JSON.parse(text) + } catch (error) { + throw WebrpcBadResponseError.new({ + status: res.status, + cause: `JSON.parse(): ${error instanceof Error ? error.message : String(error)}: response text: ${text}` + }) + } + if (!res.ok) { + const code: number = typeof data.code === 'number' ? data.code : 0 + throw (webrpcErrorByCode[code] || WebrpcError).new(data) + } + return data + }) +} + +export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise + +export const JsonEncode = (obj: T): string => { + return JSON.stringify(obj) +} + +export const JsonDecode = (data: string | any, _typ: string = ''): T => { + let parsed: any = data + if (typeof data === 'string') { + try { + parsed = JSON.parse(data) + } catch (err) { + throw WebrpcBadResponseError.new({ cause: `JsonDecode: JSON.parse failed: ${(err as Error).message}` }) + } + } + return parsed as T +} + +// +// Errors +// + +type WebrpcErrorParams = { name?: string; code?: number; message?: string; status?: number; cause?: string } + +export class WebrpcError extends Error { + code: number + status: number + + constructor(error: WebrpcErrorParams = {}) { + super(error.message) + this.name = error.name || 'WebrpcEndpointError' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcError.prototype) + } + + static new(payload: any): WebrpcError { + return new this({ message: payload.message, code: payload.code, status: payload.status, cause: payload.cause }) + } +} + +export class WebrpcEndpointError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcEndpoint' + this.code = typeof error.code === 'number' ? error.code : 0 + this.message = error.message || `endpoint error` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcEndpointError.prototype) + } +} + +export class WebrpcRequestFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcRequestFailed' + this.code = typeof error.code === 'number' ? error.code : -1 + this.message = error.message || `request failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcRequestFailedError.prototype) + } +} + +export class WebrpcBadRouteError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRoute' + this.code = typeof error.code === 'number' ? error.code : -2 + this.message = error.message || `bad route` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadRouteError.prototype) + } +} + +export class WebrpcBadMethodError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadMethod' + this.code = typeof error.code === 'number' ? error.code : -3 + this.message = error.message || `bad method` + this.status = typeof error.status === 'number' ? error.status : 405 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadMethodError.prototype) + } +} + +export class WebrpcBadRequestError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadRequest' + this.code = typeof error.code === 'number' ? error.code : -4 + this.message = error.message || `bad request` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadRequestError.prototype) + } +} + +export class WebrpcBadResponseError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcBadResponse' + this.code = typeof error.code === 'number' ? error.code : -5 + this.message = error.message || `bad response` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcBadResponseError.prototype) + } +} + +export class WebrpcServerPanicError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcServerPanic' + this.code = typeof error.code === 'number' ? error.code : -6 + this.message = error.message || `server panic` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcServerPanicError.prototype) + } +} + +export class WebrpcInternalErrorError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcInternalError' + this.code = typeof error.code === 'number' ? error.code : -7 + this.message = error.message || `internal error` + this.status = typeof error.status === 'number' ? error.status : 500 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcInternalErrorError.prototype) + } +} + +export class WebrpcClientAbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcClientAborted' + this.code = typeof error.code === 'number' ? error.code : -8 + this.message = error.message || `request aborted by client` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcClientAbortedError.prototype) + } +} + +export class WebrpcStreamLostError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamLost' + this.code = typeof error.code === 'number' ? error.code : -9 + this.message = error.message || `stream lost` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcStreamLostError.prototype) + } +} + +export class WebrpcStreamFinishedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'WebrpcStreamFinished' + this.code = typeof error.code === 'number' ? error.code : -10 + this.message = error.message || `stream finished` + this.status = typeof error.status === 'number' ? error.status : 200 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, WebrpcStreamFinishedError.prototype) + } +} + +// +// Schema errors +// + +export class AbortedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Aborted' + this.code = typeof error.code === 'number' ? error.code : 1005 + this.message = error.message || `Request aborted` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AbortedError.prototype) + } +} + +export class AccessKeyMismatchError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AccessKeyMismatch' + this.code = typeof error.code === 'number' ? error.code : 1102 + this.message = error.message || `Access key mismatch` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AccessKeyMismatchError.prototype) + } +} + +export class AccessKeyNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AccessKeyNotFound' + this.code = typeof error.code === 'number' ? error.code : 1101 + this.message = error.message || `Access key not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AccessKeyNotFoundError.prototype) + } +} + +export class AtLeastOneKeyError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'AtLeastOneKey' + this.code = typeof error.code === 'number' ? error.code : 1302 + this.message = error.message || `You need at least one Access Key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, AtLeastOneKeyError.prototype) + } +} + +export class GeoblockedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Geoblocked' + this.code = typeof error.code === 'number' ? error.code : 1006 + this.message = error.message || `Geoblocked region` + this.status = typeof error.status === 'number' ? error.status : 451 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, GeoblockedError.prototype) + } +} + +export class InvalidArgumentError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidArgument' + this.code = typeof error.code === 'number' ? error.code : 2001 + this.message = error.message || `Invalid argument` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidArgumentError.prototype) + } +} + +export class InvalidOriginError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidOrigin' + this.code = typeof error.code === 'number' ? error.code : 1103 + this.message = error.message || `Invalid origin for Access Key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidOriginError.prototype) + } +} + +export class InvalidServiceError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'InvalidService' + this.code = typeof error.code === 'number' ? error.code : 1104 + this.message = error.message || `Service not enabled for Access key` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, InvalidServiceError.prototype) + } +} + +export class MaxAccessKeysError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MaxAccessKeys' + this.code = typeof error.code === 'number' ? error.code : 1301 + this.message = error.message || `Access keys limit reached` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MaxAccessKeysError.prototype) + } +} + +export class MetadataCallFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MetadataCallFailed' + this.code = typeof error.code === 'number' ? error.code : 3003 + this.message = error.message || `Metadata service call failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MetadataCallFailedError.prototype) + } +} + +export class MethodNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'MethodNotFound' + this.code = typeof error.code === 'number' ? error.code : 1003 + this.message = error.message || `Method not found` + this.status = typeof error.status === 'number' ? error.status : 404 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, MethodNotFoundError.prototype) + } +} + +export class NoDefaultKeyError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NoDefaultKey' + this.code = typeof error.code === 'number' ? error.code : 1300 + this.message = error.message || `No default access key found` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, NoDefaultKeyError.prototype) + } +} + +export class NotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'NotFound' + this.code = typeof error.code === 'number' ? error.code : 3000 + this.message = error.message || `Resource not found` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, NotFoundError.prototype) + } +} + +export class PermissionDeniedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'PermissionDenied' + this.code = typeof error.code === 'number' ? error.code : 1001 + this.message = error.message || `Permission denied` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, PermissionDeniedError.prototype) + } +} + +export class ProjectNotFoundError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ProjectNotFound' + this.code = typeof error.code === 'number' ? error.code : 1100 + this.message = error.message || `Project not found` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, ProjectNotFoundError.prototype) + } +} + +export class QueryFailedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QueryFailed' + this.code = typeof error.code === 'number' ? error.code : 2003 + this.message = error.message || `Query failed` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, QueryFailedError.prototype) + } +} + +export class QuotaExceededError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'QuotaExceeded' + this.code = typeof error.code === 'number' ? error.code : 1200 + this.message = error.message || `Quota exceeded` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, QuotaExceededError.prototype) + } +} + +export class RateLimitError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RateLimit' + this.code = typeof error.code === 'number' ? error.code : 1201 + this.message = error.message || `Rate limit exceeded` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RateLimitError.prototype) + } +} + +export class RateLimitedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RateLimited' + this.code = typeof error.code === 'number' ? error.code : 1007 + this.message = error.message || `Rate-limited. Please slow down.` + this.status = typeof error.status === 'number' ? error.status : 429 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RateLimitedError.prototype) + } +} + +export class RequestConflictError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'RequestConflict' + this.code = typeof error.code === 'number' ? error.code : 1004 + this.message = error.message || `Conflict with target resource` + this.status = typeof error.status === 'number' ? error.status : 409 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, RequestConflictError.prototype) + } +} + +export class ResourceExhaustedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'ResourceExhausted' + this.code = typeof error.code === 'number' ? error.code : 2004 + this.message = error.message || `Resource exhausted` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, ResourceExhaustedError.prototype) + } +} + +export class SessionExpiredError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'SessionExpired' + this.code = typeof error.code === 'number' ? error.code : 1002 + this.message = error.message || `Session expired` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, SessionExpiredError.prototype) + } +} + +export class TimeoutError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Timeout' + this.code = typeof error.code === 'number' ? error.code : 1900 + this.message = error.message || `Request timed out` + this.status = typeof error.status === 'number' ? error.status : 408 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, TimeoutError.prototype) + } +} + +export class UnauthorizedError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unauthorized' + this.code = typeof error.code === 'number' ? error.code : 1000 + this.message = error.message || `Unauthorized access` + this.status = typeof error.status === 'number' ? error.status : 401 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnauthorizedError.prototype) + } +} + +export class UnauthorizedUserError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'UnauthorizedUser' + this.code = typeof error.code === 'number' ? error.code : 1105 + this.message = error.message || `Unauthorized user` + this.status = typeof error.status === 'number' ? error.status : 403 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnauthorizedUserError.prototype) + } +} + +export class UnavailableError extends WebrpcError { + constructor(error: WebrpcErrorParams = {}) { + super(error) + this.name = error.name || 'Unavailable' + this.code = typeof error.code === 'number' ? error.code : 2002 + this.message = error.message || `Unavailable resource` + this.status = typeof error.status === 'number' ? error.status : 400 + if (error.cause !== undefined) this.cause = error.cause + Object.setPrototypeOf(this, UnavailableError.prototype) + } +} + +export enum errors { + WebrpcEndpoint = 'WebrpcEndpoint', + WebrpcRequestFailed = 'WebrpcRequestFailed', + WebrpcBadRoute = 'WebrpcBadRoute', + WebrpcBadMethod = 'WebrpcBadMethod', + WebrpcBadRequest = 'WebrpcBadRequest', + WebrpcBadResponse = 'WebrpcBadResponse', + WebrpcServerPanic = 'WebrpcServerPanic', + WebrpcInternalError = 'WebrpcInternalError', + WebrpcClientAborted = 'WebrpcClientAborted', + WebrpcStreamLost = 'WebrpcStreamLost', + WebrpcStreamFinished = 'WebrpcStreamFinished', + Aborted = 'Aborted', + AccessKeyMismatch = 'AccessKeyMismatch', + AccessKeyNotFound = 'AccessKeyNotFound', + AtLeastOneKey = 'AtLeastOneKey', + Geoblocked = 'Geoblocked', + InvalidArgument = 'InvalidArgument', + InvalidOrigin = 'InvalidOrigin', + InvalidService = 'InvalidService', + MaxAccessKeys = 'MaxAccessKeys', + MetadataCallFailed = 'MetadataCallFailed', + MethodNotFound = 'MethodNotFound', + NoDefaultKey = 'NoDefaultKey', + NotFound = 'NotFound', + PermissionDenied = 'PermissionDenied', + ProjectNotFound = 'ProjectNotFound', + QueryFailed = 'QueryFailed', + QuotaExceeded = 'QuotaExceeded', + RateLimit = 'RateLimit', + RateLimited = 'RateLimited', + RequestConflict = 'RequestConflict', + ResourceExhausted = 'ResourceExhausted', + SessionExpired = 'SessionExpired', + Timeout = 'Timeout', + Unauthorized = 'Unauthorized', + UnauthorizedUser = 'UnauthorizedUser', + Unavailable = 'Unavailable' +} + +export enum WebrpcErrorCodes { + WebrpcEndpoint = 0, + WebrpcRequestFailed = -1, + WebrpcBadRoute = -2, + WebrpcBadMethod = -3, + WebrpcBadRequest = -4, + WebrpcBadResponse = -5, + WebrpcServerPanic = -6, + WebrpcInternalError = -7, + WebrpcClientAborted = -8, + WebrpcStreamLost = -9, + WebrpcStreamFinished = -10, + Aborted = 1005, + AccessKeyMismatch = 1102, + AccessKeyNotFound = 1101, + AtLeastOneKey = 1302, + Geoblocked = 1006, + InvalidArgument = 2001, + InvalidOrigin = 1103, + InvalidService = 1104, + MaxAccessKeys = 1301, + MetadataCallFailed = 3003, + MethodNotFound = 1003, + NoDefaultKey = 1300, + NotFound = 3000, + PermissionDenied = 1001, + ProjectNotFound = 1100, + QueryFailed = 2003, + QuotaExceeded = 1200, + RateLimit = 1201, + RateLimited = 1007, + RequestConflict = 1004, + ResourceExhausted = 2004, + SessionExpired = 1002, + Timeout = 1900, + Unauthorized = 1000, + UnauthorizedUser = 1105, + Unavailable = 2002 +} + +export const webrpcErrorByCode: { [code: number]: any } = { + [0]: WebrpcEndpointError, + [-1]: WebrpcRequestFailedError, + [-2]: WebrpcBadRouteError, + [-3]: WebrpcBadMethodError, + [-4]: WebrpcBadRequestError, + [-5]: WebrpcBadResponseError, + [-6]: WebrpcServerPanicError, + [-7]: WebrpcInternalErrorError, + [-8]: WebrpcClientAbortedError, + [-9]: WebrpcStreamLostError, + [-10]: WebrpcStreamFinishedError, + [1005]: AbortedError, + [1102]: AccessKeyMismatchError, + [1101]: AccessKeyNotFoundError, + [1302]: AtLeastOneKeyError, + [1006]: GeoblockedError, + [2001]: InvalidArgumentError, + [1103]: InvalidOriginError, + [1104]: InvalidServiceError, + [1301]: MaxAccessKeysError, + [3003]: MetadataCallFailedError, + [1003]: MethodNotFoundError, + [1300]: NoDefaultKeyError, + [3000]: NotFoundError, + [1001]: PermissionDeniedError, + [1100]: ProjectNotFoundError, + [2003]: QueryFailedError, + [1200]: QuotaExceededError, + [1201]: RateLimitError, + [1007]: RateLimitedError, + [1004]: RequestConflictError, + [2004]: ResourceExhaustedError, + [1002]: SessionExpiredError, + [1900]: TimeoutError, + [1000]: UnauthorizedError, + [1105]: UnauthorizedUserError, + [2002]: UnavailableError +} + +// +// Webrpc +// + +export const WebrpcHeader = 'Webrpc' + +export const WebrpcHeaderValue = 'webrpc@v0.31.2;gen-typescript@v0.23.1;sequence-indexer@v0.4.0' + +type WebrpcGenVersions = { + WebrpcGenVersion: string + codeGenName: string + codeGenVersion: string + schemaName: string + schemaVersion: string +} + +export function VersionFromHeader(headers: Headers): WebrpcGenVersions { + const headerValue = headers.get(WebrpcHeader) + if (!headerValue) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '' + } + } + + return parseWebrpcGenVersions(headerValue) +} + +function parseWebrpcGenVersions(header: string): WebrpcGenVersions { + const versions = header.split(';') + if (versions.length < 3) { + return { + WebrpcGenVersion: '', + codeGenName: '', + codeGenVersion: '', + schemaName: '', + schemaVersion: '' + } + } + + const [_, WebrpcGenVersion] = versions[0]!.split('@') + const [codeGenName, codeGenVersion] = versions[1]!.split('@') + const [schemaName, schemaVersion] = versions[2]!.split('@') + + return { + WebrpcGenVersion: WebrpcGenVersion ?? '', + codeGenName: codeGenName ?? '', + codeGenVersion: codeGenVersion ?? '', + schemaName: schemaName ?? '', + schemaVersion: schemaVersion ?? '' + } +} From 4cd416573f50ee428f386cfb128b44c89465d119 Mon Sep 17 00:00:00 2001 From: Dargon789 <64915515+Dargon789@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:05:04 +0000 Subject: [PATCH 07/23] 3.0.0-beta.12 (#265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(dapp-client): cache signed calls from fee options * Add new userdata client (#954) * Fix build error * 3.0.0-beta.10 * fix(dapp-client): remove _refreshExplicitSession use that causes blocked pop up * 3.0.0-beta.12 --------- Co-authored-by: Tolgahan Arikan Co-authored-by: Ahmet Buğra Yiğiter Co-authored-by: Taylan Pince --- .changeset/bright-pots-hope.md | 18 + .changeset/free-tips-switch.md | 18 + .changeset/pre.json | 30 +- packages/services/api/CHANGELOG.md | 12 + packages/services/api/package.json | 2 +- packages/services/builder/CHANGELOG.md | 12 + packages/services/builder/package.json | 2 +- packages/services/guard/CHANGELOG.md | 12 + packages/services/guard/package.json | 2 +- .../services/identity-instrument/CHANGELOG.md | 12 + .../services/identity-instrument/package.json | 2 +- packages/services/indexer/CHANGELOG.md | 12 + packages/services/indexer/package.json | 2 +- packages/services/marketplace/CHANGELOG.md | 12 + packages/services/marketplace/package.json | 2 +- packages/services/metadata/CHANGELOG.md | 12 + packages/services/metadata/package.json | 2 +- packages/services/relayer/CHANGELOG.md | 16 + packages/services/relayer/package.json | 2 +- packages/services/userdata/CHANGELOG.md | 12 + packages/services/userdata/package.json | 2 +- .../services/userdata/src/userdata.gen.ts | 931 +++++++++++++++++- packages/utils/abi/CHANGELOG.md | 12 + packages/utils/abi/package.json | 2 +- packages/wallet/core/CHANGELOG.md | 20 + packages/wallet/core/package.json | 2 +- packages/wallet/dapp-client/CHANGELOG.md | 22 + packages/wallet/dapp-client/package.json | 2 +- .../dapp-client/src/ChainSessionManager.ts | 66 +- packages/wallet/primitives/CHANGELOG.md | 12 + packages/wallet/primitives/package.json | 2 +- packages/wallet/wdk/CHANGELOG.md | 24 + packages/wallet/wdk/package.json | 2 +- 33 files changed, 1244 insertions(+), 49 deletions(-) create mode 100644 .changeset/bright-pots-hope.md create mode 100644 .changeset/free-tips-switch.md diff --git a/.changeset/bright-pots-hope.md b/.changeset/bright-pots-hope.md new file mode 100644 index 0000000000..6bd9a887ca --- /dev/null +++ b/.changeset/bright-pots-hope.md @@ -0,0 +1,18 @@ +--- +'@0xsequence/api': patch +'@0xsequence/builder': patch +'@0xsequence/guard': patch +'@0xsequence/identity-instrument': patch +'@0xsequence/indexer': patch +'@0xsequence/marketplace': patch +'@0xsequence/metadata': patch +'@0xsequence/relayer': patch +'@0xsequence/userdata': patch +'@0xsequence/abi': patch +'@0xsequence/wallet-core': patch +'@0xsequence/dapp-client': patch +'@0xsequence/wallet-primitives': patch +'@0xsequence/wallet-wdk': patch +--- + +Beta release with dapp connector fixes diff --git a/.changeset/free-tips-switch.md b/.changeset/free-tips-switch.md new file mode 100644 index 0000000000..0921b6d355 --- /dev/null +++ b/.changeset/free-tips-switch.md @@ -0,0 +1,18 @@ +--- +'@0xsequence/api': patch +'@0xsequence/builder': patch +'@0xsequence/guard': patch +'@0xsequence/identity-instrument': patch +'@0xsequence/indexer': patch +'@0xsequence/marketplace': patch +'@0xsequence/metadata': patch +'@0xsequence/relayer': patch +'@0xsequence/userdata': patch +'@0xsequence/abi': patch +'@0xsequence/wallet-core': patch +'@0xsequence/dapp-client': patch +'@0xsequence/wallet-primitives': patch +'@0xsequence/wallet-wdk': patch +--- + +3.0.0 beta diff --git a/.changeset/pre.json b/.changeset/pre.json index 5483a869a0..3b0804872d 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -4,27 +4,29 @@ "initialVersions": { "docs": "0.1.0", "web": "0.1.0", - "@0xsequence/api": "3.0.0-beta.9", - "@0xsequence/builder": "3.0.0-beta.9", - "@0xsequence/guard": "3.0.0-beta.9", - "@0xsequence/identity-instrument": "3.0.0-beta.9", - "@0xsequence/indexer": "3.0.0-beta.9", - "@0xsequence/marketplace": "3.0.0-beta.9", - "@0xsequence/metadata": "3.0.0-beta.9", - "@0xsequence/relayer": "3.0.0-beta.9", - "@0xsequence/userdata": "3.0.0-beta.9", - "@0xsequence/abi": "3.0.0-beta.9", - "@0xsequence/wallet-core": "3.0.0-beta.9", - "@0xsequence/dapp-client": "3.0.0-beta.9", - "@0xsequence/wallet-primitives": "3.0.0-beta.9", - "@0xsequence/wallet-wdk": "3.0.0-beta.9", + "@0xsequence/api": "3.0.0-beta.11", + "@0xsequence/builder": "3.0.0-beta.11", + "@0xsequence/guard": "3.0.0-beta.11", + "@0xsequence/identity-instrument": "3.0.0-beta.11", + "@0xsequence/indexer": "3.0.0-beta.11", + "@0xsequence/marketplace": "3.0.0-beta.11", + "@0xsequence/metadata": "3.0.0-beta.11", + "@0xsequence/relayer": "3.0.0-beta.11", + "@0xsequence/userdata": "3.0.0-beta.11", + "@0xsequence/abi": "3.0.0-beta.11", + "@0xsequence/wallet-core": "3.0.0-beta.11", + "@0xsequence/dapp-client": "3.0.0-beta.11", + "@0xsequence/wallet-primitives": "3.0.0-beta.11", + "@0xsequence/wallet-wdk": "3.0.0-beta.11", "@repo/eslint-config": "0.0.1-beta.1", "@repo/typescript-config": "0.0.1-beta.1", "@repo/ui": "0.0.1-beta.1" }, "changesets": [ + "bright-pots-hope", "crisp-zoos-retire", "cyan-radios-relax", + "free-tips-switch", "goofy-laws-serve", "new-turkeys-double", "nice-tips-slide", diff --git a/packages/services/api/CHANGELOG.md b/packages/services/api/CHANGELOG.md index 8a7997a58c..369f5a0c60 100644 --- a/packages/services/api/CHANGELOG.md +++ b/packages/services/api/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/api +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/api/package.json b/packages/services/api/package.json index 1dcb3dae25..36714de7e9 100644 --- a/packages/services/api/package.json +++ b/packages/services/api/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/api", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "description": "api sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/api", "author": "Sequence Platforms Inc.", diff --git a/packages/services/builder/CHANGELOG.md b/packages/services/builder/CHANGELOG.md index 76bd0fe599..1faea9afa4 100644 --- a/packages/services/builder/CHANGELOG.md +++ b/packages/services/builder/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/builder +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/builder/package.json b/packages/services/builder/package.json index f0b0d62b08..7dbaf13d77 100644 --- a/packages/services/builder/package.json +++ b/packages/services/builder/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/builder", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "description": "builder sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/builder", "author": "Sequence Platforms Inc.", diff --git a/packages/services/guard/CHANGELOG.md b/packages/services/guard/CHANGELOG.md index d20ea859e3..5588185ac7 100644 --- a/packages/services/guard/CHANGELOG.md +++ b/packages/services/guard/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/guard +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/guard/package.json b/packages/services/guard/package.json index cacf82ffaf..727a5049ef 100644 --- a/packages/services/guard/package.json +++ b/packages/services/guard/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/guard", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "description": "guard sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/guard", "author": "Sequence Platforms Inc.", diff --git a/packages/services/identity-instrument/CHANGELOG.md b/packages/services/identity-instrument/CHANGELOG.md index 9dca729849..9215b75967 100644 --- a/packages/services/identity-instrument/CHANGELOG.md +++ b/packages/services/identity-instrument/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/identity-instrument +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/identity-instrument/package.json b/packages/services/identity-instrument/package.json index a05aff0124..d1adaba0c1 100644 --- a/packages/services/identity-instrument/package.json +++ b/packages/services/identity-instrument/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/identity-instrument", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/services/indexer/CHANGELOG.md b/packages/services/indexer/CHANGELOG.md index 0898bb564d..a92bbe45d0 100644 --- a/packages/services/indexer/CHANGELOG.md +++ b/packages/services/indexer/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/indexer +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/indexer/package.json b/packages/services/indexer/package.json index 332d217325..db6986b7b1 100644 --- a/packages/services/indexer/package.json +++ b/packages/services/indexer/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/indexer", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "description": "indexer sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/indexer", "author": "Sequence Platforms Inc.", diff --git a/packages/services/marketplace/CHANGELOG.md b/packages/services/marketplace/CHANGELOG.md index 9fe90e1d3d..0744e1ebdd 100644 --- a/packages/services/marketplace/CHANGELOG.md +++ b/packages/services/marketplace/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/marketplace +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/marketplace/package.json b/packages/services/marketplace/package.json index 17cc4cf514..81c7cea93f 100644 --- a/packages/services/marketplace/package.json +++ b/packages/services/marketplace/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/marketplace", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "description": "marketplace sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/marketplace", "author": "Sequence Platforms Inc.", diff --git a/packages/services/metadata/CHANGELOG.md b/packages/services/metadata/CHANGELOG.md index f0831c0a50..a44a72ff73 100644 --- a/packages/services/metadata/CHANGELOG.md +++ b/packages/services/metadata/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/metadata +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/metadata/package.json b/packages/services/metadata/package.json index afed07373a..202346f94d 100644 --- a/packages/services/metadata/package.json +++ b/packages/services/metadata/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/metadata", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "publishConfig": { "access": "public" }, diff --git a/packages/services/relayer/CHANGELOG.md b/packages/services/relayer/CHANGELOG.md index 0e23d886cd..6a4eacc87d 100644 --- a/packages/services/relayer/CHANGELOG.md +++ b/packages/services/relayer/CHANGELOG.md @@ -1,5 +1,21 @@ # @0xsequence/relayer +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.0-beta.12 + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.0-beta.11 + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/relayer/package.json b/packages/services/relayer/package.json index 3c2802d78f..10bceb82bf 100644 --- a/packages/services/relayer/package.json +++ b/packages/services/relayer/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/relayer", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/services/userdata/CHANGELOG.md b/packages/services/userdata/CHANGELOG.md index 659989d4c5..f66f7dec2d 100644 --- a/packages/services/userdata/CHANGELOG.md +++ b/packages/services/userdata/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/userdata +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/services/userdata/package.json b/packages/services/userdata/package.json index 3b40863853..22956ef662 100644 --- a/packages/services/userdata/package.json +++ b/packages/services/userdata/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/userdata", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "description": "userdata sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/userdata", "author": "Sequence Platforms Inc.", diff --git a/packages/services/userdata/src/userdata.gen.ts b/packages/services/userdata/src/userdata.gen.ts index a26fdb9950..18ba5d847e 100644 --- a/packages/services/userdata/src/userdata.gen.ts +++ b/packages/services/userdata/src/userdata.gen.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// userdata v0.1.0 99a19ff0218eda6f5e544642d0fd72f66736bdaf +// userdata v0.1.0 4797326ffeb063c7256cf02523d563066fdaec9b // -- // Code generated by Webrpc-gen@v0.30.2 with typescript generator. DO NOT EDIT. // @@ -12,7 +12,7 @@ export const WebrpcVersion = 'v1' export const WebrpcSchemaVersion = 'v0.1.0' // Schema hash generated from your RIDL schema -export const WebrpcSchemaHash = '99a19ff0218eda6f5e544642d0fd72f66736bdaf' +export const WebrpcSchemaHash = '4797326ffeb063c7256cf02523d563066fdaec9b' // // Client interface @@ -28,26 +28,160 @@ export interface UserDataClient { headers?: object, signal?: AbortSignal, ): Promise + + getWalletPreferences( + req: GetWalletPreferencesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + putWalletPreferences( + req: PutWalletPreferencesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + listWalletSigners( + req: ListWalletSignersRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + putWalletSigner(req: PutWalletSignerRequest, headers?: object, signal?: AbortSignal): Promise + + deleteWalletSigner( + req: DeleteWalletSignerRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + listSessions(req: ListSessionsRequest, headers?: object, signal?: AbortSignal): Promise + + putSession(req: PutSessionRequest, headers?: object, signal?: AbortSignal): Promise + + deleteSession(req: DeleteSessionRequest, headers?: object, signal?: AbortSignal): Promise + + listContacts(req: ListContactsRequest, headers?: object, signal?: AbortSignal): Promise + + putContact(req: PutContactRequest, headers?: object, signal?: AbortSignal): Promise + + deleteContact(req: DeleteContactRequest, headers?: object, signal?: AbortSignal): Promise + + listWatchedWallets( + req: ListWatchedWalletsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + putWatchedWallet( + req: PutWatchedWalletRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + deleteWatchedWallet( + req: DeleteWatchedWalletRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + listDiscoverFavorites( + req: ListDiscoverFavoritesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + putDiscoverFavorite( + req: PutDiscoverFavoriteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + deleteDiscoverFavorite( + req: DeleteDiscoverFavoriteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + listDiscoverHistory( + req: ListDiscoverHistoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + putDiscoverHistory( + req: PutDiscoverHistoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + deleteDiscoverHistory( + req: DeleteDiscoverHistoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + listTokenFavorites( + req: ListTokenFavoritesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + putTokenFavorite( + req: PutTokenFavoriteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise + + deleteTokenFavorite( + req: DeleteTokenFavoriteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise } // // Schema types // +export interface Version { + webrpcVersion: string + schemaVersion: string + schemaHash: string + appVersion: string +} + +export interface RuntimeStatus { + healthOK: boolean + startTime: string + uptime: number + ver: string + branch: string + commitHash: string +} + export interface Wallet { address: string - ecosystem: number + ecosystem?: number + preferences: WalletPreferences + updatedAt: string + createdAt: string } -export interface Signer { - address: string - kind: string - email?: string +export interface WalletPreferences { + manualSigning?: boolean + hideUnlistedTokens?: boolean + includeTestnets?: boolean + currency?: string } export interface WalletSigner { walletAddress: string signerAddress: string + kind: string + email?: string + updatedAt: string + createdAt: string } export interface Session { @@ -60,11 +194,76 @@ export interface Session { createdAt: string } +export interface Contact { + walletAddress: string + contactAddress: string + nickname: string + updatedAt: string + createdAt: string +} + +export interface WatchedWallet { + walletAddress: string + watchedAddress: string + nickname: string + updatedAt: string + createdAt: string +} + +export interface DiscoverFavorite { + walletAddress: string + id: number + dappId: string + createdAt: string +} + +export interface DiscoverHistory { + walletAddress: string + id: number + dappId: string + accessedAt: string +} + +export interface TokenFavorite { + walletAddress: string + id: number + chainId: string + contractAddress: string + tokenId: string + createdAt: string +} + export interface SessionProps { address: string appUrl: string } +export interface WalletSignerProps { + address: string + kind: string + email?: string +} + +export interface ContactProps { + address: string + nickname?: string +} + +export interface DiscoverProps { + dappId: string +} + +export interface TokenFavoriteProps { + chainId: string + contractAddress: string + tokenId: string +} + +export interface WatchedWalletProps { + watchedAddress: string + nickname?: string +} + export interface GetCapabilitiesRequest {} export interface GetCapabilitiesResponse { @@ -90,6 +289,210 @@ export interface GetIdentityTokenResponse { idToken: string } +export interface GetWalletPreferencesRequest { + wallet: string +} + +export interface GetWalletPreferencesResponse { + preferences: WalletPreferences +} + +export interface PutWalletPreferencesRequest { + wallet: string + preferences: WalletPreferences +} + +export interface PutWalletPreferencesResponse {} + +export interface ListWalletSignersRequest { + wallet: string + pageSize: number + cursor: string +} + +export interface ListWalletSignersResponse { + signers: Array + nextCursor: string +} + +export interface PutWalletSignerRequest { + wallet: string + signer: WalletSignerProps +} + +export interface PutWalletSignerResponse { + signer: WalletSigner +} + +export interface DeleteWalletSignerRequest { + wallet: string + signer: string +} + +export interface DeleteWalletSignerResponse {} + +export interface ListSessionsRequest { + wallet: string + pageSize: number + cursor: string +} + +export interface ListSessionsResponse { + sessions: Array + nextCursor: string +} + +export interface PutSessionRequest { + wallet: string + session: SessionProps +} + +export interface PutSessionResponse { + session: Session +} + +export interface DeleteSessionRequest { + wallet: string + session: string +} + +export interface DeleteSessionResponse {} + +export interface ListContactsRequest { + wallet: string + pageSize: number + cursor: string +} + +export interface ListContactsResponse { + contacts: Array + nextCursor: string +} + +export interface PutContactRequest { + wallet: string + contact: ContactProps +} + +export interface PutContactResponse { + contact: Contact +} + +export interface DeleteContactRequest { + wallet: string + contact: string +} + +export interface DeleteContactResponse {} + +export interface ListWatchedWalletsRequest { + wallet: string + pageSize: number + cursor: string +} + +export interface ListWatchedWalletsResponse { + watchedWallets: Array + nextCursor: string +} + +export interface PutWatchedWalletRequest { + wallet: string + watchedWallet: WatchedWalletProps +} + +export interface PutWatchedWalletResponse { + watchedWallet: WatchedWallet +} + +export interface DeleteWatchedWalletRequest { + wallet: string + watchedWallet: string +} + +export interface DeleteWatchedWalletResponse {} + +export interface ListDiscoverFavoritesRequest { + wallet: string + pageSize: number + cursor: string +} + +export interface ListDiscoverFavoritesResponse { + favorites: Array + nextCursor: string +} + +export interface PutDiscoverFavoriteRequest { + wallet: string + favorite: DiscoverProps +} + +export interface PutDiscoverFavoriteResponse { + favorite: DiscoverFavorite +} + +export interface DeleteDiscoverFavoriteRequest { + wallet: string + id: number +} + +export interface DeleteDiscoverFavoriteResponse {} + +export interface ListDiscoverHistoryRequest { + wallet: string + pageSize: number + cursor: string +} + +export interface ListDiscoverHistoryResponse { + history: Array + nextCursor: string +} + +export interface PutDiscoverHistoryRequest { + wallet: string + history: DiscoverProps +} + +export interface PutDiscoverHistoryResponse { + history: DiscoverHistory +} + +export interface DeleteDiscoverHistoryRequest { + wallet: string + id: number +} + +export interface DeleteDiscoverHistoryResponse {} + +export interface ListTokenFavoritesRequest { + wallet: string + pageSize: number + cursor: string +} + +export interface ListTokenFavoritesResponse { + favorites: Array + nextCursor: string +} + +export interface PutTokenFavoriteRequest { + wallet: string + favorite: TokenFavoriteProps +} + +export interface PutTokenFavoriteResponse { + favorite: TokenFavorite +} + +export interface DeleteTokenFavoriteRequest { + wallet: string + id: number +} + +export interface DeleteTokenFavoriteResponse {} + // // Client // @@ -112,6 +515,30 @@ export class UserData implements UserDataClient { getCapabilities: () => ['UserData', 'getCapabilities'] as const, getAccessToken: (req: GetAccessTokenRequest) => ['UserData', 'getAccessToken', req] as const, getIdentityToken: (req: GetIdentityTokenRequest) => ['UserData', 'getIdentityToken', req] as const, + getWalletPreferences: (req: GetWalletPreferencesRequest) => ['UserData', 'getWalletPreferences', req] as const, + putWalletPreferences: (req: PutWalletPreferencesRequest) => ['UserData', 'putWalletPreferences', req] as const, + listWalletSigners: (req: ListWalletSignersRequest) => ['UserData', 'listWalletSigners', req] as const, + putWalletSigner: (req: PutWalletSignerRequest) => ['UserData', 'putWalletSigner', req] as const, + deleteWalletSigner: (req: DeleteWalletSignerRequest) => ['UserData', 'deleteWalletSigner', req] as const, + listSessions: (req: ListSessionsRequest) => ['UserData', 'listSessions', req] as const, + putSession: (req: PutSessionRequest) => ['UserData', 'putSession', req] as const, + deleteSession: (req: DeleteSessionRequest) => ['UserData', 'deleteSession', req] as const, + listContacts: (req: ListContactsRequest) => ['UserData', 'listContacts', req] as const, + putContact: (req: PutContactRequest) => ['UserData', 'putContact', req] as const, + deleteContact: (req: DeleteContactRequest) => ['UserData', 'deleteContact', req] as const, + listWatchedWallets: (req: ListWatchedWalletsRequest) => ['UserData', 'listWatchedWallets', req] as const, + putWatchedWallet: (req: PutWatchedWalletRequest) => ['UserData', 'putWatchedWallet', req] as const, + deleteWatchedWallet: (req: DeleteWatchedWalletRequest) => ['UserData', 'deleteWatchedWallet', req] as const, + listDiscoverFavorites: (req: ListDiscoverFavoritesRequest) => ['UserData', 'listDiscoverFavorites', req] as const, + putDiscoverFavorite: (req: PutDiscoverFavoriteRequest) => ['UserData', 'putDiscoverFavorite', req] as const, + deleteDiscoverFavorite: (req: DeleteDiscoverFavoriteRequest) => + ['UserData', 'deleteDiscoverFavorite', req] as const, + listDiscoverHistory: (req: ListDiscoverHistoryRequest) => ['UserData', 'listDiscoverHistory', req] as const, + putDiscoverHistory: (req: PutDiscoverHistoryRequest) => ['UserData', 'putDiscoverHistory', req] as const, + deleteDiscoverHistory: (req: DeleteDiscoverHistoryRequest) => ['UserData', 'deleteDiscoverHistory', req] as const, + listTokenFavorites: (req: ListTokenFavoritesRequest) => ['UserData', 'listTokenFavorites', req] as const, + putTokenFavorite: (req: PutTokenFavoriteRequest) => ['UserData', 'putTokenFavorite', req] as const, + deleteTokenFavorite: (req: DeleteTokenFavoriteRequest) => ['UserData', 'deleteTokenFavorite', req] as const, } getCapabilities = (headers?: object, signal?: AbortSignal): Promise => { @@ -172,6 +599,496 @@ export class UserData implements UserDataClient { }, ) } + + getWalletPreferences = ( + req: GetWalletPreferencesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('GetWalletPreferences'), + createHttpRequest(JsonEncode(req, 'GetWalletPreferencesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'GetWalletPreferencesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + putWalletPreferences = ( + req: PutWalletPreferencesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('PutWalletPreferences'), + createHttpRequest(JsonEncode(req, 'PutWalletPreferencesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PutWalletPreferencesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listWalletSigners = ( + req: ListWalletSignersRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('ListWalletSigners'), + createHttpRequest(JsonEncode(req, 'ListWalletSignersRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListWalletSignersResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + putWalletSigner = ( + req: PutWalletSignerRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('PutWalletSigner'), + createHttpRequest(JsonEncode(req, 'PutWalletSignerRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PutWalletSignerResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deleteWalletSigner = ( + req: DeleteWalletSignerRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('DeleteWalletSigner'), + createHttpRequest(JsonEncode(req, 'DeleteWalletSignerRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeleteWalletSignerResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listSessions = (req: ListSessionsRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('ListSessions'), + createHttpRequest(JsonEncode(req, 'ListSessionsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListSessionsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + putSession = (req: PutSessionRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('PutSession'), + createHttpRequest(JsonEncode(req, 'PutSessionRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PutSessionResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deleteSession = ( + req: DeleteSessionRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('DeleteSession'), + createHttpRequest(JsonEncode(req, 'DeleteSessionRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeleteSessionResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listContacts = (req: ListContactsRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('ListContacts'), + createHttpRequest(JsonEncode(req, 'ListContactsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListContactsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + putContact = (req: PutContactRequest, headers?: object, signal?: AbortSignal): Promise => { + return this.fetch( + this.url('PutContact'), + createHttpRequest(JsonEncode(req, 'PutContactRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PutContactResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deleteContact = ( + req: DeleteContactRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('DeleteContact'), + createHttpRequest(JsonEncode(req, 'DeleteContactRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeleteContactResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listWatchedWallets = ( + req: ListWatchedWalletsRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('ListWatchedWallets'), + createHttpRequest(JsonEncode(req, 'ListWatchedWalletsRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListWatchedWalletsResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + putWatchedWallet = ( + req: PutWatchedWalletRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('PutWatchedWallet'), + createHttpRequest(JsonEncode(req, 'PutWatchedWalletRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PutWatchedWalletResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deleteWatchedWallet = ( + req: DeleteWatchedWalletRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('DeleteWatchedWallet'), + createHttpRequest(JsonEncode(req, 'DeleteWatchedWalletRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeleteWatchedWalletResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listDiscoverFavorites = ( + req: ListDiscoverFavoritesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('ListDiscoverFavorites'), + createHttpRequest(JsonEncode(req, 'ListDiscoverFavoritesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListDiscoverFavoritesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + putDiscoverFavorite = ( + req: PutDiscoverFavoriteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('PutDiscoverFavorite'), + createHttpRequest(JsonEncode(req, 'PutDiscoverFavoriteRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PutDiscoverFavoriteResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deleteDiscoverFavorite = ( + req: DeleteDiscoverFavoriteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('DeleteDiscoverFavorite'), + createHttpRequest(JsonEncode(req, 'DeleteDiscoverFavoriteRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeleteDiscoverFavoriteResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listDiscoverHistory = ( + req: ListDiscoverHistoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('ListDiscoverHistory'), + createHttpRequest(JsonEncode(req, 'ListDiscoverHistoryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListDiscoverHistoryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + putDiscoverHistory = ( + req: PutDiscoverHistoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('PutDiscoverHistory'), + createHttpRequest(JsonEncode(req, 'PutDiscoverHistoryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PutDiscoverHistoryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deleteDiscoverHistory = ( + req: DeleteDiscoverHistoryRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('DeleteDiscoverHistory'), + createHttpRequest(JsonEncode(req, 'DeleteDiscoverHistoryRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeleteDiscoverHistoryResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + listTokenFavorites = ( + req: ListTokenFavoritesRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('ListTokenFavorites'), + createHttpRequest(JsonEncode(req, 'ListTokenFavoritesRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'ListTokenFavoritesResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + putTokenFavorite = ( + req: PutTokenFavoriteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('PutTokenFavorite'), + createHttpRequest(JsonEncode(req, 'PutTokenFavoriteRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'PutTokenFavoriteResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } + + deleteTokenFavorite = ( + req: DeleteTokenFavoriteRequest, + headers?: object, + signal?: AbortSignal, + ): Promise => { + return this.fetch( + this.url('DeleteTokenFavorite'), + createHttpRequest(JsonEncode(req, 'DeleteTokenFavoriteRequest'), headers, signal), + ).then( + (res) => { + return buildResponse(res).then((_data) => { + return JsonDecode(_data, 'DeleteTokenFavoriteResponse') + }) + }, + (error) => { + throw WebrpcRequestFailedError.new({ + cause: `fetch(): ${error instanceof Error ? error.message : String(error)}`, + }) + }, + ) + } } const createHttpRequest = (body: string = '{}', headers: object = {}, signal: AbortSignal | null = null): object => { diff --git a/packages/utils/abi/CHANGELOG.md b/packages/utils/abi/CHANGELOG.md index e62c45c4b0..fc61642ed1 100644 --- a/packages/utils/abi/CHANGELOG.md +++ b/packages/utils/abi/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/abi +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/utils/abi/package.json b/packages/utils/abi/package.json index 83978c3d8d..a98f343bbf 100644 --- a/packages/utils/abi/package.json +++ b/packages/utils/abi/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/abi", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "description": "abi sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/utils/abi", "author": "Sequence Platforms Inc.", diff --git a/packages/wallet/core/CHANGELOG.md b/packages/wallet/core/CHANGELOG.md index a8a4c01217..496ed06578 100644 --- a/packages/wallet/core/CHANGELOG.md +++ b/packages/wallet/core/CHANGELOG.md @@ -1,5 +1,25 @@ # @0xsequence/wallet-core +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.12 + - @0xsequence/relayer@3.0.0-beta.12 + - @0xsequence/wallet-primitives@3.0.0-beta.12 + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.11 + - @0xsequence/relayer@3.0.0-beta.11 + - @0xsequence/wallet-primitives@3.0.0-beta.11 + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/wallet/core/package.json b/packages/wallet/core/package.json index 2b575325f8..c99a354cc5 100644 --- a/packages/wallet/core/package.json +++ b/packages/wallet/core/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-core", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/wallet/dapp-client/CHANGELOG.md b/packages/wallet/dapp-client/CHANGELOG.md index 3a46bc2427..a4df685529 100644 --- a/packages/wallet/dapp-client/CHANGELOG.md +++ b/packages/wallet/dapp-client/CHANGELOG.md @@ -1,5 +1,27 @@ # @0xsequence/dapp-client +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.12 + - @0xsequence/relayer@3.0.0-beta.12 + - @0xsequence/wallet-core@3.0.0-beta.12 + - @0xsequence/wallet-primitives@3.0.0-beta.12 + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.11 + - @0xsequence/relayer@3.0.0-beta.11 + - @0xsequence/wallet-core@3.0.0-beta.11 + - @0xsequence/wallet-primitives@3.0.0-beta.11 + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/wallet/dapp-client/package.json b/packages/wallet/dapp-client/package.json index 8ab8875c49..784422dc08 100644 --- a/packages/wallet/dapp-client/package.json +++ b/packages/wallet/dapp-client/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/dapp-client", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/wallet/dapp-client/src/ChainSessionManager.ts b/packages/wallet/dapp-client/src/ChainSessionManager.ts index cd67c6b609..324843ba41 100644 --- a/packages/wallet/dapp-client/src/ChainSessionManager.ts +++ b/packages/wallet/dapp-client/src/ChainSessionManager.ts @@ -84,6 +84,11 @@ export class ChainSessionManager { public loginMethod: LoginMethod | null = null public userEmail: string | null = null private guard?: GuardConfig + private lastSignedCallCache?: { + fingerprint: string + signedCall: { to: Address.Address; data: Hex.Hex } + createdAtMs: number + } /** * @param chainId The ID of the chain this manager is responsible for. @@ -811,19 +816,6 @@ export class ChainSessionManager { await this.sessionManager.findSignersForCalls(this.wallet.address, this.chainId, calls) return true } catch (error) { - if (error instanceof Error && error.message.includes('Signer supporting call is expired')) { - // Extract the expired signer address from the message with address regex - const expiredSignerAddress = error.message.match(/(0x[0-9a-fA-F]{40})/)?.[1] - if (expiredSignerAddress) { - // Refresh the session - await this._refreshExplicitSession(Address.from(expiredSignerAddress)) - // Retry the permission check - return this.hasPermission(transactions) - } else { - // Could not parse error message. Rethrow as this shouldn't happen. - throw error - } - } // An error from findSignersForCalls indicates a permission failure. console.warn( `Permission check failed for chain ${this.chainId}:`, @@ -851,6 +843,14 @@ export class ChainSessionManager { })) try { const signedCall = await this._buildAndSignCalls(callsToSend) + const fingerprint = this._fingerprintCalls(callsToSend) + if (fingerprint) { + this.lastSignedCallCache = { + fingerprint, + signedCall, + createdAtMs: Date.now(), + } + } const feeOptions = await this.relayer.feeOptions(signedCall.to, this.chainId, callsToSend) return feeOptions.options } catch (err) { @@ -907,7 +907,7 @@ export class ChainSessionManager { callsToSend.unshift(transferCall) } } - const signedCalls = await this._buildAndSignCalls(callsToSend) + const signedCalls = this._getCachedSignedCall(callsToSend) ?? (await this._buildAndSignCalls(callsToSend)) const hash = await this.relayer.relay(signedCalls.to, signedCalls.data, this.chainId) const status = await this._waitForTransactionReceipt(hash.opHash, this.chainId) if (status.status === 'confirmed') { @@ -1101,4 +1101,42 @@ export class ChainSessionManager { await this.sequenceStorage.clearExplicitSessions() await this.sequenceStorage.clearSessionlessConnection() } + + private _getCachedSignedCall(calls: Payload.Call[]): { to: Address.Address; data: Hex.Hex } | null { + if (!this.lastSignedCallCache) { + return null + } + const ttlMs = 30_000 + if (Date.now() - this.lastSignedCallCache.createdAtMs > ttlMs) { + this.lastSignedCallCache = undefined + return null + } + const fingerprint = this._fingerprintCalls(calls) + if (!fingerprint) { + return null + } + if (fingerprint !== this.lastSignedCallCache.fingerprint) { + return null + } + return this.lastSignedCallCache.signedCall + } + + private _fingerprintCalls(calls: Payload.Call[]): string | null { + try { + return JSON.stringify( + calls.map((call) => ({ + to: call.to, + value: call.value?.toString() ?? '0', + data: call.data ?? '0x', + gasLimit: call.gasLimit?.toString() ?? '0', + delegateCall: call.delegateCall ?? false, + onlyFallback: call.onlyFallback ?? false, + behaviorOnError: call.behaviorOnError ?? 'revert', + })), + ) + } catch (error) { + console.warn('ChainSessionManager._fingerprintCalls failed:', error) + return null + } + } } diff --git a/packages/wallet/primitives/CHANGELOG.md b/packages/wallet/primitives/CHANGELOG.md index 0a422e0314..9163a5f154 100644 --- a/packages/wallet/primitives/CHANGELOG.md +++ b/packages/wallet/primitives/CHANGELOG.md @@ -1,5 +1,17 @@ # @0xsequence/wallet-primitives +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/wallet/primitives/package.json b/packages/wallet/primitives/package.json index 7b1aa271fd..b4ffa58da7 100644 --- a/packages/wallet/primitives/package.json +++ b/packages/wallet/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-primitives", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/wallet/wdk/CHANGELOG.md b/packages/wallet/wdk/CHANGELOG.md index eedb582ced..eb47587675 100644 --- a/packages/wallet/wdk/CHANGELOG.md +++ b/packages/wallet/wdk/CHANGELOG.md @@ -1,5 +1,29 @@ # @0xsequence/wallet-wdk +## 3.0.0-beta.12 + +### Patch Changes + +- Beta release with dapp connector fixes +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.12 + - @0xsequence/identity-instrument@3.0.0-beta.12 + - @0xsequence/relayer@3.0.0-beta.12 + - @0xsequence/wallet-core@3.0.0-beta.12 + - @0xsequence/wallet-primitives@3.0.0-beta.12 + +## 3.0.0-beta.11 + +### Patch Changes + +- 3.0.0 beta +- Updated dependencies + - @0xsequence/guard@3.0.0-beta.11 + - @0xsequence/identity-instrument@3.0.0-beta.11 + - @0xsequence/relayer@3.0.0-beta.11 + - @0xsequence/wallet-core@3.0.0-beta.11 + - @0xsequence/wallet-primitives@3.0.0-beta.11 + ## 3.0.0-beta.10 ### Patch Changes diff --git a/packages/wallet/wdk/package.json b/packages/wallet/wdk/package.json index ed1843b760..7cca52e6e3 100644 --- a/packages/wallet/wdk/package.json +++ b/packages/wallet/wdk/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-wdk", - "version": "3.0.0-beta.10", + "version": "3.0.0-beta.12", "license": "Apache-2.0", "type": "module", "publishConfig": { From 1234bf545c258a7590fde8abe41fb882bd25c555 Mon Sep 17 00:00:00 2001 From: "snyk-io[bot]" <141718529+snyk-io[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:26:33 +0700 Subject: [PATCH 08/23] fix: extras/web/package.json to reduce vulnerabilities (#264) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NEXT-15104645 - https://snyk.io/vuln/SNYK-JS-NEXT-15105315 Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> --- extras/web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/web/package.json b/extras/web/package.json index b0b621e95f..badcc82b91 100644 --- a/extras/web/package.json +++ b/extras/web/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@repo/ui": "workspace:^", - "next": "^15.5.9", + "next": "^16.1.5", "react": "^19.2.3", "react-dom": "^19.2.3" }, From a50e100db18d3c98afdbcf701241ee8023b87984 Mon Sep 17 00:00:00 2001 From: "snyk-io[bot]" <141718529+snyk-io[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:03:21 +0700 Subject: [PATCH 09/23] fix: extras/docs/package.json to reduce vulnerabilities (#263) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-NEXT-15104645 - https://snyk.io/vuln/SNYK-JS-NEXT-15105315 Co-authored-by: snyk-io[bot] <141718529+snyk-io[bot]@users.noreply.github.com> --- extras/docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/docs/package.json b/extras/docs/package.json index 29fbc4bc74..04810e85c1 100644 --- a/extras/docs/package.json +++ b/extras/docs/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@repo/ui": "workspace:^", - "next": "^15.5.9", + "next": "^16.1.5", "react": "^19.2.3", "react-dom": "^19.2.3" }, From b26154fd19b3db24f4de427ee1513463d0bad7bb Mon Sep 17 00:00:00 2001 From: googleworkspace-bot Date: Wed, 4 Mar 2026 13:24:45 +0700 Subject: [PATCH 10/23] wagmi templates --- .changeset/config.json | 22 +- .changeset/new-elephants-travel.md | 5 + .changeset/nice-pandas-clap.md | 5 + .changeset/quick-hairs-scream.md | 6 + .changeset/spicy-bats-juggle.md | 6 + .changeset/tall-fans-mate.md | 6 + .changeset/tiny-laws-dream.md | 5 + .changeset/young-guests-care.md | 5 + .circleci/config.yml | 26 + .github/CODEOWNERS | 6 +- .github/CONTRIBUTING.md | 1 + .../DISCUSSION_TEMPLATE/connector-request.yml | 51 + .github/ISSUE_TEMPLATE/bug_report.yml | 88 + .github/ISSUE_TEMPLATE/config.yml | 14 + .github/ISSUE_TEMPLATE/docs_issue.yml | 34 + .github/README.md | 256 + .github/SECURITY.md | 6 + .github/dependabot.yml | 6 + .github/logo-dark.svg | 27 + .github/logo-light.svg | 27 + .github/pull_request_template.md | 12 + .../workflows/Vercel Preview Deployment.yml | 22 + .github/workflows/changesets.yml | 60 + .github/workflows/dependency-review.yml | 39 + .github/workflows/issue-labeled.yml | 19 + .github/workflows/jekyll-docker.yml | 23 + .github/workflows/lock-issue.yml | 16 + .github/workflows/octopusdeploy.yml | 112 + .github/workflows/pull-request.yml | 32 + .github/workflows/release.yml | 44 + .github/workflows/snapshot.yml | 32 + .github/workflows/verify.yml | 127 + .gitignore | 66 +- .npmrc | 5 + .vscode/extensions.json | 7 + .vscode/settings.json | 20 +- .vscode/workspace.code-workspace | 16 + FUNDING.json | 4 +- LICENSE | 223 +- README.md | 40 +- biome.json | 89 + index.html | 12 + package.json | 180 +- packages/cli/CHANGELOG.md | 449 + packages/cli/README.md | 13 + packages/cli/package.json | 94 + packages/cli/src/cli.ts | 53 + packages/cli/src/commands/generate.test.ts | 409 + packages/cli/src/commands/generate.ts | 409 + packages/cli/src/commands/init.test.ts | 189 + packages/cli/src/commands/init.ts | 95 + packages/cli/src/config.test.ts | 39 + packages/cli/src/config.ts | 121 + packages/cli/src/errors.ts | 57 + packages/cli/src/exports/config.test.ts | 12 + packages/cli/src/exports/config.ts | 10 + packages/cli/src/exports/index.test-d.ts | 4 + packages/cli/src/exports/index.test.ts | 14 + packages/cli/src/exports/index.ts | 14 + packages/cli/src/exports/plugins.test.ts | 20 + packages/cli/src/exports/plugins.ts | 27 + packages/cli/src/logger.test.ts | 32 + packages/cli/src/logger.ts | 37 + .../plugins/__fixtures__/foundry/.gitignore | 11 + .../plugins/__fixtures__/foundry/foundry.toml | 7 + .../__fixtures__/foundry/src/Counter.sol | 14 + .../plugins/__fixtures__/foundry/src/Foo.sol | 11 + .../plugins/__fixtures__/hardhat/.gitignore | 10 + .../hardhat/contracts/Counter.sol | 14 + .../__fixtures__/hardhat/contracts/Foo.sol | 10 + .../__fixtures__/hardhat/hardhat.config.js | 3 + .../plugins/__fixtures__/hardhat/package.json | 7 + .../__snapshots__/blockExplorer.test.ts.snap | 736 + .../__snapshots__/etherscan.test.ts.snap | 1238 + .../plugins/__snapshots__/fetch.test.ts.snap | 367 + .../__snapshots__/sourcify.test.ts.snap | 214 + packages/cli/src/plugins/actions.test.ts | 359 + packages/cli/src/plugins/actions.ts | 321 + .../cli/src/plugins/blockExplorer.test.ts | 53 + packages/cli/src/plugins/blockExplorer.ts | 107 + packages/cli/src/plugins/etherscan.test.ts | 112 + packages/cli/src/plugins/etherscan.ts | 268 + packages/cli/src/plugins/fetch.test.ts | 186 + packages/cli/src/plugins/fetch.ts | 127 + packages/cli/src/plugins/foundry.test.ts | 153 + packages/cli/src/plugins/foundry.ts | 263 + packages/cli/src/plugins/hardhat.test.ts | 85 + packages/cli/src/plugins/hardhat.ts | 235 + packages/cli/src/plugins/react.test.ts | 337 + packages/cli/src/plugins/react.ts | 312 + packages/cli/src/plugins/sourcify.test.ts | 83 + packages/cli/src/plugins/sourcify.ts | 312 + packages/cli/src/types.ts | 10 + packages/cli/src/utils/findConfig.test.ts | 42 + packages/cli/src/utils/findConfig.ts | 39 + packages/cli/src/utils/format.test.ts | 12 + packages/cli/src/utils/format.ts | 17 + .../cli/src/utils/getAddressDocString.test.ts | 40 + packages/cli/src/utils/getAddressDocString.ts | 53 + .../src/utils/getIsUsingTypeScript.test.ts | 43 + .../cli/src/utils/getIsUsingTypeScript.ts | 33 + packages/cli/src/utils/loadEnv.test.ts | 77 + packages/cli/src/utils/loadEnv.ts | 90 + packages/cli/src/utils/packages.test.ts | 19 + packages/cli/src/utils/packages.ts | 124 + packages/cli/src/utils/resolveConfig.test.ts | 83 + packages/cli/src/utils/resolveConfig.ts | 21 + packages/cli/src/version.ts | 1 + packages/cli/test/constants.ts | 32 + packages/cli/test/setup.ts | 57 + packages/cli/test/utils.ts | 292 + packages/cli/tsconfig.build.json | 9 + packages/cli/tsconfig.json | 5 + packages/cli/types/fixturez.d.ts | 18 + packages/connectors/CHANGELOG.md | 1640 + packages/connectors/README.md | 13 + packages/connectors/package.json | 71 + .../connectors/src/coinbaseWallet.test.ts | 17 + packages/connectors/src/coinbaseWallet.ts | 546 + packages/connectors/src/exports/index.test.ts | 17 + packages/connectors/src/exports/index.ts | 23 + packages/connectors/src/metaMask.test.ts | 10 + packages/connectors/src/metaMask.ts | 505 + packages/connectors/src/safe.test.ts | 23 + packages/connectors/src/safe.ts | 145 + packages/connectors/src/version.ts | 1 + packages/connectors/src/walletConnect.test.ts | 67 + packages/connectors/src/walletConnect.ts | 468 + packages/connectors/tsconfig.build.json | 8 + packages/connectors/tsconfig.json | 5 + packages/core/CHANGELOG.md | 3386 +- packages/core/README.md | 13 + packages/core/package.json | 114 +- packages/core/src/actions/call.test.ts | 149 + packages/core/src/actions/call.ts | 27 + .../codegen/createReadContract.test-d.ts | 130 + .../codegen/createReadContract.test.ts | 50 + .../src/actions/codegen/createReadContract.ts | 100 + .../codegen/createSimulateContract.test-d.ts | 211 + .../codegen/createSimulateContract.test.ts | 137 + .../actions/codegen/createSimulateContract.ts | 122 + .../createWatchContractEvent.test-d.ts | 123 + .../codegen/createWatchContractEvent.test.ts | 41 + .../codegen/createWatchContractEvent.ts | 88 + .../codegen/createWriteContract.test-d.ts | 129 + .../codegen/createWriteContract.test.ts | 11 + .../actions/codegen/createWriteContract.ts | 145 + packages/core/src/actions/connect.test-d.ts | 48 + packages/core/src/actions/connect.test.ts | 71 + packages/core/src/actions/connect.ts | 110 + .../core/src/actions/deployContract.test-d.ts | 71 + .../core/src/actions/deployContract.test.ts | 67 + packages/core/src/actions/deployContract.ts | 87 + packages/core/src/actions/disconnect.test.ts | 33 + packages/core/src/actions/disconnect.ts | 71 + .../src/actions/estimateFeesPerGas.test-d.ts | 41 + .../src/actions/estimateFeesPerGas.test.ts | 16 + .../core/src/actions/estimateFeesPerGas.ts | 87 + .../core/src/actions/estimateGas.test-d.ts | 47 + packages/core/src/actions/estimateGas.test.ts | 47 + packages/core/src/actions/estimateGas.ts | 73 + .../estimateMaxPriorityFeePerGas.test.ts | 16 + .../actions/estimateMaxPriorityFeePerGas.ts | 49 + .../core/src/actions/getAccount.test-d.ts | 69 + packages/core/src/actions/getAccount.test.ts | 37 + packages/core/src/actions/getAccount.ts | 126 + packages/core/src/actions/getBalance.test.ts | 102 + packages/core/src/actions/getBalance.ts | 149 + packages/core/src/actions/getBlock.test-d.ts | 35 + packages/core/src/actions/getBlock.test.ts | 153 + packages/core/src/actions/getBlock.ts | 74 + .../core/src/actions/getBlockNumber.test.ts | 8 + packages/core/src/actions/getBlockNumber.ts | 36 + .../actions/getBlockTransactionCount.test.ts | 61 + .../src/actions/getBlockTransactionCount.ts | 44 + packages/core/src/actions/getBytecode.test.ts | 45 + packages/core/src/actions/getBytecode.ts | 30 + .../core/src/actions/getCallsStatus.test.ts | 70 + packages/core/src/actions/getCallsStatus.ts | 27 + .../core/src/actions/getCapabilities.test.ts | 64 + packages/core/src/actions/getCapabilities.ts | 39 + packages/core/src/actions/getChainId.test.ts | 10 + packages/core/src/actions/getChainId.ts | 11 + packages/core/src/actions/getChains.test-d.ts | 12 + packages/core/src/actions/getChains.test.ts | 14 + packages/core/src/actions/getChains.ts | 21 + packages/core/src/actions/getClient.test-d.ts | 27 + packages/core/src/actions/getClient.test.ts | 17 + packages/core/src/actions/getClient.ts | 52 + .../core/src/actions/getConnections.test.ts | 15 + packages/core/src/actions/getConnections.ts | 16 + .../src/actions/getConnectorClient.test-d.ts | 19 + .../src/actions/getConnectorClient.test.ts | 106 + .../core/src/actions/getConnectorClient.ts | 147 + .../core/src/actions/getConnectors.test.ts | 8 + packages/core/src/actions/getConnectors.ts | 17 + .../core/src/actions/getEnsAddress.test.ts | 12 + packages/core/src/actions/getEnsAddress.ts | 30 + .../core/src/actions/getEnsAvatar.test.ts | 12 + packages/core/src/actions/getEnsAvatar.ts | 30 + packages/core/src/actions/getEnsName.test.ts | 12 + packages/core/src/actions/getEnsName.ts | 30 + .../core/src/actions/getEnsResolver.test.ts | 14 + packages/core/src/actions/getEnsResolver.ts | 30 + packages/core/src/actions/getEnsText.test.ts | 13 + packages/core/src/actions/getEnsText.ts | 30 + .../core/src/actions/getFeeHistory.test.ts | 63 + packages/core/src/actions/getFeeHistory.ts | 36 + packages/core/src/actions/getGasPrice.test.ts | 21 + packages/core/src/actions/getGasPrice.ts | 35 + packages/core/src/actions/getProof.test.ts | 16 + packages/core/src/actions/getProof.ts | 30 + .../src/actions/getPublicClient.test-d.ts | 27 + .../core/src/actions/getPublicClient.test.ts | 17 + packages/core/src/actions/getPublicClient.ts | 52 + .../core/src/actions/getStorageAt.test.ts | 59 + packages/core/src/actions/getStorageAt.ts | 30 + packages/core/src/actions/getToken.test.ts | 84 + packages/core/src/actions/getToken.ts | 141 + .../core/src/actions/getTransaction.test-d.ts | 29 + .../core/src/actions/getTransaction.test.ts | 36 + packages/core/src/actions/getTransaction.ts | 51 + .../getTransactionConfirmations.test-d.ts | 85 + .../getTransactionConfirmations.test.ts | 25 + .../actions/getTransactionConfirmations.ts | 52 + .../src/actions/getTransactionCount.test.ts | 50 + .../core/src/actions/getTransactionCount.ts | 34 + .../actions/getTransactionReceipt.test-d.ts | 36 + .../src/actions/getTransactionReceipt.test.ts | 35 + .../core/src/actions/getTransactionReceipt.ts | 57 + .../src/actions/getWalletClient.test-d.ts | 22 + .../core/src/actions/getWalletClient.test.ts | 24 + packages/core/src/actions/getWalletClient.ts | 50 + packages/core/src/actions/multicall.test-d.ts | 106 + packages/core/src/actions/multicall.test.ts | 46 + packages/core/src/actions/multicall.ts | 42 + .../prepareTransactionRequest.test-d.ts | 80 + .../actions/prepareTransactionRequest.test.ts | 108 + .../src/actions/prepareTransactionRequest.ts | 125 + .../core/src/actions/readContract.test-d.ts | 74 + .../core/src/actions/readContract.test.ts | 37 + packages/core/src/actions/readContract.ts | 58 + .../core/src/actions/readContracts.test-d.ts | 118 + .../core/src/actions/readContracts.test.ts | 678 + packages/core/src/actions/readContracts.ts | 96 + packages/core/src/actions/reconnect.test.ts | 119 + packages/core/src/actions/reconnect.ts | 127 + packages/core/src/actions/sendCalls.test.ts | 121 + packages/core/src/actions/sendCalls.ts | 74 + .../src/actions/sendTransaction.test-d.ts | 50 + .../core/src/actions/sendTransaction.test.ts | 105 + packages/core/src/actions/sendTransaction.ts | 86 + .../core/src/actions/showCallsStatus.test.ts | 36 + packages/core/src/actions/showCallsStatus.ts | 27 + packages/core/src/actions/signMessage.test.ts | 67 + packages/core/src/actions/signMessage.ts | 51 + .../core/src/actions/signTypedData.test-d.ts | 31 + .../core/src/actions/signTypedData.test.ts | 85 + packages/core/src/actions/signTypedData.ts | 60 + .../src/actions/simulateContract.test-d.ts | 160 + .../core/src/actions/simulateContract.test.ts | 84 + packages/core/src/actions/simulateContract.ts | 166 + .../core/src/actions/switchAccount.test.ts | 32 + packages/core/src/actions/switchAccount.ts | 45 + packages/core/src/actions/switchChain.test.ts | 73 + packages/core/src/actions/switchChain.ts | 83 + .../core/src/actions/verifyMessage.test.ts | 72 + packages/core/src/actions/verifyMessage.ts | 30 + .../core/src/actions/verifyTypedData.test.ts | 42 + packages/core/src/actions/verifyTypedData.ts | 40 + .../src/actions/waitForCallsStatus.test.ts | 77 + .../core/src/actions/waitForCallsStatus.ts | 27 + .../waitForTransactionReceipt.test-d.ts | 36 + .../actions/waitForTransactionReceipt.test.ts | 58 + .../src/actions/waitForTransactionReceipt.ts | 86 + .../core/src/actions/watchAccount.test.ts | 38 + packages/core/src/actions/watchAccount.ts | 33 + packages/core/src/actions/watchAsset.test.ts | 23 + packages/core/src/actions/watchAsset.ts | 44 + .../src/actions/watchBlockNumber.test-d.ts | 56 + .../core/src/actions/watchBlockNumber.test.ts | 27 + packages/core/src/actions/watchBlockNumber.ts | 78 + .../core/src/actions/watchBlocks.test-d.ts | 59 + packages/core/src/actions/watchBlocks.test.ts | 30 + packages/core/src/actions/watchBlocks.ts | 90 + .../core/src/actions/watchChainId.test.ts | 26 + packages/core/src/actions/watchChainId.ts | 20 + packages/core/src/actions/watchChains.test.ts | 37 + packages/core/src/actions/watchChains.ts | 29 + .../core/src/actions/watchClient.test-d.ts | 15 + packages/core/src/actions/watchClient.test.ts | 23 + packages/core/src/actions/watchClient.ts | 35 + .../core/src/actions/watchConnections.test.ts | 25 + packages/core/src/actions/watchConnections.ts | 26 + .../core/src/actions/watchConnectors.test.ts | 27 + packages/core/src/actions/watchConnectors.ts | 22 + .../src/actions/watchContractEvent.test-d.ts | 142 + .../src/actions/watchContractEvent.test.ts | 96 + .../core/src/actions/watchContractEvent.ts | 102 + .../watchPendingTransactions.test-d.ts | 56 + .../actions/watchPendingTransactions.test.ts | 49 + .../src/actions/watchPendingTransactions.ts | 82 + .../src/actions/watchPublicClient.test-d.ts | 15 + .../src/actions/watchPublicClient.test.ts | 23 + .../core/src/actions/watchPublicClient.ts | 38 + .../core/src/actions/writeContract.test-d.ts | 152 + packages/core/src/actions/writeContract.ts | 114 + .../src/connectors/createConnector.test.ts | 31 + .../core/src/connectors/createConnector.ts | 93 + packages/core/src/connectors/injected.test.ts | 25 + packages/core/src/connectors/injected.ts | 697 + packages/core/src/connectors/mock.test.ts | 112 + packages/core/src/connectors/mock.ts | 315 + packages/core/src/createConfig.test-d.ts | 110 + packages/core/src/createConfig.test.ts | 440 + packages/core/src/createConfig.ts | 653 + packages/core/src/createEmitter.test.ts | 19 + packages/core/src/createEmitter.ts | 68 + packages/core/src/createStorage.test-d.ts | 74 + packages/core/src/createStorage.test.ts | 45 + packages/core/src/createStorage.ts | 112 + packages/core/src/errors/base.test.ts | 155 + packages/core/src/errors/base.ts | 74 + packages/core/src/errors/config.test.ts | 68 + packages/core/src/errors/config.ts | 103 + packages/core/src/errors/connector.test.ts | 24 + packages/core/src/errors/connector.ts | 23 + .../actions/writeContracts.test.ts | 99 + .../experimental/actions/writeContracts.ts | 78 + .../experimental/query/writeContracts.test.ts | 15 + .../src/experimental/query/writeContracts.ts | 70 + packages/core/src/exports/actions.test.ts | 86 + packages/core/src/exports/actions.ts | 460 + packages/core/src/exports/chains.ts | 7 + packages/core/src/exports/codegen.test.ts | 14 + packages/core/src/exports/codegen.ts | 24 + packages/core/src/exports/experimental.ts | 158 + packages/core/src/exports/index.test.ts | 117 + packages/core/src/exports/index.ts | 594 + packages/core/src/exports/internal.test.ts | 15 + packages/core/src/exports/internal.ts | 52 + packages/core/src/exports/query.test.ts | 97 + packages/core/src/exports/query.ts | 434 + packages/core/src/hydrate.test.ts | 114 + packages/core/src/hydrate.ts | 62 + packages/core/src/query/call.test.ts | 306 + packages/core/src/query/call.ts | 51 + packages/core/src/query/connect.test.ts | 15 + packages/core/src/query/connect.ts | 70 + .../core/src/query/deployContract.test.ts | 15 + packages/core/src/query/deployContract.ts | 73 + packages/core/src/query/disconnect.test.ts | 15 + packages/core/src/query/disconnect.ts | 43 + .../core/src/query/estimateFeesPerGas.test.ts | 32 + packages/core/src/query/estimateFeesPerGas.ts | 56 + packages/core/src/query/estimateGas.test-d.ts | 55 + packages/core/src/query/estimateGas.test.ts | 25 + packages/core/src/query/estimateGas.ts | 56 + .../estimateMaxPriorityFeePerGas.test.ts | 36 + .../src/query/estimateMaxPriorityFeePerGas.ts | 51 + packages/core/src/query/getBalance.test.ts | 63 + packages/core/src/query/getBalance.ts | 53 + packages/core/src/query/getBlock.test.ts | 32 + packages/core/src/query/getBlock.ts | 84 + .../core/src/query/getBlockNumber.test.ts | 34 + packages/core/src/query/getBlockNumber.ts | 55 + .../query/getBlockTransactionCount.test.ts | 50 + .../src/query/getBlockTransactionCount.ts | 62 + packages/core/src/query/getBytecode.test.ts | 82 + packages/core/src/query/getBytecode.ts | 49 + .../core/src/query/getCallsStatus.test.ts | 23 + packages/core/src/query/getCallsStatus.ts | 50 + .../core/src/query/getCapabilities.test.ts | 36 + packages/core/src/query/getCapabilities.ts | 65 + .../core/src/query/getConnectorClient.test.ts | 37 + packages/core/src/query/getConnectorClient.ts | 69 + packages/core/src/query/getEnsAddress.test.ts | 32 + packages/core/src/query/getEnsAddress.ts | 49 + packages/core/src/query/getEnsAvatar.test.ts | 32 + packages/core/src/query/getEnsAvatar.ts | 49 + packages/core/src/query/getEnsName.test.ts | 32 + packages/core/src/query/getEnsName.ts | 49 + .../core/src/query/getEnsResolver.test.ts | 32 + packages/core/src/query/getEnsResolver.ts | 49 + packages/core/src/query/getEnsText.test.ts | 46 + packages/core/src/query/getEnsText.ts | 49 + packages/core/src/query/getFeeHistory.test.ts | 128 + packages/core/src/query/getFeeHistory.ts | 69 + packages/core/src/query/getGasPrice.test.ts | 32 + packages/core/src/query/getGasPrice.ts | 54 + packages/core/src/query/getProof.test.ts | 106 + packages/core/src/query/getProof.ts | 50 + packages/core/src/query/getStorageAt.test.ts | 90 + packages/core/src/query/getStorageAt.ts | 49 + packages/core/src/query/getToken.test.ts | 32 + packages/core/src/query/getToken.ts | 49 + .../core/src/query/getTransaction.test.ts | 32 + packages/core/src/query/getTransaction.ts | 69 + .../query/getTransactionConfirmations.test.ts | 42 + .../src/query/getTransactionConfirmations.ts | 78 + .../src/query/getTransactionCount.test.ts | 82 + .../core/src/query/getTransactionCount.ts | 55 + .../src/query/getTransactionReceipt.test.ts | 42 + .../core/src/query/getTransactionReceipt.ts | 60 + .../core/src/query/getWalletClient.test.ts | 37 + packages/core/src/query/getWalletClient.ts | 65 + .../src/query/infiniteReadContracts.test-d.ts | 201 + .../src/query/infiniteReadContracts.test.ts | 60 + .../core/src/query/infiniteReadContracts.ts | 127 + .../query/prepareTransactionRequest.test.ts | 241 + .../src/query/prepareTransactionRequest.ts | 101 + .../core/src/query/readContract.test-d.ts | 15 + packages/core/src/query/readContract.test.ts | 29 + packages/core/src/query/readContract.ts | 93 + .../core/src/query/readContracts.test-d.ts | 58 + packages/core/src/query/readContracts.test.ts | 38 + packages/core/src/query/readContracts.ts | 98 + packages/core/src/query/reconnect.test.ts | 15 + packages/core/src/query/reconnect.ts | 42 + packages/core/src/query/sendCalls.test.ts | 15 + packages/core/src/query/sendCalls.ts | 67 + .../core/src/query/sendTransaction.test.ts | 15 + packages/core/src/query/sendTransaction.ts | 65 + .../core/src/query/showCallsStatus.test.ts | 15 + packages/core/src/query/showCallsStatus.ts | 57 + packages/core/src/query/signMessage.test.ts | 15 + packages/core/src/query/signMessage.ts | 42 + packages/core/src/query/signTypedData.test.ts | 15 + packages/core/src/query/signTypedData.ts | 75 + .../core/src/query/simulateContract.test-d.ts | 81 + .../core/src/query/simulateContract.test.ts | 31 + packages/core/src/query/simulateContract.ts | 132 + packages/core/src/query/switchAccount.test.ts | 15 + packages/core/src/query/switchAccount.ts | 52 + packages/core/src/query/switchChain.test.ts | 15 + packages/core/src/query/switchChain.ts | 67 + packages/core/src/query/types.ts | 84 + packages/core/src/query/utils.test.ts | 20 + packages/core/src/query/utils.ts | 73 + packages/core/src/query/verifyMessage.test.ts | 104 + packages/core/src/query/verifyMessage.ts | 56 + .../core/src/query/verifyTypedData.test.ts | 284 + packages/core/src/query/verifyTypedData.ts | 82 + .../core/src/query/waitForCallsStatus.test.ts | 23 + packages/core/src/query/waitForCallsStatus.ts | 53 + .../query/waitForTransactionReceipt.test.ts | 18 + .../src/query/waitForTransactionReceipt.ts | 71 + packages/core/src/query/watchAsset.test.ts | 15 + packages/core/src/query/watchAsset.ts | 42 + .../core/src/query/writeContract.test-d.ts | 145 + packages/core/src/query/writeContract.test.ts | 15 + packages/core/src/query/writeContract.ts | 116 + .../core/src/transports/connector.test.ts | 97 + packages/core/src/transports/connector.ts | 87 + packages/core/src/transports/fallback.test.ts | 63 + packages/core/src/transports/fallback.ts | 10 + packages/core/src/types/chain.test-d.ts | 33 + packages/core/src/types/chain.ts | 26 + packages/core/src/types/properties.ts | 23 + packages/core/src/types/register.ts | 9 + packages/core/src/types/unit.ts | 1 + packages/core/src/types/utils.test-d.ts | 40 + packages/core/src/types/utils.ts | 101 + packages/core/src/utils/cookie.test.ts | 69 + packages/core/src/utils/cookie.ts | 33 + packages/core/src/utils/deepEqual.test.ts | 40 + packages/core/src/utils/deepEqual.ts | 43 + packages/core/src/utils/deserialize.test.ts | 114 + packages/core/src/utils/deserialize.ts | 10 + .../core/src/utils/extractRpcUrls.test.ts | 92 + packages/core/src/utils/extractRpcUrls.ts | 19 + packages/core/src/utils/getAction.test.ts | 49 + packages/core/src/utils/getAction.ts | 44 + packages/core/src/utils/getUnit.test.ts | 9 + packages/core/src/utils/getUnit.ts | 9 + packages/core/src/utils/getVersion.test.ts | 7 + packages/core/src/utils/getVersion.ts | 3 + .../core/src/utils/normalizeChainId.test.ts | 24 + packages/core/src/utils/normalizeChainId.ts | 13 + packages/core/src/utils/serialize.test.ts | 241 + packages/core/src/utils/serialize.ts | 116 + packages/core/src/utils/uid.ts | 49 + packages/core/src/version.ts | 2 +- packages/core/test/setup.ts | 5 + packages/core/tsconfig.build.json | 8 + packages/core/tsconfig.json | 5 + packages/create-wagmi/CHANGELOG.md | 278 + packages/create-wagmi/README.md | 17 + packages/create-wagmi/package.json | 49 + packages/create-wagmi/src/cli.test.ts | 151 + packages/create-wagmi/src/cli.ts | 284 + packages/create-wagmi/src/frameworks.ts | 66 + packages/create-wagmi/src/index.test-d.ts | 4 + packages/create-wagmi/src/utils.ts | 79 + packages/create-wagmi/src/version.ts | 1 + .../create-wagmi/templates/next/README.md | 1 + .../create-wagmi/templates/next/_env.local | 2 + .../create-wagmi/templates/next/_gitignore | 35 + packages/create-wagmi/templates/next/_npmrc | 1 + .../create-wagmi/templates/next/next-env.d.ts | 5 + .../templates/next/next.config.js | 4 + .../create-wagmi/templates/next/package.json | 32 + .../templates/next/src/app/globals.css | 21 + .../templates/next/src/app/layout.tsx | 30 + .../templates/next/src/app/page.tsx | 48 + .../templates/next/src/app/providers.tsx | 23 + .../create-wagmi/templates/next/src/wagmi.ts | 28 + .../create-wagmi/templates/next/tsconfig.json | 27 + .../create-wagmi/templates/nuxt/_env.local | 3 + .../create-wagmi/templates/nuxt/_gitignore | 24 + packages/create-wagmi/templates/nuxt/_npmrc | 2 + packages/create-wagmi/templates/nuxt/app.vue | 28 + .../templates/nuxt/components/Account.vue | 22 + .../templates/nuxt/components/Connect.vue | 19 + .../templates/nuxt/nuxt.config.ts | 7 + .../create-wagmi/templates/nuxt/package.json | 21 + .../templates/nuxt/plugins/wagmi.ts | 10 + .../templates/nuxt/server/tsconfig.json | 3 + .../create-wagmi/templates/nuxt/tsconfig.json | 4 + packages/create-wagmi/templates/nuxt/wagmi.ts | 29 + .../templates/vite-react/README.md | 1 + .../templates/vite-react/_env.local | 1 + .../templates/vite-react/_gitignore | 24 + .../create-wagmi/templates/vite-react/_npmrc | 1 + .../templates/vite-react/biome.json | 13 + .../templates/vite-react/index.html | 12 + .../templates/vite-react/package.json | 29 + .../templates/vite-react/src/App.tsx | 46 + .../templates/vite-react/src/index.css | 21 + .../templates/vite-react/src/main.tsx | 24 + .../templates/vite-react/src/vite-env.d.ts | 1 + .../templates/vite-react/src/wagmi.ts | 22 + .../templates/vite-react/tsconfig.json | 25 + .../templates/vite-react/tsconfig.node.json | 10 + .../templates/vite-react/vite.config.ts | 7 + .../templates/vite-vanilla/_env.local | 1 + .../templates/vite-vanilla/_gitignore | 24 + .../templates/vite-vanilla/_npmrc | 1 + .../templates/vite-vanilla/index.html | 12 + .../templates/vite-vanilla/package.json | 24 + .../templates/vite-vanilla/src/main.ts | 89 + .../templates/vite-vanilla/src/style.css | 21 + .../templates/vite-vanilla/src/vite-env.d.ts | 1 + .../templates/vite-vanilla/src/wagmi.ts | 16 + .../templates/vite-vanilla/tsconfig.json | 23 + .../create-wagmi/templates/vite-vue/README.md | 1 + .../templates/vite-vue/_env.local | 1 + .../templates/vite-vue/_gitignore | 24 + .../create-wagmi/templates/vite-vue/_npmrc | 1 + .../templates/vite-vue/biome.json | 13 + .../templates/vite-vue/index.html | 12 + .../templates/vite-vue/package.json | 24 + .../templates/vite-vue/src/App.vue | 19 + .../vite-vue/src/components/Account.vue | 22 + .../vite-vue/src/components/Connect.vue | 19 + .../templates/vite-vue/src/main.ts | 17 + .../templates/vite-vue/src/style.css | 21 + .../templates/vite-vue/src/vite-env.d.ts | 1 + .../templates/vite-vue/src/wagmi.ts | 25 + .../templates/vite-vue/tsconfig.json | 25 + .../templates/vite-vue/tsconfig.node.json | 11 + .../templates/vite-vue/vite.config.ts | 7 + packages/create-wagmi/tsconfig.build.json | 8 + packages/create-wagmi/tsconfig.json | 5 + packages/react/CHANGELOG.md | 5037 ++ packages/react/README.md | 13 + packages/react/package.json | 119 + packages/react/src/context.test.tsx | 101 + packages/react/src/context.ts | 28 + packages/react/src/errors/base.test.ts | 155 + packages/react/src/errors/base.ts | 14 + packages/react/src/errors/context.test.ts | 12 + packages/react/src/errors/context.ts | 13 + .../hooks/useWriteContracts.test.ts | 45 + .../experimental/hooks/useWriteContracts.ts | 85 + packages/react/src/exports/actions.test.ts | 86 + packages/react/src/exports/actions.ts | 7 + .../src/exports/actions/experimental.test.ts | 25 + .../react/src/exports/actions/experimental.ts | 7 + packages/react/src/exports/chains.ts | 7 + packages/react/src/exports/codegen.test.ts | 18 + packages/react/src/exports/codegen.ts | 35 + packages/react/src/exports/connectors.test.ts | 17 + packages/react/src/exports/connectors.ts | 7 + packages/react/src/exports/experimental.ts | 58 + packages/react/src/exports/index.test.ts | 111 + packages/react/src/exports/index.ts | 487 + packages/react/src/exports/query.test.ts | 100 + packages/react/src/exports/query.ts | 19 + .../codegen/createUseReadContract.test-d.ts | 152 + .../codegen/createUseReadContract.test.ts | 177 + .../hooks/codegen/createUseReadContract.ts | 127 + .../createUseSimulateContract.test-d.ts | 199 + .../codegen/createUseSimulateContract.test.ts | 258 + .../codegen/createUseSimulateContract.ts | 120 + .../createUseWatchContractEvent.test-d.ts | 123 + .../createUseWatchContractEvent.test.ts | 44 + .../codegen/createUseWatchContractEvent.ts | 101 + .../codegen/createUseWriteContract.test-d.ts | 153 + .../codegen/createUseWriteContract.test.ts | 13 + .../hooks/codegen/createUseWriteContract.ts | 297 + packages/react/src/hooks/useAccount.test-d.ts | 68 + packages/react/src/hooks/useAccount.test.ts | 29 + packages/react/src/hooks/useAccount.ts | 31 + .../react/src/hooks/useAccountEffect.test.ts | 77 + packages/react/src/hooks/useAccountEffect.ts | 62 + packages/react/src/hooks/useBalance.test-d.ts | 14 + packages/react/src/hooks/useBalance.test.ts | 312 + packages/react/src/hooks/useBalance.ts | 54 + packages/react/src/hooks/useBlock.test-d.ts | 64 + packages/react/src/hooks/useBlock.test.ts | 67 + packages/react/src/hooks/useBlock.ts | 131 + .../react/src/hooks/useBlockNumber.test-d.ts | 65 + .../react/src/hooks/useBlockNumber.test.ts | 68 + packages/react/src/hooks/useBlockNumber.ts | 97 + .../hooks/useBlockTransactionCount.test-d.ts | 14 + .../hooks/useBlockTransactionCount.test.ts | 231 + .../src/hooks/useBlockTransactionCount.ts | 67 + .../react/src/hooks/useBytecode.test-d.ts | 15 + packages/react/src/hooks/useBytecode.test.ts | 291 + packages/react/src/hooks/useBytecode.ts | 57 + packages/react/src/hooks/useCall.test-d.ts | 14 + packages/react/src/hooks/useCall.test.ts | 224 + packages/react/src/hooks/useCall.ts | 55 + .../react/src/hooks/useCallsStatus.test.ts | 101 + packages/react/src/hooks/useCallsStatus.ts | 52 + .../react/src/hooks/useCapabilities.test.ts | 167 + packages/react/src/hooks/useCapabilities.ts | 62 + packages/react/src/hooks/useChainId.test-d.ts | 14 + packages/react/src/hooks/useChainId.test.ts | 24 + packages/react/src/hooks/useChainId.ts | 32 + packages/react/src/hooks/useChains.test.ts | 31 + packages/react/src/hooks/useChains.ts | 32 + packages/react/src/hooks/useClient.test-d.ts | 40 + packages/react/src/hooks/useClient.test.ts | 30 + packages/react/src/hooks/useClient.ts | 49 + packages/react/src/hooks/useConfig.test-d.ts | 16 + packages/react/src/hooks/useConfig.test.ts | 23 + packages/react/src/hooks/useConfig.ts | 22 + packages/react/src/hooks/useConnect.test-d.ts | 124 + packages/react/src/hooks/useConnect.test.ts | 35 + packages/react/src/hooks/useConnect.ts | 90 + .../react/src/hooks/useConnections.test.ts | 25 + packages/react/src/hooks/useConnections.ts | 28 + .../src/hooks/useConnectorClient.test-d.ts | 12 + .../src/hooks/useConnectorClient.test.tsx | 239 + .../react/src/hooks/useConnectorClient.ts | 113 + .../react/src/hooks/useConnectors.test.ts | 30 + packages/react/src/hooks/useConnectors.ts | 34 + .../src/hooks/useDeployContract.test-d.ts | 105 + .../react/src/hooks/useDeployContract.test.ts | 24 + packages/react/src/hooks/useDeployContract.ts | 78 + .../react/src/hooks/useDisconnect.test-d.ts | 87 + .../react/src/hooks/useDisconnect.test.ts | 32 + packages/react/src/hooks/useDisconnect.ts | 70 + .../react/src/hooks/useEnsAddress.test.ts | 50 + packages/react/src/hooks/useEnsAddress.ts | 58 + packages/react/src/hooks/useEnsAvatar.test.ts | 50 + packages/react/src/hooks/useEnsAvatar.ts | 58 + packages/react/src/hooks/useEnsName.test.ts | 50 + packages/react/src/hooks/useEnsName.ts | 54 + .../react/src/hooks/useEnsResolver.test.ts | 50 + packages/react/src/hooks/useEnsResolver.ts | 58 + packages/react/src/hooks/useEnsText.test.ts | 150 + packages/react/src/hooks/useEnsText.ts | 54 + .../src/hooks/useEstimateFeesPerGas.test-d.ts | 49 + .../src/hooks/useEstimateFeesPerGas.test.ts | 19 + .../react/src/hooks/useEstimateFeesPerGas.ts | 62 + .../react/src/hooks/useEstimateGas.test-d.ts | 14 + .../react/src/hooks/useEstimateGas.test.ts | 139 + packages/react/src/hooks/useEstimateGas.ts | 70 + .../useEstimateMaxPriorityFeePerGas.test-d.ts | 13 + .../useEstimateMaxPriorityFeePerGas.test.ts | 96 + .../hooks/useEstimateMaxPriorityFeePerGas.ts | 61 + .../react/src/hooks/useFeeHistory.test-d.ts | 14 + .../react/src/hooks/useFeeHistory.test.ts | 452 + packages/react/src/hooks/useFeeHistory.ts | 64 + .../react/src/hooks/useGasPrice.test-d.ts | 14 + packages/react/src/hooks/useGasPrice.test.ts | 103 + packages/react/src/hooks/useGasPrice.ts | 62 + .../hooks/useInfiniteReadContracts.test-d.ts | 44 + .../hooks/useInfiniteReadContracts.test.ts | 91 + .../src/hooks/useInfiniteReadContracts.ts | 89 + .../usePrepareTransactionRequest.test-d.ts | 27 + .../usePrepareTransactionRequest.test.ts | 81 + .../src/hooks/usePrepareTransactionRequest.ts | 102 + packages/react/src/hooks/useProof.test-d.ts | 14 + packages/react/src/hooks/useProof.test.ts | 163 + packages/react/src/hooks/useProof.ts | 56 + .../react/src/hooks/usePublicClient.test-d.ts | 40 + .../react/src/hooks/usePublicClient.test.ts | 30 + packages/react/src/hooks/usePublicClient.ts | 51 + .../react/src/hooks/useReadContract.test-d.ts | 96 + .../react/src/hooks/useReadContract.test.ts | 194 + packages/react/src/hooks/useReadContract.ts | 99 + .../src/hooks/useReadContracts.test-d.ts | 93 + .../react/src/hooks/useReadContracts.test.ts | 262 + packages/react/src/hooks/useReadContracts.ts | 91 + .../react/src/hooks/useReconnect.test-d.ts | 154 + packages/react/src/hooks/useReconnect.test.ts | 83 + packages/react/src/hooks/useReconnect.ts | 67 + packages/react/src/hooks/useSendCalls.test.ts | 44 + packages/react/src/hooks/useSendCalls.ts | 75 + .../src/hooks/useSendTransaction.test-d.ts | 78 + .../src/hooks/useSendTransaction.test.ts | 25 + .../react/src/hooks/useSendTransaction.ts | 79 + .../react/src/hooks/useShowCallsStatus.ts | 76 + .../react/src/hooks/useSignMessage.test-d.ts | 62 + .../react/src/hooks/useSignMessage.test.ts | 43 + packages/react/src/hooks/useSignMessage.ts | 65 + .../src/hooks/useSignTypedData.test-d.ts | 93 + .../react/src/hooks/useSignTypedData.test.ts | 56 + packages/react/src/hooks/useSignTypedData.ts | 66 + .../src/hooks/useSimulateContract.test-d.ts | 104 + .../src/hooks/useSimulateContract.test.ts | 95 + .../react/src/hooks/useSimulateContract.ts | 117 + .../react/src/hooks/useStorageAt.test-d.ts | 15 + packages/react/src/hooks/useStorageAt.test.ts | 299 + packages/react/src/hooks/useStorageAt.ts | 57 + .../src/hooks/useSwitchAccount.test-d.ts | 87 + .../react/src/hooks/useSwitchAccount.test.ts | 44 + packages/react/src/hooks/useSwitchAccount.ts | 84 + .../react/src/hooks/useSwitchChain.test-d.ts | 118 + .../react/src/hooks/useSwitchChain.test.ts | 114 + packages/react/src/hooks/useSwitchChain.ts | 82 + .../useSyncExternalStoreWithTracked.test.tsx | 275 + .../hooks/useSyncExternalStoreWithTracked.ts | 67 + packages/react/src/hooks/useToken.test-d.ts | 14 + packages/react/src/hooks/useToken.test.ts | 59 + packages/react/src/hooks/useToken.ts | 60 + .../react/src/hooks/useTransaction.test-d.ts | 14 + .../react/src/hooks/useTransaction.test.ts | 72 + packages/react/src/hooks/useTransaction.ts | 72 + .../useTransactionConfirmations.test-d.ts | 14 + .../hooks/useTransactionConfirmations.test.ts | 215 + .../src/hooks/useTransactionConfirmations.ts | 66 + .../src/hooks/useTransactionCount.test-d.ts | 14 + .../src/hooks/useTransactionCount.test.ts | 238 + .../react/src/hooks/useTransactionCount.ts | 59 + .../src/hooks/useTransactionReceipt.test-d.ts | 14 + .../src/hooks/useTransactionReceipt.test.ts | 237 + .../react/src/hooks/useTransactionReceipt.ts | 69 + .../src/hooks/useVerifyMessage.test-d.ts | 14 + .../react/src/hooks/useVerifyMessage.test.ts | 318 + packages/react/src/hooks/useVerifyMessage.ts | 59 + .../src/hooks/useVerifyTypedData.test-d.ts | 40 + .../src/hooks/useVerifyTypedData.test.ts | 481 + .../react/src/hooks/useVerifyTypedData.ts | 81 + .../src/hooks/useWaitForCallsStatus.test.ts | 101 + .../react/src/hooks/useWaitForCallsStatus.ts | 54 + .../useWaitForTransactionReceipt.test-d.ts | 14 + .../useWaitForTransactionReceipt.test.ts | 77 + .../src/hooks/useWaitForTransactionReceipt.ts | 74 + .../react/src/hooks/useWalletClient.test-d.ts | 12 + .../react/src/hooks/useWalletClient.test.tsx | 222 + packages/react/src/hooks/useWalletClient.ts | 116 + .../react/src/hooks/useWatchAsset.test-d.ts | 66 + .../react/src/hooks/useWatchAsset.test.ts | 27 + packages/react/src/hooks/useWatchAsset.ts | 65 + .../src/hooks/useWatchBlockNumber.test-d.ts | 71 + .../src/hooks/useWatchBlockNumber.test.ts | 28 + .../react/src/hooks/useWatchBlockNumber.ts | 65 + .../react/src/hooks/useWatchBlocks.test-d.ts | 73 + .../react/src/hooks/useWatchBlocks.test.ts | 31 + packages/react/src/hooks/useWatchBlocks.ts | 79 + .../src/hooks/useWatchContractEvent.test-d.ts | 128 + .../src/hooks/useWatchContractEvent.test.ts | 86 + .../react/src/hooks/useWatchContractEvent.ts | 85 + .../useWatchPendingTransactions.test-d.ts | 67 + .../hooks/useWatchPendingTransactions.test.ts | 49 + .../src/hooks/useWatchPendingTransactions.ts | 67 + .../src/hooks/useWriteContract.test-d.ts | 185 + .../react/src/hooks/useWriteContract.test.ts | 25 + packages/react/src/hooks/useWriteContract.ts | 87 + packages/react/src/hydrate.ts | 36 + packages/react/src/types/properties.ts | 51 + packages/react/src/utils/getVersion.test.ts | 7 + packages/react/src/utils/getVersion.ts | 3 + packages/react/src/utils/query.ts | 145 + packages/react/src/version.ts | 1 + packages/react/test/setup.ts | 8 + packages/react/tsconfig.build.json | 8 + packages/react/tsconfig.json | 8 + packages/register-tests/react/package.json | 16 + packages/register-tests/react/src/config.ts | 26 + .../src/createUseSimulateContract.test-d.ts | 39 + .../src/createUseWriteContract.test-d.ts | 66 + .../react/src/useAccount.test-d.ts | 15 + .../react/src/useBlock.test-d.ts | 27 + .../react/src/useChainId.test-d.ts | 15 + .../react/src/useChains.test-d.ts | 11 + .../react/src/useClient.test-d.ts | 38 + .../react/src/useConfig.test-d.ts | 17 + .../react/src/useConnect.test-d.ts | 13 + .../usePrepareTransactionRequest.test-d.ts | 42 + .../react/src/usePublicClient.ts | 38 + .../react/src/useReadContract.test-d.ts | 24 + .../react/src/useReadContracts.test-d.ts | 45 + .../react/src/useSendTransaction.test-d.ts | 55 + .../react/src/useSimulateContract.test-d.ts | 94 + .../react/src/useSwitchChain.test-d.ts | 25 + .../react/src/useTransaction.test-d.ts | 23 + .../src/useTransactionConfirmations.test-d.ts | 66 + .../react/src/useTransactionReceipt.test-d.ts | 31 + .../react/src/useWaitForTransactionReceipt.ts | 31 + .../react/src/useWriteContract.test-d.ts | 65 + packages/register-tests/react/tsconfig.json | 5 + packages/register-tests/vue/package.json | 13 + packages/register-tests/vue/src/config.ts | 26 + .../vue/src/useAccount.test-d.ts | 17 + .../vue/src/useChainId.test-d.ts | 15 + .../vue/src/useChains.test-d.ts | 11 + .../vue/src/useClient.test-d.ts | 38 + .../vue/src/useConfig.test-d.ts | 17 + .../vue/src/useConnect.test-d.ts | 13 + .../vue/src/useReadContract.test-d.ts | 30 + .../vue/src/useSendTransaction.test-d.ts | 55 + .../vue/src/useSimulateContract.test-d.ts | 86 + .../vue/src/useSwitchChain.test-d.ts | 27 + .../vue/src/useTransaction.test-d.ts | 25 + .../vue/src/useTransactionReceipt.test-d.ts | 41 + .../vue/src/useWaitForTransaction.test-d.ts | 41 + .../vue/src/useWriteContract.test-d.ts | 65 + packages/register-tests/vue/tsconfig.json | 5 + .../sequence-core-1.0.0/.changeset/README.md | 8 + .../.changeset/config.json | 11 + .../sequence-core-1.0.0/.github/CODEOWNERS | 1 + .../.github/ISSUE_TEMPLATE/bug_report.md | 40 + .../.github/ISSUE_TEMPLATE/custom.md | 7 + .../.github/ISSUE_TEMPLATE/feature_request.md | 19 + .../actions/install-dependencies/action.yml | 39 + .../workflows/on_pr_pnpm-format-label.yml | 23 + .../.github/workflows/pnpm-format.yml | 27 + .../.github/workflows/publish-dists.yml | 88 + .../.github/workflows/tests.yml | 63 + packages/sequence-core-1.0.0/.gitmodules | 3 + packages/sequence-core-1.0.0/.prettierrc | 5 + .../sequence-core-1.0.0/.vscode/launch.json | 28 + .../sequence-core-1.0.0/.vscode/settings.json | 7 + packages/sequence-core-1.0.0/LICENSE | 202 + packages/sequence-core-1.0.0/README.md | 39 + packages/sequence-core-1.0.0/corepack.tgz | Bin 0 -> 4311947 bytes .../sequence-core-1.0.0/extras/docs/README.md | 36 + .../extras/docs/app/favicon.ico | Bin 0 -> 25931 bytes .../extras/docs/app/fonts/GeistMonoVF.woff | Bin 0 -> 67864 bytes .../extras/docs/app/fonts/GeistVF.woff | Bin 0 -> 66268 bytes .../extras/docs/app/globals.css | 50 + .../extras/docs/app/layout.tsx | 29 + .../extras/docs/app/page.module.css | 188 + .../extras/docs/app/page.tsx | 80 + .../extras/docs/eslint.config.js | 4 + .../extras/docs/next.config.js | 4 + .../extras/docs/package.json | 28 + .../extras/docs/public/file-text.svg | 3 + .../extras/docs/public/globe.svg | 10 + .../extras/docs/public/next.svg | 1 + .../extras/docs/public/turborepo-dark.svg | 19 + .../extras/docs/public/turborepo-light.svg | 19 + .../extras/docs/public/vercel.svg | 10 + .../extras/docs/public/window.svg | 3 + .../extras/docs/tsconfig.json | 12 + .../sequence-core-1.0.0/extras/web/README.md | 36 + .../extras/web/app/favicon.ico | Bin 0 -> 25931 bytes .../extras/web/app/fonts/GeistMonoVF.woff | Bin 0 -> 67864 bytes .../extras/web/app/fonts/GeistVF.woff | Bin 0 -> 66268 bytes .../extras/web/app/globals.css | 50 + .../extras/web/app/layout.tsx | 29 + .../extras/web/app/page.module.css | 188 + .../extras/web/app/page.tsx | 80 + .../extras/web/eslint.config.js | 4 + .../extras/web/next.config.js | 4 + .../extras/web/package.json | 28 + .../extras/web/public/file-text.svg | 3 + .../extras/web/public/globe.svg | 10 + .../extras/web/public/next.svg | 1 + .../extras/web/public/turborepo-dark.svg | 19 + .../extras/web/public/turborepo-light.svg | 19 + .../extras/web/public/vercel.svg | 10 + .../extras/web/public/window.svg | 3 + .../extras/web/tsconfig.json | 12 + packages/sequence-core-1.0.0/foundry.lock | 1 + packages/sequence-core-1.0.0/lefthook.yml | 12 + .../lib/signals-implicit-mode/.env.sample | 1 + .../.github/workflows/test.yml | 56 + .../lib/signals-implicit-mode/.gitmodules | 9 + .../lib/signals-implicit-mode/README.md | 84 + .../lib/signals-implicit-mode/foundry.toml | 22 + .../lib/forge-std/.gitattributes | 1 + .../lib/forge-std/.github/CODEOWNERS | 1 + .../lib/forge-std/.github/workflows/ci.yml | 95 + .../lib/forge-std/.github/workflows/sync.yml | 31 + .../lib/forge-std/CONTRIBUTING.md | 193 + .../lib/forge-std/LICENSE-APACHE | 203 + .../lib/forge-std/LICENSE-MIT | 25 + .../lib/forge-std/README.md | 268 + .../lib/forge-std/RELEASE_CHECKLIST.md | 12 + .../lib/forge-std/foundry.toml | 27 + .../lib/forge-std/package.json | 16 + .../lib/forge-std/scripts/vm.py | 646 + .../lib/forge-std/src/Base.sol | 48 + .../lib/forge-std/src/Config.sol | 60 + .../lib/forge-std/src/LibVariable.sol | 477 + .../lib/forge-std/src/Script.sol | 28 + .../lib/forge-std/src/StdAssertions.sol | 764 + .../lib/forge-std/src/StdChains.sol | 287 + .../lib/forge-std/src/StdCheats.sol | 829 + .../lib/forge-std/src/StdConfig.sol | 612 + .../lib/forge-std/src/StdConstants.sol | 30 + .../lib/forge-std/src/StdError.sol | 15 + .../lib/forge-std/src/StdInvariant.sol | 122 + .../lib/forge-std/src/StdJson.sol | 283 + .../lib/forge-std/src/StdMath.sol | 43 + .../lib/forge-std/src/StdStorage.sol | 473 + .../lib/forge-std/src/StdStyle.sol | 333 + .../lib/forge-std/src/StdToml.sol | 283 + .../lib/forge-std/src/StdUtils.sol | 208 + .../lib/forge-std/src/Test.sol | 34 + .../lib/forge-std/src/Vm.sol | 2494 + .../lib/forge-std/src/console.sol | 1560 + .../lib/forge-std/src/console2.sol | 4 + .../lib/forge-std/src/interfaces/IERC1155.sol | 105 + .../lib/forge-std/src/interfaces/IERC165.sol | 12 + .../lib/forge-std/src/interfaces/IERC20.sol | 43 + .../lib/forge-std/src/interfaces/IERC4626.sol | 190 + .../lib/forge-std/src/interfaces/IERC6909.sol | 72 + .../lib/forge-std/src/interfaces/IERC721.sol | 164 + .../lib/forge-std/src/interfaces/IERC7540.sol | 150 + .../lib/forge-std/src/interfaces/IERC7575.sol | 241 + .../forge-std/src/interfaces/IMulticall3.sol | 73 + .../lib/forge-std/src/safeconsole.sol | 13937 ++++++ .../lib/forge-std/test/CommonBase.t.sol | 44 + .../lib/forge-std/test/Config.t.sol | 352 + .../lib/forge-std/test/LibVariable.t.sol | 434 + .../lib/forge-std/test/StdAssertions.t.sol | 141 + .../lib/forge-std/test/StdChains.t.sol | 227 + .../lib/forge-std/test/StdCheats.t.sol | 639 + .../lib/forge-std/test/StdConstants.t.sol | 38 + .../lib/forge-std/test/StdError.t.sol | 120 + .../lib/forge-std/test/StdJson.t.sol | 49 + .../lib/forge-std/test/StdMath.t.sol | 202 + .../lib/forge-std/test/StdStorage.t.sol | 488 + .../lib/forge-std/test/StdStyle.t.sol | 110 + .../lib/forge-std/test/StdToml.t.sol | 49 + .../lib/forge-std/test/StdUtils.t.sol | 342 + .../lib/forge-std/test/Vm.t.sol | 18 + .../test/compilation/CompilationScript.sol | 10 + .../compilation/CompilationScriptBase.sol | 10 + .../test/compilation/CompilationTest.sol | 10 + .../test/compilation/CompilationTestBase.sol | 10 + .../test/fixtures/broadcast.log.json | 187 + .../lib/forge-std/test/fixtures/config.toml | 81 + .../lib/forge-std/test/fixtures/test.json | 8 + .../lib/forge-std/test/fixtures/test.toml | 6 + .../lib/sequence-v3/.env.sample | 7 + .../sequence-v3/.github/workflows/tests.yml | 148 + .../lib/sequence-v3/.gitmodules | 12 + .../lib/sequence-v3/.prettierignore | 4 + .../lib/sequence-v3/.prettierrc | 7 + .../lib/sequence-v3/LICENSE | 201 + .../lib/sequence-v3/README.md | 53 + .../lib/sequence-v3/build_proxy.sh | 21 + .../lib/sequence-v3/docs/CHAINED.md | 450 + .../lib/sequence-v3/docs/CONFIGURATIONS.md | 279 + .../lib/sequence-v3/docs/PAYLOAD.md | 392 + .../lib/sequence-v3/docs/SESSIONS.md | 502 + .../lib/sequence-v3/docs/SIGNATURE.md | 367 + .../lib/sequence-v3/foundry.toml | 31 + .../lib/sequence-v3/lefthook.yml | 8 + .../lib/account-abstraction/.eslintrc.js | 79 + .../.github/workflows/build.yml | 89 + .../lib/account-abstraction/.solcover.js | 8 + .../lib/account-abstraction/.solhint.json | 12 + .../lib/account-abstraction/.solhintignore | 1 + .../lib/account-abstraction/LICENSE | 674 + .../lib/account-abstraction/README.md | 11 + ...bstraction_Incremental_Audit_Feb_2023.pdf" | Bin 0 -> 565821 bytes .../contracts/core/BaseAccount.sol | 113 + .../contracts/core/BasePaymaster.sol | 151 + .../contracts/core/EntryPoint.sol | 800 + .../contracts/core/EntryPointSimulations.sol | 190 + .../contracts/core/Helpers.sol | 106 + .../contracts/core/NonceManager.sol | 43 + .../contracts/core/SenderCreator.sol | 38 + .../contracts/core/StakeManager.sol | 145 + .../contracts/core/UserOperationLib.sol | 139 + .../contracts/interfaces/IAccount.sol | 39 + .../contracts/interfaces/IAccountExecute.sol | 20 + .../contracts/interfaces/IAggregator.sol | 44 + .../contracts/interfaces/IEntryPoint.sol | 223 + .../interfaces/IEntryPointSimulations.sol | 73 + .../contracts/interfaces/INonceManager.sol | 27 + .../contracts/interfaces/IPaymaster.sol | 63 + .../contracts/interfaces/IStakeManager.sol | 111 + .../interfaces/PackedUserOperation.sol | 28 + .../contracts/package.json | 29 + .../samples/LegacyTokenPaymaster.sol | 115 + .../contracts/samples/SimpleAccount.sol | 150 + .../samples/SimpleAccountFactory.sol | 52 + .../contracts/samples/TokenPaymaster.sol | 217 + .../contracts/samples/VerifyingPaymaster.sol | 96 + .../contracts/samples/bls/BLSAccount.sol | 65 + .../samples/bls/BLSAccountFactory.sol | 61 + .../contracts/samples/bls/BLSHelper.sol | 232 + .../samples/bls/BLSSignatureAggregator.sol | 171 + .../contracts/samples/bls/IBLSAccount.sol | 16 + .../contracts/samples/bls/lib/BLSOpen.sol | 64 + .../hubble-contracts/contracts/libs/BLS.sol | 440 + .../contracts/libs/ModExp.sol | 652 + .../samples/callback/TokenCallbackHandler.sol | 51 + .../contracts/samples/utils/IOracle.sol | 10 + .../contracts/samples/utils/OracleHelper.sol | 170 + .../contracts/samples/utils/UniswapHelper.sol | 111 + .../contracts/utils/Exec.sol | 70 + .../deploy/1_deploy_entrypoint.ts | 19 + .../deploy/2_deploy_SimpleAccountFactory.ts | 33 + .../deployments/arbitrum/.chainId | 1 + .../deployments/arbitrum/EntryPoint.json | 1318 + .../a4c52f0671aad8941c53d6ead2063803.json | 68 + .../deployments/gnosis/.chainId | 1 + .../deployments/gnosis/EntryPoint.json | 1318 + .../a4c52f0671aad8941c53d6ead2063803.json | 68 + .../deployments/goerli/.chainId | 1 + .../deployments/goerli/EntryPoint.json | 1318 + .../a4c52f0671aad8941c53d6ead2063803.json | 68 + .../deployments/kovan/.chainId | 1 + .../deployments/kovan/EntryPoint.json | 1073 + .../deployments/kovan/SimpleWallet.json | 338 + .../deployments/kovan/TestCounter.json | 118 + .../9255faacf3ae4e81db1326413027bfa0.json | 86 + .../deployments/mainnet/.chainId | 1 + .../deployments/mainnet/EntryPoint.json | 1318 + .../mainnet/SimpleAccountFactory.json | 107 + .../02113a2ed1850c3774563305ee607f11.json | 329 + .../a4c52f0671aad8941c53d6ead2063803.json | 68 + .../cfbebdf1101dd2bc0f310cb0b7d62cb7.json | 59 + .../deployments/matic/.chainId | 1 + .../deployments/matic/EntryPoint.json | 1318 + .../a4c52f0671aad8941c53d6ead2063803.json | 68 + .../cfbebdf1101dd2bc0f310cb0b7d62cb7.json | 59 + .../deployments/mumbai/.chainId | 1 + .../deployments/mumbai/EntryPoint.json | 1318 + .../a4c52f0671aad8941c53d6ead2063803.json | 68 + .../deployments/optimism/.chainId | 1 + .../deployments/optimism/EntryPoint.json | 1318 + .../a4c52f0671aad8941c53d6ead2063803.json | 68 + .../deployments/sepolia/.chainId | 1 + .../deployments/sepolia/EntryPoint.json | 1318 + .../a4c52f0671aad8941c53d6ead2063803.json | 68 + .../account-abstraction/erc/ERCS/erc-4337.md | 1017 + .../account-abstraction/erc/ERCS/erc-7562.md | 393 + .../erc/assets/erc-4337/bundle-seq-pm.svg | 1 + .../erc/assets/erc-4337/bundle-seq.svg | 1 + .../erc/assets/erc-4337/image1.png | Bin 0 -> 32181 bytes .../erc/assets/erc-4337/image2.png | Bin 0 -> 27514 bytes .../gascalc/0-init-gas-checker.ts | 12 + .../gascalc/1-simple-wallet.gas.ts | 17 + .../gascalc/2-paymaster.gas.ts | 59 + .../gascalc/3-huge-tx-gas.ts | 27 + .../gascalc/4-paymaster-postop.gas.ts | 62 + .../gascalc/5-token-paymaster.gas.ts | 159 + .../account-abstraction/gascalc/GasChecker.ts | 459 + .../lib/account-abstraction/hardhat.config.ts | 86 + .../lib/account-abstraction/package.json | 71 + .../reports/gas-checker.txt | 55 + .../scripts/check-gas-reports | 17 + .../scripts/docker-gascalc | 7 + .../scripts/docker-gascalc.yml | 20 + .../lib/account-abstraction/scripts/gascalc | 15 + .../account-abstraction/scripts/hh-wrapper | 5 + .../scripts/postpack-contracts-package.sh | 6 + .../scripts/prepack-contracts-package.sh | 17 + .../scripts/sample-script.js | 32 + .../account-abstraction/scripts/solcErrors | 12 + .../lib/account-abstraction/src/AASigner.ts | 458 + .../account-abstraction/src/Create2Factory.ts | 126 + .../lib/account-abstraction/src/Utils.ts | 15 + .../lib/account-abstraction/src/runop.ts | 158 + .../test/0-create2factory.test.ts | 43 + .../lib/account-abstraction/test/UserOp.ts | 364 + .../account-abstraction/test/UserOperation.ts | 30 + .../lib/account-abstraction/test/aa.init.ts | 8 + .../account-abstraction/test/chaiHelper.ts | 73 + .../lib/account-abstraction/test/debugTx.ts | 34 + .../test/entrypoint.test.ts | 1929 + .../test/entrypointsimulations.test.ts | 534 + .../account-abstraction/test/helpers.test.ts | 74 + .../test/paymaster.test.ts | 415 + .../test/samples/OracleHelper.test.ts | 287 + .../test/samples/TokenPaymaster.test.ts | 588 + .../test/simple-wallet.test.ts | 208 + .../account-abstraction/test/solidityTypes.ts | 11 + .../test/testExecAccount.test.ts | 61 + .../lib/account-abstraction/test/testutils.ts | 529 + .../test/verifying_paymaster.test.ts | 191 + .../account-abstraction/test/y.bls.test.ts | 249 + .../lib/account-abstraction/tsconfig.json | 31 + .../lib/account-abstraction/yarn.lock | 10171 ++++ .../sequence-v3/lib/forge-std/.gitattributes | 1 + .../lib/forge-std/.github/workflows/ci.yml | 128 + .../lib/forge-std/.github/workflows/sync.yml | 31 + .../sequence-v3/lib/forge-std/CONTRIBUTING.md | 193 + .../sequence-v3/lib/forge-std/LICENSE-APACHE | 203 + .../lib/sequence-v3/lib/forge-std/LICENSE-MIT | 25 + .../lib/sequence-v3/lib/forge-std/README.md | 268 + .../sequence-v3/lib/forge-std/foundry.toml | 23 + .../sequence-v3/lib/forge-std/package.json | 16 + .../sequence-v3/lib/forge-std/scripts/vm.py | 646 + .../sequence-v3/lib/forge-std/src/Base.sol | 42 + .../sequence-v3/lib/forge-std/src/Script.sol | 28 + .../lib/forge-std/src/StdAssertions.sol | 669 + .../lib/forge-std/src/StdChains.sol | 286 + .../lib/forge-std/src/StdCheats.sol | 829 + .../lib/forge-std/src/StdConstants.sol | 30 + .../lib/forge-std/src/StdError.sol | 15 + .../lib/forge-std/src/StdInvariant.sol | 122 + .../sequence-v3/lib/forge-std/src/StdJson.sol | 283 + .../sequence-v3/lib/forge-std/src/StdMath.sol | 43 + .../lib/forge-std/src/StdStorage.sol | 473 + .../lib/forge-std/src/StdStyle.sol | 333 + .../sequence-v3/lib/forge-std/src/StdToml.sol | 283 + .../lib/forge-std/src/StdUtils.sol | 209 + .../sequence-v3/lib/forge-std/src/Test.sol | 34 + .../lib/sequence-v3/lib/forge-std/src/Vm.sol | 2397 + .../sequence-v3/lib/forge-std/src/console.sol | 1560 + .../lib/forge-std/src/console2.sol | 4 + .../lib/forge-std/src/interfaces/IERC1155.sol | 105 + .../lib/forge-std/src/interfaces/IERC165.sol | 12 + .../lib/forge-std/src/interfaces/IERC20.sol | 43 + .../lib/forge-std/src/interfaces/IERC4626.sol | 190 + .../lib/forge-std/src/interfaces/IERC6909.sol | 72 + .../lib/forge-std/src/interfaces/IERC721.sol | 164 + .../lib/forge-std/src/interfaces/IERC7540.sol | 150 + .../lib/forge-std/src/interfaces/IERC7575.sol | 241 + .../forge-std/src/interfaces/IMulticall3.sol | 73 + .../lib/forge-std/src/safeconsole.sol | 13937 ++++++ .../lib/forge-std/test/CommonBase.t.sol | 44 + .../lib/forge-std/test/StdAssertions.t.sol | 141 + .../lib/forge-std/test/StdChains.t.sol | 227 + .../lib/forge-std/test/StdCheats.t.sol | 639 + .../lib/forge-std/test/StdConstants.t.sol | 38 + .../lib/forge-std/test/StdError.t.sol | 120 + .../lib/forge-std/test/StdJson.t.sol | 49 + .../lib/forge-std/test/StdMath.t.sol | 202 + .../lib/forge-std/test/StdStorage.t.sol | 488 + .../lib/forge-std/test/StdStyle.t.sol | 110 + .../lib/forge-std/test/StdToml.t.sol | 49 + .../lib/forge-std/test/StdUtils.t.sol | 342 + .../sequence-v3/lib/forge-std/test/Vm.t.sol | 18 + .../test/compilation/CompilationScript.sol | 10 + .../compilation/CompilationScriptBase.sol | 10 + .../test/compilation/CompilationTest.sol | 10 + .../test/compilation/CompilationTestBase.sol | 10 + .../test/fixtures/broadcast.log.json | 187 + .../lib/forge-std/test/fixtures/test.json | 8 + .../lib/forge-std/test/fixtures/test.toml | 6 + .../.changeset/config.json | 12 + .../lib/openzeppelin-contracts/.codecov.yml | 16 + .../lib/openzeppelin-contracts/.editorconfig | 21 + .../openzeppelin-contracts/.githooks/pre-push | 8 + .../.github/ISSUE_TEMPLATE/bug_report.md | 20 + .../.github/ISSUE_TEMPLATE/config.yml | 4 + .../.github/ISSUE_TEMPLATE/feature_request.md | 15 + .../.github/PULL_REQUEST_TEMPLATE.md | 19 + .../.github/actions/gas-compare/action.yml | 50 + .../.github/actions/setup/action.yml | 22 + .../.github/actions/storage-layout/action.yml | 56 + .../.github/workflows/actionlint.yml | 18 + .../.github/workflows/changeset.yml | 28 + .../.github/workflows/checks.yml | 136 + .../.github/workflows/docs.yml | 19 + .../.github/workflows/formal-verification.yml | 86 + .../.github/workflows/release-cycle.yml | 212 + .../.github/workflows/upgradeable.yml | 34 + .../lib/openzeppelin-contracts/.gitmodules | 10 + .../lib/openzeppelin-contracts/.mocharc.js | 4 + .../lib/openzeppelin-contracts/.prettierrc | 15 + .../lib/openzeppelin-contracts/.solcover.js | 21 + .../lib/openzeppelin-contracts/CHANGELOG.md | 1158 + .../openzeppelin-contracts/CODE_OF_CONDUCT.md | 73 + .../openzeppelin-contracts/CONTRIBUTING.md | 36 + .../lib/openzeppelin-contracts/FUNDING.json | 7 + .../lib/openzeppelin-contracts/GUIDELINES.md | 154 + .../lib/openzeppelin-contracts/LICENSE | 22 + .../lib/openzeppelin-contracts/README.md | 106 + .../lib/openzeppelin-contracts/RELEASING.md | 45 + .../lib/openzeppelin-contracts/SECURITY.md | 43 + .../openzeppelin-contracts/audits/2017-03.md | 293 + .../openzeppelin-contracts/audits/2018-10.pdf | Bin 0 -> 1000527 bytes .../audits/2022-10-Checkpoints.pdf | Bin 0 -> 155606 bytes .../audits/2022-10-ERC4626.pdf | Bin 0 -> 204184 bytes .../audits/2023-05-v4.9.pdf | Bin 0 -> 485395 bytes .../audits/2023-10-v5.0.pdf | Bin 0 -> 910284 bytes .../audits/2024-10-v5.1.pdf | Bin 0 -> 395831 bytes .../openzeppelin-contracts/audits/README.md | 18 + .../openzeppelin-contracts/certora/Makefile | 54 + .../openzeppelin-contracts/certora/README.md | 60 + .../access_manager_AccessManager.sol.patch | 97 + .../AccessControlDefaultAdminRulesHarness.sol | 46 + .../harnesses/AccessControlHarness.sol | 6 + .../harnesses/AccessManagedHarness.sol | 36 + .../harnesses/AccessManagerHarness.sol | 116 + .../harnesses/DoubleEndedQueueHarness.sol | 58 + .../harnesses/ERC20FlashMintHarness.sol | 36 + .../certora/harnesses/ERC20PermitHarness.sol | 16 + .../certora/harnesses/ERC20WrapperHarness.sol | 34 + .../harnesses/ERC3156FlashBorrowerHarness.sol | 13 + .../certora/harnesses/ERC721Harness.sol | 33 + .../harnesses/ERC721ReceiverHarness.sol | 11 + .../harnesses/EnumerableMapHarness.sol | 55 + .../harnesses/EnumerableSetHarness.sol | 35 + .../harnesses/InitializableHarness.sol | 23 + .../certora/harnesses/NoncesHarness.sol | 14 + .../certora/harnesses/Ownable2StepHarness.sol | 10 + .../certora/harnesses/OwnableHarness.sol | 10 + .../certora/harnesses/PausableHarness.sol | 18 + .../harnesses/TimelockControllerHarness.sol | 13 + .../certora/reports/2021-10.pdf | Bin 0 -> 92882 bytes .../certora/reports/2022-03.pdf | Bin 0 -> 199401 bytes .../certora/reports/2022-05.pdf | Bin 0 -> 132223 bytes .../lib/openzeppelin-contracts/certora/run.js | 160 + .../openzeppelin-contracts/certora/specs.json | 110 + .../certora/specs/AccessControl.spec | 119 + .../specs/AccessControlDefaultAdminRules.spec | 464 + .../certora/specs/AccessManaged.spec | 34 + .../certora/specs/AccessManager.spec | 826 + .../certora/specs/DoubleEndedQueue.spec | 300 + .../certora/specs/ERC20.spec | 352 + .../certora/specs/ERC20FlashMint.spec | 55 + .../certora/specs/ERC20Wrapper.spec | 198 + .../certora/specs/ERC721.spec | 679 + .../certora/specs/EnumerableMap.spec | 333 + .../certora/specs/EnumerableSet.spec | 246 + .../certora/specs/Initializable.spec | 165 + .../certora/specs/Nonces.spec | 92 + .../certora/specs/Ownable.spec | 77 + .../certora/specs/Ownable2Step.spec | 108 + .../certora/specs/Pausable.spec | 96 + .../certora/specs/TimelockController.spec | 274 + .../certora/specs/helpers/helpers.spec | 12 + .../certora/specs/methods/IAccessControl.spec | 8 + .../IAccessControlDefaultAdminRules.spec | 36 + .../certora/specs/methods/IAccessManaged.spec | 5 + .../certora/specs/methods/IAccessManager.spec | 33 + .../certora/specs/methods/IERC20.spec | 11 + .../certora/specs/methods/IERC2612.spec | 5 + .../specs/methods/IERC3156FlashBorrower.spec | 3 + .../specs/methods/IERC3156FlashLender.spec | 5 + .../certora/specs/methods/IERC5313.spec | 3 + .../certora/specs/methods/IERC721.spec | 17 + .../specs/methods/IERC721Receiver.spec | 3 + .../certora/specs/methods/IOwnable.spec | 5 + .../certora/specs/methods/IOwnable2Step.spec | 7 + .../contracts/access/AccessControl.sol | 209 + .../contracts/access/IAccessControl.sol | 98 + .../contracts/access/Ownable.sol | 100 + .../contracts/access/Ownable2Step.sol | 67 + .../contracts/access/README.adoc | 45 + .../AccessControlDefaultAdminRules.sol | 396 + .../extensions/AccessControlEnumerable.sol | 82 + .../IAccessControlDefaultAdminRules.sol | 192 + .../extensions/IAccessControlEnumerable.sol | 31 + .../access/manager/AccessManaged.sol | 113 + .../access/manager/AccessManager.sol | 740 + .../access/manager/AuthorityUtils.sol | 32 + .../access/manager/IAccessManaged.sol | 32 + .../access/manager/IAccessManager.sol | 401 + .../contracts/access/manager/IAuthority.sol | 14 + .../contracts/account/README.adoc | 12 + .../account/utils/draft-ERC4337Utils.sol | 170 + .../account/utils/draft-ERC7579Utils.sol | 278 + .../contracts/finance/README.adoc | 14 + .../contracts/finance/VestingWallet.sol | 159 + .../contracts/finance/VestingWalletCliff.sol | 54 + .../contracts/governance/Governor.sol | 833 + .../contracts/governance/IGovernor.sol | 441 + .../contracts/governance/README.adoc | 181 + .../governance/TimelockController.sol | 472 + .../extensions/GovernorCountingFractional.sol | 196 + .../GovernorCountingOverridable.sol | 225 + .../extensions/GovernorCountingSimple.sol | 102 + .../extensions/GovernorPreventLateQuorum.sol | 92 + .../extensions/GovernorSettings.sol | 112 + .../governance/extensions/GovernorStorage.sol | 125 + .../extensions/GovernorTimelockAccess.sol | 349 + .../extensions/GovernorTimelockCompound.sol | 167 + .../extensions/GovernorTimelockControl.sol | 170 + .../governance/extensions/GovernorVotes.sol | 64 + .../GovernorVotesQuorumFraction.sol | 110 + .../contracts/governance/utils/IVotes.sol | 59 + .../contracts/governance/utils/Votes.sol | 252 + .../governance/utils/VotesExtended.sol | 84 + .../contracts/interfaces/IERC1155.sol | 6 + .../interfaces/IERC1155MetadataURI.sol | 6 + .../contracts/interfaces/IERC1155Receiver.sol | 6 + .../contracts/interfaces/IERC1271.sol | 17 + .../contracts/interfaces/IERC1363.sol | 86 + .../contracts/interfaces/IERC1363Receiver.sol | 32 + .../contracts/interfaces/IERC1363Spender.sol | 26 + .../contracts/interfaces/IERC165.sol | 6 + .../interfaces/IERC1820Implementer.sol | 20 + .../contracts/interfaces/IERC1820Registry.sol | 112 + .../contracts/interfaces/IERC1967.sol | 24 + .../contracts/interfaces/IERC20.sol | 6 + .../contracts/interfaces/IERC20Metadata.sol | 6 + .../contracts/interfaces/IERC2309.sol | 19 + .../contracts/interfaces/IERC2612.sol | 8 + .../contracts/interfaces/IERC2981.sol | 26 + .../contracts/interfaces/IERC3156.sol | 7 + .../interfaces/IERC3156FlashBorrower.sol | 27 + .../interfaces/IERC3156FlashLender.sol | 41 + .../contracts/interfaces/IERC4626.sol | 230 + .../contracts/interfaces/IERC4906.sol | 20 + .../contracts/interfaces/IERC5267.sol | 28 + .../contracts/interfaces/IERC5313.sol | 16 + .../contracts/interfaces/IERC5805.sol | 9 + .../contracts/interfaces/IERC6372.sol | 17 + .../contracts/interfaces/IERC721.sol | 6 + .../interfaces/IERC721Enumerable.sol | 6 + .../contracts/interfaces/IERC721Metadata.sol | 6 + .../contracts/interfaces/IERC721Receiver.sol | 6 + .../contracts/interfaces/IERC777.sol | 200 + .../contracts/interfaces/IERC777Recipient.sol | 35 + .../contracts/interfaces/IERC777Sender.sol | 35 + .../contracts/interfaces/README.adoc | 85 + .../contracts/interfaces/draft-IERC1822.sol | 20 + .../contracts/interfaces/draft-IERC4337.sol | 253 + .../contracts/interfaces/draft-IERC6093.sol | 161 + .../contracts/interfaces/draft-IERC7579.sol | 226 + .../contracts/interfaces/draft-IERC7674.sol | 17 + .../contracts/metatx/ERC2771Context.sol | 86 + .../contracts/metatx/ERC2771Forwarder.sol | 369 + .../contracts/metatx/README.adoc | 17 + .../contracts/mocks/AccessManagedTarget.sol | 34 + .../contracts/mocks/AccessManagerMock.sol | 21 + .../contracts/mocks/ArraysMock.sol | 127 + .../contracts/mocks/AuthorityMock.sol | 69 + .../contracts/mocks/Base64Dirty.sol | 19 + .../contracts/mocks/BatchCaller.sol | 20 + .../contracts/mocks/CallReceiverMock.sol | 73 + .../contracts/mocks/ConstructorMock.sol | 34 + .../contracts/mocks/ContextMock.sol | 35 + .../contracts/mocks/DummyImplementation.sol | 65 + .../contracts/mocks/EIP712Verifier.sol | 16 + .../contracts/mocks/ERC1271WalletMock.sol | 24 + .../ERC165/ERC165InterfacesSupported.sol | 58 + .../mocks/ERC165/ERC165MaliciousData.sol | 12 + .../mocks/ERC165/ERC165MissingData.sol | 7 + .../mocks/ERC165/ERC165NotSupported.sol | 5 + .../mocks/ERC165/ERC165ReturnBomb.sol | 18 + .../contracts/mocks/ERC2771ContextMock.sol | 28 + .../mocks/ERC3156FlashBorrowerMock.sol | 53 + .../contracts/mocks/EtherReceiverMock.sol | 17 + .../contracts/mocks/InitializableMock.sol | 130 + .../mocks/MerkleProofCustomHashMock.sol | 62 + .../contracts/mocks/MerkleTreeMock.sol | 44 + .../contracts/mocks/MulticallHelper.sol | 23 + .../MultipleInheritanceInitializableMocks.sol | 131 + .../contracts/mocks/PausableMock.sol | 31 + .../contracts/mocks/ReentrancyAttack.sol | 12 + .../contracts/mocks/ReentrancyMock.sol | 50 + .../mocks/ReentrancyTransientMock.sol | 50 + .../mocks/RegressionImplementation.sol | 61 + .../SingleInheritanceInitializableMocks.sol | 49 + .../contracts/mocks/Stateless.sol | 49 + .../contracts/mocks/StorageSlotMock.sol | 87 + .../contracts/mocks/TimelockReentrant.sol | 26 + .../contracts/mocks/TransientSlotMock.sol | 61 + .../contracts/mocks/UpgradeableBeaconMock.sol | 27 + .../contracts/mocks/VotesExtendedMock.sol | 42 + .../contracts/mocks/VotesMock.sol | 42 + .../mocks/account/utils/ERC7579UtilsMock.sol | 23 + .../contracts/mocks/compound/CompTimelock.sol | 174 + .../mocks/docs/ERC20WithAutoMinerReward.sol | 22 + .../contracts/mocks/docs/ERC4626Fees.sol | 109 + .../contracts/mocks/docs/MyNFT.sol | 9 + .../AccessControlERC20MintBase.sol | 25 + .../AccessControlERC20MintMissing.sol | 24 + .../AccessControlERC20MintOnlyRole.sol | 23 + .../access-control/AccessControlModified.sol | 14 + .../AccessControlNonRevokableAdmin.sol | 17 + .../AccessManagedERC20MintBase.sol | 16 + .../docs/access-control/MyContractOwnable.sol | 17 + .../mocks/docs/governance/MyGovernor.sol | 81 + .../mocks/docs/governance/MyToken.sol | 21 + .../docs/governance/MyTokenTimestampBased.sol | 32 + .../mocks/docs/governance/MyTokenWrapped.sol | 28 + .../mocks/docs/token/ERC1155/GameItems.sol | 21 + .../token/ERC1155/MyERC115HolderContract.sol | 7 + .../mocks/docs/token/ERC20/GLDToken.sol | 11 + .../mocks/docs/token/ERC721/GameItem.sol | 19 + .../mocks/docs/utilities/Base64NFT.sol | 27 + .../mocks/docs/utilities/Multicall.sol | 15 + .../GovernorCountingOverridableMock.sol | 18 + .../governance/GovernorFractionalMock.sol | 14 + .../mocks/governance/GovernorMock.sol | 14 + .../GovernorPreventLateQuorumMock.sol | 40 + .../mocks/governance/GovernorStorageMock.sol | 79 + .../governance/GovernorTimelockAccessMock.sol | 70 + .../GovernorTimelockCompoundMock.sol | 69 + .../GovernorTimelockControlMock.sol | 69 + .../mocks/governance/GovernorVoteMock.sol | 20 + .../governance/GovernorWithParamsMock.sol | 51 + .../contracts/mocks/proxy/BadBeacon.sol | 11 + .../mocks/proxy/ClashingImplementation.sol | 19 + .../mocks/proxy/UUPSUpgradeableMock.sol | 35 + .../mocks/token/ERC1155ReceiverMock.sol | 74 + .../mocks/token/ERC1363ForceApproveMock.sol | 14 + .../mocks/token/ERC1363NoReturnMock.sol | 34 + .../mocks/token/ERC1363ReceiverMock.sol | 52 + .../mocks/token/ERC1363ReturnFalseMock.sol | 34 + .../mocks/token/ERC1363SpenderMock.sol | 47 + .../mocks/token/ERC20ApprovalMock.sol | 10 + .../mocks/token/ERC20DecimalsMock.sol | 17 + .../mocks/token/ERC20ExcessDecimalsMock.sol | 9 + .../mocks/token/ERC20FlashMintMock.sol | 26 + .../mocks/token/ERC20ForceApproveMock.sol | 13 + .../mocks/token/ERC20GetterHelper.sol | 38 + .../contracts/mocks/token/ERC20Mock.sol | 16 + .../mocks/token/ERC20MulticallMock.sol | 8 + .../mocks/token/ERC20NoReturnMock.sol | 28 + .../contracts/mocks/token/ERC20Reentrant.sol | 39 + .../mocks/token/ERC20ReturnFalseMock.sol | 19 + .../ERC20VotesAdditionalCheckpointsMock.sol | 31 + .../mocks/token/ERC20VotesLegacyMock.sol | 253 + .../mocks/token/ERC20VotesTimestampMock.sol | 29 + .../mocks/token/ERC4626LimitsMock.sol | 23 + .../contracts/mocks/token/ERC4626Mock.sol | 17 + .../mocks/token/ERC4626OffsetMock.sol | 17 + .../contracts/mocks/token/ERC4646FeesMock.sol | 40 + .../token/ERC721ConsecutiveEnumerableMock.sol | 42 + .../mocks/token/ERC721ConsecutiveMock.sol | 61 + .../mocks/token/ERC721ReceiverMock.sol | 47 + .../mocks/token/ERC721URIStorageMock.sol | 17 + .../contracts/package.json | 32 + .../contracts/proxy/Clones.sol | 262 + .../contracts/proxy/ERC1967/ERC1967Proxy.sol | 40 + .../contracts/proxy/ERC1967/ERC1967Utils.sol | 177 + .../contracts/proxy/Proxy.sol | 69 + .../contracts/proxy/README.adoc | 87 + .../contracts/proxy/beacon/BeaconProxy.sol | 57 + .../contracts/proxy/beacon/IBeacon.sol | 16 + .../proxy/beacon/UpgradeableBeacon.sol | 70 + .../proxy/transparent/ProxyAdmin.sol | 45 + .../TransparentUpgradeableProxy.sol | 118 + .../contracts/proxy/utils/Initializable.sol | 228 + .../contracts/proxy/utils/UUPSUpgradeable.sol | 147 + .../contracts/token/ERC1155/ERC1155.sol | 401 + .../contracts/token/ERC1155/IERC1155.sol | 123 + .../token/ERC1155/IERC1155Receiver.sol | 59 + .../contracts/token/ERC1155/README.adoc | 43 + .../ERC1155/extensions/ERC1155Burnable.sol | 28 + .../ERC1155/extensions/ERC1155Pausable.sol | 38 + .../ERC1155/extensions/ERC1155Supply.sol | 90 + .../ERC1155/extensions/ERC1155URIStorage.sol | 61 + .../extensions/IERC1155MetadataURI.sol | 20 + .../token/ERC1155/utils/ERC1155Holder.sol | 42 + .../token/ERC1155/utils/ERC1155Utils.sol | 88 + .../contracts/token/ERC20/ERC20.sol | 312 + .../contracts/token/ERC20/IERC20.sol | 79 + .../contracts/token/ERC20/README.adoc | 75 + .../token/ERC20/extensions/ERC1363.sol | 137 + .../token/ERC20/extensions/ERC20Burnable.sol | 39 + .../token/ERC20/extensions/ERC20Capped.sol | 56 + .../token/ERC20/extensions/ERC20FlashMint.sol | 134 + .../token/ERC20/extensions/ERC20Pausable.sol | 33 + .../token/ERC20/extensions/ERC20Permit.sol | 83 + .../token/ERC20/extensions/ERC20Votes.sol | 83 + .../token/ERC20/extensions/ERC20Wrapper.sol | 91 + .../token/ERC20/extensions/ERC4626.sol | 282 + .../token/ERC20/extensions/IERC20Metadata.sol | 26 + .../token/ERC20/extensions/IERC20Permit.sol | 90 + .../draft-ERC20TemporaryApproval.sol | 119 + .../token/ERC20/utils/ERC1363Utils.sol | 95 + .../contracts/token/ERC20/utils/SafeERC20.sol | 198 + .../contracts/token/ERC721/ERC721.sol | 456 + .../contracts/token/ERC721/IERC721.sol | 135 + .../token/ERC721/IERC721Receiver.sol | 28 + .../contracts/token/ERC721/README.adoc | 69 + .../ERC721/extensions/ERC721Burnable.sol | 26 + .../ERC721/extensions/ERC721Consecutive.sol | 176 + .../ERC721/extensions/ERC721Enumerable.sol | 174 + .../ERC721/extensions/ERC721Pausable.sol | 37 + .../token/ERC721/extensions/ERC721Royalty.sol | 27 + .../ERC721/extensions/ERC721URIStorage.sol | 61 + .../token/ERC721/extensions/ERC721Votes.sol | 47 + .../token/ERC721/extensions/ERC721Wrapper.sol | 102 + .../ERC721/extensions/IERC721Enumerable.sol | 29 + .../ERC721/extensions/IERC721Metadata.sol | 27 + .../token/ERC721/utils/ERC721Holder.sol | 24 + .../token/ERC721/utils/ERC721Utils.sol | 50 + .../contracts/token/common/ERC2981.sol | 143 + .../contracts/token/common/README.adoc | 10 + .../contracts/utils/Address.sol | 150 + .../contracts/utils/Arrays.sol | 482 + .../contracts/utils/Base64.sol | 123 + .../contracts/utils/Bytes.sol | 114 + .../contracts/utils/CAIP10.sol | 54 + .../contracts/utils/CAIP2.sol | 51 + .../contracts/utils/Comparators.sol | 19 + .../contracts/utils/Context.sol | 28 + .../contracts/utils/Create2.sol | 92 + .../contracts/utils/Errors.sol | 34 + .../contracts/utils/Multicall.sol | 37 + .../contracts/utils/Nonces.sol | 46 + .../contracts/utils/NoncesKeyed.sol | 74 + .../contracts/utils/Packing.sol | 1656 + .../contracts/utils/Panic.sol | 57 + .../contracts/utils/Pausable.sol | 119 + .../contracts/utils/README.adoc | 149 + .../contracts/utils/ReentrancyGuard.sol | 87 + .../utils/ReentrancyGuardTransient.sol | 61 + .../contracts/utils/ShortStrings.sol | 122 + .../contracts/utils/SlotDerivation.sol | 155 + .../contracts/utils/StorageSlot.sol | 143 + .../contracts/utils/Strings.sol | 441 + .../contracts/utils/TransientSlot.sol | 183 + .../contracts/utils/cryptography/ECDSA.sol | 180 + .../contracts/utils/cryptography/EIP712.sol | 160 + .../contracts/utils/cryptography/Hashes.sol | 31 + .../utils/cryptography/MerkleProof.sol | 514 + .../utils/cryptography/MessageHashUtils.sol | 84 + .../contracts/utils/cryptography/P256.sol | 370 + .../contracts/utils/cryptography/RSA.sol | 154 + .../utils/cryptography/SignatureChecker.sol | 50 + .../contracts/utils/introspection/ERC165.sol | 27 + .../utils/introspection/ERC165Checker.sol | 124 + .../contracts/utils/introspection/IERC165.sol | 25 + .../contracts/utils/math/Math.sol | 685 + .../contracts/utils/math/SafeCast.sol | 1162 + .../contracts/utils/math/SignedMath.sol | 68 + .../contracts/utils/structs/BitMaps.sol | 60 + .../contracts/utils/structs/Checkpoints.sol | 630 + .../utils/structs/CircularBuffer.sol | 140 + .../utils/structs/DoubleEndedQueue.sol | 156 + .../contracts/utils/structs/EnumerableMap.sol | 905 + .../contracts/utils/structs/EnumerableSet.sol | 375 + .../contracts/utils/structs/Heap.sol | 256 + .../contracts/utils/structs/MerkleTree.sol | 175 + .../contracts/utils/types/Time.sol | 133 + .../vendor/compound/ICompoundTimelock.sol | 86 + .../contracts/vendor/compound/LICENSE | 11 + .../lib/openzeppelin-contracts/docs/README.md | 16 + .../openzeppelin-contracts/docs/antora.yml | 7 + .../lib/openzeppelin-contracts/docs/config.js | 21 + .../ROOT/images/access-control-multiple.svg | 97 + .../ROOT/images/access-manager-functions.svg | 47 + .../modules/ROOT/images/access-manager.svg | 99 + .../modules/ROOT/images/erc4626-attack-3a.png | Bin 0 -> 60433 bytes .../modules/ROOT/images/erc4626-attack-3b.png | Bin 0 -> 66184 bytes .../modules/ROOT/images/erc4626-attack-6.png | Bin 0 -> 56290 bytes .../modules/ROOT/images/erc4626-attack.png | Bin 0 -> 58886 bytes .../modules/ROOT/images/erc4626-deposit.png | Bin 0 -> 115497 bytes .../docs/modules/ROOT/images/erc4626-mint.png | Bin 0 -> 112787 bytes .../ROOT/images/erc4626-rate-linear.png | Bin 0 -> 50813 bytes .../ROOT/images/erc4626-rate-loglog.png | Bin 0 -> 72818 bytes .../ROOT/images/erc4626-rate-loglogext.png | Bin 0 -> 109923 bytes .../docs/modules/ROOT/images/tally-exec.png | Bin 0 -> 231859 bytes .../docs/modules/ROOT/images/tally-vote.png | Bin 0 -> 40507 bytes .../docs/modules/ROOT/nav.adoc | 23 + .../modules/ROOT/pages/access-control.adoc | 288 + .../ROOT/pages/backwards-compatibility.adoc | 48 + .../docs/modules/ROOT/pages/crowdsales.adoc | 11 + .../docs/modules/ROOT/pages/drafts.adoc | 19 + .../docs/modules/ROOT/pages/erc1155.adoc | 118 + .../docs/modules/ROOT/pages/erc20-supply.adoc | 71 + .../docs/modules/ROOT/pages/erc20.adoc | 67 + .../docs/modules/ROOT/pages/erc4626.adoc | 214 + .../docs/modules/ROOT/pages/erc721.adoc | 58 + .../ROOT/pages/extending-contracts.adoc | 51 + .../docs/modules/ROOT/pages/faq.adoc | 13 + .../docs/modules/ROOT/pages/governance.adoc | 239 + .../docs/modules/ROOT/pages/index.adoc | 70 + .../docs/modules/ROOT/pages/tokens.adoc | 31 + .../docs/modules/ROOT/pages/upgradeable.adoc | 77 + .../docs/modules/ROOT/pages/utilities.adoc | 388 + .../docs/modules/ROOT/pages/wizard.adoc | 15 + .../docs/templates/contract.hbs | 137 + .../docs/templates/helpers.js | 46 + .../docs/templates/page.hbs | 4 + .../docs/templates/properties.js | 72 + .../openzeppelin-contracts/eslint.config.mjs | 26 + .../lib/openzeppelin-contracts/foundry.toml | 15 + .../fv-requirements.txt | 4 + .../openzeppelin-contracts/hardhat.config.js | 124 + .../hardhat/async-test-sanity.js | 3 + .../hardhat/env-artifacts.js | 29 + .../hardhat/ignore-unreachable-warnings.js | 45 + .../hardhat/remappings.js | 18 + .../hardhat/skip-foundry-tests.js | 6 + .../hardhat/task-test-get-files.js | 25 + .../lib/erc4626-tests/ERC4626.prop.sol | 404 + .../lib/erc4626-tests/ERC4626.test.sol | 349 + .../lib/erc4626-tests/LICENSE | 661 + .../lib/erc4626-tests/README.md | 121 + .../lib/forge-std/.gitattributes | 1 + .../lib/forge-std/.github/workflows/ci.yml | 128 + .../lib/forge-std/.github/workflows/sync.yml | 31 + .../lib/forge-std/LICENSE-APACHE | 203 + .../lib/forge-std/LICENSE-MIT | 25 + .../lib/forge-std/README.md | 235 + .../lib/forge-std/foundry.toml | 21 + .../lib/forge-std/package.json | 16 + .../lib/forge-std/scripts/vm.py | 635 + .../lib/forge-std/src/Base.sol | 35 + .../lib/forge-std/src/Script.sol | 27 + .../lib/forge-std/src/StdAssertions.sol | 669 + .../lib/forge-std/src/StdChains.sol | 263 + .../lib/forge-std/src/StdCheats.sol | 817 + .../lib/forge-std/src/StdError.sol | 15 + .../lib/forge-std/src/StdInvariant.sol | 122 + .../lib/forge-std/src/StdJson.sol | 283 + .../lib/forge-std/src/StdMath.sol | 43 + .../lib/forge-std/src/StdStorage.sol | 473 + .../lib/forge-std/src/StdStyle.sol | 333 + .../lib/forge-std/src/StdToml.sol | 283 + .../lib/forge-std/src/StdUtils.sol | 226 + .../lib/forge-std/src/Test.sol | 33 + .../lib/forge-std/src/Vm.sol | 1968 + .../lib/forge-std/src/console.sol | 1560 + .../lib/forge-std/src/console2.sol | 4 + .../lib/forge-std/src/interfaces/IERC1155.sol | 105 + .../lib/forge-std/src/interfaces/IERC165.sol | 12 + .../lib/forge-std/src/interfaces/IERC20.sol | 43 + .../lib/forge-std/src/interfaces/IERC4626.sol | 190 + .../lib/forge-std/src/interfaces/IERC721.sol | 164 + .../forge-std/src/interfaces/IMulticall3.sol | 73 + .../lib/forge-std/src/mocks/MockERC20.sol | 234 + .../lib/forge-std/src/mocks/MockERC721.sol | 231 + .../lib/forge-std/src/safeconsole.sol | 13937 ++++++ .../lib/forge-std/test/StdAssertions.t.sol | 145 + .../lib/forge-std/test/StdChains.t.sol | 228 + .../lib/forge-std/test/StdCheats.t.sol | 618 + .../lib/forge-std/test/StdError.t.sol | 120 + .../lib/forge-std/test/StdJson.t.sol | 49 + .../lib/forge-std/test/StdMath.t.sol | 202 + .../lib/forge-std/test/StdStorage.t.sol | 471 + .../lib/forge-std/test/StdStyle.t.sol | 110 + .../lib/forge-std/test/StdToml.t.sol | 49 + .../lib/forge-std/test/StdUtils.t.sol | 342 + .../lib/forge-std/test/Vm.t.sol | 18 + .../test/compilation/CompilationScript.sol | 10 + .../compilation/CompilationScriptBase.sol | 10 + .../test/compilation/CompilationTest.sol | 10 + .../test/compilation/CompilationTestBase.sol | 10 + .../test/fixtures/broadcast.log.json | 187 + .../lib/forge-std/test/fixtures/test.json | 8 + .../lib/forge-std/test/fixtures/test.toml | 6 + .../lib/forge-std/test/mocks/MockERC20.t.sol | 441 + .../lib/forge-std/test/mocks/MockERC721.t.sol | 721 + .../lib/halmos-cheatcodes/LICENSE | 661 + .../lib/halmos-cheatcodes/README.md | 100 + .../lib/halmos-cheatcodes/src/SVM.sol | 35 + .../lib/halmos-cheatcodes/src/SymTest.sol | 11 + .../lib/openzeppelin-contracts/logo.svg | 15 + .../lib/openzeppelin-contracts/netlify.toml | 3 + .../openzeppelin-contracts/package-lock.json | 11419 +++++ .../lib/openzeppelin-contracts/package.json | 92 + .../lib/openzeppelin-contracts/remappings.txt | 1 + .../lib/openzeppelin-contracts/renovate.json | 4 + .../scripts/checks/compare-layout.js | 20 + .../scripts/checks/compareGasReports.js | 247 + .../scripts/checks/coverage.sh | 18 + .../scripts/checks/extract-layout.js | 38 + .../scripts/checks/generation.sh | 6 + .../scripts/checks/inheritance-ordering.js | 55 + .../scripts/checks/pragma-consistency.js | 49 + .../openzeppelin-contracts/scripts/gen-nav.js | 41 + .../scripts/generate/format-lines.js | 16 + .../scripts/generate/helpers/sanitize.js | 5 + .../scripts/generate/run.js | 58 + .../scripts/generate/templates/Arrays.js | 384 + .../scripts/generate/templates/Arrays.opts.js | 3 + .../scripts/generate/templates/Checkpoints.js | 242 + .../generate/templates/Checkpoints.opts.js | 17 + .../generate/templates/Checkpoints.t.js | 137 + .../generate/templates/EnumerableMap.js | 272 + .../generate/templates/EnumerableMap.opts.js | 19 + .../generate/templates/EnumerableSet.js | 247 + .../generate/templates/EnumerableSet.opts.js | 12 + .../scripts/generate/templates/MerkleProof.js | 187 + .../generate/templates/MerkleProof.opts.js | 11 + .../scripts/generate/templates/Packing.js | 92 + .../generate/templates/Packing.opts.js | 3 + .../scripts/generate/templates/Packing.t.js | 48 + .../scripts/generate/templates/SafeCast.js | 136 + .../scripts/generate/templates/Slot.opts.js | 15 + .../generate/templates/SlotDerivation.js | 119 + .../generate/templates/SlotDerivation.t.js | 127 + .../scripts/generate/templates/StorageSlot.js | 77 + .../generate/templates/StorageSlotMock.js | 57 + .../generate/templates/TransientSlot.js | 80 + .../generate/templates/TransientSlotMock.js | 35 + .../scripts/generate/templates/conversion.js | 30 + .../scripts/git-user-config.sh | 6 + .../openzeppelin-contracts/scripts/helpers.js | 7 + .../openzeppelin-contracts/scripts/prepack.sh | 23 + .../scripts/prepare-docs.sh | 26 + .../openzeppelin-contracts/scripts/prepare.sh | 5 + .../scripts/release/format-changelog.js | 33 + .../scripts/release/synchronize-versions.js | 15 + .../scripts/release/update-comment.js | 34 + .../scripts/release/version.sh | 11 + .../release/workflow/exit-prerelease.sh | 8 + .../release/workflow/github-release.js | 48 + .../release/workflow/integrity-check.sh | 20 + .../scripts/release/workflow/pack.sh | 26 + .../scripts/release/workflow/publish.sh | 26 + .../scripts/release/workflow/rerun.js | 7 + .../workflow/set-changesets-pr-title.js | 17 + .../scripts/release/workflow/start.sh | 35 + .../scripts/release/workflow/state.js | 112 + .../scripts/remove-ignored-artifacts.js | 45 + .../scripts/solhint-custom/index.js | 84 + .../scripts/solhint-custom/package.json | 5 + .../scripts/update-docs-branch.js | 65 + .../scripts/upgradeable/README.md | 21 + .../scripts/upgradeable/patch-apply.sh | 19 + .../scripts/upgradeable/patch-save.sh | 18 + .../scripts/upgradeable/transpile-onto.sh | 54 + .../scripts/upgradeable/transpile.sh | 50 + .../scripts/upgradeable/upgradeable.patch | 361 + .../slither.config.json | 5 + .../openzeppelin-contracts/solhint.config.js | 26 + .../openzeppelin-contracts/test/TESTING.md | 3 + .../test/access/AccessControl.behavior.js | 874 + .../test/access/AccessControl.test.js | 19 + .../test/access/Ownable.test.js | 79 + .../test/access/Ownable2Step.test.js | 102 + .../AccessControlDefaultAdminRules.test.js | 32 + .../AccessControlEnumerable.test.js | 24 + .../test/access/manager/AccessManaged.test.js | 146 + .../access/manager/AccessManager.behavior.js | 257 + .../access/manager/AccessManager.predicate.js | 456 + .../test/access/manager/AccessManager.test.js | 2489 + .../access/manager/AuthorityUtils.test.js | 102 + .../account/utils/draft-ERC4337Utils.test.js | 288 + .../account/utils/draft-ERC7579Utils.t.sol | 421 + .../account/utils/draft-ERC7579Utils.test.js | 353 + .../test/bin/EntryPoint070.abi | 1 + .../test/bin/EntryPoint070.bytecode | Bin 0 -> 16035 bytes .../test/bin/SenderCreator070.abi | 1 + .../test/bin/SenderCreator070.bytecode | Bin 0 -> 451 bytes .../test/finance/VestingWallet.behavior.js | 87 + .../test/finance/VestingWallet.test.js | 65 + .../test/finance/VestingWalletCliff.test.js | 70 + .../test/governance/Governor.t.sol | 55 + .../test/governance/Governor.test.js | 992 + .../governance/TimelockController.test.js | 1279 + .../GovernorCountingFractional.test.js | 248 + .../GovernorCountingOverridable.test.js | 346 + .../extensions/GovernorERC721.test.js | 131 + .../GovernorPreventLateQuorum.test.js | 185 + .../extensions/GovernorStorage.test.js | 155 + .../extensions/GovernorTimelockAccess.test.js | 864 + .../GovernorTimelockCompound.test.js | 448 + .../GovernorTimelockControl.test.js | 504 + .../GovernorVotesQuorumFraction.test.js | 165 + .../extensions/GovernorWithParams.test.js | 245 + .../test/governance/utils/ERC6372.behavior.js | 28 + .../test/governance/utils/Votes.behavior.js | 325 + .../test/governance/utils/Votes.test.js | 102 + .../governance/utils/VotesExtended.test.js | 152 + .../test/helpers/access-manager.js | 85 + .../test/helpers/account.js | 14 + .../test/helpers/chains.js | 109 + .../test/helpers/constants.js | 4 + .../test/helpers/deploy.js | 14 + .../test/helpers/eip712-types.js | 59 + .../test/helpers/eip712.js | 45 + .../test/helpers/enums.js | 12 + .../test/helpers/erc4337-entrypoint.js | 31 + .../test/helpers/erc4337.js | 111 + .../test/helpers/erc7579.js | 58 + .../test/helpers/governance.js | 215 + .../test/helpers/iterate.js | 36 + .../test/helpers/math.js | 33 + .../test/helpers/methods.js | 14 + .../test/helpers/random.js | 19 + .../test/helpers/storage.js | 48 + .../test/helpers/strings.js | 5 + .../test/helpers/time.js | 30 + .../test/helpers/txpool.js | 29 + .../test/metatx/ERC2771Context.test.js | 133 + .../test/metatx/ERC2771Forwarder.t.sol | 279 + .../test/metatx/ERC2771Forwarder.test.js | 384 + .../test/proxy/Clones.behaviour.js | 160 + .../test/proxy/Clones.t.sol | 91 + .../test/proxy/Clones.test.js | 175 + .../test/proxy/ERC1967/ERC1967Proxy.test.js | 23 + .../test/proxy/ERC1967/ERC1967Utils.test.js | 162 + .../test/proxy/Proxy.behaviour.js | 185 + .../test/proxy/beacon/BeaconProxy.test.js | 141 + .../proxy/beacon/UpgradeableBeacon.test.js | 55 + .../test/proxy/transparent/ProxyAdmin.test.js | 82 + .../TransparentUpgradeableProxy.behaviour.js | 357 + .../TransparentUpgradeableProxy.test.js | 28 + .../test/proxy/utils/Initializable.test.js | 216 + .../test/proxy/utils/UUPSUpgradeable.test.js | 120 + .../test/sanity.test.js | 27 + .../test/token/ERC1155/ERC1155.behavior.js | 763 + .../test/token/ERC1155/ERC1155.test.js | 213 + .../extensions/ERC1155Burnable.test.js | 66 + .../extensions/ERC1155Pausable.test.js | 105 + .../ERC1155/extensions/ERC1155Supply.test.js | 119 + .../extensions/ERC1155URIStorage.test.js | 70 + .../token/ERC1155/utils/ERC1155Holder.test.js | 56 + .../token/ERC1155/utils/ERC1155Utils.test.js | 299 + .../test/token/ERC20/ERC20.behavior.js | 269 + .../test/token/ERC20/ERC20.test.js | 199 + .../token/ERC20/extensions/ERC1363.test.js | 370 + .../ERC20/extensions/ERC20Burnable.test.js | 105 + .../ERC20/extensions/ERC20Capped.test.js | 55 + .../ERC20/extensions/ERC20FlashMint.test.js | 164 + .../ERC20/extensions/ERC20Pausable.test.js | 129 + .../ERC20/extensions/ERC20Permit.test.js | 109 + .../token/ERC20/extensions/ERC20Votes.test.js | 546 + .../ERC20/extensions/ERC20Wrapper.test.js | 203 + .../test/token/ERC20/extensions/ERC4626.t.sol | 41 + .../token/ERC20/extensions/ERC4626.test.js | 888 + .../draft-ERC20TemporaryApproval.test.js | 142 + .../test/token/ERC20/utils/SafeERC20.test.js | 427 + .../test/token/ERC721/ERC721.behavior.js | 972 + .../test/token/ERC721/ERC721.test.js | 23 + .../token/ERC721/ERC721Enumerable.test.js | 28 + .../ERC721/extensions/ERC721Burnable.test.js | 77 + .../ERC721/extensions/ERC721Consecutive.t.sol | 181 + .../extensions/ERC721Consecutive.test.js | 236 + .../ERC721/extensions/ERC721Pausable.test.js | 81 + .../ERC721/extensions/ERC721Royalty.test.js | 57 + .../extensions/ERC721URIStorage.test.js | 121 + .../ERC721/extensions/ERC721Votes.test.js | 194 + .../ERC721/extensions/ERC721Wrapper.test.js | 201 + .../token/ERC721/utils/ERC721Holder.test.js | 20 + .../token/ERC721/utils/ERC721Utils.test.js | 94 + .../test/token/common/ERC2981.behavior.js | 152 + .../test/utils/Address.test.js | 281 + .../test/utils/Arrays.t.sol | 31 + .../test/utils/Arrays.test.js | 223 + .../test/utils/Base64.t.sol | 34 + .../test/utils/Base64.test.js | 59 + .../test/utils/Bytes.test.js | 88 + .../test/utils/CAIP.test.js | 53 + .../test/utils/Context.behavior.js | 48 + .../test/utils/Context.test.js | 18 + .../test/utils/Create2.t.sol | 17 + .../test/utils/Create2.test.js | 190 + .../test/utils/Multicall.test.js | 72 + .../test/utils/Nonces.behavior.js | 189 + .../test/utils/Nonces.test.js | 16 + .../test/utils/NoncesKeyed.test.js | 17 + .../test/utils/Packing.t.sol | 993 + .../test/utils/Packing.test.js | 70 + .../test/utils/Panic.test.js | 37 + .../test/utils/Pausable.test.js | 90 + .../test/utils/ReentrancyGuard.test.js | 50 + .../test/utils/ShortStrings.t.sol | 109 + .../test/utils/ShortStrings.test.js | 64 + .../test/utils/SlotDerivation.t.sol | 248 + .../test/utils/SlotDerivation.test.js | 58 + .../test/utils/StorageSlot.test.js | 73 + .../test/utils/Strings.t.sol | 50 + .../test/utils/Strings.test.js | 342 + .../test/utils/TransientSlot.test.js | 59 + .../test/utils/cryptography/ECDSA.test.js | 211 + .../test/utils/cryptography/EIP712.test.js | 105 + .../utils/cryptography/MerkleProof.test.js | 213 + .../cryptography/MessageHashUtils.test.js | 68 + .../test/utils/cryptography/P256.t.sol | 44 + .../test/utils/cryptography/P256.test.js | 156 + .../test/utils/cryptography/RSA.helper.js | 17 + .../test/utils/cryptography/RSA.test.js | 102 + .../utils/cryptography/SigVer15_186-3.rsp | 3850 ++ .../cryptography/SignatureChecker.test.js | 61 + .../ecdsa_secp256r1_sha256_p1363_test.json | 3719 ++ .../test/utils/introspection/ERC165.test.js | 18 + .../utils/introspection/ERC165Checker.test.js | 245 + .../SupportsInterface.behavior.js | 145 + .../test/utils/math/Math.t.sol | 311 + .../test/utils/math/Math.test.js | 562 + .../test/utils/math/SafeCast.test.js | 159 + .../test/utils/math/SignedMath.t.sol | 80 + .../test/utils/math/SignedMath.test.js | 53 + .../test/utils/structs/BitMap.test.js | 149 + .../test/utils/structs/Checkpoints.t.sol | 332 + .../test/utils/structs/Checkpoints.test.js | 146 + .../test/utils/structs/CircularBuffer.test.js | 83 + .../utils/structs/DoubleEndedQueue.test.js | 102 + .../utils/structs/EnumerableMap.behavior.js | 151 + .../test/utils/structs/EnumerableMap.test.js | 65 + .../utils/structs/EnumerableSet.behavior.js | 116 + .../test/utils/structs/EnumerableSet.test.js | 61 + .../test/utils/structs/Heap.t.sol | 74 + .../test/utils/structs/Heap.test.js | 113 + .../test/utils/structs/MerkleTree.test.js | 100 + .../test/utils/types/Time.test.js | 135 + .../lib/sequence-v3/package.json | 13 + .../lib/sequence-v3/pnpm-lock.yaml | 157 + .../lib/sequence-v3/pnpm-workspace.yaml | 2 + .../lib/sequence-v3/script/Deploy.s.sol | 36 + .../lib/sequence-v3/src/Estimator.sol | 111 + .../lib/sequence-v3/src/Factory.sol | 28 + .../lib/sequence-v3/src/Guest.sol | 76 + .../lib/sequence-v3/src/Simulator.sol | 109 + .../lib/sequence-v3/src/Stage1Module.sol | 29 + .../lib/sequence-v3/src/Stage2Module.sol | 27 + .../lib/sequence-v3/src/Wallet.huff | 55 + .../lib/sequence-v3/src/Wallet.sol | 67 + .../src/extensions/passkeys/Passkeys.sol | 123 + .../src/extensions/recovery/Recovery.sol | 217 + .../src/extensions/sessions/SessionErrors.sol | 56 + .../extensions/sessions/SessionManager.sol | 133 + .../src/extensions/sessions/SessionSig.sol | 421 + .../explicit/ExplicitSessionManager.sol | 171 + .../explicit/IExplicitSessionManager.sol | 41 + .../sessions/explicit/Permission.sol | 112 + .../sessions/explicit/PermissionValidator.sol | 135 + .../sessions/implicit/Attestation.sol | 122 + .../implicit/ISignalsImplicitMode.sol | 26 + .../implicit/ImplicitSessionManager.sol | 83 + .../lib/sequence-v3/src/modules/Calls.sol | 124 + .../sequence-v3/src/modules/ERC4337v07.sol | 73 + .../lib/sequence-v3/src/modules/Hooks.sol | 124 + .../src/modules/Implementation.sol | 50 + .../lib/sequence-v3/src/modules/Nonce.sol | 48 + .../lib/sequence-v3/src/modules/Payload.sol | 278 + .../src/modules/ReentrancyGuard.sol | 38 + .../lib/sequence-v3/src/modules/Storage.sol | 37 + .../sequence-v3/src/modules/auth/BaseAuth.sol | 174 + .../sequence-v3/src/modules/auth/BaseSig.sol | 500 + .../sequence-v3/src/modules/auth/SelfAuth.sol | 19 + .../src/modules/auth/Stage1Auth.sol | 62 + .../src/modules/auth/Stage2Auth.sol | 45 + .../src/modules/interfaces/IAccount.sol | 65 + .../src/modules/interfaces/IAuth.sol | 17 + .../src/modules/interfaces/ICheckpointer.sol | 25 + .../interfaces/IDelegatedExtension.sol | 25 + .../modules/interfaces/IERC1155Receiver.sol | 38 + .../src/modules/interfaces/IERC1271.sol | 35 + .../modules/interfaces/IERC223Receiver.sol | 15 + .../modules/interfaces/IERC721Receiver.sol | 21 + .../modules/interfaces/IERC777Receiver.sol | 24 + .../src/modules/interfaces/IEntryPoint.sol | 10 + .../src/modules/interfaces/IPartialAuth.sol | 35 + .../src/modules/interfaces/ISapient.sol | 38 + .../lib/sequence-v3/src/utils/Base64.sol | 169 + .../lib/sequence-v3/src/utils/LibBytes.sol | 120 + .../lib/sequence-v3/src/utils/LibOptim.sol | 68 + .../lib/sequence-v3/src/utils/P256.sol | 153 + .../lib/sequence-v3/src/utils/WebAuthn.sol | 333 + .../lib/sequence-v3/test/Factory.t.sol | 32 + .../lib/sequence-v3/test/Guest.t.sol | 287 + .../lib/sequence-v3/test/Stage1Module.t.sol | 1305 + .../lib/sequence-v3/test/Wallet.t.sol | 110 + .../test/extensions/passkeys/Passkeys.t.sol | 540 + .../test/extensions/recovery/Recovery.t.sol | 595 + .../extensions/sessions/Attestation.t.sol | 51 + .../test/extensions/sessions/Permission.t.sol | 157 + .../extensions/sessions/SessionCalls.t.sol | 299 + .../extensions/sessions/SessionManager.t.sol | 855 + .../test/extensions/sessions/SessionSig.t.sol | 1020 + .../extensions/sessions/SessionTestBase.sol | 209 + .../explicit/ExplicitSessionManager.t.sol | 866 + .../explicit/PermissionValidator.t.sol | 323 + .../implicit/ImplicitSessionManager.t.sol | 221 + .../recovery/RecoveryDenialOfService.t.sol | 199 + .../sessions/ExtendedSessionTestBase.sol | 175 + .../sessions/SessionDenialOfService.t.sol | 191 + .../sessions/SessionLimitIncrementTest.t.sol | 76 + .../extensions/sessions/SessionSelfCall.t.sol | 72 + .../sessions/SessionSignatureAbuse.t.sol | 143 + .../sessions/SessionUsingERC4337.t.sol | 142 + .../sessions/SessionValueForwarding.t.sol | 295 + .../ERC4337v07/ERC4337Entrypoint.t.sol | 179 + .../lib/sequence-v3/test/mocks/AcceptAll.sol | 10 + .../lib/sequence-v3/test/mocks/CanReenter.sol | 11 + .../lib/sequence-v3/test/mocks/Emitter.sol | 39 + .../lib/sequence-v3/test/mocks/MockERC20.sol | 46 + .../test/mocks/MockPayableReceiver.sol | 8 + .../sequence-v3/test/mocks/ValueForwarder.sol | 14 + .../sequence-v3/test/modules/BaseSig.t.sol | 2550 ++ .../lib/sequence-v3/test/modules/Calls.t.sol | 457 + .../sequence-v3/test/modules/ERC4337v07.t.sol | 246 + .../lib/sequence-v3/test/modules/Hooks.t.sol | 174 + .../test/modules/Implementation.t.sol | 31 + .../lib/sequence-v3/test/modules/Nonce.t.sol | 50 + .../sequence-v3/test/modules/Payload.t.sol | 381 + .../lib/sequence-v3/test/utils/Base64.t.sol | 161 + .../lib/sequence-v3/test/utils/LibBytes.t.sol | 335 + .../sequence-v3/test/utils/PrimitivesRPC.sol | 581 + .../lib/sequence-v3/test/utils/TestUtils.sol | 129 + .../signals-implicit-mode/script/Deploy.s.sol | 18 + .../src/helper/SignalsImplicitMode.sol | 55 + .../src/registry/IImplicitProjectRegistry.sol | 70 + .../registry/IImplicitProjectValidation.sol | 25 + .../src/registry/ImplicitProjectRegistry.sol | 189 + .../test/ImplicitProjectRegistry.t.sol | 710 + .../test/SignalsImplicitMode.t.sol | 115 + .../signals-implicit-mode/test/TestHelper.sol | 61 + .../test/mock/SignalsImplicitModeMock.sol | 12 + packages/sequence-core-1.0.0/package.json | 60 + .../packages/services/README.md | 3 + .../packages/services/api/CHANGELOG.md | 2154 + .../packages/services/api/README.md | 3 + .../packages/services/api/package.json | 28 + .../packages/services/api/src/api.gen.ts | 4517 ++ .../packages/services/api/src/index.ts | 36 + .../packages/services/api/tsconfig.json | 10 + .../packages/services/builder/CHANGELOG.md | 155 + .../packages/services/builder/README.md | 3 + .../packages/services/builder/package.json | 28 + .../services/builder/src/builder.gen.ts | 714 + .../packages/services/builder/src/index.ts | 30 + .../packages/services/builder/tsconfig.json | 10 + .../packages/services/guard/CHANGELOG.md | 2241 + .../packages/services/guard/README.md | 3 + .../packages/services/guard/package.json | 35 + .../services/guard/src/client/guard.gen.ts | 1087 + .../packages/services/guard/src/index.ts | 6 + .../packages/services/guard/src/local.ts | 23 + .../packages/services/guard/src/sequence.ts | 56 + .../packages/services/guard/src/types.ts | 27 + .../services/guard/test/sequence.test.ts | 189 + .../packages/services/guard/tsconfig.json | 10 + .../services/identity-instrument/package.json | 32 + .../identity-instrument/src/challenge.ts | 221 + .../src/identity-instrument.gen.ts | 781 + .../services/identity-instrument/src/index.ts | 88 + .../test/challenge.test.ts | 197 + .../identity-instrument/tsconfig.json | 10 + .../identity-instrument/vitest.config.ts | 8 + .../packages/services/indexer/CHANGELOG.md | 1785 + .../packages/services/indexer/README.md | 3 + .../packages/services/indexer/package.json | 28 + .../packages/services/indexer/src/index.ts | 71 + .../services/indexer/src/indexer.gen.ts | 2353 + .../services/indexer/src/indexergw.gen.ts | 1523 + .../packages/services/indexer/tsconfig.json | 10 + .../services/marketplace/CHANGELOG.md | 238 + .../packages/services/marketplace/README.md | 3 + .../services/marketplace/package.json | 28 + .../services/marketplace/src/index.ts | 36 + .../marketplace/src/marketplace.gen.ts | 2483 + .../services/marketplace/tsconfig.json | 10 + .../packages/services/metadata/CHANGELOG.md | 1795 + .../packages/services/metadata/README.md | 3 + .../packages/services/metadata/package.json | 28 + .../packages/services/metadata/src/index.ts | 66 + .../services/metadata/src/metadata.gen.ts | 2818 ++ .../packages/services/metadata/tsconfig.json | 10 + .../packages/services/relayer/CHANGELOG.md | 3207 ++ .../packages/services/relayer/README.md | 3 + .../services/relayer/hardhat.config.js | 15 + .../packages/services/relayer/package.json | 33 + .../packages/services/relayer/src/index.ts | 1 + .../services/relayer/src/local-relayer.ts | 125 + .../services/relayer/src/provider-relayer.ts | 284 + .../services/relayer/src/rpc-relayer/index.ts | 1 + .../relayer/src/rpc-relayer/relayer.gen.ts | 2037 + .../packages/services/relayer/tsconfig.json | 10 + .../packages/utils/README.md | 4 + .../packages/utils/abi/CHANGELOG.md | 1707 + .../packages/utils/abi/README.md | 3 + .../packages/utils/abi/package.json | 28 + .../packages/utils/abi/src/index.ts | 22 + .../utils/abi/src/sale/erc1155Sale.ts | 352 + .../packages/utils/abi/src/sale/erc721Sale.ts | 352 + .../packages/utils/abi/src/tokens/erc1155.ts | 422 + .../utils/abi/src/tokens/erc1155Items.ts | 378 + .../packages/utils/abi/src/tokens/erc20.ts | 316 + .../packages/utils/abi/src/tokens/erc6909.ts | 404 + .../packages/utils/abi/src/tokens/erc721.ts | 441 + .../utils/abi/src/tokens/erc721Items.ts | 441 + .../packages/utils/abi/src/wallet/erc1271.ts | 26 + .../packages/utils/abi/src/wallet/erc5719.ts | 19 + .../packages/utils/abi/src/wallet/erc6492.ts | 61 + .../packages/utils/abi/src/wallet/factory.ts | 18 + .../packages/utils/abi/src/wallet/index.ts | 26 + .../src/wallet/libs/requireFreshSigners.ts | 15 + .../utils/abi/src/wallet/mainModule.ts | 158 + .../abi/src/wallet/mainModuleUpgradable.ts | 28 + .../utils/abi/src/wallet/moduleHooks.ts | 248 + .../utils/abi/src/wallet/sequenceUtils.ts | 516 + .../utils/abi/src/wallet/walletProxyHook.ts | 9 + .../packages/utils/abi/tsconfig.json | 10 + .../packages/wallet/core/package.json | 41 + .../packages/wallet/core/src/envelope.ts | 148 + .../packages/wallet/core/src/index.ts | 8 + .../wallet/core/src/preconditions/codec.ts | 193 + .../wallet/core/src/preconditions/index.ts | 3 + .../core/src/preconditions/selectors.ts | 41 + .../wallet/core/src/preconditions/types.ts | 201 + .../wallet/core/src/relayer/bundler.ts | 23 + .../wallet/core/src/relayer/bundlers/index.ts | 1 + .../core/src/relayer/bundlers/pimlico.ts | 177 + .../packages/wallet/core/src/relayer/index.ts | 7 + .../wallet/core/src/relayer/relayer.ts | 87 + .../wallet/core/src/relayer/standard/abi.ts | 13 + .../core/src/relayer/standard/eip6963.ts | 70 + .../wallet/core/src/relayer/standard/index.ts | 5 + .../wallet/core/src/relayer/standard/local.ts | 347 + .../core/src/relayer/standard/pk-relayer.ts | 133 + .../core/src/relayer/standard/rpc/index.ts | 375 + .../src/relayer/standard/rpc/relayer.gen.ts | 2037 + .../core/src/relayer/standard/sequence.ts | 95 + .../packages/wallet/core/src/signers/guard.ts | 110 + .../packages/wallet/core/src/signers/index.ts | 45 + .../wallet/core/src/signers/passkey.ts | 284 + .../wallet/core/src/signers/pk/encrypted.ts | 157 + .../wallet/core/src/signers/pk/index.ts | 77 + .../core/src/signers/session-manager.ts | 369 + .../core/src/signers/session/explicit.ts | 385 + .../core/src/signers/session/implicit.ts | 176 + .../wallet/core/src/signers/session/index.ts | 3 + .../core/src/signers/session/session.ts | 62 + .../packages/wallet/core/src/state/cached.ts | 235 + .../packages/wallet/core/src/state/debug.ts | 126 + .../packages/wallet/core/src/state/index.ts | 87 + .../wallet/core/src/state/local/index.ts | 441 + .../wallet/core/src/state/local/indexed-db.ts | 204 + .../wallet/core/src/state/local/memory.ts | 156 + .../wallet/core/src/state/remote/dev-http.ts | 253 + .../wallet/core/src/state/remote/index.ts | 1 + .../wallet/core/src/state/sequence/index.ts | 673 + .../core/src/state/sequence/sessions.gen.ts | 1021 + .../packages/wallet/core/src/state/utils.ts | 59 + .../packages/wallet/core/src/utils/index.ts | 1 + .../src/utils/session/permission-builder.ts | 337 + .../packages/wallet/core/src/wallet.ts | 609 + .../packages/wallet/core/test/constants.ts | 18 + .../wallet/core/test/envelope.test.ts | 617 + .../wallet/core/test/preconditions.test.ts | 283 + .../core/test/preconditions/codec.test.ts | 554 + .../core/test/preconditions/selectors.test.ts | 415 + .../core/test/preconditions/types.test.ts | 443 + .../wallet/core/test/relayer/bundler.test.ts | 306 + .../wallet/core/test/relayer/relayer.test.ts | 366 + .../wallet/core/test/session-manager.test.ts | 1097 + .../packages/wallet/core/test/setup.ts | 63 + .../wallet/core/test/signers-guard.test.ts | 298 + .../wallet/core/test/signers-index.test.ts | 96 + .../wallet/core/test/signers-passkey.test.ts | 666 + .../core/test/signers-pk-encrypted.test.ts | 425 + .../wallet/core/test/signers-pk.test.ts | 252 + .../test/signers-session-explicit.test.ts | 571 + .../test/signers-session-implicit.test.ts | 488 + .../wallet/core/test/state/cached.test.ts | 536 + .../wallet/core/test/state/debug.test.ts | 335 + .../core/test/state/local/memory.test.ts | 220 + .../wallet/core/test/state/utils.test.ts | 410 + .../utils/session/permission-builder.test.ts | 767 + .../packages/wallet/core/test/wallet.test.ts | 392 + .../packages/wallet/core/tsconfig.json | 10 + .../packages/wallet/core/vitest.config.ts | 9 + .../packages/wallet/dapp-client/README.md | 238 + .../wallet/dapp-client/eslint.config.mjs | 4 + .../packages/wallet/dapp-client/package.json | 38 + .../dapp-client/src/ChainSessionManager.ts | 991 + .../wallet/dapp-client/src/DappClient.ts | 801 + .../wallet/dapp-client/src/DappTransport.ts | 527 + .../packages/wallet/dapp-client/src/index.ts | 48 + .../wallet/dapp-client/src/types/index.ts | 218 + .../wallet/dapp-client/src/utils/constants.ts | 5 + .../wallet/dapp-client/src/utils/errors.ts | 62 + .../wallet/dapp-client/src/utils/index.ts | 186 + .../wallet/dapp-client/src/utils/storage.ts | 272 + .../packages/wallet/dapp-client/tsconfig.json | 10 + .../wallet/primitives-cli/eslint.config.mjs | 4 + .../wallet/primitives-cli/package.json | 36 + .../wallet/primitives-cli/src/index.ts | 27 + .../primitives-cli/src/subcommands/address.ts | 68 + .../primitives-cli/src/subcommands/config.ts | 221 + .../src/subcommands/devTools.ts | 269 + .../src/subcommands/passkeys.ts | 298 + .../primitives-cli/src/subcommands/payload.ts | 159 + .../src/subcommands/recovery.ts | 191 + .../primitives-cli/src/subcommands/server.ts | 403 + .../primitives-cli/src/subcommands/session.ts | 142 + .../src/subcommands/sessionExplicit.ts | 95 + .../src/subcommands/sessionImplicit.ts | 79 + .../src/subcommands/signature.ts | 223 + .../wallet/primitives-cli/src/utils.ts | 37 + .../wallet/primitives-cli/tsconfig.json | 10 + .../wallet/primitives/eslint.config.mjs | 4 + .../packages/wallet/primitives/package.json | 33 + .../packages/wallet/primitives/src/address.ts | 19 + .../wallet/primitives/src/attestation.ts | 124 + .../packages/wallet/primitives/src/config.ts | 619 + .../wallet/primitives/src/constants.ts | 65 + .../packages/wallet/primitives/src/context.ts | 77 + .../wallet/primitives/src/erc-6492.ts | 97 + .../wallet/primitives/src/extensions/index.ts | 28 + .../primitives/src/extensions/passkeys.ts | 283 + .../primitives/src/extensions/recovery.ts | 548 + .../wallet/primitives/src/generic-tree.ts | 55 + .../packages/wallet/primitives/src/index.ts | 16 + .../packages/wallet/primitives/src/network.ts | 1013 + .../packages/wallet/primitives/src/payload.ts | 951 + .../wallet/primitives/src/permission.ts | 285 + .../wallet/primitives/src/precondition.ts | 117 + .../wallet/primitives/src/session-config.ts | 681 + .../primitives/src/session-signature.ts | 199 + .../wallet/primitives/src/signature.ts | 1399 + .../packages/wallet/primitives/src/utils.ts | 109 + .../wallet/primitives/test/address.test.ts | 320 + .../primitives/test/attestation.test.ts | 419 + .../wallet/primitives/test/config.test.ts | 932 + .../wallet/primitives/test/erc-6492.test.ts | 485 + .../primitives/test/generic-tree.test.ts | 453 + .../wallet/primitives/test/passkeys.test.ts | 828 + .../wallet/primitives/test/payload.test.ts | 1075 + .../wallet/primitives/test/permission.test.ts | 822 + .../primitives/test/precondition.test.ts | 695 + .../wallet/primitives/test/recovery.test.ts | 925 + .../primitives/test/session-config.test.ts | 931 + .../primitives/test/session-signature.test.ts | 833 + .../wallet/primitives/test/signature.test.ts | 2183 + .../wallet/primitives/test/utils.test.ts | 541 + .../packages/wallet/primitives/tsconfig.json | 10 + .../wallet/primitives/vitest.config.ts | 9 + .../packages/wallet/wdk/.env.test | 5 + .../packages/wallet/wdk/package.json | 45 + .../wallet/wdk/src/dbs/auth-commitments.ts | 31 + .../packages/wallet/wdk/src/dbs/auth-keys.ts | 125 + .../packages/wallet/wdk/src/dbs/generic.ts | 196 + .../packages/wallet/wdk/src/dbs/index.ts | 16 + .../packages/wallet/wdk/src/dbs/messages.ts | 21 + .../wallet/wdk/src/dbs/passkey-credentials.ts | 68 + .../packages/wallet/wdk/src/dbs/recovery.ts | 20 + .../packages/wallet/wdk/src/dbs/signatures.ts | 20 + .../wallet/wdk/src/dbs/transactions.ts | 21 + .../packages/wallet/wdk/src/dbs/wallets.ts | 21 + .../wallet/wdk/src/identity/signer.ts | 78 + .../packages/wallet/wdk/src/index.ts | 2 + .../packages/wallet/wdk/src/sequence/cron.ts | 174 + .../wallet/wdk/src/sequence/devices.ts | 53 + .../wallet/wdk/src/sequence/errors.ts | 20 + .../wallet/wdk/src/sequence/guards.ts | 44 + .../src/sequence/handlers/authcode-pkce.ts | 71 + .../wdk/src/sequence/handlers/authcode.ts | 114 + .../wdk/src/sequence/handlers/devices.ts | 53 + .../wallet/wdk/src/sequence/handlers/guard.ts | 111 + .../wdk/src/sequence/handlers/handler.ts | 14 + .../wdk/src/sequence/handlers/identity.ts | 100 + .../wallet/wdk/src/sequence/handlers/index.ts | 6 + .../wdk/src/sequence/handlers/mnemonic.ts | 121 + .../wallet/wdk/src/sequence/handlers/otp.ts | 128 + .../wdk/src/sequence/handlers/passkeys.ts | 110 + .../wdk/src/sequence/handlers/recovery.ts | 88 + .../packages/wallet/wdk/src/sequence/index.ts | 25 + .../wallet/wdk/src/sequence/logger.ts | 11 + .../wallet/wdk/src/sequence/manager.ts | 551 + .../wallet/wdk/src/sequence/messages.ts | 249 + .../wallet/wdk/src/sequence/recovery.ts | 610 + .../wallet/wdk/src/sequence/sessions.ts | 441 + .../wallet/wdk/src/sequence/signatures.ts | 440 + .../wallet/wdk/src/sequence/signers.ts | 95 + .../wallet/wdk/src/sequence/transactions.ts | 661 + .../wallet/wdk/src/sequence/types/device.ts | 17 + .../wallet/wdk/src/sequence/types/index.ts | 31 + .../wdk/src/sequence/types/message-request.ts | 26 + .../wallet/wdk/src/sequence/types/module.ts | 7 + .../wallet/wdk/src/sequence/types/recovery.ts | 15 + .../src/sequence/types/signature-request.ts | 170 + .../wallet/wdk/src/sequence/types/signer.ts | 33 + .../src/sequence/types/transaction-request.ts | 87 + .../wallet/wdk/src/sequence/types/wallet.ts | 120 + .../wallet/wdk/src/sequence/wallets.ts | 1392 + .../wallet/wdk/test/authcode-pkce.test.ts | 363 + .../packages/wallet/wdk/test/authcode.test.ts | 764 + .../packages/wallet/wdk/test/constants.ts | 139 + .../packages/wallet/wdk/test/guard.test.ts | 318 + .../wallet/wdk/test/identity-auth-dbs.test.ts | 430 + .../wallet/wdk/test/identity-signer.test.ts | 527 + .../packages/wallet/wdk/test/messages.test.ts | 428 + .../packages/wallet/wdk/test/otp.test.ts | 750 + .../packages/wallet/wdk/test/passkeys.test.ts | 640 + .../packages/wallet/wdk/test/recovery.test.ts | 503 + .../packages/wallet/wdk/test/sessions.test.ts | 526 + .../packages/wallet/wdk/test/setup.ts | 86 + .../wallet/wdk/test/transactions.test.ts | 965 + .../packages/wallet/wdk/test/wallets.test.ts | 872 + .../packages/wallet/wdk/tsconfig.json | 10 + .../packages/wallet/wdk/vitest.config.ts | 21 + .../sequence-core-1.0.0/pnpm-workspace.yaml | 22 + packages/sequence-core-1.0.0/repo/README.md | 4 + .../repo/eslint-config/README.md | 3 + .../repo/eslint-config/base.js | 52 + .../repo/eslint-config/next.js | 49 + .../repo/eslint-config/package.json | 24 + .../repo/eslint-config/react-internal.js | 39 + .../repo/typescript-config/base.json | 19 + .../repo/typescript-config/nextjs.json | 12 + .../repo/typescript-config/package.json | 9 + .../repo/typescript-config/react-library.json | 7 + .../repo/ui/eslint.config.mjs | 4 + .../sequence-core-1.0.0/repo/ui/package.json | 28 + .../repo/ui/src/button.tsx | 17 + .../sequence-core-1.0.0/repo/ui/src/card.tsx | 27 + .../sequence-core-1.0.0/repo/ui/src/code.tsx | 5 + .../sequence-core-1.0.0/repo/ui/tsconfig.json | 8 + .../repo/ui/turbo/generators/config.ts | 30 + .../turbo/generators/templates/component.hbs | 8 + packages/sequence-core-1.0.0/turbo.json | 36 + .../wallet-contracts/.eslintignore | 3 + .../wallet-contracts/.eslintrc.js | 38 + .../wallet-contracts/.gitattributes | 1 + .../actions/install-dependencies/action.yml | 36 + .../wallet-contracts/.github/workflows/ci.yml | 108 + .../wallet-contracts/.gitmodules | 6 + .../wallet-contracts/.prettierrc | 9 + .../wallet-contracts/.solcover.js | 3 + .../wallet-contracts/.solhint.json | 26 + .../0xsequence-wallet-contracts-3.0.1.tgz | Bin 0 -> 34322 bytes .../Counter/.github/workflows/test.yml | 38 + .../wallet-contracts/Counter/.gitmodules | 3 + .../wallet-contracts/Counter/README.md | 66 + .../Counter.s.sol/1/run-1763223074249.json | 47 + .../Counter.s.sol/1/run-1763223530987.json | 47 + .../broadcast/Counter.s.sol/1/run-latest.json | 47 + .../wallet-contracts/Counter/foundry.lock | 8 + .../wallet-contracts/Counter/foundry.toml | 6 + .../Counter/lib/forge-std/.gitattributes | 1 + .../Counter/lib/forge-std/.github/CODEOWNERS | 1 + .../lib/forge-std/.github/dependabot.yml | 6 + .../lib/forge-std/.github/workflows/ci.yml | 142 + .../lib/forge-std/.github/workflows/sync.yml | 36 + .../Counter/lib/forge-std/CONTRIBUTING.md | 193 + .../Counter/lib/forge-std/LICENSE-APACHE | 203 + .../Counter/lib/forge-std/LICENSE-MIT | 25 + .../Counter/lib/forge-std/README.md | 266 + .../lib/forge-std/RELEASE_CHECKLIST.md | 12 + .../Counter/lib/forge-std/foundry.toml | 27 + .../Counter/lib/forge-std/package.json | 16 + .../Counter/lib/forge-std/scripts/vm.py | 646 + .../Counter/lib/forge-std/src/Base.sol | 48 + .../Counter/lib/forge-std/src/Config.sol | 60 + .../Counter/lib/forge-std/src/LibVariable.sol | 477 + .../Counter/lib/forge-std/src/Script.sol | 28 + .../lib/forge-std/src/StdAssertions.sol | 764 + .../Counter/lib/forge-std/src/StdChains.sol | 287 + .../Counter/lib/forge-std/src/StdCheats.sol | 829 + .../Counter/lib/forge-std/src/StdConfig.sol | 612 + .../lib/forge-std/src/StdConstants.sol | 30 + .../Counter/lib/forge-std/src/StdError.sol | 15 + .../lib/forge-std/src/StdInvariant.sol | 122 + .../Counter/lib/forge-std/src/StdJson.sol | 283 + .../Counter/lib/forge-std/src/StdMath.sol | 43 + .../Counter/lib/forge-std/src/StdStorage.sol | 473 + .../Counter/lib/forge-std/src/StdStyle.sol | 333 + .../Counter/lib/forge-std/src/StdToml.sol | 283 + .../Counter/lib/forge-std/src/StdUtils.sol | 208 + .../Counter/lib/forge-std/src/Test.sol | 34 + .../Counter/lib/forge-std/src/Vm.sol | 2494 + .../Counter/lib/forge-std/src/console.sol | 1560 + .../Counter/lib/forge-std/src/console2.sol | 4 + .../lib/forge-std/src/interfaces/IERC1155.sol | 105 + .../lib/forge-std/src/interfaces/IERC165.sol | 12 + .../lib/forge-std/src/interfaces/IERC20.sol | 43 + .../lib/forge-std/src/interfaces/IERC4626.sol | 190 + .../lib/forge-std/src/interfaces/IERC6909.sol | 72 + .../lib/forge-std/src/interfaces/IERC721.sol | 164 + .../lib/forge-std/src/interfaces/IERC7540.sol | 150 + .../lib/forge-std/src/interfaces/IERC7575.sol | 241 + .../forge-std/src/interfaces/IMulticall3.sol | 73 + .../Counter/lib/forge-std/src/safeconsole.sol | 13937 ++++++ .../lib/forge-std/test/CommonBase.t.sol | 44 + .../Counter/lib/forge-std/test/Config.t.sol | 352 + .../lib/forge-std/test/LibVariable.t.sol | 434 + .../lib/forge-std/test/StdAssertions.t.sol | 141 + .../lib/forge-std/test/StdChains.t.sol | 227 + .../lib/forge-std/test/StdCheats.t.sol | 639 + .../lib/forge-std/test/StdConstants.t.sol | 38 + .../Counter/lib/forge-std/test/StdError.t.sol | 120 + .../Counter/lib/forge-std/test/StdJson.t.sol | 49 + .../Counter/lib/forge-std/test/StdMath.t.sol | 202 + .../lib/forge-std/test/StdStorage.t.sol | 488 + .../Counter/lib/forge-std/test/StdStyle.t.sol | 110 + .../Counter/lib/forge-std/test/StdToml.t.sol | 49 + .../Counter/lib/forge-std/test/StdUtils.t.sol | 342 + .../Counter/lib/forge-std/test/Vm.t.sol | 18 + .../test/compilation/CompilationScript.sol | 10 + .../compilation/CompilationScriptBase.sol | 10 + .../test/compilation/CompilationTest.sol | 10 + .../test/compilation/CompilationTestBase.sol | 10 + .../test/fixtures/broadcast.log.json | 187 + .../lib/forge-std/test/fixtures/config.toml | 81 + .../lib/forge-std/test/fixtures/test.json | 8 + .../lib/forge-std/test/fixtures/test.toml | 6 + .../Counter/script/Counter.s.sol | 19 + .../wallet-contracts/Counter/src/Counter.sol | 14 + .../Counter/test/Counter.t.sol | 24 + .../Counter/utils/JsonBindings.sol | 18 + .../wallet-contracts/LICENSE | 219 + .../wallet-contracts/README.md | 56 + .../audits/v1/Consensys_Diligence.md | 7 + .../v1/Quantstamp_Arcadeum_Report_Final.pdf | Bin 0 -> 399997 bytes .../v1/sequence_quantstamp_audit_feb_2021.pdf | Bin 0 -> 376935 bytes .../Sequence Wallet - Zellic Audit Report.pdf | Bin 0 -> 1669108 bytes ...-horizon-sequence-wallet-audit-2023-02.pdf | Bin 0 -> 1020223 bytes .../wallet-contracts/config/PROD.env.sample | 3 + .../wallet-contracts/contracts/Factory.sol | 23 + .../wallet-contracts/contracts/Wallet.sol | 60 + .../contracts/hooks/WalletProxyHook.sol | 12 + .../hooks/interfaces/IWalletProxy.sol | 12 + .../contracts/interfaces/IERC1271Wallet.sol | 26 + .../interfaces/receivers/IERC1155Receiver.sol | 9 + .../interfaces/receivers/IERC223Receiver.sol | 6 + .../interfaces/receivers/IERC721Receiver.sol | 6 + .../interfaces/receivers/IERC777Receiver.sol | 6 + .../contracts/interfaces/tokens/IERC1155.sol | 27 + .../contracts/interfaces/tokens/IERC20.sol | 14 + .../contracts/interfaces/tokens/IERC721.sol | 18 + .../contracts/mocks/AlwaysRevertMock.sol | 8 + .../contracts/mocks/CallReceiverMock.sol | 22 + .../contracts/mocks/DelegateCallMock.sol | 23 + .../contracts/mocks/ERC1155Mock.sol | 63 + .../contracts/mocks/ERC165CheckerMock.sol | 53 + .../contracts/mocks/ERC20Mock.sol | 51 + .../contracts/mocks/ERC721Mock.sol | 86 + .../contracts/mocks/GasBurnerMock.sol | 17 + .../contracts/mocks/HookCallerMock.sol | 55 + .../contracts/mocks/HookMock.sol | 8 + .../contracts/mocks/LibBytesImpl.sol | 20 + .../contracts/mocks/LibBytesPointerImpl.sol | 24 + .../contracts/mocks/LibStringImp.sol | 24 + .../contracts/mocks/ModuleMock.sol | 10 + .../contracts/modules/GuestModule.sol | 103 + .../contracts/modules/MainModule.sol | 49 + .../modules/MainModuleGasEstimation.sol | 104 + .../modules/MainModuleUpgradable.sol | 61 + .../modules/commons/Implementation.sol | 31 + .../contracts/modules/commons/ModuleAuth.sol | 166 + .../modules/commons/ModuleAuthConvenience.sol | 43 + .../modules/commons/ModuleAuthFixed.sol | 74 + .../modules/commons/ModuleAuthUpgradable.sol | 50 + .../contracts/modules/commons/ModuleCalls.sol | 131 + .../modules/commons/ModuleCreator.sol | 35 + .../modules/commons/ModuleERC165.sol | 17 + .../modules/commons/ModuleERC5719.sol | 18 + .../modules/commons/ModuleExtraAuth.sol | 75 + .../contracts/modules/commons/ModuleHooks.sol | 142 + .../contracts/modules/commons/ModuleIPFS.sol | 37 + .../contracts/modules/commons/ModuleNonce.sol | 66 + .../commons/ModuleOnlyDelegatecall.sol | 22 + .../modules/commons/ModuleSelfAuth.sol | 13 + .../modules/commons/ModuleStorage.sol | 22 + .../modules/commons/ModuleUpdate.sol | 49 + .../ModuleIgnoreAuthUpgradable.sol | 20 + .../gas-estimation/ModuleIgnoreNonceCalls.sol | 34 + .../commons/interfaces/IModuleAuth.sol | 46 + .../interfaces/IModuleAuthUpgradable.sol | 9 + .../commons/interfaces/IModuleCalls.sol | 37 + .../commons/interfaces/IModuleCreator.sol | 13 + .../commons/interfaces/IModuleHooks.sol | 31 + .../commons/interfaces/IModuleUpdate.sol | 21 + .../submodules/auth/SequenceBaseSig.sol | 255 + .../submodules/auth/SequenceChainedSig.sol | 112 + .../submodules/auth/SequenceDynamicSig.sol | 25 + .../submodules/auth/SequenceNoChainIdSig.sol | 14 + .../submodules/nonce/SubModuleNonce.sol | 33 + .../contracts/modules/utils/GasEstimator.sol | 15 + .../modules/utils/MultiCallUtils.sol | 105 + .../contracts/modules/utils/RequireUtils.sol | 112 + .../contracts/modules/utils/SequenceUtils.sol | 7 + .../contracts/trust/Trust.sol | 160 + .../contracts/trust/TrustFactory.sol | 33 + .../contracts/utils/LibAddress.sol | 17 + .../contracts/utils/LibBytes.sol | 75 + .../contracts/utils/LibBytesPointer.sol | 150 + .../contracts/utils/LibOptim.sol | 74 + .../contracts/utils/LibString.sol | 87 + .../contracts/utils/SignatureValidator.sol | 127 + .../wallet-contracts/docs/book.css | 13 + .../wallet-contracts/docs/book.toml | 13 + .../wallet-contracts/docs/solidity.min.js | 74 + .../wallet-contracts/docs/src/README.md | 56 + .../wallet-contracts/docs/src/SUMMARY.md | 92 + .../contracts/Factory.sol/contract.Factory.md | 32 + .../docs/src/contracts/README.md | 11 + .../contracts/Wallet.sol/library.Wallet.md | 58 + .../docs/src/contracts/hooks/README.md | 5 + .../contract.WalletProxyHook.md | 15 + .../interface.IWalletProxy.md | 14 + .../src/contracts/hooks/interfaces/README.md | 4 + .../interface.IERC1271Wallet.md | 57 + .../docs/src/contracts/interfaces/README.md | 6 + .../interface.IERC1155Receiver.md | 21 + .../interface.IERC223Receiver.md | 12 + .../interface.IERC721Receiver.md | 12 + .../interface.IERC777Receiver.md | 12 + .../contracts/interfaces/receivers/README.md | 7 + .../tokens/IERC1155.sol/interface.IERC1155.md | 83 + .../tokens/IERC20.sol/interface.IERC20.md | 60 + .../tokens/IERC721.sol/interface.IERC721.md | 87 + .../src/contracts/interfaces/tokens/README.md | 6 + .../contract.AlwaysRevertMock.md | 12 + .../contract.CallReceiverMock.md | 48 + .../contract.DelegateCallMock.md | 48 + .../ERC1155Mock.sol/contract.ERC1155Mock.md | 110 + .../contract.ERC165CheckerMock.md | 37 + .../mocks/ERC20Mock.sol/contract.ERC20Mock.md | 103 + .../ERC721Mock.sol/contract.ERC721Mock.md | 151 + .../contract.GasBurnerMock.md | 19 + .../contract.HookCallerMock.md | 49 + .../mocks/HookMock.sol/contract.HookMock.md | 12 + .../LibBytesImpl.sol/contract.LibBytesImpl.md | 26 + .../contract.LibBytesPointerImpl.md | 33 + .../LibStringImp.sol/contract.LibStringImp.md | 33 + .../ModuleMock.sol/contract.ModuleMock.md | 19 + .../docs/src/contracts/mocks/README.md | 17 + .../GuestModule.sol/contract.GuestModule.md | 124 + .../MainModule.sol/contract.MainModule.md | 57 + .../contract.MainModuleGasEstimation.md | 85 + .../contract.MainModuleUpgradable.md | 63 + .../docs/src/contracts/modules/README.md | 9 + .../contract.Implementation.md | 42 + .../ModuleAuth.sol/abstract.ModuleAuth.md | 203 + .../abstract.ModuleAuthConvenience.md | 53 + .../abstract.ModuleAuthFixed.md | 108 + .../abstract.ModuleAuthUpgradable.md | 75 + .../ModuleCalls.sol/abstract.ModuleCalls.md | 104 + .../contract.ModuleCreator.md | 57 + .../ModuleERC165.sol/abstract.ModuleERC165.md | 31 + .../contract.ModuleERC5719.md | 15 + .../abstract.ModuleExtraAuth.md | 92 + .../ModuleHooks.sol/contract.ModuleHooks.md | 202 + .../ModuleIPFS.sol/contract.ModuleIPFS.md | 52 + .../ModuleNonce.sol/contract.ModuleNonce.md | 96 + .../contract.ModuleOnlyDelegatecall.md | 36 + .../contract.ModuleSelfAuth.md | 19 + .../library.ModuleStorage.md | 33 + .../ModuleUpdate.sol/contract.ModuleUpdate.md | 70 + .../src/contracts/modules/commons/README.md | 23 + .../abstract.ModuleIgnoreAuthUpgradable.md | 32 + .../abstract.ModuleIgnoreNonceCalls.md | 26 + .../modules/commons/gas-estimation/README.md | 5 + .../IModuleAuth.sol/abstract.IModuleAuth.md | 101 + .../interface.IModuleAuthUpgradable.md | 14 + .../interface.IModuleCalls.md | 78 + .../interface.IModuleCreator.md | 33 + .../interface.IModuleHooks.md | 77 + .../abstract.IModuleUpdate.md | 46 + .../modules/commons/interfaces/README.md | 9 + .../modules/commons/submodules/README.md | 5 + .../modules/commons/submodules/auth/README.md | 7 + .../library.SequenceBaseSig.md | 225 + .../abstract.SequenceChainedSig.md | 91 + .../library.SequenceDynamicSig.md | 35 + .../library.SequenceNoChainIdSig.md | 28 + .../commons/submodules/nonce/README.md | 4 + .../library.SubModuleNonce.md | 51 + .../GasEstimator.sol/contract.GasEstimator.md | 14 + .../contract.MultiCallUtils.md | 133 + .../src/contracts/modules/utils/README.md | 7 + .../RequireUtils.sol/contract.RequireUtils.md | 149 + .../contract.SequenceUtils.md | 7 + .../docs/src/contracts/trust/README.md | 6 + .../trust/Trust.sol/contract.Trust.md | 186 + .../trust/Trust.sol/function.absDiff.md | 8 + .../TrustFactory.sol/contract.TrustFactory.md | 26 + .../LibAddress.sol/library.LibAddress.md | 23 + .../utils/LibBytes.sol/library.LibBytes.md | 114 + .../library.LibBytesPointer.md | 201 + .../utils/LibOptim.sol/library.LibOptim.md | 100 + .../utils/LibString.sol/library.LibString.md | 106 + .../docs/src/contracts/utils/README.md | 9 + .../library.SignatureValidator.md | 130 + .../wallet-contracts/foundry.lock | 8 + .../wallet-contracts/foundry.toml | 11 + .../foundry_test/base/AdvTest.sol | 205 + .../foundry_test/hooks/WalletProxyHook.t.sol | 37 + .../modules/commons/Implementation.t.sol | 66 + .../modules/commons/ModuleCalls.t.sol | 351 + .../modules/commons/ModuleERC5719.t.sol | 42 + .../modules/commons/ModuleExtraAuth.t.sol | 175 + .../modules/commons/ModuleIPFS.t.sol | 46 + .../modules/commons/ModuleStorage.t.sol | 84 + .../submodules/auth/SequenceBaseSig.t.sol | 325 + .../submodules/auth/SequenceChainedSig.t.sol | 276 + .../submodules/auth/SequenceDynamicSig.t.sol | 57 + .../auth/SequenceNoChainIdSig.t.sol | 51 + .../submodules/nonce/SubModuleNonce.t.sol | 16 + .../modules/utils/L2CompressorEncoder.sol | 393 + .../modules/utils/L2CompressorHuff.t.sol | 284 + .../utils/L2CompressorHuffReadExecute.sol | 143 + .../utils/L2CompressorHuffReadFlag.t.sol | 1018 + .../utils/L2CompressorHuffReadNonce.sol | 55 + .../utils/L2CompressorHuffReadTx.t.sol | 93 + .../utils/L2CompressorHuffReadTxs.t.sol | 123 + .../modules/utils/RequireUtils.t.sol | 158 + .../foundry_test/trust/Trust.t.sol | 867 + .../foundry_test/trust/TrustFactory.t.sol | 53 + .../foundry_test/utils/LibAddress.t.sol | 15 + .../foundry_test/utils/LibBytes.t.sol | 100 + .../foundry_test/utils/LibBytesPointer.t.sol | 152 + .../foundry_test/utils/LibOptim.t.sol | 63 + .../utils/SignatureValidator.t.sol | 199 + .../wallet-contracts/funding.json | 5 + .../wallet-contracts/hardhat.config.ts | 72 + .../lib/forge-std/.github/workflows/ci.yml | 92 + .../lib/forge-std/.github/workflows/sync.yml | 29 + .../lib/forge-std/.gitmodules | 3 + .../lib/forge-std/LICENSE-APACHE | 203 + .../lib/forge-std/LICENSE-MIT | 25 + .../wallet-contracts/lib/forge-std/README.md | 250 + .../lib/forge-std/foundry.toml | 21 + .../lib/ds-test/.github/workflows/build.yml | 41 + .../lib/forge-std/lib/ds-test/LICENSE | 674 + .../lib/forge-std/lib/ds-test/Makefile | 14 + .../lib/forge-std/lib/ds-test/default.nix | 4 + .../lib/forge-std/lib/ds-test/demo/demo.sol | 222 + .../lib/forge-std/lib/ds-test/package.json | 15 + .../lib/forge-std/lib/ds-test/src/test.sol | 592 + .../lib/forge-std/lib/ds-test/src/test.t.sol | 417 + .../lib/forge-std/package.json | 16 + .../lib/forge-std/src/Base.sol | 33 + .../lib/forge-std/src/Script.sol | 26 + .../lib/forge-std/src/StdAssertions.sol | 376 + .../lib/forge-std/src/StdChains.sol | 231 + .../lib/forge-std/src/StdCheats.sol | 639 + .../lib/forge-std/src/StdError.sol | 15 + .../lib/forge-std/src/StdInvariant.sol | 92 + .../lib/forge-std/src/StdJson.sol | 179 + .../lib/forge-std/src/StdMath.sol | 43 + .../lib/forge-std/src/StdStorage.sol | 327 + .../lib/forge-std/src/StdStyle.sol | 333 + .../lib/forge-std/src/StdUtils.sol | 192 + .../lib/forge-std/src/Test.sol | 32 + .../wallet-contracts/lib/forge-std/src/Vm.sol | 490 + .../lib/forge-std/src/console.sol | 1533 + .../lib/forge-std/src/console2.sol | 1546 + .../lib/forge-std/src/interfaces/IERC1155.sol | 105 + .../lib/forge-std/src/interfaces/IERC165.sol | 12 + .../lib/forge-std/src/interfaces/IERC20.sol | 43 + .../lib/forge-std/src/interfaces/IERC4626.sol | 190 + .../lib/forge-std/src/interfaces/IERC721.sol | 164 + .../forge-std/src/interfaces/IMulticall3.sol | 73 + .../lib/forge-std/test/StdAssertions.t.sol | 999 + .../lib/forge-std/test/StdChains.t.sol | 160 + .../lib/forge-std/test/StdCheats.t.sol | 418 + .../lib/forge-std/test/StdError.t.sol | 118 + .../lib/forge-std/test/StdMath.t.sol | 197 + .../lib/forge-std/test/StdStorage.t.sol | 283 + .../lib/forge-std/test/StdStyle.t.sol | 110 + .../lib/forge-std/test/StdUtils.t.sol | 297 + .../test/compilation/CompilationScript.sol | 10 + .../compilation/CompilationScriptBase.sol | 10 + .../test/compilation/CompilationTest.sol | 10 + .../test/compilation/CompilationTestBase.sol | 10 + .../test/fixtures/broadcast.log.json | 187 + .../lib/foundry-huff/.git-blame-ignore-revs | 2 + .../foundry-huff/.github/workflows/tests.yaml | 31 + .../lib/foundry-huff/.gitmodules | 6 + .../wallet-contracts/lib/foundry-huff/LICENSE | 201 + .../lib/foundry-huff/README.md | 167 + .../lib/foundry-huff/assets/Group 1.png | Bin 0 -> 23776 bytes .../lib/foundry-huff/assets/banner.jpg | Bin 0 -> 45536 bytes .../black_white_huff-removebg-preview.png | Bin 0 -> 5748 bytes .../foundry-huff/assets/black_white_huff.png | Bin 0 -> 8644 bytes .../lib/foundry-huff/assets/foundry.png | Bin 0 -> 14491 bytes .../assets/foundry_huff_banner.jpg | Bin 0 -> 45502 bytes .../assets/foundry_huff_banner.png | Bin 0 -> 23481 bytes .../lib/foundry-huff/assets/huff.png | Bin 0 -> 11820 bytes .../lib/foundry-huff/assets/inverted_huff.png | Bin 0 -> 14651 bytes .../assets/x-removebg-preview.png | Bin 0 -> 11985 bytes .../lib/foundry-huff/assets/x.jpg | Bin 0 -> 8013 bytes .../lib/foundry-huff/foundry.toml | 10 + .../lib/forge-std/.github/workflows/ci.yml | 134 + .../lib/forge-std/.github/workflows/sync.yml | 29 + .../foundry-huff/lib/forge-std/.gitmodules | 3 + .../foundry-huff/lib/forge-std/LICENSE-APACHE | 203 + .../foundry-huff/lib/forge-std/LICENSE-MIT | 25 + .../lib/foundry-huff/lib/forge-std/README.md | 250 + .../foundry-huff/lib/forge-std/foundry.toml | 21 + .../lib/ds-test/.github/workflows/build.yml | 41 + .../lib/forge-std/lib/ds-test/LICENSE | 674 + .../lib/forge-std/lib/ds-test/Makefile | 14 + .../lib/forge-std/lib/ds-test/default.nix | 4 + .../lib/forge-std/lib/ds-test/demo/demo.sol | 222 + .../lib/forge-std/lib/ds-test/package.json | 15 + .../lib/forge-std/lib/ds-test/src/test.sol | 592 + .../lib/forge-std/lib/ds-test/src/test.t.sol | 417 + .../foundry-huff/lib/forge-std/package.json | 16 + .../foundry-huff/lib/forge-std/src/Base.sol | 35 + .../foundry-huff/lib/forge-std/src/Script.sol | 27 + .../lib/forge-std/src/StdAssertions.sol | 376 + .../lib/forge-std/src/StdChains.sol | 234 + .../lib/forge-std/src/StdCheats.sol | 817 + .../lib/forge-std/src/StdError.sol | 15 + .../lib/forge-std/src/StdInvariant.sol | 92 + .../lib/forge-std/src/StdJson.sol | 179 + .../lib/forge-std/src/StdMath.sol | 43 + .../lib/forge-std/src/StdStorage.sol | 331 + .../lib/forge-std/src/StdStyle.sol | 333 + .../lib/forge-std/src/StdUtils.sol | 198 + .../foundry-huff/lib/forge-std/src/Test.sol | 33 + .../lib/foundry-huff/lib/forge-std/src/Vm.sol | 542 + .../lib/forge-std/src/console.sol | 1533 + .../lib/forge-std/src/console2.sol | 1558 + .../lib/forge-std/src/interfaces/IERC1155.sol | 105 + .../lib/forge-std/src/interfaces/IERC165.sol | 12 + .../lib/forge-std/src/interfaces/IERC20.sol | 43 + .../lib/forge-std/src/interfaces/IERC4626.sol | 190 + .../lib/forge-std/src/interfaces/IERC721.sol | 164 + .../forge-std/src/interfaces/IMulticall3.sol | 73 + .../lib/forge-std/src/safeconsole.sol | 13248 ++++++ .../lib/forge-std/test/StdAssertions.t.sol | 999 + .../lib/forge-std/test/StdChains.t.sol | 215 + .../lib/forge-std/test/StdCheats.t.sol | 610 + .../lib/forge-std/test/StdError.t.sol | 118 + .../lib/forge-std/test/StdMath.t.sol | 212 + .../lib/forge-std/test/StdStorage.t.sol | 293 + .../lib/forge-std/test/StdStyle.t.sol | 110 + .../lib/forge-std/test/StdUtils.t.sol | 342 + .../test/compilation/CompilationScript.sol | 10 + .../compilation/CompilationScriptBase.sol | 10 + .../test/compilation/CompilationTest.sol | 10 + .../test/compilation/CompilationTestBase.sol | 10 + .../test/fixtures/broadcast.log.json | 187 + .../lib/foundry-huff/scripts/binary_check.sh | 11 + .../lib/foundry-huff/scripts/file_writer.sh | 3 + .../lib/foundry-huff/scripts/rand_bytes.sh | 3 + .../foundry-huff/scripts/read_and_append.sh | 3 + .../lib/solidity-stringutils/.gitattributes | 1 + .../.github/workflows/ci.yml | 14 + .../lib/solidity-stringutils/.gitmodules | 3 + .../lib/solidity-stringutils/LICENSE | 201 + .../lib/solidity-stringutils/Makefile | 4 + .../lib/solidity-stringutils/README | 1 + .../lib/solidity-stringutils/README.md | 357 + .../lib/solidity-stringutils/dappfile | 8 + .../solidity-stringutils/lib/ds-test/LICENSE | 674 + .../solidity-stringutils/lib/ds-test/Makefile | 14 + .../lib/ds-test/default.nix | 4 + .../lib/ds-test/demo/demo.sol | 223 + .../lib/ds-test/src/test.sol | 434 + .../lib/solidity-stringutils/src/strings.sol | 727 + .../solidity-stringutils/src/strings.t.sol | 216 + .../lib/foundry-huff/remappings.txt | 2 + .../lib/foundry-huff/scripts/binary_check.sh | 11 + .../lib/foundry-huff/scripts/file_writer.sh | 3 + .../lib/foundry-huff/scripts/rand_bytes.sh | 3 + .../foundry-huff/scripts/read_and_append.sh | 3 + .../lib/foundry-huff/src/HuffConfig.sol | 247 + .../lib/foundry-huff/src/HuffDeployer.sol | 134 + .../src/depreciated/StatefulDeployer.sol | 73 + .../src/depreciated/StatefulDeployer.t.sol | 56 + .../foundry-huff/src/test/HuffConfig.t.sol | 55 + .../foundry-huff/src/test/HuffDeployer.t.sol | 276 + .../lib/foundry-huff/src/test/Logging.t.sol | 83 + .../src/test/contracts/ConstOverride.huff | 6 + .../src/test/contracts/Constructor.huff | 69 + .../test/contracts/ConstructorNeedsValue.huff | 16 + .../src/test/contracts/EVMVersionCheck.huff | 5 + .../src/test/contracts/LotsOfLogging.huff | 56 + .../src/test/contracts/NoConstructor.huff | 38 + .../src/test/contracts/Number.huff | 38 + .../src/test/contracts/RememberCreator.huff | 24 + .../src/test/interfaces/IConstructor.sol | 7 + .../src/test/interfaces/INumber.sol | 7 + .../src/test/interfaces/IRememberCreator.sol | 6 + .../wallet-contracts/networks/arbitrum.json | 22 + .../networks/arbitrumGoerli.json | 22 + .../networks/arbitrumNova.json | 22 + .../wallet-contracts/networks/avalanche.json | 22 + .../networks/avalancheFuji.json | 22 + .../wallet-contracts/networks/bnb.json | 22 + .../wallet-contracts/networks/bnbTestnet.json | 22 + .../wallet-contracts/networks/gnosis.json | 22 + .../wallet-contracts/networks/goerli.json | 22 + .../wallet-contracts/networks/hardhat.json | 22 + .../wallet-contracts/networks/mainnet.json | 22 + .../wallet-contracts/networks/mumbai.json | 22 + .../wallet-contracts/networks/optimism.json | 22 + .../wallet-contracts/networks/polygon.json | 26 + .../networks/polygonZkevm.json | 22 + .../wallet-contracts/package-lock.json | 38191 ++++++++++++++++ .../wallet-contracts/package.json | 105 + .../wallet-contracts/pnpm-lock.yaml | 13554 ++++++ .../wallet-contracts/remappings.txt | 3 + .../wallet-contracts/run_huff_tests.sh | 17 + .../wallet-contracts/soldeer.lock | 6 + .../wallet-contracts/src/Errors.huff | 158 + .../wallet-contracts/src/L2Compressor.huff | 140 + .../wallet-contracts/src/L2CompressorLib.huff | 2562 ++ ...jkqagwbukjfojatjegwtjmxhdL2Compressor.huff | 142 + ...vvfcuoxtlisicumgcpzqhgavjL2Compressor.huff | 142 + ...egfauooldcwyvlordhurwepivL2Compressor.huff | 142 + ...shkgzisywrbnkhcrodypkvbvyL2Compressor.huff | 142 + ...drtxibkzevmerruajxffksduvL2Compressor.huff | 142 + ...lnugcaogzpvcdfhvolsvzerleL2Compressor.huff | 142 + ...ovsnvnyyhadlushmcopgxtlcwL2Compressor.huff | 142 + ...sqtnysjlyfovitokyykvxttnoL2Compressor.huff | 142 + ...ufexucahwkkbkfdcmuiabvbfeL2Compressor.huff | 142 + ...gvfqaqtuxwpgejdnyqkualpwlL2Compressor.huff | 142 + ...orkxonmlujouewhlrvjjrmurcL2Compressor.huff | 142 + ...skwojupcsygtzovabokiotusaL2Compressor.huff | 142 + ...wzygdglttftvfnbgkuxhmtqwiL2Compressor.huff | 142 + ...aqrahclmuoszdisbtgmuppjvfL2Compressor.huff | 142 + ...kbktfjhzdpajytwhcpkhlmscqL2Compressor.huff | 142 + ...eweytjiloryhjilsqguaprwtwL2Compressor.huff | 142 + ...onlpbmziexnicojtlgsgfouyrL2Compressor.huff | 142 + ...zwlsndhjyagmukhxiugwadpxxL2Compressor.huff | 142 + ...kngjvjjjqfpzmxlycnvbslwevL2Compressor.huff | 142 + ...nijxlttsivcfgzleurbgpchhzL2Compressor.huff | 142 + ...fmyszewnycpzlqmcopsqccarqL2Compressor.huff | 142 + ...vbjyubdfmmdxzehdfcdiwufcrL2Compressor.huff | 142 + ...mhbdbiuueqxutuvcxicgrmnxwL2Compressor.huff | 142 + ...nxxrhkhfelgdrwmnbwwirribaL2Compressor.huff | 142 + ...yicdnumnesfhcjlgtqguwdpnpL2Compressor.huff | 142 + ...csyytsvygxsyhvyojjbqsghpbL2Compressor.huff | 142 + ...yvcmrjzhibkdksklxflvtfefdL2Compressor.huff | 142 + ...fcykqjxgggzptgdvokhmzcvamL2Compressor.huff | 142 + ...lozoqyyfvpcdqeanjxglmtzbbL2Compressor.huff | 142 + .../src/imps/L2CompressorImps.huff | 52 + .../src/imps/L2CompressorReadExecute.huff | 30 + .../src/imps/L2CompressorReadFlag.huff | 30 + .../src/imps/L2CompressorReadNonce.huff | 30 + .../src/imps/L2CompressorReadTx.huff | 30 + .../src/imps/L2CompressorReadTxs.huff | 30 + ...fvplniuqleqmglgpL2CompressorReadNonce.huff | 32 + ...brskpnttujfxlnsuqL2CompressorReadFlag.huff | 32 + ...kvygupcezykxnllxhooL2CompressorReadTx.huff | 32 + ...hmuynutuutdpetfmL2CompressorReadNonce.huff | 32 + ...ijkxkzjebeugweL2CompressorReadExecute.huff | 32 + ...vimfyeuyukrpkefkpjvL2CompressorReadTx.huff | 32 + ...xubslwqheqqdtpojdxL2CompressorReadTxs.huff | 32 + ...fpbluzvxgnvfnbjbvdL2CompressorReadTxs.huff | 32 + ...juvxauktfiveuhazL2CompressorReadNonce.huff | 32 + ...eablvdedlohtwcpgruiL2CompressorReadTx.huff | 32 + ...ujxdddmmbuqrzcvpqlL2CompressorReadTxs.huff | 32 + ...muiiddtfwsyytzwszL2CompressorReadFlag.huff | 32 + ...ecwtcwuiczrfadymzpwL2CompressorReadTx.huff | 32 + ...lfabzhyfjejoyhqoxvnL2CompressorReadTx.huff | 32 + ...licrbibyyssybguhL2CompressorReadNonce.huff | 32 + ...svglcrgoqbepkaL2CompressorReadExecute.huff | 32 + ...ksdozrardbmkofvggL2CompressorReadFlag.huff | 32 + ...zxedpzkrqkrxgpL2CompressorReadExecute.huff | 32 + ...ssijauhsotgyqtayygL2CompressorReadTxs.huff | 32 + ...dwrkmombuemzhicuirL2CompressorReadTxs.huff | 32 + ...tvptvgszvdeemgpomnL2CompressorReadTxs.huff | 32 + ...glrinqvkqirnywzkmmmL2CompressorReadTx.huff | 32 + ...sulrygitvkhredhzhL2CompressorReadFlag.huff | 32 + ...aalvdegpzaqhuqqhiasL2CompressorReadTx.huff | 32 + ...mdvycqormkvpykanxnL2CompressorReadTxs.huff | 32 + ...ucihkxihmonvdzfxL2CompressorReadNonce.huff | 32 + ...fcvecckcbuhhhpvpfL2CompressorReadFlag.huff | 32 + ...ftzbzzrxvcmizbL2CompressorReadExecute.huff | 32 + ...cwxtmeqbuwldxifmL2CompressorReadNonce.huff | 32 + ...jwbyxrediuokmghmftbL2CompressorReadTx.huff | 32 + ...rdpemqiejqiknsfiL2CompressorReadNonce.huff | 32 + ...foughzygdwrhxuzibL2CompressorReadFlag.huff | 32 + ...rixmvtxkffgwchL2CompressorReadExecute.huff | 32 + ...tqmujkoilcaudgL2CompressorReadExecute.huff | 32 + ...ycyuqniujobcpgjuysL2CompressorReadTxs.huff | 32 + ...ixyvzwfzordmogvuL2CompressorReadNonce.huff | 32 + ...yygzhjrvozrogmL2CompressorReadExecute.huff | 32 + ...epdhwqsahoduwcqrrpL2CompressorReadTxs.huff | 32 + ...izmyrwtgelqlacL2CompressorReadExecute.huff | 32 + ...dhdecpajexqefympmL2CompressorReadFlag.huff | 32 + ...xkesysithbtrbvqqL2CompressorReadNonce.huff | 32 + ...vwlvozjzwlctwrzrL2CompressorReadNonce.huff | 32 + ...rexbaruoucdulzvqvL2CompressorReadFlag.huff | 32 + ...zsmahiisgmgdjzfjxqL2CompressorReadTxs.huff | 32 + ...aqwygxkipfvshmgystL2CompressorReadTxs.huff | 32 + ...perstvmceseoukqtcL2CompressorReadFlag.huff | 32 + ...lvuclsezveuuexubagL2CompressorReadTxs.huff | 32 + ...pfceqpqhdfzbiiL2CompressorReadExecute.huff | 32 + ...pfucdlmsxiuwbhxrsL2CompressorReadFlag.huff | 32 + ...xcpetgqmymkladcanwtL2CompressorReadTx.huff | 32 + ...llmivwlrgrfhmdzslL2CompressorReadFlag.huff | 32 + ...rmebqsqsnoleofcgyciL2CompressorReadTx.huff | 32 + ...aqrlwcoejtxsysubcL2CompressorReadFlag.huff | 32 + ...saspukxeydggrhL2CompressorReadExecute.huff | 32 + ...qlpehxidlrhdvkcoyL2CompressorReadFlag.huff | 32 + ...bqyenjlfkzsbjkxcL2CompressorReadNonce.huff | 32 + ...rwmufefnibyzayqaaciL2CompressorReadTx.huff | 32 + ...suogqwefdorgsntvcxL2CompressorReadTxs.huff | 32 + ...ihfqfuruuvbxqbL2CompressorReadExecute.huff | 32 + ...medlqkqqhszmvuuxioL2CompressorReadTxs.huff | 32 + ...bouzrwkvkhpzncpjzsL2CompressorReadTxs.huff | 32 + ...kuiktwofhzwbszraL2CompressorReadNonce.huff | 32 + ...ntvdsgkeofzbhbhdsL2CompressorReadFlag.huff | 32 + ...obnextlfkyykzrdomL2CompressorReadFlag.huff | 32 + ...zdduewzkbcwlvsakcL2CompressorReadFlag.huff | 32 + ...fkhccrhufdhjjazeL2CompressorReadNonce.huff | 32 + ...htlarvljeqysguhoizaL2CompressorReadTx.huff | 32 + ...wrmogebamkqmhdvljmlL2CompressorReadTx.huff | 32 + ...dnlbnftvkuearfL2CompressorReadExecute.huff | 32 + ...stckycmjquytqlfsdxoL2CompressorReadTx.huff | 32 + ...rrpmypzdufaanmpuL2CompressorReadNonce.huff | 32 + ...asdtggpxqllpajsfgL2CompressorReadFlag.huff | 32 + ...tgrrlpmnzupwinL2CompressorReadExecute.huff | 32 + ...mbynuotwqkanwdlaqwpL2CompressorReadTx.huff | 32 + ...gzyejxobzrgvpizgfdL2CompressorReadTxs.huff | 32 + ...ihlkysfiliofczzxkhL2CompressorReadTxs.huff | 32 + ...eikmnrgvhavunciniubL2CompressorReadTx.huff | 32 + ...lvopyxfsbdteqegnyogL2CompressorReadTx.huff | 32 + ...mzpjfotpjfuqxtnqrznL2CompressorReadTx.huff | 32 + ...mayqquwflixlquuhfxhL2CompressorReadTx.huff | 32 + ...itanbofqxpeepnlpnL2CompressorReadFlag.huff | 32 + ...dekencowviukvlcrylcL2CompressorReadTx.huff | 32 + ...wbvymwfzttbygcbfmqL2CompressorReadTxs.huff | 32 + ...hrcqjmlolnoulkL2CompressorReadExecute.huff | 32 + ...ckjtrcwgaqmtqrL2CompressorReadExecute.huff | 32 + ...wzkubprcmgpexkL2CompressorReadExecute.huff | 32 + ...vfvlctpjeldqbrshL2CompressorReadNonce.huff | 32 + ...jpleorchotchzsecL2CompressorReadNonce.huff | 32 + ...wcvynodibgfqnidibL2CompressorReadFlag.huff | 32 + ...jvdzyjcjrsbhplpsL2CompressorReadNonce.huff | 32 + ...gxsfgldujuphcmuueL2CompressorReadFlag.huff | 32 + ...zffpwypehvvksjzbvnL2CompressorReadTxs.huff | 32 + ...klqlujjtzpxrejpmL2CompressorReadNonce.huff | 32 + ...sisgwnqqymvpjvskcL2CompressorReadFlag.huff | 32 + ...htgjgwawtaakhwL2CompressorReadExecute.huff | 32 + ...voiaurnusvgnrhusL2CompressorReadNonce.huff | 32 + ...wupwcjsnyherfswgsgcL2CompressorReadTx.huff | 32 + ...uaqyoulenyhrwfuvsiL2CompressorReadTxs.huff | 32 + ...prlkfulinnjttkL2CompressorReadExecute.huff | 32 + ...xerdftkvyxqwwiL2CompressorReadExecute.huff | 32 + ...hkmwlwmhlgvymnL2CompressorReadExecute.huff | 32 + ...vxsxrlsluymfdiL2CompressorReadExecute.huff | 32 + ...bwcqavoskmutltL2CompressorReadExecute.huff | 32 + ...gmtbremlhrxnejL2CompressorReadExecute.huff | 32 + ...bspndmkchcjaofllL2CompressorReadNonce.huff | 32 + ...dfbtyakjkbvehkjdbmgL2CompressorReadTx.huff | 32 + ...pekynjthxzellyhsxeL2CompressorReadTxs.huff | 32 + ...uggwhjnxbrgifehtxhfL2CompressorReadTx.huff | 32 + .../test/ChainedSignatures.spec.ts | 136 + .../wallet-contracts/test/ERC165.spec.ts | 73 + .../wallet-contracts/test/Factory.spec.ts | 65 + .../test/GasEstimation.spec.ts | 353 + .../wallet-contracts/test/GuestModule.spec.ts | 98 + .../wallet-contracts/test/LibBytes.spec.ts | 185 + .../wallet-contracts/test/LibString.spec.ts | 83 + .../wallet-contracts/test/MainModule.bench.ts | 198 + .../wallet-contracts/test/MainModule.spec.ts | 2247 + .../test/MerkleSignatures.spec.ts | 30 + .../test/MultiCallUtils.spec.ts | 282 + .../wallet-contracts/test/utils/contracts.ts | 87 + .../wallet-contracts/test/utils/imposter.ts | 43 + .../wallet-contracts/test/utils/index.ts | 118 + .../wallet-contracts/test/utils/sequence.ts | 613 + .../wallet-contracts/test/utils/wallet.ts | 329 + .../wallet-contracts/tsconfig.json | 37 + .../typings/chai-bignumber.d.ts | 16 + .../wallet-contracts/typings/chai-bn.ts | 19 + .../wallet-contracts/typings/truffle.d.ts | 12 + .../wallet-contracts/utils/JsonBindings.sol | 193 + .../wallet-contracts/utils/benchmarker.ts | 106 + .../wallet-contracts/utils/config-loader.ts | 159 + .../utils/deploy-contracts.ts | 207 + .../utils/workers/bench-worker.ts | 89 + packages/test/package.json | 118 + packages/test/src/chains.ts | 51 + packages/test/src/clients.ts | 62 + packages/test/src/config.ts | 29 + packages/test/src/constants.ts | 318 + packages/test/src/exports/index.test-d.ts | 4 + packages/test/src/exports/index.test.ts | 29 + packages/test/src/exports/index.ts | 25 + packages/test/src/exports/react.ts | 63 + packages/test/src/exports/vue.ts | 66 + packages/test/src/globalSetup.ts | 26 + packages/test/src/regex.ts | 3 + packages/test/src/setup.ts | 8 + packages/test/src/utils.ts | 25 + packages/test/tsconfig.build.json | 8 + packages/test/tsconfig.json | 5 + packages/vue/CHANGELOG.md | 706 + packages/vue/README.md | 14 + packages/vue/package.json | 113 + .../vue/src/composables/useAccount.test-d.ts | 69 + .../vue/src/composables/useAccount.test.ts | 20 + packages/vue/src/composables/useAccount.ts | 37 + .../src/composables/useAccountEffect.test.ts | 75 + .../vue/src/composables/useAccountEffect.ts | 66 + .../vue/src/composables/useBalance.test-d.ts | 15 + .../vue/src/composables/useBalance.test.ts | 119 + packages/vue/src/composables/useBalance.ts | 65 + .../src/composables/useBlockNumber.test-d.ts | 65 + .../src/composables/useBlockNumber.test.ts | 65 + .../vue/src/composables/useBlockNumber.ts | 120 + .../vue/src/composables/useBytecode.test-d.ts | 16 + .../vue/src/composables/useBytecode.test.ts | 296 + packages/vue/src/composables/useBytecode.ts | 72 + .../vue/src/composables/useChainId.test-d.ts | 14 + .../vue/src/composables/useChainId.test.ts | 22 + packages/vue/src/composables/useChainId.ts | 35 + .../vue/src/composables/useChains.test.ts | 16 + packages/vue/src/composables/useChains.ts | 35 + .../vue/src/composables/useClient.test-d.ts | 42 + .../vue/src/composables/useClient.test.ts | 41 + packages/vue/src/composables/useClient.ts | 66 + .../vue/src/composables/useConfig.test-d.ts | 16 + .../vue/src/composables/useConfig.test.ts | 24 + packages/vue/src/composables/useConfig.ts | 34 + .../vue/src/composables/useConnect.test-d.ts | 121 + .../vue/src/composables/useConnect.test.ts | 31 + packages/vue/src/composables/useConnect.ts | 92 + .../src/composables/useConnections.test.ts | 16 + .../vue/src/composables/useConnections.ts | 30 + .../composables/useConnectorClient.test-d.ts | 12 + .../composables/useConnectorClient.test.ts | 156 + .../vue/src/composables/useConnectorClient.ts | 132 + .../vue/src/composables/useConnectors.test.ts | 21 + packages/vue/src/composables/useConnectors.ts | 37 + .../src/composables/useDisconnect.test-d.ts | 87 + .../vue/src/composables/useDisconnect.test.ts | 30 + packages/vue/src/composables/useDisconnect.ts | 70 + .../vue/src/composables/useEnsAddress.test.ts | 52 + packages/vue/src/composables/useEnsAddress.ts | 65 + .../vue/src/composables/useEnsAvatar.test.ts | 52 + packages/vue/src/composables/useEnsAvatar.ts | 65 + .../vue/src/composables/useEnsName.test.ts | 52 + packages/vue/src/composables/useEnsName.ts | 65 + .../src/composables/useEstimateGas.test-d.ts | 14 + .../src/composables/useEstimateGas.test.ts | 117 + .../vue/src/composables/useEstimateGas.ts | 83 + .../src/composables/useReadContract.test-d.ts | 99 + .../src/composables/useReadContract.test.ts | 105 + .../vue/src/composables/useReadContract.ts | 116 + .../src/composables/useReconnect.test-d.ts | 154 + .../vue/src/composables/useReconnect.test.ts | 81 + packages/vue/src/composables/useReconnect.ts | 65 + .../composables/useSendTransaction.test-d.ts | 78 + .../composables/useSendTransaction.test.ts | 25 + .../vue/src/composables/useSendTransaction.ts | 76 + .../src/composables/useSignMessage.test-d.ts | 64 + .../src/composables/useSignMessage.test.ts | 43 + .../vue/src/composables/useSignMessage.ts | 63 + .../composables/useSignTypedData.test-d.ts | 95 + .../src/composables/useSignTypedData.test.ts | 56 + .../vue/src/composables/useSignTypedData.ts | 64 + .../composables/useSimulateContract.test-d.ts | 96 + .../composables/useSimulateContract.test.ts | 59 + .../src/composables/useSimulateContract.ts | 145 + .../composables/useSwitchAccount.test-d.ts | 89 + .../src/composables/useSwitchAccount.test.ts | 42 + .../vue/src/composables/useSwitchAccount.ts | 84 + .../src/composables/useSwitchChain.test-d.ts | 118 + .../src/composables/useSwitchChain.test.ts | 35 + .../vue/src/composables/useSwitchChain.ts | 81 + .../src/composables/useTransaction.test-d.ts | 14 + .../src/composables/useTransaction.test.ts | 74 + .../vue/src/composables/useTransaction.ts | 91 + .../useTransactionReceipt.test-d.ts | 14 + .../composables/useTransactionReceipt.test.ts | 208 + .../src/composables/useTransactionReceipt.ts | 85 + .../useWaitForTransactionReceipt.test-d.ts | 14 + .../useWaitForTransactionReceipt.test.ts | 46 + .../useWaitForTransactionReceipt.ts | 84 + .../composables/useWatchBlockNumber.test-d.ts | 75 + .../composables/useWatchBlockNumber.test.ts | 67 + .../src/composables/useWatchBlockNumber.ts | 63 + .../useWatchContractEvent.test-d.ts | 126 + .../composables/useWatchContractEvent.test.ts | 106 + .../src/composables/useWatchContractEvent.ts | 77 + .../composables/useWriteContract.test-d.ts | 136 + .../src/composables/useWriteContract.test.ts | 25 + .../vue/src/composables/useWriteContract.ts | 85 + packages/vue/src/errors/base.test.ts | 155 + packages/vue/src/errors/base.ts | 14 + packages/vue/src/errors/plugin.test.ts | 24 + packages/vue/src/errors/plugin.ts | 31 + packages/vue/src/exports/actions.test.ts | 86 + packages/vue/src/exports/actions.ts | 7 + .../src/exports/actions/experimental.test.ts | 25 + .../vue/src/exports/actions/experimental.ts | 7 + packages/vue/src/exports/chains.ts | 7 + packages/vue/src/exports/connectors.test.ts | 17 + packages/vue/src/exports/connectors.ts | 7 + packages/vue/src/exports/index.test.ts | 74 + packages/vue/src/exports/index.ts | 280 + packages/vue/src/exports/nuxt.test.ts | 11 + packages/vue/src/exports/nuxt.ts | 4 + packages/vue/src/exports/query.test.ts | 99 + packages/vue/src/exports/query.ts | 19 + packages/vue/src/nuxt/module.ts | 60 + packages/vue/src/nuxt/runtime/composables.ts | 3 + packages/vue/src/plugin.ts | 22 + packages/vue/src/types/properties.ts | 27 + packages/vue/src/types/ref.ts | 39 + packages/vue/src/utils/cloneDeep.ts | 44 + packages/vue/src/utils/getVersion.test.ts | 7 + packages/vue/src/utils/getVersion.ts | 3 + packages/vue/src/utils/query.ts | 161 + packages/vue/src/utils/updateState.ts | 10 + packages/vue/src/version.ts | 1 + packages/vue/test/setup.ts | 8 + packages/vue/tsconfig.build.json | 8 + packages/vue/tsconfig.json | 5 + playgrounds/next/.gitignore | 35 + playgrounds/next/next.config.mjs | 20 + playgrounds/next/package.json | 31 + playgrounds/next/src/app/contracts.ts | 202 + playgrounds/next/src/app/globals.css | 21 + playgrounds/next/src/app/layout.tsx | 30 + playgrounds/next/src/app/page.tsx | 412 + playgrounds/next/src/app/providers.tsx | 23 + playgrounds/next/src/wagmi.ts | 31 + playgrounds/next/tsconfig.json | 24 + playgrounds/nuxt/.gitignore | 24 + playgrounds/nuxt/app.vue | 28 + playgrounds/nuxt/components/Account.vue | 22 + playgrounds/nuxt/components/Connect.vue | 19 + playgrounds/nuxt/nuxt.config.ts | 7 + playgrounds/nuxt/package.json | 20 + playgrounds/nuxt/plugins/wagmi.ts | 10 + playgrounds/nuxt/public/favicon.ico | Bin 0 -> 4286 bytes playgrounds/nuxt/server/tsconfig.json | 3 + playgrounds/nuxt/tsconfig.json | 3 + playgrounds/nuxt/wagmi.ts | 29 + playgrounds/vite-core/.gitignore | 24 + playgrounds/vite-core/index.html | 12 + playgrounds/vite-core/package.json | 24 + playgrounds/vite-core/src/App.tsx | 186 + playgrounds/vite-core/src/index.css | 21 + playgrounds/vite-core/src/main.tsx | 16 + playgrounds/vite-core/src/vite-env.d.ts | 1 + playgrounds/vite-core/src/wagmi.ts | 22 + playgrounds/vite-core/tsconfig.json | 28 + playgrounds/vite-core/tsconfig.node.json | 10 + playgrounds/vite-core/vite.config.ts | 7 + playgrounds/vite-react/.gitignore | 26 + playgrounds/vite-react/index.html | 12 + playgrounds/vite-react/package.json | 29 + playgrounds/vite-react/public/manifest.json | 5 + playgrounds/vite-react/src/App.tsx | 389 + playgrounds/vite-react/src/contracts.ts | 202 + playgrounds/vite-react/src/index.css | 21 + playgrounds/vite-react/src/main.tsx | 49 + playgrounds/vite-react/src/vite-env.d.ts | 1 + playgrounds/vite-react/src/wagmi.ts | 40 + playgrounds/vite-react/tsconfig.json | 30 + playgrounds/vite-react/tsconfig.node.json | 10 + playgrounds/vite-react/vite.config.ts | 7 + playgrounds/vite-react/wagmi.config.ts | 17 + playgrounds/vite-vue/.gitignore | 24 + playgrounds/vite-vue/index.html | 13 + playgrounds/vite-vue/package.json | 22 + playgrounds/vite-vue/public/vite.svg | 1 + playgrounds/vite-vue/src/App.vue | 29 + .../vite-vue/src/components/Account.vue | 22 + .../vite-vue/src/components/Balance.vue | 12 + .../vite-vue/src/components/BlockNumber.vue | 17 + .../vite-vue/src/components/Client.vue | 14 + .../vite-vue/src/components/Connect.vue | 19 + .../vite-vue/src/components/Connections.vue | 15 + .../src/components/ConnectorClient.vue | 14 + .../vite-vue/src/components/ReadContract.vue | 16 + .../src/components/SendTransaction.vue | 35 + .../vite-vue/src/components/SwitchAccount.vue | 20 + .../vite-vue/src/components/SwitchChain.vue | 22 + .../vite-vue/src/components/WriteContract.vue | 28 + playgrounds/vite-vue/src/contracts.ts | 202 + playgrounds/vite-vue/src/main.ts | 17 + playgrounds/vite-vue/src/style.css | 21 + playgrounds/vite-vue/src/vite-env.d.ts | 1 + playgrounds/vite-vue/src/wagmi.ts | 26 + playgrounds/vite-vue/tsconfig.json | 23 + playgrounds/vite-vue/tsconfig.node.json | 11 + playgrounds/vite-vue/vite.config.ts | 7 + pnpm-lock.yaml | 23954 +++++----- pnpm-workspace.yaml | 35 +- scripts/formatPackageJson.ts | 36 + scripts/generateProxyPackages.ts | 57 + scripts/preconstruct.ts | 87 + scripts/restorePackageJson.ts | 29 + scripts/updateBlockExplorerPluginChains.ts | 53 + scripts/updateVersion.ts | 43 + scripts/updateViemVersion.ts | 44 + site/.vitepress/config.ts | 151 + site/.vitepress/constants.ts | 2 + site/.vitepress/sidebar.ts | 1085 + .../theme/components/AsideSponsors.vue | 27 + site/.vitepress/theme/components/Banner.vue | 16 + .../theme/components/HomeBanner.vue | 13 + site/.vitepress/theme/components/HomePage.vue | 118 + .../theme/composables/useSponsors.ts | 212 + site/.vitepress/theme/index.ts | 30 + site/.vitepress/theme/style.css | 148 + site/cli/api/commands.md | 53 + site/cli/api/commands/generate.md | 49 + site/cli/api/commands/init.md | 40 + site/cli/api/plugins.md | 42 + site/cli/api/plugins/actions.md | 72 + site/cli/api/plugins/blockExplorer.md | 223 + site/cli/api/plugins/etherscan.md | 182 + site/cli/api/plugins/fetch.md | 269 + site/cli/api/plugins/foundry.md | 217 + site/cli/api/plugins/hardhat.md | 199 + site/cli/api/plugins/react.md | 52 + site/cli/api/plugins/sourcify.md | 115 + site/cli/config/configuring-cli.md | 124 + site/cli/config/options.md | 132 + site/cli/create-wagmi.md | 75 + site/cli/getting-started.md | 167 + site/cli/guides/migrate-from-v1-to-v2.md | 51 + site/cli/installation.md | 60 + site/cli/why.md | 92 + site/components/Browsers.vue | 9 + site/components/SearchChains.vue | 82 + site/core/api/actions.md | 25 + site/core/api/actions/call.md | 340 + site/core/api/actions/connect.md | 102 + site/core/api/actions/deployContract.md | 264 + site/core/api/actions/disconnect.md | 60 + site/core/api/actions/estimateFeesPerGas.md | 139 + site/core/api/actions/estimateGas.md | 340 + .../actions/estimateMaxPriorityFeePerGas.md | 74 + site/core/api/actions/getAccount.md | 29 + site/core/api/actions/getBalance.md | 197 + site/core/api/actions/getBlock.md | 146 + site/core/api/actions/getBlockNumber.md | 93 + .../api/actions/getBlockTransactionCount.md | 92 + site/core/api/actions/getBytecode.md | 133 + site/core/api/actions/getCallsStatus.md | 97 + site/core/api/actions/getCapabilities.md | 96 + site/core/api/actions/getChainId.md | 38 + site/core/api/actions/getChains.md | 31 + site/core/api/actions/getClient.md | 56 + site/core/api/actions/getConnections.md | 31 + site/core/api/actions/getConnectorClient.md | 108 + site/core/api/actions/getConnectors.md | 31 + site/core/api/actions/getEnsAddress.md | 187 + site/core/api/actions/getEnsAvatar.md | 210 + site/core/api/actions/getEnsName.md | 157 + site/core/api/actions/getEnsResolver.md | 167 + site/core/api/actions/getEnsText.md | 195 + site/core/api/actions/getFeeHistory.md | 157 + site/core/api/actions/getGasPrice.md | 74 + site/core/api/actions/getProof.md | 169 + site/core/api/actions/getPublicClient.md | 60 + site/core/api/actions/getStorageAt.md | 157 + site/core/api/actions/getToken.md | 141 + site/core/api/actions/getTransaction.md | 173 + .../actions/getTransactionConfirmations.md | 117 + site/core/api/actions/getTransactionCount.md | 139 + .../core/api/actions/getTransactionReceipt.md | 95 + site/core/api/actions/getWalletClient.md | 112 + site/core/api/actions/multicall.md | 355 + .../api/actions/prepareTransactionRequest.md | 307 + site/core/api/actions/readContract.md | 258 + site/core/api/actions/readContracts.md | 363 + site/core/api/actions/reconnect.md | 72 + site/core/api/actions/sendCalls.md | 223 + site/core/api/actions/sendTransaction.md | 341 + site/core/api/actions/showCallsStatus.md | 99 + site/core/api/actions/signMessage.md | 125 + site/core/api/actions/signTypedData.md | 409 + site/core/api/actions/simulateContract.md | 598 + site/core/api/actions/switchAccount.md | 81 + site/core/api/actions/switchChain.md | 122 + site/core/api/actions/verifyMessage.md | 200 + site/core/api/actions/verifyTypedData.md | 595 + site/core/api/actions/waitForCallsStatus.md | 143 + .../api/actions/waitForTransactionReceipt.md | 155 + site/core/api/actions/watchAccount.md | 61 + site/core/api/actions/watchAsset.md | 134 + site/core/api/actions/watchBlockNumber.md | 226 + site/core/api/actions/watchBlocks.md | 249 + site/core/api/actions/watchChainId.md | 61 + site/core/api/actions/watchClient.md | 61 + site/core/api/actions/watchConnections.md | 61 + site/core/api/actions/watchConnectors.md | 61 + site/core/api/actions/watchContractEvent.md | 376 + .../api/actions/watchPendingTransactions.md | 207 + site/core/api/actions/watchPublicClient.md | 61 + site/core/api/actions/writeContract.md | 560 + site/core/api/actions/writeContracts.md | 317 + site/core/api/chains.md | 24 + site/core/api/connectors.md | 28 + site/core/api/connectors/coinbaseWallet.md | 6 + site/core/api/connectors/injected.md | 7 + site/core/api/connectors/metaMask.md | 7 + site/core/api/connectors/mock.md | 6 + site/core/api/connectors/safe.md | 6 + site/core/api/connectors/walletConnect.md | 6 + site/core/api/createConfig.md | 7 + site/core/api/createConnector.md | 31 + site/core/api/createStorage.md | 6 + site/core/api/errors.md | 11 + site/core/api/transports.md | 28 + site/core/api/transports/custom.md | 5 + site/core/api/transports/fallback.md | 5 + site/core/api/transports/http.md | 5 + .../core/api/transports/unstable_connector.md | 6 + site/core/api/transports/webSocket.md | 5 + .../api/utilities/cookieToInitialState.md | 5 + site/core/api/utilities/deserialize.md | 5 + site/core/api/utilities/normalizeChainId.md | 5 + site/core/api/utilities/serialize.md | 5 + site/core/getting-started.md | 71 + site/core/guides/chain-properties.md | 91 + site/core/guides/error-handling.md | 37 + site/core/guides/ethers.md | 306 + site/core/guides/faq.md | 9 + site/core/guides/framework-adapters.md | 35 + site/core/guides/migrate-from-v1-to-v2.md | 585 + site/core/guides/testing.md | 3 + site/core/guides/viem.md | 197 + site/core/installation.md | 52 + site/core/typescript.md | 241 + site/core/why.md | 46 + site/dev/contributing.md | 171 + site/dev/creating-connectors.md | 155 + site/index.md | 58 + site/package.json | 28 + site/public/browsers/chrome.png | Bin 0 -> 12226 bytes site/public/browsers/edge.png | Bin 0 -> 11311 bytes site/public/browsers/firefox.png | Bin 0 -> 15035 bytes site/public/browsers/opera.png | Bin 0 -> 5749 bytes site/public/browsers/safari.png | Bin 0 -> 24056 bytes site/public/favicon.svg | 10 + site/public/logo-dark.svg | 6 + site/public/logo-light.svg | 6 + site/public/og.png | Bin 0 -> 12491 bytes site/react/api/WagmiProvider.md | 112 + site/react/api/actions.md | 28 + site/react/api/chains.md | 26 + site/react/api/connectors.md | 28 + site/react/api/connectors/coinbaseWallet.md | 6 + site/react/api/connectors/injected.md | 7 + site/react/api/connectors/metaMask.md | 7 + site/react/api/connectors/mock.md | 6 + site/react/api/connectors/safe.md | 6 + site/react/api/connectors/walletConnect.md | 6 + site/react/api/createConfig.md | 7 + site/react/api/createStorage.md | 6 + site/react/api/errors.md | 20 + site/react/api/hooks.md | 25 + site/react/api/hooks/useAccount.md | 65 + site/react/api/hooks/useAccountEffect.md | 113 + site/react/api/hooks/useBalance.md | 226 + site/react/api/hooks/useBlock.md | 227 + site/react/api/hooks/useBlockNumber.md | 169 + .../api/hooks/useBlockTransactionCount.md | 175 + site/react/api/hooks/useBytecode.md | 181 + site/react/api/hooks/useCall.md | 397 + site/react/api/hooks/useCallsStatus.md | 143 + site/react/api/hooks/useCapabilities.md | 138 + site/react/api/hooks/useChainId.md | 74 + site/react/api/hooks/useChains.md | 67 + site/react/api/hooks/useClient.md | 89 + site/react/api/hooks/useConfig.md | 75 + site/react/api/hooks/useConnect.md | 117 + site/react/api/hooks/useConnections.md | 64 + site/react/api/hooks/useConnectorClient.md | 128 + site/react/api/hooks/useConnectors.md | 41 + site/react/api/hooks/useDeployContract.md | 145 + site/react/api/hooks/useDisconnect.md | 113 + site/react/api/hooks/useEnsAddress.md | 238 + site/react/api/hooks/useEnsAvatar.md | 262 + site/react/api/hooks/useEnsName.md | 206 + site/react/api/hooks/useEnsResolver.md | 217 + site/react/api/hooks/useEnsText.md | 247 + site/react/api/hooks/useEstimateFeesPerGas.md | 155 + site/react/api/hooks/useEstimateGas.md | 387 + .../hooks/useEstimateMaxPriorityFeePerGas.md | 116 + site/react/api/hooks/useFeeHistory.md | 210 + site/react/api/hooks/useGasPrice.md | 117 + .../api/hooks/useInfiniteReadContracts.md | 358 + .../api/hooks/usePrepareTransactionRequest.md | 368 + site/react/api/hooks/useProof.md | 224 + site/react/api/hooks/usePublicClient.md | 93 + site/react/api/hooks/useReadContract.md | 406 + site/react/api/hooks/useReadContracts.md | 394 + site/react/api/hooks/useReconnect.md | 111 + site/react/api/hooks/useSendCalls.md | 103 + site/react/api/hooks/useSendTransaction.md | 93 + site/react/api/hooks/useShowCallsStatus.md | 96 + site/react/api/hooks/useSignMessage.md | 85 + site/react/api/hooks/useSignTypedData.md | 217 + site/react/api/hooks/useSimulateContract.md | 686 + site/react/api/hooks/useStorageAt.md | 208 + site/react/api/hooks/useSwitchAccount.md | 116 + site/react/api/hooks/useSwitchChain.md | 120 + site/react/api/hooks/useToken.md | 162 + site/react/api/hooks/useTransaction.md | 184 + .../api/hooks/useTransactionConfirmations.md | 153 + site/react/api/hooks/useTransactionCount.md | 185 + site/react/api/hooks/useTransactionReceipt.md | 142 + site/react/api/hooks/useVerifyMessage.md | 257 + site/react/api/hooks/useVerifyTypedData.md | 724 + site/react/api/hooks/useWaitForCallsStatus.md | 181 + .../api/hooks/useWaitForTransactionReceipt.md | 168 + site/react/api/hooks/useWalletClient.md | 132 + site/react/api/hooks/useWatchAsset.md | 94 + site/react/api/hooks/useWatchBlockNumber.md | 276 + site/react/api/hooks/useWatchBlocks.md | 318 + site/react/api/hooks/useWatchContractEvent.md | 413 + .../api/hooks/useWatchPendingTransactions.md | 229 + site/react/api/hooks/useWriteContract.md | 116 + site/react/api/hooks/useWriteContracts.md | 119 + site/react/api/transports.md | 28 + site/react/api/transports/custom.md | 5 + site/react/api/transports/fallback.md | 5 + site/react/api/transports/http.md | 5 + .../api/transports/unstable_connector.md | 6 + site/react/api/transports/webSocket.md | 5 + .../api/utilities/cookieToInitialState.md | 5 + site/react/api/utilities/deserialize.md | 5 + site/react/api/utilities/normalizeChainId.md | 5 + site/react/api/utilities/serialize.md | 5 + site/react/comparisons.md | 90 + site/react/getting-started.md | 214 + site/react/guides/chain-properties.md | 97 + site/react/guides/connect-wallet.md | 421 + site/react/guides/error-handling.md | 42 + site/react/guides/ethers.md | 285 + site/react/guides/faq.md | 24 + site/react/guides/migrate-from-v1-to-v2.md | 658 + site/react/guides/read-from-contract.md | 202 + site/react/guides/send-transaction.md | 362 + site/react/guides/ssr.md | 168 + site/react/guides/tanstack-query.md | 403 + site/react/guides/testing.md | 2 + site/react/guides/viem.md | 150 + site/react/guides/write-to-contract.md | 438 + site/react/installation.md | 56 + site/react/typescript.md | 302 + site/react/why.md | 46 + site/shared/connectors/coinbaseWallet.md | 160 + site/shared/connectors/injected.md | 89 + site/shared/connectors/metaMask.md | 124 + site/shared/connectors/mock.md | 128 + site/shared/connectors/safe.md | 77 + site/shared/connectors/walletConnect.md | 215 + site/shared/create-chain.md | 93 + site/shared/createConfig.md | 485 + site/shared/createStorage.md | 161 + site/shared/errors.md | 90 + site/shared/faq.md | 81 + site/shared/getAccount-return-type.md | 107 + site/shared/installation.md | 62 + site/shared/mutation-imports.md | 19 + site/shared/mutation-options.md | 89 + site/shared/mutation-result.md | 122 + site/shared/query-imports.md | 20 + site/shared/query-options.md | 208 + site/shared/query-result.md | 193 + site/shared/transports/custom.md | 110 + site/shared/transports/fallback.md | 36 + site/shared/transports/http.md | 178 + site/shared/transports/unstable_connector.md | 123 + site/shared/transports/webSocket.md | 108 + site/shared/utilities/cookieToInitialState.md | 74 + site/shared/utilities/deserialize.md | 44 + site/shared/utilities/normalizeChainId.md | 56 + site/shared/utilities/serialize.md | 53 + site/snippets/abi-event.ts | 20 + site/snippets/abi-infinite-read.ts | 23 + site/snippets/abi-read.ts | 16 + site/snippets/abi-write.ts | 23 + site/snippets/core/config-chain-properties.ts | 11 + site/snippets/core/config.ts | 10 + site/snippets/react/app.tsx | 16 + .../snippets/react/config-chain-properties.ts | 17 + site/snippets/react/config.ts | 10 + site/snippets/typedData.ts | 13 + site/snippets/vue/App.vue | 5 + site/snippets/vue/config-chain-properties.ts | 17 + site/snippets/vue/config.ts | 10 + site/snippets/vue/main.ts | 13 + site/tsconfig.json | 23 + site/vercel.json | 148 + site/vue/api/Nuxt.md | 27 + site/vue/api/WagmiPlugin.md | 113 + site/vue/api/actions.md | 30 + site/vue/api/chains.md | 26 + site/vue/api/composables.md | 25 + site/vue/api/composables/useAccount.md | 80 + site/vue/api/composables/useAccountEffect.md | 113 + site/vue/api/composables/useBalance.md | 226 + site/vue/api/composables/useBlockNumber.md | 172 + site/vue/api/composables/useBytecode.md | 209 + site/vue/api/composables/useChainId.md | 74 + site/vue/api/composables/useChains.md | 67 + site/vue/api/composables/useClient.md | 89 + site/vue/api/composables/useConfig.md | 33 + site/vue/api/composables/useConnect.md | 115 + site/vue/api/composables/useConnections.md | 64 + .../vue/api/composables/useConnectorClient.md | 128 + site/vue/api/composables/useConnectors.md | 41 + site/vue/api/composables/useDisconnect.md | 111 + site/vue/api/composables/useEnsAddress.md | 238 + site/vue/api/composables/useEnsAvatar.md | 262 + site/vue/api/composables/useEnsName.md | 206 + site/vue/api/composables/useEstimateGas.md | 387 + site/vue/api/composables/useReadContract.md | 410 + site/vue/api/composables/useReconnect.md | 106 + .../vue/api/composables/useSendTransaction.md | 91 + site/vue/api/composables/useSignMessage.md | 85 + site/vue/api/composables/useSignTypedData.md | 214 + .../api/composables/useSimulateContract.md | 686 + site/vue/api/composables/useSwitchAccount.md | 120 + site/vue/api/composables/useSwitchChain.md | 124 + site/vue/api/composables/useTransaction.md | 184 + .../api/composables/useTransactionReceipt.md | 141 + .../useWaitForTransactionReceipt.md | 168 + .../api/composables/useWatchBlockNumber.md | 109 + .../api/composables/useWatchContractEvent.md | 134 + site/vue/api/composables/useWriteContract.md | 112 + site/vue/api/connectors.md | 28 + site/vue/api/connectors/coinbaseWallet.md | 6 + site/vue/api/connectors/injected.md | 7 + site/vue/api/connectors/metaMask.md | 7 + site/vue/api/connectors/mock.md | 6 + site/vue/api/connectors/safe.md | 6 + site/vue/api/connectors/walletConnect.md | 6 + site/vue/api/createConfig.md | 7 + site/vue/api/createStorage.md | 6 + site/vue/api/errors.md | 10 + site/vue/api/transports.md | 28 + site/vue/api/transports/custom.md | 5 + site/vue/api/transports/fallback.md | 5 + site/vue/api/transports/http.md | 5 + site/vue/api/transports/unstable_connector.md | 6 + site/vue/api/transports/webSocket.md | 5 + site/vue/api/utilities/deserialize.md | 5 + site/vue/api/utilities/serialize.md | 5 + site/vue/getting-started.md | 217 + site/vue/guides/chain-properties.md | 97 + site/vue/guides/connect-wallet.md | 387 + site/vue/guides/error-handling.md | 39 + site/vue/guides/faq.md | 9 + site/vue/guides/read-from-contract.md | 206 + site/vue/guides/send-transaction.md | 311 + site/vue/guides/ssr.md | 77 + site/vue/guides/tanstack-query.md | 287 + site/vue/guides/viem.md | 94 + site/vue/guides/write-to-contract.md | 387 + site/vue/installation.md | 41 + site/vue/typescript.md | 302 + site/vue/why.md | 46 + src/App.tsx | 1362 + src/index.css | 21 + src/main.tsx | 24 + src/vite-env.d.ts | 1 + src/wagmi.ts | 22 + tsconfig.base.json | 42 + tsconfig.json | 8 + tsconfig.node.json | 10 + vite.config.ts | 7 + vitest.config.ts | 27 + vitest.workspace.ts | 85 + 3547 files changed, 636661 insertions(+), 12234 deletions(-) create mode 100644 .changeset/new-elephants-travel.md create mode 100644 .changeset/nice-pandas-clap.md create mode 100644 .changeset/quick-hairs-scream.md create mode 100644 .changeset/spicy-bats-juggle.md create mode 100644 .changeset/tall-fans-mate.md create mode 100644 .changeset/tiny-laws-dream.md create mode 100644 .changeset/young-guests-care.md create mode 100644 .circleci/config.yml create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/DISCUSSION_TEMPLATE/connector-request.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/docs_issue.yml create mode 100644 .github/README.md create mode 100644 .github/SECURITY.md create mode 100644 .github/dependabot.yml create mode 100644 .github/logo-dark.svg create mode 100644 .github/logo-light.svg create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/Vercel Preview Deployment.yml create mode 100644 .github/workflows/changesets.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/issue-labeled.yml create mode 100644 .github/workflows/jekyll-docker.yml create mode 100644 .github/workflows/lock-issue.yml create mode 100644 .github/workflows/octopusdeploy.yml create mode 100644 .github/workflows/pull-request.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/snapshot.yml create mode 100644 .github/workflows/verify.yml create mode 100644 .npmrc create mode 100644 .vscode/extensions.json create mode 100644 .vscode/workspace.code-workspace create mode 100644 biome.json create mode 100644 index.html create mode 100644 packages/cli/CHANGELOG.md create mode 100644 packages/cli/README.md create mode 100644 packages/cli/package.json create mode 100644 packages/cli/src/cli.ts create mode 100644 packages/cli/src/commands/generate.test.ts create mode 100644 packages/cli/src/commands/generate.ts create mode 100644 packages/cli/src/commands/init.test.ts create mode 100644 packages/cli/src/commands/init.ts create mode 100644 packages/cli/src/config.test.ts create mode 100644 packages/cli/src/config.ts create mode 100644 packages/cli/src/errors.ts create mode 100644 packages/cli/src/exports/config.test.ts create mode 100644 packages/cli/src/exports/config.ts create mode 100644 packages/cli/src/exports/index.test-d.ts create mode 100644 packages/cli/src/exports/index.test.ts create mode 100644 packages/cli/src/exports/index.ts create mode 100644 packages/cli/src/exports/plugins.test.ts create mode 100644 packages/cli/src/exports/plugins.ts create mode 100644 packages/cli/src/logger.test.ts create mode 100644 packages/cli/src/logger.ts create mode 100644 packages/cli/src/plugins/__fixtures__/foundry/.gitignore create mode 100644 packages/cli/src/plugins/__fixtures__/foundry/foundry.toml create mode 100644 packages/cli/src/plugins/__fixtures__/foundry/src/Counter.sol create mode 100644 packages/cli/src/plugins/__fixtures__/foundry/src/Foo.sol create mode 100644 packages/cli/src/plugins/__fixtures__/hardhat/.gitignore create mode 100644 packages/cli/src/plugins/__fixtures__/hardhat/contracts/Counter.sol create mode 100644 packages/cli/src/plugins/__fixtures__/hardhat/contracts/Foo.sol create mode 100644 packages/cli/src/plugins/__fixtures__/hardhat/hardhat.config.js create mode 100644 packages/cli/src/plugins/__fixtures__/hardhat/package.json create mode 100644 packages/cli/src/plugins/__snapshots__/blockExplorer.test.ts.snap create mode 100644 packages/cli/src/plugins/__snapshots__/etherscan.test.ts.snap create mode 100644 packages/cli/src/plugins/__snapshots__/fetch.test.ts.snap create mode 100644 packages/cli/src/plugins/__snapshots__/sourcify.test.ts.snap create mode 100644 packages/cli/src/plugins/actions.test.ts create mode 100644 packages/cli/src/plugins/actions.ts create mode 100644 packages/cli/src/plugins/blockExplorer.test.ts create mode 100644 packages/cli/src/plugins/blockExplorer.ts create mode 100644 packages/cli/src/plugins/etherscan.test.ts create mode 100644 packages/cli/src/plugins/etherscan.ts create mode 100644 packages/cli/src/plugins/fetch.test.ts create mode 100644 packages/cli/src/plugins/fetch.ts create mode 100644 packages/cli/src/plugins/foundry.test.ts create mode 100644 packages/cli/src/plugins/foundry.ts create mode 100644 packages/cli/src/plugins/hardhat.test.ts create mode 100644 packages/cli/src/plugins/hardhat.ts create mode 100644 packages/cli/src/plugins/react.test.ts create mode 100644 packages/cli/src/plugins/react.ts create mode 100644 packages/cli/src/plugins/sourcify.test.ts create mode 100644 packages/cli/src/plugins/sourcify.ts create mode 100644 packages/cli/src/types.ts create mode 100644 packages/cli/src/utils/findConfig.test.ts create mode 100644 packages/cli/src/utils/findConfig.ts create mode 100644 packages/cli/src/utils/format.test.ts create mode 100644 packages/cli/src/utils/format.ts create mode 100644 packages/cli/src/utils/getAddressDocString.test.ts create mode 100644 packages/cli/src/utils/getAddressDocString.ts create mode 100644 packages/cli/src/utils/getIsUsingTypeScript.test.ts create mode 100644 packages/cli/src/utils/getIsUsingTypeScript.ts create mode 100644 packages/cli/src/utils/loadEnv.test.ts create mode 100644 packages/cli/src/utils/loadEnv.ts create mode 100644 packages/cli/src/utils/packages.test.ts create mode 100644 packages/cli/src/utils/packages.ts create mode 100644 packages/cli/src/utils/resolveConfig.test.ts create mode 100644 packages/cli/src/utils/resolveConfig.ts create mode 100644 packages/cli/src/version.ts create mode 100644 packages/cli/test/constants.ts create mode 100644 packages/cli/test/setup.ts create mode 100644 packages/cli/test/utils.ts create mode 100644 packages/cli/tsconfig.build.json create mode 100644 packages/cli/tsconfig.json create mode 100644 packages/cli/types/fixturez.d.ts create mode 100644 packages/connectors/CHANGELOG.md create mode 100644 packages/connectors/README.md create mode 100644 packages/connectors/package.json create mode 100644 packages/connectors/src/coinbaseWallet.test.ts create mode 100644 packages/connectors/src/coinbaseWallet.ts create mode 100644 packages/connectors/src/exports/index.test.ts create mode 100644 packages/connectors/src/exports/index.ts create mode 100644 packages/connectors/src/metaMask.test.ts create mode 100644 packages/connectors/src/metaMask.ts create mode 100644 packages/connectors/src/safe.test.ts create mode 100644 packages/connectors/src/safe.ts create mode 100644 packages/connectors/src/version.ts create mode 100644 packages/connectors/src/walletConnect.test.ts create mode 100644 packages/connectors/src/walletConnect.ts create mode 100644 packages/connectors/tsconfig.build.json create mode 100644 packages/connectors/tsconfig.json create mode 100644 packages/core/README.md create mode 100644 packages/core/src/actions/call.test.ts create mode 100644 packages/core/src/actions/call.ts create mode 100644 packages/core/src/actions/codegen/createReadContract.test-d.ts create mode 100644 packages/core/src/actions/codegen/createReadContract.test.ts create mode 100644 packages/core/src/actions/codegen/createReadContract.ts create mode 100644 packages/core/src/actions/codegen/createSimulateContract.test-d.ts create mode 100644 packages/core/src/actions/codegen/createSimulateContract.test.ts create mode 100644 packages/core/src/actions/codegen/createSimulateContract.ts create mode 100644 packages/core/src/actions/codegen/createWatchContractEvent.test-d.ts create mode 100644 packages/core/src/actions/codegen/createWatchContractEvent.test.ts create mode 100644 packages/core/src/actions/codegen/createWatchContractEvent.ts create mode 100644 packages/core/src/actions/codegen/createWriteContract.test-d.ts create mode 100644 packages/core/src/actions/codegen/createWriteContract.test.ts create mode 100644 packages/core/src/actions/codegen/createWriteContract.ts create mode 100644 packages/core/src/actions/connect.test-d.ts create mode 100644 packages/core/src/actions/connect.test.ts create mode 100644 packages/core/src/actions/connect.ts create mode 100644 packages/core/src/actions/deployContract.test-d.ts create mode 100644 packages/core/src/actions/deployContract.test.ts create mode 100644 packages/core/src/actions/deployContract.ts create mode 100644 packages/core/src/actions/disconnect.test.ts create mode 100644 packages/core/src/actions/disconnect.ts create mode 100644 packages/core/src/actions/estimateFeesPerGas.test-d.ts create mode 100644 packages/core/src/actions/estimateFeesPerGas.test.ts create mode 100644 packages/core/src/actions/estimateFeesPerGas.ts create mode 100644 packages/core/src/actions/estimateGas.test-d.ts create mode 100644 packages/core/src/actions/estimateGas.test.ts create mode 100644 packages/core/src/actions/estimateGas.ts create mode 100644 packages/core/src/actions/estimateMaxPriorityFeePerGas.test.ts create mode 100644 packages/core/src/actions/estimateMaxPriorityFeePerGas.ts create mode 100644 packages/core/src/actions/getAccount.test-d.ts create mode 100644 packages/core/src/actions/getAccount.test.ts create mode 100644 packages/core/src/actions/getAccount.ts create mode 100644 packages/core/src/actions/getBalance.test.ts create mode 100644 packages/core/src/actions/getBalance.ts create mode 100644 packages/core/src/actions/getBlock.test-d.ts create mode 100644 packages/core/src/actions/getBlock.test.ts create mode 100644 packages/core/src/actions/getBlock.ts create mode 100644 packages/core/src/actions/getBlockNumber.test.ts create mode 100644 packages/core/src/actions/getBlockNumber.ts create mode 100644 packages/core/src/actions/getBlockTransactionCount.test.ts create mode 100644 packages/core/src/actions/getBlockTransactionCount.ts create mode 100644 packages/core/src/actions/getBytecode.test.ts create mode 100644 packages/core/src/actions/getBytecode.ts create mode 100644 packages/core/src/actions/getCallsStatus.test.ts create mode 100644 packages/core/src/actions/getCallsStatus.ts create mode 100644 packages/core/src/actions/getCapabilities.test.ts create mode 100644 packages/core/src/actions/getCapabilities.ts create mode 100644 packages/core/src/actions/getChainId.test.ts create mode 100644 packages/core/src/actions/getChainId.ts create mode 100644 packages/core/src/actions/getChains.test-d.ts create mode 100644 packages/core/src/actions/getChains.test.ts create mode 100644 packages/core/src/actions/getChains.ts create mode 100644 packages/core/src/actions/getClient.test-d.ts create mode 100644 packages/core/src/actions/getClient.test.ts create mode 100644 packages/core/src/actions/getClient.ts create mode 100644 packages/core/src/actions/getConnections.test.ts create mode 100644 packages/core/src/actions/getConnections.ts create mode 100644 packages/core/src/actions/getConnectorClient.test-d.ts create mode 100644 packages/core/src/actions/getConnectorClient.test.ts create mode 100644 packages/core/src/actions/getConnectorClient.ts create mode 100644 packages/core/src/actions/getConnectors.test.ts create mode 100644 packages/core/src/actions/getConnectors.ts create mode 100644 packages/core/src/actions/getEnsAddress.test.ts create mode 100644 packages/core/src/actions/getEnsAddress.ts create mode 100644 packages/core/src/actions/getEnsAvatar.test.ts create mode 100644 packages/core/src/actions/getEnsAvatar.ts create mode 100644 packages/core/src/actions/getEnsName.test.ts create mode 100644 packages/core/src/actions/getEnsName.ts create mode 100644 packages/core/src/actions/getEnsResolver.test.ts create mode 100644 packages/core/src/actions/getEnsResolver.ts create mode 100644 packages/core/src/actions/getEnsText.test.ts create mode 100644 packages/core/src/actions/getEnsText.ts create mode 100644 packages/core/src/actions/getFeeHistory.test.ts create mode 100644 packages/core/src/actions/getFeeHistory.ts create mode 100644 packages/core/src/actions/getGasPrice.test.ts create mode 100644 packages/core/src/actions/getGasPrice.ts create mode 100644 packages/core/src/actions/getProof.test.ts create mode 100644 packages/core/src/actions/getProof.ts create mode 100644 packages/core/src/actions/getPublicClient.test-d.ts create mode 100644 packages/core/src/actions/getPublicClient.test.ts create mode 100644 packages/core/src/actions/getPublicClient.ts create mode 100644 packages/core/src/actions/getStorageAt.test.ts create mode 100644 packages/core/src/actions/getStorageAt.ts create mode 100644 packages/core/src/actions/getToken.test.ts create mode 100644 packages/core/src/actions/getToken.ts create mode 100644 packages/core/src/actions/getTransaction.test-d.ts create mode 100644 packages/core/src/actions/getTransaction.test.ts create mode 100644 packages/core/src/actions/getTransaction.ts create mode 100644 packages/core/src/actions/getTransactionConfirmations.test-d.ts create mode 100644 packages/core/src/actions/getTransactionConfirmations.test.ts create mode 100644 packages/core/src/actions/getTransactionConfirmations.ts create mode 100644 packages/core/src/actions/getTransactionCount.test.ts create mode 100644 packages/core/src/actions/getTransactionCount.ts create mode 100644 packages/core/src/actions/getTransactionReceipt.test-d.ts create mode 100644 packages/core/src/actions/getTransactionReceipt.test.ts create mode 100644 packages/core/src/actions/getTransactionReceipt.ts create mode 100644 packages/core/src/actions/getWalletClient.test-d.ts create mode 100644 packages/core/src/actions/getWalletClient.test.ts create mode 100644 packages/core/src/actions/getWalletClient.ts create mode 100644 packages/core/src/actions/multicall.test-d.ts create mode 100644 packages/core/src/actions/multicall.test.ts create mode 100644 packages/core/src/actions/multicall.ts create mode 100644 packages/core/src/actions/prepareTransactionRequest.test-d.ts create mode 100644 packages/core/src/actions/prepareTransactionRequest.test.ts create mode 100644 packages/core/src/actions/prepareTransactionRequest.ts create mode 100644 packages/core/src/actions/readContract.test-d.ts create mode 100644 packages/core/src/actions/readContract.test.ts create mode 100644 packages/core/src/actions/readContract.ts create mode 100644 packages/core/src/actions/readContracts.test-d.ts create mode 100644 packages/core/src/actions/readContracts.test.ts create mode 100644 packages/core/src/actions/readContracts.ts create mode 100644 packages/core/src/actions/reconnect.test.ts create mode 100644 packages/core/src/actions/reconnect.ts create mode 100644 packages/core/src/actions/sendCalls.test.ts create mode 100644 packages/core/src/actions/sendCalls.ts create mode 100644 packages/core/src/actions/sendTransaction.test-d.ts create mode 100644 packages/core/src/actions/sendTransaction.test.ts create mode 100644 packages/core/src/actions/sendTransaction.ts create mode 100644 packages/core/src/actions/showCallsStatus.test.ts create mode 100644 packages/core/src/actions/showCallsStatus.ts create mode 100644 packages/core/src/actions/signMessage.test.ts create mode 100644 packages/core/src/actions/signMessage.ts create mode 100644 packages/core/src/actions/signTypedData.test-d.ts create mode 100644 packages/core/src/actions/signTypedData.test.ts create mode 100644 packages/core/src/actions/signTypedData.ts create mode 100644 packages/core/src/actions/simulateContract.test-d.ts create mode 100644 packages/core/src/actions/simulateContract.test.ts create mode 100644 packages/core/src/actions/simulateContract.ts create mode 100644 packages/core/src/actions/switchAccount.test.ts create mode 100644 packages/core/src/actions/switchAccount.ts create mode 100644 packages/core/src/actions/switchChain.test.ts create mode 100644 packages/core/src/actions/switchChain.ts create mode 100644 packages/core/src/actions/verifyMessage.test.ts create mode 100644 packages/core/src/actions/verifyMessage.ts create mode 100644 packages/core/src/actions/verifyTypedData.test.ts create mode 100644 packages/core/src/actions/verifyTypedData.ts create mode 100644 packages/core/src/actions/waitForCallsStatus.test.ts create mode 100644 packages/core/src/actions/waitForCallsStatus.ts create mode 100644 packages/core/src/actions/waitForTransactionReceipt.test-d.ts create mode 100644 packages/core/src/actions/waitForTransactionReceipt.test.ts create mode 100644 packages/core/src/actions/waitForTransactionReceipt.ts create mode 100644 packages/core/src/actions/watchAccount.test.ts create mode 100644 packages/core/src/actions/watchAccount.ts create mode 100644 packages/core/src/actions/watchAsset.test.ts create mode 100644 packages/core/src/actions/watchAsset.ts create mode 100644 packages/core/src/actions/watchBlockNumber.test-d.ts create mode 100644 packages/core/src/actions/watchBlockNumber.test.ts create mode 100644 packages/core/src/actions/watchBlockNumber.ts create mode 100644 packages/core/src/actions/watchBlocks.test-d.ts create mode 100644 packages/core/src/actions/watchBlocks.test.ts create mode 100644 packages/core/src/actions/watchBlocks.ts create mode 100644 packages/core/src/actions/watchChainId.test.ts create mode 100644 packages/core/src/actions/watchChainId.ts create mode 100644 packages/core/src/actions/watchChains.test.ts create mode 100644 packages/core/src/actions/watchChains.ts create mode 100644 packages/core/src/actions/watchClient.test-d.ts create mode 100644 packages/core/src/actions/watchClient.test.ts create mode 100644 packages/core/src/actions/watchClient.ts create mode 100644 packages/core/src/actions/watchConnections.test.ts create mode 100644 packages/core/src/actions/watchConnections.ts create mode 100644 packages/core/src/actions/watchConnectors.test.ts create mode 100644 packages/core/src/actions/watchConnectors.ts create mode 100644 packages/core/src/actions/watchContractEvent.test-d.ts create mode 100644 packages/core/src/actions/watchContractEvent.test.ts create mode 100644 packages/core/src/actions/watchContractEvent.ts create mode 100644 packages/core/src/actions/watchPendingTransactions.test-d.ts create mode 100644 packages/core/src/actions/watchPendingTransactions.test.ts create mode 100644 packages/core/src/actions/watchPendingTransactions.ts create mode 100644 packages/core/src/actions/watchPublicClient.test-d.ts create mode 100644 packages/core/src/actions/watchPublicClient.test.ts create mode 100644 packages/core/src/actions/watchPublicClient.ts create mode 100644 packages/core/src/actions/writeContract.test-d.ts create mode 100644 packages/core/src/actions/writeContract.ts create mode 100644 packages/core/src/connectors/createConnector.test.ts create mode 100644 packages/core/src/connectors/createConnector.ts create mode 100644 packages/core/src/connectors/injected.test.ts create mode 100644 packages/core/src/connectors/injected.ts create mode 100644 packages/core/src/connectors/mock.test.ts create mode 100644 packages/core/src/connectors/mock.ts create mode 100644 packages/core/src/createConfig.test-d.ts create mode 100644 packages/core/src/createConfig.test.ts create mode 100644 packages/core/src/createConfig.ts create mode 100644 packages/core/src/createEmitter.test.ts create mode 100644 packages/core/src/createEmitter.ts create mode 100644 packages/core/src/createStorage.test-d.ts create mode 100644 packages/core/src/createStorage.test.ts create mode 100644 packages/core/src/createStorage.ts create mode 100644 packages/core/src/errors/base.test.ts create mode 100644 packages/core/src/errors/base.ts create mode 100644 packages/core/src/errors/config.test.ts create mode 100644 packages/core/src/errors/config.ts create mode 100644 packages/core/src/errors/connector.test.ts create mode 100644 packages/core/src/errors/connector.ts create mode 100644 packages/core/src/experimental/actions/writeContracts.test.ts create mode 100644 packages/core/src/experimental/actions/writeContracts.ts create mode 100644 packages/core/src/experimental/query/writeContracts.test.ts create mode 100644 packages/core/src/experimental/query/writeContracts.ts create mode 100644 packages/core/src/exports/actions.test.ts create mode 100644 packages/core/src/exports/actions.ts create mode 100644 packages/core/src/exports/chains.ts create mode 100644 packages/core/src/exports/codegen.test.ts create mode 100644 packages/core/src/exports/codegen.ts create mode 100644 packages/core/src/exports/experimental.ts create mode 100644 packages/core/src/exports/index.test.ts create mode 100644 packages/core/src/exports/index.ts create mode 100644 packages/core/src/exports/internal.test.ts create mode 100644 packages/core/src/exports/internal.ts create mode 100644 packages/core/src/exports/query.test.ts create mode 100644 packages/core/src/exports/query.ts create mode 100644 packages/core/src/hydrate.test.ts create mode 100644 packages/core/src/hydrate.ts create mode 100644 packages/core/src/query/call.test.ts create mode 100644 packages/core/src/query/call.ts create mode 100644 packages/core/src/query/connect.test.ts create mode 100644 packages/core/src/query/connect.ts create mode 100644 packages/core/src/query/deployContract.test.ts create mode 100644 packages/core/src/query/deployContract.ts create mode 100644 packages/core/src/query/disconnect.test.ts create mode 100644 packages/core/src/query/disconnect.ts create mode 100644 packages/core/src/query/estimateFeesPerGas.test.ts create mode 100644 packages/core/src/query/estimateFeesPerGas.ts create mode 100644 packages/core/src/query/estimateGas.test-d.ts create mode 100644 packages/core/src/query/estimateGas.test.ts create mode 100644 packages/core/src/query/estimateGas.ts create mode 100644 packages/core/src/query/estimateMaxPriorityFeePerGas.test.ts create mode 100644 packages/core/src/query/estimateMaxPriorityFeePerGas.ts create mode 100644 packages/core/src/query/getBalance.test.ts create mode 100644 packages/core/src/query/getBalance.ts create mode 100644 packages/core/src/query/getBlock.test.ts create mode 100644 packages/core/src/query/getBlock.ts create mode 100644 packages/core/src/query/getBlockNumber.test.ts create mode 100644 packages/core/src/query/getBlockNumber.ts create mode 100644 packages/core/src/query/getBlockTransactionCount.test.ts create mode 100644 packages/core/src/query/getBlockTransactionCount.ts create mode 100644 packages/core/src/query/getBytecode.test.ts create mode 100644 packages/core/src/query/getBytecode.ts create mode 100644 packages/core/src/query/getCallsStatus.test.ts create mode 100644 packages/core/src/query/getCallsStatus.ts create mode 100644 packages/core/src/query/getCapabilities.test.ts create mode 100644 packages/core/src/query/getCapabilities.ts create mode 100644 packages/core/src/query/getConnectorClient.test.ts create mode 100644 packages/core/src/query/getConnectorClient.ts create mode 100644 packages/core/src/query/getEnsAddress.test.ts create mode 100644 packages/core/src/query/getEnsAddress.ts create mode 100644 packages/core/src/query/getEnsAvatar.test.ts create mode 100644 packages/core/src/query/getEnsAvatar.ts create mode 100644 packages/core/src/query/getEnsName.test.ts create mode 100644 packages/core/src/query/getEnsName.ts create mode 100644 packages/core/src/query/getEnsResolver.test.ts create mode 100644 packages/core/src/query/getEnsResolver.ts create mode 100644 packages/core/src/query/getEnsText.test.ts create mode 100644 packages/core/src/query/getEnsText.ts create mode 100644 packages/core/src/query/getFeeHistory.test.ts create mode 100644 packages/core/src/query/getFeeHistory.ts create mode 100644 packages/core/src/query/getGasPrice.test.ts create mode 100644 packages/core/src/query/getGasPrice.ts create mode 100644 packages/core/src/query/getProof.test.ts create mode 100644 packages/core/src/query/getProof.ts create mode 100644 packages/core/src/query/getStorageAt.test.ts create mode 100644 packages/core/src/query/getStorageAt.ts create mode 100644 packages/core/src/query/getToken.test.ts create mode 100644 packages/core/src/query/getToken.ts create mode 100644 packages/core/src/query/getTransaction.test.ts create mode 100644 packages/core/src/query/getTransaction.ts create mode 100644 packages/core/src/query/getTransactionConfirmations.test.ts create mode 100644 packages/core/src/query/getTransactionConfirmations.ts create mode 100644 packages/core/src/query/getTransactionCount.test.ts create mode 100644 packages/core/src/query/getTransactionCount.ts create mode 100644 packages/core/src/query/getTransactionReceipt.test.ts create mode 100644 packages/core/src/query/getTransactionReceipt.ts create mode 100644 packages/core/src/query/getWalletClient.test.ts create mode 100644 packages/core/src/query/getWalletClient.ts create mode 100644 packages/core/src/query/infiniteReadContracts.test-d.ts create mode 100644 packages/core/src/query/infiniteReadContracts.test.ts create mode 100644 packages/core/src/query/infiniteReadContracts.ts create mode 100644 packages/core/src/query/prepareTransactionRequest.test.ts create mode 100644 packages/core/src/query/prepareTransactionRequest.ts create mode 100644 packages/core/src/query/readContract.test-d.ts create mode 100644 packages/core/src/query/readContract.test.ts create mode 100644 packages/core/src/query/readContract.ts create mode 100644 packages/core/src/query/readContracts.test-d.ts create mode 100644 packages/core/src/query/readContracts.test.ts create mode 100644 packages/core/src/query/readContracts.ts create mode 100644 packages/core/src/query/reconnect.test.ts create mode 100644 packages/core/src/query/reconnect.ts create mode 100644 packages/core/src/query/sendCalls.test.ts create mode 100644 packages/core/src/query/sendCalls.ts create mode 100644 packages/core/src/query/sendTransaction.test.ts create mode 100644 packages/core/src/query/sendTransaction.ts create mode 100644 packages/core/src/query/showCallsStatus.test.ts create mode 100644 packages/core/src/query/showCallsStatus.ts create mode 100644 packages/core/src/query/signMessage.test.ts create mode 100644 packages/core/src/query/signMessage.ts create mode 100644 packages/core/src/query/signTypedData.test.ts create mode 100644 packages/core/src/query/signTypedData.ts create mode 100644 packages/core/src/query/simulateContract.test-d.ts create mode 100644 packages/core/src/query/simulateContract.test.ts create mode 100644 packages/core/src/query/simulateContract.ts create mode 100644 packages/core/src/query/switchAccount.test.ts create mode 100644 packages/core/src/query/switchAccount.ts create mode 100644 packages/core/src/query/switchChain.test.ts create mode 100644 packages/core/src/query/switchChain.ts create mode 100644 packages/core/src/query/types.ts create mode 100644 packages/core/src/query/utils.test.ts create mode 100644 packages/core/src/query/utils.ts create mode 100644 packages/core/src/query/verifyMessage.test.ts create mode 100644 packages/core/src/query/verifyMessage.ts create mode 100644 packages/core/src/query/verifyTypedData.test.ts create mode 100644 packages/core/src/query/verifyTypedData.ts create mode 100644 packages/core/src/query/waitForCallsStatus.test.ts create mode 100644 packages/core/src/query/waitForCallsStatus.ts create mode 100644 packages/core/src/query/waitForTransactionReceipt.test.ts create mode 100644 packages/core/src/query/waitForTransactionReceipt.ts create mode 100644 packages/core/src/query/watchAsset.test.ts create mode 100644 packages/core/src/query/watchAsset.ts create mode 100644 packages/core/src/query/writeContract.test-d.ts create mode 100644 packages/core/src/query/writeContract.test.ts create mode 100644 packages/core/src/query/writeContract.ts create mode 100644 packages/core/src/transports/connector.test.ts create mode 100644 packages/core/src/transports/connector.ts create mode 100644 packages/core/src/transports/fallback.test.ts create mode 100644 packages/core/src/transports/fallback.ts create mode 100644 packages/core/src/types/chain.test-d.ts create mode 100644 packages/core/src/types/chain.ts create mode 100644 packages/core/src/types/properties.ts create mode 100644 packages/core/src/types/register.ts create mode 100644 packages/core/src/types/unit.ts create mode 100644 packages/core/src/types/utils.test-d.ts create mode 100644 packages/core/src/types/utils.ts create mode 100644 packages/core/src/utils/cookie.test.ts create mode 100644 packages/core/src/utils/cookie.ts create mode 100644 packages/core/src/utils/deepEqual.test.ts create mode 100644 packages/core/src/utils/deepEqual.ts create mode 100644 packages/core/src/utils/deserialize.test.ts create mode 100644 packages/core/src/utils/deserialize.ts create mode 100644 packages/core/src/utils/extractRpcUrls.test.ts create mode 100644 packages/core/src/utils/extractRpcUrls.ts create mode 100644 packages/core/src/utils/getAction.test.ts create mode 100644 packages/core/src/utils/getAction.ts create mode 100644 packages/core/src/utils/getUnit.test.ts create mode 100644 packages/core/src/utils/getUnit.ts create mode 100644 packages/core/src/utils/getVersion.test.ts create mode 100644 packages/core/src/utils/getVersion.ts create mode 100644 packages/core/src/utils/normalizeChainId.test.ts create mode 100644 packages/core/src/utils/normalizeChainId.ts create mode 100644 packages/core/src/utils/serialize.test.ts create mode 100644 packages/core/src/utils/serialize.ts create mode 100644 packages/core/src/utils/uid.ts create mode 100644 packages/core/test/setup.ts create mode 100644 packages/core/tsconfig.build.json create mode 100644 packages/core/tsconfig.json create mode 100644 packages/create-wagmi/CHANGELOG.md create mode 100644 packages/create-wagmi/README.md create mode 100644 packages/create-wagmi/package.json create mode 100644 packages/create-wagmi/src/cli.test.ts create mode 100644 packages/create-wagmi/src/cli.ts create mode 100644 packages/create-wagmi/src/frameworks.ts create mode 100644 packages/create-wagmi/src/index.test-d.ts create mode 100644 packages/create-wagmi/src/utils.ts create mode 100644 packages/create-wagmi/src/version.ts create mode 100644 packages/create-wagmi/templates/next/README.md create mode 100644 packages/create-wagmi/templates/next/_env.local create mode 100644 packages/create-wagmi/templates/next/_gitignore create mode 100644 packages/create-wagmi/templates/next/_npmrc create mode 100644 packages/create-wagmi/templates/next/next-env.d.ts create mode 100644 packages/create-wagmi/templates/next/next.config.js create mode 100644 packages/create-wagmi/templates/next/package.json create mode 100644 packages/create-wagmi/templates/next/src/app/globals.css create mode 100644 packages/create-wagmi/templates/next/src/app/layout.tsx create mode 100644 packages/create-wagmi/templates/next/src/app/page.tsx create mode 100644 packages/create-wagmi/templates/next/src/app/providers.tsx create mode 100644 packages/create-wagmi/templates/next/src/wagmi.ts create mode 100644 packages/create-wagmi/templates/next/tsconfig.json create mode 100644 packages/create-wagmi/templates/nuxt/_env.local create mode 100644 packages/create-wagmi/templates/nuxt/_gitignore create mode 100644 packages/create-wagmi/templates/nuxt/_npmrc create mode 100644 packages/create-wagmi/templates/nuxt/app.vue create mode 100644 packages/create-wagmi/templates/nuxt/components/Account.vue create mode 100644 packages/create-wagmi/templates/nuxt/components/Connect.vue create mode 100644 packages/create-wagmi/templates/nuxt/nuxt.config.ts create mode 100644 packages/create-wagmi/templates/nuxt/package.json create mode 100644 packages/create-wagmi/templates/nuxt/plugins/wagmi.ts create mode 100644 packages/create-wagmi/templates/nuxt/server/tsconfig.json create mode 100644 packages/create-wagmi/templates/nuxt/tsconfig.json create mode 100644 packages/create-wagmi/templates/nuxt/wagmi.ts create mode 100644 packages/create-wagmi/templates/vite-react/README.md create mode 100644 packages/create-wagmi/templates/vite-react/_env.local create mode 100644 packages/create-wagmi/templates/vite-react/_gitignore create mode 100644 packages/create-wagmi/templates/vite-react/_npmrc create mode 100644 packages/create-wagmi/templates/vite-react/biome.json create mode 100644 packages/create-wagmi/templates/vite-react/index.html create mode 100644 packages/create-wagmi/templates/vite-react/package.json create mode 100644 packages/create-wagmi/templates/vite-react/src/App.tsx create mode 100644 packages/create-wagmi/templates/vite-react/src/index.css create mode 100644 packages/create-wagmi/templates/vite-react/src/main.tsx create mode 100644 packages/create-wagmi/templates/vite-react/src/vite-env.d.ts create mode 100644 packages/create-wagmi/templates/vite-react/src/wagmi.ts create mode 100644 packages/create-wagmi/templates/vite-react/tsconfig.json create mode 100644 packages/create-wagmi/templates/vite-react/tsconfig.node.json create mode 100644 packages/create-wagmi/templates/vite-react/vite.config.ts create mode 100644 packages/create-wagmi/templates/vite-vanilla/_env.local create mode 100644 packages/create-wagmi/templates/vite-vanilla/_gitignore create mode 100644 packages/create-wagmi/templates/vite-vanilla/_npmrc create mode 100644 packages/create-wagmi/templates/vite-vanilla/index.html create mode 100644 packages/create-wagmi/templates/vite-vanilla/package.json create mode 100644 packages/create-wagmi/templates/vite-vanilla/src/main.ts create mode 100644 packages/create-wagmi/templates/vite-vanilla/src/style.css create mode 100644 packages/create-wagmi/templates/vite-vanilla/src/vite-env.d.ts create mode 100644 packages/create-wagmi/templates/vite-vanilla/src/wagmi.ts create mode 100644 packages/create-wagmi/templates/vite-vanilla/tsconfig.json create mode 100644 packages/create-wagmi/templates/vite-vue/README.md create mode 100644 packages/create-wagmi/templates/vite-vue/_env.local create mode 100644 packages/create-wagmi/templates/vite-vue/_gitignore create mode 100644 packages/create-wagmi/templates/vite-vue/_npmrc create mode 100644 packages/create-wagmi/templates/vite-vue/biome.json create mode 100644 packages/create-wagmi/templates/vite-vue/index.html create mode 100644 packages/create-wagmi/templates/vite-vue/package.json create mode 100644 packages/create-wagmi/templates/vite-vue/src/App.vue create mode 100644 packages/create-wagmi/templates/vite-vue/src/components/Account.vue create mode 100644 packages/create-wagmi/templates/vite-vue/src/components/Connect.vue create mode 100644 packages/create-wagmi/templates/vite-vue/src/main.ts create mode 100644 packages/create-wagmi/templates/vite-vue/src/style.css create mode 100644 packages/create-wagmi/templates/vite-vue/src/vite-env.d.ts create mode 100644 packages/create-wagmi/templates/vite-vue/src/wagmi.ts create mode 100644 packages/create-wagmi/templates/vite-vue/tsconfig.json create mode 100644 packages/create-wagmi/templates/vite-vue/tsconfig.node.json create mode 100644 packages/create-wagmi/templates/vite-vue/vite.config.ts create mode 100644 packages/create-wagmi/tsconfig.build.json create mode 100644 packages/create-wagmi/tsconfig.json create mode 100644 packages/react/CHANGELOG.md create mode 100644 packages/react/README.md create mode 100644 packages/react/package.json create mode 100644 packages/react/src/context.test.tsx create mode 100644 packages/react/src/context.ts create mode 100644 packages/react/src/errors/base.test.ts create mode 100644 packages/react/src/errors/base.ts create mode 100644 packages/react/src/errors/context.test.ts create mode 100644 packages/react/src/errors/context.ts create mode 100644 packages/react/src/experimental/hooks/useWriteContracts.test.ts create mode 100644 packages/react/src/experimental/hooks/useWriteContracts.ts create mode 100644 packages/react/src/exports/actions.test.ts create mode 100644 packages/react/src/exports/actions.ts create mode 100644 packages/react/src/exports/actions/experimental.test.ts create mode 100644 packages/react/src/exports/actions/experimental.ts create mode 100644 packages/react/src/exports/chains.ts create mode 100644 packages/react/src/exports/codegen.test.ts create mode 100644 packages/react/src/exports/codegen.ts create mode 100644 packages/react/src/exports/connectors.test.ts create mode 100644 packages/react/src/exports/connectors.ts create mode 100644 packages/react/src/exports/experimental.ts create mode 100644 packages/react/src/exports/index.test.ts create mode 100644 packages/react/src/exports/index.ts create mode 100644 packages/react/src/exports/query.test.ts create mode 100644 packages/react/src/exports/query.ts create mode 100644 packages/react/src/hooks/codegen/createUseReadContract.test-d.ts create mode 100644 packages/react/src/hooks/codegen/createUseReadContract.test.ts create mode 100644 packages/react/src/hooks/codegen/createUseReadContract.ts create mode 100644 packages/react/src/hooks/codegen/createUseSimulateContract.test-d.ts create mode 100644 packages/react/src/hooks/codegen/createUseSimulateContract.test.ts create mode 100644 packages/react/src/hooks/codegen/createUseSimulateContract.ts create mode 100644 packages/react/src/hooks/codegen/createUseWatchContractEvent.test-d.ts create mode 100644 packages/react/src/hooks/codegen/createUseWatchContractEvent.test.ts create mode 100644 packages/react/src/hooks/codegen/createUseWatchContractEvent.ts create mode 100644 packages/react/src/hooks/codegen/createUseWriteContract.test-d.ts create mode 100644 packages/react/src/hooks/codegen/createUseWriteContract.test.ts create mode 100644 packages/react/src/hooks/codegen/createUseWriteContract.ts create mode 100644 packages/react/src/hooks/useAccount.test-d.ts create mode 100644 packages/react/src/hooks/useAccount.test.ts create mode 100644 packages/react/src/hooks/useAccount.ts create mode 100644 packages/react/src/hooks/useAccountEffect.test.ts create mode 100644 packages/react/src/hooks/useAccountEffect.ts create mode 100644 packages/react/src/hooks/useBalance.test-d.ts create mode 100644 packages/react/src/hooks/useBalance.test.ts create mode 100644 packages/react/src/hooks/useBalance.ts create mode 100644 packages/react/src/hooks/useBlock.test-d.ts create mode 100644 packages/react/src/hooks/useBlock.test.ts create mode 100644 packages/react/src/hooks/useBlock.ts create mode 100644 packages/react/src/hooks/useBlockNumber.test-d.ts create mode 100644 packages/react/src/hooks/useBlockNumber.test.ts create mode 100644 packages/react/src/hooks/useBlockNumber.ts create mode 100644 packages/react/src/hooks/useBlockTransactionCount.test-d.ts create mode 100644 packages/react/src/hooks/useBlockTransactionCount.test.ts create mode 100644 packages/react/src/hooks/useBlockTransactionCount.ts create mode 100644 packages/react/src/hooks/useBytecode.test-d.ts create mode 100644 packages/react/src/hooks/useBytecode.test.ts create mode 100644 packages/react/src/hooks/useBytecode.ts create mode 100644 packages/react/src/hooks/useCall.test-d.ts create mode 100644 packages/react/src/hooks/useCall.test.ts create mode 100644 packages/react/src/hooks/useCall.ts create mode 100644 packages/react/src/hooks/useCallsStatus.test.ts create mode 100644 packages/react/src/hooks/useCallsStatus.ts create mode 100644 packages/react/src/hooks/useCapabilities.test.ts create mode 100644 packages/react/src/hooks/useCapabilities.ts create mode 100644 packages/react/src/hooks/useChainId.test-d.ts create mode 100644 packages/react/src/hooks/useChainId.test.ts create mode 100644 packages/react/src/hooks/useChainId.ts create mode 100644 packages/react/src/hooks/useChains.test.ts create mode 100644 packages/react/src/hooks/useChains.ts create mode 100644 packages/react/src/hooks/useClient.test-d.ts create mode 100644 packages/react/src/hooks/useClient.test.ts create mode 100644 packages/react/src/hooks/useClient.ts create mode 100644 packages/react/src/hooks/useConfig.test-d.ts create mode 100644 packages/react/src/hooks/useConfig.test.ts create mode 100644 packages/react/src/hooks/useConfig.ts create mode 100644 packages/react/src/hooks/useConnect.test-d.ts create mode 100644 packages/react/src/hooks/useConnect.test.ts create mode 100644 packages/react/src/hooks/useConnect.ts create mode 100644 packages/react/src/hooks/useConnections.test.ts create mode 100644 packages/react/src/hooks/useConnections.ts create mode 100644 packages/react/src/hooks/useConnectorClient.test-d.ts create mode 100644 packages/react/src/hooks/useConnectorClient.test.tsx create mode 100644 packages/react/src/hooks/useConnectorClient.ts create mode 100644 packages/react/src/hooks/useConnectors.test.ts create mode 100644 packages/react/src/hooks/useConnectors.ts create mode 100644 packages/react/src/hooks/useDeployContract.test-d.ts create mode 100644 packages/react/src/hooks/useDeployContract.test.ts create mode 100644 packages/react/src/hooks/useDeployContract.ts create mode 100644 packages/react/src/hooks/useDisconnect.test-d.ts create mode 100644 packages/react/src/hooks/useDisconnect.test.ts create mode 100644 packages/react/src/hooks/useDisconnect.ts create mode 100644 packages/react/src/hooks/useEnsAddress.test.ts create mode 100644 packages/react/src/hooks/useEnsAddress.ts create mode 100644 packages/react/src/hooks/useEnsAvatar.test.ts create mode 100644 packages/react/src/hooks/useEnsAvatar.ts create mode 100644 packages/react/src/hooks/useEnsName.test.ts create mode 100644 packages/react/src/hooks/useEnsName.ts create mode 100644 packages/react/src/hooks/useEnsResolver.test.ts create mode 100644 packages/react/src/hooks/useEnsResolver.ts create mode 100644 packages/react/src/hooks/useEnsText.test.ts create mode 100644 packages/react/src/hooks/useEnsText.ts create mode 100644 packages/react/src/hooks/useEstimateFeesPerGas.test-d.ts create mode 100644 packages/react/src/hooks/useEstimateFeesPerGas.test.ts create mode 100644 packages/react/src/hooks/useEstimateFeesPerGas.ts create mode 100644 packages/react/src/hooks/useEstimateGas.test-d.ts create mode 100644 packages/react/src/hooks/useEstimateGas.test.ts create mode 100644 packages/react/src/hooks/useEstimateGas.ts create mode 100644 packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.test-d.ts create mode 100644 packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.test.ts create mode 100644 packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.ts create mode 100644 packages/react/src/hooks/useFeeHistory.test-d.ts create mode 100644 packages/react/src/hooks/useFeeHistory.test.ts create mode 100644 packages/react/src/hooks/useFeeHistory.ts create mode 100644 packages/react/src/hooks/useGasPrice.test-d.ts create mode 100644 packages/react/src/hooks/useGasPrice.test.ts create mode 100644 packages/react/src/hooks/useGasPrice.ts create mode 100644 packages/react/src/hooks/useInfiniteReadContracts.test-d.ts create mode 100644 packages/react/src/hooks/useInfiniteReadContracts.test.ts create mode 100644 packages/react/src/hooks/useInfiniteReadContracts.ts create mode 100644 packages/react/src/hooks/usePrepareTransactionRequest.test-d.ts create mode 100644 packages/react/src/hooks/usePrepareTransactionRequest.test.ts create mode 100644 packages/react/src/hooks/usePrepareTransactionRequest.ts create mode 100644 packages/react/src/hooks/useProof.test-d.ts create mode 100644 packages/react/src/hooks/useProof.test.ts create mode 100644 packages/react/src/hooks/useProof.ts create mode 100644 packages/react/src/hooks/usePublicClient.test-d.ts create mode 100644 packages/react/src/hooks/usePublicClient.test.ts create mode 100644 packages/react/src/hooks/usePublicClient.ts create mode 100644 packages/react/src/hooks/useReadContract.test-d.ts create mode 100644 packages/react/src/hooks/useReadContract.test.ts create mode 100644 packages/react/src/hooks/useReadContract.ts create mode 100644 packages/react/src/hooks/useReadContracts.test-d.ts create mode 100644 packages/react/src/hooks/useReadContracts.test.ts create mode 100644 packages/react/src/hooks/useReadContracts.ts create mode 100644 packages/react/src/hooks/useReconnect.test-d.ts create mode 100644 packages/react/src/hooks/useReconnect.test.ts create mode 100644 packages/react/src/hooks/useReconnect.ts create mode 100644 packages/react/src/hooks/useSendCalls.test.ts create mode 100644 packages/react/src/hooks/useSendCalls.ts create mode 100644 packages/react/src/hooks/useSendTransaction.test-d.ts create mode 100644 packages/react/src/hooks/useSendTransaction.test.ts create mode 100644 packages/react/src/hooks/useSendTransaction.ts create mode 100644 packages/react/src/hooks/useShowCallsStatus.ts create mode 100644 packages/react/src/hooks/useSignMessage.test-d.ts create mode 100644 packages/react/src/hooks/useSignMessage.test.ts create mode 100644 packages/react/src/hooks/useSignMessage.ts create mode 100644 packages/react/src/hooks/useSignTypedData.test-d.ts create mode 100644 packages/react/src/hooks/useSignTypedData.test.ts create mode 100644 packages/react/src/hooks/useSignTypedData.ts create mode 100644 packages/react/src/hooks/useSimulateContract.test-d.ts create mode 100644 packages/react/src/hooks/useSimulateContract.test.ts create mode 100644 packages/react/src/hooks/useSimulateContract.ts create mode 100644 packages/react/src/hooks/useStorageAt.test-d.ts create mode 100644 packages/react/src/hooks/useStorageAt.test.ts create mode 100644 packages/react/src/hooks/useStorageAt.ts create mode 100644 packages/react/src/hooks/useSwitchAccount.test-d.ts create mode 100644 packages/react/src/hooks/useSwitchAccount.test.ts create mode 100644 packages/react/src/hooks/useSwitchAccount.ts create mode 100644 packages/react/src/hooks/useSwitchChain.test-d.ts create mode 100644 packages/react/src/hooks/useSwitchChain.test.ts create mode 100644 packages/react/src/hooks/useSwitchChain.ts create mode 100644 packages/react/src/hooks/useSyncExternalStoreWithTracked.test.tsx create mode 100644 packages/react/src/hooks/useSyncExternalStoreWithTracked.ts create mode 100644 packages/react/src/hooks/useToken.test-d.ts create mode 100644 packages/react/src/hooks/useToken.test.ts create mode 100644 packages/react/src/hooks/useToken.ts create mode 100644 packages/react/src/hooks/useTransaction.test-d.ts create mode 100644 packages/react/src/hooks/useTransaction.test.ts create mode 100644 packages/react/src/hooks/useTransaction.ts create mode 100644 packages/react/src/hooks/useTransactionConfirmations.test-d.ts create mode 100644 packages/react/src/hooks/useTransactionConfirmations.test.ts create mode 100644 packages/react/src/hooks/useTransactionConfirmations.ts create mode 100644 packages/react/src/hooks/useTransactionCount.test-d.ts create mode 100644 packages/react/src/hooks/useTransactionCount.test.ts create mode 100644 packages/react/src/hooks/useTransactionCount.ts create mode 100644 packages/react/src/hooks/useTransactionReceipt.test-d.ts create mode 100644 packages/react/src/hooks/useTransactionReceipt.test.ts create mode 100644 packages/react/src/hooks/useTransactionReceipt.ts create mode 100644 packages/react/src/hooks/useVerifyMessage.test-d.ts create mode 100644 packages/react/src/hooks/useVerifyMessage.test.ts create mode 100644 packages/react/src/hooks/useVerifyMessage.ts create mode 100644 packages/react/src/hooks/useVerifyTypedData.test-d.ts create mode 100644 packages/react/src/hooks/useVerifyTypedData.test.ts create mode 100644 packages/react/src/hooks/useVerifyTypedData.ts create mode 100644 packages/react/src/hooks/useWaitForCallsStatus.test.ts create mode 100644 packages/react/src/hooks/useWaitForCallsStatus.ts create mode 100644 packages/react/src/hooks/useWaitForTransactionReceipt.test-d.ts create mode 100644 packages/react/src/hooks/useWaitForTransactionReceipt.test.ts create mode 100644 packages/react/src/hooks/useWaitForTransactionReceipt.ts create mode 100644 packages/react/src/hooks/useWalletClient.test-d.ts create mode 100644 packages/react/src/hooks/useWalletClient.test.tsx create mode 100644 packages/react/src/hooks/useWalletClient.ts create mode 100644 packages/react/src/hooks/useWatchAsset.test-d.ts create mode 100644 packages/react/src/hooks/useWatchAsset.test.ts create mode 100644 packages/react/src/hooks/useWatchAsset.ts create mode 100644 packages/react/src/hooks/useWatchBlockNumber.test-d.ts create mode 100644 packages/react/src/hooks/useWatchBlockNumber.test.ts create mode 100644 packages/react/src/hooks/useWatchBlockNumber.ts create mode 100644 packages/react/src/hooks/useWatchBlocks.test-d.ts create mode 100644 packages/react/src/hooks/useWatchBlocks.test.ts create mode 100644 packages/react/src/hooks/useWatchBlocks.ts create mode 100644 packages/react/src/hooks/useWatchContractEvent.test-d.ts create mode 100644 packages/react/src/hooks/useWatchContractEvent.test.ts create mode 100644 packages/react/src/hooks/useWatchContractEvent.ts create mode 100644 packages/react/src/hooks/useWatchPendingTransactions.test-d.ts create mode 100644 packages/react/src/hooks/useWatchPendingTransactions.test.ts create mode 100644 packages/react/src/hooks/useWatchPendingTransactions.ts create mode 100644 packages/react/src/hooks/useWriteContract.test-d.ts create mode 100644 packages/react/src/hooks/useWriteContract.test.ts create mode 100644 packages/react/src/hooks/useWriteContract.ts create mode 100644 packages/react/src/hydrate.ts create mode 100644 packages/react/src/types/properties.ts create mode 100644 packages/react/src/utils/getVersion.test.ts create mode 100644 packages/react/src/utils/getVersion.ts create mode 100644 packages/react/src/utils/query.ts create mode 100644 packages/react/src/version.ts create mode 100644 packages/react/test/setup.ts create mode 100644 packages/react/tsconfig.build.json create mode 100644 packages/react/tsconfig.json create mode 100644 packages/register-tests/react/package.json create mode 100644 packages/register-tests/react/src/config.ts create mode 100644 packages/register-tests/react/src/createUseSimulateContract.test-d.ts create mode 100644 packages/register-tests/react/src/createUseWriteContract.test-d.ts create mode 100644 packages/register-tests/react/src/useAccount.test-d.ts create mode 100644 packages/register-tests/react/src/useBlock.test-d.ts create mode 100644 packages/register-tests/react/src/useChainId.test-d.ts create mode 100644 packages/register-tests/react/src/useChains.test-d.ts create mode 100644 packages/register-tests/react/src/useClient.test-d.ts create mode 100644 packages/register-tests/react/src/useConfig.test-d.ts create mode 100644 packages/register-tests/react/src/useConnect.test-d.ts create mode 100644 packages/register-tests/react/src/usePrepareTransactionRequest.test-d.ts create mode 100644 packages/register-tests/react/src/usePublicClient.ts create mode 100644 packages/register-tests/react/src/useReadContract.test-d.ts create mode 100644 packages/register-tests/react/src/useReadContracts.test-d.ts create mode 100644 packages/register-tests/react/src/useSendTransaction.test-d.ts create mode 100644 packages/register-tests/react/src/useSimulateContract.test-d.ts create mode 100644 packages/register-tests/react/src/useSwitchChain.test-d.ts create mode 100644 packages/register-tests/react/src/useTransaction.test-d.ts create mode 100644 packages/register-tests/react/src/useTransactionConfirmations.test-d.ts create mode 100644 packages/register-tests/react/src/useTransactionReceipt.test-d.ts create mode 100644 packages/register-tests/react/src/useWaitForTransactionReceipt.ts create mode 100644 packages/register-tests/react/src/useWriteContract.test-d.ts create mode 100644 packages/register-tests/react/tsconfig.json create mode 100644 packages/register-tests/vue/package.json create mode 100644 packages/register-tests/vue/src/config.ts create mode 100644 packages/register-tests/vue/src/useAccount.test-d.ts create mode 100644 packages/register-tests/vue/src/useChainId.test-d.ts create mode 100644 packages/register-tests/vue/src/useChains.test-d.ts create mode 100644 packages/register-tests/vue/src/useClient.test-d.ts create mode 100644 packages/register-tests/vue/src/useConfig.test-d.ts create mode 100644 packages/register-tests/vue/src/useConnect.test-d.ts create mode 100644 packages/register-tests/vue/src/useReadContract.test-d.ts create mode 100644 packages/register-tests/vue/src/useSendTransaction.test-d.ts create mode 100644 packages/register-tests/vue/src/useSimulateContract.test-d.ts create mode 100644 packages/register-tests/vue/src/useSwitchChain.test-d.ts create mode 100644 packages/register-tests/vue/src/useTransaction.test-d.ts create mode 100644 packages/register-tests/vue/src/useTransactionReceipt.test-d.ts create mode 100644 packages/register-tests/vue/src/useWaitForTransaction.test-d.ts create mode 100644 packages/register-tests/vue/src/useWriteContract.test-d.ts create mode 100644 packages/register-tests/vue/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/.changeset/README.md create mode 100644 packages/sequence-core-1.0.0/.changeset/config.json create mode 100644 packages/sequence-core-1.0.0/.github/CODEOWNERS create mode 100644 packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/custom.md create mode 100644 packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 packages/sequence-core-1.0.0/.github/actions/install-dependencies/action.yml create mode 100644 packages/sequence-core-1.0.0/.github/workflows/on_pr_pnpm-format-label.yml create mode 100644 packages/sequence-core-1.0.0/.github/workflows/pnpm-format.yml create mode 100644 packages/sequence-core-1.0.0/.github/workflows/publish-dists.yml create mode 100644 packages/sequence-core-1.0.0/.github/workflows/tests.yml create mode 100644 packages/sequence-core-1.0.0/.gitmodules create mode 100644 packages/sequence-core-1.0.0/.prettierrc create mode 100644 packages/sequence-core-1.0.0/.vscode/launch.json create mode 100644 packages/sequence-core-1.0.0/.vscode/settings.json create mode 100644 packages/sequence-core-1.0.0/LICENSE create mode 100644 packages/sequence-core-1.0.0/README.md create mode 100644 packages/sequence-core-1.0.0/corepack.tgz create mode 100644 packages/sequence-core-1.0.0/extras/docs/README.md create mode 100644 packages/sequence-core-1.0.0/extras/docs/app/favicon.ico create mode 100644 packages/sequence-core-1.0.0/extras/docs/app/fonts/GeistMonoVF.woff create mode 100644 packages/sequence-core-1.0.0/extras/docs/app/fonts/GeistVF.woff create mode 100644 packages/sequence-core-1.0.0/extras/docs/app/globals.css create mode 100644 packages/sequence-core-1.0.0/extras/docs/app/layout.tsx create mode 100644 packages/sequence-core-1.0.0/extras/docs/app/page.module.css create mode 100644 packages/sequence-core-1.0.0/extras/docs/app/page.tsx create mode 100644 packages/sequence-core-1.0.0/extras/docs/eslint.config.js create mode 100644 packages/sequence-core-1.0.0/extras/docs/next.config.js create mode 100644 packages/sequence-core-1.0.0/extras/docs/package.json create mode 100644 packages/sequence-core-1.0.0/extras/docs/public/file-text.svg create mode 100644 packages/sequence-core-1.0.0/extras/docs/public/globe.svg create mode 100644 packages/sequence-core-1.0.0/extras/docs/public/next.svg create mode 100644 packages/sequence-core-1.0.0/extras/docs/public/turborepo-dark.svg create mode 100644 packages/sequence-core-1.0.0/extras/docs/public/turborepo-light.svg create mode 100644 packages/sequence-core-1.0.0/extras/docs/public/vercel.svg create mode 100644 packages/sequence-core-1.0.0/extras/docs/public/window.svg create mode 100644 packages/sequence-core-1.0.0/extras/docs/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/extras/web/README.md create mode 100644 packages/sequence-core-1.0.0/extras/web/app/favicon.ico create mode 100644 packages/sequence-core-1.0.0/extras/web/app/fonts/GeistMonoVF.woff create mode 100644 packages/sequence-core-1.0.0/extras/web/app/fonts/GeistVF.woff create mode 100644 packages/sequence-core-1.0.0/extras/web/app/globals.css create mode 100644 packages/sequence-core-1.0.0/extras/web/app/layout.tsx create mode 100644 packages/sequence-core-1.0.0/extras/web/app/page.module.css create mode 100644 packages/sequence-core-1.0.0/extras/web/app/page.tsx create mode 100644 packages/sequence-core-1.0.0/extras/web/eslint.config.js create mode 100644 packages/sequence-core-1.0.0/extras/web/next.config.js create mode 100644 packages/sequence-core-1.0.0/extras/web/package.json create mode 100644 packages/sequence-core-1.0.0/extras/web/public/file-text.svg create mode 100644 packages/sequence-core-1.0.0/extras/web/public/globe.svg create mode 100644 packages/sequence-core-1.0.0/extras/web/public/next.svg create mode 100644 packages/sequence-core-1.0.0/extras/web/public/turborepo-dark.svg create mode 100644 packages/sequence-core-1.0.0/extras/web/public/turborepo-light.svg create mode 100644 packages/sequence-core-1.0.0/extras/web/public/vercel.svg create mode 100644 packages/sequence-core-1.0.0/extras/web/public/window.svg create mode 100644 packages/sequence-core-1.0.0/extras/web/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/foundry.lock create mode 100644 packages/sequence-core-1.0.0/lefthook.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/.env.sample create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/.github/workflows/test.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/.gitmodules create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/foundry.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/.gitattributes create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/.github/CODEOWNERS create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/.github/workflows/ci.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/.github/workflows/sync.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/CONTRIBUTING.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/LICENSE-APACHE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/LICENSE-MIT create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/RELEASE_CHECKLIST.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/foundry.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/scripts/vm.py create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/Base.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/Config.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/LibVariable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/Script.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdAssertions.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdChains.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdCheats.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdConfig.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdConstants.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdError.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdInvariant.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdJson.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdMath.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdStorage.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdStyle.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdToml.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/StdUtils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/Test.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/Vm.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/console.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/console2.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IERC165.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IERC4626.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IERC6909.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IERC7540.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IERC7575.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/interfaces/IMulticall3.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/src/safeconsole.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/CommonBase.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/Config.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/LibVariable.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdAssertions.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdChains.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdCheats.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdConstants.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdError.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdJson.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdMath.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdStorage.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdStyle.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdToml.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/StdUtils.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/Vm.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/compilation/CompilationScript.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/compilation/CompilationScriptBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/compilation/CompilationTest.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/compilation/CompilationTestBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/fixtures/broadcast.log.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/fixtures/config.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/fixtures/test.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/forge-std/test/fixtures/test.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/.env.sample create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/.github/workflows/tests.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/.gitmodules create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/.prettierignore create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/.prettierrc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/LICENSE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/build_proxy.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/docs/CHAINED.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/docs/CONFIGURATIONS.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/docs/PAYLOAD.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/docs/SESSIONS.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/docs/SIGNATURE.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/foundry.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lefthook.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/.eslintrc.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/.github/workflows/build.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/.solcover.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/.solhint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/.solhintignore create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/LICENSE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/README.md create mode 100644 "packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/audits/EIP_4337_\342\200\223_Ethereum_Account_Abstraction_Incremental_Audit_Feb_2023.pdf" create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/BaseAccount.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/BasePaymaster.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/EntryPoint.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/EntryPointSimulations.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/Helpers.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/NonceManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/SenderCreator.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/StakeManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/core/UserOperationLib.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/IAccount.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/IAccountExecute.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/IAggregator.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/IEntryPoint.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/IEntryPointSimulations.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/INonceManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/IPaymaster.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/IStakeManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/interfaces/PackedUserOperation.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/LegacyTokenPaymaster.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/SimpleAccount.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/SimpleAccountFactory.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/TokenPaymaster.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/VerifyingPaymaster.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/bls/BLSAccount.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/bls/BLSAccountFactory.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/bls/BLSHelper.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/bls/BLSSignatureAggregator.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/bls/IBLSAccount.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/bls/lib/BLSOpen.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/bls/lib/hubble-contracts/contracts/libs/BLS.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/bls/lib/hubble-contracts/contracts/libs/ModExp.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/callback/TokenCallbackHandler.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/utils/IOracle.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/utils/OracleHelper.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/samples/utils/UniswapHelper.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/contracts/utils/Exec.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deploy/1_deploy_entrypoint.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deploy/2_deploy_SimpleAccountFactory.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/arbitrum/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/arbitrum/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/arbitrum/solcInputs/a4c52f0671aad8941c53d6ead2063803.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/gnosis/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/gnosis/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/gnosis/solcInputs/a4c52f0671aad8941c53d6ead2063803.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/goerli/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/goerli/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/goerli/solcInputs/a4c52f0671aad8941c53d6ead2063803.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/kovan/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/kovan/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/kovan/SimpleWallet.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/kovan/TestCounter.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/kovan/solcInputs/9255faacf3ae4e81db1326413027bfa0.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mainnet/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mainnet/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mainnet/SimpleAccountFactory.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mainnet/solcInputs/02113a2ed1850c3774563305ee607f11.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mainnet/solcInputs/a4c52f0671aad8941c53d6ead2063803.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mainnet/solcInputs/cfbebdf1101dd2bc0f310cb0b7d62cb7.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/matic/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/matic/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/matic/solcInputs/a4c52f0671aad8941c53d6ead2063803.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/matic/solcInputs/cfbebdf1101dd2bc0f310cb0b7d62cb7.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mumbai/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mumbai/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/mumbai/solcInputs/a4c52f0671aad8941c53d6ead2063803.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/optimism/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/optimism/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/optimism/solcInputs/a4c52f0671aad8941c53d6ead2063803.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/sepolia/.chainId create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/sepolia/EntryPoint.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/deployments/sepolia/solcInputs/a4c52f0671aad8941c53d6ead2063803.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/erc/ERCS/erc-4337.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/erc/ERCS/erc-7562.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/erc/assets/erc-4337/bundle-seq-pm.svg create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/erc/assets/erc-4337/bundle-seq.svg create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/erc/assets/erc-4337/image1.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/erc/assets/erc-4337/image2.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/gascalc/0-init-gas-checker.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/gascalc/1-simple-wallet.gas.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/gascalc/2-paymaster.gas.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/gascalc/3-huge-tx-gas.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/gascalc/4-paymaster-postop.gas.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/gascalc/5-token-paymaster.gas.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/gascalc/GasChecker.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/hardhat.config.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/reports/gas-checker.txt create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/check-gas-reports create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/docker-gascalc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/docker-gascalc.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/gascalc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/hh-wrapper create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/postpack-contracts-package.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/prepack-contracts-package.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/sample-script.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/scripts/solcErrors create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/src/AASigner.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/src/Create2Factory.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/src/Utils.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/src/runop.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/0-create2factory.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/UserOp.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/UserOperation.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/aa.init.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/chaiHelper.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/debugTx.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/entrypoint.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/entrypointsimulations.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/helpers.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/paymaster.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/samples/OracleHelper.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/samples/TokenPaymaster.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/simple-wallet.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/solidityTypes.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/testExecAccount.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/testutils.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/verifying_paymaster.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/test/y.bls.test.ts create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/account-abstraction/yarn.lock create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/.gitattributes create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/.github/workflows/ci.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/.github/workflows/sync.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/CONTRIBUTING.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/LICENSE-APACHE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/LICENSE-MIT create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/foundry.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/scripts/vm.py create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/Base.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/Script.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdAssertions.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdChains.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdCheats.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdConstants.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdError.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdInvariant.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdJson.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdMath.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdStorage.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdStyle.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdToml.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/StdUtils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/Test.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/Vm.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/console.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/console2.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IERC165.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IERC4626.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IERC6909.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IERC7540.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IERC7575.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/interfaces/IMulticall3.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/src/safeconsole.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/CommonBase.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdAssertions.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdChains.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdCheats.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdConstants.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdError.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdJson.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdMath.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdStorage.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdStyle.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdToml.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/StdUtils.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/Vm.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/compilation/CompilationScript.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/compilation/CompilationScriptBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/compilation/CompilationTest.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/compilation/CompilationTestBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/fixtures/broadcast.log.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/fixtures/test.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/forge-std/test/fixtures/test.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.changeset/config.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.codecov.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.editorconfig create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.githooks/pre-push create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/bug_report.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/config.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/feature_request.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/actions/gas-compare/action.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/actions/setup/action.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/actions/storage-layout/action.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/workflows/actionlint.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/workflows/changeset.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/workflows/checks.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/workflows/docs.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/workflows/formal-verification.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/workflows/release-cycle.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.github/workflows/upgradeable.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.gitmodules create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.mocharc.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.prettierrc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/.solcover.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/CODE_OF_CONDUCT.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/CONTRIBUTING.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/FUNDING.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/GUIDELINES.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/LICENSE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/RELEASING.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/SECURITY.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/audits/2017-03.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/audits/2018-10.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/audits/2022-10-Checkpoints.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/audits/2022-10-ERC4626.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/audits/2023-05-v4.9.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/audits/2023-10-v5.0.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/audits/2024-10-v5.1.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/audits/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/Makefile create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/diff/access_manager_AccessManager.sol.patch create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/AccessControlDefaultAdminRulesHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/AccessControlHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/AccessManagedHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/AccessManagerHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/DoubleEndedQueueHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/ERC20FlashMintHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/ERC20PermitHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/ERC20WrapperHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/ERC3156FlashBorrowerHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/ERC721Harness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/ERC721ReceiverHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/EnumerableMapHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/EnumerableSetHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/InitializableHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/NoncesHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/Ownable2StepHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/OwnableHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/PausableHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/harnesses/TimelockControllerHarness.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/reports/2021-10.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/reports/2022-03.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/reports/2022-05.pdf create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/run.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/AccessControl.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/AccessControlDefaultAdminRules.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/AccessManaged.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/AccessManager.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/DoubleEndedQueue.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/ERC20.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/ERC20FlashMint.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/ERC20Wrapper.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/ERC721.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/EnumerableMap.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/EnumerableSet.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/Initializable.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/Nonces.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/Ownable.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/Ownable2Step.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/Pausable.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/TimelockController.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/helpers/helpers.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IAccessControl.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IAccessControlDefaultAdminRules.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IAccessManaged.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IAccessManager.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IERC20.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IERC2612.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IERC3156FlashBorrower.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IERC3156FlashLender.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IERC5313.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IERC721.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IERC721Receiver.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IOwnable.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/certora/specs/methods/IOwnable2Step.spec create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/AccessControl.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/Ownable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/extensions/AccessControlDefaultAdminRules.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/extensions/AccessControlEnumerable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/extensions/IAccessControlDefaultAdminRules.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/extensions/IAccessControlEnumerable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/manager/AccessManaged.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/manager/AccessManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/manager/AuthorityUtils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/manager/IAccessManaged.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/manager/IAccessManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/access/manager/IAuthority.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/account/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/account/utils/draft-ERC4337Utils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/account/utils/draft-ERC7579Utils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/finance/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/finance/VestingWallet.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/finance/VestingWalletCliff.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/Governor.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/IGovernor.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/TimelockController.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorCountingFractional.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorCountingOverridable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorCountingSimple.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorPreventLateQuorum.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorSettings.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorStorage.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockAccess.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockCompound.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockControl.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotes.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotesQuorumFraction.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/utils/IVotes.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/utils/Votes.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/governance/utils/VotesExtended.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1155MetadataURI.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1155Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1271.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1363Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1363Spender.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1820Implementer.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1820Registry.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC1967.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC2309.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC2612.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC3156.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashLender.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC4906.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC5267.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC5313.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC5805.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC6372.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC721Enumerable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC721Metadata.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC721Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC777.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC777Recipient.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/IERC777Sender.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC4337.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC7579.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC7674.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/metatx/ERC2771Context.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/metatx/ERC2771Forwarder.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/metatx/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/AccessManagedTarget.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/AccessManagerMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ArraysMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/AuthorityMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/Base64Dirty.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/BatchCaller.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/CallReceiverMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ConstructorMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ContextMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/DummyImplementation.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/EIP712Verifier.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165InterfacesSupported.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MaliciousData.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MissingData.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165NotSupported.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165ReturnBomb.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ERC2771ContextMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ERC3156FlashBorrowerMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/EtherReceiverMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/InitializableMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/MerkleProofCustomHashMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/MerkleTreeMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/MulticallHelper.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/MultipleInheritanceInitializableMocks.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/PausableMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ReentrancyAttack.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ReentrancyMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/ReentrancyTransientMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/RegressionImplementation.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/SingleInheritanceInitializableMocks.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/Stateless.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/StorageSlotMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/TimelockReentrant.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/TransientSlotMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/UpgradeableBeaconMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/VotesExtendedMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/VotesMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/account/utils/ERC7579UtilsMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/compound/CompTimelock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/ERC20WithAutoMinerReward.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/ERC4626Fees.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/MyNFT.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessControlERC20MintBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessControlERC20MintMissing.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessControlERC20MintOnlyRole.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessControlModified.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessControlNonRevokableAdmin.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessManagedERC20MintBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/access-control/MyContractOwnable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/governance/MyGovernor.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/governance/MyToken.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/governance/MyTokenTimestampBased.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/governance/MyTokenWrapped.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/token/ERC1155/GameItems.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/token/ERC1155/MyERC115HolderContract.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/token/ERC20/GLDToken.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/token/ERC721/GameItem.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/utilities/Base64NFT.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/docs/utilities/Multicall.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorCountingOverridableMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorFractionalMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorPreventLateQuorumMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorStorageMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorTimelockAccessMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorTimelockCompoundMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorTimelockControlMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorVoteMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/governance/GovernorWithParamsMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/proxy/BadBeacon.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/proxy/ClashingImplementation.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/proxy/UUPSUpgradeableMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC1155ReceiverMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC1363ForceApproveMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC1363NoReturnMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC1363ReceiverMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC1363ReturnFalseMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC1363SpenderMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20ApprovalMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20DecimalsMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20ExcessDecimalsMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20FlashMintMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20ForceApproveMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20GetterHelper.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20Mock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20MulticallMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20NoReturnMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20Reentrant.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20ReturnFalseMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20VotesAdditionalCheckpointsMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20VotesLegacyMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC20VotesTimestampMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC4626LimitsMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC4626Mock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC4626OffsetMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC4646FeesMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC721ConsecutiveEnumerableMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC721ConsecutiveMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC721ReceiverMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/mocks/token/ERC721URIStorageMock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/Clones.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/Proxy.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/ERC1155.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Burnable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Pausable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Supply.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Holder.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Utils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC1363.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Capped.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20FlashMint.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Pausable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Wrapper.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20TemporaryApproval.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/utils/ERC1363Utils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Burnable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Consecutive.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Enumerable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Pausable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Royalty.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Wrapper.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Enumerable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Utils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/common/ERC2981.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/token/common/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Address.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Arrays.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Base64.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Bytes.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/CAIP10.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/CAIP2.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Comparators.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Context.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Create2.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Errors.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Multicall.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Nonces.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/NoncesKeyed.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Packing.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Panic.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Pausable.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/README.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/ReentrancyGuardTransient.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/ShortStrings.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/SlotDerivation.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/Strings.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/TransientSlot.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/cryptography/Hashes.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/cryptography/P256.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/cryptography/RSA.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165Checker.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/math/Math.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/structs/BitMaps.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/structs/Checkpoints.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/structs/CircularBuffer.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/structs/DoubleEndedQueue.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableMap.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/structs/Heap.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/structs/MerkleTree.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/utils/types/Time.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/vendor/compound/ICompoundTimelock.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/contracts/vendor/compound/LICENSE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/antora.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/config.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/access-control-multiple.svg create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/access-manager-functions.svg create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/access-manager.svg create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-attack-3a.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-attack-3b.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-attack-6.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-attack.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-deposit.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-mint.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-rate-linear.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-rate-loglog.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/erc4626-rate-loglogext.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/tally-exec.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/images/tally-vote.png create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/nav.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/access-control.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/backwards-compatibility.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/crowdsales.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/drafts.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc1155.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc20-supply.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc20.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc4626.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc721.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/extending-contracts.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/faq.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/governance.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/index.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/tokens.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/upgradeable.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/utilities.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/modules/ROOT/pages/wizard.adoc create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/templates/contract.hbs create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/templates/helpers.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/templates/page.hbs create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/docs/templates/properties.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/eslint.config.mjs create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/foundry.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/fv-requirements.txt create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/hardhat.config.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/hardhat/async-test-sanity.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/hardhat/env-artifacts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/hardhat/ignore-unreachable-warnings.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/hardhat/remappings.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/hardhat/skip-foundry-tests.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/hardhat/task-test-get-files.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/erc4626-tests/ERC4626.prop.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/erc4626-tests/ERC4626.test.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/erc4626-tests/LICENSE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/erc4626-tests/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/.gitattributes create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/.github/workflows/ci.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/.github/workflows/sync.yml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/LICENSE-APACHE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/LICENSE-MIT create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/foundry.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/scripts/vm.py create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/Base.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/Script.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdAssertions.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdChains.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdCheats.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdError.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdInvariant.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdJson.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdMath.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdStorage.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdStyle.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdToml.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/StdUtils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/Test.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/Vm.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/console.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/console2.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/interfaces/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/interfaces/IERC165.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/interfaces/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/interfaces/IERC4626.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/interfaces/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/interfaces/IMulticall3.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/mocks/MockERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/mocks/MockERC721.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/src/safeconsole.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdAssertions.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdChains.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdCheats.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdError.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdJson.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdMath.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdStorage.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdStyle.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdToml.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/StdUtils.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/Vm.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/compilation/CompilationScript.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/compilation/CompilationScriptBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/compilation/CompilationTest.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/compilation/CompilationTestBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/fixtures/broadcast.log.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/fixtures/test.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/fixtures/test.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/mocks/MockERC20.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/forge-std/test/mocks/MockERC721.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/halmos-cheatcodes/LICENSE create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/halmos-cheatcodes/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/SVM.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/SymTest.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/logo.svg create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/netlify.toml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/package-lock.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/remappings.txt create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/renovate.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/checks/compare-layout.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/checks/compareGasReports.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/checks/coverage.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/checks/extract-layout.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/checks/generation.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/checks/inheritance-ordering.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/checks/pragma-consistency.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/gen-nav.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/format-lines.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/helpers/sanitize.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/run.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Arrays.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Arrays.opts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Checkpoints.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Checkpoints.opts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Checkpoints.t.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableMap.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableMap.opts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableSet.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableSet.opts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/MerkleProof.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/MerkleProof.opts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Packing.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Packing.opts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Packing.t.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/SafeCast.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/Slot.opts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/SlotDerivation.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/SlotDerivation.t.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/StorageSlot.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/StorageSlotMock.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/TransientSlot.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/TransientSlotMock.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/generate/templates/conversion.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/git-user-config.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/helpers.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/prepack.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/prepare-docs.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/prepare.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/format-changelog.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/synchronize-versions.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/update-comment.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/version.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/exit-prerelease.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/github-release.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/integrity-check.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/pack.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/publish.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/rerun.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/set-changesets-pr-title.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/start.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/release/workflow/state.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/remove-ignored-artifacts.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/solhint-custom/index.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/solhint-custom/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/update-docs-branch.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/upgradeable/README.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/upgradeable/patch-apply.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/upgradeable/patch-save.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/upgradeable/transpile-onto.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/upgradeable/transpile.sh create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/scripts/upgradeable/upgradeable.patch create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/slither.config.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/solhint.config.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/TESTING.md create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/AccessControl.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/AccessControl.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/Ownable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/Ownable2Step.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/extensions/AccessControlDefaultAdminRules.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/extensions/AccessControlEnumerable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/manager/AccessManaged.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/manager/AccessManager.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/manager/AccessManager.predicate.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/manager/AccessManager.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/access/manager/AuthorityUtils.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/account/utils/draft-ERC4337Utils.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/account/utils/draft-ERC7579Utils.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/account/utils/draft-ERC7579Utils.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/bin/EntryPoint070.abi create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/bin/EntryPoint070.bytecode create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/bin/SenderCreator070.abi create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/bin/SenderCreator070.bytecode create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/finance/VestingWallet.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/finance/VestingWallet.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/finance/VestingWalletCliff.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/Governor.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/Governor.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/TimelockController.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorCountingFractional.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorCountingOverridable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorERC721.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorPreventLateQuorum.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorStorage.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockAccess.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockCompound.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockControl.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorVotesQuorumFraction.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/extensions/GovernorWithParams.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/utils/ERC6372.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/utils/Votes.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/utils/Votes.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/governance/utils/VotesExtended.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/access-manager.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/account.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/chains.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/constants.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/deploy.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/eip712-types.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/eip712.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/enums.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/erc4337-entrypoint.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/erc4337.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/erc7579.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/governance.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/iterate.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/math.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/methods.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/random.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/storage.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/strings.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/time.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/helpers/txpool.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/metatx/ERC2771Context.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/metatx/ERC2771Forwarder.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/metatx/ERC2771Forwarder.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/Clones.behaviour.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/Clones.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/Clones.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/ERC1967/ERC1967Proxy.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/ERC1967/ERC1967Utils.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/Proxy.behaviour.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/beacon/BeaconProxy.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/beacon/UpgradeableBeacon.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/transparent/ProxyAdmin.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/transparent/TransparentUpgradeableProxy.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/utils/Initializable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/proxy/utils/UUPSUpgradeable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/sanity.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC1155/ERC1155.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC1155/ERC1155.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Burnable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Pausable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Supply.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155URIStorage.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC1155/utils/ERC1155Holder.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC1155/utils/ERC1155Utils.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/ERC20.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/ERC20.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC1363.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Burnable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Capped.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20FlashMint.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Pausable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Permit.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Votes.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Wrapper.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC4626.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC4626.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/extensions/draft-ERC20TemporaryApproval.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC20/utils/SafeERC20.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/ERC721.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/ERC721.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/ERC721Enumerable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Burnable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Consecutive.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Consecutive.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Pausable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Royalty.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721URIStorage.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Votes.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Wrapper.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/utils/ERC721Holder.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/ERC721/utils/ERC721Utils.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/token/common/ERC2981.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Address.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Arrays.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Arrays.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Base64.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Base64.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Bytes.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/CAIP.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Context.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Context.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Create2.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Create2.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Multicall.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Nonces.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Nonces.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/NoncesKeyed.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Packing.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Packing.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Panic.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Pausable.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/ReentrancyGuard.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/ShortStrings.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/ShortStrings.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/SlotDerivation.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/SlotDerivation.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/StorageSlot.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Strings.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/Strings.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/TransientSlot.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/ECDSA.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/EIP712.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/MerkleProof.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/MessageHashUtils.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/P256.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/P256.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/RSA.helper.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/RSA.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/SigVer15_186-3.rsp create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/SignatureChecker.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/cryptography/ecdsa_secp256r1_sha256_p1363_test.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/introspection/ERC165.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/introspection/ERC165Checker.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/introspection/SupportsInterface.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/math/Math.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/math/Math.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/math/SafeCast.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/math/SignedMath.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/math/SignedMath.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/BitMap.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/Checkpoints.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/Checkpoints.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/CircularBuffer.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/DoubleEndedQueue.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/EnumerableMap.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/EnumerableMap.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/EnumerableSet.behavior.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/EnumerableSet.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/Heap.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/Heap.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/structs/MerkleTree.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/lib/openzeppelin-contracts/test/utils/types/Time.test.js create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/package.json create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/pnpm-lock.yaml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/pnpm-workspace.yaml create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/script/Deploy.s.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/Estimator.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/Factory.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/Guest.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/Simulator.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/Stage1Module.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/Stage2Module.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/Wallet.huff create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/Wallet.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/passkeys/Passkeys.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/recovery/Recovery.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/SessionErrors.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/SessionManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/SessionSig.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/explicit/ExplicitSessionManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/explicit/IExplicitSessionManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/explicit/Permission.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/explicit/PermissionValidator.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/implicit/Attestation.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/implicit/ISignalsImplicitMode.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/extensions/sessions/implicit/ImplicitSessionManager.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/Calls.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/ERC4337v07.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/Hooks.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/Implementation.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/Nonce.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/Payload.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/ReentrancyGuard.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/Storage.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/auth/BaseAuth.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/auth/BaseSig.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/auth/SelfAuth.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/auth/Stage1Auth.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/auth/Stage2Auth.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IAccount.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IAuth.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/ICheckpointer.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IDelegatedExtension.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IERC1155Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IERC1271.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IERC223Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IERC721Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IERC777Receiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IEntryPoint.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/IPartialAuth.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/modules/interfaces/ISapient.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/utils/Base64.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/utils/LibBytes.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/utils/LibOptim.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/utils/P256.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/src/utils/WebAuthn.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/Factory.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/Guest.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/Stage1Module.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/Wallet.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/passkeys/Passkeys.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/recovery/Recovery.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/Attestation.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/Permission.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/SessionCalls.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/SessionManager.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/SessionSig.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/SessionTestBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/explicit/ExplicitSessionManager.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/explicit/PermissionValidator.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/extensions/sessions/implicit/ImplicitSessionManager.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/extensions/recovery/RecoveryDenialOfService.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/extensions/sessions/ExtendedSessionTestBase.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/extensions/sessions/SessionDenialOfService.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/extensions/sessions/SessionLimitIncrementTest.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/extensions/sessions/SessionSelfCall.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/extensions/sessions/SessionSignatureAbuse.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/extensions/sessions/SessionUsingERC4337.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/extensions/sessions/SessionValueForwarding.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/integrations/modules/ERC4337v07/ERC4337Entrypoint.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/mocks/AcceptAll.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/mocks/CanReenter.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/mocks/Emitter.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/mocks/MockERC20.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/mocks/MockPayableReceiver.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/mocks/ValueForwarder.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/modules/BaseSig.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/modules/Calls.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/modules/ERC4337v07.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/modules/Hooks.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/modules/Implementation.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/modules/Nonce.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/modules/Payload.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/utils/Base64.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/utils/LibBytes.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/utils/PrimitivesRPC.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/lib/sequence-v3/test/utils/TestUtils.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/script/Deploy.s.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/src/helper/SignalsImplicitMode.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/src/registry/IImplicitProjectRegistry.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/src/registry/IImplicitProjectValidation.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/src/registry/ImplicitProjectRegistry.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/test/ImplicitProjectRegistry.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/test/SignalsImplicitMode.t.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/test/TestHelper.sol create mode 100644 packages/sequence-core-1.0.0/lib/signals-implicit-mode/test/mock/SignalsImplicitModeMock.sol create mode 100644 packages/sequence-core-1.0.0/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/README.md create mode 100644 packages/sequence-core-1.0.0/packages/services/api/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/packages/services/api/README.md create mode 100644 packages/sequence-core-1.0.0/packages/services/api/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/api/src/api.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/api/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/api/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/services/builder/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/packages/services/builder/README.md create mode 100644 packages/sequence-core-1.0.0/packages/services/builder/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/builder/src/builder.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/builder/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/builder/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/README.md create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/src/client/guard.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/src/local.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/src/sequence.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/src/types.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/test/sequence.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/guard/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/services/identity-instrument/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/identity-instrument/src/challenge.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/identity-instrument/src/identity-instrument.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/identity-instrument/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/identity-instrument/test/challenge.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/identity-instrument/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/services/identity-instrument/vitest.config.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/indexer/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/packages/services/indexer/README.md create mode 100644 packages/sequence-core-1.0.0/packages/services/indexer/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/indexer/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/indexer/src/indexer.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/indexer/src/indexergw.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/indexer/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/services/marketplace/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/packages/services/marketplace/README.md create mode 100644 packages/sequence-core-1.0.0/packages/services/marketplace/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/marketplace/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/marketplace/src/marketplace.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/marketplace/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/services/metadata/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/packages/services/metadata/README.md create mode 100644 packages/sequence-core-1.0.0/packages/services/metadata/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/metadata/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/metadata/src/metadata.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/metadata/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/README.md create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/hardhat.config.js create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/package.json create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/src/local-relayer.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/src/provider-relayer.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/src/rpc-relayer/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/src/rpc-relayer/relayer.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/services/relayer/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/utils/README.md create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/CHANGELOG.md create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/README.md create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/package.json create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/sale/erc1155Sale.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/sale/erc721Sale.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/tokens/erc1155.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/tokens/erc1155Items.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/tokens/erc20.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/tokens/erc6909.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/tokens/erc721.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/tokens/erc721Items.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/erc1271.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/erc5719.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/erc6492.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/factory.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/libs/requireFreshSigners.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/mainModule.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/mainModuleUpgradable.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/moduleHooks.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/sequenceUtils.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/src/wallet/walletProxyHook.ts create mode 100644 packages/sequence-core-1.0.0/packages/utils/abi/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/package.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/envelope.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/preconditions/codec.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/preconditions/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/preconditions/selectors.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/preconditions/types.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/bundler.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/bundlers/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/bundlers/pimlico.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/relayer.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/standard/abi.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/standard/eip6963.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/standard/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/standard/local.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/standard/pk-relayer.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/standard/rpc/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/standard/rpc/relayer.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/relayer/standard/sequence.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/guard.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/passkey.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/pk/encrypted.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/pk/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/session-manager.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/session/explicit.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/session/implicit.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/session/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/signers/session/session.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/cached.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/debug.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/local/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/local/indexed-db.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/local/memory.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/remote/dev-http.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/remote/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/sequence/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/sequence/sessions.gen.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/state/utils.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/utils/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/utils/session/permission-builder.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/src/wallet.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/constants.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/envelope.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/preconditions.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/preconditions/codec.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/preconditions/selectors.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/preconditions/types.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/relayer/bundler.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/relayer/relayer.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/session-manager.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/setup.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/signers-guard.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/signers-index.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/signers-passkey.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/signers-pk-encrypted.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/signers-pk.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/signers-session-explicit.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/signers-session-implicit.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/state/cached.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/state/debug.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/state/local/memory.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/state/utils.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/utils/session/permission-builder.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/test/wallet.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/core/vitest.config.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/README.md create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/eslint.config.mjs create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/package.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/ChainSessionManager.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/DappClient.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/DappTransport.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/types/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/utils/constants.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/utils/errors.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/utils/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/src/utils/storage.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/dapp-client/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/eslint.config.mjs create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/package.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/address.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/config.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/devTools.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/passkeys.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/payload.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/recovery.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/server.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/session.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/sessionExplicit.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/sessionImplicit.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/subcommands/signature.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/src/utils.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives-cli/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/eslint.config.mjs create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/package.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/address.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/attestation.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/config.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/constants.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/context.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/erc-6492.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/extensions/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/extensions/passkeys.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/extensions/recovery.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/generic-tree.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/network.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/payload.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/permission.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/precondition.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/session-config.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/session-signature.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/signature.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/src/utils.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/address.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/attestation.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/config.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/erc-6492.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/generic-tree.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/passkeys.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/payload.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/permission.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/precondition.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/recovery.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/session-config.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/session-signature.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/signature.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/test/utils.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/primitives/vitest.config.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/.env.test create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/package.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/auth-commitments.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/auth-keys.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/generic.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/messages.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/passkey-credentials.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/recovery.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/signatures.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/transactions.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/dbs/wallets.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/identity/signer.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/cron.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/devices.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/errors.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/guards.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/authcode.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/devices.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/guard.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/handler.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/identity.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/otp.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/passkeys.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/handlers/recovery.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/logger.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/manager.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/messages.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/recovery.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/sessions.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/signatures.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/signers.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/transactions.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/device.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/index.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/message-request.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/module.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/recovery.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/signature-request.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/signer.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/transaction-request.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/types/wallet.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/src/sequence/wallets.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/authcode-pkce.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/authcode.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/constants.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/guard.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/identity-auth-dbs.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/identity-signer.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/messages.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/otp.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/passkeys.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/recovery.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/sessions.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/setup.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/transactions.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/test/wallets.test.ts create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/packages/wallet/wdk/vitest.config.ts create mode 100644 packages/sequence-core-1.0.0/pnpm-workspace.yaml create mode 100644 packages/sequence-core-1.0.0/repo/README.md create mode 100644 packages/sequence-core-1.0.0/repo/eslint-config/README.md create mode 100644 packages/sequence-core-1.0.0/repo/eslint-config/base.js create mode 100644 packages/sequence-core-1.0.0/repo/eslint-config/next.js create mode 100644 packages/sequence-core-1.0.0/repo/eslint-config/package.json create mode 100644 packages/sequence-core-1.0.0/repo/eslint-config/react-internal.js create mode 100644 packages/sequence-core-1.0.0/repo/typescript-config/base.json create mode 100644 packages/sequence-core-1.0.0/repo/typescript-config/nextjs.json create mode 100644 packages/sequence-core-1.0.0/repo/typescript-config/package.json create mode 100644 packages/sequence-core-1.0.0/repo/typescript-config/react-library.json create mode 100644 packages/sequence-core-1.0.0/repo/ui/eslint.config.mjs create mode 100644 packages/sequence-core-1.0.0/repo/ui/package.json create mode 100644 packages/sequence-core-1.0.0/repo/ui/src/button.tsx create mode 100644 packages/sequence-core-1.0.0/repo/ui/src/card.tsx create mode 100644 packages/sequence-core-1.0.0/repo/ui/src/code.tsx create mode 100644 packages/sequence-core-1.0.0/repo/ui/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/repo/ui/turbo/generators/config.ts create mode 100644 packages/sequence-core-1.0.0/repo/ui/turbo/generators/templates/component.hbs create mode 100644 packages/sequence-core-1.0.0/turbo.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.eslintignore create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.eslintrc.js create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.gitattributes create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.github/actions/install-dependencies/action.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.github/workflows/ci.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.gitmodules create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.prettierrc create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.solcover.js create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/.solhint.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/0xsequence-wallet-contracts-3.0.1.tgz create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/.github/workflows/test.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/.gitmodules create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/broadcast/Counter.s.sol/1/run-1763223074249.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/broadcast/Counter.s.sol/1/run-1763223530987.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/broadcast/Counter.s.sol/1/run-latest.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/foundry.lock create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/foundry.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/.gitattributes create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/.github/CODEOWNERS create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/.github/dependabot.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/.github/workflows/ci.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/.github/workflows/sync.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/CONTRIBUTING.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/LICENSE-APACHE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/LICENSE-MIT create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/RELEASE_CHECKLIST.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/foundry.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/package.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/scripts/vm.py create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/Base.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/Config.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/LibVariable.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/Script.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdAssertions.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdChains.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdCheats.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdConfig.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdConstants.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdError.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdInvariant.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdJson.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdMath.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdStorage.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdStyle.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdToml.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/StdUtils.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/Test.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/Vm.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/console.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/console2.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IERC165.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IERC4626.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IERC6909.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IERC7540.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IERC7575.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/interfaces/IMulticall3.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/src/safeconsole.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/CommonBase.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/Config.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/LibVariable.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdAssertions.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdChains.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdCheats.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdConstants.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdError.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdJson.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdMath.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdStorage.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdStyle.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdToml.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/StdUtils.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/Vm.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/compilation/CompilationScript.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/compilation/CompilationScriptBase.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/compilation/CompilationTest.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/compilation/CompilationTestBase.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/fixtures/broadcast.log.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/fixtures/config.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/fixtures/test.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/lib/forge-std/test/fixtures/test.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/script/Counter.s.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/src/Counter.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/test/Counter.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/Counter/utils/JsonBindings.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/LICENSE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/audits/v1/Consensys_Diligence.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/audits/v1/Quantstamp_Arcadeum_Report_Final.pdf create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/audits/v1/sequence_quantstamp_audit_feb_2021.pdf create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/audits/v2/Sequence Wallet - Zellic Audit Report.pdf create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/audits/v2/consensys-horizon-sequence-wallet-audit-2023-02.pdf create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/config/PROD.env.sample create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/Factory.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/Wallet.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/hooks/WalletProxyHook.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/hooks/interfaces/IWalletProxy.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/interfaces/IERC1271Wallet.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/interfaces/receivers/IERC1155Receiver.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/interfaces/receivers/IERC223Receiver.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/interfaces/receivers/IERC721Receiver.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/interfaces/receivers/IERC777Receiver.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/interfaces/tokens/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/interfaces/tokens/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/interfaces/tokens/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/AlwaysRevertMock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/CallReceiverMock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/DelegateCallMock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/ERC1155Mock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/ERC165CheckerMock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/ERC20Mock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/ERC721Mock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/GasBurnerMock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/HookCallerMock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/HookMock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/LibBytesImpl.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/LibBytesPointerImpl.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/LibStringImp.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/mocks/ModuleMock.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/GuestModule.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/MainModule.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/MainModuleGasEstimation.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/MainModuleUpgradable.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/Implementation.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleAuth.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleAuthConvenience.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleAuthFixed.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleAuthUpgradable.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleCalls.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleCreator.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleERC165.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleERC5719.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleExtraAuth.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleHooks.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleIPFS.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleNonce.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleOnlyDelegatecall.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleSelfAuth.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleStorage.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/ModuleUpdate.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/gas-estimation/ModuleIgnoreAuthUpgradable.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/gas-estimation/ModuleIgnoreNonceCalls.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/interfaces/IModuleAuth.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/interfaces/IModuleAuthUpgradable.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/interfaces/IModuleCalls.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/interfaces/IModuleCreator.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/interfaces/IModuleHooks.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/interfaces/IModuleUpdate.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/submodules/auth/SequenceBaseSig.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/submodules/auth/SequenceChainedSig.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/submodules/auth/SequenceDynamicSig.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/submodules/auth/SequenceNoChainIdSig.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/commons/submodules/nonce/SubModuleNonce.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/utils/GasEstimator.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/utils/MultiCallUtils.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/utils/RequireUtils.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/modules/utils/SequenceUtils.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/trust/Trust.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/trust/TrustFactory.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/utils/LibAddress.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/utils/LibBytes.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/utils/LibBytesPointer.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/utils/LibOptim.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/utils/LibString.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/contracts/utils/SignatureValidator.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/book.css create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/book.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/solidity.min.js create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/SUMMARY.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/Factory.sol/contract.Factory.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/Wallet.sol/library.Wallet.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/hooks/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/hooks/WalletProxyHook.sol/contract.WalletProxyHook.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/hooks/interfaces/IWalletProxy.sol/interface.IWalletProxy.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/hooks/interfaces/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/IERC1271Wallet.sol/interface.IERC1271Wallet.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/receivers/IERC1155Receiver.sol/interface.IERC1155Receiver.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/receivers/IERC223Receiver.sol/interface.IERC223Receiver.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/receivers/IERC721Receiver.sol/interface.IERC721Receiver.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/receivers/IERC777Receiver.sol/interface.IERC777Receiver.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/receivers/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/tokens/IERC1155.sol/interface.IERC1155.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/tokens/IERC20.sol/interface.IERC20.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/tokens/IERC721.sol/interface.IERC721.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/interfaces/tokens/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/AlwaysRevertMock.sol/contract.AlwaysRevertMock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/CallReceiverMock.sol/contract.CallReceiverMock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/DelegateCallMock.sol/contract.DelegateCallMock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/ERC1155Mock.sol/contract.ERC1155Mock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/ERC165CheckerMock.sol/contract.ERC165CheckerMock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/ERC20Mock.sol/contract.ERC20Mock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/ERC721Mock.sol/contract.ERC721Mock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/GasBurnerMock.sol/contract.GasBurnerMock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/HookCallerMock.sol/contract.HookCallerMock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/HookMock.sol/contract.HookMock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/LibBytesImpl.sol/contract.LibBytesImpl.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/LibBytesPointerImpl.sol/contract.LibBytesPointerImpl.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/LibStringImp.sol/contract.LibStringImp.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/ModuleMock.sol/contract.ModuleMock.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/mocks/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/GuestModule.sol/contract.GuestModule.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/MainModule.sol/contract.MainModule.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/MainModuleGasEstimation.sol/contract.MainModuleGasEstimation.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/MainModuleUpgradable.sol/contract.MainModuleUpgradable.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/Implementation.sol/contract.Implementation.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleAuth.sol/abstract.ModuleAuth.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleAuthConvenience.sol/abstract.ModuleAuthConvenience.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleAuthFixed.sol/abstract.ModuleAuthFixed.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleAuthUpgradable.sol/abstract.ModuleAuthUpgradable.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleCalls.sol/abstract.ModuleCalls.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleCreator.sol/contract.ModuleCreator.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleERC165.sol/abstract.ModuleERC165.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleERC5719.sol/contract.ModuleERC5719.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleExtraAuth.sol/abstract.ModuleExtraAuth.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleHooks.sol/contract.ModuleHooks.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleIPFS.sol/contract.ModuleIPFS.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleNonce.sol/contract.ModuleNonce.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleOnlyDelegatecall.sol/contract.ModuleOnlyDelegatecall.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleSelfAuth.sol/contract.ModuleSelfAuth.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleStorage.sol/library.ModuleStorage.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/ModuleUpdate.sol/contract.ModuleUpdate.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/gas-estimation/ModuleIgnoreAuthUpgradable.sol/abstract.ModuleIgnoreAuthUpgradable.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/gas-estimation/ModuleIgnoreNonceCalls.sol/abstract.ModuleIgnoreNonceCalls.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/gas-estimation/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/interfaces/IModuleAuth.sol/abstract.IModuleAuth.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/interfaces/IModuleAuthUpgradable.sol/interface.IModuleAuthUpgradable.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/interfaces/IModuleCalls.sol/interface.IModuleCalls.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/interfaces/IModuleCreator.sol/interface.IModuleCreator.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/interfaces/IModuleHooks.sol/interface.IModuleHooks.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/interfaces/IModuleUpdate.sol/abstract.IModuleUpdate.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/interfaces/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/submodules/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/submodules/auth/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/submodules/auth/SequenceBaseSig.sol/library.SequenceBaseSig.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/submodules/auth/SequenceChainedSig.sol/abstract.SequenceChainedSig.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/submodules/auth/SequenceDynamicSig.sol/library.SequenceDynamicSig.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/submodules/auth/SequenceNoChainIdSig.sol/library.SequenceNoChainIdSig.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/submodules/nonce/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/commons/submodules/nonce/SubModuleNonce.sol/library.SubModuleNonce.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/utils/GasEstimator.sol/contract.GasEstimator.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/utils/MultiCallUtils.sol/contract.MultiCallUtils.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/utils/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/utils/RequireUtils.sol/contract.RequireUtils.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/modules/utils/SequenceUtils.sol/contract.SequenceUtils.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/trust/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/trust/Trust.sol/contract.Trust.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/trust/Trust.sol/function.absDiff.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/trust/TrustFactory.sol/contract.TrustFactory.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/utils/LibAddress.sol/library.LibAddress.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/utils/LibBytes.sol/library.LibBytes.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/utils/LibBytesPointer.sol/library.LibBytesPointer.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/utils/LibOptim.sol/library.LibOptim.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/utils/LibString.sol/library.LibString.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/utils/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/docs/src/contracts/utils/SignatureValidator.sol/library.SignatureValidator.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry.lock create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/base/AdvTest.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/hooks/WalletProxyHook.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/Implementation.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/ModuleCalls.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/ModuleERC5719.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/ModuleExtraAuth.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/ModuleIPFS.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/ModuleStorage.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/submodules/auth/SequenceBaseSig.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/submodules/auth/SequenceChainedSig.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/submodules/auth/SequenceDynamicSig.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/submodules/auth/SequenceNoChainIdSig.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/commons/submodules/nonce/SubModuleNonce.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/utils/L2CompressorEncoder.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/utils/L2CompressorHuff.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/utils/L2CompressorHuffReadExecute.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/utils/L2CompressorHuffReadFlag.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/utils/L2CompressorHuffReadNonce.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/utils/L2CompressorHuffReadTx.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/utils/L2CompressorHuffReadTxs.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/modules/utils/RequireUtils.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/trust/Trust.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/trust/TrustFactory.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/utils/LibAddress.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/utils/LibBytes.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/utils/LibBytesPointer.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/utils/LibOptim.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/foundry_test/utils/SignatureValidator.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/funding.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/hardhat.config.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/.github/workflows/ci.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/.github/workflows/sync.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/.gitmodules create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/LICENSE-APACHE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/LICENSE-MIT create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/foundry.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/lib/ds-test/.github/workflows/build.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/lib/ds-test/LICENSE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/lib/ds-test/Makefile create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/lib/ds-test/default.nix create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/lib/ds-test/demo/demo.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/lib/ds-test/package.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/lib/ds-test/src/test.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/lib/ds-test/src/test.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/package.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/Base.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/Script.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdAssertions.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdChains.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdCheats.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdError.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdInvariant.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdJson.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdMath.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdStorage.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdStyle.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/StdUtils.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/Test.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/Vm.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/console.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/console2.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/interfaces/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/interfaces/IERC165.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/interfaces/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/interfaces/IERC4626.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/interfaces/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/src/interfaces/IMulticall3.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/StdAssertions.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/StdChains.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/StdCheats.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/StdError.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/StdMath.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/StdStorage.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/StdStyle.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/StdUtils.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/compilation/CompilationScript.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/compilation/CompilationScriptBase.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/compilation/CompilationTest.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/compilation/CompilationTestBase.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/forge-std/test/fixtures/broadcast.log.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/.git-blame-ignore-revs create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/.github/workflows/tests.yaml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/.gitmodules create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/LICENSE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/Group 1.png create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/banner.jpg create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/black_white_huff-removebg-preview.png create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/black_white_huff.png create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/foundry.png create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/foundry_huff_banner.jpg create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/foundry_huff_banner.png create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/huff.png create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/inverted_huff.png create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/x-removebg-preview.png create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/assets/x.jpg create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/foundry.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/.github/workflows/ci.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/.github/workflows/sync.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/.gitmodules create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/LICENSE-APACHE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/LICENSE-MIT create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/foundry.toml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/lib/ds-test/.github/workflows/build.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/lib/ds-test/LICENSE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/lib/ds-test/Makefile create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/lib/ds-test/default.nix create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/lib/ds-test/demo/demo.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/lib/ds-test/package.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/lib/ds-test/src/test.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/lib/ds-test/src/test.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/package.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/Base.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/Script.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdAssertions.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdChains.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdCheats.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdError.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdInvariant.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdJson.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdMath.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdStorage.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdStyle.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/StdUtils.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/Test.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/Vm.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/console.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/console2.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/interfaces/IERC1155.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/interfaces/IERC165.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/interfaces/IERC20.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/interfaces/IERC4626.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/interfaces/IERC721.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/interfaces/IMulticall3.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/src/safeconsole.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/StdAssertions.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/StdChains.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/StdCheats.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/StdError.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/StdMath.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/StdStorage.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/StdStyle.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/StdUtils.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/compilation/CompilationScript.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/compilation/CompilationScriptBase.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/compilation/CompilationTest.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/compilation/CompilationTestBase.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/forge-std/test/fixtures/broadcast.log.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/foundry-huff/scripts/binary_check.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/foundry-huff/scripts/file_writer.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/foundry-huff/scripts/rand_bytes.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/foundry-huff/scripts/read_and_append.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/.gitattributes create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/.github/workflows/ci.yml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/.gitmodules create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/LICENSE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/Makefile create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/README create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/README.md create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/dappfile create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/lib/ds-test/LICENSE create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/lib/ds-test/Makefile create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/lib/ds-test/default.nix create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/lib/ds-test/demo/demo.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/lib/ds-test/src/test.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/src/strings.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/lib/solidity-stringutils/src/strings.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/remappings.txt create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/scripts/binary_check.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/scripts/file_writer.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/scripts/rand_bytes.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/scripts/read_and_append.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/HuffConfig.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/HuffDeployer.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/depreciated/StatefulDeployer.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/depreciated/StatefulDeployer.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/HuffConfig.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/HuffDeployer.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/Logging.t.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/contracts/ConstOverride.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/contracts/Constructor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/contracts/ConstructorNeedsValue.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/contracts/EVMVersionCheck.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/contracts/LotsOfLogging.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/contracts/NoConstructor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/contracts/Number.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/contracts/RememberCreator.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/interfaces/IConstructor.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/interfaces/INumber.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/lib/foundry-huff/src/test/interfaces/IRememberCreator.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/arbitrum.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/arbitrumGoerli.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/arbitrumNova.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/avalanche.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/avalancheFuji.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/bnb.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/bnbTestnet.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/gnosis.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/goerli.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/hardhat.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/mainnet.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/mumbai.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/optimism.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/polygon.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/networks/polygonZkevm.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/package-lock.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/package.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/pnpm-lock.yaml create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/remappings.txt create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/run_huff_tests.sh create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/soldeer.lock create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/Errors.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/L2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/L2CompressorLib.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__csfnkjajkqagwbukjfojatjegwtjmxhdL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__cvwewezvvfcuoxtlisicumgcpzqhgavjL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__cvyihdgegfauooldcwyvlordhurwepivL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__dpdvzwushkgzisywrbnkhcrodypkvbvyL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__ecomtgzdrtxibkzevmerruajxffksduvL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__iacuotklnugcaogzpvcdfhvolsvzerleL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__jcmwhtnovsnvnyyhadlushmcopgxtlcwL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__jdfvnyqsqtnysjlyfovitokyykvxttnoL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__jyyzvebufexucahwkkbkfdcmuiabvbfeL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__kbisarygvfqaqtuxwpgejdnyqkualpwlL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__lszmdtforkxonmlujouewhlrvjjrmurcL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__mgltnswskwojupcsygtzovabokiotusaL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__mzwfsngwzygdglttftvfnbgkuxhmtqwiL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__nxqmasuaqrahclmuoszdisbtgmuppjvfL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__pgmagdckbktfjhzdpajytwhcpkhlmscqL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__pwbfbdaeweytjiloryhjilsqguaprwtwL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__pxqvpgsonlpbmziexnicojtlgsgfouyrL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__qcagqkszwlsndhjyagmukhxiugwadpxxL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__qymdioekngjvjjjqfpzmxlycnvbslwevL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__rpzdmcdnijxlttsivcfgzleurbgpchhzL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__rulclzzfmyszewnycpzlqmcopsqccarqL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__uexsnlcvbjyubdfmmdxzehdfcdiwufcrL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__ughbfnqmhbdbiuueqxutuvcxicgrmnxwL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__uzsvtiqnxxrhkhfelgdrwmnbwwirribaL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__vffsqkjyicdnumnesfhcjlgtqguwdpnpL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__vtbpzpccsyytsvygxsyhvyojjbqsghpbL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__xhuychsyvcmrjzhibkdksklxflvtfefdL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__ylxrpbrfcykqjxgggzptgdvokhmzcvamL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/__TEMP__zkzjzrglozoqyyfvpcdqeanjxglmtzbbL2Compressor.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/L2CompressorImps.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/L2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/L2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/L2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/L2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/L2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__aayxscdgqfgqckqcfvplniuqleqmglgpL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__agjsoduxivwxbunbrskpnttujfxlnsuqL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__aozfhsjnhfgdhkvygupcezykxnllxhooL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__auoqqxuvhsugblxkhmuynutuutdpetfmL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__bgiysfzlfrqnqopegdijkxkzjebeugweL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__bhcstacsnwaeevimfyeuyukrpkefkpjvL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__bjodacjzehrzeexubslwqheqqdtpojdxL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__bqzqrnjvdcsevkfpbluzvxgnvfnbjbvdL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__bxxjqefyxrrctwjijuvxauktfiveuhazL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__cczbajsfeehqceablvdedlohtwcpgruiL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__ceqwybpkmvqejmujxdddmmbuqrzcvpqlL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__cinzltxsvuofyvnmuiiddtfwsyytzwszL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__ckntqzzuqphqsecwtcwuiczrfadymzpwL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__ctxqvslrdaagplfabzhyfjejoyhqoxvnL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__cxyftrlslztbljuqlicrbibyyssybguhL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__cyxkjqcsonpojbyksbsvglcrgoqbepkaL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__deeivxbssjzviqkksdozrardbmkofvggL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__dexeqmbbqijqudvjnczxedpzkrqkrxgpL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__ebpduriemctxazssijauhsotgyqtayygL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__edfwesooemxojadwrkmombuemzhicuirL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__eicqyirjstlxuotvptvgszvdeemgpomnL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__eiovqvzojbuhtglrinqvkqirnywzkmmmL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__elmwrfrphdzesjosulrygitvkhredhzhL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__eqatcxksiizfyaalvdegpzaqhuqqhiasL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__etsleznlprfdbomdvycqormkvpykanxnL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__ezolvnoridtqgdaoucihkxihmonvdzfxL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__fjxkkzopmkrdujlfcvecckcbuhhhpvpfL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__fnqywxkjihwbqdurhcftzbzzrxvcmizbL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__fophszhswirxqqcvcwxtmeqbuwldxifmL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__fvrcmlbliwfwkjwbyxrediuokmghmftbL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__fzlawzzpwhflvpxtrdpemqiejqiknsfiL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__gllqgrsektwpyxkfoughzygdwrhxuzibL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__gmuxapiaoacldjbqqarixmvtxkffgwchL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__gnbrxkaxjdmimbyepztqmujkoilcaudgL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__grtgxrlusabnzjycyuqniujobcpgjuysL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__guymonsuywmugusjixyvzwfzordmogvuL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__gvmiqpcdvchroaxhcnyygzhjrvozrogmL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__hahsuwlfvumxsoepdhwqsahoduwcqrrpL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__hbzqhssjozyiqufyamizmyrwtgelqlacL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__hfunbeczbrqifdmdhdecpajexqefympmL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__htawaymiowrkxyrnxkesysithbtrbvqqL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__hwddxmqkngntwieovwlvozjzwlctwrzrL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__iepjlcxcjkjplwmrexbaruoucdulzvqvL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__igcaczanzcfoaizsmahiisgmgdjzfjxqL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__irlchgckchrpalaqwygxkipfvshmgystL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__ixvgjbblpufenoaperstvmceseoukqtcL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__jehdemzdnzfibjlvuclsezveuuexubagL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__jgpbsiwsniszmajdqepfceqpqhdfzbiiL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__jowmzjwgdpwndoapfucdlmsxiuwbhxrsL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__jujqtnlfowbvuxcpetgqmymkladcanwtL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__jvyndfqmdiikuahllmivwlrgrfhmdzslL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__kqlasykhhfirrrmebqsqsnoleofcgyciL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__krffnjyglejvywvaqrlwcoejtxsysubcL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__lcamxsrkjvabmusmojsaspukxeydggrhL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__lhhzpskainxqfqaqlpehxidlrhdvkcoyL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__lqfhpuhoyykhenkubqyenjlfkzsbjkxcL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__lvxixehkgptearwmufefnibyzayqaaciL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__lyxapewhygradusuogqwefdorgsntvcxL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__mfzlovayqwfmitftdbihfqfuruuvbxqbL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__mimqucgooefgrgmedlqkqqhszmvuuxioL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__mlmvtiltzqbjdwbouzrwkvkhpzncpjzsL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__mscgrvpcuajxspktkuiktwofhzwbszraL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__mscqnapvfrrvforntvdsgkeofzbhbhdsL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__nikafqmlyyfgnksobnextlfkyykzrdomL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__nmkhnrsondojimozdduewzkbcwlvsakcL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__nwvyiqgthdyhbeqefkhccrhufdhjjazeL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__obfmnrpwkqhjnhtlarvljeqysguhoizaL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__osezdxhuqmvwkwrmogebamkqmhdvljmlL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__pabiechkuxsxbofnkwdnlbnftvkuearfL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__paowmqtaesjuestckycmjquytqlfsdxoL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__pbsqieaiicgyhrvarrpmypzdufaanmpuL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__pdhdqmtavohyssvasdtggpxqllpajsfgL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__petemgomrtnkvriaaltgrrlpmnzupwinL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__prsagizbgnvlqmbynuotwqkanwdlaqwpL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__pwmrquntendpgjgzyejxobzrgvpizgfdL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__qcnbdgafrzidybihlkysfiliofczzxkhL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__qmgpovlxptxlyeikmnrgvhavunciniubL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__qotscdrriksxilvopyxfsbdteqegnyogL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__qpcuafrrwylbrmzpjfotpjfuqxtnqrznL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__qrusinhmosfcvmayqquwflixlquuhfxhL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__raqvmbdtosqccjiitanbofqxpeepnlpnL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__ruqfgeoaamevndekencowviukvlcrylcL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__sccswirfbmatrvwbvymwfzttbygcbfmqL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__sgfhxhvokcpqraldrihrcqjmlolnoulkL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__smnsryuyuhbbunwequckjtrcwgaqmtqrL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__stxwzpcueqdwmcsxvxwzkubprcmgpexkL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__syiwghttzfntbuucvfvlctpjeldqbrshL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__tadxtxolhcweyyokjpleorchotchzsecL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__tlpzvbklzufubgnwcvynodibgfqnidibL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__uobsemubvemlejzpjvdzyjcjrsbhplpsL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__uudbvubkefushlwgxsfgldujuphcmuueL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__uyprsxxzmvxmqqzffpwypehvvksjzbvnL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__vmmtlehczdflhfwnklqlujjtzpxrejpmL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__voqybenghboelsmsisgwnqqymvpjvskcL2CompressorReadFlag.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__vtdvfmunyolkgcpjpzhtgjgwawtaakhwL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__vtynudukibsyoxexvoiaurnusvgnrhusL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__vvictwvevvjztwupwcjsnyherfswgsgcL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__vzqupagxqpkbexuaqyoulenyhrwfuvsiL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__wcgjgrxjyjenenrnolprlkfulinnjttkL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__weeyfqxkqtokrkcwbrxerdftkvyxqwwiL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__xbuxrypubsjveaucjyhkmwlwmhlgvymnL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__xeqwdqdlujruiiyywivxsxrlsluymfdiL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__xprohfjolmgvmuxskobwcqavoskmutltL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__yhfcljsbzcghwkonkegmtbremlhrxnejL2CompressorReadExecute.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__yphredkigvzhxnjzbspndmkchcjaofllL2CompressorReadNonce.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__zfeyxnkrahdyldfbtyakjkbvehkjdbmgL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__zmblbdmtdrpbbopekynjthxzellyhsxeL2CompressorReadTxs.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/src/imps/__TEMP__zvcrygcudiscauggwhjnxbrgifehtxhfL2CompressorReadTx.huff create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/ChainedSignatures.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/ERC165.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/Factory.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/GasEstimation.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/GuestModule.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/LibBytes.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/LibString.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/MainModule.bench.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/MainModule.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/MerkleSignatures.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/MultiCallUtils.spec.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/utils/contracts.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/utils/imposter.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/utils/index.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/utils/sequence.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/test/utils/wallet.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/tsconfig.json create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/typings/chai-bignumber.d.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/typings/chai-bn.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/typings/truffle.d.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/utils/JsonBindings.sol create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/utils/benchmarker.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/utils/config-loader.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/utils/deploy-contracts.ts create mode 100644 packages/sequence-core-1.0.0/wallet-contracts/utils/workers/bench-worker.ts create mode 100644 packages/test/package.json create mode 100644 packages/test/src/chains.ts create mode 100644 packages/test/src/clients.ts create mode 100644 packages/test/src/config.ts create mode 100644 packages/test/src/constants.ts create mode 100644 packages/test/src/exports/index.test-d.ts create mode 100644 packages/test/src/exports/index.test.ts create mode 100644 packages/test/src/exports/index.ts create mode 100644 packages/test/src/exports/react.ts create mode 100644 packages/test/src/exports/vue.ts create mode 100644 packages/test/src/globalSetup.ts create mode 100644 packages/test/src/regex.ts create mode 100644 packages/test/src/setup.ts create mode 100644 packages/test/src/utils.ts create mode 100644 packages/test/tsconfig.build.json create mode 100644 packages/test/tsconfig.json create mode 100644 packages/vue/CHANGELOG.md create mode 100644 packages/vue/README.md create mode 100644 packages/vue/package.json create mode 100644 packages/vue/src/composables/useAccount.test-d.ts create mode 100644 packages/vue/src/composables/useAccount.test.ts create mode 100644 packages/vue/src/composables/useAccount.ts create mode 100644 packages/vue/src/composables/useAccountEffect.test.ts create mode 100644 packages/vue/src/composables/useAccountEffect.ts create mode 100644 packages/vue/src/composables/useBalance.test-d.ts create mode 100644 packages/vue/src/composables/useBalance.test.ts create mode 100644 packages/vue/src/composables/useBalance.ts create mode 100644 packages/vue/src/composables/useBlockNumber.test-d.ts create mode 100644 packages/vue/src/composables/useBlockNumber.test.ts create mode 100644 packages/vue/src/composables/useBlockNumber.ts create mode 100644 packages/vue/src/composables/useBytecode.test-d.ts create mode 100644 packages/vue/src/composables/useBytecode.test.ts create mode 100644 packages/vue/src/composables/useBytecode.ts create mode 100644 packages/vue/src/composables/useChainId.test-d.ts create mode 100644 packages/vue/src/composables/useChainId.test.ts create mode 100644 packages/vue/src/composables/useChainId.ts create mode 100644 packages/vue/src/composables/useChains.test.ts create mode 100644 packages/vue/src/composables/useChains.ts create mode 100644 packages/vue/src/composables/useClient.test-d.ts create mode 100644 packages/vue/src/composables/useClient.test.ts create mode 100644 packages/vue/src/composables/useClient.ts create mode 100644 packages/vue/src/composables/useConfig.test-d.ts create mode 100644 packages/vue/src/composables/useConfig.test.ts create mode 100644 packages/vue/src/composables/useConfig.ts create mode 100644 packages/vue/src/composables/useConnect.test-d.ts create mode 100644 packages/vue/src/composables/useConnect.test.ts create mode 100644 packages/vue/src/composables/useConnect.ts create mode 100644 packages/vue/src/composables/useConnections.test.ts create mode 100644 packages/vue/src/composables/useConnections.ts create mode 100644 packages/vue/src/composables/useConnectorClient.test-d.ts create mode 100644 packages/vue/src/composables/useConnectorClient.test.ts create mode 100644 packages/vue/src/composables/useConnectorClient.ts create mode 100644 packages/vue/src/composables/useConnectors.test.ts create mode 100644 packages/vue/src/composables/useConnectors.ts create mode 100644 packages/vue/src/composables/useDisconnect.test-d.ts create mode 100644 packages/vue/src/composables/useDisconnect.test.ts create mode 100644 packages/vue/src/composables/useDisconnect.ts create mode 100644 packages/vue/src/composables/useEnsAddress.test.ts create mode 100644 packages/vue/src/composables/useEnsAddress.ts create mode 100644 packages/vue/src/composables/useEnsAvatar.test.ts create mode 100644 packages/vue/src/composables/useEnsAvatar.ts create mode 100644 packages/vue/src/composables/useEnsName.test.ts create mode 100644 packages/vue/src/composables/useEnsName.ts create mode 100644 packages/vue/src/composables/useEstimateGas.test-d.ts create mode 100644 packages/vue/src/composables/useEstimateGas.test.ts create mode 100644 packages/vue/src/composables/useEstimateGas.ts create mode 100644 packages/vue/src/composables/useReadContract.test-d.ts create mode 100644 packages/vue/src/composables/useReadContract.test.ts create mode 100644 packages/vue/src/composables/useReadContract.ts create mode 100644 packages/vue/src/composables/useReconnect.test-d.ts create mode 100644 packages/vue/src/composables/useReconnect.test.ts create mode 100644 packages/vue/src/composables/useReconnect.ts create mode 100644 packages/vue/src/composables/useSendTransaction.test-d.ts create mode 100644 packages/vue/src/composables/useSendTransaction.test.ts create mode 100644 packages/vue/src/composables/useSendTransaction.ts create mode 100644 packages/vue/src/composables/useSignMessage.test-d.ts create mode 100644 packages/vue/src/composables/useSignMessage.test.ts create mode 100644 packages/vue/src/composables/useSignMessage.ts create mode 100644 packages/vue/src/composables/useSignTypedData.test-d.ts create mode 100644 packages/vue/src/composables/useSignTypedData.test.ts create mode 100644 packages/vue/src/composables/useSignTypedData.ts create mode 100644 packages/vue/src/composables/useSimulateContract.test-d.ts create mode 100644 packages/vue/src/composables/useSimulateContract.test.ts create mode 100644 packages/vue/src/composables/useSimulateContract.ts create mode 100644 packages/vue/src/composables/useSwitchAccount.test-d.ts create mode 100644 packages/vue/src/composables/useSwitchAccount.test.ts create mode 100644 packages/vue/src/composables/useSwitchAccount.ts create mode 100644 packages/vue/src/composables/useSwitchChain.test-d.ts create mode 100644 packages/vue/src/composables/useSwitchChain.test.ts create mode 100644 packages/vue/src/composables/useSwitchChain.ts create mode 100644 packages/vue/src/composables/useTransaction.test-d.ts create mode 100644 packages/vue/src/composables/useTransaction.test.ts create mode 100644 packages/vue/src/composables/useTransaction.ts create mode 100644 packages/vue/src/composables/useTransactionReceipt.test-d.ts create mode 100644 packages/vue/src/composables/useTransactionReceipt.test.ts create mode 100644 packages/vue/src/composables/useTransactionReceipt.ts create mode 100644 packages/vue/src/composables/useWaitForTransactionReceipt.test-d.ts create mode 100644 packages/vue/src/composables/useWaitForTransactionReceipt.test.ts create mode 100644 packages/vue/src/composables/useWaitForTransactionReceipt.ts create mode 100644 packages/vue/src/composables/useWatchBlockNumber.test-d.ts create mode 100644 packages/vue/src/composables/useWatchBlockNumber.test.ts create mode 100644 packages/vue/src/composables/useWatchBlockNumber.ts create mode 100644 packages/vue/src/composables/useWatchContractEvent.test-d.ts create mode 100644 packages/vue/src/composables/useWatchContractEvent.test.ts create mode 100644 packages/vue/src/composables/useWatchContractEvent.ts create mode 100644 packages/vue/src/composables/useWriteContract.test-d.ts create mode 100644 packages/vue/src/composables/useWriteContract.test.ts create mode 100644 packages/vue/src/composables/useWriteContract.ts create mode 100644 packages/vue/src/errors/base.test.ts create mode 100644 packages/vue/src/errors/base.ts create mode 100644 packages/vue/src/errors/plugin.test.ts create mode 100644 packages/vue/src/errors/plugin.ts create mode 100644 packages/vue/src/exports/actions.test.ts create mode 100644 packages/vue/src/exports/actions.ts create mode 100644 packages/vue/src/exports/actions/experimental.test.ts create mode 100644 packages/vue/src/exports/actions/experimental.ts create mode 100644 packages/vue/src/exports/chains.ts create mode 100644 packages/vue/src/exports/connectors.test.ts create mode 100644 packages/vue/src/exports/connectors.ts create mode 100644 packages/vue/src/exports/index.test.ts create mode 100644 packages/vue/src/exports/index.ts create mode 100644 packages/vue/src/exports/nuxt.test.ts create mode 100644 packages/vue/src/exports/nuxt.ts create mode 100644 packages/vue/src/exports/query.test.ts create mode 100644 packages/vue/src/exports/query.ts create mode 100644 packages/vue/src/nuxt/module.ts create mode 100644 packages/vue/src/nuxt/runtime/composables.ts create mode 100644 packages/vue/src/plugin.ts create mode 100644 packages/vue/src/types/properties.ts create mode 100644 packages/vue/src/types/ref.ts create mode 100644 packages/vue/src/utils/cloneDeep.ts create mode 100644 packages/vue/src/utils/getVersion.test.ts create mode 100644 packages/vue/src/utils/getVersion.ts create mode 100644 packages/vue/src/utils/query.ts create mode 100644 packages/vue/src/utils/updateState.ts create mode 100644 packages/vue/src/version.ts create mode 100644 packages/vue/test/setup.ts create mode 100644 packages/vue/tsconfig.build.json create mode 100644 packages/vue/tsconfig.json create mode 100644 playgrounds/next/.gitignore create mode 100644 playgrounds/next/next.config.mjs create mode 100644 playgrounds/next/package.json create mode 100644 playgrounds/next/src/app/contracts.ts create mode 100644 playgrounds/next/src/app/globals.css create mode 100644 playgrounds/next/src/app/layout.tsx create mode 100644 playgrounds/next/src/app/page.tsx create mode 100644 playgrounds/next/src/app/providers.tsx create mode 100644 playgrounds/next/src/wagmi.ts create mode 100644 playgrounds/next/tsconfig.json create mode 100644 playgrounds/nuxt/.gitignore create mode 100644 playgrounds/nuxt/app.vue create mode 100644 playgrounds/nuxt/components/Account.vue create mode 100644 playgrounds/nuxt/components/Connect.vue create mode 100644 playgrounds/nuxt/nuxt.config.ts create mode 100644 playgrounds/nuxt/package.json create mode 100644 playgrounds/nuxt/plugins/wagmi.ts create mode 100644 playgrounds/nuxt/public/favicon.ico create mode 100644 playgrounds/nuxt/server/tsconfig.json create mode 100644 playgrounds/nuxt/tsconfig.json create mode 100644 playgrounds/nuxt/wagmi.ts create mode 100644 playgrounds/vite-core/.gitignore create mode 100644 playgrounds/vite-core/index.html create mode 100644 playgrounds/vite-core/package.json create mode 100644 playgrounds/vite-core/src/App.tsx create mode 100644 playgrounds/vite-core/src/index.css create mode 100644 playgrounds/vite-core/src/main.tsx create mode 100644 playgrounds/vite-core/src/vite-env.d.ts create mode 100644 playgrounds/vite-core/src/wagmi.ts create mode 100644 playgrounds/vite-core/tsconfig.json create mode 100644 playgrounds/vite-core/tsconfig.node.json create mode 100644 playgrounds/vite-core/vite.config.ts create mode 100644 playgrounds/vite-react/.gitignore create mode 100644 playgrounds/vite-react/index.html create mode 100644 playgrounds/vite-react/package.json create mode 100644 playgrounds/vite-react/public/manifest.json create mode 100644 playgrounds/vite-react/src/App.tsx create mode 100644 playgrounds/vite-react/src/contracts.ts create mode 100644 playgrounds/vite-react/src/index.css create mode 100644 playgrounds/vite-react/src/main.tsx create mode 100644 playgrounds/vite-react/src/vite-env.d.ts create mode 100644 playgrounds/vite-react/src/wagmi.ts create mode 100644 playgrounds/vite-react/tsconfig.json create mode 100644 playgrounds/vite-react/tsconfig.node.json create mode 100644 playgrounds/vite-react/vite.config.ts create mode 100644 playgrounds/vite-react/wagmi.config.ts create mode 100644 playgrounds/vite-vue/.gitignore create mode 100644 playgrounds/vite-vue/index.html create mode 100644 playgrounds/vite-vue/package.json create mode 100644 playgrounds/vite-vue/public/vite.svg create mode 100644 playgrounds/vite-vue/src/App.vue create mode 100644 playgrounds/vite-vue/src/components/Account.vue create mode 100644 playgrounds/vite-vue/src/components/Balance.vue create mode 100644 playgrounds/vite-vue/src/components/BlockNumber.vue create mode 100644 playgrounds/vite-vue/src/components/Client.vue create mode 100644 playgrounds/vite-vue/src/components/Connect.vue create mode 100644 playgrounds/vite-vue/src/components/Connections.vue create mode 100644 playgrounds/vite-vue/src/components/ConnectorClient.vue create mode 100644 playgrounds/vite-vue/src/components/ReadContract.vue create mode 100644 playgrounds/vite-vue/src/components/SendTransaction.vue create mode 100644 playgrounds/vite-vue/src/components/SwitchAccount.vue create mode 100644 playgrounds/vite-vue/src/components/SwitchChain.vue create mode 100644 playgrounds/vite-vue/src/components/WriteContract.vue create mode 100644 playgrounds/vite-vue/src/contracts.ts create mode 100644 playgrounds/vite-vue/src/main.ts create mode 100644 playgrounds/vite-vue/src/style.css create mode 100644 playgrounds/vite-vue/src/vite-env.d.ts create mode 100644 playgrounds/vite-vue/src/wagmi.ts create mode 100644 playgrounds/vite-vue/tsconfig.json create mode 100644 playgrounds/vite-vue/tsconfig.node.json create mode 100644 playgrounds/vite-vue/vite.config.ts create mode 100644 scripts/formatPackageJson.ts create mode 100644 scripts/generateProxyPackages.ts create mode 100644 scripts/preconstruct.ts create mode 100644 scripts/restorePackageJson.ts create mode 100644 scripts/updateBlockExplorerPluginChains.ts create mode 100644 scripts/updateVersion.ts create mode 100644 scripts/updateViemVersion.ts create mode 100644 site/.vitepress/config.ts create mode 100644 site/.vitepress/constants.ts create mode 100644 site/.vitepress/sidebar.ts create mode 100644 site/.vitepress/theme/components/AsideSponsors.vue create mode 100644 site/.vitepress/theme/components/Banner.vue create mode 100644 site/.vitepress/theme/components/HomeBanner.vue create mode 100644 site/.vitepress/theme/components/HomePage.vue create mode 100644 site/.vitepress/theme/composables/useSponsors.ts create mode 100644 site/.vitepress/theme/index.ts create mode 100644 site/.vitepress/theme/style.css create mode 100644 site/cli/api/commands.md create mode 100644 site/cli/api/commands/generate.md create mode 100644 site/cli/api/commands/init.md create mode 100644 site/cli/api/plugins.md create mode 100644 site/cli/api/plugins/actions.md create mode 100644 site/cli/api/plugins/blockExplorer.md create mode 100644 site/cli/api/plugins/etherscan.md create mode 100644 site/cli/api/plugins/fetch.md create mode 100644 site/cli/api/plugins/foundry.md create mode 100644 site/cli/api/plugins/hardhat.md create mode 100644 site/cli/api/plugins/react.md create mode 100644 site/cli/api/plugins/sourcify.md create mode 100644 site/cli/config/configuring-cli.md create mode 100644 site/cli/config/options.md create mode 100644 site/cli/create-wagmi.md create mode 100644 site/cli/getting-started.md create mode 100644 site/cli/guides/migrate-from-v1-to-v2.md create mode 100644 site/cli/installation.md create mode 100644 site/cli/why.md create mode 100644 site/components/Browsers.vue create mode 100644 site/components/SearchChains.vue create mode 100644 site/core/api/actions.md create mode 100644 site/core/api/actions/call.md create mode 100644 site/core/api/actions/connect.md create mode 100644 site/core/api/actions/deployContract.md create mode 100644 site/core/api/actions/disconnect.md create mode 100644 site/core/api/actions/estimateFeesPerGas.md create mode 100644 site/core/api/actions/estimateGas.md create mode 100644 site/core/api/actions/estimateMaxPriorityFeePerGas.md create mode 100644 site/core/api/actions/getAccount.md create mode 100644 site/core/api/actions/getBalance.md create mode 100644 site/core/api/actions/getBlock.md create mode 100644 site/core/api/actions/getBlockNumber.md create mode 100644 site/core/api/actions/getBlockTransactionCount.md create mode 100644 site/core/api/actions/getBytecode.md create mode 100644 site/core/api/actions/getCallsStatus.md create mode 100644 site/core/api/actions/getCapabilities.md create mode 100644 site/core/api/actions/getChainId.md create mode 100644 site/core/api/actions/getChains.md create mode 100644 site/core/api/actions/getClient.md create mode 100644 site/core/api/actions/getConnections.md create mode 100644 site/core/api/actions/getConnectorClient.md create mode 100644 site/core/api/actions/getConnectors.md create mode 100644 site/core/api/actions/getEnsAddress.md create mode 100644 site/core/api/actions/getEnsAvatar.md create mode 100644 site/core/api/actions/getEnsName.md create mode 100644 site/core/api/actions/getEnsResolver.md create mode 100644 site/core/api/actions/getEnsText.md create mode 100644 site/core/api/actions/getFeeHistory.md create mode 100644 site/core/api/actions/getGasPrice.md create mode 100644 site/core/api/actions/getProof.md create mode 100644 site/core/api/actions/getPublicClient.md create mode 100644 site/core/api/actions/getStorageAt.md create mode 100644 site/core/api/actions/getToken.md create mode 100644 site/core/api/actions/getTransaction.md create mode 100644 site/core/api/actions/getTransactionConfirmations.md create mode 100644 site/core/api/actions/getTransactionCount.md create mode 100644 site/core/api/actions/getTransactionReceipt.md create mode 100644 site/core/api/actions/getWalletClient.md create mode 100644 site/core/api/actions/multicall.md create mode 100644 site/core/api/actions/prepareTransactionRequest.md create mode 100644 site/core/api/actions/readContract.md create mode 100644 site/core/api/actions/readContracts.md create mode 100644 site/core/api/actions/reconnect.md create mode 100644 site/core/api/actions/sendCalls.md create mode 100644 site/core/api/actions/sendTransaction.md create mode 100644 site/core/api/actions/showCallsStatus.md create mode 100644 site/core/api/actions/signMessage.md create mode 100644 site/core/api/actions/signTypedData.md create mode 100644 site/core/api/actions/simulateContract.md create mode 100644 site/core/api/actions/switchAccount.md create mode 100644 site/core/api/actions/switchChain.md create mode 100644 site/core/api/actions/verifyMessage.md create mode 100644 site/core/api/actions/verifyTypedData.md create mode 100644 site/core/api/actions/waitForCallsStatus.md create mode 100644 site/core/api/actions/waitForTransactionReceipt.md create mode 100644 site/core/api/actions/watchAccount.md create mode 100644 site/core/api/actions/watchAsset.md create mode 100644 site/core/api/actions/watchBlockNumber.md create mode 100644 site/core/api/actions/watchBlocks.md create mode 100644 site/core/api/actions/watchChainId.md create mode 100644 site/core/api/actions/watchClient.md create mode 100644 site/core/api/actions/watchConnections.md create mode 100644 site/core/api/actions/watchConnectors.md create mode 100644 site/core/api/actions/watchContractEvent.md create mode 100644 site/core/api/actions/watchPendingTransactions.md create mode 100644 site/core/api/actions/watchPublicClient.md create mode 100644 site/core/api/actions/writeContract.md create mode 100644 site/core/api/actions/writeContracts.md create mode 100644 site/core/api/chains.md create mode 100644 site/core/api/connectors.md create mode 100644 site/core/api/connectors/coinbaseWallet.md create mode 100644 site/core/api/connectors/injected.md create mode 100644 site/core/api/connectors/metaMask.md create mode 100644 site/core/api/connectors/mock.md create mode 100644 site/core/api/connectors/safe.md create mode 100644 site/core/api/connectors/walletConnect.md create mode 100644 site/core/api/createConfig.md create mode 100644 site/core/api/createConnector.md create mode 100644 site/core/api/createStorage.md create mode 100644 site/core/api/errors.md create mode 100644 site/core/api/transports.md create mode 100644 site/core/api/transports/custom.md create mode 100644 site/core/api/transports/fallback.md create mode 100644 site/core/api/transports/http.md create mode 100644 site/core/api/transports/unstable_connector.md create mode 100644 site/core/api/transports/webSocket.md create mode 100644 site/core/api/utilities/cookieToInitialState.md create mode 100644 site/core/api/utilities/deserialize.md create mode 100644 site/core/api/utilities/normalizeChainId.md create mode 100644 site/core/api/utilities/serialize.md create mode 100644 site/core/getting-started.md create mode 100644 site/core/guides/chain-properties.md create mode 100644 site/core/guides/error-handling.md create mode 100644 site/core/guides/ethers.md create mode 100644 site/core/guides/faq.md create mode 100644 site/core/guides/framework-adapters.md create mode 100644 site/core/guides/migrate-from-v1-to-v2.md create mode 100644 site/core/guides/testing.md create mode 100644 site/core/guides/viem.md create mode 100644 site/core/installation.md create mode 100644 site/core/typescript.md create mode 100644 site/core/why.md create mode 100644 site/dev/contributing.md create mode 100644 site/dev/creating-connectors.md create mode 100644 site/index.md create mode 100644 site/package.json create mode 100644 site/public/browsers/chrome.png create mode 100644 site/public/browsers/edge.png create mode 100644 site/public/browsers/firefox.png create mode 100644 site/public/browsers/opera.png create mode 100644 site/public/browsers/safari.png create mode 100644 site/public/favicon.svg create mode 100644 site/public/logo-dark.svg create mode 100644 site/public/logo-light.svg create mode 100644 site/public/og.png create mode 100644 site/react/api/WagmiProvider.md create mode 100644 site/react/api/actions.md create mode 100644 site/react/api/chains.md create mode 100644 site/react/api/connectors.md create mode 100644 site/react/api/connectors/coinbaseWallet.md create mode 100644 site/react/api/connectors/injected.md create mode 100644 site/react/api/connectors/metaMask.md create mode 100644 site/react/api/connectors/mock.md create mode 100644 site/react/api/connectors/safe.md create mode 100644 site/react/api/connectors/walletConnect.md create mode 100644 site/react/api/createConfig.md create mode 100644 site/react/api/createStorage.md create mode 100644 site/react/api/errors.md create mode 100644 site/react/api/hooks.md create mode 100644 site/react/api/hooks/useAccount.md create mode 100644 site/react/api/hooks/useAccountEffect.md create mode 100644 site/react/api/hooks/useBalance.md create mode 100644 site/react/api/hooks/useBlock.md create mode 100644 site/react/api/hooks/useBlockNumber.md create mode 100644 site/react/api/hooks/useBlockTransactionCount.md create mode 100644 site/react/api/hooks/useBytecode.md create mode 100644 site/react/api/hooks/useCall.md create mode 100644 site/react/api/hooks/useCallsStatus.md create mode 100644 site/react/api/hooks/useCapabilities.md create mode 100644 site/react/api/hooks/useChainId.md create mode 100644 site/react/api/hooks/useChains.md create mode 100644 site/react/api/hooks/useClient.md create mode 100644 site/react/api/hooks/useConfig.md create mode 100644 site/react/api/hooks/useConnect.md create mode 100644 site/react/api/hooks/useConnections.md create mode 100644 site/react/api/hooks/useConnectorClient.md create mode 100644 site/react/api/hooks/useConnectors.md create mode 100644 site/react/api/hooks/useDeployContract.md create mode 100644 site/react/api/hooks/useDisconnect.md create mode 100644 site/react/api/hooks/useEnsAddress.md create mode 100644 site/react/api/hooks/useEnsAvatar.md create mode 100644 site/react/api/hooks/useEnsName.md create mode 100644 site/react/api/hooks/useEnsResolver.md create mode 100644 site/react/api/hooks/useEnsText.md create mode 100644 site/react/api/hooks/useEstimateFeesPerGas.md create mode 100644 site/react/api/hooks/useEstimateGas.md create mode 100644 site/react/api/hooks/useEstimateMaxPriorityFeePerGas.md create mode 100644 site/react/api/hooks/useFeeHistory.md create mode 100644 site/react/api/hooks/useGasPrice.md create mode 100644 site/react/api/hooks/useInfiniteReadContracts.md create mode 100644 site/react/api/hooks/usePrepareTransactionRequest.md create mode 100644 site/react/api/hooks/useProof.md create mode 100644 site/react/api/hooks/usePublicClient.md create mode 100644 site/react/api/hooks/useReadContract.md create mode 100644 site/react/api/hooks/useReadContracts.md create mode 100644 site/react/api/hooks/useReconnect.md create mode 100644 site/react/api/hooks/useSendCalls.md create mode 100644 site/react/api/hooks/useSendTransaction.md create mode 100644 site/react/api/hooks/useShowCallsStatus.md create mode 100644 site/react/api/hooks/useSignMessage.md create mode 100644 site/react/api/hooks/useSignTypedData.md create mode 100644 site/react/api/hooks/useSimulateContract.md create mode 100644 site/react/api/hooks/useStorageAt.md create mode 100644 site/react/api/hooks/useSwitchAccount.md create mode 100644 site/react/api/hooks/useSwitchChain.md create mode 100644 site/react/api/hooks/useToken.md create mode 100644 site/react/api/hooks/useTransaction.md create mode 100644 site/react/api/hooks/useTransactionConfirmations.md create mode 100644 site/react/api/hooks/useTransactionCount.md create mode 100644 site/react/api/hooks/useTransactionReceipt.md create mode 100644 site/react/api/hooks/useVerifyMessage.md create mode 100644 site/react/api/hooks/useVerifyTypedData.md create mode 100644 site/react/api/hooks/useWaitForCallsStatus.md create mode 100644 site/react/api/hooks/useWaitForTransactionReceipt.md create mode 100644 site/react/api/hooks/useWalletClient.md create mode 100644 site/react/api/hooks/useWatchAsset.md create mode 100644 site/react/api/hooks/useWatchBlockNumber.md create mode 100644 site/react/api/hooks/useWatchBlocks.md create mode 100644 site/react/api/hooks/useWatchContractEvent.md create mode 100644 site/react/api/hooks/useWatchPendingTransactions.md create mode 100644 site/react/api/hooks/useWriteContract.md create mode 100644 site/react/api/hooks/useWriteContracts.md create mode 100644 site/react/api/transports.md create mode 100644 site/react/api/transports/custom.md create mode 100644 site/react/api/transports/fallback.md create mode 100644 site/react/api/transports/http.md create mode 100644 site/react/api/transports/unstable_connector.md create mode 100644 site/react/api/transports/webSocket.md create mode 100644 site/react/api/utilities/cookieToInitialState.md create mode 100644 site/react/api/utilities/deserialize.md create mode 100644 site/react/api/utilities/normalizeChainId.md create mode 100644 site/react/api/utilities/serialize.md create mode 100644 site/react/comparisons.md create mode 100644 site/react/getting-started.md create mode 100644 site/react/guides/chain-properties.md create mode 100644 site/react/guides/connect-wallet.md create mode 100644 site/react/guides/error-handling.md create mode 100644 site/react/guides/ethers.md create mode 100644 site/react/guides/faq.md create mode 100644 site/react/guides/migrate-from-v1-to-v2.md create mode 100644 site/react/guides/read-from-contract.md create mode 100644 site/react/guides/send-transaction.md create mode 100644 site/react/guides/ssr.md create mode 100644 site/react/guides/tanstack-query.md create mode 100644 site/react/guides/testing.md create mode 100644 site/react/guides/viem.md create mode 100644 site/react/guides/write-to-contract.md create mode 100644 site/react/installation.md create mode 100644 site/react/typescript.md create mode 100644 site/react/why.md create mode 100644 site/shared/connectors/coinbaseWallet.md create mode 100644 site/shared/connectors/injected.md create mode 100644 site/shared/connectors/metaMask.md create mode 100644 site/shared/connectors/mock.md create mode 100644 site/shared/connectors/safe.md create mode 100644 site/shared/connectors/walletConnect.md create mode 100644 site/shared/create-chain.md create mode 100644 site/shared/createConfig.md create mode 100644 site/shared/createStorage.md create mode 100644 site/shared/errors.md create mode 100644 site/shared/faq.md create mode 100644 site/shared/getAccount-return-type.md create mode 100644 site/shared/installation.md create mode 100644 site/shared/mutation-imports.md create mode 100644 site/shared/mutation-options.md create mode 100644 site/shared/mutation-result.md create mode 100644 site/shared/query-imports.md create mode 100644 site/shared/query-options.md create mode 100644 site/shared/query-result.md create mode 100644 site/shared/transports/custom.md create mode 100644 site/shared/transports/fallback.md create mode 100644 site/shared/transports/http.md create mode 100644 site/shared/transports/unstable_connector.md create mode 100644 site/shared/transports/webSocket.md create mode 100644 site/shared/utilities/cookieToInitialState.md create mode 100644 site/shared/utilities/deserialize.md create mode 100644 site/shared/utilities/normalizeChainId.md create mode 100644 site/shared/utilities/serialize.md create mode 100644 site/snippets/abi-event.ts create mode 100644 site/snippets/abi-infinite-read.ts create mode 100644 site/snippets/abi-read.ts create mode 100644 site/snippets/abi-write.ts create mode 100644 site/snippets/core/config-chain-properties.ts create mode 100644 site/snippets/core/config.ts create mode 100644 site/snippets/react/app.tsx create mode 100644 site/snippets/react/config-chain-properties.ts create mode 100644 site/snippets/react/config.ts create mode 100644 site/snippets/typedData.ts create mode 100644 site/snippets/vue/App.vue create mode 100644 site/snippets/vue/config-chain-properties.ts create mode 100644 site/snippets/vue/config.ts create mode 100644 site/snippets/vue/main.ts create mode 100644 site/tsconfig.json create mode 100644 site/vercel.json create mode 100644 site/vue/api/Nuxt.md create mode 100644 site/vue/api/WagmiPlugin.md create mode 100644 site/vue/api/actions.md create mode 100644 site/vue/api/chains.md create mode 100644 site/vue/api/composables.md create mode 100644 site/vue/api/composables/useAccount.md create mode 100644 site/vue/api/composables/useAccountEffect.md create mode 100644 site/vue/api/composables/useBalance.md create mode 100644 site/vue/api/composables/useBlockNumber.md create mode 100644 site/vue/api/composables/useBytecode.md create mode 100644 site/vue/api/composables/useChainId.md create mode 100644 site/vue/api/composables/useChains.md create mode 100644 site/vue/api/composables/useClient.md create mode 100644 site/vue/api/composables/useConfig.md create mode 100644 site/vue/api/composables/useConnect.md create mode 100644 site/vue/api/composables/useConnections.md create mode 100644 site/vue/api/composables/useConnectorClient.md create mode 100644 site/vue/api/composables/useConnectors.md create mode 100644 site/vue/api/composables/useDisconnect.md create mode 100644 site/vue/api/composables/useEnsAddress.md create mode 100644 site/vue/api/composables/useEnsAvatar.md create mode 100644 site/vue/api/composables/useEnsName.md create mode 100644 site/vue/api/composables/useEstimateGas.md create mode 100644 site/vue/api/composables/useReadContract.md create mode 100644 site/vue/api/composables/useReconnect.md create mode 100644 site/vue/api/composables/useSendTransaction.md create mode 100644 site/vue/api/composables/useSignMessage.md create mode 100644 site/vue/api/composables/useSignTypedData.md create mode 100644 site/vue/api/composables/useSimulateContract.md create mode 100644 site/vue/api/composables/useSwitchAccount.md create mode 100644 site/vue/api/composables/useSwitchChain.md create mode 100644 site/vue/api/composables/useTransaction.md create mode 100644 site/vue/api/composables/useTransactionReceipt.md create mode 100644 site/vue/api/composables/useWaitForTransactionReceipt.md create mode 100644 site/vue/api/composables/useWatchBlockNumber.md create mode 100644 site/vue/api/composables/useWatchContractEvent.md create mode 100644 site/vue/api/composables/useWriteContract.md create mode 100644 site/vue/api/connectors.md create mode 100644 site/vue/api/connectors/coinbaseWallet.md create mode 100644 site/vue/api/connectors/injected.md create mode 100644 site/vue/api/connectors/metaMask.md create mode 100644 site/vue/api/connectors/mock.md create mode 100644 site/vue/api/connectors/safe.md create mode 100644 site/vue/api/connectors/walletConnect.md create mode 100644 site/vue/api/createConfig.md create mode 100644 site/vue/api/createStorage.md create mode 100644 site/vue/api/errors.md create mode 100644 site/vue/api/transports.md create mode 100644 site/vue/api/transports/custom.md create mode 100644 site/vue/api/transports/fallback.md create mode 100644 site/vue/api/transports/http.md create mode 100644 site/vue/api/transports/unstable_connector.md create mode 100644 site/vue/api/transports/webSocket.md create mode 100644 site/vue/api/utilities/deserialize.md create mode 100644 site/vue/api/utilities/serialize.md create mode 100644 site/vue/getting-started.md create mode 100644 site/vue/guides/chain-properties.md create mode 100644 site/vue/guides/connect-wallet.md create mode 100644 site/vue/guides/error-handling.md create mode 100644 site/vue/guides/faq.md create mode 100644 site/vue/guides/read-from-contract.md create mode 100644 site/vue/guides/send-transaction.md create mode 100644 site/vue/guides/ssr.md create mode 100644 site/vue/guides/tanstack-query.md create mode 100644 site/vue/guides/viem.md create mode 100644 site/vue/guides/write-to-contract.md create mode 100644 site/vue/installation.md create mode 100644 site/vue/typescript.md create mode 100644 site/vue/why.md create mode 100644 src/App.tsx create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 src/vite-env.d.ts create mode 100644 src/wagmi.ts create mode 100644 tsconfig.base.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts create mode 100644 vitest.config.ts create mode 100644 vitest.workspace.ts diff --git a/.changeset/config.json b/.changeset/config.json index bfd12d2782..c47279e4c8 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,11 +1,19 @@ { - "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", - "changelog": "@changesets/cli/changelog", + "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", + "access": "public", + "baseBranch": "main", + "changelog": ["@changesets/changelog-github", { "repo": "wevm/wagmi" }], "commit": false, - "fixed": [], - "linked": [], - "access": "restricted", - "baseBranch": "master", + "ignore": [ + "*-register", + "@wagmi/test", + "site", + "next-app", + "nuxt-app", + "vite-*" + ], "updateInternalDependencies": "patch", - "ignore": [] + "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { + "onlyUpdatePeerDependentsWhenOutOfRange": true + } } 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/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/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..709c9a7474 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/hardhat-project/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7f34c7a889..12451d4bc9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,5 @@ -* @0xsequence/disable-codeowners-notifications @0xsequence/core +@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 new file mode 100644 index 0000000000..d3ab387e17 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1 @@ +[View Contributing Guide on wagmi.sh](https://wagmi.sh/dev/contributing) \ No newline at end of file diff --git a/.github/DISCUSSION_TEMPLATE/connector-request.yml b/.github/DISCUSSION_TEMPLATE/connector-request.yml new file mode 100644 index 0000000000..c1e31b1b6b --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/connector-request.yml @@ -0,0 +1,51 @@ +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.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..8a561abba1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,88 @@ +name: Bug Report +description: Report bugs or issues. +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. + + - 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. + options: + - label: I checked there isn't [already an issue](https://github.com/wevm/wagmi/issues) for the bug I encountered. + required: true + + - type: textarea + 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… + validations: + required: true + + - type: input + id: reproduction + 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 + validations: + required: false + + - type: textarea + attributes: + label: Steps To Reproduce + description: Steps or code snippets to reproduce the behavior. + 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 + attributes: + label: Viem Version + description: What version of [Viem](https://viem.sh) are you using? + placeholder: x.y.z (do not write `latest`) + validations: + required: true + + - 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`) + 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. + validations: + required: false + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..fc8027c871 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +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 + diff --git a/.github/ISSUE_TEMPLATE/docs_issue.yml b/.github/ISSUE_TEMPLATE/docs_issue.yml new file mode 100644 index 0000000000..f2d53b8a98 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs_issue.yml @@ -0,0 +1,34 @@ +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/README.md b/.github/README.md new file mode 100644 index 0000000000..6b5f336419 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,256 @@ + + + +
+ +

+ + + + wagmi logo + + +

+ +

+ Reactive primitives for Ethereum apps +

+ +

+ + + + Version + + + + + + MIT License + + + + + + Downloads per month + + + + + + Best of JS + + + + + + Code coverage + + +

+ +--- + +## Documentation + +For documentation and guides, visit [wagmi.sh](https://wagmi.sh). + +## Community + +For help, discussion about best practices, or any other conversation that would benefit from being searchable: + +[Discuss Wagmi on GitHub](https://github.com/wevm/wagmi/discussions) + +For casual chit-chat with others using the framework: + +[Join the Wagmi Discord](https://discord.gg/SghfWBKexF) + +## Contributing + +Contributions to Wagmi are greatly appreciated! If you're interested in contributing to Wagmi, please read the [Contributing Guide](https://wagmi.sh/dev/contributing) **before submitting a pull request**. + +## Sponsors + +If you find Wagmi useful or use it for work, please consider [sponsoring Wagmi](https://github.com/sponsors/wevm?metadata_campaign=gh_readme_support). Thank you 🙏 + +

+ + + + paradigm logo + + + + + + ithaca logo + + +

+ +

+ + + + family logo + + + + + + context logo + + + + + + WalletConnect logo + + + + + + PartyDAO logo + + + + + + Dynamic logo + + + + + + Sushi logo + + + + + + Stripe logo + + + + + + Privy logo + + + + + + pancake logo + + + + + + celo logo + + + + + + rainbow logo + + + + + + pimlico logo + + + + + + zora logo + + + + + + lattice logo + + + + + + supa logo + + + + + + zksync logo + + + + + + syndicate logo + + + + + + reservoir logo + + + + + + linea logo + + + + + + uniswap logo + + + + + + biconomy logo + + + + + + thirdweb logo + + + + + + polymarket logo + + + + + + routescan logo + + + + + + sequence logo + + +

+ +[Sponsor Wagmi](https://github.com/sponsors/wevm?metadata_campaign=gh_readme_support_bottom) + +
+
+ + + Powered by Vercel + +
+ + Powered by QuickNode + + diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000000..54f40f38df --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,6 @@ +# Security Policy + +## Reporting a Vulnerability + +Contact [dev@wevm.dev](mailto:dev@wevm.dev). + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..bc63aca35b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'monthly' diff --git a/.github/logo-dark.svg b/.github/logo-dark.svg new file mode 100644 index 0000000000..5d47cce337 --- /dev/null +++ b/.github/logo-dark.svg @@ -0,0 +1,27 @@ + + + + + + + diff --git a/.github/logo-light.svg b/.github/logo-light.svg new file mode 100644 index 0000000000..4e28590c36 --- /dev/null +++ b/.github/logo-light.svg @@ -0,0 +1,27 @@ + + + + + + + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..602a32d0a8 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,12 @@ + + + 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/changesets.yml b/.github/workflows/changesets.yml new file mode 100644 index 0000000000..745341ed97 --- /dev/null +++ b/.github/workflows/changesets.yml @@ -0,0 +1,60 @@ +name: Changesets +on: + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify: + name: Verify + uses: ./.github/workflows/verify.yml + secrets: inherit + + changesets: + name: Publish + needs: verify + permissions: + contents: write + id-token: write + pull-requests: write + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Clone repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + 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 + + - name: PR or publish + uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba + with: + title: 'chore: version packages' + commit: 'chore: version packages' + createGithubReleases: ${{ github.ref == 'refs/heads/main' }} + publish: pnpm changeset:publish + 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/issue-labeled.yml b/.github/workflows/issue-labeled.yml new file mode 100644 index 0000000000..39b98291d1 --- /dev/null +++ b/.github/workflows/issue-labeled.yml @@ -0,0 +1,19 @@ +name: Issue Labeled + +on: + issues: + types: [labeled] + +jobs: + issue-labeled: + if: ${{ github.repository_owner == 'wevm' }} + uses: wevm/actions/.github/workflows/issue-labeled.yml@main + with: + needs-reproduction-body: | + Hello @${{ github.event.issue.user.login }}. + + Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using [StackBlitz](https://new.wagmi.sh), [TypeScript Playground](https://www.typescriptlang.org/play) (for type issues), or a separate minimal GitHub repository. + + [Minimal reproductions are required](https://antfu.me/posts/why-reproductions-are-required) as they save us a lot of time reproducing your config/environment and issue, and allow us to help you faster. + + Once a minimal reproduction is added, a team member will confirm it works, then re-open the issue. 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 new file mode 100644 index 0000000000..279452d223 --- /dev/null +++ b/.github/workflows/lock-issue.yml @@ -0,0 +1,16 @@ +name: Lock Issue + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + lock-issue: + if: ${{ github.repository_owner == 'wevm' }} + uses: wevm/actions/.github/workflows/lock-issue.yml@main + with: + issue-comment: | + This issue has been locked since it has been closed for more than 14 days. + + If you found a concrete bug or regression related to it, please open a new [bug report](https://github.com/wevm/wagmi/issues/new?template=bug_report.yml) with a reproduction against the latest Wagmi version. If you have any questions or comments you can create a new [discussion thread](https://github.com/wevm/wagmi/discussions). + 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 new file mode 100644 index 0000000000..9ff4c5bb76 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,32 @@ +name: Pull Request +on: + pull_request: + types: [opened, reopened, synchronize, ready_for_review] + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + verify: + name: Verify + uses: ./.github/workflows/verify.yml + secrets: inherit + + size: + name: Size + 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: Report build size + uses: preactjs/compressed-size-action@v2 + 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..297c74f635 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,44 @@ +name: Release + +on: + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Setup Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + + - name: Install Dependencies + run: pnpm install + + - name: Create Release Pull Request or Publish to npm + id: changesets + uses: changesets/action@v1 + with: + # This expects you to have a script called release which does a build for your packages and calls changeset publish + publish: pnpm release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Send a Slack notification if a publish happens + if: steps.changesets.outputs.published == 'true' + # You can do something when a publish happens. + run: my-slack-bot send-notification --message "A new version of ${GITHUB_REPOSITORY} was published!" diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 0000000000..39683bb684 --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,32 @@ +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 new file mode 100644 index 0000000000..582eef2d55 --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,127 @@ +name: Verify +on: + workflow_call: + workflow_dispatch: + +jobs: + check: + name: Check + permissions: + contents: write + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Clone repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_PTOKEN }} + + - name: Install dependencies + uses: wevm/actions/.github/actions/pnpm@main + + - name: Check repo + run: pnpm check:repo + + - name: Check code + run: pnpm check + + - name: Update package versions + run: pnpm version:update + + - uses: stefanzweifel/git-auto-commit-action@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + commit_message: 'chore: format' + commit_user_name: 'github-actions[bot]' + commit_user_email: 'github-actions[bot]@users.noreply.github.com' + + build: + name: Build + needs: check + 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: Build + run: pnpm build + + - name: Publint + run: pnpm test:build + + - name: Check for unused files, dependencies, and exports + run: pnpm knip --production + + types: + name: Types + needs: check + runs-on: ubuntu-latest + timeout-minutes: 5 + strategy: + matrix: + typescript-version: ['5.7.3', '5.8.3', 'latest'] + viem-version: ['2.29.2', 'latest'] + + steps: + - name: Clone repository + uses: actions/checkout@v4 + + - name: Install dependencies + uses: wevm/actions/.github/actions/pnpm@main + + - run: pnpm add -D -w typescript@${{ matrix.typescript-version }} viem@${{ matrix.viem-version }} + + - name: Link packages + run: pnpm preconstruct + + - name: Check types + run: pnpm check: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 + # run: pnpm test:typecheck + + test: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + max-parallel: 3 + matrix: + shard: [1, 2, 3] + total-shards: [3] + + steps: + - name: Clone repository + uses: actions/checkout@v4 + + - name: Install dependencies + uses: wevm/actions/.github/actions/pnpm@main + + - name: Set up foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run tests + uses: nick-fields/retry@v3 + with: + command: CI=true pnpm test:cov --shard=${{ matrix.shard }}/${{ matrix.total-shards }} --retry=3 --bail=1 + max_attempts: 3 + timeout_minutes: 5 + env: + VITE_MAINNET_FORK_URL: ${{ secrets.VITE_MAINNET_FORK_URL }} + VITE_OPTIMISM_FORK_URL: ${{ secrets.VITE_OPTIMISM_FORK_URL }} + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index e70ecd7f00..1834fe72ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,41 +1,39 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# Dependencies +.DS_Store +.next +.nuxt +.pnpm-debug.log* +cache +coverage +dist node_modules -.pnp -.pnp.js +tsconfig.tsbuildinfo +*.vitest-temp.json -# Local env files +# local env files .env .env.local .env.development.local .env.test.local .env.production.local - -# Testing -coverage - -# Turbo -.turbo - -# Vercel -.vercel - -# Build Outputs -.next/ -out/ -build -dist - - -# Debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Misc -.DS_Store -*.pem - -# Husky -.husky/ \ No newline at end of file +.envrc + +# proxy packages +packages/cli/config +packages/cli/plugins +packages/core/actions +packages/core/chains +packages/core/codegen +packages/core/experimental +packages/core/internal +packages/core/query +packages/react/actions +packages/react/chains +packages/react/codegen +packages/react/connectors +packages/react/experimental +packages/react/query +packages/vue/actions +packages/vue/chains +packages/vue/connectors +packages/vue/nuxt +packages/vue/query diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..47687565bf --- /dev/null +++ b/.npmrc @@ -0,0 +1,5 @@ +auto-install-peers=false +enable-pre-post-scripts=true +link-workspace-packages=deep +provenance=true +strict-peer-dependencies=false diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..9cb435094a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "biomejs.biome", + "orta.vscode-twoslash-queries", + "Vue.volar" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 44a73ec3a9..c32e8fa4c3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,17 @@ { - "eslint.workingDirectories": [ - { - "mode": "auto" - } - ] + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + "typescript.enablePromptUseWorkspaceTsdk": true, + "typescript.preferences.importModuleSpecifier": "shortest", + "typescript.tsdk": "node_modules/typescript/lib", + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "[javascript][javascriptreact][json][typescript][typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[vue]": { + "editor.defaultFormatter": "Vue.volar" + } } diff --git a/.vscode/workspace.code-workspace b/.vscode/workspace.code-workspace new file mode 100644 index 0000000000..0d626129da --- /dev/null +++ b/.vscode/workspace.code-workspace @@ -0,0 +1,16 @@ +{ + "folders": [ + { + "name": "docs", + "path": "../docs" + }, + { + "name": "packages", + "path": "../packages" + }, + { + "name": "playgrounds", + "path": "../playgrounds" + } + ] +} diff --git a/FUNDING.json b/FUNDING.json index cb7bbaf783..5e01254162 100644 --- a/FUNDING.json +++ b/FUNDING.json @@ -1,10 +1,10 @@ { "drips": { "ethereum": { - "ownedBy": "0x9a72807e1BC8A5e1E178f51E26239d58F511EB3D" + "ownedBy": "0xd2135CfB216b74109775236E36d4b433F1DF507B" } }, "opRetro": { - "projectId": "0x62408999652f3bfa1be746d256bf5a4eb4719b993d40f07d2d60aaebee015018" + "projectId": "0xc0615947773148cbc340b175fb9afc98dbb4e0acd31d018b1ee41a5538785abf" } } diff --git a/LICENSE b/LICENSE index d645695673..650c3c1c00 100644 --- a/LICENSE +++ b/LICENSE @@ -1,202 +1,21 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +MIT License + +Copyright (c) 2022-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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 4c663ccf86..15f6f79541 100644 --- a/README.md +++ b/README.md @@ -1,39 +1 @@ -## sequence.js v3 core libraries and SDK - -**NOTE: please see [v2](https://github.com/0xsequence/sequence.js/tree/v2) branch for sequence.js 2.x.x** - ---- - -Sequence v3 core libraries and [wallet-contracts-v3](https://github.com/0xsequence/wallet-contracts-v3) SDK. - -## Packages - -- `@0xsequence/wallet-primitives`: stateless low-level utilities specifically for interacting directly with sequence wallet's smart contracts -- `@0xsequence/wallet-core`: higher level utilities for creating and using sequence wallets -- `@0xsequence/wallet-wdk`: all-in-one wallet development kit for building a sequence wallet product - -## Development - -### Getting Started - -1. Install dependencies: - `pnpm install` - -2. Build all packages: - `pnpm build` - -### Development Workflow - -- Run development mode across all packages: - `pnpm dev` - -- Run tests: - `pnpm test` - - > **Note:** Tests require [anvil](https://github.com/foundry-rs/foundry/tree/master/anvil) and [forge](https://github.com/foundry-rs/foundry) to be installed. You can run a local anvil instance using `pnpm run test:anvil`. - -- Linting and formatting is enforced via git hooks - -## License - -Apache-2.0 +This is a [Vite](https://vitejs.dev) project bootstrapped with [`create-wagmi`](https://github.com/wevm/wagmi/tree/main/packages/create-wagmi). diff --git a/biome.json b/biome.json new file mode 100644 index 0000000000..ce99662cb0 --- /dev/null +++ b/biome.json @@ -0,0 +1,89 @@ +{ + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "files": { + "ignore": ["CHANGELOG.md", "pnpm-lock.yaml", "tsconfig.base.json"] + }, + "formatter": { + "enabled": true, + "formatWithErrors": false, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 80 + }, + "linter": { + "ignore": ["packages/create-wagmi/templates/*"], + "enabled": true, + "rules": { + "recommended": true, + "a11y": { + "useButtonType": "off" + }, + "correctness": { + "noUnusedVariables": "error", + "useExhaustiveDependencies": "error" + }, + "performance": { + "noBarrelFile": "error", + "noReExportAll": "error", + "noDelete": "off" + }, + "style": { + "noNonNullAssertion": "off", + "useShorthandArrayType": "error" + }, + "suspicious": { + "noArrayIndexKey": "off", + "noConfusingVoidType": "off", + "noConsoleLog": "error", + "noExplicitAny": "off" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "all", + "semicolons": "asNeeded" + } + }, + "organizeImports": { + "enabled": true + }, + "overrides": [ + { + "include": ["*.vue"], + "linter": { + "rules": { + "correctness": { + "noUnusedVariables": "off" + } + } + } + }, + { + "include": ["./scripts/**/*.ts"], + "linter": { + "rules": { + "suspicious": { + "noConsoleLog": "off" + } + } + } + }, + { + "include": ["./playgrounds/**"], + "linter": { + "rules": { + "style": { + "useNodejsImportProtocol": "off" + } + } + } + } + ], + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000000..f519ce85a7 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + Create Wagmi + + +
+ + + diff --git a/package.json b/package.json index 2566e6d6ec..ddc9a6a5a4 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,136 @@ { - "name": "sequence-core", - "license": "Apache-2.0", "private": true, - "devDependencies": { - "@changesets/cli": "^2.29.7", - "lefthook": "^1.13.6", - "prettier": "^3.6.2", - "rimraf": "^6.1.0", - "turbo": "^2.6.1", - "typescript": "5.8.3" - }, - "engines": { - "node": ">=18" + "type": "module", + "scripts": { + "build": "pnpm run --r --filter \"./packages/**\" build", + "changeset:prepublish": "pnpm version:update && pnpm build && bun scripts/formatPackageJson.ts && bun scripts/generateProxyPackages.ts", + "changeset:publish": "pnpm changeset:prepublish && changeset publish", + "changeset:version": "changeset version && pnpm version:update && pnpm format", + "check": "biome check --write", + "check:repo": "sherif -i viem", + "check:types": "pnpm run --r --parallel check:types && tsc --noEmit", + "check:unused": "pnpm clean && knip", + "clean": "pnpm run --r --parallel clean && rm -rf packages/**/*.json.tmp", + "deps": "pnpx taze -r", + "dev": "pnpm dev:react", + "dev:cli": "pnpm --filter cli dev", + "dev:core": "pnpm --filter vite-core dev", + "dev:create-wagmi": "pnpm --filter create-wagmi dev", + "dev:next": "pnpm --filter next-app dev", + "dev:nuxt": "pnpm --filter nuxt-app dev", + "dev:react": "pnpm --filter vite-react dev", + "dev:vue": "pnpm --filter vite-vue dev", + "docs:dev": "pnpm --filter site dev", + "format": "biome format --write", + "postinstall": "pnpm preconstruct", + "preconstruct": "bun scripts/preconstruct.ts", + "preinstall": "pnpx only-allow pnpm", + "prepare": "pnpm simple-git-hooks", + "test": "vitest", + "test:build": "bun scripts/generateProxyPackages.ts && pnpm run --r --parallel test:build", + "test:cli": "vitest --project @wagmi/cli", + "test:connectors": "vitest --project @wagmi/connectors", + "test:core": "vitest --project @wagmi/core", + "test:create-wagmi": "vitest --project create-wagmi", + "test:cov": "vitest run --coverage", + "test:react": "vitest --project wagmi", + "test:typecheck": "vitest typecheck", + "test:update": "vitest --update", + "test:vue": "vitest --project @wagmi/vue", + "version:update": "bun scripts/updateVersion.ts", + "version:update:viem": "bun scripts/updateViemVersion.ts" }, - "dependencies": { - "0xsequence": "^2.3.33", - "@0xsequence/connect": "0.0.0-20250924112110", - "@0xsequence/waas": "^2.3.33", - "@0xsequence/wallet": "~2.3.33", - "@0xsequence/wallet-contracts": "~3.0.1", - "@0xsequence/wallet-primitives": "0.0.0-20250915145821", - "@vercel/analytics": "^1.5.0", - "ethers": "^6.15.0", - "flag": "^5.0.1", - "prettier-plugin-solidity": "^2.2.0", - "vercel": "^48.10.2" + "devDependencies": { + "@arethetypeswrong/cli": "^0.16.4", + "@biomejs/biome": "^1.9.4", + "@changesets/changelog-github": "0.4.6", + "@changesets/cli": "^2.27.8", + "@types/bun": "^1.1.10", + "@vitest/coverage-v8": "^3.2.4", + "@wagmi/test": "workspace:*", + "bun": "^1.1.30", + "happy-dom": "^20.0.2", + "knip": "^5.30.6", + "prool": "^0.0.23", + "publint": "^0.3.0", + "sherif": "^1.0.0", + "simple-git-hooks": "^2.11.1", + "typescript": "5.8.3", + "viem": "2.31.4", + "vitest": "^2.1.9" }, - "version": "1.0.0", - "description": "**NOTE: please see [v2](https://github.com/0xsequence/sequence.js/tree/v2) branch for sequence.js 2.x.x**", - "main": "sequence.js", - "directories": { - "lib": "lib" + "packageManager": "pnpm@9.11.0", + "pnpm": { + "peerDependencyRules": { + "ignoreMissing": [ + "@algolia/client-search", + "react", + "react-native", + "search-insights" + ] + } }, - "repository": { - "type": "git", - "url": "git+https://github.com/Dargon789/sequence.js.git" + "engines": { + "node": "22.x" }, - "keywords": [], - "author": "", - "bugs": { - "url": "https://github.com/Dargon789/sequence.js/issues" + "simple-git-hooks": { + "pre-commit": "pnpm check" }, - "homepage": "https://github.com/Dargon789/sequence.js#readme", - "scripts": { - "build:all": "turbo build", - "build:packages": "turbo build --filter=\"./packages/**/*\"", - "build": "pnpm build:packages", - "dev": "turbo dev", - "test": "turbo test --concurrency=1", - "lint": "turbo lint", - "format": "prettier --list-different --write \"**/*.{ts,tsx,md}\"", - "typecheck": "turbo typecheck", - "postinstall": "lefthook install", - "dev:server": "node packages/wallet/primitives-cli/dist/index.js server", - "reinstall": "rimraf -g ./**/node_modules && pnpm install", - "test:anvil": "anvil --fork-url https://nodes.sequence.app/arbitrum", - "clean": "turbo clean" + "knip": { + "ignore": ["**/templates/**", "**/hardhat.config.js"], + "ignoreBinaries": ["only-allow"], + "ignoreWorkspaces": [ + "packages/register-tests/**", + "packages/test", + "playgrounds/**" + ], + "workspaces": { + ".": { + "project": "scripts/*.ts" + }, + "packages/cli": { + "entry": [ + "src/cli.ts!", + "src/exports/{config,index,plugins}.ts!", + "types/*.d.ts!" + ], + "ignore": ["test/{constants,setup,utils}.ts"] + }, + "packages/connectors": { + "entry": "src/exports/index.ts!" + }, + "packages/core": { + "entry": "src/exports/{actions,chains,codegen,experimental,index,internal,query}.ts!", + "ignore": ["test/setup.ts"], + "ignoreDependencies": ["@tanstack/query-core"] + }, + "packages/create-wagmi": { + "entry": "src/cli.ts!" + }, + "packages/react": { + "entry": [ + "src/exports/{actions,chains,codegen,connectors,experimental,index,query}.ts!", + "src/exports/actions/experimental.ts!" + ], + "ignore": ["test/setup.ts"] + }, + "packages/test": { + "entry": [ + "src/{globalSetup,setup}.ts!", + "src/exports/{index,react}.ts!" + ] + }, + "packages/vue": { + "entry": [ + "src/exports/{actions,chains,connectors,index,nuxt,query}.ts!", + "src/exports/actions/experimental.ts!" + ], + "ignore": ["src/nuxt/runtime/*", "test/setup.ts"], + "ignoreDependencies": ["nuxt"] + }, + "site": { + "project": ["**/*.ts", "**/*.tsx"] + } + } } -} \ No newline at end of file +} diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md new file mode 100644 index 0000000000..a2911e2304 --- /dev/null +++ b/packages/cli/CHANGELOG.md @@ -0,0 +1,449 @@ +# @wagmi/cli + +## 2.3.1 + +### Patch Changes + +- [#4655](https://github.com/wevm/wagmi/pull/4655) [`43241c8417f3c342036bb46ec8e507d052ae2691`](https://github.com/wevm/wagmi/commit/43241c8417f3c342036bb46ec8e507d052ae2691) Thanks [@tmm](https://github.com/tmm)! - Bumped internal deps. + +## 2.3.0 + +### Minor Changes + +- [#4629](https://github.com/wevm/wagmi/pull/4629) [`66dec7d75d580b3121ebc7e8162c1f9ae37cfd41`](https://github.com/wevm/wagmi/commit/66dec7d75d580b3121ebc7e8162c1f9ae37cfd41) Thanks [@allezxandre](https://github.com/allezxandre)! - Upgraded to Sourcify v2 API in `sourcify` plugin + +## 2.2.1 + +### Patch Changes + +- [`7b0dbe3886c1a7c6dbbdab945d7436ec20ad8f93`](https://github.com/wevm/wagmi/commit/7b0dbe3886c1a7c6dbbdab945d7436ec20ad8f93) Thanks [@tmm](https://github.com/tmm)! - Updated block explorer chains. + +## 2.2.0 + +### Minor Changes + +- [#4503](https://github.com/wevm/wagmi/pull/4503) [`8fce8a6f97aa2ee5fd1bda6a3ece422b10324b5a`](https://github.com/wevm/wagmi/commit/8fce8a6f97aa2ee5fd1bda6a3ece422b10324b5a) Thanks [@tmm](https://github.com/tmm)! - Updated Etherscan Plugin to use Etherscan API v2. + +- [#4507](https://github.com/wevm/wagmi/pull/4507) [`6f09cc57935891e1c67d6df3459f6998985c69dc`](https://github.com/wevm/wagmi/commit/6f09cc57935891e1c67d6df3459f6998985c69dc) Thanks [@tmm](https://github.com/tmm)! - Added `tryFetchProxyImplementation` flag to Etherscan Plugin to enable fetching the implementation ABI instead of the proxy ABI. + +## 2.1.22 + +### Patch Changes + +- [#4462](https://github.com/wevm/wagmi/pull/4462) [`0b2238d27cecbcd33aee64fb0e30ddc18b6ddf74`](https://github.com/wevm/wagmi/commit/0b2238d27cecbcd33aee64fb0e30ddc18b6ddf74) Thanks [@groninge01](https://github.com/groninge01)! - Added Sonic to Etherscan plugin. + +## 2.1.21 + +### Patch Changes + +- [#4457](https://github.com/wevm/wagmi/pull/4457) [`21ec74da7f93fc13e253d7b35ddeddc23422a6c1`](https://github.com/wevm/wagmi/commit/21ec74da7f93fc13e253d7b35ddeddc23422a6c1) Thanks [@tmm](https://github.com/tmm)! - Removed internal dependency. + +## 2.1.20 + +### Patch Changes + +- [#4450](https://github.com/wevm/wagmi/pull/4450) [`7b9a6bb35881b657a00bdd7ccd7edea32660f5bf`](https://github.com/wevm/wagmi/commit/7b9a6bb35881b657a00bdd7ccd7edea32660f5bf) Thanks [@tmm](https://github.com/tmm)! - Removed internal usage of `fs-extra`. + +## 2.1.19 + +### Patch Changes + +- [#4449](https://github.com/wevm/wagmi/pull/4449) [`3fa5c238baa13d948e89974b0bb8530f8fa264fd`](https://github.com/wevm/wagmi/commit/3fa5c238baa13d948e89974b0bb8530f8fa264fd) Thanks [@tmm](https://github.com/tmm)! - Removed `ora` for `nanospinner`. + +## 2.1.18 + +### Patch Changes + +- [#4399](https://github.com/wevm/wagmi/pull/4399) [`bc18673e4c272e3b60a1b6016934fe3fbeb6d93a`](https://github.com/wevm/wagmi/commit/bc18673e4c272e3b60a1b6016934fe3fbeb6d93a) Thanks [@tmm](https://github.com/tmm)! - Added Polygon Amoy to Sourcify and Etherscan plugins. + +## 2.1.17 + +### Patch Changes + +- [#4370](https://github.com/wevm/wagmi/pull/4370) [`cb58b1ea3ad40e77210f24eb598f9d2306db998c`](https://github.com/wevm/wagmi/commit/cb58b1ea3ad40e77210f24eb598f9d2306db998c) Thanks [@talentlessguy](https://github.com/talentlessguy)! - Bumped internal dependencies. + +## 2.1.16 + +### Patch Changes + +- [#4224](https://github.com/wevm/wagmi/pull/4224) [`b0eb89c2a0781bb3434996fa53ee7ceb3bb44db9`](https://github.com/wevm/wagmi/commit/b0eb89c2a0781bb3434996fa53ee7ceb3bb44db9) Thanks [@roderik](https://github.com/roderik)! - Fixed package detection for Bun. + +## 2.1.15 + +### Patch Changes + +- [`0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e`](https://github.com/wevm/wagmi/commit/0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e) Thanks [@tmm](https://github.com/tmm)! - Improved TypeScript `'exactOptionalPropertyTypes'` support. + +## 2.1.14 + +### Patch Changes + +- [#4120](https://github.com/wevm/wagmi/pull/4120) [`59407bf1276a46e6f1f22a370dde71c92524cd0f`](https://github.com/wevm/wagmi/commit/59407bf1276a46e6f1f22a370dde71c92524cd0f) Thanks [@tmm](https://github.com/tmm)! - Fixed an issue where the Foundry and Hardhat plugins' `exclude` option was ignored. + +## 2.1.13 + +### Patch Changes + +- [`7264d1f450727f6ba0cbea8aa1c7a83e22a5bf20`](https://github.com/wevm/wagmi/commit/7264d1f450727f6ba0cbea8aa1c7a83e22a5bf20) Thanks [@tmm](https://github.com/tmm)! - Fixed generate not exiting for long-running processes. + +## 2.1.12 + +### Patch Changes + +- [`ac038b29623ccb0d2fee40d9f943c8df28138dac`](https://github.com/wevm/wagmi/commit/ac038b29623ccb0d2fee40d9f943c8df28138dac) Thanks [@tmm](https://github.com/tmm)! - Updated Foundry default excludes. + +## 2.1.11 + +### Patch Changes + +- [#4084](https://github.com/wevm/wagmi/pull/4084) [`b54203bf8fa911e6f14b9675980cf38fb95d7d3e`](https://github.com/wevm/wagmi/commit/b54203bf8fa911e6f14b9675980cf38fb95d7d3e) Thanks [@tmm](https://github.com/tmm)! - Reduced internal dependencies. + +## 2.1.10 + +### Patch Changes + +- [#4051](https://github.com/wevm/wagmi/pull/4051) [`275e78b0e585f0ec9da2f9661ce9990aed18e9f4`](https://github.com/wevm/wagmi/commit/275e78b0e585f0ec9da2f9661ce9990aed18e9f4) Thanks [@tmm](https://github.com/tmm)! - Updated Sourcify plugin internals. + +## 2.1.9 + +### Patch Changes + +- [`f9346dbcffaf57a8949cb96e43df111a89d733b1`](https://github.com/wevm/wagmi/commit/f9346dbcffaf57a8949cb96e43df111a89d733b1) Thanks [@tmm](https://github.com/tmm)! - Updated Foundry plugin default excludes. + +## 2.1.8 + +### Patch Changes + +- [#3957](https://github.com/wevm/wagmi/pull/3957) [`7d00680f73b090eb34af928ae74277bec1973953`](https://github.com/wevm/wagmi/commit/7d00680f73b090eb34af928ae74277bec1973953) Thanks [@cstoneham](https://github.com/cstoneham)! - Added Blast to Etherscan plugin + +## 2.1.7 + +### Patch Changes + +- [`1122678bbad0232590bd4060a73752de2c84982d`](https://github.com/wevm/wagmi/commit/1122678bbad0232590bd4060a73752de2c84982d) Thanks [@tmm](https://github.com/tmm)! - Published unpublished changes in [#3756](https://github.com/wevm/wagmi/pull/3756). + +## 2.1.6 + +### Patch Changes + +- [#3756](https://github.com/wevm/wagmi/pull/3756) [`c7d6f467`](https://github.com/wevm/wagmi/commit/c7d6f4679125fd2f6cca5b5ef362abf47e37f934) Thanks [@jrfrantz](https://github.com/jrfrantz)! - Added basescan to etherscan cli plugin + +## 2.1.5 + +### Patch Changes + +- [`e1ca4e63`](https://github.com/wevm/wagmi/commit/e1ca4e637ae6cec7f5902b0a2c0e0efc3b751a1d) Thanks [@tmm](https://github.com/tmm)! - Added title to CLI process. + +- [#3723](https://github.com/wevm/wagmi/pull/3723) [`d6bc98ca`](https://github.com/wevm/wagmi/commit/d6bc98ca0ce9081f192f62e0b0fcfea3cb07a2bb) Thanks [@leecobaby](https://github.com/leecobaby)! - Broadened TypeScript detection. + +## 2.1.4 + +### Patch Changes + +- [#3737](https://github.com/wevm/wagmi/pull/3737) [`11020fed`](https://github.com/wevm/wagmi/commit/11020fedfc68639eace241e328331cff43bf91af) Thanks [@oskarvu](https://github.com/oskarvu)! - Added Gnosis to Etherscan plugin. + +## 2.1.3 + +### Patch Changes + +- [#3660](https://github.com/wevm/wagmi/pull/3660) [`11a22a23`](https://github.com/wevm/wagmi/commit/11a22a23d88c025cde9c91610e9ddf62cd4fa650) Thanks [@JazzBashara](https://github.com/JazzBashara)! - Replaced SnowTrace with SnowScan for the Etherscan plugin + +## 2.1.2 + +### Patch Changes + +- [#3641](https://github.com/wevm/wagmi/pull/3641) [`0a866403`](https://github.com/wevm/wagmi/commit/0a866403182ea6b8ba7f976c45be294e48fb7de8) Thanks [@cmwhited](https://github.com/cmwhited)! - Added Arbitrum Sepolia testnet to Etherscan plugin + +- [#3633](https://github.com/wevm/wagmi/pull/3633) [`a1d3d1ab`](https://github.com/wevm/wagmi/commit/a1d3d1ab2b023c61c0dbb5d7bf867a9fca673630) Thanks [@pegahcarter](https://github.com/pegahcarter)! - Added Fraxtal to Etherscan plugin + +- [#3616](https://github.com/wevm/wagmi/pull/3616) [`2a9f4473`](https://github.com/wevm/wagmi/commit/2a9f4473adc5bcdddf388389387ed5459583769e) Thanks [@petermazzocco](https://github.com/petermazzocco)! - Added Holesky Testnet to Etherscan Plugin + +## 2.1.1 + +### Patch Changes + +- [#3579](https://github.com/wevm/wagmi/pull/3579) [`a057919c`](https://github.com/wevm/wagmi/commit/a057919ca3942adeed90af2e343403dc5274e84c) Thanks [@FaisalAli19](https://github.com/FaisalAli19)! - Added Optimism Sepolia Etherscan support + +## 2.1.0 + +### Minor Changes + +- [#3506](https://github.com/wevm/wagmi/pull/3506) [`134eb4a1`](https://github.com/wevm/wagmi/commit/134eb4a1e0e29aab87bd5c7cdf05b06dfd7c4fc4) Thanks [@vmaark](https://github.com/vmaark)! - Added resolution of TypeScript Wagmi CLI config to determine if TypeScript generated output is allowed. + +## 2.0.4 + +### Patch Changes + +- [#3462](https://github.com/wevm/wagmi/pull/3462) [`d25573ea`](https://github.com/wevm/wagmi/commit/d25573ea03358f967953e37c176b220a7b341769) Thanks [@cruzdanilo](https://github.com/cruzdanilo)! - Upgraded dependencies + +## 2.0.3 + +### Patch Changes + +- [#3410](https://github.com/wevm/wagmi/pull/3410) [`55e31c3e`](https://github.com/wevm/wagmi/commit/55e31c3e96c2cbd1d9eb44e5a89f4365489c8310) Thanks [@o-az](https://github.com/o-az)! - Fixed actions plugin issue where `functionName` was used instead of `eventName` for generated contract event actions. + +## 2.0.2 + +### Patch Changes + +- [#3371](https://github.com/wevm/wagmi/pull/3371) [`8294d9e5`](https://github.com/wevm/wagmi/commit/8294d9e5b358018ba869b2018cd7ed95462e021f) Thanks [@iceanddust](https://github.com/iceanddust)! - Fixed prop name when generating contract event watch hooks + +## 2.0.1 + +### Major Changes + +- [#3333](https://github.com/wevm/wagmi/pull/3333) [`b3a0baaa`](https://github.com/wevm/wagmi/commit/b3a0baaaee7decf750d376aab2502cd33ca4825a) Thanks [@tmm](https://github.com/tmm)! - Wagmi CLI 2.0. + + [Breaking Changes & Migration Guide](https://wagmi.sh/cli/guides/migrate-from-v1-to-v2) + +## 1.5.2 + +### Patch Changes + +- [#3051](https://github.com/wagmi-dev/wagmi/pull/3051) [`4704d351`](https://github.com/wagmi-dev/wagmi/commit/4704d351164d39704a4e375c06525554fcc8340e) Thanks [@oxSaturn](https://github.com/oxSaturn)! - Fixed ESM require issue for prettier + +## 1.5.1 + +### Patch Changes + +- [#3035](https://github.com/wagmi-dev/wagmi/pull/3035) [`187bf96c`](https://github.com/wagmi-dev/wagmi/commit/187bf96c9fd31675b9d17a7cb4d4e24eea3fa777) Thanks [@cruzdanilo](https://github.com/cruzdanilo)! - ignore foundry invariant lib + +## 1.5.0 + +### Minor Changes + +- [#2956](https://github.com/wevm/wagmi/pull/2956) [`2abeb285`](https://github.com/wevm/wagmi/commit/2abeb285674af3e539cc2550b1f5027b1eb0c895) Thanks [@tmm](https://github.com/tmm)! - Replaced `@wagmi/chains` with `viem/chains`. + +## 1.4.1 + +### Patch Changes + +- [#2962](https://github.com/wevm/wagmi/pull/2962) [`8ac5b572`](https://github.com/wevm/wagmi/commit/8ac5b57254f77eeb0e07dd83f7d49f396d4581d8) Thanks [@tmm](https://github.com/tmm)! - Fixed esbuild version + +## 1.4.0 + +### Minor Changes + +- [#2946](https://github.com/wevm/wagmi/pull/2946) [`1c3228bf`](https://github.com/wevm/wagmi/commit/1c3228bf3fe99b0900b2c9a223c9b81c70bdcd90) Thanks [@tomquirk](https://github.com/tomquirk)! - Added default chain ID to generated `useContractRead` hook. + +### Patch Changes + +- [#2547](https://github.com/wevm/wagmi/pull/2547) [`8c3889fe`](https://github.com/wevm/wagmi/commit/8c3889fe82c5a1ddb29e74e3863ea6f4917b777a) Thanks [@Iamshankhadeep](https://github.com/Iamshankhadeep)! - Deterministic CLI output + +- [#2958](https://github.com/wevm/wagmi/pull/2958) [`b31f36d5`](https://github.com/wevm/wagmi/commit/b31f36d522a634f53d44349d6a9ea47f59d84d7a) Thanks [@tmm](https://github.com/tmm)! - Removed generated file header + +- [#2960](https://github.com/wevm/wagmi/pull/2960) [`5d4c4592`](https://github.com/wevm/wagmi/commit/5d4c4592009568cd0b096906a424f27469721a42) Thanks [@tmm](https://github.com/tmm)! - Updated esbuild version + +## 1.3.0 + +### Minor Changes + +- [#2616](https://github.com/wevm/wagmi/pull/2616) [`c282a8f7`](https://github.com/wevm/wagmi/commit/c282a8f786d57fec77c931fe99dc20220e843bc8) Thanks [@portdeveloper](https://github.com/portdeveloper)! - Added sepolia chain id + +## 1.2.1 + +### Patch Changes + +- [#2607](https://github.com/wevm/wagmi/pull/2607) [`79335b4c`](https://github.com/wevm/wagmi/commit/79335b4c0fcd5e8152a2a1d28314c634db9d9cbf) Thanks [@roninjin10](https://github.com/roninjin10)! - Fixed opitmism goerli chain id + +## 1.2.0 + +### Minor Changes + +- [#2536](https://github.com/wevm/wagmi/pull/2536) [`85e9760a`](https://github.com/wevm/wagmi/commit/85e9760a140cb169ac6236d9466b96e2105dd193) Thanks [@tmm](https://github.com/tmm)! - Changed `Address` type import from ABIType to viem. + +## 1.1.0 + +### Minor Changes + +- [#2482](https://github.com/wevm/wagmi/pull/2482) [`8764b54a`](https://github.com/wevm/wagmi/commit/8764b54aab68020063946112e8fe52aff650c99c) Thanks [@tmm](https://github.com/tmm)! - Bumped minimum TypeScript version to v5.0.4. + +### Patch Changes + +- [#2484](https://github.com/wevm/wagmi/pull/2484) [`3adf1f4f`](https://github.com/wevm/wagmi/commit/3adf1f4feab863cb7b5d52c81ad46f7e4eb56f09) Thanks [@jxom](https://github.com/jxom)! - Updated `abitype` to 0.8.7 + +- [#2484](https://github.com/wevm/wagmi/pull/2484) [`3adf1f4f`](https://github.com/wevm/wagmi/commit/3adf1f4feab863cb7b5d52c81ad46f7e4eb56f09) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +## 1.0.3 + +### Patch Changes + +- [#2441](https://github.com/wevm/wagmi/pull/2441) [`326edee4`](https://github.com/wevm/wagmi/commit/326edee4bc85db84a7a4e3768e33785849ab8d8e) Thanks [@tmm](https://github.com/tmm)! - Fixed internal type issue + +## 1.0.2 + +### Patch Changes + +- [#2430](https://github.com/wevm/wagmi/pull/2430) [`71d92029`](https://github.com/wevm/wagmi/commit/71d92029ee4344842cd41698858a330fee95b6e0) Thanks [@tmm](https://github.com/tmm)! - Added message when command is not found. + +## 1.0.1 + +### Patch Changes + +- [`ea651cd7`](https://github.com/wevm/wagmi/commit/ea651cd7fc75b7866272605467db11fd6e1d81af) Thanks [@jxom](https://github.com/jxom)! - Downgraded abitype. + +## 1.0.0 + +### Major Changes + +- [#2235](https://github.com/wevm/wagmi/pull/2235) [`5be0655c`](https://github.com/wevm/wagmi/commit/5be0655c8e48b25d38009022461fbf611af54349) Thanks [@jxom](https://github.com/jxom)! - Released v1. Read [Migration Guide](https://next.wagmi.sh/react/migration-guide#1xx-breaking-changes). + +## 1.0.0-next.7 + +### Patch Changes + +- Fixed react plugin generic. + +## 1.0.0-next.6 + +### Major Changes + +- Updated references. + +## 1.0.0-next.5 + +### Major Changes + +- Added `config.setConnectors` + +## 1.0.0-next.4 + +### Major Changes + +- Updated viem. + Removed `goerli` export from main entrypoint. + +## 1.0.0-next.3 + +### Major Changes + +- Updated references. + +## 1.0.0-next.2 + +### Major Changes + +- Updated dependencies + +### Patch Changes + +- Updated dependencies []: + - @wagmi/chains@1.0.0-next.0 + +## 1.0.0-next.1 + +### Major Changes + +- updated viem + +## 1.0.0-next.0 + +### Major Changes + +- [`a7dda00c`](https://github.com/wevm/wagmi/commit/a7dda00c5b546f8b2c42b527e4d9ac1b9e9ab1fb) Thanks [@jxom](https://github.com/jxom)! - Released v1. + +### Patch Changes + +- Updated dependencies [[`a7dda00c`](https://github.com/wevm/wagmi/commit/a7dda00c5b546f8b2c42b527e4d9ac1b9e9ab1fb)]: + - @wagmi/core@1.0.0-next.0 + - wagmi@1.0.0-next.0 + +## 0.1.15 + +### Patch Changes + +- [#2145](https://github.com/wevm/wagmi/pull/2145) [`2520743c`](https://github.com/wevm/wagmi/commit/2520743c417a158a00d5edca13a9aa92cefb0cfd) Thanks [@tmm](https://github.com/tmm)! - Fixed issue using Hardhat Plugin with npm. + +## 0.1.14 + +### Patch Changes + +- [#2039](https://github.com/wevm/wagmi/pull/2039) [`bac893ab`](https://github.com/wevm/wagmi/commit/bac893ab26012d4d8741c4f80e8b8813aee26f0c) Thanks [@tmm](https://github.com/tmm)! - Updated references. + +- [#2039](https://github.com/wevm/wagmi/pull/2039) [`bac893ab`](https://github.com/wevm/wagmi/commit/bac893ab26012d4d8741c4f80e8b8813aee26f0c) Thanks [@tmm](https://github.com/tmm)! - Fixed Actions plugin `overridePackageName` option. + +## 0.1.13 + +### Patch Changes + +- [#2000](https://github.com/wevm/wagmi/pull/2000) [`01254765`](https://github.com/wevm/wagmi/commit/01254765eb37b77aca26500c00c721f08a260912) Thanks [@tmm](https://github.com/tmm)! - Fixed React plugin name conflict. + +## 0.1.12 + +### Patch Changes + +- [#1992](https://github.com/wevm/wagmi/pull/1992) [`efc93cad`](https://github.com/wevm/wagmi/commit/efc93cadacdb9c9960644dabe4ae837d384df52b) Thanks [@tmm](https://github.com/tmm)! - Refactored internals from ethers to viem. + +## 0.1.11 + +### Patch Changes + +- [#1916](https://github.com/wevm/wagmi/pull/1916) [`950490fd`](https://github.com/wevm/wagmi/commit/950490fd132b3fb5b3455e77b58d70f134b8e5c9) Thanks [@technophile-04](https://github.com/technophile-04)! - Updated React plugin to use `Address` type instead of hardcoding `` `0x{string}` ``. + +## 0.1.10 + +### Patch Changes + +- [#1892](https://github.com/wevm/wagmi/pull/1892) [`d3d6973b`](https://github.com/wevm/wagmi/commit/d3d6973ba9407e490140d2434eb83aad88d6e10d) Thanks [@greg-schrammel](https://github.com/greg-schrammel)! - Fixed generated read hooks `select` type. + +## 0.1.9 + +### Patch Changes + +- [#1886](https://github.com/wevm/wagmi/pull/1886) [`36e119c6`](https://github.com/wevm/wagmi/commit/36e119c6d4bc28a7ae15c9602d0c613bc9681356) Thanks [@roninjin10](https://github.com/roninjin10)! - Fixed package detection for yarn^3 + +## 0.1.8 + +### Patch Changes + +- [#1884](https://github.com/wevm/wagmi/pull/1884) [`cc03bb44`](https://github.com/wevm/wagmi/commit/cc03bb44268874f95203de67f6d32586e34c0857) Thanks [@roninjin10](https://github.com/roninjin10)! - Added better compatibility for yarn@^3 in `@wagmi/cli`. + +## 0.1.7 + +### Patch Changes + +- [#1841](https://github.com/wevm/wagmi/pull/1841) [`cb707f01`](https://github.com/wevm/wagmi/commit/cb707f01cbdcc62a70cf5c8a162d77948d6b6a56) Thanks [@tmm](https://github.com/tmm)! - Added [Sourcify](https://sourcify.dev) CLI plugin. + +## 0.1.6 + +### Patch Changes + +- [#1803](https://github.com/wevm/wagmi/pull/1803) [`09b13538`](https://github.com/wevm/wagmi/commit/09b13538abcde879034293cae39551c30cc81445) Thanks [@shotaronowhere](https://github.com/shotaronowhere)! - Swapped deprecated Arbitrum Rinkeby for Arbitrum Goerli URL for Etherscan Plugin. + +## 0.1.5 + +### Patch Changes + +- [#1788](https://github.com/wevm/wagmi/pull/1788) [`c3e16d82`](https://github.com/wevm/wagmi/commit/c3e16d82c9c39b8b1c2f3c51037e11d642a20cd6) Thanks [@tmm](https://github.com/tmm)! - Fixed CLI import + +## 0.1.4 + +### Patch Changes + +- [#1779](https://github.com/wevm/wagmi/pull/1779) [`97346750`](https://github.com/wevm/wagmi/commit/973467505dc2bb46198a3e9fe6072306170d24c0) Thanks [@tmm](https://github.com/tmm)! - Made `project` optional for Foundry plugin + +## 0.1.3 + +### Patch Changes + +- [#1754](https://github.com/wevm/wagmi/pull/1754) [`298728b5`](https://github.com/wevm/wagmi/commit/298728b5918fa15b6b5b082597204a268d4b01f1) Thanks [@tmm](https://github.com/tmm)! - Updated project resolution for Foundry and Hardhat plugins. + +- [#1738](https://github.com/wevm/wagmi/pull/1738) [`37c221d0`](https://github.com/wevm/wagmi/commit/37c221d0f4d175084e23a6b172d72f177bfa0c81) Thanks [@roninjin10](https://github.com/roninjin10)! - Added automatic Foundry config detection for artifacts directory. + +## 0.1.2 + +### Patch Changes + +- [#1743](https://github.com/wevm/wagmi/pull/1743) [`379315fa`](https://github.com/wevm/wagmi/commit/379315fa359c3118b5d200ec50db3812b0cdd984) Thanks [@kyscott18](https://github.com/kyscott18)! - Add celoscan to `etherscan` plugin + +## 0.1.1 + +### Patch Changes + +- [#1736](https://github.com/wevm/wagmi/pull/1736) [`7c43e431`](https://github.com/wevm/wagmi/commit/7c43e431e2eb970610cc6490cee6a4093655a683) Thanks [@tmm](https://github.com/tmm)! - Fixed generated address object key type. + +## 0.1.0 + +### Minor Changes + +- [#1732](https://github.com/wevm/wagmi/pull/1732) [`01e21897`](https://github.com/wevm/wagmi/commit/01e2189747a5c22dc758c6d719b4145adc2a643c) Thanks [@tmm](https://github.com/tmm)! - Initial release diff --git a/packages/cli/README.md b/packages/cli/README.md new file mode 100644 index 0000000000..640acb22d2 --- /dev/null +++ b/packages/cli/README.md @@ -0,0 +1,13 @@ +# @wagmi/cli + +Manage and generate code from Ethereum ABIs + +## Installation + +```bash +pnpm add @wagmi/cli +``` + +## Documentation + +For documentation and guides, visit [wagmi.sh](https://wagmi.sh). diff --git a/packages/cli/package.json b/packages/cli/package.json new file mode 100644 index 0000000000..f3848081e5 --- /dev/null +++ b/packages/cli/package.json @@ -0,0 +1,94 @@ +{ + "name": "@wagmi/cli", + "description": "Manage and generate code from Ethereum ABIs", + "version": "2.3.1", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/wevm/wagmi.git", + "directory": "packages/cli" + }, + "scripts": { + "build": "pnpm run clean && pnpm run build:esm+types", + "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", + "check:types": "tsc --noEmit", + "clean": "rm -rf dist tsconfig.tsbuildinfo config plugins", + "dev": "bun src/cli.ts", + "test:build": "publint --strict && attw --pack --ignore-rules cjs-resolves-to-esm" + }, + "files": [ + "dist/**", + "!dist/**/*.tsbuildinfo", + "src/**/*.ts", + "!src/**/*.test.ts", + "!src/**/*.test-d.ts", + "/config", + "/plugins" + ], + "bin": { + "wagmi": "./dist/esm/cli.js" + }, + "sideEffects": false, + "type": "module", + "main": "./dist/esm/exports/index.js", + "types": "./dist/types/exports/index.d.ts", + "typings": "./dist/types/exports/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/exports/index.d.ts", + "default": "./dist/esm/exports/index.js" + }, + "./config": { + "types": "./dist/types/exports/config.d.ts", + "default": "./dist/esm/exports/config.js" + }, + "./plugins": { + "types": "./dist/types/exports/plugins.d.ts", + "default": "./dist/esm/exports/plugins.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "config": ["./dist/types/exports/config.d.ts"], + "plugins": ["./dist/types/exports/plugins.d.ts"] + } + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + }, + "dependencies": { + "abitype": "^1.0.4", + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "change-case": "^5.4.4", + "chokidar": "4.0.3", + "dedent": "^1.5.3", + "dotenv": "^16.3.1", + "dotenv-expand": "^10.0.0", + "esbuild": "~0.27.0", + "escalade": "3.2.0", + "fdir": "^6.1.1", + "nanospinner": "1.2.2", + "pathe": "^2.0.3", + "picocolors": "^1.0.0", + "picomatch": "^4.0.2", + "prettier": "^3.0.3", + "viem": "2.x", + "zod": "^3.22.3" + }, + "devDependencies": { + "@types/dedent": "^0.7.2", + "@types/node": "^22.14.0", + "fixturez": "^1.1.0", + "msw": "^2.4.9" + }, + "contributors": ["awkweb.eth ", "jxom.eth "], + "funding": "https://github.com/sponsors/wevm", + "keywords": ["wagmi", "eth", "ethereum", "dapps", "wallet", "web3", "cli"] +} diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts new file mode 100644 index 0000000000..551543bb2b --- /dev/null +++ b/packages/cli/src/cli.ts @@ -0,0 +1,53 @@ +#!/usr/bin/env node +import { cac } from 'cac' + +import { type Generate, generate } from './commands/generate.js' +import { type Init, init } from './commands/init.js' +import * as logger from './logger.js' +import { version } from './version.js' + +const cli = cac('wagmi') + +cli + .command('generate', 'generate code based on configuration') + .option('-c, --config ', '[string] path to config file') + .option('-r, --root ', '[string] root path to resolve config from') + .option('-w, --watch', '[boolean] watch for changes') + .example((name) => `${name} generate`) + .action(async (options: Generate) => { + await generate(options) + if (!options.watch) process.exit(0) + }) + +cli + .command('init', 'create configuration file') + .option('-c, --config ', '[string] path to config file') + .option('-r, --root ', '[string] root path to resolve config from') + .example((name) => `${name} init`) + .action(async (options: Init) => { + await init(options) + process.exit(0) + }) + +cli.help() +cli.version(version) + +void (async () => { + try { + process.title = 'node (wagmi)' + } catch {} + + try { + // Parse CLI args without running command + cli.parse(process.argv, { run: false }) + if (!cli.matchedCommand) { + if (cli.args.length === 0) { + if (!cli.options.help && !cli.options.version) cli.outputHelp() + } else throw new Error(`Unknown command: ${cli.args.join(' ')}`) + } + await cli.runMatchedCommand() + } catch (error) { + logger.error(`\n${(error as Error).message}`) + process.exit(1) + } +})() diff --git a/packages/cli/src/commands/generate.test.ts b/packages/cli/src/commands/generate.test.ts new file mode 100644 index 0000000000..91e4265216 --- /dev/null +++ b/packages/cli/src/commands/generate.test.ts @@ -0,0 +1,409 @@ +import { readFile } from 'node:fs/promises' +import dedent from 'dedent' +import { resolve } from 'pathe' +import { afterEach, beforeEach, expect, test, vi } from 'vitest' + +import { createFixture, typecheck, watchConsole } from '../../test/utils.js' +import { generate } from './generate.js' + +let console: ReturnType +beforeEach(() => { + console = watchConsole() + vi.useFakeTimers() + + const date = new Date(2023, 0, 30, 12) + vi.setSystemTime(date) +}) + +afterEach(() => { + vi.restoreAllMocks() + vi.useRealTimers() +}) + +test('generates output', async () => { + const { dir } = await createFixture({ + files: { + tsconfig: true, + 'wagmi.config.js': dedent` + export default { + out: 'generated.js', + contracts: [ + { + abi: [], + name: 'Foo', + }, + ], + } + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await generate() + + expect(console.formatted).toMatchInlineSnapshot(` + "- Validating plugins + √ Validating plugins + - Resolving contracts + √ Resolving contracts + - Running plugins + √ Running plugins + - Writing to generated.js + √ Writing to generated.js" + `) +}) + +test('generates typescript output', async () => { + const { dir, paths } = await createFixture({ + files: { + tsconfig: true, + 'wagmi.config.ts': dedent` + export default { + out: 'generated.ts', + contracts: [ + { + abi: [], + name: 'Foo', + }, + ], + } + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await generate() + + expect(console.formatted).toMatchInlineSnapshot(` + "- Validating plugins + √ Validating plugins + - Resolving contracts + √ Resolving contracts + - Running plugins + √ Running plugins + - Writing to generated.ts + √ Writing to generated.ts" + `) + await expect(typecheck(paths.tsconfig)).resolves.toMatchInlineSnapshot('""') +}) + +test('generates output with plugin', async () => { + const { dir } = await createFixture({ + files: { + tsconfig: true, + 'wagmi.config.ts': dedent` + export default { + out: 'generated.ts', + contracts: [ + { + abi: [], + name: 'Foo', + }, + ], + plugins: [ + { + name: 'Test', + async run({ contracts, isTypeScript, outputs }) { + return { + imports: '/* imports test */', + prepend: '/* prepend test */', + content: '/* content test */', + } + }, + }, + ], + } + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await generate() + + expect(console.formatted).toMatchInlineSnapshot(` + "- Validating plugins + √ Validating plugins + - Resolving contracts + √ Resolving contracts + - Running plugins + √ Running plugins + - Writing to generated.ts + √ Writing to generated.ts" + `) + /* eslint-disable no-irregular-whitespace */ + await expect( + readFile(resolve(dir, 'generated.ts'), 'utf8'), + ).resolves.toMatchInlineSnapshot(` + "/* imports test */ + + /* prepend test */ + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Foo + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + export const fooAbi = [] as const + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Test + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /* content test */ + " + `) + /* eslint-enable no-irregular-whitespace */ +}) + +test('behavior: invalid cli options', async () => { + const { dir } = await createFixture() + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect( + generate({ + // @ts-expect-error possible to pass untyped options through from cli + config: 1, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid option + - Expected string, received number at \`config\`] + `) +}) + +test('behavior: config not found', async () => { + const { dir } = await createFixture() + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(generate()).rejects.toThrowErrorMatchingInlineSnapshot( + '[Error: Config not found]', + ) +}) + +test('behavior: config not found for path', async () => { + const { dir } = await createFixture() + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + try { + await generate({ config: 'wagmi.config.js' }) + } catch (error) { + expect( + (error as Error).message.replace(dir, 'path/to/project'), + ).toMatchInlineSnapshot('"Config not found at wagmi.config.js"') + } +}) + +test('behavior: config out not unique', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.js': dedent` + export default [ + { + out: 'generated.ts', + contracts: [ + { + abi: [], + name: 'Foo', + }, + ] + }, + { + out: 'generated.ts', + contracts: [ + { + abi: [], + name: 'Foo', + }, + ], + }, + ] + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(generate()).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: out "generated.ts" must be unique.]`, + ) +}) + +test('behavior: config contract names not unique', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.js': dedent` + export default { + out: 'generated.ts', + contracts: [ + { + abi: [], + name: 'Foo', + }, + { + abi: [], + name: 'Foo', + }, + ], + } + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(generate()).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Contract name "Foo" must be unique.]`, + ) +}) + +test('behavior: displays message if no contracts found', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.js': "export default { out: 'generated.ts' }", + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await generate() + + expect(console.formatted).toMatchInlineSnapshot( + ` + "- Validating plugins + √ Validating plugins + - Resolving contracts + × Resolving contracts + No contracts found." + `, + ) +}) + +test('behavior: throws when abi is invalid', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.js': dedent` + export default { + out: 'generated.ts', + contracts: [ + { + abi: [{ + type: 'function', + name: 'balanceOf', + stateMutability: 'view', + inputs: [{ type: 'address' }], + }], + name: 'Foo', + }, + ], + } + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(generate()).rejects.toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid ABI for contract "Foo" + - Invalid input at \`[0]\`] + `) +}) + +test('behavior: throws when address is invalid', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.js': dedent` + export default { + out: 'generated.ts', + contracts: [ + { + abi: [], + address: '0xfoo', + name: 'Foo', + }, + ], + } + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(generate()).rejects.toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid address for contract "Foo" + - Invalid address] + `) +}) + +test('behavior: throws when multichain address is invalid', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.js': dedent` + export default { + out: 'generated.ts', + contracts: [ + { + abi: [], + address: { + 1: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + 5: '0xfoo', + }, + name: 'Foo', + }, + ], + } + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(generate()).rejects.toThrowErrorMatchingInlineSnapshot(` + [Error: Invalid address for contract "Foo" + - Invalid address at \`5\`] + `) +}) + +test('behavior: displays message if using --watch flag without watchers configured', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.js': dedent` + export default { + out: 'generated.ts', + contracts: [ + { + abi: [], + name: 'Foo', + }, + ], + } + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await generate({ watch: true }) + + expect(console.formatted).toMatchInlineSnapshot(` + "- Validating plugins + √ Validating plugins + - Resolving contracts + √ Resolving contracts + - Running plugins + √ Running plugins + - Writing to generated.ts + √ Writing to generated.ts + Used --watch flag, but no plugins are watching." + `) +}) + +test.todo('behavior: save config file logs change') +test.todo('behavior: updates on add file') +test.todo('behavior: updates on change file') +test.todo('behavior: updates on unlink file') +test.todo('behavior: runs watch command') +test.todo('behavior: shuts down watch on SIGINT/SIGTERM') diff --git a/packages/cli/src/commands/generate.ts b/packages/cli/src/commands/generate.ts new file mode 100644 index 0000000000..992a2e2923 --- /dev/null +++ b/packages/cli/src/commands/generate.ts @@ -0,0 +1,409 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { Abi as AbiSchema } from 'abitype/zod' +import { camelCase } from 'change-case' +import type { ChokidarOptions, FSWatcher } from 'chokidar' +import { watch } from 'chokidar' +import { default as dedent } from 'dedent' +import { basename, dirname, resolve } from 'pathe' +import pc from 'picocolors' +import { type Abi, type Address, getAddress } from 'viem' +import { z } from 'zod' + +import type { Contract, ContractConfig, Plugin, Watch } from '../config.js' +import { fromZodError } from '../errors.js' +import * as logger from '../logger.js' +import { findConfig } from '../utils/findConfig.js' +import { format } from '../utils/format.js' +import { getAddressDocString } from '../utils/getAddressDocString.js' +import { getIsUsingTypeScript } from '../utils/getIsUsingTypeScript.js' +import { resolveConfig } from '../utils/resolveConfig.js' + +const Generate = z.object({ + /** Path to config file */ + config: z.string().optional(), + /** Directory to search for config file */ + root: z.string().optional(), + /** Watch for file system changes to config and plugins */ + watch: z.boolean().optional(), +}) +export type Generate = z.infer + +export async function generate(options: Generate = {}) { + // Validate command line options + try { + await Generate.parseAsync(options) + } catch (error) { + if (error instanceof z.ZodError) + throw fromZodError(error, { prefix: 'Invalid option' }) + throw error + } + + // Get cli config file + const configPath = await findConfig(options) + if (!configPath) { + if (options.config) + throw new Error(`Config not found at ${pc.gray(options.config)}`) + throw new Error('Config not found') + } + + const resolvedConfigs = await resolveConfig({ configPath }) + const isTypeScript = await getIsUsingTypeScript() + + type Watcher = FSWatcher & { config?: Watch } + const watchers: Watcher[] = [] + const watchWriteDelay = 100 + const watchOptions = { + atomic: true, + // awaitWriteFinish: true, + ignoreInitial: true, + persistent: true, + } satisfies ChokidarOptions + + const outNames = new Set() + const isArrayConfig = Array.isArray(resolvedConfigs) + const configs = isArrayConfig ? resolvedConfigs : [resolvedConfigs] + for (const config of configs) { + if (isArrayConfig) + logger.log(`Using config ${pc.gray(basename(configPath))}`) + if (!config.out) throw new Error('out is required.') + if (outNames.has(config.out)) + throw new Error(`out "${config.out}" must be unique.`) + outNames.add(config.out) + + // Collect contracts and watch configs from plugins + const plugins = (config.plugins ?? []).map((x, i) => ({ + ...x, + id: `${x.name}-${i}`, + })) + const spinner = logger.spinner('Validating plugins') + spinner.start() + for (const plugin of plugins) { + await plugin.validate?.() + } + spinner.success() + + // Add plugin contracts to config contracts + const contractConfigs = config.contracts ?? [] + const watchConfigs: Watch[] = [] + spinner.start('Resolving contracts') + for (const plugin of plugins) { + if (plugin.watch) watchConfigs.push(plugin.watch) + if (plugin.contracts) { + const contracts = await plugin.contracts() + contractConfigs.push(...contracts) + } + } + + // Get contracts from config + const contractNames = new Set() + const contractMap = new Map() + for (const contractConfig of contractConfigs) { + if (contractNames.has(contractConfig.name)) + throw new Error( + `Contract name "${contractConfig.name}" must be unique.`, + ) + const contract = await getContract({ ...contractConfig, isTypeScript }) + contractMap.set(contract.name, contract) + + contractNames.add(contractConfig.name) + } + + // Sort contracts by name Ascending (low to high) as the key is `String` + const sortedAscContractMap = new Map([...contractMap].sort()) + const contracts = [...sortedAscContractMap.values()] + if (!contracts.length && !options.watch) { + spinner.error() + logger.warn('No contracts found.') + return + } + spinner.success() + + // Run plugins + const imports = [] + const prepend = [] + const content = [] + type Output = { + plugin: Pick + } & Awaited>> + const outputs: Output[] = [] + spinner.start('Running plugins') + for (const plugin of plugins) { + if (!plugin.run) continue + const result = await plugin.run({ + contracts, + isTypeScript, + outputs, + }) + outputs.push({ + plugin: { name: plugin.name }, + ...result, + }) + if (!result.imports && !result.prepend && !result.content) continue + content.push(getBannerContent({ name: plugin.name }), result.content) + result.imports && imports.push(result.imports) + result.prepend && prepend.push(result.prepend) + } + spinner.success() + + // Write output to file + spinner.start(`Writing to ${pc.gray(config.out)}`) + await writeContracts({ + content, + contracts, + imports, + prepend, + filename: config.out, + }) + spinner.success() + + if (options.watch) { + if (!watchConfigs.length) { + logger.log(pc.gray('Used --watch flag, but no plugins are watching.')) + continue + } + logger.log() + logger.log('Setting up watch process') + + // Watch for changes + let timeout: NodeJS.Timeout | null + for (const watchConfig of watchConfigs) { + const paths = + typeof watchConfig.paths === 'function' + ? await watchConfig.paths() + : watchConfig.paths + const watcher = watch(paths, watchOptions) + // Watch for changes to files, new files, and deleted files + watcher.on('all', async (event, path) => { + if (event !== 'change' && event !== 'add' && event !== 'unlink') + return + + let needsWrite = false + if (event === 'change' || event === 'add') { + const eventFn = + event === 'change' ? watchConfig.onChange : watchConfig.onAdd + const config = await eventFn?.(path) + if (!config) return + const contract = await getContract({ ...config, isTypeScript }) + contractMap.set(contract.name, contract) + needsWrite = true + } else if (event === 'unlink') { + const name = await watchConfig.onRemove?.(path) + if (!name) return + contractMap.delete(name) + needsWrite = true + } + + // Debounce writes + if (needsWrite) { + if (timeout) clearTimeout(timeout) + timeout = setTimeout(async () => { + timeout = null + // Sort contracts by name Ascending (low to high) as the key is `String` + const sortedAscContractMap = new Map([...contractMap].sort()) + const contracts = [...sortedAscContractMap.values()] + const imports = [] + const prepend = [] + const content = [] + const outputs: Output[] = [] + for (const plugin of plugins) { + if (!plugin.run) continue + const result = await plugin.run({ + contracts, + isTypeScript, + outputs, + }) + outputs.push({ + plugin: { name: plugin.name }, + ...result, + }) + if (!result.imports && !result.prepend && !result.content) + continue + content.push( + getBannerContent({ name: plugin.name }), + result.content, + ) + result.imports && imports.push(result.imports) + result.prepend && prepend.push(result.prepend) + } + + const spinner = logger.spinner( + `Writing to ${pc.gray(config.out)}`, + ) + spinner.start() + await writeContracts({ + content, + contracts, + imports, + prepend, + filename: config.out, + }) + spinner.success() + }, watchWriteDelay) + needsWrite = false + } + }) + + // Run parallel command on ready + if (watchConfig.command) + watcher.on('ready', async () => { + await watchConfig.command?.() + }) + ;(watcher as Watcher).config = watchConfig + watchers.push(watcher) + } + } + } + + if (!watchers.length) return + + // Watch `@wagmi/cli` config file for changes + const watcher = watch(configPath).on('change', async (path) => { + logger.log( + `> Found a change to config ${pc.gray( + basename(path), + )}. Restart process for changes to take effect.`, + ) + }) + watchers.push(watcher) + + // Display message and close watchers on exit + process.once('SIGINT', shutdown) + process.once('SIGTERM', shutdown) + async function shutdown() { + logger.log() + logger.log('Shutting down watch process') + const promises = [] + for (const watcher of watchers) { + if (watcher.config?.onClose) promises.push(watcher.config?.onClose?.()) + promises.push(watcher.close()) + } + await Promise.allSettled(promises) + process.exit(0) + } +} + +async function getContract({ + abi, + address, + name, + isTypeScript, +}: ContractConfig & { isTypeScript: boolean }): Promise { + const constAssertion = isTypeScript ? ' as const' : '' + const abiName = `${camelCase(name)}Abi` + try { + abi = (await AbiSchema.parseAsync(abi)) as Abi + } catch (error) { + if (error instanceof z.ZodError) + throw fromZodError(error, { + prefix: `Invalid ABI for contract "${name}"`, + }) + throw error + } + const docString = + typeof address === 'object' + ? dedent`\n + /** + ${getAddressDocString({ address })} + */ + ` + : '' + let content = dedent` + ${getBannerContent({ name })} + + ${docString} + export const ${abiName} = ${JSON.stringify(abi)}${constAssertion} + ` + + let meta: Contract['meta'] = { abiName } + if (address) { + let resolvedAddress: Address | Record + try { + const Address = z + .string() + .regex(/^0x[a-fA-F0-9]{40}$/, { message: 'Invalid address' }) + .transform((val) => getAddress(val)) as z.ZodType
+ const MultiChainAddress = z.record(z.string(), Address) + const AddressSchema = z.union([Address, MultiChainAddress]) + resolvedAddress = await AddressSchema.parseAsync(address) + } catch (error) { + if (error instanceof z.ZodError) + throw fromZodError(error, { + prefix: `Invalid address for contract "${name}"`, + }) + throw error + } + + const addressName = `${camelCase(name)}Address` + const configName = `${camelCase(name)}Config` + meta = { + ...meta, + addressName, + configName, + } + + const addressContent = + typeof resolvedAddress === 'string' + ? JSON.stringify(resolvedAddress) + : // Remove quotes from chain id key + JSON.stringify(resolvedAddress, null, 2).replace(/"(\d*)":/gm, '$1:') + content = dedent` + ${content} + + ${docString} + export const ${addressName} = ${addressContent}${constAssertion} + + ${docString} + export const ${configName} = { address: ${addressName}, abi: ${abiName} }${constAssertion} + ` + } + + return { abi, address, content, meta, name } +} + +async function writeContracts({ + content, + contracts, + imports, + prepend, + filename, +}: { + content: string[] + contracts: Contract[] + imports: string[] + prepend: string[] + filename: string +}) { + // Assemble code + let code = dedent` + ${imports.join('\n\n') ?? ''} + + ${prepend.join('\n\n') ?? ''} + ` + for (const contract of contracts) { + code = dedent` + ${code} + + ${contract.content} + ` + } + code = dedent` + ${code} + + ${content.join('\n\n') ?? ''} + ` + + // Format and write output + const cwd = process.cwd() + const outPath = resolve(cwd, filename) + await mkdir(dirname(outPath), { recursive: true }) + const formatted = await format(code) + await writeFile(outPath, formatted) +} + +function getBannerContent({ name }: { name: string }) { + return dedent` + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ${name} + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ` +} diff --git a/packages/cli/src/commands/init.test.ts b/packages/cli/src/commands/init.test.ts new file mode 100644 index 0000000000..ed4a1d1644 --- /dev/null +++ b/packages/cli/src/commands/init.test.ts @@ -0,0 +1,189 @@ +import { existsSync } from 'node:fs' +import { mkdir, readFile } from 'node:fs/promises' +import { resolve } from 'pathe' +import { afterEach, beforeEach, expect, test, vi } from 'vitest' + +import { createFixture, watchConsole } from '../../test/utils.js' +import { defaultConfig } from '../config.js' +import { init } from './init.js' + +let console: ReturnType +beforeEach(() => { + console = watchConsole() +}) + +afterEach(() => { + vi.restoreAllMocks() +}) + +test('creates config file', async () => { + const { dir } = await createFixture() + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + const configFile = await init() + + expect(existsSync(configFile)).toBeTruthy() + expect(await readFile(configFile, 'utf-8')).toMatchInlineSnapshot(` + "// @ts-check + + /** @type {import('@wagmi/cli').Config} */ + export default { + out: 'src/generated.js', + contracts: [], + plugins: [], + } + " + `) + expect( + console.formatted.replaceAll(dir, 'path/to/project'), + ).toMatchInlineSnapshot(` + "- Creating config + √ Creating config + Config created at wagmi.config.js" + `) +}) + +test('parameters: config', async () => { + const { dir } = await createFixture() + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + const configFile = await init({ + config: 'foo.config.ts', + }) + + expect(existsSync(configFile)).toBeTruthy() + expect(await readFile(configFile, 'utf-8')).toMatchInlineSnapshot(` + "// @ts-check + + /** @type {import('@wagmi/cli').Config} */ + export default { + out: 'src/generated.js', + contracts: [], + plugins: [], + } + " + `) + expect( + console.formatted.replaceAll(dir, 'path/to/project'), + ).toMatchInlineSnapshot(` + "- Creating config + √ Creating config + Config created at foo.config.ts" + `) +}) + +test('parameters: content', async () => { + const { dir } = await createFixture({ + files: { + 'tsconfig.json': '{}', + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + const configFile = await init({ + content: { + ...defaultConfig, + out: 'foo/bar/baz.ts', + }, + }) + + expect(existsSync(configFile)).toBeTruthy() + expect(await readFile(configFile, 'utf-8')).toMatchInlineSnapshot(` + "import { defineConfig } from '@wagmi/cli' + + export default defineConfig({ + out: 'foo/bar/baz.ts', + contracts: [], + plugins: [], + }) + " + `) + expect( + console.formatted.replaceAll(dir, 'path/to/project'), + ).toMatchInlineSnapshot(` + "- Creating config + √ Creating config + Config created at wagmi.config.ts" + `) +}) + +test('parameters: root', async () => { + const { dir } = await createFixture() + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + mkdir(resolve(dir, 'foo')) + + const configFile = await init({ + root: 'foo/', + }) + + expect(existsSync(configFile)).toBeTruthy() + expect(await readFile(configFile, 'utf-8')).toMatchInlineSnapshot(` + "// @ts-check + + /** @type {import('@wagmi/cli').Config} */ + export default { + out: 'src/generated.js', + contracts: [], + plugins: [], + } + " + `) + expect( + console.formatted.replaceAll(dir, 'path/to/project'), + ).toMatchInlineSnapshot(` + "- Creating config + √ Creating config + Config created at foo/wagmi.config.js" + `) +}) + +test('behavior: creates config file in TypeScript format', async () => { + const { dir } = await createFixture({ + files: { + 'tsconfig.json': '{}', + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + const configFile = await init() + + expect(existsSync(configFile)).toBeTruthy() + expect(await readFile(configFile, 'utf-8')).toMatchInlineSnapshot(` + "import { defineConfig } from '@wagmi/cli' + + export default defineConfig({ + out: 'src/generated.ts', + contracts: [], + plugins: [], + }) + " + `) + expect( + console.formatted.replaceAll(dir, 'path/to/project'), + ).toMatchInlineSnapshot(` + "- Creating config + √ Creating config + Config created at wagmi.config.ts" + `) +}) + +test('behavior: displays config file location when config exists', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.ts': '', + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + const configFile = await init() + + expect( + console.formatted.replaceAll(configFile, 'path/to/project/wagmi.config.ts'), + ).toMatchInlineSnapshot('"Config already exists at wagmi.config.ts"') +}) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts new file mode 100644 index 0000000000..ce4e5b32d9 --- /dev/null +++ b/packages/cli/src/commands/init.ts @@ -0,0 +1,95 @@ +import { writeFile } from 'node:fs/promises' +import dedent from 'dedent' +import { relative, resolve } from 'pathe' +import pc from 'picocolors' +import { z } from 'zod' + +import { type Config, defaultConfig } from '../config.js' +import { fromZodError } from '../errors.js' +import * as logger from '../logger.js' +import { findConfig } from '../utils/findConfig.js' +import { format } from '../utils/format.js' +import { getIsUsingTypeScript } from '../utils/getIsUsingTypeScript.js' + +export type Init = { + /** Path to config file */ + config?: string + /** Watch for file system changes to config and plugins */ + content?: Config + /** Directory to init config file */ + root?: string +} + +const Init = z.object({ + config: z.string().optional(), + content: z.object({}).optional(), + root: z.string().optional(), +}) + +export async function init(options: Init = {}) { + // Validate command line options + try { + await Init.parseAsync(options) + } catch (error) { + if (error instanceof z.ZodError) + throw fromZodError(error, { prefix: 'Invalid option' }) + throw error + } + + // Check for existing config file + const configPath = await findConfig(options) + if (configPath) { + logger.info( + `Config already exists at ${pc.gray( + relative(process.cwd(), configPath), + )}`, + ) + return configPath + } + + const spinner = logger.spinner('Creating config') + spinner.start() + // Check if project is using TypeScript + const isUsingTypeScript = await getIsUsingTypeScript() + const rootDir = resolve(options.root || process.cwd()) + let outPath: string + if (options.config) { + outPath = resolve(rootDir, options.config) + } else { + const extension = isUsingTypeScript ? 'ts' : 'js' + outPath = resolve(rootDir, `wagmi.config.${extension}`) + } + + let content: string + if (isUsingTypeScript) { + const config = options.content ?? defaultConfig + content = dedent(` + import { defineConfig } from '@wagmi/cli' + + export default defineConfig(${JSON.stringify(config)}) + `) + } else { + const config = options.content ?? { + ...defaultConfig, + out: defaultConfig.out.replace('.ts', '.js'), + } + content = dedent(` + // @ts-check + + /** @type {import('@wagmi/cli').Config} */ + export default ${JSON.stringify(config, null, 2).replace( + /"(\d*)":/gm, + '$1:', + )} + `) + } + + const formatted = await format(content) + await writeFile(outPath, formatted) + spinner.success() + logger.success( + `Config created at ${pc.gray(relative(process.cwd(), outPath))}`, + ) + + return outPath +} diff --git a/packages/cli/src/config.test.ts b/packages/cli/src/config.test.ts new file mode 100644 index 0000000000..f95d7cb6d3 --- /dev/null +++ b/packages/cli/src/config.test.ts @@ -0,0 +1,39 @@ +import { expect, test, vi } from 'vitest' + +import { type Config, defineConfig } from './config.js' + +test('object', () => { + const config: Config = { + contracts: [], + out: 'wagmi.ts', + plugins: [], + } + expect(defineConfig(config)).toEqual(config) +}) + +test('array', () => { + const config: Config = { + contracts: [], + out: 'wagmi.ts', + plugins: [], + } + expect(defineConfig([config, config])).toEqual([config, config]) +}) + +test('function', () => { + const config = vi.fn().mockImplementation(() => ({ + contracts: [], + out: 'wagmi.ts', + plugins: [], + })) + expect(defineConfig(config)).toEqual(config) +}) + +test('async function', () => { + const config = vi.fn().mockImplementation(async () => ({ + contracts: [], + out: 'wagmi.ts', + plugins: [], + })) + expect(defineConfig(config)).toEqual(config) +}) diff --git a/packages/cli/src/config.ts b/packages/cli/src/config.ts new file mode 100644 index 0000000000..146a1e3424 --- /dev/null +++ b/packages/cli/src/config.ts @@ -0,0 +1,121 @@ +import type { Abi, Address } from 'viem' + +import type { Compute, MaybeArray, MaybePromise } from './types.js' + +export type ContractConfig< + chainId extends number = number, + requiredChainId extends number | undefined = undefined, +> = { + /** + * Contract ABI + */ + abi: Abi + /** + * Contract address or addresses. + * + * Accepts an object `{ [chainId]: address }` to support multiple chains. + * + * @example + * '0x314159265dd8dbb310642f98f50c066173c1259b' + * + * @example + * { + * 1: '0x314159265dd8dbb310642f98f50c066173c1259b', + * 5: '0x112234455c3a32fd11230c42e7bccd4a84e02010', + * } + */ + address?: + | Address + | (requiredChainId extends number + ? Record & Partial> + : Record) + | undefined + /** + * Name of contract. + */ + name: string +} + +export type Contract = Compute< + ContractConfig & { + /** Generated string content */ + content: string + /** Meta info about contract */ + meta: { + abiName: string + addressName?: string | undefined + configName?: string | undefined + } + } +> + +export type Watch = { + /** Command to run along with watch process */ + command?: (() => MaybePromise) | undefined + /** Paths to watch for changes. */ + paths: string[] | (() => MaybePromise) + /** Callback that fires when file is added */ + onAdd?: + | ((path: string) => MaybePromise) + | undefined + /** Callback that fires when file changes */ + onChange: (path: string) => MaybePromise + /** Callback that fires when watcher is shutdown */ + onClose?: (() => MaybePromise) | undefined + /** Callback that fires when file is removed */ + onRemove?: ((path: string) => MaybePromise) | undefined +} + +export type Plugin = { + /** Contracts provided by plugin */ + contracts?: (() => MaybePromise) | undefined + /** Plugin name */ + name: string + /** Run plugin logic */ + run?: + | ((config: { + /** All resolved contracts from config and plugins */ + contracts: Contract[] + /** Whether TypeScript is detected in project */ + isTypeScript: boolean + /** Previous plugin outputs */ + outputs: readonly { + plugin: Pick + imports?: string + prepend?: string + content: string + }[] + }) => MaybePromise<{ + imports?: string + prepend?: string + content: string + }>) + | undefined + /** + * Validate plugin configuration or other @wagmi/cli settings require for plugin. + */ + validate?: (() => MaybePromise) | undefined + /** File system watch config */ + watch?: Watch | undefined +} + +export type Config = { + /** Contracts to use in commands */ + contracts?: ContractConfig[] | undefined + /** Output file path */ + out: string + /** Plugins to run */ + plugins?: Plugin[] | undefined +} + +export function defineConfig( + config: MaybeArray | (() => MaybePromise>), +) { + return config +} + +export const defaultConfig = { + out: 'src/generated.ts', + contracts: [], + plugins: [], +} satisfies Config diff --git a/packages/cli/src/errors.ts b/packages/cli/src/errors.ts new file mode 100644 index 0000000000..6ef37093fc --- /dev/null +++ b/packages/cli/src/errors.ts @@ -0,0 +1,57 @@ +import type { z } from 'zod' + +class ValidationError extends Error { + details: Zod.ZodIssue[] + + constructor( + message: string, + options: { + details: Zod.ZodIssue[] + }, + ) { + super(message) + this.details = options.details + } +} + +// From https://github.com/causaly/zod-validation-error +export function fromZodError( + zError: z.ZodError, + { + maxIssuesInMessage = 99, + issueSeparator = '\n- ', + prefixSeparator = '\n- ', + prefix = 'Validation Error', + }: { + maxIssuesInMessage?: number + issueSeparator?: string + prefixSeparator?: string + prefix?: string + } = {}, +): ValidationError { + function joinPath(arr: Array): string { + return arr.reduce((acc, value) => { + if (typeof value === 'number') return `${acc}[${value}]` + const separator = acc === '' ? '' : '.' + return acc + separator + value + }, '') + } + + const reason = zError.errors + // limit max number of issues printed in the reason section + .slice(0, maxIssuesInMessage) + // format error message + .map((issue) => { + const { message, path } = issue + if (path.length > 0) return `${message} at \`${joinPath(path)}\`` + return message + }) + // concat as string + .join(issueSeparator) + + const message = reason ? [prefix, reason].join(prefixSeparator) : prefix + + return new ValidationError(message, { + details: zError.errors, + }) +} diff --git a/packages/cli/src/exports/config.test.ts b/packages/cli/src/exports/config.test.ts new file mode 100644 index 0000000000..c833780ffc --- /dev/null +++ b/packages/cli/src/exports/config.test.ts @@ -0,0 +1,12 @@ +import { expect, test } from 'vitest' + +import * as Exports from './config.js' + +test('exports', () => { + expect(Object.keys(Exports)).toMatchInlineSnapshot(` + [ + "defineConfig", + "defaultConfig", + ] + `) +}) diff --git a/packages/cli/src/exports/config.ts b/packages/cli/src/exports/config.ts new file mode 100644 index 0000000000..b3c4a83ba4 --- /dev/null +++ b/packages/cli/src/exports/config.ts @@ -0,0 +1,10 @@ +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type ContractConfig, + type Contract, + type Watch, + type Plugin, + type Config, + defineConfig, + defaultConfig, +} from '../config.js' diff --git a/packages/cli/src/exports/index.test-d.ts b/packages/cli/src/exports/index.test-d.ts new file mode 100644 index 0000000000..b056d56358 --- /dev/null +++ b/packages/cli/src/exports/index.test-d.ts @@ -0,0 +1,4 @@ +import { expectTypeOf } from 'vitest' + +// noop test because vitest typecheck fails unless each workspace project has type test +expectTypeOf(1).toEqualTypeOf() diff --git a/packages/cli/src/exports/index.test.ts b/packages/cli/src/exports/index.test.ts new file mode 100644 index 0000000000..2da78e8da1 --- /dev/null +++ b/packages/cli/src/exports/index.test.ts @@ -0,0 +1,14 @@ +import { expect, test } from 'vitest' + +import * as Exports from './index.js' + +test('exports', () => { + expect(Object.keys(Exports)).toMatchInlineSnapshot(` + [ + "defineConfig", + "logger", + "loadEnv", + "version", + ] + `) +}) diff --git a/packages/cli/src/exports/index.ts b/packages/cli/src/exports/index.ts new file mode 100644 index 0000000000..1c5e624df6 --- /dev/null +++ b/packages/cli/src/exports/index.ts @@ -0,0 +1,14 @@ +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + defineConfig, + type Config, + type ContractConfig, + type Plugin, +} from '../config.js' + +// biome-ignore lint/performance/noReExportAll: entrypoint module +export * as logger from '../logger.js' + +export { loadEnv } from '../utils/loadEnv.js' + +export { version } from '../version.js' diff --git a/packages/cli/src/exports/plugins.test.ts b/packages/cli/src/exports/plugins.test.ts new file mode 100644 index 0000000000..4d7b5a97cd --- /dev/null +++ b/packages/cli/src/exports/plugins.test.ts @@ -0,0 +1,20 @@ +import { expect, test } from 'vitest' + +import * as Exports from './plugins.js' + +test('exports', () => { + expect(Object.keys(Exports)).toMatchInlineSnapshot(` + [ + "actions", + "blockExplorer", + "etherscan", + "fetch", + "foundry", + "foundryDefaultExcludes", + "hardhat", + "hardhatDefaultExcludes", + "react", + "sourcify", + ] + `) +}) diff --git a/packages/cli/src/exports/plugins.ts b/packages/cli/src/exports/plugins.ts new file mode 100644 index 0000000000..a289b5c576 --- /dev/null +++ b/packages/cli/src/exports/plugins.ts @@ -0,0 +1,27 @@ +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { actions, type ActionsConfig } from '../plugins/actions.js' + +export { + blockExplorer, + type BlockExplorerConfig, +} from '../plugins/blockExplorer.js' + +export { etherscan, type EtherscanConfig } from '../plugins/etherscan.js' + +export { fetch, type FetchConfig } from '../plugins/fetch.js' + +export { + foundry, + foundryDefaultExcludes, + type FoundryConfig, +} from '../plugins/foundry.js' + +export { + hardhat, + hardhatDefaultExcludes, + type HardhatConfig, +} from '../plugins/hardhat.js' + +export { react, type ReactConfig } from '../plugins/react.js' + +export { sourcify, type SourcifyConfig } from '../plugins/sourcify.js' diff --git a/packages/cli/src/logger.test.ts b/packages/cli/src/logger.test.ts new file mode 100644 index 0000000000..7338c3bb14 --- /dev/null +++ b/packages/cli/src/logger.test.ts @@ -0,0 +1,32 @@ +import { afterEach, expect, test, vi } from 'vitest' + +import { watchConsole } from '../test/utils.js' + +import * as logger from './logger.js' + +const mockLog = vi.fn() + +afterEach(() => { + vi.restoreAllMocks() +}) + +test.each(['success', 'info', 'log', 'warn', 'error'])('%s()', (level) => { + const spy = vi.spyOn(logger, level as any) + spy.mockImplementation(mockLog) + const loggerFn = (logger as any)[level] + loggerFn(level) + expect(spy).toHaveBeenCalledWith(level) +}) + +test('spinner', () => { + const console = watchConsole() + const spinner = logger.spinner('start') + spinner.start() + spinner.success('success') + spinner.error('error') + expect(console.formatted).toMatchInlineSnapshot(` + "- start + √ success + × error" + `) +}) diff --git a/packages/cli/src/logger.ts b/packages/cli/src/logger.ts new file mode 100644 index 0000000000..b56fb9728b --- /dev/null +++ b/packages/cli/src/logger.ts @@ -0,0 +1,37 @@ +import { format as utilFormat } from 'node:util' +import { createSpinner } from 'nanospinner' +import pc from 'picocolors' + +function format(args: any[]) { + return utilFormat(...args) + .split('\n') + .join('\n') +} + +export function success(...args: any[]) { + // biome-ignore lint/suspicious/noConsoleLog: console.log is used for logging + console.log(pc.green(format(args))) +} + +export function info(...args: any[]) { + console.info(pc.blue(format(args))) +} + +export function log(...args: any[]) { + // biome-ignore lint/suspicious/noConsoleLog: console.log is used for logging + console.log(pc.white(format(args))) +} + +export function warn(...args: any[]) { + console.warn(pc.yellow(format(args))) +} + +export function error(...args: any[]) { + console.error(pc.red(format(args))) +} + +export function spinner(text: string) { + return createSpinner(text, { + color: 'yellow', + }) +} diff --git a/packages/cli/src/plugins/__fixtures__/foundry/.gitignore b/packages/cli/src/plugins/__fixtures__/foundry/.gitignore new file mode 100644 index 0000000000..3269660cc7 --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/foundry/.gitignore @@ -0,0 +1,11 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Dotenv file +.env diff --git a/packages/cli/src/plugins/__fixtures__/foundry/foundry.toml b/packages/cli/src/plugins/__fixtures__/foundry/foundry.toml new file mode 100644 index 0000000000..59374b16cf --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/foundry/foundry.toml @@ -0,0 +1,7 @@ +[profile.default] +libs = ['lib'] +out = 'out' +solc = '0.8.13' +src = 'src' + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/packages/cli/src/plugins/__fixtures__/foundry/src/Counter.sol b/packages/cli/src/plugins/__fixtures__/foundry/src/Counter.sol new file mode 100644 index 0000000000..5242caa433 --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/foundry/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/packages/cli/src/plugins/__fixtures__/foundry/src/Foo.sol b/packages/cli/src/plugins/__fixtures__/foundry/src/Foo.sol new file mode 100644 index 0000000000..f478736520 --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/foundry/src/Foo.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Foo { + string public bar; + + function setFoo(string memory baz) public { + bar = baz; + } +} + diff --git a/packages/cli/src/plugins/__fixtures__/hardhat/.gitignore b/packages/cli/src/plugins/__fixtures__/hardhat/.gitignore new file mode 100644 index 0000000000..85d361b914 --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/hardhat/.gitignore @@ -0,0 +1,10 @@ +node_modules +.env +coverage +coverage.json +typechain +typechain-types + +# Hardhat files +cache +artifacts \ No newline at end of file diff --git a/packages/cli/src/plugins/__fixtures__/hardhat/contracts/Counter.sol b/packages/cli/src/plugins/__fixtures__/hardhat/contracts/Counter.sol new file mode 100644 index 0000000000..5242caa433 --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/hardhat/contracts/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/packages/cli/src/plugins/__fixtures__/hardhat/contracts/Foo.sol b/packages/cli/src/plugins/__fixtures__/hardhat/contracts/Foo.sol new file mode 100644 index 0000000000..699a63ce0f --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/hardhat/contracts/Foo.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Foo { + string public bar; + + function setFoo(string memory baz) public { + bar = baz; + } +} diff --git a/packages/cli/src/plugins/__fixtures__/hardhat/hardhat.config.js b/packages/cli/src/plugins/__fixtures__/hardhat/hardhat.config.js new file mode 100644 index 0000000000..c8126eedfa --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/hardhat/hardhat.config.js @@ -0,0 +1,3 @@ +module.exports = { + solidity: '0.8.17', +} diff --git a/packages/cli/src/plugins/__fixtures__/hardhat/package.json b/packages/cli/src/plugins/__fixtures__/hardhat/package.json new file mode 100644 index 0000000000..85c9ffb7bd --- /dev/null +++ b/packages/cli/src/plugins/__fixtures__/hardhat/package.json @@ -0,0 +1,7 @@ +{ + "name": "hardhat-fixture", + "private": true, + "devDependencies": { + "hardhat": "^2.22.3" + } +} diff --git a/packages/cli/src/plugins/__snapshots__/blockExplorer.test.ts.snap b/packages/cli/src/plugins/__snapshots__/blockExplorer.test.ts.snap new file mode 100644 index 0000000000..2abd351741 --- /dev/null +++ b/packages/cli/src/plugins/__snapshots__/blockExplorer.test.ts.snap @@ -0,0 +1,736 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`fetches ABI 1`] = ` +[ + { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Approval", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "ApprovalForAll", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Transfer", + "type": "event", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4", + }, + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "pure", + "type": "function", + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": "0xaf0326d92b97df1221759476b072abfd8084f9be", + "name": "WagmiMintExample", + }, +] +`; + +exports[`fetches ABI with multichain deployment 1`] = ` +[ + { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Approval", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "ApprovalForAll", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Transfer", + "type": "event", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4", + }, + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "pure", + "type": "function", + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": { + "1": "0xaf0326d92b97df1221759476b072abfd8084f9be", + "10": "0xaf0326d92b97df1221759476b072abfd8084f9be", + }, + "name": "WagmiMintExample", + }, +] +`; diff --git a/packages/cli/src/plugins/__snapshots__/etherscan.test.ts.snap b/packages/cli/src/plugins/__snapshots__/etherscan.test.ts.snap new file mode 100644 index 0000000000..e03ee30f81 --- /dev/null +++ b/packages/cli/src/plugins/__snapshots__/etherscan.test.ts.snap @@ -0,0 +1,1238 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`fetches ABI 1`] = ` +[ + { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Approval", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "ApprovalForAll", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Transfer", + "type": "event", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4", + }, + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "pure", + "type": "function", + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": { + "1": "0xaf0326d92b97df1221759476b072abfd8084f9be", + }, + "name": "WagmiMintExample", + }, +] +`; + +exports[`fetches ABI with multichain deployment 1`] = ` +[ + { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Approval", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "ApprovalForAll", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Transfer", + "type": "event", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4", + }, + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "pure", + "type": "function", + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": { + "1": "0xaf0326d92b97df1221759476b072abfd8084f9be", + "10": "0xaf0326d92b97df1221759476b072abfd8084f9be", + }, + "name": "WagmiMintExample", + }, +] +`; + +exports[`tryFetchProxyImplementation: fetches ABI 1`] = ` +[ + { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Approval", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "ApprovalForAll", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Transfer", + "type": "event", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4", + }, + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "pure", + "type": "function", + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": { + "1": "0xaf0326d92b97df1221759476b072abfd8084f9be", + }, + "name": "WagmiMintExample", + }, +] +`; + +exports[`tryFetchProxyImplementation: fetches implementation ABI 1`] = ` +[ + { + "abi": [ + { + "constant": false, + "inputs": [ + { + "name": "newImplementation", + "type": "address", + }, + ], + "name": "upgradeTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + }, + { + "constant": false, + "inputs": [ + { + "name": "newImplementation", + "type": "address", + }, + { + "name": "data", + "type": "bytes", + }, + ], + "name": "upgradeToAndCall", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function", + }, + { + "constant": true, + "inputs": [], + "name": "implementation", + "outputs": [ + { + "name": "", + "type": "address", + }, + ], + "payable": false, + "stateMutability": "view", + "type": "function", + }, + { + "constant": false, + "inputs": [ + { + "name": "newAdmin", + "type": "address", + }, + ], + "name": "changeAdmin", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + }, + { + "constant": true, + "inputs": [], + "name": "admin", + "outputs": [ + { + "name": "", + "type": "address", + }, + ], + "payable": false, + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "name": "_implementation", + "type": "address", + }, + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "previousAdmin", + "type": "address", + }, + { + "indexed": false, + "name": "newAdmin", + "type": "address", + }, + ], + "name": "AdminChanged", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "implementation", + "type": "address", + }, + ], + "name": "Upgraded", + "type": "event", + }, + ], + "address": { + "1": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + }, + "name": "FiatToken", + }, +] +`; diff --git a/packages/cli/src/plugins/__snapshots__/fetch.test.ts.snap b/packages/cli/src/plugins/__snapshots__/fetch.test.ts.snap new file mode 100644 index 0000000000..83c4e81f53 --- /dev/null +++ b/packages/cli/src/plugins/__snapshots__/fetch.test.ts.snap @@ -0,0 +1,367 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`fetches ABI 1`] = ` +[ + { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Approval", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "ApprovalForAll", + "type": "event", + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "Transfer", + "type": "event", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address", + }, + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes", + }, + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address", + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool", + }, + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4", + }, + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string", + }, + ], + "stateMutability": "pure", + "type": "function", + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address", + }, + { + "internalType": "address", + "name": "to", + "type": "address", + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256", + }, + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": "0xaf0326d92b97df1221759476b072abfd8084f9be", + "name": "WagmiMintExample", + }, +] +`; diff --git a/packages/cli/src/plugins/__snapshots__/sourcify.test.ts.snap b/packages/cli/src/plugins/__snapshots__/sourcify.test.ts.snap new file mode 100644 index 0000000000..77e82fecde --- /dev/null +++ b/packages/cli/src/plugins/__snapshots__/sourcify.test.ts.snap @@ -0,0 +1,214 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`fetches ABI 1`] = ` +[ + { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "inputs": [ + { + "name": "pubkey", + "type": "bytes", + }, + { + "name": "withdrawal_credentials", + "type": "bytes", + }, + { + "name": "amount", + "type": "bytes", + }, + { + "name": "signature", + "type": "bytes", + }, + { + "name": "index", + "type": "bytes", + }, + ], + "name": "DepositEvent", + "type": "event", + }, + { + "inputs": [ + { + "name": "pubkey", + "type": "bytes", + }, + { + "name": "withdrawal_credentials", + "type": "bytes", + }, + { + "name": "signature", + "type": "bytes", + }, + { + "name": "deposit_data_root", + "type": "bytes32", + }, + ], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function", + }, + { + "inputs": [], + "name": "get_deposit_count", + "outputs": [ + { + "type": "bytes", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "get_deposit_root", + "outputs": [ + { + "type": "bytes32", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "name": "interfaceId", + "type": "bytes4", + }, + ], + "name": "supportsInterface", + "outputs": [ + { + "type": "bool", + }, + ], + "stateMutability": "pure", + "type": "function", + }, + ], + "address": { + "1": "0x00000000219ab540356cbb839cbe05303d7705fa", + }, + "name": "DepositContract", + }, +] +`; + +exports[`fetches ABI with multichain deployment 1`] = ` +[ + { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor", + }, + { + "inputs": [ + { + "name": "pubkey", + "type": "bytes", + }, + { + "name": "withdrawal_credentials", + "type": "bytes", + }, + { + "name": "amount", + "type": "bytes", + }, + { + "name": "signature", + "type": "bytes", + }, + { + "name": "index", + "type": "bytes", + }, + ], + "name": "DepositEvent", + "type": "event", + }, + { + "inputs": [ + { + "name": "pubkey", + "type": "bytes", + }, + { + "name": "withdrawal_credentials", + "type": "bytes", + }, + { + "name": "signature", + "type": "bytes", + }, + { + "name": "deposit_data_root", + "type": "bytes32", + }, + ], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function", + }, + { + "inputs": [], + "name": "get_deposit_count", + "outputs": [ + { + "type": "bytes", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [], + "name": "get_deposit_root", + "outputs": [ + { + "type": "bytes32", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "name": "interfaceId", + "type": "bytes4", + }, + ], + "name": "supportsInterface", + "outputs": [ + { + "type": "bool", + }, + ], + "stateMutability": "pure", + "type": "function", + }, + ], + "address": { + "100": "0xC4c622862a8F548997699bE24EA4bc504e5cA865", + "137": "0xC4c622862a8F548997699bE24EA4bc504e5cA865", + }, + "name": "Community", + }, +] +`; diff --git a/packages/cli/src/plugins/actions.test.ts b/packages/cli/src/plugins/actions.test.ts new file mode 100644 index 0000000000..51b445b616 --- /dev/null +++ b/packages/cli/src/plugins/actions.test.ts @@ -0,0 +1,359 @@ +import { erc20Abi } from 'viem' +import { expect, test } from 'vitest' + +import { actions } from './actions.js' + +test('default', async () => { + const result = await actions().run?.({ + contracts: [ + { + name: 'erc20', + abi: erc20Abi, + content: '', + meta: { + abiName: 'erc20Abi', + }, + }, + ], + isTypeScript: true, + outputs: [], + }) + + expect(result?.imports).toMatchInlineSnapshot(` + "import { createReadContract, createWriteContract, createSimulateContract, createWatchContractEvent } from '@wagmi/core/codegen' + " + `) + expect(result?.content).toMatchInlineSnapshot(` + "/** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const readErc20 = /*#__PURE__*/ createReadContract({ abi: erc20Abi }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"allowance"\` + */ + export const readErc20Allowance = /*#__PURE__*/ createReadContract({ abi: erc20Abi, functionName: 'allowance' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"balanceOf"\` + */ + export const readErc20BalanceOf = /*#__PURE__*/ createReadContract({ abi: erc20Abi, functionName: 'balanceOf' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"decimals"\` + */ + export const readErc20Decimals = /*#__PURE__*/ createReadContract({ abi: erc20Abi, functionName: 'decimals' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"name"\` + */ + export const readErc20Name = /*#__PURE__*/ createReadContract({ abi: erc20Abi, functionName: 'name' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"symbol"\` + */ + export const readErc20Symbol = /*#__PURE__*/ createReadContract({ abi: erc20Abi, functionName: 'symbol' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"totalSupply"\` + */ + export const readErc20TotalSupply = /*#__PURE__*/ createReadContract({ abi: erc20Abi, functionName: 'totalSupply' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const writeErc20 = /*#__PURE__*/ createWriteContract({ abi: erc20Abi }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const writeErc20Approve = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, functionName: 'approve' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const writeErc20Transfer = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, functionName: 'transfer' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const writeErc20TransferFrom = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, functionName: 'transferFrom' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const simulateErc20 = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const simulateErc20Approve = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, functionName: 'approve' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const simulateErc20Transfer = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, functionName: 'transfer' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const simulateErc20TransferFrom = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, functionName: 'transferFrom' }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const watchErc20Event = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Approval"\` + */ + export const watchErc20ApprovalEvent = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi, eventName: 'Approval' }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Transfer"\` + */ + export const watchErc20TransferEvent = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi, eventName: 'Transfer' })" + `) +}) + +test('address', async () => { + const result = await actions().run?.({ + contracts: [ + { + name: 'erc20', + abi: erc20Abi, + content: '', + meta: { + abiName: 'erc20Abi', + addressName: 'erc20Address', + }, + }, + ], + isTypeScript: true, + outputs: [], + }) + + expect(result?.content).toMatchInlineSnapshot(` + "/** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const readErc20 = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"allowance"\` + */ + export const readErc20Allowance = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'allowance' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"balanceOf"\` + */ + export const readErc20BalanceOf = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'balanceOf' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"decimals"\` + */ + export const readErc20Decimals = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'decimals' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"name"\` + */ + export const readErc20Name = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'name' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"symbol"\` + */ + export const readErc20Symbol = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'symbol' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"totalSupply"\` + */ + export const readErc20TotalSupply = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'totalSupply' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const writeErc20 = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const writeErc20Approve = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'approve' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const writeErc20Transfer = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'transfer' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const writeErc20TransferFrom = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'transferFrom' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const simulateErc20 = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const simulateErc20Approve = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'approve' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const simulateErc20Transfer = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'transfer' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const simulateErc20TransferFrom = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'transferFrom' }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const watchErc20Event = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Approval"\` + */ + export const watchErc20ApprovalEvent = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi, address: erc20Address, eventName: 'Approval' }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Transfer"\` + */ + export const watchErc20TransferEvent = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi, address: erc20Address, eventName: 'Transfer' })" + `) +}) + +test('legacy hook names', async () => { + const result = await actions({ getActionName: 'legacy' }).run?.({ + contracts: [ + { + name: 'erc20', + abi: erc20Abi, + content: '', + meta: { + abiName: 'erc20Abi', + addressName: 'erc20Address', + }, + }, + ], + isTypeScript: true, + outputs: [], + }) + + expect(result?.content).toMatchInlineSnapshot(` + "/** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const readErc20 = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"allowance"\` + */ + export const readErc20Allowance = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'allowance' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"balanceOf"\` + */ + export const readErc20BalanceOf = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'balanceOf' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"decimals"\` + */ + export const readErc20Decimals = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'decimals' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"name"\` + */ + export const readErc20Name = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'name' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"symbol"\` + */ + export const readErc20Symbol = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'symbol' }) + + /** + * Wraps __{@link readContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"totalSupply"\` + */ + export const readErc20TotalSupply = /*#__PURE__*/ createReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'totalSupply' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const writeErc20 = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const writeErc20Approve = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'approve' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const writeErc20Transfer = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'transfer' }) + + /** + * Wraps __{@link writeContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const writeErc20TransferFrom = /*#__PURE__*/ createWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'transferFrom' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const prepareWriteErc20 = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const prepareWriteErc20Approve = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'approve' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const prepareWriteErc20Transfer = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'transfer' }) + + /** + * Wraps __{@link simulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const prepareWriteErc20TransferFrom = /*#__PURE__*/ createSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'transferFrom' }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const watchErc20Event = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Approval"\` + */ + export const watchErc20ApprovalEvent = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi, address: erc20Address, eventName: 'Approval' }) + + /** + * Wraps __{@link watchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Transfer"\` + */ + export const watchErc20TransferEvent = /*#__PURE__*/ createWatchContractEvent({ abi: erc20Abi, address: erc20Address, eventName: 'Transfer' })" + `) +}) + +test('override package name', async () => { + const result = await actions({ overridePackageName: 'wagmi' }).run?.({ + contracts: [ + { + name: 'erc20', + abi: erc20Abi, + content: '', + meta: { + abiName: 'erc20Abi', + }, + }, + ], + isTypeScript: true, + outputs: [], + }) + + expect(result?.imports).toMatchInlineSnapshot(` + "import { createReadContract, createWriteContract, createSimulateContract, createWatchContractEvent } from 'wagmi/codegen' + " + `) +}) diff --git a/packages/cli/src/plugins/actions.ts b/packages/cli/src/plugins/actions.ts new file mode 100644 index 0000000000..01c804fd91 --- /dev/null +++ b/packages/cli/src/plugins/actions.ts @@ -0,0 +1,321 @@ +import { pascalCase } from 'change-case' + +import type { Contract, Plugin } from '../config.js' +import type { Compute, RequiredBy } from '../types.js' +import { getAddressDocString } from '../utils/getAddressDocString.js' +import { getIsPackageInstalled } from '../utils/packages.js' + +export type ActionsConfig = { + getActionName?: + | 'legacy' // TODO: Deprecate `'legacy'` option + | ((options: { + contractName: string + itemName?: string | undefined + type: 'read' | 'simulate' | 'watch' | 'write' + }) => string) + overridePackageName?: '@wagmi/core' | 'wagmi' | undefined +} + +type ActionsResult = Compute> + +export function actions(config: ActionsConfig = {}): ActionsResult { + return { + name: 'Action', + async run({ contracts }) { + const imports = new Set([]) + const content: string[] = [] + const pure = '/*#__PURE__*/' + + const actionNames = new Set() + for (const contract of contracts) { + let hasReadFunction = false + let hasWriteFunction = false + let hasEvent = false + const readItems = [] + const writeItems = [] + const eventItems = [] + for (const item of contract.abi) { + if (item.type === 'function') + if ( + item.stateMutability === 'view' || + item.stateMutability === 'pure' + ) { + hasReadFunction = true + readItems.push(item) + } else { + hasWriteFunction = true + writeItems.push(item) + } + else if (item.type === 'event') { + hasEvent = true + eventItems.push(item) + } + } + + let innerContent: string + if (contract.meta.addressName) + innerContent = `abi: ${contract.meta.abiName}, address: ${contract.meta.addressName}` + else innerContent = `abi: ${contract.meta.abiName}` + + if (hasReadFunction) { + const actionName = getActionName( + config, + actionNames, + 'read', + contract.name, + ) + const docString = genDocString('readContract', contract) + const functionName = 'createReadContract' + imports.add(functionName) + content.push( + `${docString} +export const ${actionName} = ${pure} ${functionName}({ ${innerContent} })`, + ) + + const names = new Set() + for (const item of readItems) { + if (item.type !== 'function') continue + if ( + item.stateMutability !== 'pure' && + item.stateMutability !== 'view' + ) + continue + + // Skip overrides since they are captured by same hook + if (names.has(item.name)) continue + names.add(item.name) + + const hookName = getActionName( + config, + actionNames, + 'read', + contract.name, + item.name, + ) + const docString = genDocString('readContract', contract, { + name: 'functionName', + value: item.name, + }) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent}, functionName: '${item.name}' })`, + ) + } + } + + if (hasWriteFunction) { + { + const actionName = getActionName( + config, + actionNames, + 'write', + contract.name, + ) + const docString = genDocString('writeContract', contract) + const functionName = 'createWriteContract' + imports.add(functionName) + content.push( + `${docString} +export const ${actionName} = ${pure} ${functionName}({ ${innerContent} })`, + ) + + const names = new Set() + for (const item of writeItems) { + if (item.type !== 'function') continue + if ( + item.stateMutability !== 'nonpayable' && + item.stateMutability !== 'payable' + ) + continue + + // Skip overrides since they are captured by same hook + if (names.has(item.name)) continue + names.add(item.name) + + const actionName = getActionName( + config, + actionNames, + 'write', + contract.name, + item.name, + ) + const docString = genDocString('writeContract', contract, { + name: 'functionName', + value: item.name, + }) + content.push( + `${docString} +export const ${actionName} = ${pure} ${functionName}({ ${innerContent}, functionName: '${item.name}' })`, + ) + } + } + + { + const actionName = getActionName( + config, + actionNames, + 'simulate', + contract.name, + ) + const docString = genDocString('simulateContract', contract) + const functionName = 'createSimulateContract' + imports.add(functionName) + content.push( + `${docString} +export const ${actionName} = ${pure} ${functionName}({ ${innerContent} })`, + ) + + const names = new Set() + for (const item of writeItems) { + if (item.type !== 'function') continue + if ( + item.stateMutability !== 'nonpayable' && + item.stateMutability !== 'payable' + ) + continue + + // Skip overrides since they are captured by same hook + if (names.has(item.name)) continue + names.add(item.name) + + const actionName = getActionName( + config, + actionNames, + 'simulate', + contract.name, + item.name, + ) + const docString = genDocString('simulateContract', contract, { + name: 'functionName', + value: item.name, + }) + content.push( + `${docString} +export const ${actionName} = ${pure} ${functionName}({ ${innerContent}, functionName: '${item.name}' })`, + ) + } + } + } + + if (hasEvent) { + const actionName = getActionName( + config, + actionNames, + 'watch', + contract.name, + ) + const docString = genDocString('watchContractEvent', contract) + const functionName = 'createWatchContractEvent' + imports.add(functionName) + content.push( + `${docString} +export const ${actionName} = ${pure} ${functionName}({ ${innerContent} })`, + ) + + const names = new Set() + for (const item of eventItems) { + if (item.type !== 'event') continue + + // Skip overrides since they are captured by same hook + if (names.has(item.name)) continue + names.add(item.name) + + const actionName = getActionName( + config, + actionNames, + 'watch', + contract.name, + item.name, + ) + const docString = genDocString('watchContractEvent', contract, { + name: 'eventName', + value: item.name, + }) + content.push( + `${docString} +export const ${actionName} = ${pure} ${functionName}({ ${innerContent}, eventName: '${item.name}' })`, + ) + } + } + } + + const importValues = [...imports.values()] + + let packageName = '@wagmi/core/codegen' + if (config.overridePackageName) { + switch (config.overridePackageName) { + case '@wagmi/core': + packageName = '@wagmi/core/codegen' + break + case 'wagmi': + packageName = 'wagmi/codegen' + break + } + } else if (await getIsPackageInstalled({ packageName: 'wagmi' })) + packageName = 'wagmi/codegen' + else if (await getIsPackageInstalled({ packageName: '@wagmi/core' })) + packageName = '@wagmi/core/codegen' + + return { + imports: importValues.length + ? `import { ${importValues.join(', ')} } from '${packageName}'\n` + : '', + content: content.join('\n\n'), + } + }, + } +} + +function genDocString( + actionName: string, + contract: Contract, + item?: { name: string; value: string }, +) { + let description = `Wraps __{@link ${actionName}}__ with \`abi\` set to __{@link ${contract.meta.abiName}}__` + if (item) description += ` and \`${item.name}\` set to \`"${item.value}"\`` + + const docString = getAddressDocString({ address: contract.address }) + if (docString) + return `/** + * ${description} + * + ${docString} + */` + + return `/** + * ${description} + */` +} + +function getActionName( + config: ActionsConfig, + actionNames: Set, + type: 'read' | 'simulate' | 'watch' | 'write', + contractName: string, + itemName?: string | undefined, +) { + const ContractName = pascalCase(contractName) + const ItemName = itemName ? pascalCase(itemName) : undefined + + let actionName: string + if (typeof config.getActionName === 'function') + actionName = config.getActionName({ + type, + contractName: ContractName, + itemName: ItemName, + }) + else if (typeof config.getActionName === 'string' && type === 'simulate') { + actionName = `prepareWrite${ContractName}${ItemName ?? ''}` + } else { + actionName = `${type}${ContractName}${ItemName ?? ''}` + if (type === 'watch') actionName = `${actionName}Event` + } + + if (actionNames.has(actionName)) + throw new Error( + `Action name "${actionName}" must be unique for contract "${contractName}". Try using \`getActionName\` to create a unique name.`, + ) + + actionNames.add(actionName) + return actionName +} diff --git a/packages/cli/src/plugins/blockExplorer.test.ts b/packages/cli/src/plugins/blockExplorer.test.ts new file mode 100644 index 0000000000..13372f53ec --- /dev/null +++ b/packages/cli/src/plugins/blockExplorer.test.ts @@ -0,0 +1,53 @@ +import { setupServer } from 'msw/node' +import { afterAll, afterEach, beforeAll, expect, test } from 'vitest' + +import { + address, + apiKey, + baseUrl, + handlers, + unverifiedContractAddress, +} from '../../test/utils.js' +import { blockExplorer } from './blockExplorer.js' + +const server = setupServer(...handlers) + +beforeAll(() => server.listen()) +afterEach(() => server.resetHandlers()) +afterAll(() => server.close()) + +test('fetches ABI', async () => { + await expect( + blockExplorer({ + apiKey, + baseUrl, + contracts: [{ name: 'WagmiMintExample', address }], + }).contracts!(), + ).resolves.toMatchSnapshot() +}) + +test('fetches ABI with multichain deployment', async () => { + await expect( + blockExplorer({ + apiKey, + baseUrl, + contracts: [ + { name: 'WagmiMintExample', address: { 1: address, 10: address } }, + ], + }).contracts?.(), + ).resolves.toMatchSnapshot() +}) + +test('fails to fetch for unverified contract', async () => { + await expect( + blockExplorer({ + apiKey, + baseUrl, + contracts: [ + { name: 'WagmiMintExample', address: unverifiedContractAddress }, + ], + }).contracts?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + '[Error: Contract source code not verified]', + ) +}) diff --git a/packages/cli/src/plugins/blockExplorer.ts b/packages/cli/src/plugins/blockExplorer.ts new file mode 100644 index 0000000000..2518b6e936 --- /dev/null +++ b/packages/cli/src/plugins/blockExplorer.ts @@ -0,0 +1,107 @@ +import { camelCase } from 'change-case' +import type { Address } from 'viem' +import { z } from 'zod' + +import type { ContractConfig } from '../config.js' +import { fromZodError } from '../errors.js' +import type { Compute } from '../types.js' +import { fetch } from './fetch.js' + +export type BlockExplorerConfig = { + /** + * API key for block explorer. Appended to the request URL as query param `&apikey=${apiKey}`. + */ + apiKey?: string | undefined + /** + * Base URL for block explorer. + */ + baseUrl: string + /** + * Duration in milliseconds to cache ABIs. + * + * @default 1_800_000 // 30m in ms + */ + cacheDuration?: number | undefined + /** + * Chain ID for block explorer. Appended to the request URL as query param `&chainId=${chainId}`. + */ + chainId?: number | undefined + /** + * Contracts to fetch ABIs for. + */ + contracts: Compute>[] + /** + * Function to get address from contract config. + */ + getAddress?: + | ((config: { + address: NonNullable + }) => Address) + | undefined + /** + * Name of source. + */ + name?: ContractConfig['name'] | undefined +} + +const BlockExplorerResponse = z.discriminatedUnion('status', [ + z.object({ + status: z.literal('1'), + message: z.literal('OK'), + result: z + .string() + .transform((val) => JSON.parse(val) as ContractConfig['abi']), + }), + z.object({ + status: z.literal('0'), + message: z.literal('NOTOK'), + result: z.string(), + }), +]) + +/** + * Fetches contract ABIs from block explorers, supporting `?module=contract&action=getabi` requests. + */ +export function blockExplorer(config: BlockExplorerConfig) { + const { + apiKey, + baseUrl, + cacheDuration, + chainId, + contracts, + getAddress = ({ address }) => { + if (typeof address === 'string') return address + return Object.values(address)[0]! + }, + name = 'Block Explorer', + } = config + + return fetch({ + cacheDuration, + contracts, + name, + getCacheKey({ contract }) { + if (typeof contract.address === 'string') + return `${camelCase(name)}:${contract.address}` + return `${camelCase(name)}:${JSON.stringify(contract.address)}` + }, + async parse({ response }) { + const json = await response.json() + const parsed = await BlockExplorerResponse.safeParseAsync(json) + if (!parsed.success) + throw fromZodError(parsed.error, { prefix: 'Invalid response' }) + if (parsed.data.status === '0') throw new Error(parsed.data.result) + return parsed.data.result + }, + request({ address }) { + if (!address) throw new Error('address is required') + return { + url: `${baseUrl}?${chainId ? `chainId=${chainId}&` : ''}module=contract&action=getabi&address=${getAddress( + { + address, + }, + )}${apiKey ? `&apikey=${apiKey}` : ''}`, + } + }, + }) +} diff --git a/packages/cli/src/plugins/etherscan.test.ts b/packages/cli/src/plugins/etherscan.test.ts new file mode 100644 index 0000000000..dc496f4630 --- /dev/null +++ b/packages/cli/src/plugins/etherscan.test.ts @@ -0,0 +1,112 @@ +import { mkdir, rm } from 'node:fs/promises' +import { setupServer } from 'msw/node' +import { afterAll, afterEach, beforeAll, expect, test } from 'vitest' + +import { + address, + apiKey, + handlers, + invalidApiKey, + proxyAddress, + timeoutAddress, + unverifiedContractAddress, +} from '../../test/utils.js' +import { etherscan } from './etherscan.js' +import { getCacheDir } from './fetch.js' + +const server = setupServer(...handlers) + +beforeAll(() => server.listen()) +afterEach(() => server.resetHandlers()) +afterAll(() => server.close()) + +test('fetches ABI', async () => { + await expect( + etherscan({ + apiKey, + chainId: 1, + contracts: [{ name: 'WagmiMintExample', address }], + }).contracts?.(), + ).resolves.toMatchSnapshot() +}) + +test('fetches ABI with multichain deployment', async () => { + await expect( + etherscan({ + apiKey, + chainId: 1, + contracts: [ + { name: 'WagmiMintExample', address: { 1: address, 10: address } }, + ], + }).contracts?.(), + ).resolves.toMatchSnapshot() +}) + +test('fails to fetch for unverified contract', async () => { + await expect( + etherscan({ + apiKey, + chainId: 1, + contracts: [ + { name: 'WagmiMintExample', address: unverifiedContractAddress }, + ], + }).contracts?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + '[Error: Contract source code not verified]', + ) +}) + +test('missing address for chainId', async () => { + await expect( + etherscan({ + apiKey, + chainId: 1, + // @ts-expect-error `chainId` and `keyof typeof contracts[number].address` mismatch + contracts: [{ name: 'WagmiMintExample', address: { 10: address } }], + }).contracts?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: No address found for chainId "1". Make sure chainId "1" is set as an address.]`, + ) +}) + +test('invalid api key', async () => { + await expect( + etherscan({ + apiKey: invalidApiKey, + chainId: 1, + contracts: [{ name: 'WagmiMintExample', address: timeoutAddress }], + }).contracts?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot('[Error: Invalid API Key]') +}) + +test('tryFetchProxyImplementation: fetches ABI', async () => { + const cacheDir = getCacheDir() + await mkdir(cacheDir, { recursive: true }) + + await expect( + etherscan({ + apiKey, + chainId: 1, + contracts: [{ name: 'WagmiMintExample', address }], + tryFetchProxyImplementation: true, + }).contracts?.(), + ).resolves.toMatchSnapshot() + + await rm(cacheDir, { recursive: true }) +}) + +test('tryFetchProxyImplementation: fetches implementation ABI', async () => { + const cacheDir = getCacheDir() + await mkdir(cacheDir, { recursive: true }) + + await expect( + etherscan({ + apiKey, + chainId: 1, + contracts: [{ name: 'FiatToken', address: proxyAddress }], + tryFetchProxyImplementation: true, + }).contracts?.(), + ).resolves.toMatchSnapshot() + + await rm(cacheDir, { recursive: true }) +}) diff --git a/packages/cli/src/plugins/etherscan.ts b/packages/cli/src/plugins/etherscan.ts new file mode 100644 index 0000000000..fda375c240 --- /dev/null +++ b/packages/cli/src/plugins/etherscan.ts @@ -0,0 +1,268 @@ +import { mkdir, writeFile } from 'node:fs/promises' +import { Address as AddressSchema } from 'abitype/zod' +import { camelCase } from 'change-case' +import { join } from 'pathe' +import type { Abi, Address } from 'viem' +import { z } from 'zod' + +import type { ContractConfig } from '../config.js' +import { fromZodError } from '../errors.js' +import type { Compute } from '../types.js' +import { fetch, getCacheDir } from './fetch.js' + +export type EtherscanConfig = { + /** + * Etherscan API key. + * + * Create or manage keys at https://etherscan.io/myapikey + */ + apiKey: string + /** + * Duration in milliseconds to cache ABIs. + * + * @default 1_800_000 // 30m in ms + */ + cacheDuration?: number | undefined + /** + * Chain ID to use for fetching ABI. + * + * If `address` is an object, `chainId` is used to select the address. + * + * View supported chains on the [Etherscan docs](https://docs.etherscan.io/etherscan-v2/getting-started/supported-chains). + */ + chainId: (chainId extends ChainId ? chainId : never) | (ChainId & {}) + /** + * Contracts to fetch ABIs for. + */ + contracts: Compute, 'abi'>>[] + /** + * Whether to try fetching proxy implementation address of the contract + * + * @default false + */ + tryFetchProxyImplementation?: boolean | undefined +} + +/** + * Fetches contract ABIs from Etherscan. + */ +export function etherscan( + config: EtherscanConfig, +) { + const { + apiKey, + cacheDuration = 1_800_000, + chainId, + tryFetchProxyImplementation = false, + } = config + + const contracts = config.contracts.map((x) => ({ + ...x, + address: + typeof x.address === 'string' ? { [chainId]: x.address } : x.address, + })) as Omit[] + + const name = 'Etherscan' + + const getCacheKey: Parameters[0]['getCacheKey'] = ({ + contract, + }) => { + if (typeof contract.address === 'string') + return `${camelCase(name)}:${contract.address}` + return `${camelCase(name)}:${JSON.stringify(contract.address)}` + } + + return fetch({ + cacheDuration, + contracts, + name, + getCacheKey, + async parse({ response }) { + const json = await response.json() + const parsed = await GetAbiResponse.safeParseAsync(json) + if (!parsed.success) + throw fromZodError(parsed.error, { prefix: 'Invalid response' }) + if (parsed.data.status === '0') throw new Error(parsed.data.result) + return parsed.data.result + }, + async request(contract) { + if (!contract.address) throw new Error('address is required') + + const resolvedAddress = (() => { + if (!contract.address) throw new Error('address is required') + if (typeof contract.address === 'string') return contract.address + const contractAddress = contract.address[chainId] + if (!contractAddress) + throw new Error( + `No address found for chainId "${chainId}". Make sure chainId "${chainId}" is set as an address.`, + ) + return contractAddress + })() + + const options = { + address: resolvedAddress, + apiKey, + chainId, + } + + let abi: Abi | undefined + const implementationAddress = await (async () => { + if (!tryFetchProxyImplementation) return + const json = await globalThis + .fetch(buildUrl({ ...options, action: 'getsourcecode' })) + .then((res) => res.json()) + const parsed = await GetSourceCodeResponse.safeParseAsync(json) + if (!parsed.success) + throw fromZodError(parsed.error, { prefix: 'Invalid response' }) + if (parsed.data.status === '0') throw new Error(parsed.data.result) + if (!parsed.data.result[0]) return + abi = parsed.data.result[0].ABI + return parsed.data.result[0].Implementation as Address + })() + + if (abi) { + const cacheDir = getCacheDir() + await mkdir(cacheDir, { recursive: true }) + const cacheKey = getCacheKey({ contract }) + const cacheFilePath = join(cacheDir, `${cacheKey}.json`) + await writeFile( + cacheFilePath, + `${JSON.stringify({ abi, timestamp: Date.now() + cacheDuration }, undefined, 2)}\n`, + ) + } + + return { + url: buildUrl({ + ...options, + action: 'getabi', + address: implementationAddress || resolvedAddress, + }), + } + }, + }) +} + +function buildUrl(options: { + action: 'getabi' | 'getsourcecode' + address: Address + apiKey: string + chainId: ChainId | undefined +}) { + const baseUrl = 'https://api.etherscan.io/v2/api' + const { action, address, apiKey, chainId } = options + return `${baseUrl}?${chainId ? `chainId=${chainId}&` : ''}module=contract&action=${action}&address=${address}${apiKey ? `&apikey=${apiKey}` : ''}` +} + +const GetAbiResponse = z.discriminatedUnion('status', [ + z.object({ + status: z.literal('1'), + message: z.literal('OK'), + result: z.string().transform((val) => JSON.parse(val) as Abi), + }), + z.object({ + status: z.literal('0'), + message: z.literal('NOTOK'), + result: z.string(), + }), +]) + +const GetSourceCodeResponse = z.discriminatedUnion('status', [ + z.object({ + status: z.literal('1'), + message: z.literal('OK'), + result: z.array( + z.discriminatedUnion('Proxy', [ + z.object({ + ABI: z.string().transform((val) => JSON.parse(val) as Abi), + Implementation: AddressSchema, + Proxy: z.literal('1'), + }), + z.object({ + ABI: z.string().transform((val) => JSON.parse(val) as Abi), + Implementation: z.string(), + Proxy: z.literal('0'), + }), + ]), + ), + }), + z.object({ + status: z.literal('0'), + message: z.literal('NOTOK'), + result: z.string(), + }), +]) + +// Supported chains +// https://docs.etherscan.io/etherscan-v2/getting-started/supported-chains +type ChainId = + | 1 // Ethereum Mainnet + | 11155111 // Sepolia Testnet + | 17000 // Holesky Testnet + | 560048 // Hoodi Testnet + | 56 // BNB Smart Chain Mainnet + | 97 // BNB Smart Chain Testnet + | 137 // Polygon Mainnet + | 80002 // Polygon Amoy Testnet + | 1101 // Polygon zkEVM Mainnet + | 2442 // Polygon zkEVM Cardona Testnet + | 8453 // Base Mainnet + | 84532 // Base Sepolia Testnet + | 42161 // Arbitrum One Mainnet + | 42170 // Arbitrum Nova Mainnet + | 421614 // Arbitrum Sepolia Testnet + | 59144 // Linea Mainnet + | 59141 // Linea Sepolia Testnet + | 250 // Fantom Opera Mainnet + | 4002 // Fantom Testnet + | 81457 // Blast Mainnet + | 168587773 // Blast Sepolia Testnet + | 10 // OP Mainnet + | 11155420 // OP Sepolia Testnet + | 43114 // Avalanche C-Chain + | 43113 // Avalanche Fuji Testnet + | 199 // BitTorrent Chain Mainnet + | 1028 // BitTorrent Chain Testnet + | 42220 // Celo Mainnet + | 44787 // Celo Alfajores Testnet + | 25 // Cronos Mainnet + | 252 // Fraxtal Mainnet + | 2522 // Fraxtal Testnet + | 100 // Gnosis + | 255 // Kroma Mainnet + | 2358 // Kroma Sepolia Testnet + | 5000 // Mantle Mainnet + | 5003 // Mantle Sepolia Testnet + | 1284 // Moonbeam Mainnet + | 1285 // Moonriver Mainnet + | 1287 // Moonbase Alpha Testnet + | 204 // opBNB Mainnet + | 5611 // opBNB Testnet + | 534352 // Scroll Mainnet + | 534351 // Scroll Sepolia Testnet + | 167000 // Taiko Mainnet + | 167009 // Taiko Hekla L2 Testnet + | 1111 // WEMIX3.0 Mainnet + | 1112 // WEMIX3.0 Testnet + | 324 // zkSync Mainnet + | 300 // zkSync Sepolia Testnet + | 660279 // Xai Mainnet + | 37714555429 // Xai Sepolia Testnet + | 50 // XDC Mainnet + | 51 // XDC Apothem Testnet + | 33139 // ApeChain Mainnet + | 33111 // ApeChain Curtis Testnet + | 480 // World Mainnet + | 4801 // World Sepolia Testnet + | 50104 // Sophon Mainnet + | 531050104 // Sophon Sepolia Testnet + | 146 // Sonic Mainnet + | 57054 // Sonic Blaze Testnet + | 130 // Unichain Mainnet + | 1301 // Unichain Sepolia Testnet + | 2741 // Abstract Mainnet + | 11124 // Abstract Sepolia Testnet + | 80094 // Berachain Mainnet + | 80069 // Berachain Bepolia Testnet + | 1923 // Swellchain Mainnet + | 1924 // Swellchain Testnet + | 10143 // Monad Testnet diff --git a/packages/cli/src/plugins/fetch.test.ts b/packages/cli/src/plugins/fetch.test.ts new file mode 100644 index 0000000000..600cbfeda7 --- /dev/null +++ b/packages/cli/src/plugins/fetch.test.ts @@ -0,0 +1,186 @@ +import { mkdir, rm, writeFile } from 'node:fs/promises' +import { homedir } from 'node:os' +import { setupServer } from 'msw/node' +import { afterAll, afterEach, beforeAll, expect, test } from 'vitest' + +import { + address, + apiKey, + baseUrl, + handlers, + timeoutAddress, + unverifiedContractAddress, +} from '../../test/utils.js' +import { fetch, getCacheDir } from './fetch.js' + +const server = setupServer(...handlers) + +beforeAll(() => server.listen()) +afterEach(() => server.resetHandlers()) +afterAll(() => server.close()) + +type Fetch = Parameters[0] +const request: Fetch['request'] = ({ address }) => { + return { + url: `${baseUrl}?module=contract&action=getabi&address=${address}&apikey=${apiKey}`, + } +} +const parse: Fetch['parse'] = async ({ response }) => { + const data = (await response.json()) as + | { status: '1'; message: 'OK'; result: string } + | { status: '0'; message: 'NOTOK'; result: string } + if (data.status === '0') throw new Error(data.result) + return JSON.parse(data.result) +} + +test('fetches ABI', async () => { + await expect( + fetch({ + contracts: [{ name: 'WagmiMintExample', address }], + request, + parse, + }).contracts?.(), + ).resolves.toMatchSnapshot() +}) + +test('fails to fetch for unverified contract', async () => { + await expect( + fetch({ + contracts: [ + { name: 'WagmiMintExample', address: unverifiedContractAddress }, + ], + request, + parse, + }).contracts?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + '[Error: Contract source code not verified]', + ) +}) + +test('aborts request', async () => { + await expect( + fetch({ + contracts: [{ name: 'WagmiMintExample', address: timeoutAddress }], + request, + parse, + timeoutDuration: 1_000, + }).contracts?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + '[AbortError: This operation was aborted]', + ) +}) + +test('reads from cache', async () => { + const cacheDir = `${homedir}/.wagmi-cli/plugins/fetch/cache` + await mkdir(cacheDir, { recursive: true }) + + const contract = { + name: 'WagmiMintExample', + address: timeoutAddress, + } as const + const cacheKey = JSON.stringify(contract) + const cacheFilePath = `${cacheDir}/${cacheKey}.json` + await writeFile( + cacheFilePath, + JSON.stringify( + { + abi: [ + { + inputs: [], + name: 'mint', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ], + timestamp: Date.now() + 30_000, + }, + null, + 2, + ), + ) + + await expect( + fetch({ + contracts: [contract], + request, + parse, + }).contracts?.(), + ).resolves.toMatchInlineSnapshot(` + [ + { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + "name": "WagmiMintExample", + }, + ] + `) + + await rm(cacheDir, { recursive: true }) +}) + +test('fails and reads from cache', async () => { + const cacheDir = getCacheDir() + await mkdir(cacheDir, { recursive: true }) + + const contract = { + name: 'WagmiMintExample', + address: timeoutAddress, + } as const + const cacheKey = JSON.stringify(contract) + const cacheFilePath = `${cacheDir}/${cacheKey}.json` + await writeFile( + cacheFilePath, + JSON.stringify( + { + abi: [ + { + inputs: [], + name: 'mint', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + ], + timestamp: Date.now() - 30_000, + }, + null, + 2, + ), + ) + + await expect( + fetch({ + contracts: [contract], + request, + parse, + timeoutDuration: 1, + }).contracts?.(), + ).resolves.toMatchInlineSnapshot(` + [ + { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + "name": "WagmiMintExample", + }, + ] + `) + + await rm(cacheDir, { recursive: true }) +}) diff --git a/packages/cli/src/plugins/fetch.ts b/packages/cli/src/plugins/fetch.ts new file mode 100644 index 0000000000..778d4a8162 --- /dev/null +++ b/packages/cli/src/plugins/fetch.ts @@ -0,0 +1,127 @@ +import { mkdir, readFile, writeFile } from 'node:fs/promises' +import { homedir } from 'node:os' +import { join } from 'pathe' + +import type { Abi } from 'viem' +import type { ContractConfig, Plugin } from '../config.js' +import type { Compute, RequiredBy } from '../types.js' + +export type FetchConfig = { + /** + * Duration in milliseconds to cache ABIs from request. + * + * @default 1_800_000 // 30m in ms + */ + cacheDuration?: number | undefined + /** + * Contracts to fetch ABIs for. + */ + contracts: Compute>[] + /** + * Function for creating a cache key for contract. + */ + getCacheKey?: + | ((config: { contract: Compute> }) => string) + | undefined + /** + * Name of source. + */ + name?: ContractConfig['name'] | undefined + /** + * Function for parsing ABI from fetch response. + * + * @default ({ response }) => response.json() + */ + parse?: + | ((config: { + response: Response + }) => ContractConfig['abi'] | Promise) + | undefined + /** + * Function for returning a request to fetch ABI from. + */ + request: (config: { + address?: ContractConfig['address'] | undefined + name: ContractConfig['name'] + }) => + | { url: RequestInfo; init?: RequestInit | undefined } + | Promise<{ url: RequestInfo; init?: RequestInit | undefined }> + /** + * Duration in milliseconds before request times out. + * + * @default 5_000 // 5s in ms + */ + timeoutDuration?: number | undefined +} + +type FetchResult = Compute> + +/** Fetches and parses contract ABIs from network resource with `fetch`. */ +export function fetch(config: FetchConfig): FetchResult { + const { + cacheDuration = 1_800_000, + contracts: contractConfigs, + getCacheKey = ({ contract }) => JSON.stringify(contract), + name = 'Fetch', + parse = ({ response }) => response.json(), + request, + timeoutDuration = 5_000, + } = config + + return { + async contracts() { + const cacheDir = getCacheDir() + await mkdir(cacheDir, { recursive: true }) + + const timestamp = Date.now() + cacheDuration + const contracts = [] + for (const contract of contractConfigs) { + const cacheKey = getCacheKey({ contract }) + const cacheFilePath = join(cacheDir, `${cacheKey}.json`) + const cachedFile = JSON.parse( + await readFile(cacheFilePath, 'utf8').catch(() => 'null'), + ) + + let abi: Abi | undefined + if (cachedFile?.timestamp > Date.now()) abi = cachedFile.abi + else { + try { + const controller = new globalThis.AbortController() + const timeout = setTimeout( + () => controller.abort(), + timeoutDuration, + ) + + const { url, init } = await request(contract) + const response = await globalThis.fetch(url, { + ...init, + signal: controller.signal, + }) + clearTimeout(timeout) + + abi = await parse({ response }) + await writeFile( + cacheFilePath, + `${JSON.stringify({ abi, timestamp }, undefined, 2)}\n`, + ) + } catch (error) { + try { + // Attempt to read from cache if fetch fails. + abi = JSON.parse(await readFile(cacheFilePath, 'utf8')).abi + } catch {} + if (!abi) throw error + } + } + + if (!abi) throw Error('Failed to fetch ABI for contract.') + contracts.push({ abi, address: contract.address, name: contract.name }) + } + return contracts + }, + name, + } +} + +export function getCacheDir() { + return join(homedir(), '.wagmi-cli/plugins/fetch/cache') +} diff --git a/packages/cli/src/plugins/foundry.test.ts b/packages/cli/src/plugins/foundry.test.ts new file mode 100644 index 0000000000..75e5ec73ee --- /dev/null +++ b/packages/cli/src/plugins/foundry.test.ts @@ -0,0 +1,153 @@ +import fixtures from 'fixturez' +import { dirname, resolve } from 'pathe' +import { afterEach, expect, test, vi } from 'vitest' + +import { foundry } from './foundry.js' + +const f = fixtures(__dirname) + +afterEach(() => { + vi.restoreAllMocks() +}) + +test('forge not installed', async () => { + const dir = f.temp() + expect( + foundry({ + project: dir, + forge: { + path: '/path/to/forge', + }, + }).validate?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [Error: forge must be installed to use Foundry plugin. + To install, follow the instructions at https://book.getfoundry.sh/getting-started/installation] + `) +}) + +test('project does not exist', async () => { + const dir = f.temp() + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + try { + await foundry({ project: '../path/to/project' }).validate?.() + } catch (error) { + expect( + (error as Error).message.replace(dirname(dir), '..'), + ).toMatchInlineSnapshot('"Foundry project ../path/to/project not found."') + } +}) + +test('validates without project', async () => { + const dir = resolve(__dirname, '__fixtures__/foundry/') + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(foundry().validate?.()).resolves.toBeUndefined() +}) + +test('contracts', async () => { + await expect( + foundry({ + project: resolve(__dirname, '__fixtures__/foundry/'), + exclude: ['Foo.sol/**'], + }).contracts?.(), + ).resolves.toMatchInlineSnapshot(` + [ + { + "abi": [ + { + "inputs": [], + "name": "increment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "number", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newNumber", + "type": "uint256", + }, + ], + "name": "setNumber", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": undefined, + "name": "Counter", + }, + ] + `) +}) + +test('contracts without project', async () => { + const dir = resolve(__dirname, '__fixtures__/foundry/') + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect( + foundry({ + exclude: ['Foo.sol/**'], + }).contracts?.(), + ).resolves.toMatchInlineSnapshot(` + [ + { + "abi": [ + { + "inputs": [], + "name": "increment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "number", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newNumber", + "type": "uint256", + }, + ], + "name": "setNumber", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": undefined, + "name": "Counter", + }, + ] + `) +}) diff --git a/packages/cli/src/plugins/foundry.ts b/packages/cli/src/plugins/foundry.ts new file mode 100644 index 0000000000..dab307a58f --- /dev/null +++ b/packages/cli/src/plugins/foundry.ts @@ -0,0 +1,263 @@ +import { execSync, spawn, spawnSync } from 'node:child_process' +import { existsSync } from 'node:fs' +import { readFile } from 'node:fs/promises' +import dedent from 'dedent' +import { fdir } from 'fdir' +import { basename, extname, join, resolve } from 'pathe' +import pc from 'picocolors' +import { z } from 'zod' + +import type { ContractConfig, Plugin } from '../config.js' +import * as logger from '../logger.js' +import type { Compute, RequiredBy } from '../types.js' + +export const foundryDefaultExcludes = [ + 'Base.sol/**', + 'Common.sol/**', + 'Components.sol/**', + 'IERC165.sol/**', + 'IERC20.sol/**', + 'IERC721.sol/**', + 'IMulticall2.sol/**', + 'MockERC20.sol/**', + 'MockERC721.sol/**', + 'Script.sol/**', + 'StdAssertions.sol/**', + 'StdChains.sol/**', + 'StdCheats.sol/**', + 'StdError.sol/**', + 'StdInvariant.sol/**', + 'StdJson.sol/**', + 'StdMath.sol/**', + 'StdStorage.sol/**', + 'StdStyle.sol/**', + 'StdToml.sol/**', + 'StdUtils.sol/**', + 'Test.sol/**', + 'Vm.sol/**', + 'build-info/**', + 'console.sol/**', + 'console2.sol/**', + 'safeconsole.sol/**', + '**.s.sol/*.json', + '**.t.sol/*.json', +] + +export type FoundryConfig = { + /** + * Project's artifacts directory. + * + * Same as your project's `--out` (`-o`) option. + * + * @default foundry.config#out | 'out' + */ + artifacts?: string | undefined + /** Mapping of addresses to attach to artifacts. */ + deployments?: { [key: string]: ContractConfig['address'] } | undefined + /** Artifact files to exclude. */ + exclude?: string[] | undefined + /** [Forge](https://book.getfoundry.sh/forge) configuration */ + forge?: + | { + /** + * Remove build artifacts and cache directories on start up. + * + * @default false + */ + clean?: boolean | undefined + /** + * Build Foundry project before fetching artifacts. + * + * @default true + */ + build?: boolean | undefined + /** + * Path to `forge` executable command + * + * @default "forge" + */ + path?: string | undefined + /** + * Rebuild every time a watched file or directory is changed. + * + * @default true + */ + rebuild?: boolean | undefined + } + | undefined + /** Artifact files to include. */ + include?: string[] | undefined + /** Optional prefix to prepend to artifact names. */ + namePrefix?: string | undefined + /** Path to foundry project. */ + project?: string | undefined +} + +type FoundryResult = Compute< + RequiredBy +> + +const FoundryConfigSchema = z.object({ + out: z.string().default('out'), + src: z.string().default('src'), +}) + +/** Resolves ABIs from [Foundry](https://github.com/foundry-rs/foundry) project. */ +export function foundry(config: FoundryConfig = {}): FoundryResult { + const { + artifacts, + deployments = {}, + exclude = foundryDefaultExcludes, + forge: { + clean = false, + build = true, + path: forgeExecutable = 'forge', + rebuild = true, + } = {}, + include = ['*.json'], + namePrefix = '', + } = config + + function getContractName(artifactPath: string, usePrefix = true) { + const filename = basename(artifactPath) + const extension = extname(artifactPath) + return `${usePrefix ? namePrefix : ''}${filename.replace(extension, '')}` + } + + async function getContract(artifactPath: string) { + const artifact = await JSON.parse(await readFile(artifactPath, 'utf8')) + return { + abi: artifact.abi, + address: (deployments as Record)[ + getContractName(artifactPath, false) + ], + name: getContractName(artifactPath), + } + } + + function getArtifactPaths(artifactsDirectory: string) { + const crawler = new fdir().withBasePath().globWithOptions( + include.map((x) => `${artifactsDirectory}/**/${x}`), + { + dot: true, + ignore: exclude.map((x) => `${artifactsDirectory}/**/${x}`), + }, + ) + return crawler.crawl(artifactsDirectory).withPromise() + } + + const project = resolve(process.cwd(), config.project ?? '') + + let foundryConfig: z.infer = { + out: 'out', + src: 'src', + } + try { + const result = spawnSync( + forgeExecutable, + ['config', '--json', '--root', project], + { + encoding: 'utf-8', + shell: true, + }, + ) + if (result.error) throw result.error + if (result.status !== 0) + throw new Error(`Failed with code ${result.status}`) + if (result.signal) throw new Error('Process terminated by signal') + foundryConfig = FoundryConfigSchema.parse(JSON.parse(result.stdout)) + } catch { + } finally { + foundryConfig = { + ...foundryConfig, + out: artifacts ?? foundryConfig.out, + } + } + + const artifactsDirectory = join(project, foundryConfig.out) + + return { + async contracts() { + if (clean) + execSync(`${forgeExecutable} clean --root ${project}`, { + encoding: 'utf-8', + stdio: 'pipe', + }) + if (build) + execSync(`${forgeExecutable} build --root ${project}`, { + encoding: 'utf-8', + stdio: 'pipe', + }) + if (!existsSync(artifactsDirectory)) + throw new Error('Artifacts not found.') + + const artifactPaths = await getArtifactPaths(artifactsDirectory) + const contracts = [] + for (const artifactPath of artifactPaths) { + const contract = await getContract(artifactPath) + if (!contract.abi?.length) continue + contracts.push(contract) + } + return contracts + }, + name: 'Foundry', + async validate() { + // Check that project directory exists + if (!existsSync(project)) + throw new Error(`Foundry project ${pc.gray(config.project)} not found.`) + + // Ensure forge is installed + if (clean || build || rebuild) + try { + execSync(`${forgeExecutable} --version`, { + encoding: 'utf-8', + stdio: 'pipe', + }) + } catch (_error) { + throw new Error(dedent` + forge must be installed to use Foundry plugin. + To install, follow the instructions at https://book.getfoundry.sh/getting-started/installation + `) + } + }, + watch: { + command: rebuild + ? async () => { + logger.log( + `${pc.magenta('Foundry')} Watching project at ${pc.gray( + project, + )}`, + ) + const subprocess = spawn(forgeExecutable, [ + 'build', + '--watch', + '--root', + project, + ]) + subprocess.stdout?.on('data', (data) => { + process.stdout.write(`${pc.magenta('Foundry')} ${data}`) + }) + + process.once('SIGINT', shutdown) + process.once('SIGTERM', shutdown) + function shutdown() { + subprocess?.kill() + } + } + : undefined, + paths: [ + ...include.map((x) => `${artifactsDirectory}/**/${x}`), + ...exclude.map((x) => `!${artifactsDirectory}/**/${x}`), + ], + async onAdd(path) { + return getContract(path) + }, + async onChange(path) { + return getContract(path) + }, + async onRemove(path) { + return getContractName(path) + }, + }, + } +} diff --git a/packages/cli/src/plugins/hardhat.test.ts b/packages/cli/src/plugins/hardhat.test.ts new file mode 100644 index 0000000000..efb416c5e6 --- /dev/null +++ b/packages/cli/src/plugins/hardhat.test.ts @@ -0,0 +1,85 @@ +import fixtures from 'fixturez' +import { dirname, resolve } from 'pathe' +import { afterEach, expect, test, vi } from 'vitest' + +import { hardhat } from './hardhat.js' + +const f = fixtures(__dirname) + +afterEach(() => { + vi.restoreAllMocks() +}) + +test('validate', async () => { + const temp = f.temp() + expect( + hardhat({ project: temp }).validate?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + '[Error: hardhat must be installed to use Hardhat plugin.]', + ) +}) + +test('project does not exist', async () => { + const dir = f.temp() + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + try { + await hardhat({ project: '../path/to/project' }).validate?.() + } catch (error) { + expect( + (error as Error).message.replace(dirname(dir), '..'), + ).toMatchInlineSnapshot('"Hardhat project ../path/to/project not found."') + } +}) + +test('contracts', async () => { + expect( + hardhat({ + project: resolve(__dirname, '__fixtures__/hardhat/'), + exclude: ['Foo.sol/**'], + }).contracts?.(), + ).resolves.toMatchInlineSnapshot(` + [ + { + "abi": [ + { + "inputs": [], + "name": "increment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + { + "inputs": [], + "name": "number", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256", + }, + ], + "stateMutability": "view", + "type": "function", + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newNumber", + "type": "uint256", + }, + ], + "name": "setNumber", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "address": undefined, + "name": "Counter", + }, + ] + `) +}, 10_000) diff --git a/packages/cli/src/plugins/hardhat.ts b/packages/cli/src/plugins/hardhat.ts new file mode 100644 index 0000000000..a4feb6efdf --- /dev/null +++ b/packages/cli/src/plugins/hardhat.ts @@ -0,0 +1,235 @@ +import { execSync, spawn } from 'node:child_process' +import { existsSync } from 'node:fs' +import { readFile } from 'node:fs/promises' +import { fdir } from 'fdir' +import { basename, extname, join, resolve } from 'pathe' +import pc from 'picocolors' + +import type { ContractConfig, Plugin } from '../config.js' +import * as logger from '../logger.js' +import type { Compute, RequiredBy } from '../types.js' +import { getIsPackageInstalled, getPackageManager } from '../utils/packages.js' + +export const hardhatDefaultExcludes = ['build-info/**', '*.dbg.json'] + +export type HardhatConfig = { + /** + * Project's artifacts directory. + * + * Same as your project's `artifacts` [path configuration](https://hardhat.org/hardhat-runner/docs/config#path-configuration) option. + * + * @default 'artifacts/' + */ + artifacts?: string | undefined + /** Mapping of addresses to attach to artifacts. */ + deployments?: { [key: string]: ContractConfig['address'] } | undefined + /** Artifact files to exclude. */ + exclude?: string[] | undefined + /** Commands to run */ + commands?: + | { + /** + * Remove build artifacts and cache directories on start up. + * + * @default `${packageManger} hardhat clean` + */ + clean?: string | boolean | undefined + /** + * Build Hardhat project before fetching artifacts. + * + * @default `${packageManger} hardhat compile` + */ + build?: string | boolean | undefined + /** + * Command to run when watched file or directory is changed. + * + * @default `${packageManger} hardhat compile` + */ + rebuild?: string | boolean | undefined + } + | undefined + /** Artifact files to include. */ + include?: string[] | undefined + /** Optional prefix to prepend to artifact names. */ + namePrefix?: string | undefined + /** Path to Hardhat project. */ + project: string + /** + * Project's artifacts directory. + * + * Same as your project's `sources` [path configuration](https://hardhat.org/hardhat-runner/docs/config#path-configuration) option. + * + * @default 'contracts/' + */ + sources?: string | undefined +} + +type HardhatResult = Compute< + RequiredBy +> + +/** Resolves ABIs from [Hardhat](https://github.com/NomicFoundation/hardhat) project. */ +export function hardhat(config: HardhatConfig): HardhatResult { + const { + artifacts = 'artifacts', + deployments = {}, + exclude = hardhatDefaultExcludes, + commands = {}, + include = ['*.json'], + namePrefix = '', + sources = 'contracts', + } = config + + function getContractName(artifact: { contractName: string }) { + return `${namePrefix}${artifact.contractName}` + } + + async function getContract(artifactPath: string) { + const artifact = await JSON.parse(await readFile(artifactPath, 'utf8')) + return { + abi: artifact.abi, + address: deployments[artifact.contractName], + name: getContractName(artifact), + } + } + + function getArtifactPaths(artifactsDirectory: string) { + const crawler = new fdir().withBasePath().globWithOptions( + include.map((x) => `${artifactsDirectory}/**/${x}`), + { + dot: true, + ignore: exclude.map((x) => `${artifactsDirectory}/**/${x}`), + }, + ) + return crawler.crawl(artifactsDirectory).withPromise() + } + + const project = resolve(process.cwd(), config.project) + const artifactsDirectory = join(project, artifacts) + const sourcesDirectory = join(project, sources) + + const { build = true, clean = false, rebuild = true } = commands + return { + async contracts() { + if (clean) { + const packageManager = await getPackageManager(true) + const [command, ...options] = ( + typeof clean === 'boolean' ? `${packageManager} hardhat clean` : clean + ).split(' ') + execSync(`${command!} ${options.join(' ')}`, { + cwd: project, + encoding: 'utf-8', + stdio: 'pipe', + }) + } + if (build) { + const packageManager = await getPackageManager(true) + const [command, ...options] = ( + typeof build === 'boolean' + ? `${packageManager} hardhat compile` + : build + ).split(' ') + execSync(`${command!} ${options.join(' ')}`, { + cwd: project, + encoding: 'utf-8', + stdio: 'pipe', + }) + } + if (!existsSync(artifactsDirectory)) + throw new Error('Artifacts not found.') + + const artifactPaths = await getArtifactPaths(artifactsDirectory) + const contracts = [] + for (const artifactPath of artifactPaths) { + const contract = await getContract(artifactPath) + if (!contract.abi?.length) continue + contracts.push(contract) + } + return contracts + }, + name: 'Hardhat', + async validate() { + // Check that project directory exists + if (!existsSync(project)) + throw new Error(`Hardhat project ${pc.gray(project)} not found.`) + + // Check that `hardhat` is installed + const packageName = 'hardhat' + const isPackageInstalled = await getIsPackageInstalled({ + packageName, + cwd: project, + }) + if (isPackageInstalled) return + throw new Error(`${packageName} must be installed to use Hardhat plugin.`) + }, + watch: { + command: rebuild + ? async () => { + logger.log( + `${pc.blue('Hardhat')} Watching project at ${pc.gray(project)}`, + ) + + const [command, ...options] = ( + typeof rebuild === 'boolean' + ? `${await getPackageManager(true)} hardhat compile` + : rebuild + ).split(' ') + + const { watch } = await import('chokidar') + const watcher = watch(sourcesDirectory, { + atomic: true, + awaitWriteFinish: true, + ignoreInitial: true, + persistent: true, + }) + watcher.on('all', async (event, path) => { + if (event !== 'change' && event !== 'add' && event !== 'unlink') + return + logger.log( + `${pc.blue('Hardhat')} Detected ${event} at ${basename(path)}`, + ) + const subprocess = spawn(command!, options, { + cwd: project, + }) + subprocess.stdout?.on('data', (data) => { + process.stdout.write(`${pc.blue('Hardhat')} ${data}`) + }) + }) + + process.once('SIGINT', shutdown) + process.once('SIGTERM', shutdown) + async function shutdown() { + await watcher.close() + } + } + : undefined, + paths: [ + artifactsDirectory, + ...include.map((x) => `${artifactsDirectory}/**/${x}`), + ...exclude.map((x) => `!${artifactsDirectory}/**/${x}`), + ], + async onAdd(path) { + return getContract(path) + }, + async onChange(path) { + return getContract(path) + }, + async onRemove(path) { + const filename = basename(path) + const extension = extname(path) + // Since we can't use `getContractName`, guess from path + const removedContractName = `${namePrefix}${filename.replace( + extension, + '', + )}` + const artifactPaths = await getArtifactPaths(artifactsDirectory) + for (const artifactPath of artifactPaths) { + const contract = await getContract(artifactPath) + // If contract with same name exists, don't remove + if (contract.name === removedContractName) return + } + return removedContractName + }, + }, + } +} diff --git a/packages/cli/src/plugins/react.test.ts b/packages/cli/src/plugins/react.test.ts new file mode 100644 index 0000000000..939a5299ae --- /dev/null +++ b/packages/cli/src/plugins/react.test.ts @@ -0,0 +1,337 @@ +import { erc20Abi } from 'viem' +import { expect, test } from 'vitest' + +import { react } from './react.js' + +test('default', async () => { + const result = await react().run?.({ + contracts: [ + { + name: 'erc20', + abi: erc20Abi, + content: '', + meta: { + abiName: 'erc20Abi', + }, + }, + ], + isTypeScript: true, + outputs: [], + }) + + expect(result?.imports).toMatchInlineSnapshot(` + "import { createUseReadContract, createUseWriteContract, createUseSimulateContract, createUseWatchContractEvent } from 'wagmi/codegen' + " + `) + expect(result?.content).toMatchInlineSnapshot(` + "/** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useReadErc20 = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"allowance"\` + */ + export const useReadErc20Allowance = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, functionName: 'allowance' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"balanceOf"\` + */ + export const useReadErc20BalanceOf = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, functionName: 'balanceOf' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"decimals"\` + */ + export const useReadErc20Decimals = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, functionName: 'decimals' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"name"\` + */ + export const useReadErc20Name = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, functionName: 'name' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"symbol"\` + */ + export const useReadErc20Symbol = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, functionName: 'symbol' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"totalSupply"\` + */ + export const useReadErc20TotalSupply = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, functionName: 'totalSupply' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useWriteErc20 = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const useWriteErc20Approve = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, functionName: 'approve' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const useWriteErc20Transfer = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, functionName: 'transfer' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const useWriteErc20TransferFrom = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, functionName: 'transferFrom' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useSimulateErc20 = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const useSimulateErc20Approve = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, functionName: 'approve' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const useSimulateErc20Transfer = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, functionName: 'transfer' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const useSimulateErc20TransferFrom = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, functionName: 'transferFrom' }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useWatchErc20Event = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Approval"\` + */ + export const useWatchErc20ApprovalEvent = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi, eventName: 'Approval' }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Transfer"\` + */ + export const useWatchErc20TransferEvent = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi, eventName: 'Transfer' })" + `) +}) + +test('address', async () => { + const result = await react().run?.({ + contracts: [ + { + name: 'erc20', + abi: erc20Abi, + content: '', + meta: { + abiName: 'erc20Abi', + addressName: 'erc20Address', + }, + }, + ], + isTypeScript: true, + outputs: [], + }) + + expect(result?.content).toMatchInlineSnapshot(` + "/** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useReadErc20 = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"allowance"\` + */ + export const useReadErc20Allowance = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'allowance' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"balanceOf"\` + */ + export const useReadErc20BalanceOf = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'balanceOf' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"decimals"\` + */ + export const useReadErc20Decimals = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'decimals' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"name"\` + */ + export const useReadErc20Name = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'name' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"symbol"\` + */ + export const useReadErc20Symbol = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'symbol' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"totalSupply"\` + */ + export const useReadErc20TotalSupply = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'totalSupply' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useWriteErc20 = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const useWriteErc20Approve = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'approve' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const useWriteErc20Transfer = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'transfer' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const useWriteErc20TransferFrom = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'transferFrom' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useSimulateErc20 = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const useSimulateErc20Approve = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'approve' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const useSimulateErc20Transfer = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'transfer' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const useSimulateErc20TransferFrom = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'transferFrom' }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useWatchErc20Event = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Approval"\` + */ + export const useWatchErc20ApprovalEvent = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi, address: erc20Address, eventName: 'Approval' }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Transfer"\` + */ + export const useWatchErc20TransferEvent = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi, address: erc20Address, eventName: 'Transfer' })" + `) +}) + +test('legacy hook names', async () => { + const result = await react({ getHookName: 'legacy' }).run?.({ + contracts: [ + { + name: 'erc20', + abi: erc20Abi, + content: '', + meta: { + abiName: 'erc20Abi', + addressName: 'erc20Address', + }, + }, + ], + isTypeScript: true, + outputs: [], + }) + + expect(result?.content).toMatchInlineSnapshot(` + "/** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useErc20Read = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"allowance"\` + */ + export const useErc20Allowance = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'allowance' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"balanceOf"\` + */ + export const useErc20BalanceOf = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'balanceOf' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"decimals"\` + */ + export const useErc20Decimals = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'decimals' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"name"\` + */ + export const useErc20Name = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'name' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"symbol"\` + */ + export const useErc20Symbol = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'symbol' }) + + /** + * Wraps __{@link useReadContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"totalSupply"\` + */ + export const useErc20TotalSupply = /*#__PURE__*/ createUseReadContract({ abi: erc20Abi, address: erc20Address, functionName: 'totalSupply' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useErc20Write = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const useErc20Approve = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'approve' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const useErc20Transfer = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'transfer' }) + + /** + * Wraps __{@link useWriteContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const useErc20TransferFrom = /*#__PURE__*/ createUseWriteContract({ abi: erc20Abi, address: erc20Address, functionName: 'transferFrom' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const usePrepareErc20Write = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"approve"\` + */ + export const usePrepareErc20Approve = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'approve' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transfer"\` + */ + export const usePrepareErc20Transfer = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'transfer' }) + + /** + * Wraps __{@link useSimulateContract}__ with \`abi\` set to __{@link erc20Abi}__ and \`functionName\` set to \`"transferFrom"\` + */ + export const usePrepareErc20TransferFrom = /*#__PURE__*/ createUseSimulateContract({ abi: erc20Abi, address: erc20Address, functionName: 'transferFrom' }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ + */ + export const useErc20Event = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi, address: erc20Address }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Approval"\` + */ + export const useErc20ApprovalEvent = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi, address: erc20Address, eventName: 'Approval' }) + + /** + * Wraps __{@link useWatchContractEvent}__ with \`abi\` set to __{@link erc20Abi}__ and \`eventName\` set to \`"Transfer"\` + */ + export const useErc20TransferEvent = /*#__PURE__*/ createUseWatchContractEvent({ abi: erc20Abi, address: erc20Address, eventName: 'Transfer' })" + `) +}) diff --git a/packages/cli/src/plugins/react.ts b/packages/cli/src/plugins/react.ts new file mode 100644 index 0000000000..b76ea006a5 --- /dev/null +++ b/packages/cli/src/plugins/react.ts @@ -0,0 +1,312 @@ +import { pascalCase } from 'change-case' + +import type { Contract, Plugin } from '../config.js' +import type { Compute, RequiredBy } from '../types.js' +import { getAddressDocString } from '../utils/getAddressDocString.js' + +export type ReactConfig = { + getHookName?: + | 'legacy' // TODO: Deprecate `'legacy'` option + | ((options: { + contractName: string + itemName?: string | undefined + type: 'read' | 'simulate' | 'watch' | 'write' + }) => `use${string}`) +} + +type ReactResult = Compute> + +export function react(config: ReactConfig = {}): ReactResult { + return { + name: 'React', + async run({ contracts }) { + const imports = new Set([]) + const content: string[] = [] + const pure = '/*#__PURE__*/' + + const hookNames = new Set() + for (const contract of contracts) { + let hasReadFunction = false + let hasWriteFunction = false + let hasEvent = false + const readItems = [] + const writeItems = [] + const eventItems = [] + for (const item of contract.abi) { + if (item.type === 'function') + if ( + item.stateMutability === 'view' || + item.stateMutability === 'pure' + ) { + hasReadFunction = true + readItems.push(item) + } else { + hasWriteFunction = true + writeItems.push(item) + } + else if (item.type === 'event') { + hasEvent = true + eventItems.push(item) + } + } + + let innerContent: string + if (contract.meta.addressName) + innerContent = `abi: ${contract.meta.abiName}, address: ${contract.meta.addressName}` + else innerContent = `abi: ${contract.meta.abiName}` + + if (hasReadFunction) { + const hookName = getHookName(config, hookNames, 'read', contract.name) + const docString = genDocString('useReadContract', contract) + const functionName = 'createUseReadContract' + imports.add(functionName) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent} })`, + ) + + const names = new Set() + for (const item of readItems) { + if (item.type !== 'function') continue + if ( + item.stateMutability !== 'pure' && + item.stateMutability !== 'view' + ) + continue + + // Skip overrides since they are captured by same hook + if (names.has(item.name)) continue + names.add(item.name) + + const hookName = getHookName( + config, + hookNames, + 'read', + contract.name, + item.name, + ) + const docString = genDocString('useReadContract', contract, { + name: 'functionName', + value: item.name, + }) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent}, functionName: '${item.name}' })`, + ) + } + } + + if (hasWriteFunction) { + { + const hookName = getHookName( + config, + hookNames, + 'write', + contract.name, + ) + const docString = genDocString('useWriteContract', contract) + const functionName = 'createUseWriteContract' + imports.add(functionName) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent} })`, + ) + + const names = new Set() + for (const item of writeItems) { + if (item.type !== 'function') continue + if ( + item.stateMutability !== 'nonpayable' && + item.stateMutability !== 'payable' + ) + continue + + // Skip overrides since they are captured by same hook + if (names.has(item.name)) continue + names.add(item.name) + + const hookName = getHookName( + config, + hookNames, + 'write', + contract.name, + item.name, + ) + const docString = genDocString('useWriteContract', contract, { + name: 'functionName', + value: item.name, + }) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent}, functionName: '${item.name}' })`, + ) + } + } + + { + const hookName = getHookName( + config, + hookNames, + 'simulate', + contract.name, + ) + const docString = genDocString('useSimulateContract', contract) + const functionName = 'createUseSimulateContract' + imports.add(functionName) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent} })`, + ) + + const names = new Set() + for (const item of writeItems) { + if (item.type !== 'function') continue + if ( + item.stateMutability !== 'nonpayable' && + item.stateMutability !== 'payable' + ) + continue + + // Skip overrides since they are captured by same hook + if (names.has(item.name)) continue + names.add(item.name) + + const hookName = getHookName( + config, + hookNames, + 'simulate', + contract.name, + item.name, + ) + const docString = genDocString('useSimulateContract', contract, { + name: 'functionName', + value: item.name, + }) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent}, functionName: '${item.name}' })`, + ) + } + } + } + + if (hasEvent) { + const hookName = getHookName( + config, + hookNames, + 'watch', + contract.name, + ) + const docString = genDocString('useWatchContractEvent', contract) + const functionName = 'createUseWatchContractEvent' + imports.add(functionName) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent} })`, + ) + + const names = new Set() + for (const item of eventItems) { + if (item.type !== 'event') continue + + // Skip overrides since they are captured by same hook + if (names.has(item.name)) continue + names.add(item.name) + + const hookName = getHookName( + config, + hookNames, + 'watch', + contract.name, + item.name, + ) + const docString = genDocString('useWatchContractEvent', contract, { + name: 'eventName', + value: item.name, + }) + content.push( + `${docString} +export const ${hookName} = ${pure} ${functionName}({ ${innerContent}, eventName: '${item.name}' })`, + ) + } + } + } + + const importValues = [...imports.values()] + + return { + imports: importValues.length + ? `import { ${importValues.join(', ')} } from 'wagmi/codegen'\n` + : '', + content: content.join('\n\n'), + } + }, + } +} + +function genDocString( + hookName: string, + contract: Contract, + item?: { name: string; value: string }, +) { + let description = `Wraps __{@link ${hookName}}__ with \`abi\` set to __{@link ${contract.meta.abiName}}__` + if (item) description += ` and \`${item.name}\` set to \`"${item.value}"\`` + + const docString = getAddressDocString({ address: contract.address }) + if (docString) + return `/** + * ${description} + * + ${docString} + */` + + return `/** + * ${description} + */` +} + +function getHookName( + config: ReactConfig, + hookNames: Set, + type: 'read' | 'simulate' | 'watch' | 'write', + contractName: string, + itemName?: string | undefined, +) { + const ContractName = pascalCase(contractName) + const ItemName = itemName ? pascalCase(itemName) : undefined + + let hookName: string + if (typeof config.getHookName === 'function') + hookName = config.getHookName({ + type, + contractName: ContractName, + itemName: ItemName, + }) + else if (typeof config.getHookName === 'string') { + switch (type) { + case 'read': + hookName = `use${ContractName}${ItemName ?? 'Read'}` + break + case 'simulate': + hookName = `usePrepare${ContractName}${ItemName ?? 'Write'}` + break + case 'watch': + hookName = `use${ContractName}${ItemName ?? ''}Event` + break + case 'write': + hookName = `use${ContractName}${ItemName ?? 'Write'}` + break + } + } else { + hookName = `use${pascalCase(type)}${ContractName}${ItemName ?? ''}` + if (type === 'watch') hookName = `${hookName}Event` + } + + if (hookNames.has(hookName)) + throw new Error( + `Hook name "${hookName}" must be unique for contract "${contractName}". Try using \`getHookName\` to create a unique name.`, + ) + + hookNames.add(hookName) + return hookName +} diff --git a/packages/cli/src/plugins/sourcify.test.ts b/packages/cli/src/plugins/sourcify.test.ts new file mode 100644 index 0000000000..842a291147 --- /dev/null +++ b/packages/cli/src/plugins/sourcify.test.ts @@ -0,0 +1,83 @@ +import { http, HttpResponse } from 'msw' +import { setupServer } from 'msw/node' +import { afterAll, afterEach, beforeAll, expect, test } from 'vitest' + +import { depositAbi } from '../../test/constants.js' +import { sourcify } from './sourcify.js' + +const baseUrl = 'https://sourcify.dev/server/v2/contract' +const address = '0x00000000219ab540356cbb839cbe05303d7705fa' +const chainId = 1 +const multichainAddress = '0xC4c622862a8F548997699bE24EA4bc504e5cA865' +const multichainIdGnosis = 100 +const multichainIdPolygon = 137 +const successJson = { + abi: depositAbi, +} + +const handlers = [ + http.get(`${baseUrl}/${chainId}/${address}`, () => + HttpResponse.json(successJson), + ), + http.get(`${baseUrl}/${multichainIdGnosis}/${address}`, () => + HttpResponse.json({}, { status: 404 }), + ), + http.get(`${baseUrl}/${multichainIdGnosis}/${multichainAddress}`, () => + HttpResponse.json(successJson), + ), + http.get(`${baseUrl}/${multichainIdPolygon}/${multichainAddress}`, () => + HttpResponse.json(successJson), + ), +] + +const server = setupServer(...handlers) + +beforeAll(() => server.listen()) +afterEach(() => server.resetHandlers()) +afterAll(() => server.close()) + +test('fetches ABI', () => { + expect( + sourcify({ + chainId: chainId, + contracts: [{ name: 'DepositContract', address }], + }).contracts?.(), + ).resolves.toMatchSnapshot() +}) + +test('fetches ABI with multichain deployment', () => { + expect( + sourcify({ + chainId: 100, + contracts: [ + { + name: 'Community', + address: { 100: multichainAddress, 137: multichainAddress }, + }, + ], + }).contracts?.(), + ).resolves.toMatchSnapshot() +}) + +test('fails to fetch for unverified contract', () => { + expect( + sourcify({ + chainId: 100, + contracts: [{ name: 'DepositContract', address }], + }).contracts?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + '[Error: Contract not found in Sourcify repository.]', + ) +}) + +test('missing address for chainId', () => { + expect( + sourcify({ + chainId: 1, + // @ts-expect-error `chainId` and `keyof typeof contracts[number].address` mismatch + contracts: [{ name: 'DepositContract', address: { 10: address } }], + }).contracts?.(), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: No address found for chainId "1". Make sure chainId "1" is set as an address.]`, + ) +}) diff --git a/packages/cli/src/plugins/sourcify.ts b/packages/cli/src/plugins/sourcify.ts new file mode 100644 index 0000000000..0d452046ca --- /dev/null +++ b/packages/cli/src/plugins/sourcify.ts @@ -0,0 +1,312 @@ +import { Abi as AbiSchema } from 'abitype/zod' +import type { Address } from 'viem' +import { z } from 'zod' + +import type { ContractConfig } from '../config.js' +import { fromZodError } from '../errors.js' +import type { Compute } from '../types.js' +import { fetch } from './fetch.js' + +export type SourcifyConfig = { + /** + * Duration in milliseconds to cache ABIs. + * + * @default 1_800_000 // 30m in ms + */ + cacheDuration?: number | undefined + /** + * Chain id to use for fetching ABI. + * + * If `address` is an object, `chainId` is used to select the address. + * + * See https://docs.sourcify.dev/docs/chains for supported chains. + */ + chainId: (chainId extends ChainId ? chainId : never) | (ChainId & {}) + /** + * Contracts to fetch ABIs for. + */ + contracts: Compute, 'abi'>>[] +} + +const SourcifyResponse = z.object({ + abi: AbiSchema, +}) + +/** Fetches contract ABIs from Sourcify. */ +export function sourcify( + config: SourcifyConfig, +) { + const { cacheDuration, chainId, contracts: contracts_ } = config + + const contracts = contracts_.map((x) => ({ + ...x, + address: + typeof x.address === 'string' ? { [chainId]: x.address } : x.address, + })) as Omit[] + + return fetch({ + cacheDuration, + contracts, + async parse({ response }) { + if (response.status === 404) + throw new Error('Contract not found in Sourcify repository.') + + const json = await response.json() + const parsed = await SourcifyResponse.safeParseAsync(json) + if (!parsed.success) + throw fromZodError(parsed.error, { prefix: 'Invalid response' }) + + if (parsed.data.abi) return parsed.data.abi as ContractConfig['abi'] + throw new Error('contract not found') + }, + request({ address }) { + if (!address) throw new Error('address is required') + + let contractAddress: Address | undefined + if (typeof address === 'string') contractAddress = address + else if (typeof address === 'object') contractAddress = address[chainId] + + if (!contractAddress) + throw new Error( + `No address found for chainId "${chainId}". Make sure chainId "${chainId}" is set as an address.`, + ) + return { + url: `https://sourcify.dev/server/v2/contract/${chainId}/${contractAddress}?fields=abi`, + } + }, + }) +} + +// Supported chains +// https://docs.sourcify.dev/docs/chains +type ChainId = + | 1 // Ethereum Mainnet + | 17000 // Ethereum Testnet Holesky + | 5 // Ethereum Testnet Goerli + | 11155111 // Ethereum Testnet Sepolia + | 3 // Ethereum Testnet Ropsten + | 4 // Ethereum Testnet Rinkeby + | 10 // OP Mainnet + | 100 // Gnosis + | 100009 // VeChain + | 100010 // VeChain Testnet + | 1001 // Kaia Kairos Testnet + | 10200 // Gnosis Chiado Testnet + | 10242 // Arthera Mainnet + | 10243 // Arthera Testnet + | 1030 // Conflux eSpace + | 103090 // Crystaleum + | 105105 // Stratis Mainnet + | 106 // Velas EVM Mainnet + | 10849 // Lamina1 + | 10850 // Lamina1 Identity + | 1088 // Metis Andromeda Mainnet + | 1101 // Polygon zkEVM + | 111000 // Siberium Test Network + | 11111 // WAGMI + | 1114 // Core Blockchain Testnet2 + | 1115 // Core Blockchain Testnet + | 11155420 // OP Sepolia Testnet + | 1116 // Core Blockchain Mainnet + | 11235 // Haqq Network + | 1127469 // Tiltyard Subnet + | 11297108099 // Palm Testnet + | 11297108109 // Palm + | 1149 // Symplexia Smart Chain + | 122 // Fuse Mainnet + | 1284 // Moonbeam + | 1285 // Moonriver + | 1287 // Moonbase Alpha + | 12898 // PlayFair Testnet Subnet + | 1291 // Swisstronik Testnet + | 1313161554 // Aurora Mainnet + | 1313161555 // Aurora Testnet + | 13337 // Beam Testnet + | 13381 // Phoenix Mainnet + | 1339 // Elysium Mainnet + | 137 // Polygon Mainnet + | 14 // Flare Mainnet + | 1433 // Rikeza Network Mainnet + | 1516 // Story Odyssey Testnet + | 16180 // PLYR PHI + | 16350 // Incentiv Devnet + | 167005 // Taiko Grimsvotn L2 + | 167006 // Taiko Eldfell L3 + | 17069 // Garnet Holesky + | 180 // AME Chain Mainnet + | 1890 // Lightlink Phoenix Mainnet + | 1891 // Lightlink Pegasus Testnet + | 19 // Songbird Canary-Network + | 19011 // HOME Verse Mainnet + | 192837465 // Gather Mainnet Network + | 2000 // Dogechain Mainnet + | 200810 // Bitlayer Testnet + | 200901 // Bitlayer Mainnet + | 2017 // Adiri + | 2020 // Ronin Mainnet + | 2021 // Edgeware EdgeEVM Mainnet + | 202401 // YMTECH-BESU Testnet + | 2037 // Kiwi Subnet + | 2038 // Shrapnel Testnet + | 2044 // Shrapnel Subnet + | 2047 // Stratos Testnet + | 2048 // Stratos + | 205205 // Auroria Testnet + | 212 // MAPO Makalu + | 216 // Happychain Testnet + | 222000222 // Kanazawa + | 2221 // Kava Testnet + | 2222 // Kava + | 223 // B2 Mainnet + | 22776 // MAP Protocol + | 23294 // Oasis Sapphire + | 23295 // Oasis Sapphire Testnet + | 2358 // Kroma Sepolia + | 2442 // Polygon zkEVM Cardona Testnet + | 246 // Energy Web Chain + | 25 // Cronos Mainnet + | 250 // Fantom Opera + | 252 // Fraxtal + | 2522 // Fraxtal Testnet + | 255 // Kroma + | 25925 // KUB Testnet + | 26100 // Ferrum Quantum Portal Network + | 28 // Boba Network Rinkeby Testnet + | 28528 // Optimism Bedrock (Goerli Alpha Testnet) + | 288 // Boba Network + | 295 // Hedera Mainnet + | 30 // Rootstock Mainnet + | 300 // zkSync Sepolia Testnet + | 311752642 // OneLedger Mainnet + | 314 // Filecoin - Mainnet + | 314159 // Filecoin - Calibration testnet + | 32769 // Zilliqa EVM + | 32770 // Zilliqa 2 EVM proto-mainnet + | 33101 // Zilliqa EVM Testnet + | 33103 // Zilliqa 2 EVM proto-testnet + | 33111 // Curtis + | 333000333 // Meld + | 335 // DFK Chain Test + | 336 // Shiden + | 34443 // Mode + | 35441 // Q Mainnet + | 35443 // Q Testnet + | 356256156 // Gather Testnet Network + | 369 // PulseChain + | 3737 // Crossbell + | 37714555429 // Xai Testnet v2 + | 383414847825 // Zeniq + | 39797 // Energi Mainnet + | 40 // Telos EVM Mainnet + | 4000 // Ozone Chain Mainnet + | 41 // Telos EVM Testnet + | 4157 // CrossFi Testnet + | 420 // Optimism Goerli Testnet + | 4200 // Merlin Mainnet + | 420420 // Kekchain + | 420666 // Kekchain (kektest) + | 42161 // Arbitrum One + | 421611 // Arbitrum Rinkeby + | 421613 // Arbitrum Goerli + | 4216137055 // OneLedger Testnet Frankenstein + | 421614 // Arbitrum Sepolia + | 42170 // Arbitrum Nova + | 42220 // Celo Mainnet + | 42261 // Oasis Emerald Testnet + | 42262 // Oasis Emerald + | 42766 // ZKFair Mainnet + | 43 // Darwinia Pangolin Testnet + | 43113 // Avalanche Fuji Testnet + | 43114 // Avalanche C-Chain + | 432201 // Dexalot Subnet Testnet + | 432204 // Dexalot Subnet + | 4337 // Beam + | 44 // Crab Network + | 44787 // Celo Alfajores Testnet + | 46 // Darwinia Network + | 486217935 // Gather Devnet Network + | 48898 // Zircuit Garfield Testnet + | 48899 // Zircuit Testnet + | 48900 // Zircuit Mainnet + | 49797 // Energi Testnet + | 50 // XDC Network + | 5000 // Mantle + | 5003 // Mantle Sepolia Testnet + | 51 // XDC Apothem Network + | 5115 // Citrea Testnet + | 534 // Candle + | 534351 // Scroll Sepolia Testnet + | 534352 // Scroll + | 53935 // DFK Chain + | 54211 // Haqq Chain Testnet + | 56 // BNB Smart Chain Mainnet + | 560048 // Hoodi testnet + | 57 // Syscoin Mainnet + | 570 // Rollux Mainnet + | 5700 // Syscoin Tanenbaum Testnet + | 57000 // Rollux Testnet + | 5845 // Tangle + | 59141 // Linea Sepolia + | 59144 // Linea + | 592 // Astar + | 59902 // Metis Sepolia Testnet + | 61 // Ethereum Classic + | 6119 // UPTN + | 62320 // Celo Baklava Testnet + | 62621 // MultiVAC Mainnet + | 62831 // PLYR TAU Testnet + | 6321 // Aura Euphoria Testnet + | 6322 // Aura Mainnet + | 641230 // Bear Network Chain Mainnet + | 648 // Endurance Smart Chain Mainnet + | 660279 // Xai Mainnet + | 666666666 // Degen Chain + | 69 // Optimism Kovan + | 690 // Redstone + | 7000 // ZetaChain Mainnet + | 7001 // ZetaChain Testnet + | 7078815900 // Mekong + | 710420 // Tiltyard Mainnet Subnet + | 71401 // Godwoken Testnet v1 + | 71402 // Godwoken Mainnet + | 7171 // Bitrock Mainnet + | 7200 // exSat Mainnet + | 723107 // TixChain Testnet + | 73799 // Energy Web Volta Testnet + | 764984 // Lamina1 Testnet + | 7668 // The Root Network - Mainnet + | 7672 // The Root Network - Porcini Testnet + | 767368 // Lamina1 Identity Testnet + | 77 // POA Network Sokol + | 7700 // Canto + | 7701 // Canto Tesnet + | 7771 // Bitrock Testnet + | 7777777 // Zora + | 78430 // Amplify Subnet + | 78431 // Bulletin Subnet + | 78432 // Conduit Subnet + | 8 // Ubiq + | 80001 // Mumbai + | 80002 // Amoy + | 82 // Meter Mainnet + | 8217 // Kaia Mainnet + | 83 // Meter Testnet + | 839999 // exSat Testnet + | 841 // Taraxa Mainnet + | 842 // Taraxa Testnet + | 8453 // Base + | 84531 // Base Goerli Testnet + | 84532 // Base Sepolia Testnet + | 888 // Wanchain + | 9000 // Evmos Testnet + | 9001 // Evmos + | 919 // Mode Testnet + | 957 // Lyra Chain + | 96 // KUB Mainnet + | 97 // BNB Smart Chain Testnet + | 970 // Oort Mainnet + | 99 // POA Network Core + | 9977 // Mind Smart Chain Testnet + | 999 // Wanchain Testnet + | 9996 // Mind Smart Chain Mainnet + | 999999999 // Zora Sepolia Testnet diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts new file mode 100644 index 0000000000..adb2889348 --- /dev/null +++ b/packages/cli/src/types.ts @@ -0,0 +1,10 @@ +export type Compute = { [key in keyof type]: type[key] } & unknown + +export type MaybeArray = T | T[] + +export type MaybePromise = T | Promise + +export type RequiredBy = Required< + Pick +> & + Omit diff --git a/packages/cli/src/utils/findConfig.test.ts b/packages/cli/src/utils/findConfig.test.ts new file mode 100644 index 0000000000..52c2066773 --- /dev/null +++ b/packages/cli/src/utils/findConfig.test.ts @@ -0,0 +1,42 @@ +import { afterEach, expect, test, vi } from 'vitest' + +import { createFixture } from '../../test/utils.js' +import { findConfig } from './findConfig.js' + +afterEach(() => { + vi.restoreAllMocks() +}) + +test('finds config file', async () => { + const { dir, paths } = await createFixture({ + files: { 'wagmi.config.ts': '' }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(findConfig()).resolves.toBe(paths['wagmi.config.ts']) +}) + +test('finds config file at location', async () => { + const { dir, paths } = await createFixture({ + files: { 'wagmi.config.ts': '' }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(findConfig({ config: paths['wagmi.config.ts'] })).resolves.toBe( + paths['wagmi.config.ts'], + ) +}) + +test('finds config file at root', async () => { + const { dir, paths } = await createFixture({ + files: { 'wagmi.config.ts': '' }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(findConfig({ root: dir })).resolves.toBe( + paths['wagmi.config.ts'], + ) +}) diff --git a/packages/cli/src/utils/findConfig.ts b/packages/cli/src/utils/findConfig.ts new file mode 100644 index 0000000000..2e94e2d5a4 --- /dev/null +++ b/packages/cli/src/utils/findConfig.ts @@ -0,0 +1,39 @@ +import { existsSync } from 'node:fs' +import escalade from 'escalade' +import { resolve } from 'pathe' + +// Do not reorder +// In order of preference files are checked +const configFiles = [ + 'wagmi.config.ts', + 'wagmi.config.js', + 'wagmi.config.mjs', + 'wagmi.config.mts', +] + +type FindConfigParameters = { + /** Config file name */ + config?: string | undefined + /** Config file directory */ + root?: string | undefined +} + +/** + * Resolves path to wagmi CLI config file. + */ +export async function findConfig(parameters: FindConfigParameters = {}) { + const { config, root } = parameters + const rootDir = resolve(root || process.cwd()) + if (config) { + const path = resolve(rootDir, config) + if (existsSync(path)) return path + return + } + const configPath = await escalade(rootDir, (_dir, names) => { + for (const name of names) { + if (configFiles.includes(name)) return name + } + return undefined + }) + return configPath +} diff --git a/packages/cli/src/utils/format.test.ts b/packages/cli/src/utils/format.test.ts new file mode 100644 index 0000000000..6d04eb5465 --- /dev/null +++ b/packages/cli/src/utils/format.test.ts @@ -0,0 +1,12 @@ +import { expect, test } from 'vitest' + +import { format } from './format.js' + +test('formats code', async () => { + await expect( + format(`const foo = "bar"`), + ).resolves.toMatchInlineSnapshot(` + "const foo = 'bar' + " + `) +}) diff --git a/packages/cli/src/utils/format.ts b/packages/cli/src/utils/format.ts new file mode 100644 index 0000000000..d440e60471 --- /dev/null +++ b/packages/cli/src/utils/format.ts @@ -0,0 +1,17 @@ +import prettier from 'prettier' + +export async function format(content: string) { + const config = await prettier.resolveConfig(process.cwd()) + return prettier.format(content, { + arrowParens: 'always', + endOfLine: 'lf', + parser: 'typescript', + printWidth: 80, + semi: false, + singleQuote: true, + tabWidth: 2, + trailingComma: 'all', + ...config, + plugins: [], + }) +} diff --git a/packages/cli/src/utils/getAddressDocString.test.ts b/packages/cli/src/utils/getAddressDocString.test.ts new file mode 100644 index 0000000000..798e6e14e5 --- /dev/null +++ b/packages/cli/src/utils/getAddressDocString.test.ts @@ -0,0 +1,40 @@ +import { expect, test } from 'vitest' + +import { getAddressDocString } from './getAddressDocString.js' + +test('address', async () => { + expect( + getAddressDocString({ + address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + }), + ).toMatchInlineSnapshot('""') +}) + +test('multichain address with known chain ids', async () => { + expect( + getAddressDocString({ + address: { + 1: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + 5: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + 10: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + }, + }), + ).toMatchInlineSnapshot(` + "* - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e) + * - [__View Contract on Goerli Etherscan__](https://goerli.etherscan.io/address/0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e) + * - [__View Contract on Op Mainnet Optimism Explorer__](https://optimistic.etherscan.io/address/0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e)" + `) +}) + +test('multichain address with unknown chain id', async () => { + expect( + getAddressDocString({ + address: { + 1: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + 2: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + }, + }), + ).toMatchInlineSnapshot( + '"* [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e)"', + ) +}) diff --git a/packages/cli/src/utils/getAddressDocString.ts b/packages/cli/src/utils/getAddressDocString.ts new file mode 100644 index 0000000000..d0e137928c --- /dev/null +++ b/packages/cli/src/utils/getAddressDocString.ts @@ -0,0 +1,53 @@ +import { capitalCase } from 'change-case' +import dedent from 'dedent' +import * as allChains from 'viem/chains' + +import type { Contract } from '../config.js' + +const chainMap: Record = {} +for (const chain of Object.values(allChains)) { + if (typeof chain !== 'object') continue + if (!('id' in chain)) continue + chainMap[chain.id] = chain +} + +export function getAddressDocString(parameters: { + address: Contract['address'] +}) { + const { address } = parameters + if (!address || typeof address === 'string') return '' + + if (Object.keys(address).length === 1) + return `* ${getLink({ + address: address[Number.parseInt(Object.keys(address)[0]!)]!, + chainId: Number.parseInt(Object.keys(address)[0]!), + })}` + + const addresses = Object.entries(address).filter( + (x) => chainMap[Number.parseInt(x[0])], + ) + if (addresses.length === 0) return '' + if (addresses.length === 1 && addresses[0]) + return `* ${getLink({ + address: addresses[0][1], + chainId: Number.parseInt(addresses[0][0])!, + })}` + + return dedent` + ${addresses.reduce((prev, curr) => { + const chainId = Number.parseInt(curr[0]) + const address = curr[1] + return `${prev}\n* - ${getLink({ address, chainId })}` + }, '')} + ` +} + +function getLink({ address, chainId }: { address: string; chainId: number }) { + const chain = chainMap[chainId] + if (!chain) return '' + const blockExplorer = chain.blockExplorers?.default + if (!blockExplorer) return '' + return `[__View Contract on ${capitalCase(chain.name)} ${capitalCase( + blockExplorer.name, + )}__](${blockExplorer.url}/address/${address})` +} diff --git a/packages/cli/src/utils/getIsUsingTypeScript.test.ts b/packages/cli/src/utils/getIsUsingTypeScript.test.ts new file mode 100644 index 0000000000..3ba7c86a65 --- /dev/null +++ b/packages/cli/src/utils/getIsUsingTypeScript.test.ts @@ -0,0 +1,43 @@ +import { afterEach, expect, test, vi } from 'vitest' + +import { createFixture } from '../../test/utils.js' +import { getIsUsingTypeScript } from './getIsUsingTypeScript.js' + +afterEach(() => { + vi.restoreAllMocks() +}) + +test('true if has tsconfig', async () => { + const { dir } = await createFixture({ + files: { + 'tsconfig.json': '', + }, + }) + + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(getIsUsingTypeScript()).resolves.toBe(true) +}) + +test('true if has wagmi.config', async () => { + const { dir } = await createFixture({ + files: { + 'wagmi.config.ts': '', + }, + }) + + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(getIsUsingTypeScript()).resolves.toBe(true) +}) + +test('false', async () => { + const { dir } = await createFixture() + + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + await expect(getIsUsingTypeScript()).resolves.toBe(false) +}) diff --git a/packages/cli/src/utils/getIsUsingTypeScript.ts b/packages/cli/src/utils/getIsUsingTypeScript.ts new file mode 100644 index 0000000000..bd2383b0eb --- /dev/null +++ b/packages/cli/src/utils/getIsUsingTypeScript.ts @@ -0,0 +1,33 @@ +import escalade from 'escalade' + +export async function getIsUsingTypeScript() { + try { + const cwd = process.cwd() + const tsconfig = await escalade(cwd, (_dir, names) => { + const files = [ + 'tsconfig.json', + 'tsconfig.base.json', + 'tsconfig.lib.json', + 'tsconfig.node.json', + ] + for (const name of names) { + if (files.includes(name)) return name + } + return undefined + }) + if (tsconfig) return true + + const wagmiConfig = await escalade(cwd, (_dir, names) => { + const files = ['wagmi.config.ts', 'wagmi.config.mts'] + for (const name of names) { + if (files.includes(name)) return name + } + return undefined + }) + if (wagmiConfig) return true + + return false + } catch { + return false + } +} diff --git a/packages/cli/src/utils/loadEnv.test.ts b/packages/cli/src/utils/loadEnv.test.ts new file mode 100644 index 0000000000..d577778eb0 --- /dev/null +++ b/packages/cli/src/utils/loadEnv.test.ts @@ -0,0 +1,77 @@ +import { afterEach, expect, test, vi } from 'vitest' + +import { createFixture } from '../../test/utils.js' +import { loadEnv } from './loadEnv.js' + +afterEach(() => { + vi.restoreAllMocks() +}) + +test('loads env', async () => { + const { dir } = await createFixture({ + files: { + '.env': ` + FOO=bar + SOME_ENV_VAR=1 + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + expect(loadEnv()).toMatchInlineSnapshot(` + { + "FOO": "bar", + "SOME_ENV_VAR": "1", + } + `) +}) + +test('loads env from envDir', async () => { + const { dir } = await createFixture({ + files: { + '.env': ` + FOO=bar + SOME_ENV_VAR=1 + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + expect(loadEnv({ envDir: dir })).toMatchInlineSnapshot(` + { + "FOO": "bar", + "SOME_ENV_VAR": "1", + } + `) +}) + +test('loads env with mode', async () => { + const mode = 'dev' + const { dir } = await createFixture({ + files: { + [`.env.${mode}`]: ` + FOO=bar + SOME_ENV_VAR=1 + `, + }, + }) + const spy = vi.spyOn(process, 'cwd') + spy.mockImplementation(() => dir) + + expect(loadEnv({ mode })).toMatchInlineSnapshot(` + { + "FOO": "bar", + "SOME_ENV_VAR": "1", + } + `) +}) + +test('throws error when mode is "local"', async () => { + expect(() => { + loadEnv({ mode: 'local' }) + }).toThrowErrorMatchingInlineSnapshot( + `[Error: "local" cannot be used as a mode name because it conflicts with the .local postfix for .env files.]`, + ) +}) diff --git a/packages/cli/src/utils/loadEnv.ts b/packages/cli/src/utils/loadEnv.ts new file mode 100644 index 0000000000..d7ffa99919 --- /dev/null +++ b/packages/cli/src/utils/loadEnv.ts @@ -0,0 +1,90 @@ +import { parse } from 'dotenv' +import { expand } from 'dotenv-expand' + +import { existsSync, readFileSync, statSync } from 'node:fs' +import { dirname, join } from 'node:path' + +// https://github.com/vitejs/vite/blob/main/packages/vite/src/node/env.ts#L7 +export function loadEnv( + config: { + mode?: string + envDir?: string + } = {}, +): Record { + const mode = config.mode + if (mode === 'local') { + throw new Error( + `"local" cannot be used as a mode name because it conflicts with the .local postfix for .env files.`, + ) + } + + const envFiles = [ + /** default file */ '.env', + /** local file */ '.env.local', + ...(mode + ? [ + /** mode file */ `.env.${mode}`, + /** mode local file */ `.env.${mode}.local`, + ] + : []), + ] + + const envDir = config.envDir ?? process.cwd() + const parsed = Object.fromEntries( + envFiles.flatMap((file) => { + const path = lookupFile(envDir, [file], { + pathOnly: true, + rootDir: envDir, + }) + if (!path) return [] + return Object.entries(parse(readFileSync(path))) + }), + ) + + try { + // let environment variables use each other + expand({ parsed }) + } catch (error) { + // custom error handling until https://github.com/motdotla/dotenv-expand/issues/65 is fixed upstream + // check for message "TypeError: Cannot read properties of undefined (reading 'split')" + if ((error as Error).message.includes('split')) { + throw new Error( + 'dotenv-expand failed to expand env vars. Maybe you need to escape `$`?', + ) + } + throw error + } + + return parsed +} + +function lookupFile( + dir: string, + formats: string[], + options?: { + pathOnly?: boolean + rootDir?: string + predicate?: (file: string) => boolean + }, +): string | undefined { + for (const format of formats) { + const fullPath = join(dir, format) + if (existsSync(fullPath) && statSync(fullPath).isFile()) { + const result = options?.pathOnly + ? fullPath + : readFileSync(fullPath, 'utf-8') + if (!options?.predicate || options.predicate(result)) { + return result + } + } + } + + const parentDir = dirname(dir) + if ( + parentDir !== dir && + (!options?.rootDir || parentDir.startsWith(options?.rootDir)) + ) + return lookupFile(parentDir, formats, options) + + return undefined +} diff --git a/packages/cli/src/utils/packages.test.ts b/packages/cli/src/utils/packages.test.ts new file mode 100644 index 0000000000..96ea2e26ef --- /dev/null +++ b/packages/cli/src/utils/packages.test.ts @@ -0,0 +1,19 @@ +import { expect, test } from 'vitest' + +import { getIsPackageInstalled, getPackageManager } from './packages.js' + +test('getIsPackageInstalled: true', async () => { + await expect(getIsPackageInstalled({ packageName: 'vitest' })).resolves.toBe( + true, + ) +}) + +test('getIsPackageInstalled: false', async () => { + await expect( + getIsPackageInstalled({ packageName: 'vitest-unknown' }), + ).resolves.toBe(false) +}) + +test('getPackageManager', async () => { + await expect(getPackageManager()).resolves.toMatchInlineSnapshot('"pnpm"') +}) diff --git a/packages/cli/src/utils/packages.ts b/packages/cli/src/utils/packages.ts new file mode 100644 index 0000000000..e55eeaf0ea --- /dev/null +++ b/packages/cli/src/utils/packages.ts @@ -0,0 +1,124 @@ +import { execSync } from 'node:child_process' +import { promises as fs } from 'node:fs' +import { resolve } from 'node:path' + +export async function getIsPackageInstalled(parameters: { + packageName: string + cwd?: string +}) { + const { packageName, cwd = process.cwd() } = parameters + try { + const packageManager = await getPackageManager() + const command = (() => { + switch (packageManager) { + case 'yarn': + return ['why', packageName] + case 'bun': + return ['pm', 'ls', '--all'] + default: + return ['ls', packageName] + } + })() + + const result = execSync(`${packageManager} ${command.join(' ')}`, { + cwd, + encoding: 'utf-8', + stdio: 'pipe', + }) + + // For Bun, we need to check if the package name is in the output + if (packageManager === 'bun') return result.includes(packageName) + + return result !== '' + } catch (_error) { + return false + } +} + +export async function getPackageManager(executable?: boolean | undefined) { + const userAgent = process.env.npm_config_user_agent + if (userAgent) { + if (userAgent.includes('pnpm')) return 'pnpm' + // The yarn@^3 user agent includes npm, so yarn must be checked first. + if (userAgent.includes('yarn')) return 'yarn' + if (userAgent.includes('npm')) return executable ? 'npx' : 'npm' + if (userAgent.includes('bun')) return executable ? 'bunx' : 'bun' + } + + const packageManager = await detect() + if (packageManager === 'npm' && executable) return 'npx' + return packageManager +} + +type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun' + +async function detect( + parameters: { cwd?: string; includeGlobalBun?: boolean } = {}, +) { + const { cwd, includeGlobalBun } = parameters + const type = await getTypeofLockFile(cwd) + if (type) { + return type + } + const [hasYarn, hasPnpm, hasBun] = await Promise.all([ + hasGlobalInstallation('yarn'), + hasGlobalInstallation('pnpm'), + includeGlobalBun && hasGlobalInstallation('bun'), + ]) + if (hasYarn) return 'yarn' + if (hasPnpm) return 'pnpm' + if (hasBun) return 'bun' + return 'npm' +} + +const cache = new Map() + +function hasGlobalInstallation(pm: PackageManager): boolean { + const key = `has_global_${pm}` + if (cache.has(key)) return cache.get(key) + + try { + const result = execSync(`${pm} --version`, { + encoding: 'utf-8', + stdio: 'pipe', + }) + const isGlobal = /^\d+.\d+.\d+$/.test(result) + cache.set(key, isGlobal) + return isGlobal + } catch { + return false + } +} + +function getTypeofLockFile(cwd = '.'): Promise { + const key = `lockfile_${cwd}` + if (cache.has(key)) { + return Promise.resolve(cache.get(key)) + } + + return Promise.all([ + pathExists(resolve(cwd, 'yarn.lock')), + pathExists(resolve(cwd, 'package-lock.json')), + pathExists(resolve(cwd, 'pnpm-lock.yaml')), + pathExists(resolve(cwd, 'bun.lockb')), + ]).then(([isYarn, isNpm, isPnpm, isBun]) => { + let value: PackageManager | null = null + + if (isYarn) value = 'yarn' + else if (isPnpm) value = 'pnpm' + else if (isBun) value = 'bun' + else if (isNpm) value = 'npm' + + cache.set(key, value) + return value + }) +} + +async function pathExists(p: string) { + try { + await fs.access(p) + return true + } catch { + return false + } +} diff --git a/packages/cli/src/utils/resolveConfig.test.ts b/packages/cli/src/utils/resolveConfig.test.ts new file mode 100644 index 0000000000..6669edc9b6 --- /dev/null +++ b/packages/cli/src/utils/resolveConfig.test.ts @@ -0,0 +1,83 @@ +import { expect, test } from 'vitest' + +import { createFixture } from '../../test/utils.js' +import { defaultConfig } from '../config.js' +import { findConfig } from './findConfig.js' +import { resolveConfig } from './resolveConfig.js' + +test.skip('resolves config', async () => { + const { paths } = await createFixture({ + files: { + 'wagmi.config.ts': ` + import { defineConfig } from '@wagmi/cli' + + export default defineConfig(${JSON.stringify(defaultConfig)}) + `, + }, + }) + + const configPath = await findConfig({ + config: paths['wagmi.config.ts'], + }) + await expect( + resolveConfig({ configPath: configPath! }), + ).resolves.toMatchInlineSnapshot(` + { + "contracts": [], + "out": "src/generated.ts", + "plugins": [], + } + `) +}) + +test.skip('resolves function config', async () => { + const { paths } = await createFixture({ + files: { + 'wagmi.config.ts': ` + import { defineConfig } from '@wagmi/cli' + + export default defineConfig(() => (${JSON.stringify(defaultConfig)})) + `, + }, + }) + + const configPath = await findConfig({ + config: paths['wagmi.config.ts'], + }) + await expect( + resolveConfig({ configPath: configPath! }), + ).resolves.toMatchInlineSnapshot(` + { + "contracts": [], + "out": "src/generated.ts", + "plugins": [], + } + `) +}) + +test.skip('resolves array config', async () => { + const { paths } = await createFixture({ + files: { + 'wagmi.config.ts': ` + import { defineConfig } from '@wagmi/cli' + + export default defineConfig([${JSON.stringify(defaultConfig)}]) + `, + }, + }) + + const configPath = await findConfig({ + config: paths['wagmi.config.ts'], + }) + await expect( + resolveConfig({ configPath: configPath! }), + ).resolves.toMatchInlineSnapshot(` + [ + { + "contracts": [], + "out": "src/generated.ts", + "plugins": [], + }, + ] + `) +}) diff --git a/packages/cli/src/utils/resolveConfig.ts b/packages/cli/src/utils/resolveConfig.ts new file mode 100644 index 0000000000..048b1c337f --- /dev/null +++ b/packages/cli/src/utils/resolveConfig.ts @@ -0,0 +1,21 @@ +import { bundleRequire } from 'bundle-require' + +import type { Config } from '../config.js' +import type { MaybeArray } from '../types.js' + +type ResolveConfigParameters = { + /** Path to config file */ + configPath: string +} + +/** Bundles and returns wagmi config object from path. */ +export async function resolveConfig( + parameters: ResolveConfigParameters, +): Promise> { + const { configPath } = parameters + const res = await bundleRequire({ filepath: configPath }) + let config = res.mod.default + if (config.default) config = config.default + if (typeof config !== 'function') return config + return await config() +} diff --git a/packages/cli/src/version.ts b/packages/cli/src/version.ts new file mode 100644 index 0000000000..166b988ccb --- /dev/null +++ b/packages/cli/src/version.ts @@ -0,0 +1 @@ +export const version = '2.3.1' diff --git a/packages/cli/test/constants.ts b/packages/cli/test/constants.ts new file mode 100644 index 0000000000..9b84c5bb1b --- /dev/null +++ b/packages/cli/test/constants.ts @@ -0,0 +1,32 @@ +import { parseAbi } from 'viem' + +export const wagmiAbi = parseAbi([ + 'constructor()', + 'event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId)', + 'event ApprovalForAll(address indexed owner, address indexed operator, bool approved)', + 'event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)', + 'function approve(address to, uint256 tokenId)', + 'function balanceOf(address owner) view returns (uint256)', + 'function getApproved(uint256 tokenId) view returns (address)', + 'function isApprovedForAll(address owner, address operator) view returns (bool)', + 'function mint()', + 'function name() view returns (string)', + 'function ownerOf(uint256 tokenId) view returns (address)', + 'function safeTransferFrom(address from, address to, uint256 tokenId)', + 'function safeTransferFrom(address from, address to, uint256 tokenId, bytes _data)', + 'function setApprovalForAll(address operator, bool approved)', + 'function supportsInterface(bytes4 interfaceId) view returns (bool)', + 'function symbol() view returns (string)', + 'function tokenURI(uint256 tokenId) pure returns (string)', + 'function totalSupply() view returns (uint256)', + 'function transferFrom(address from, address to, uint256 tokenId)', +]) + +export const depositAbi = parseAbi([ + 'constructor()', + 'event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index)', + 'function deposit(bytes pubkey, bytes withdrawal_credentials, bytes signature, bytes32 deposit_data_root) payable', + 'function get_deposit_count() view returns (bytes)', + 'function get_deposit_root() view returns (bytes32)', + 'function supportsInterface(bytes4 interfaceId) pure returns (bool)', +]) diff --git a/packages/cli/test/setup.ts b/packages/cli/test/setup.ts new file mode 100644 index 0000000000..d2eed4a5e5 --- /dev/null +++ b/packages/cli/test/setup.ts @@ -0,0 +1,57 @@ +import { mkdir } from 'node:fs/promises' +import { homedir } from 'node:os' +import type { createSpinner as nanospinner_createSpinner } from 'nanospinner' +import { join } from 'pathe' +import { vi } from 'vitest' + +const cacheDir = join(homedir(), '.wagmi-cli/plugins/fetch/cache') +await mkdir(cacheDir, { recursive: true }) + +vi.mock('nanospinner', async (importOriginal) => { + const mod = await importOriginal<{ + createSpinner: typeof nanospinner_createSpinner + }>() + + function createSpinner( + initialText: string, + opts: Parameters[1], + ) { + let currentText = '' + const spinner = mod.createSpinner(initialText, opts) + return { + ...spinner, + start(text = initialText) { + // biome-ignore lint/suspicious/noConsoleLog: console.log is used for logging + console.log(`- ${text}`) + spinner.start(text) + currentText = text + }, + success(text = currentText) { + // biome-ignore lint/suspicious/noConsoleLog: console.log is used for logging + console.log(`√ ${text}`) + spinner.success(text) + }, + error(text = currentText) { + console.error(`× ${text}`) + spinner.error(text) + }, + } + } + return { createSpinner } +}) + +vi.mock('picocolors', async () => { + function pass(input: string | number | null | undefined) { + return input + } + return { + default: { + blue: pass, + gray: pass, + green: pass, + red: pass, + white: pass, + yellow: pass, + }, + } +}) diff --git a/packages/cli/test/utils.ts b/packages/cli/test/utils.ts new file mode 100644 index 0000000000..4ea6c6051e --- /dev/null +++ b/packages/cli/test/utils.ts @@ -0,0 +1,292 @@ +import { spawnSync } from 'node:child_process' +import { cp, mkdir, symlink, writeFile } from 'node:fs/promises' +import fixtures from 'fixturez' +import { http, HttpResponse } from 'msw' +import * as path from 'pathe' +import { vi } from 'vitest' + +const f = fixtures(__dirname) + +type Json = + | string + | number + | boolean + | null + | { [property: string]: Json } + | Json[] + +export async function createFixture< + TFiles extends { [filename: string]: string | Json } & { + tsconfig?: true + }, +>( + config: { + copyNodeModules?: boolean + dir?: string + files?: TFiles + } = {}, +) { + const dir = config.dir ?? f.temp() + await mkdir(dir, { recursive: true }) + + // Create test files + const paths: { [_ in keyof TFiles]: string } = {} as any + await Promise.all( + (Object.keys(config.files ?? {}) as (keyof TFiles)[]).map( + async (filename_) => { + let file: Json | true | undefined + let filename = filename_ + if (filename === 'tsconfig') { + filename = 'tsconfig.json' + file = getTsConfig(dir) + } else file = config.files![filename] + + const filePath = path.join(dir, filename.toString()) + await mkdir(path.dirname(filePath), { recursive: true }) + + await writeFile( + filePath, + typeof file === 'string' ? file : JSON.stringify(file, null, 2), + ) + paths[filename === 'tsconfig.json' ? 'tsconfig' : filename] = filePath + }, + ), + ) + + if (config.copyNodeModules) { + await symlink( + path.join(__dirname, '../node_modules'), + path.join(dir, 'node_modules'), + 'dir', + ) + await cp( + path.join(__dirname, '../package.json'), + path.join(dir, 'package.json'), + ) + } + + return { + dir, + paths: paths, + } +} + +type TsConfig = { + compilerOptions: { [property: string]: any } + exclude: string[] + include: string[] +} +function getTsConfig(baseUrl: string) { + return { + compilerOptions: { + allowJs: true, + baseUrl: '.', + esModuleInterop: true, + forceConsistentCasingInFileNames: true, + incremental: true, + isolatedModules: true, + jsx: 'preserve', + lib: ['dom', 'dom.iterable', 'esnext'], + module: 'esnext', + moduleResolution: 'node', + noEmit: true, + paths: { + '@wagmi/cli': [path.relative(baseUrl, 'packages/cli/src')], + '@wagmi/cli/*': [path.relative(baseUrl, 'packages/cli/src/*')], + '@wagmi/connectors': [ + path.relative(baseUrl, 'packages/connectors/src'), + ], + '@wagmi/connectors/*': [ + path.relative(baseUrl, 'packages/connectors/src/*'), + ], + '@wagmi/core': [path.relative(baseUrl, 'packages/core/src')], + '@wagmi/core/*': [path.relative(baseUrl, 'packages/core/src/*')], + wagmi: [path.relative(baseUrl, 'packages/react/src')], + 'wagmi/*': [path.relative(baseUrl, 'packages/react/src/*')], + }, + resolveJsonModule: true, + skipLibCheck: true, + strict: true, + target: 'es6', + }, + include: [`${baseUrl}/**/*.ts`, `${baseUrl}/**/*.tsx`], + exclude: ['node_modules'], + } as TsConfig +} + +export function watchConsole() { + type Console = 'info' | 'log' | 'warn' | 'error' + const output: { [_ in Console | 'all']: string[] } = { + info: [], + log: [], + warn: [], + error: [], + all: [], + } + function handleOutput(method: Console) { + return (message: string) => { + output[method].push(message) + output.all.push(message) + } + } + return { + debug: console.debug, + info: vi.spyOn(console, 'info').mockImplementation(handleOutput('info')), + log: vi.spyOn(console, 'log').mockImplementation(handleOutput('log')), + warn: vi.spyOn(console, 'warn').mockImplementation(handleOutput('warn')), + error: vi.spyOn(console, 'error').mockImplementation(handleOutput('error')), + output, + get formatted() { + return output.all.join('\n') + }, + } +} + +export async function typecheck(project: string) { + try { + const result = spawnSync( + 'tsc', + ['--noEmit', '--target', 'es2021', '--pretty', 'false', '-p', project], + { + encoding: 'utf-8', + stdio: 'pipe', + }, + ) + if (result.error) throw result.error + if (result.status !== 0) + throw new Error(`Failed with code ${result.status}`) + if (result.signal) throw new Error('Process terminated by signal') + return result.stdout + } catch (error) { + throw new Error( + (error as Error).message.replaceAll( + path.dirname(project), + '/path/to/project', + ), + ) + } +} + +export const baseUrl = 'https://api.etherscan.io/v2/api' +export const apiKey = 'abc' +export const invalidApiKey = 'xyz' +export const address = '0xaf0326d92b97df1221759476b072abfd8084f9be' +export const proxyAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' +export const implementationAddress = + '0x43506849d7c04f9138d1a2050bbf3a0c054402dd' +export const unverifiedContractAddress = + '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e' +export const timeoutAddress = '0xecb504d39723b0be0e3a9aa33d646642d1051ee1' + +export const handlers = [ + http.get(baseUrl, async ({ request }) => { + const url = new URL(request.url) + const search = url.search.replace(/^\?chainId=\d&/, '?') + + if ( + search === + `?module=contract&action=getabi&address=${unverifiedContractAddress}&apikey=${apiKey}` + ) + return HttpResponse.json({ + status: '0', + message: 'NOTOK', + result: 'Contract source code not verified', + }) + + if ( + search === + `?module=contract&action=getabi&address=${timeoutAddress}&apikey=${invalidApiKey}` + ) + return HttpResponse.json({ + status: '0', + message: 'NOTOK', + result: 'Invalid API Key', + }) + + if ( + search === + `?module=contract&action=getabi&address=${address}&apikey=${apiKey}` + ) + return HttpResponse.json({ + status: '1', + message: 'OK', + result: + '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]', + }) + + if ( + search === + `?module=contract&action=getabi&address=${timeoutAddress}&apikey=${apiKey}` + ) { + await new Promise((resolve) => setTimeout(resolve, 10_000)) + return HttpResponse.json({}) + } + + if ( + search === + `?module=contract&action=getabi&address=${implementationAddress}&apikey=${apiKey}` + ) + return HttpResponse.json({ + status: '1', + message: 'OK', + result: + '[{"constant":false,"inputs":[{"name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newImplementation","type":"address"},{"name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"changeAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_implementation","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"previousAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"implementation","type":"address"}],"name":"Upgraded","type":"event"}]', + }) + + if ( + search === + `?module=contract&action=getsourcecode&address=${proxyAddress}&apikey=${apiKey}` + ) + return HttpResponse.json({ + status: '1', + message: 'OK', + result: [ + { + SourceCode: '...', + ABI: '[{"constant":false,"inputs":[{"name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newImplementation","type":"address"},{"name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"changeAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_implementation","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"previousAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"implementation","type":"address"}],"name":"Upgraded","type":"event"}]', + ContractName: 'FiatTokenProxy', + CompilerVersion: 'v0.4.24+commit.e67f0147', + OptimizationUsed: '0', + Runs: '200', + ConstructorArguments: + '0000000000000000000000000882477e7895bdc5cea7cb1552ed914ab157fe56', + EVMVersion: 'Default', + Library: '', + LicenseType: '', + Proxy: '1', + Implementation: '0x43506849d7c04f9138d1a2050bbf3a0c054402dd', + SwarmSource: + 'bzzr://a4a547cfc7202c5acaaae74d428e988bc62ad5024eb0165532d3a8f91db4ed24', + }, + ], + }) + + if ( + search === + `?module=contract&action=getsourcecode&address=${address}&apikey=${apiKey}` + ) + return HttpResponse.json({ + status: '1', + message: 'OK', + result: [ + { + SourceCode: '...', + ABI: '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]', + ContractName: 'WagmiMintExample', + CompilerVersion: 'v0.8.11+commit.d7f03943', + OptimizationUsed: '1', + Runs: '10000', + ConstructorArguments: '', + EVMVersion: 'Default', + Library: '', + LicenseType: '', + Proxy: '0', + Implementation: '', + SwarmSource: '', + }, + ], + }) + + throw new Error(`Unhandled request: ${search}`) + }), +] diff --git a/packages/cli/tsconfig.build.json b/packages/cli/tsconfig.build.json new file mode 100644 index 0000000000..3a046d812b --- /dev/null +++ b/packages/cli/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"], + "compilerOptions": { + "sourceMap": true, + "types": ["@types/node"] + } +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json new file mode 100644 index 0000000000..4054dee118 --- /dev/null +++ b/packages/cli/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.build.json", + "include": ["src/**/*.ts", "test/**/*.ts", "types/**/*.d.ts"], + "exclude": [] +} diff --git a/packages/cli/types/fixturez.d.ts b/packages/cli/types/fixturez.d.ts new file mode 100644 index 0000000000..f96282756f --- /dev/null +++ b/packages/cli/types/fixturez.d.ts @@ -0,0 +1,18 @@ +declare module 'fixturez' { + interface FixturezOpts { + glob?: string + cleanup?: boolean + root?: string + } + + interface Fixturez { + find(basename: string): string + copy(basename: string): string + temp(): string + cleanup(): void + } + + function fixturez(dirname: string, opts?: FixturezOpts): Fixturez + + export = fixturez +} diff --git a/packages/connectors/CHANGELOG.md b/packages/connectors/CHANGELOG.md new file mode 100644 index 0000000000..c67e25dcb0 --- /dev/null +++ b/packages/connectors/CHANGELOG.md @@ -0,0 +1,1640 @@ +# @wagmi/connectors + +## 5.8.3 + +### Patch Changes + +- [#4660](https://github.com/wevm/wagmi/pull/4660) [`42b1fed58e9ac09da0f8ebf3e9271f98a707aaac`](https://github.com/wevm/wagmi/commit/42b1fed58e9ac09da0f8ebf3e9271f98a707aaac) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect/ethereum-provider` version to `2.20.2` + +## 5.8.2 + +### Patch Changes + +- Updated dependencies [[`29297a48af72b537173d948ccd2fe37d39914c66`](https://github.com/wevm/wagmi/commit/29297a48af72b537173d948ccd2fe37d39914c66), [`07370106d5fb6b8fe300992d93abf25b3d0eaf57`](https://github.com/wevm/wagmi/commit/07370106d5fb6b8fe300992d93abf25b3d0eaf57)]: + - @wagmi/core@2.17.2 + +## 5.8.1 + +### Patch Changes + +- Updated dependencies [[`01f64e64fa4f85cdd30023903f972f4f9023681f`](https://github.com/wevm/wagmi/commit/01f64e64fa4f85cdd30023903f972f4f9023681f)]: + - @wagmi/core@2.17.1 + +## 5.8.0 + +### Minor Changes + +- [#4644](https://github.com/wevm/wagmi/pull/4644) [`cc5517ff6880bb630f1b201930acc20dd1a0b451`](https://github.com/wevm/wagmi/commit/cc5517ff6880bb630f1b201930acc20dd1a0b451) Thanks [@lukaisailovic](https://github.com/lukaisailovic)! - Updated `@walletconnect/etherereum-provider` to `2.20.0`. + +## 5.7.13 + +### Patch Changes + +- [#4622](https://github.com/wevm/wagmi/pull/4622) [`88427b2bcd13ec375ef519e9ad1ccffef9f02a7b`](https://github.com/wevm/wagmi/commit/88427b2bcd13ec375ef519e9ad1ccffef9f02a7b) Thanks [@dan1kov](https://github.com/dan1kov)! - Added `rdns` property to Coinbase Wallet v3 connector + +- [#4605](https://github.com/wevm/wagmi/pull/4605) [`3f8b2edc4f237cccff1009bcef03d51ca27a7324`](https://github.com/wevm/wagmi/commit/3f8b2edc4f237cccff1009bcef03d51ca27a7324) Thanks [@chybisov](https://github.com/chybisov)! - Bumped `@safe-global/safe-apps-provider` version to `0.18.6`. + +- Updated dependencies [[`799ee4d4b23c2ecd64e3f3668e67634e81939719`](https://github.com/wevm/wagmi/commit/799ee4d4b23c2ecd64e3f3668e67634e81939719)]: + - @wagmi/core@2.17.0 + +## 5.7.12 + +### Patch Changes + +- [#4608](https://github.com/wevm/wagmi/pull/4608) [`b59c024b23c69f5459b17390531207cfdf126ce4`](https://github.com/wevm/wagmi/commit/b59c024b23c69f5459b17390531207cfdf126ce4) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/ethereum-provider`. + +## 5.7.11 + +### Patch Changes + +- Updated dependencies [[`a4bd0623eed28e3761a27295831a60ad835f0ee0`](https://github.com/wevm/wagmi/commit/a4bd0623eed28e3761a27295831a60ad835f0ee0)]: + - @wagmi/core@2.16.7 + +## 5.7.10 + +### Patch Changes + +- [#4573](https://github.com/wevm/wagmi/pull/4573) [`e944812ebc234a72c1417b77cff341166f5e0fef`](https://github.com/wevm/wagmi/commit/e944812ebc234a72c1417b77cff341166f5e0fef) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - updated `@walletconnect/ethereum-provider` to `2.19.1` + +- Updated dependencies [[`edf47477b2f6385a1c3ae01d36a8498c47f30a0b`](https://github.com/wevm/wagmi/commit/edf47477b2f6385a1c3ae01d36a8498c47f30a0b)]: + - @wagmi/core@2.16.6 + +## 5.7.9 + +### Patch Changes + +- [#4571](https://github.com/wevm/wagmi/pull/4571) [`5b7101fddb61df56e34b2e02b46bc409e496eaf9`](https://github.com/wevm/wagmi/commit/5b7101fddb61df56e34b2e02b46bc409e496eaf9) Thanks [@ganchoradkov](https://github.com/ganchoradkov)! - Updated `@walletconnect/ethereum-provider` to `2.19.0` + +## 5.7.8 + +### Patch Changes + +- Updated dependencies [[`d0c9a86921a4e939373cc6e763284e53f2a2e93c`](https://github.com/wevm/wagmi/commit/d0c9a86921a4e939373cc6e763284e53f2a2e93c)]: + - @wagmi/core@2.16.5 + +## 5.7.7 + +### Patch Changes + +- [`507f864d91238bfd423d0e36d3619eb9f6e52eec`](https://github.com/wevm/wagmi/commit/507f864d91238bfd423d0e36d3619eb9f6e52eec) Thanks [@jxom](https://github.com/jxom)! - Updated `@coinbase/wallet-sdk`. + +- Updated dependencies [[`507f864d91238bfd423d0e36d3619eb9f6e52eec`](https://github.com/wevm/wagmi/commit/507f864d91238bfd423d0e36d3619eb9f6e52eec)]: + - @wagmi/core@2.16.4 + +## 5.7.6 + +### Patch Changes + +- [#4524](https://github.com/wevm/wagmi/pull/4524) [`639952c97f0fe3927106f42d3c9f7f366cdf7f7a`](https://github.com/wevm/wagmi/commit/639952c97f0fe3927106f42d3c9f7f366cdf7f7a) Thanks [@chakra-guy](https://github.com/chakra-guy)! - Updated MetaMask SDK. + +- [#4525](https://github.com/wevm/wagmi/pull/4525) [`5aa2c095f7bfb6dfcf91c6945c3e1f9c9dd05766`](https://github.com/wevm/wagmi/commit/5aa2c095f7bfb6dfcf91c6945c3e1f9c9dd05766) Thanks [@EdouardBougon](https://github.com/EdouardBougon)! - Added Phantom flag to Injected Connector. + +## 5.7.5 + +### Patch Changes + +- [#4512](https://github.com/wevm/wagmi/pull/4512) [`a257e8d4f97431a4af872cda1817b4ae17c7bbed`](https://github.com/wevm/wagmi/commit/a257e8d4f97431a4af872cda1817b4ae17c7bbed) Thanks [@EdouardBougon](https://github.com/EdouardBougon)! - Fixed MetaMask switchChain/addChain handling. + +## 5.7.4 + +### Patch Changes + +- [#4505](https://github.com/wevm/wagmi/pull/4505) [`c8a257e0f6d2ece013b873895c35769a8a804fdc`](https://github.com/wevm/wagmi/commit/c8a257e0f6d2ece013b873895c35769a8a804fdc) Thanks [@chakra-guy](https://github.com/chakra-guy)! - Bumped Metamask SDK Version (changes include [bug fixes and minor changes](https://github.com/MetaMask/metamask-sdk/pull/1194)). + +## 5.7.3 + +### Patch Changes + +- [#4480](https://github.com/wevm/wagmi/pull/4480) [`384a1d91597622eb59e1c05dc13ce25017c5b6d8`](https://github.com/wevm/wagmi/commit/384a1d91597622eb59e1c05dc13ce25017c5b6d8) Thanks [@RodeRickIsWatching](https://github.com/RodeRickIsWatching)! - Fixed invocation of default storage. + +- Updated dependencies [[`384a1d91597622eb59e1c05dc13ce25017c5b6d8`](https://github.com/wevm/wagmi/commit/384a1d91597622eb59e1c05dc13ce25017c5b6d8)]: + - @wagmi/core@2.16.3 + +## 5.7.2 + +### Patch Changes + +- [`012907032b532a438fce48f407470250cbc8f0c6`](https://github.com/wevm/wagmi/commit/012907032b532a438fce48f407470250cbc8f0c6) Thanks [@jxom](https://github.com/jxom)! - Fixed assignment in `getDefaultStorage`. + +- Updated dependencies [[`012907032b532a438fce48f407470250cbc8f0c6`](https://github.com/wevm/wagmi/commit/012907032b532a438fce48f407470250cbc8f0c6)]: + - @wagmi/core@2.16.2 + +## 5.7.1 + +### Patch Changes + +- [#4471](https://github.com/wevm/wagmi/pull/4471) [`9c8c35a3b829f2c58edcd3a29e2dcd99974d7470`](https://github.com/wevm/wagmi/commit/9c8c35a3b829f2c58edcd3a29e2dcd99974d7470) Thanks [@EdouardBougon](https://github.com/EdouardBougon)! - Improved MetaMask chain switching behavior. + +- Updated dependencies [[`3892ebd21c06beef4b28ece4e70d2a38807bce6f`](https://github.com/wevm/wagmi/commit/3892ebd21c06beef4b28ece4e70d2a38807bce6f)]: + - @wagmi/core@2.16.1 + +## 5.7.0 + +### Minor Changes + +- [#4440](https://github.com/wevm/wagmi/pull/4440) [`e3f63a02c1f7d80481804584f262bc98dab0400d`](https://github.com/wevm/wagmi/commit/e3f63a02c1f7d80481804584f262bc98dab0400d) Thanks [@johanneskares](https://github.com/johanneskares)! - Added Coinbase Smart Wallet "Instant Onboarding" mode to `coinbaseWallet`. + +## 5.6.2 + +### Patch Changes + +- [#4437](https://github.com/wevm/wagmi/pull/4437) [`adf2253b10c6d4fc583e4bc9f01a8ef5ca267c85`](https://github.com/wevm/wagmi/commit/adf2253b10c6d4fc583e4bc9f01a8ef5ca267c85) Thanks [@chybisov](https://github.com/chybisov)! - Bumped `@safe-global/safe-apps-provider` version to `0.18.5`. + +## 5.6.1 + +### Patch Changes + +- [#4458](https://github.com/wevm/wagmi/pull/4458) [`987404f590c1d29ebb3cb68928f5e54aa032793d`](https://github.com/wevm/wagmi/commit/987404f590c1d29ebb3cb68928f5e54aa032793d) Thanks [@EdouardBougon](https://github.com/EdouardBougon)! - Fixed MetaMask internal metadata handling. + +## 5.6.0 + +### Minor Changes + +- [#4453](https://github.com/wevm/wagmi/pull/4453) [`070e48480194c8d7f45bda1d7dd1346e6f5d7227`](https://github.com/wevm/wagmi/commit/070e48480194c8d7f45bda1d7dd1346e6f5d7227) Thanks [@tmm](https://github.com/tmm)! - Added narrowing to `config.connectors`. + +### Patch Changes + +- [#4456](https://github.com/wevm/wagmi/pull/4456) [`8b0726c1106fce88b782e676498eabf0718b2619`](https://github.com/wevm/wagmi/commit/8b0726c1106fce88b782e676498eabf0718b2619) Thanks [@EdouardBougon](https://github.com/EdouardBougon)! - Bumped MetaMask SDK and fixed internal metadata handling. +- Updated dependencies [[`afea6b67822a7a2b96901ec851441d27ee0f7a52`](https://github.com/wevm/wagmi/commit/afea6b67822a7a2b96901ec851441d27ee0f7a52), [`070e48480194c8d7f45bda1d7dd1346e6f5d7227`](https://github.com/wevm/wagmi/commit/070e48480194c8d7f45bda1d7dd1346e6f5d7227)]: + - @wagmi/core@2.16.0 + +## 5.5.3 + +### Patch Changes + +- [#4433](https://github.com/wevm/wagmi/pull/4433) [`06e186cd679b27fe195309110e766fcf46d4efbc`](https://github.com/wevm/wagmi/commit/06e186cd679b27fe195309110e766fcf46d4efbc) Thanks [@Aerilym](https://github.com/Aerilym)! - Bumped Metamask SDK version to `0.31.1`. + +- Updated dependencies [[`06e186cd679b27fe195309110e766fcf46d4efbc`](https://github.com/wevm/wagmi/commit/06e186cd679b27fe195309110e766fcf46d4efbc)]: + - @wagmi/core@2.15.2 + +## 5.5.2 + +### Patch Changes + +- [#4422](https://github.com/wevm/wagmi/pull/4422) [`e563ef69130a511fd6f3f72ed4cd4fbe1390541f`](https://github.com/wevm/wagmi/commit/e563ef69130a511fd6f3f72ed4cd4fbe1390541f) Thanks [@abretonc7s](https://github.com/abretonc7s)! - Bumped MetaMask SDK. + +## 5.5.1 + +### Patch Changes + +- Updated dependencies [[`b8bbb409f4934538e3dd6cac5aaf7346292d0693`](https://github.com/wevm/wagmi/commit/b8bbb409f4934538e3dd6cac5aaf7346292d0693)]: + - @wagmi/core@2.15.1 + +## 5.5.0 + +### Minor Changes + +- [#4417](https://github.com/wevm/wagmi/pull/4417) [`42e65ea4fea99c639817088bba915e0933d17141`](https://github.com/wevm/wagmi/commit/42e65ea4fea99c639817088bba915e0933d17141) Thanks [@jxom](https://github.com/jxom)! - Removed simulation in `writeContract` & `sendTransaction`. + +### Patch Changes + +- Updated dependencies [[`42e65ea4fea99c639817088bba915e0933d17141`](https://github.com/wevm/wagmi/commit/42e65ea4fea99c639817088bba915e0933d17141)]: + - @wagmi/core@2.15.0 + +## 5.4.0 + +### Minor Changes + +- [#4409](https://github.com/wevm/wagmi/pull/4409) [`7ca62b44cd997d48f92c2b81343726a5908aa00b`](https://github.com/wevm/wagmi/commit/7ca62b44cd997d48f92c2b81343726a5908aa00b) Thanks [@fan-zhang-sv](https://github.com/fan-zhang-sv)! - Added `preference` object for Coinbase Wallet connector. + +## 5.3.10 + +### Patch Changes + +- [#4406](https://github.com/wevm/wagmi/pull/4406) [`a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3`](https://github.com/wevm/wagmi/commit/a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3) Thanks [@tmm](https://github.com/tmm)! - Added additional RDNS to MetaMask Connector. + +- Updated dependencies [[`a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3`](https://github.com/wevm/wagmi/commit/a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3)]: + - @wagmi/core@2.14.6 + +## 5.3.9 + +### Patch Changes + +- [`b12a04eeec985c48d2feac94b011d41fb29ca23e`](https://github.com/wevm/wagmi/commit/b12a04eeec985c48d2feac94b011d41fb29ca23e) Thanks [@tmm](https://github.com/tmm)! - Bumped Coinbase Wallet SDK version. + +## 5.3.8 + +### Patch Changes + +- [#4390](https://github.com/wevm/wagmi/pull/4390) [`dac62dc99a0679fa632a0fae49873d6053d06b35`](https://github.com/wevm/wagmi/commit/dac62dc99a0679fa632a0fae49873d6053d06b35) Thanks [@chybisov](https://github.com/chybisov)! - Bumped Safe Apps Provider version. + +- Updated dependencies [[`6b9bbacdc7bffd44fc2165362a5e65fd434e7646`](https://github.com/wevm/wagmi/commit/6b9bbacdc7bffd44fc2165362a5e65fd434e7646)]: + - @wagmi/core@2.14.5 + +## 5.3.7 + +### Patch Changes + +- Updated dependencies [[`e08681c81fbdf475213e2d0f4c5517d0abf4e743`](https://github.com/wevm/wagmi/commit/e08681c81fbdf475213e2d0f4c5517d0abf4e743)]: + - @wagmi/core@2.14.4 + +## 5.3.6 + +### Patch Changes + +- [#4385](https://github.com/wevm/wagmi/pull/4385) [`7558ff3133c11bc4c49473d08ee9a47eaa12df5b`](https://github.com/wevm/wagmi/commit/7558ff3133c11bc4c49473d08ee9a47eaa12df5b) Thanks [@cb-jake](https://github.com/cb-jake)! - Bumped Coinbase Wallet SDK version. + +## 5.3.5 + +### Patch Changes + +- [`7fe78f2d09778fc01fd0cffe85ba198e64999275`](https://github.com/wevm/wagmi/commit/7fe78f2d09778fc01fd0cffe85ba198e64999275) Thanks [@tmm](https://github.com/tmm)! - Fixed MetaMask connector not returning provider in some cases. + +- Updated dependencies [[`cb7dd2ebb871d0be8f1a11a8cd8ce592cd74b7c7`](https://github.com/wevm/wagmi/commit/cb7dd2ebb871d0be8f1a11a8cd8ce592cd74b7c7)]: + - @wagmi/core@2.14.3 + +## 5.3.4 + +### Patch Changes + +- [#4371](https://github.com/wevm/wagmi/pull/4371) [`b6861a4c378dab78d8751ae0ac2aa425f3c24b8f`](https://github.com/wevm/wagmi/commit/b6861a4c378dab78d8751ae0ac2aa425f3c24b8f) Thanks [@iceanddust](https://github.com/iceanddust)! - Fixed Safe connector not working in some Vite apps + +- Updated dependencies [[`d0d0963bb5904a15cf0355862d62dd141ce0c31c`](https://github.com/wevm/wagmi/commit/d0d0963bb5904a15cf0355862d62dd141ce0c31c), [`ecac0ba36243d94c9199d0bd21937104c835d9a0`](https://github.com/wevm/wagmi/commit/ecac0ba36243d94c9199d0bd21937104c835d9a0)]: + - @wagmi/core@2.14.2 + +## 5.3.3 + +### Patch Changes + +- [#4362](https://github.com/wevm/wagmi/pull/4362) [`83c6d16b7d6dddfa6bda036e04f00ec313c6248c`](https://github.com/wevm/wagmi/commit/83c6d16b7d6dddfa6bda036e04f00ec313c6248c) Thanks [@EdouardBougon](https://github.com/EdouardBougon)! - Fixed MetaMask connector internal logic. + +## 5.3.2 + +### Patch Changes + +- [#4357](https://github.com/wevm/wagmi/pull/4357) [`8970cc51398e1ac713435533096215c6d31ffdf9`](https://github.com/wevm/wagmi/commit/8970cc51398e1ac713435533096215c6d31ffdf9) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +## 5.3.1 + +### Patch Changes + +- Updated dependencies [[`052e72e1f8c1c14fcbdce04a9f8fa7ec28d83702`](https://github.com/wevm/wagmi/commit/052e72e1f8c1c14fcbdce04a9f8fa7ec28d83702), [`b250fc21ee577b2a75c5a34ff684f62fb4ad771a`](https://github.com/wevm/wagmi/commit/b250fc21ee577b2a75c5a34ff684f62fb4ad771a)]: + - @wagmi/core@2.14.1 + +## 5.3.0 + +### Minor Changes + +- [#4343](https://github.com/wevm/wagmi/pull/4343) [`f43e074f473820b208a6295d7c97f847332f1a1d`](https://github.com/wevm/wagmi/commit/f43e074f473820b208a6295d7c97f847332f1a1d) Thanks [@tmm](https://github.com/tmm)! - Added `rdns` property to connector interface. This is used to filter out duplicate [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) injected providers when [`createConfig#multiInjectedProviderDiscovery`](https://wagmi.sh/core/api/createConfig#multiinjectedproviderdiscovery) is enabled and `createConfig#connectors` already matches EIP-6963 providers' `rdns` property. + +### Patch Changes + +- Updated dependencies [[`f43e074f473820b208a6295d7c97f847332f1a1d`](https://github.com/wevm/wagmi/commit/f43e074f473820b208a6295d7c97f847332f1a1d)]: + - @wagmi/core@2.14.0 + +## 5.2.2 + +### Patch Changes + +- [#4347](https://github.com/wevm/wagmi/pull/4347) [`5ae49af590ff168426c9c283d54c34ae5148fcd9`](https://github.com/wevm/wagmi/commit/5ae49af590ff168426c9c283d54c34ae5148fcd9) Thanks [@EdouardBougon](https://github.com/EdouardBougon)! - Added workaround for MetaMask mobile sometimes disconnecting. + +- [#4350](https://github.com/wevm/wagmi/pull/4350) [`f3182b22e6e454d9bd74f1b940ef34431fd9555d`](https://github.com/wevm/wagmi/commit/f3182b22e6e454d9bd74f1b940ef34431fd9555d) Thanks [@abretonc7s](https://github.com/abretonc7s)! - Updated MetaMask SDK. + +- Updated dependencies [[`c05caabc20c3ced9682cfc7ba1f3f7dcfece0703`](https://github.com/wevm/wagmi/commit/c05caabc20c3ced9682cfc7ba1f3f7dcfece0703)]: + - @wagmi/core@2.13.9 + +## 5.2.1 + +### Patch Changes + +- [#4345](https://github.com/wevm/wagmi/pull/4345) [`91a40f2db08e3a91db421b8732a5511a1e6c88fd`](https://github.com/wevm/wagmi/commit/91a40f2db08e3a91db421b8732a5511a1e6c88fd) Thanks [@tmm](https://github.com/tmm)! - Bumped MetaMask SDK. + +## 5.2.0 + +### Minor Changes + +- [#4337](https://github.com/wevm/wagmi/pull/4337) [`34a0c3b7eea778aee7c27f7ace5e4b2be4e8a0a4`](https://github.com/wevm/wagmi/commit/34a0c3b7eea778aee7c27f7ace5e4b2be4e8a0a4) Thanks [@tmm](https://github.com/tmm)! - Added "Connect and Sign" behavior to MetaMask Connector. + +## 5.1.15 + +### Patch Changes + +- [`3b2123664b7ac66848390739e855c3b9702ab60c`](https://github.com/wevm/wagmi/commit/3b2123664b7ac66848390739e855c3b9702ab60c) Thanks [@tmm](https://github.com/tmm)! - Bumped WalletConnect Provider. + +## 5.1.14 + +### Patch Changes + +- [#4207](https://github.com/wevm/wagmi/pull/4207) [`56f2482508f2ba71bd6b0295c70c6abca7101e57`](https://github.com/wevm/wagmi/commit/56f2482508f2ba71bd6b0295c70c6abca7101e57) Thanks [@Smert](https://github.com/Smert)! - Updated chain switch listener for `injected` and `metaMask` to be more robust. + +- Updated dependencies [[`56f2482508f2ba71bd6b0295c70c6abca7101e57`](https://github.com/wevm/wagmi/commit/56f2482508f2ba71bd6b0295c70c6abca7101e57)]: + - @wagmi/core@2.13.8 + +## 5.1.13 + +### Patch Changes + +- Updated dependencies [[`be75c2d4ef636d7362420ab0a106bfdf63f5d1e6`](https://github.com/wevm/wagmi/commit/be75c2d4ef636d7362420ab0a106bfdf63f5d1e6)]: + - @wagmi/core@2.13.7 + +## 5.1.12 + +### Patch Changes + +- Updated dependencies [[`edcbf5d6fbe92f639bead800502edda9e0aa39f1`](https://github.com/wevm/wagmi/commit/edcbf5d6fbe92f639bead800502edda9e0aa39f1)]: + - @wagmi/core@2.13.6 + +## 5.1.11 + +### Patch Changes + +- [#4271](https://github.com/wevm/wagmi/pull/4271) [`82404c960e04c83e0bae6e1e12459ef9debf9554`](https://github.com/wevm/wagmi/commit/82404c960e04c83e0bae6e1e12459ef9debf9554) Thanks [@omridan159](https://github.com/omridan159)! - Bumped MetaMask SDK. + +- [#4227](https://github.com/wevm/wagmi/pull/4227) [`d07ad7f63a018256908a673d078aaf79e47ac703`](https://github.com/wevm/wagmi/commit/d07ad7f63a018256908a673d078aaf79e47ac703) Thanks [@xianchenxc](https://github.com/xianchenxc)! - Fixed MetaMask Connector throwing error after switching to a chain that was just added via `'wallet_addEthereumChain'`. + +## 5.1.10 + +### Patch Changes + +- [#4255](https://github.com/wevm/wagmi/pull/4255) [`81de006e66121a18c61945c1f9b8426c83a5713c`](https://github.com/wevm/wagmi/commit/81de006e66121a18c61945c1f9b8426c83a5713c) Thanks [@tomiir](https://github.com/tomiir)! - Bumped `@walletconnect/ethereum-provider` from version `2.15.3` to version `2.16.1`. + +- Updated dependencies [[`f47ce8f6d263e49fdff90b8edb3190142d2657bb`](https://github.com/wevm/wagmi/commit/f47ce8f6d263e49fdff90b8edb3190142d2657bb)]: + - @wagmi/core@2.13.5 + +## 5.1.9 + +### Patch Changes + +- [#4243](https://github.com/wevm/wagmi/pull/4243) [`21bd0e473d374cbbd7a01bececa6022d529026ba`](https://github.com/wevm/wagmi/commit/21bd0e473d374cbbd7a01bececa6022d529026ba) Thanks [@tomiir](https://github.com/tomiir)! - Bumped `@walletconnect/ethereum-provider` from version `2.15.2` to version `2.15.3` + +- [#4251](https://github.com/wevm/wagmi/pull/4251) [`5c89c6853e616437a3be2b019db895451fecfb3c`](https://github.com/wevm/wagmi/commit/5c89c6853e616437a3be2b019db895451fecfb3c) Thanks [@tmm](https://github.com/tmm)! - Bumped MM SDK. + +## 5.1.8 + +### Patch Changes + +- [`b580ad4edff1721e0b9d138cf5ae2ec74d2374c7`](https://github.com/wevm/wagmi/commit/b580ad4edff1721e0b9d138cf5ae2ec74d2374c7) Thanks [@tmm](https://github.com/tmm)! - Bumped WalletConnect Provider. + +## 5.1.7 + +### Patch Changes + +- [#4213](https://github.com/wevm/wagmi/pull/4213) [`91fd81a068789c5020e891f539bcad8f54a7a52f`](https://github.com/wevm/wagmi/commit/91fd81a068789c5020e891f539bcad8f54a7a52f) Thanks [@tomiir](https://github.com/tomiir)! - Updated `@walletconnect/ethereum-provider` from version `2.15.0` to version `2.15.1`. + +## 5.1.6 + +### Patch Changes + +- [#4208](https://github.com/wevm/wagmi/pull/4208) [`3168616298cbb6135d0ffda771cba4126e83eba8`](https://github.com/wevm/wagmi/commit/3168616298cbb6135d0ffda771cba4126e83eba8) Thanks [@tomiir](https://github.com/tomiir)! - Updated WalletConnect Ethereum Provider version from `2.14.0` to `2.15.0`. + +- [#4211](https://github.com/wevm/wagmi/pull/4211) [`d7608ef9a79459465dc8c06a2ab740465c881907`](https://github.com/wevm/wagmi/commit/d7608ef9a79459465dc8c06a2ab740465c881907) Thanks [@tmm](https://github.com/tmm)! - Added default name for MetaMask Connector. + +## 5.1.5 + +### Patch Changes + +- Updated dependencies [[`b4c8971788c70b09479946ecfa998cff2f1b3953`](https://github.com/wevm/wagmi/commit/b4c8971788c70b09479946ecfa998cff2f1b3953)]: + - @wagmi/core@2.13.4 + +## 5.1.4 + +### Patch Changes + +- Updated dependencies [[`871dbdbfe59ac8ad01d1ec6150ea7b091b7b7de4`](https://github.com/wevm/wagmi/commit/871dbdbfe59ac8ad01d1ec6150ea7b091b7b7de4)]: + - @wagmi/core@2.13.3 + +## 5.1.3 + +### Patch Changes + +- Updated dependencies [[`1b9b523fa9b9dfe839aecdf4b40caa9547d7e594`](https://github.com/wevm/wagmi/commit/1b9b523fa9b9dfe839aecdf4b40caa9547d7e594)]: + - @wagmi/core@2.13.2 + +## 5.1.2 + +### Patch Changes + +- [`abb490dac4f0f02f46cb0878e7ca9a0db6aada56`](https://github.com/wevm/wagmi/commit/abb490dac4f0f02f46cb0878e7ca9a0db6aada56) Thanks [@tmm](https://github.com/tmm)! - Bumped MetaMask SDK version. + +- [`28e0e5c9a4f856583f9d36a807502bd51a0c6ec2`](https://github.com/wevm/wagmi/commit/28e0e5c9a4f856583f9d36a807502bd51a0c6ec2) Thanks [@tmm](https://github.com/tmm)! - Bumped WalletConnect Ethereum Provider version. + +## 5.1.1 + +### Patch Changes + +- Updated dependencies [[`07c1227f306d0efb9421d4bb77a774f92f5fcf45`](https://github.com/wevm/wagmi/commit/07c1227f306d0efb9421d4bb77a774f92f5fcf45)]: + - @wagmi/core@2.13.1 + +## 5.1.0 + +### Minor Changes + +- [#4162](https://github.com/wevm/wagmi/pull/4162) [`a73a7737b756886b388f120ae423e72cca53e8a0`](https://github.com/wevm/wagmi/commit/a73a7737b756886b388f120ae423e72cca53e8a0) Thanks [@jxom](https://github.com/jxom)! - Added functionality for consumer-defined RPC URLs (`config.transports`) to be propagated to the WalletConnect & MetaMask Connectors. + +### Patch Changes + +- Updated dependencies [[`a73a7737b756886b388f120ae423e72cca53e8a0`](https://github.com/wevm/wagmi/commit/a73a7737b756886b388f120ae423e72cca53e8a0)]: + - @wagmi/core@2.13.0 + +## 5.0.26 + +### Patch Changes + +- [`8d81df5cc884d0a210dedd3c1ea0e2e9e52b83c5`](https://github.com/wevm/wagmi/commit/8d81df5cc884d0a210dedd3c1ea0e2e9e52b83c5) Thanks [@tmm](https://github.com/tmm)! - Fixed `metaMask` connector switch chain issue. + +- Updated dependencies [[`5bc8c8877810b2eec24a829df87dce40a51e6f20`](https://github.com/wevm/wagmi/commit/5bc8c8877810b2eec24a829df87dce40a51e6f20)]: + - @wagmi/core@2.12.2 + +## 5.0.25 + +### Patch Changes + +- [#4146](https://github.com/wevm/wagmi/pull/4146) [`cc996e08e930c9e88cf753a1e874652059e81a3b`](https://github.com/wevm/wagmi/commit/cc996e08e930c9e88cf753a1e874652059e81a3b) Thanks [@jxom](https://github.com/jxom)! - Updated `@safe-global/safe-apps-sdk` + `@safe-global/safe-apps-provider` dependencies. + +- Updated dependencies [[`cc996e08e930c9e88cf753a1e874652059e81a3b`](https://github.com/wevm/wagmi/commit/cc996e08e930c9e88cf753a1e874652059e81a3b)]: + - @wagmi/core@2.12.1 + +## 5.0.24 + +### Patch Changes + +- Updated dependencies [[`5581a810ef70308e99c6f8b630cd4bca59f64afc`](https://github.com/wevm/wagmi/commit/5581a810ef70308e99c6f8b630cd4bca59f64afc)]: + - @wagmi/core@2.12.0 + +## 5.0.23 + +### Patch Changes + +- [`d3814ab4b88f9f0e052b53bc3d458df87b43f01d`](https://github.com/wevm/wagmi/commit/d3814ab4b88f9f0e052b53bc3d458df87b43f01d) Thanks [@jxom](https://github.com/jxom)! - Updated `mipd` dependency. + +- Updated dependencies [[`b08013eaa9ce97c02f8a7128ea400e3da7ef74bb`](https://github.com/wevm/wagmi/commit/b08013eaa9ce97c02f8a7128ea400e3da7ef74bb), [`d3814ab4b88f9f0e052b53bc3d458df87b43f01d`](https://github.com/wevm/wagmi/commit/d3814ab4b88f9f0e052b53bc3d458df87b43f01d)]: + - @wagmi/core@2.11.8 + +## 5.0.22 + +### Patch Changes + +- [`0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e`](https://github.com/wevm/wagmi/commit/0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e) Thanks [@tmm](https://github.com/tmm)! - Improved TypeScript `'exactOptionalPropertyTypes'` support. + +- Updated dependencies [[`0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e`](https://github.com/wevm/wagmi/commit/0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e)]: + - @wagmi/core@2.11.7 + +## 5.0.21 + +### Patch Changes + +- [#4094](https://github.com/wevm/wagmi/pull/4094) [`ff0760b5900114bcfdf420a9fba3cc278ac95afe`](https://github.com/wevm/wagmi/commit/ff0760b5900114bcfdf420a9fba3cc278ac95afe) Thanks [@omridan159](https://github.com/omridan159)! - Bumped MetaMask SDK to fix `metaMask` connector error bubbling. + +- Updated dependencies [[`95965c1f19d480b97f2b297a077a9e607dee32ad`](https://github.com/wevm/wagmi/commit/95965c1f19d480b97f2b297a077a9e607dee32ad)]: + - @wagmi/core@2.11.6 + +## 5.0.20 + +### Patch Changes + +- [`43fa971d34cac57fa5a2898ad4d839b95d7af37c`](https://github.com/wevm/wagmi/commit/43fa971d34cac57fa5a2898ad4d839b95d7af37c) Thanks [@tmm](https://github.com/tmm)! - Bumped Coinbase Wallet SDK and fixed `metaMask` connector hang on mobile. + +## 5.0.19 + +### Patch Changes + +- [#4083](https://github.com/wevm/wagmi/pull/4083) [`b7ad208030d9f2e3f89912ff76b16cdbd848feda`](https://github.com/wevm/wagmi/commit/b7ad208030d9f2e3f89912ff76b16cdbd848feda) Thanks [@omridan159](https://github.com/omridan159)! - Bumped MetaMask SDK + +## 5.0.18 + +### Patch Changes + +- [#4081](https://github.com/wevm/wagmi/pull/4081) [`44d24620c9e3957f3245d14d6a042736371df70b`](https://github.com/wevm/wagmi/commit/44d24620c9e3957f3245d14d6a042736371df70b) Thanks [@tmm](https://github.com/tmm)! - Bumped MetaMask SDK + +## 5.0.17 + +### Patch Changes + +- Updated dependencies [[`04f2b846b113f3d300d82c9fa75212f1805817c5`](https://github.com/wevm/wagmi/commit/04f2b846b113f3d300d82c9fa75212f1805817c5)]: + - @wagmi/core@2.11.5 + +## 5.0.16 + +### Patch Changes + +- [#4071](https://github.com/wevm/wagmi/pull/4071) [`02c38c28d1aa0ad7a61c33775de603ed974c5c1b`](https://github.com/wevm/wagmi/commit/02c38c28d1aa0ad7a61c33775de603ed974c5c1b) Thanks [@omridan159](https://github.com/omridan159)! - Bumped MetaMask SDK + +- Updated dependencies [[`9e8345cd56186b997b5e56deaa2cfc69b30d15f6`](https://github.com/wevm/wagmi/commit/9e8345cd56186b997b5e56deaa2cfc69b30d15f6)]: + - @wagmi/core@2.11.4 + +## 5.0.15 + +### Patch Changes + +- Updated dependencies [[`8974e6269bb5d7bfaa90db0246bc7d13e8bff798`](https://github.com/wevm/wagmi/commit/8974e6269bb5d7bfaa90db0246bc7d13e8bff798)]: + - @wagmi/core@2.11.3 + +## 5.0.14 + +### Patch Changes + +- Updated dependencies [[`b4d9ef79deb554ee20fed6666a474be5e7cdd522`](https://github.com/wevm/wagmi/commit/b4d9ef79deb554ee20fed6666a474be5e7cdd522)]: + - @wagmi/core@2.11.2 + +## 5.0.13 + +### Patch Changes + +- [`9c862d8d63e3d692a22cef2a90782b74a9103f17`](https://github.com/wevm/wagmi/commit/9c862d8d63e3d692a22cef2a90782b74a9103f17) Thanks [@tmm](https://github.com/tmm)! - Reverted internal module loading utility. + +- Updated dependencies [[`9c862d8d63e3d692a22cef2a90782b74a9103f17`](https://github.com/wevm/wagmi/commit/9c862d8d63e3d692a22cef2a90782b74a9103f17)]: + - @wagmi/core@2.11.1 + +## 5.0.12 + +### Patch Changes + +- Updated dependencies [[`06bb598a7f04c7b167f5b7ff6d46bd15886a6a14`](https://github.com/wevm/wagmi/commit/06bb598a7f04c7b167f5b7ff6d46bd15886a6a14), [`24a45b269bd0214a29d6f82a84ac66ef8c3f3822`](https://github.com/wevm/wagmi/commit/24a45b269bd0214a29d6f82a84ac66ef8c3f3822)]: + - @wagmi/core@2.11.0 + +## 5.0.11 + +### Patch Changes + +- [#4020](https://github.com/wevm/wagmi/pull/4020) [`e3b124ce414b8fd1b2214e2c5a28dc72158a13d1`](https://github.com/wevm/wagmi/commit/e3b124ce414b8fd1b2214e2c5a28dc72158a13d1) Thanks [@tmm](https://github.com/tmm)! - Added reconnection support to `metaMask` on mobile and use deeplinks by default. + +- Updated dependencies [[`f2a7cefab96691ebed8b8e45ffde071c47b58dbe`](https://github.com/wevm/wagmi/commit/f2a7cefab96691ebed8b8e45ffde071c47b58dbe), [`f0ea0b2a7fe193dadfeb49a4c8031ee451c638b5`](https://github.com/wevm/wagmi/commit/f0ea0b2a7fe193dadfeb49a4c8031ee451c638b5)]: + - @wagmi/core@2.10.6 + +## 5.0.10 + +### Patch Changes + +- [`560952acd4bfe33db6c7c07b35c613cef278677c`](https://github.com/wevm/wagmi/commit/560952acd4bfe33db6c7c07b35c613cef278677c) Thanks [@tmm](https://github.com/tmm)! - Captured Coinbase Smart Wallet error when closing window as EIP-1193 `4001` error. + +## 5.0.9 + +### Patch Changes + +- [`32cdd7b7dc5aff916c040628519562c3a99d418d`](https://github.com/wevm/wagmi/commit/32cdd7b7dc5aff916c040628519562c3a99d418d) Thanks [@tmm](https://github.com/tmm)! - Bumped `@metamask/sdk` to remove peer dependency install warning. + +## 5.0.8 + +### Patch Changes + +- [#3997](https://github.com/wevm/wagmi/pull/3997) [`c1952d1ff7f0a491dc88595a49159451b07b5621`](https://github.com/wevm/wagmi/commit/c1952d1ff7f0a491dc88595a49159451b07b5621) Thanks [@nateReiners](https://github.com/nateReiners)! - Bumped Coinbase Wallet SDK. + +## 5.0.7 + +### Patch Changes + +- Updated dependencies [[`030c7c2cb380dfd67a2182f62e2aa7a6e1601898`](https://github.com/wevm/wagmi/commit/030c7c2cb380dfd67a2182f62e2aa7a6e1601898)]: + - @wagmi/core@2.10.5 + +## 5.0.6 + +### Patch Changes + +- Updated dependencies [[`51fde8a0433b4fff357c1a8d7e08b41b4c86c968`](https://github.com/wevm/wagmi/commit/51fde8a0433b4fff357c1a8d7e08b41b4c86c968)]: + - @wagmi/core@2.10.4 + +## 5.0.5 + +### Patch Changes + +- [#3979](https://github.com/wevm/wagmi/pull/3979) [`70dd28669dd8d2ce08217cd02e29a8fbba7a08d4`](https://github.com/wevm/wagmi/commit/70dd28669dd8d2ce08217cd02e29a8fbba7a08d4) Thanks [@tmm](https://github.com/tmm)! - Fixed `walletConnect` connector. + +## 5.0.4 + +### Patch Changes + +- [#3972](https://github.com/wevm/wagmi/pull/3972) [`be9e1b8a9818b92eb0654a20d9471e9e39329e7e`](https://github.com/wevm/wagmi/commit/be9e1b8a9818b92eb0654a20d9471e9e39329e7e) Thanks [@nateReiners](https://github.com/nateReiners)! - Bumped Coinbase Wallet SDK. + +## 5.0.3 + +### Patch Changes + +- [#3962](https://github.com/wevm/wagmi/pull/3962) [`2804a8a583b1874271154898b4bae38756ef581c`](https://github.com/wevm/wagmi/commit/2804a8a583b1874271154898b4bae38756ef581c) Thanks [@tmm](https://github.com/tmm)! - Added timeout to `getInfo` called in `safe` connector since [non-Safe App iFrames cause it to not resolve](https://github.com/safe-global/safe-apps-sdk/issues/263#issuecomment-1029835840). + +- Updated dependencies [[`2804a8a583b1874271154898b4bae38756ef581c`](https://github.com/wevm/wagmi/commit/2804a8a583b1874271154898b4bae38756ef581c)]: + - @wagmi/core@2.10.3 + +## 5.0.2 + +### Patch Changes + +- [#3940](https://github.com/wevm/wagmi/pull/3940) [`a5071f581dfdfb961718873643a2fc629101c72a`](https://github.com/wevm/wagmi/commit/a5071f581dfdfb961718873643a2fc629101c72a) Thanks [@jxom](https://github.com/jxom)! - Fixed usage of `metaMask` connector in Vite environments. + +- Updated dependencies [[`a5071f581dfdfb961718873643a2fc629101c72a`](https://github.com/wevm/wagmi/commit/a5071f581dfdfb961718873643a2fc629101c72a)]: + - @wagmi/core@2.10.2 + +## 5.0.1 + +### Patch Changes + +- Bumped versions. + +- Updated dependencies []: + - @wagmi/core@2.10.1 + +## 5.0.0 + +### Major Changes + +- [#3928](https://github.com/wevm/wagmi/pull/3928) [`3117e71825f9c58a0d718f3d1686f1a191fa9cb1`](https://github.com/wevm/wagmi/commit/3117e71825f9c58a0d718f3d1686f1a191fa9cb1) Thanks [@tmm](https://github.com/tmm)! - **Breaking:** Updated default Coinbase SDK in `coinbaseWallet` Connector to v4.x. + + Added a `version` property (defaults to `'4'`) to the `coinbaseWallet` Connector to target a version of the Coinbase SDK: + + ```diff + coinbaseWallet({ + + version: '3' | '4', + }) + ``` + + If `headlessMode` property is set to `true`, then the Connector will target v3 of the Coinbase SDK. + + The following properties are removed in v4 of the `coinbaseWallet` Connector: + + - `chainId` + - `darkMode` + - `diagnosticLogger` + - `enableMobileDeepLink` + - `jsonRpcUrl` + - `linkApiUrl` + - `overrideIsCoinbaseBrowser` + - `overrideIsCoinbaseWallet` + - `overrideIsMetaMask` + - `reloadOnDisconnect` + - `uiConstructor` + + Consumers can still use the above properties in v3 by passing `version: '3'` to the Connector. However, please note that v3 of the Coinbase SDK is deprecated and will be removed in a future release. + +### Patch Changes + +- Updated dependencies [[`3117e71825f9c58a0d718f3d1686f1a191fa9cb1`](https://github.com/wevm/wagmi/commit/3117e71825f9c58a0d718f3d1686f1a191fa9cb1)]: + - @wagmi/core@2.10.0 + +## 4.3.10 + +### Patch Changes + +- [#3906](https://github.com/wevm/wagmi/pull/3906) [`32fcb4a31dde6b0206961d8ffe9c651f8a459c67`](https://github.com/wevm/wagmi/commit/32fcb4a31dde6b0206961d8ffe9c651f8a459c67) Thanks [@tmm](https://github.com/tmm)! - Added support for Vue. + +- Updated dependencies [[`32fcb4a31dde6b0206961d8ffe9c651f8a459c67`](https://github.com/wevm/wagmi/commit/32fcb4a31dde6b0206961d8ffe9c651f8a459c67)]: + - @wagmi/core@2.9.8 + +## 4.3.9 + +### Patch Changes + +- [#3924](https://github.com/wevm/wagmi/pull/3924) [`1f58734f88458e0f6adb05c99f0c90f36ab286b8`](https://github.com/wevm/wagmi/commit/1f58734f88458e0f6adb05c99f0c90f36ab286b8) Thanks [@jxom](https://github.com/jxom)! - Refactored `isChainsStale` logic in `walletConnect` connector. + +- Updated dependencies [[`1f58734f88458e0f6adb05c99f0c90f36ab286b8`](https://github.com/wevm/wagmi/commit/1f58734f88458e0f6adb05c99f0c90f36ab286b8)]: + - @wagmi/core@2.9.7 + +## 4.3.8 + +### Patch Changes + +- [#3917](https://github.com/wevm/wagmi/pull/3917) [`05948fdad5bb4a56b08916d45b3dec2cb1e5f55b`](https://github.com/wevm/wagmi/commit/05948fdad5bb4a56b08916d45b3dec2cb1e5f55b) Thanks [@jxom](https://github.com/jxom)! - Updated `@metamask/sdk`. + +- Updated dependencies [[`05948fdad5bb4a56b08916d45b3dec2cb1e5f55b`](https://github.com/wevm/wagmi/commit/05948fdad5bb4a56b08916d45b3dec2cb1e5f55b)]: + - @wagmi/core@2.9.6 + +## 4.3.7 + +### Patch Changes + +- Updated dependencies [[`4fecbbb66d0aacd03b8c62a6455d11a33cde8f85`](https://github.com/wevm/wagmi/commit/4fecbbb66d0aacd03b8c62a6455d11a33cde8f85)]: + - @wagmi/core@2.9.5 + +## 4.3.6 + +### Patch Changes + +- Updated dependencies [[`e6139a97c4b8804d734b1547b5e3921ce01fbe24`](https://github.com/wevm/wagmi/commit/e6139a97c4b8804d734b1547b5e3921ce01fbe24)]: + - @wagmi/core@2.9.4 + +## 4.3.5 + +### Patch Changes + +- [#3904](https://github.com/wevm/wagmi/pull/3904) [`addca28ebc20f1a4367c35fe9ef786decff9c87e`](https://github.com/wevm/wagmi/commit/addca28ebc20f1a4367c35fe9ef786decff9c87e) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/ethereum-provider`. + +- Updated dependencies [[`addca28ebc20f1a4367c35fe9ef786decff9c87e`](https://github.com/wevm/wagmi/commit/addca28ebc20f1a4367c35fe9ef786decff9c87e)]: + - @wagmi/core@2.9.3 + +## 4.3.4 + +### Patch Changes + +- [#3902](https://github.com/wevm/wagmi/pull/3902) [`204b7b624612405500ec098fb9e35facd3f74ca4`](https://github.com/wevm/wagmi/commit/204b7b624612405500ec098fb9e35facd3f74ca4) Thanks [@jxom](https://github.com/jxom)! - Made third-party SDK imports type-only. + +- Updated dependencies [[`204b7b624612405500ec098fb9e35facd3f74ca4`](https://github.com/wevm/wagmi/commit/204b7b624612405500ec098fb9e35facd3f74ca4)]: + - @wagmi/core@2.9.2 + +## 4.3.3 + +### Patch Changes + +- Updated dependencies [[`cda6a5d5`](https://github.com/wevm/wagmi/commit/cda6a5d56328330fbde050b4ef40b01c58d2519a)]: + - @wagmi/core@2.9.1 + +## 4.3.2 + +### Patch Changes + +- Updated dependencies [[`017828fc`](https://github.com/wevm/wagmi/commit/017828fc027c7a84b54ea9d627e9389f4d60d6c2)]: + - @wagmi/core@2.9.0 + +## 4.3.1 + +### Patch Changes + +- Updated dependencies [[`d4a78eb0`](https://github.com/wevm/wagmi/commit/d4a78eb07119d2e5617e52481ac7d6c6d1583ddc)]: + - @wagmi/core@2.8.1 + +## 4.3.0 + +### Minor Changes + +- [#3868](https://github.com/wevm/wagmi/pull/3868) [`c2af20b8`](https://github.com/wevm/wagmi/commit/c2af20b88cf16970d087faaec10b463357a5836e) Thanks [@jxom](https://github.com/jxom)! - Added `supportsSimulation` property to connectors that indicates if the connector's wallet supports contract simulation. + +### Patch Changes + +- Updated dependencies [[`0d141f17`](https://github.com/wevm/wagmi/commit/0d141f171d6ec44bcbfc9c876565b5e2fb8af6de), [`c2af20b8`](https://github.com/wevm/wagmi/commit/c2af20b88cf16970d087faaec10b463357a5836e)]: + - @wagmi/core@2.8.0 + +## 4.2.0 + +### Minor Changes + +- [#3857](https://github.com/wevm/wagmi/pull/3857) [`d4274c03`](https://github.com/wevm/wagmi/commit/d4274c03a6af5f2d26d31432016ebc14950a330e) Thanks [@tmm](https://github.com/tmm)! - Added `addEthereumChainParameter` to `switchChain`-related methods. + +### Patch Changes + +- Updated dependencies [[`d4274c03`](https://github.com/wevm/wagmi/commit/d4274c03a6af5f2d26d31432016ebc14950a330e), [`4781a405`](https://github.com/wevm/wagmi/commit/4781a4056d4ffc2c74f96a75429e9b2cd2417ad8), [`400c960b`](https://github.com/wevm/wagmi/commit/400c960b30d701c134850c695ae903a382c29b5b)]: + - @wagmi/core@2.7.0 + +## 4.1.28 + +### Patch Changes + +- [`e3c832a1`](https://github.com/wevm/wagmi/commit/e3c832a12c301f9b0ee129d877b3101d220ba8b2) Thanks [@jxom](https://github.com/jxom)! - Fixed undefined `navigator` issue in MetaMask connector. + +- Updated dependencies [[`e3c832a1`](https://github.com/wevm/wagmi/commit/e3c832a12c301f9b0ee129d877b3101d220ba8b2)]: + - @wagmi/core@2.6.19 + +## 4.1.27 + +### Patch Changes + +- [#3848](https://github.com/wevm/wagmi/pull/3848) [`dd40a41c`](https://github.com/wevm/wagmi/commit/dd40a41c526ab60a288aff2250ed8dba92a27b16) Thanks [@jxom](https://github.com/jxom)! - Updated MetaMask SDK. + +- Updated dependencies [[`dd40a41c`](https://github.com/wevm/wagmi/commit/dd40a41c526ab60a288aff2250ed8dba92a27b16)]: + - @wagmi/core@2.6.18 + +## 4.1.26 + +### Patch Changes + +- Updated dependencies [[`a97bfbae`](https://github.com/wevm/wagmi/commit/a97bfbaeb615cfef04665e5e7348d85d17f960f0)]: + - @wagmi/core@2.6.17 + +## 4.1.25 + +### Patch Changes + +- [#3788](https://github.com/wevm/wagmi/pull/3788) [`42ad380d`](https://github.com/wevm/wagmi/commit/42ad380d9a5d8bc0f61d73612142dea9d098de5e) Thanks [@tmm](https://github.com/tmm)! - Refactored connectors to remove unnecessarily event listeners. + +- Updated dependencies [[`42ad380d`](https://github.com/wevm/wagmi/commit/42ad380d9a5d8bc0f61d73612142dea9d098de5e)]: + - @wagmi/core@2.6.16 + +## 4.1.24 + +### Patch Changes + +- Updated dependencies [[`b907d5ac`](https://github.com/wevm/wagmi/commit/b907d5ac3a746bcbccc06d1fe78c5bd8f9a7d685)]: + - @wagmi/core@2.6.15 + +## 4.1.23 + +### Patch Changes + +- Updated dependencies [[`b3b54ef1`](https://github.com/wevm/wagmi/commit/b3b54ef179c5fa0d1694d38d4b808549a0550409), [`3da20bb8`](https://github.com/wevm/wagmi/commit/3da20bb80e7c3efeef8227ced66ad615370fc242), [`a3d1858f`](https://github.com/wevm/wagmi/commit/a3d1858fce448d2b70e36ee692ef1589b74e9d3f)]: + - @wagmi/core@2.6.14 + +## 4.1.22 + +### Patch Changes + +- Updated dependencies [[`b80236dc`](https://github.com/wevm/wagmi/commit/b80236dc623095fe8f1e1d10957d7776fb6ab48b)]: + - @wagmi/core@2.6.13 + +## 4.1.21 + +### Patch Changes + +- Updated dependencies [[`a59069e9`](https://github.com/wevm/wagmi/commit/a59069e9fab45dd606bb89a7f829fe94c51a5494), [`0acd3132`](https://github.com/wevm/wagmi/commit/0acd31320f534993af566be5490c2978b6184f66)]: + - @wagmi/core@2.6.12 + +## 4.1.20 + +### Patch Changes + +- [`e1ca4e63`](https://github.com/wevm/wagmi/commit/e1ca4e637ae6cec7f5902b0a2c0e0efc3b751a1d) Thanks [@tmm](https://github.com/tmm)! - Deprecated `normalizeChainId`. Use `Number` instead. + +- Updated dependencies [[`e1ca4e63`](https://github.com/wevm/wagmi/commit/e1ca4e637ae6cec7f5902b0a2c0e0efc3b751a1d)]: + - @wagmi/core@2.6.11 + +## 4.1.19 + +### Patch Changes + +- Updated dependencies [[`dbdca8fd`](https://github.com/wevm/wagmi/commit/dbdca8fd14b90c166222a66a373c1b33c06ce019)]: + - @wagmi/core@2.6.10 + +## 4.1.18 + +### Patch Changes + +- Updated dependencies [[`d56edf4f`](https://github.com/wevm/wagmi/commit/d56edf4f27c52acc7a0f57114454b0d3e22cacd6)]: + - @wagmi/core@2.6.9 + +## 4.1.17 + +### Patch Changes + +- Updated dependencies [[`e46bcd47`](https://github.com/wevm/wagmi/commit/e46bcd4738a18da15b53f6612b614379c1985374)]: + - @wagmi/core@2.6.8 + +## 4.1.16 + +### Patch Changes + +- [`1c1fee6a`](https://github.com/wevm/wagmi/commit/1c1fee6ab8f01f7734ac6ce05093fa8e388beb3e) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/ethereum-provider`. + +- [#3653](https://github.com/wevm/wagmi/pull/3653) [`88a2d744`](https://github.com/wevm/wagmi/commit/88a2d744a1315908c9e54156026df3ad2435ad44) Thanks [@tash-2s](https://github.com/tash-2s)! - Fixed error occurring when adding chains without explorers to MetaMask. + +- Updated dependencies [[`b479b5e8`](https://github.com/wevm/wagmi/commit/b479b5e8a5866cba792862f22e6352c4fb566137), [`f5648dd2`](https://github.com/wevm/wagmi/commit/f5648dd28b3576b628f57732b89287f55acbb1c1), [`1c1fee6a`](https://github.com/wevm/wagmi/commit/1c1fee6ab8f01f7734ac6ce05093fa8e388beb3e), [`88a2d744`](https://github.com/wevm/wagmi/commit/88a2d744a1315908c9e54156026df3ad2435ad44)]: + - @wagmi/core@2.6.7 + +## 4.1.15 + +### Patch Changes + +- Updated dependencies [[`a91c0b64`](https://github.com/wevm/wagmi/commit/a91c0b64ba8b3e6537a560e69724eb601f26af27)]: + - @wagmi/core@2.6.6 + +## 4.1.14 + +### Patch Changes + +- [#3591](https://github.com/wevm/wagmi/pull/3591) [`ca5decdb`](https://github.com/wevm/wagmi/commit/ca5decdb712f81e3f5dab933a94b967bca5b6af4) Thanks [@tmm](https://github.com/tmm)! - Fixed Coinbase Wallet import. + +- Updated dependencies [[`c677dcd2`](https://github.com/wevm/wagmi/commit/c677dcd245dccdf69289a3d66dded237b09570a2)]: + - @wagmi/core@2.6.5 + +## 4.1.13 + +### Patch Changes + +- [#3569](https://github.com/wevm/wagmi/pull/3569) [`fa25b448`](https://github.com/wevm/wagmi/commit/fa25b4482504b4d9729a5687ea6d6dc959265bc0) Thanks [@svenvoskamp](https://github.com/svenvoskamp)! - Updated dependencies. + +- [#3558](https://github.com/wevm/wagmi/pull/3558) [`895f28e8`](https://github.com/wevm/wagmi/commit/895f28e873af7c8eda5ca85734ff67c8979fd950) Thanks [@tmm](https://github.com/tmm)! - Fixed connector warnings. + +- Updated dependencies [[`7c6618e6`](https://github.com/wevm/wagmi/commit/7c6618e6a0eb1ff39cf8f66b34d3ddc14be538fe), [`895f28e8`](https://github.com/wevm/wagmi/commit/895f28e873af7c8eda5ca85734ff67c8979fd950)]: + - @wagmi/core@2.6.4 + +## 4.1.12 + +### Patch Changes + +- Updated dependencies [[`9c3b85dd`](https://github.com/wevm/wagmi/commit/9c3b85dd0a9a4a593e1d7e029345275735330e32), [`2a72214a`](https://github.com/wevm/wagmi/commit/2a72214a2901d6b6ddd39f80238aa0bd4db670a7)]: + - @wagmi/core@2.6.3 + +## 4.1.11 + +### Patch Changes + +- [#3518](https://github.com/wevm/wagmi/pull/3518) [`338e857d`](https://github.com/wevm/wagmi/commit/338e857d8cb2fe85e13d9207bef14cada1c1962d) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +- Updated dependencies [[`414eb048`](https://github.com/wevm/wagmi/commit/414eb048af492caac70c0e874dfc87c30702804a), [`338e857d`](https://github.com/wevm/wagmi/commit/338e857d8cb2fe85e13d9207bef14cada1c1962d)]: + - @wagmi/core@2.6.2 + +## 4.1.10 + +### Patch Changes + +- [#3510](https://github.com/wevm/wagmi/pull/3510) [`660ff80d`](https://github.com/wevm/wagmi/commit/660ff80d5b046967a446eba43ee54b8359a37d0d) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where connectors returning multiple addresses didn't checksum correctly. + +- Updated dependencies [[`660ff80d`](https://github.com/wevm/wagmi/commit/660ff80d5b046967a446eba43ee54b8359a37d0d), [`101a7dd1`](https://github.com/wevm/wagmi/commit/101a7dd131b0cae2dc25579ecab9044290efd37b)]: + - @wagmi/core@2.6.1 + +## 4.1.9 + +### Patch Changes + +- [#3496](https://github.com/wevm/wagmi/pull/3496) [`ba7f8a75`](https://github.com/wevm/wagmi/commit/ba7f8a758efb07664c6e401b5e7e325e7c62341b) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +- Updated dependencies [[`ba7f8a75`](https://github.com/wevm/wagmi/commit/ba7f8a758efb07664c6e401b5e7e325e7c62341b)]: + - @wagmi/core@2.6.0 + +## 4.1.8 + +### Patch Changes + +- Updated dependencies [[`ca98041d`](https://github.com/wevm/wagmi/commit/ca98041d1b39893d90246929485f4db0d1c6f9f7)]: + - @wagmi/core@2.5.0 + +## 4.1.7 + +### Patch Changes + +- [#3427](https://github.com/wevm/wagmi/pull/3427) [`370f1b4a`](https://github.com/wevm/wagmi/commit/370f1b4a3f154d181acf381c31c2e7862e22c0e4) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Bumped dependencies. + +- Updated dependencies [[`370f1b4a`](https://github.com/wevm/wagmi/commit/370f1b4a3f154d181acf381c31c2e7862e22c0e4)]: + - @wagmi/core@2.4.0 + +## 4.1.6 + +### Patch Changes + +- Updated dependencies [[`3be5bb7b`](https://github.com/wevm/wagmi/commit/3be5bb7b0b38646e12e6da5c762ef74dff66bcc2)]: + - @wagmi/core@2.3.1 + +## 4.1.5 + +### Patch Changes + +- [#3459](https://github.com/wevm/wagmi/pull/3459) [`d950b666`](https://github.com/wevm/wagmi/commit/d950b666b56700ca039ce16cdfdf34564991e7f5) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Bumped dependencies + +- [`1cfb6e5a`](https://github.com/wevm/wagmi/commit/1cfb6e5a875e707abcee00dd5739e87da05e8c90) Thanks [@jxom](https://github.com/jxom)! - Bumped listener limit on WalletConnect connector. + +- Updated dependencies [[`d950b666`](https://github.com/wevm/wagmi/commit/d950b666b56700ca039ce16cdfdf34564991e7f5), [`90ef39bb`](https://github.com/wevm/wagmi/commit/90ef39bb0f4ecb3c914d317875348e35ba0f4524), [`1cfb6e5a`](https://github.com/wevm/wagmi/commit/1cfb6e5a875e707abcee00dd5739e87da05e8c90)]: + - @wagmi/core@2.3.0 + +## 4.1.4 + +### Patch Changes + +- [#3443](https://github.com/wevm/wagmi/pull/3443) [`007024a6`](https://github.com/wevm/wagmi/commit/007024a684ddbecf924cdc06dd6a8854fc3d5eeb) Thanks [@jmrossy](https://github.com/jmrossy)! - Bumped dependencies. + +- [#3447](https://github.com/wevm/wagmi/pull/3447) [`a02a26ad`](https://github.com/wevm/wagmi/commit/a02a26ad030d3afb78f744377d61b5c60b65d97a) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +- Updated dependencies [[`a02a26ad`](https://github.com/wevm/wagmi/commit/a02a26ad030d3afb78f744377d61b5c60b65d97a), [`007024a6`](https://github.com/wevm/wagmi/commit/007024a684ddbecf924cdc06dd6a8854fc3d5eeb)]: + - @wagmi/core@2.2.1 + +## 4.1.3 + +### Patch Changes + +- Updated dependencies [[`00bf10a4`](https://github.com/wevm/wagmi/commit/00bf10a428b0d1c5dac35ebf25b19571e033ac26), [`64c073f6`](https://github.com/wevm/wagmi/commit/64c073f6c2720961e2d6aff986670b73dbfab9c3), [`fb6c4148`](https://github.com/wevm/wagmi/commit/fb6c4148d9e9e2fccfbe74c8f343b444dc68dec5)]: + - @wagmi/core@2.2.0 + +## 4.1.2 + +### Patch Changes + +- Updated dependencies [[`e00b8205`](https://github.com/wevm/wagmi/commit/e00b82058685751637edfa9a6b2d196a12549fe7)]: + - @wagmi/core@2.1.2 + +## 4.1.1 + +### Patch Changes + +- [`ec0d8b41`](https://github.com/wevm/wagmi/commit/ec0d8b4112181fefb11025e436a94a6114761d37) Thanks [@tmm](https://github.com/tmm)! - Added note to `metaMask` connector. + +- Updated dependencies [[`64b82282`](https://github.com/wevm/wagmi/commit/64b82282c1e57e77c25aa0814673780e4d11edd4), [`ec0d8b41`](https://github.com/wevm/wagmi/commit/ec0d8b4112181fefb11025e436a94a6114761d37)]: + - @wagmi/core@2.1.1 + +## 4.1.0 + +### Minor Changes + +- Updated dependencies [[`c9cd302e`](https://github.com/wevm/wagmi/commit/c9cd302e1c65c980deaee2e12567c2a8ec08b399)]: + - @wagmi/core@2.1.0 + +## 4.0.2 + +### Patch Changes + +- [#3384](https://github.com/wevm/wagmi/pull/3384) [`ee868c33`](https://github.com/wevm/wagmi/commit/ee868c3385dae511230b6ddcb5627c1293cc1844) Thanks [@tmm](https://github.com/tmm)! - Fixed connectors not bubbling error when connecting with `chainId` and subsequent user rejection. + +- Updated dependencies [[`ee868c33`](https://github.com/wevm/wagmi/commit/ee868c3385dae511230b6ddcb5627c1293cc1844)]: + - @wagmi/core@2.0.2 + +## 4.0.1 + +### Major Changes + +- [#3333](https://github.com/wevm/wagmi/pull/3333) [`b3a0baaa`](https://github.com/wevm/wagmi/commit/b3a0baaaee7decf750d376aab2502cd33ca4825a) Thanks [@tmm](https://github.com/tmm)! - Added support for Wagmi 2.0. + +### Patch Changes + +- Updated dependencies [[`b3a0baaa`](https://github.com/wevm/wagmi/commit/b3a0baaaee7decf750d376aab2502cd33ca4825a)]: + - @wagmi/core@2.0.0 + +## 3.1.11 + +### Patch Changes + +- [#3361](https://github.com/wevm/wagmi/pull/3361) [`bbbbf587`](https://github.com/wevm/wagmi/commit/bbbbf587e41bae12b072b7a7c897d580fc07cd2b) Thanks [@0xAsimetriq](https://github.com/0xAsimetriq)! - Updated WalletConnect connector dependencies + +## 3.1.10 + +### Patch Changes + +- [`53ca1f7e`](https://github.com/wevm/wagmi/commit/53ca1f7eb411d912e11fcce7e03bd61ed067959c) Thanks [@tmm](https://github.com/tmm)! - Removed LedgerConnector due to security vulnerability + +## 3.1.9 + +### Patch Changes + +- [#3114](https://github.com/wevm/wagmi/pull/3114) [`51eca0fb`](https://github.com/wevm/wagmi/commit/51eca0fbaea6932f31a5b8b4213f0252280053e2) Thanks [@akathecoder](https://github.com/akathecoder)! - Added Okto Wallet to Injected Wallets Connector + +- [#3299](https://github.com/wevm/wagmi/pull/3299) [`b02020b3`](https://github.com/wevm/wagmi/commit/b02020b3724e0228198f35817611bb063295906e) Thanks [@dasanra](https://github.com/dasanra)! - Fixed issue with [Safe SDK](https://github.com/wevm/viem/issues/579) by bumping `@safe-global/safe-apps-provider@0.18.1` + +## 3.1.8 + +### Patch Changes + +- [#3197](https://github.com/wevm/wagmi/pull/3197) [`e8f7bcbc`](https://github.com/wevm/wagmi/commit/e8f7bcbcd9c038a901c29e71769682c088efe2ac) Thanks [@ByteZhang1024](https://github.com/ByteZhang1024)! - Added OneKey Wallet to injected connector flags. + +## 3.1.7 + +### Patch Changes + +- [#3276](https://github.com/wevm/wagmi/pull/3276) [`83223a06`](https://github.com/wevm/wagmi/commit/83223a0659e2f675d897a1d3374c7af752c16abf) Thanks [@glitch-txs](https://github.com/glitch-txs)! - Removed required namespaces from WalletConnect connector + +## 3.1.6 + +### Patch Changes + +- [#3236](https://github.com/wevm/wagmi/pull/3236) [`cc7e18f2`](https://github.com/wevm/wagmi/commit/cc7e18f2e7f6b8b989f60f0b05aee70e996a9975) Thanks [@0xAsimetriq](https://github.com/0xAsimetriq)! - Updated @walletconnect/ethereum-provider + +- [#3236](https://github.com/wevm/wagmi/pull/3236) [`cc7e18f2`](https://github.com/wevm/wagmi/commit/cc7e18f2e7f6b8b989f60f0b05aee70e996a9975) Thanks [@0xAsimetriq](https://github.com/0xAsimetriq)! - Updated @walletconnect/ethereum-provider + +## 3.1.5 + +### Patch Changes + +- [#3220](https://github.com/wagmi-dev/wagmi/pull/3220) [`a1950449`](https://github.com/wagmi-dev/wagmi/commit/a1950449127ddf72fff8ecd1fc34c3690befbb05) Thanks [@rkalis](https://github.com/rkalis)! - Fixed a bug where injected walets with an empty providers array could not connect + +## 3.1.4 + +### Patch Changes + +- [#3115](https://github.com/wagmi-dev/wagmi/pull/3115) [`4e6ec415`](https://github.com/wagmi-dev/wagmi/commit/4e6ec4151baece94e940e227e0e3711c7f8534d9) Thanks [@bifot](https://github.com/bifot)! - Added SafePal injected name mapping. + +## 3.1.3 + +### Patch Changes + +- [#3141](https://github.com/wagmi-dev/wagmi/pull/3141) [`e78aa337`](https://github.com/wagmi-dev/wagmi/commit/e78aa337c454f04b41a3cbd381d25270dd4a0afd) Thanks [@einaralex](https://github.com/einaralex)! - Updated WalletConnect libraries. + +## 3.1.2 + +### Patch Changes + +- [#3009](https://github.com/wagmi-dev/wagmi/pull/3009) [`3aaba328`](https://github.com/wagmi-dev/wagmi/commit/3aaba32808ddb4035ec885f96992c91078056715) Thanks [@0xAsimetriq](https://github.com/0xAsimetriq)! - Update WalletConnect dependencies + +## 3.1.1 + +### Patch Changes + +- [#2973](https://github.com/wevm/wagmi/pull/2973) [`bf831bb3`](https://github.com/wevm/wagmi/commit/bf831bb30df8037cc4312342d0fe3c045408c2fe) Thanks [@masm](https://github.com/masm)! - Added Zeal wallet + +## 3.1.0 + +### Minor Changes + +- [#2956](https://github.com/wevm/wagmi/pull/2956) [`2abeb285`](https://github.com/wevm/wagmi/commit/2abeb285674af3e539cc2550b1f5027b1eb0c895) Thanks [@tmm](https://github.com/tmm)! - Replaced `@wagmi/chains` with `viem/chains`. + +## 3.0.0 + +### Patch Changes + +- 0306383: Updated WalletConnect dependencies +- Updated dependencies [d1ef9b4] +- Updated dependencies [484c846] + - @wagmi/chains@1.8.0 + +## 2.7.0 + +### Minor Changes + +- a270cb9: Updated WalletConnect dependencies. + +### Patch Changes + +- 06cc1b4: Add SubWallet injected flags +- 131a337: Added Desig Wallet name mapping. +- e089d7d: Added Fordefi Wallet name mapping. +- ce84d0a: Added Coin98 Wallet injected flags. +- Updated dependencies [8fdacd8] +- Updated dependencies [2e9283a] +- Updated dependencies [a432a2b] +- Updated dependencies [408740a] +- Updated dependencies [6794a61] +- Updated dependencies [0c5a32b] +- Updated dependencies [ebc85ec] +- Updated dependencies [5683df2] +- Updated dependencies [414ff36] +- Updated dependencies [4f514c6] +- Updated dependencies [1cf72bc] +- Updated dependencies [cd68471] +- Updated dependencies [baf3143] +- Updated dependencies [9737f24] +- Updated dependencies [7797238] +- Updated dependencies [3846811] +- Updated dependencies [0ea344c] + - @wagmi/chains@1.7.0 + +## 2.6.6 + +### Patch Changes + +- 56c127d: Updated WalletConnect dependencies. +- Updated dependencies [4b411d2] +- Updated dependencies [df697ac] +- Updated dependencies [186f5a7] +- Updated dependencies [a96b514] +- Updated dependencies [0a6e6da] + - @wagmi/chains@1.5.0 + +## 2.6.5 + +### Patch Changes + +- 51e346e: Updated WalletConnectConnector logic to handle individual namespaces like eip155:\* + +## 2.6.4 + +### Patch Changes + +- 0a57de2: Added conditional for WalletConnectConnector optionalChains + +## 2.6.3 + +### Patch Changes + +- f2d532d: Updated WalletConnect dependencies, exposed `relayUrl` option for `WalletConnectConnector` +- ff53857: Fixed issue importing `EthereumProvider` in Vite environments. +- Updated dependencies [d642e1d] +- Updated dependencies [3027d7b] +- Updated dependencies [97dbd44] + - @wagmi/chains@1.4.0 + +## 2.6.2 + +### Patch Changes + +- 27bb1b3: Added explicit type annotations for the `getWalletClient()` method. + +## 2.6.1 + +### Patch Changes + +- a3507a9: Updated @walletconnect/ethereum-provider dependency + +## 2.6.0 + +### Minor Changes + +- 32dc317: Updated @walletconnect/ethereum-provider and @walletconnect/modal dependencies + +## 2.5.0 + +### Minor Changes + +- 57e674e: Updated `@safe-global/safe-apps-sdk` & `@safe-global/safe-apps-provider` + +## 2.4.0 + +### Patch Changes + +- f21c8e0: Added WalletConnect v2 support to Ledger connector. +- 27482bb: Add HAQQ Wallet detection +- 7d6aa43: Exported `normalizeChainId`. +- Updated dependencies [62b8209] +- Updated dependencies [106ac13] +- Updated dependencies [8b3f5e5] + - @wagmi/chains@1.3.0 + +## 2.3.0 + +### Minor Changes + +- 28219ae: Added metadata property to WalletConnect init function +- 6fef949: Updated @walletconnect/modal and @walletconnect/ethereum-provider deps + +### Patch Changes + +- 72f6465: Added `TTWallet` to `getInjectedName` list +- Updated dependencies [a7cbd04] +- Updated dependencies [f6ee133] + - @wagmi/chains@1.2.0 + +## 2.2.0 + +### Minor Changes + +- 6c841d4: Changed `Address` type import from ABIType to viem. + +### Patch Changes + +- 09c83f8: Update @walletconnect/ethereum-provider, Replace @web3modal/standalone with @walletconnect/modal, Fix issue with wallet_addEthereumChain method in WalletConnectConnector + +## 2.1.1 + +### Patch Changes + +- c24de75: Updated `@walletconnect/ethereum-provider` and `@web3modal/standalone` dependencies. +- 605c422: Bumped `viem` peer dependency. +- dc1c546: Throw ResourceUnavailableError on -30002 errors. + +## 2.1.0 + +### Minor Changes + +- b001569: Bumped minimum TypeScript version to v5.0.4. + +### Patch Changes + +- 0f05b2b: Updated `abitype` to `0.8.7`. +- 6aea7ee: Fixed internal types. +- b187cb0: Added `isNovaWallet` injected flag. +- 5e44429: Added Edgeware mainnet and testnet +- b18b314: Updated @walletconnect/ethereum-provider and @web3modal/standalone dependencies +- Updated dependencies [b62a199] +- Updated dependencies [b001569] +- Updated dependencies [260ab59] +- Updated dependencies [6aea7ee] +- Updated dependencies [5e44429] + - @wagmi/chains@1.0.0 + +## 2.0.0 + +### Patch Changes + +- Updated dependencies [36c14b2] + - @wagmi/chains@0.3.0 + +## 1.0.5 + +### Patch Changes + +- fa61dfe: Updated viem. +- Updated dependencies [577d2a0] + - @wagmi/chains@0.2.25 + +## 1.0.4 + +### Patch Changes + +- bbbd11b: Corrected Rabby Wallet name +- Updated dependencies [0639a1f] + - @wagmi/chains@0.2.24 + +## 1.0.3 + +### Patch Changes + +- 64dfe61: Update @web3modal/standalone to v2.4.1, Update @walletconnect/ethereum-provider to 2.7.4 +- bab7ad8: Added Defiant to injected connector flags +- 44cde07: Added Talisman wallet flag + +## 1.0.2 + +### Patch Changes + +- bce5a0c: Removed chain fallback when instantiating a Wallet Client. + +## 1.0.1 + +### Patch Changes + +- [`ea651cd7`](https://github.com/wevm/wagmi/commit/ea651cd7fc75b7866272605467db11fd6e1d81af) Thanks [@jxom](https://github.com/jxom)! - Downgraded abitype. + +## 1.0.0 + +### Major Changes + +- 7e274f5: Released v1. + +### Patch Changes + +- 0966bf7: Changed Kucoin Wallet name mapping to Halo Wallet + +## 1.0.0-next.5 + +### Major Changes + +- Updated references. + +## 1.0.0-next.4 + +### Major Changes + +- Updated references. + +## 1.0.0-next.3 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/chains@1.0.0-next.0 + +## 1.0.0-next.2 + +### Major Changes + +- updated viem + +## 1.0.0-next.1 + +### Major Changes + +- [`a7dda00c`](https://github.com/wevm/wagmi/commit/a7dda00c5b546f8b2c42b527e4d9ac1b9e9ab1fb) Thanks [@jxom](https://github.com/jxom)! - Released v1. + +## 1.0.0-next.0 + +### Major Changes + +- 33488cf: Released v1. + +## 0.3.19 + +### Patch Changes + +- 274eef3: - Updated @web3modal/standalone to 2.3.7 + - Updated @walletconnect/ethereum-provider to 2.7.1 +- 41697df: Updated @walletconnect/ethereum-provider version to 2.7.2 +- 82dcb72: Added Enkrypt extension detection + +## 0.3.18 + +### Patch Changes + +- f66e065: Added BlockWallet to injected connector flags. + +## 0.3.17 + +### Patch Changes + +- 12ab5d1: Updated @coinbase/wallet-sdk to 3.6.6 + +## 0.3.16 + +### Patch Changes + +- c1e3ddf: Reverted ABIType version change. + +## 0.3.15 + +### Patch Changes + +- d4825e6: Fixed ABIType version to match downstream packages. + +## 0.3.14 + +### Patch Changes + +- c25ac82: Added more flags to `MetaMaskConnector` `getProvider` check. +- b19a932: Updated @web3modal/standalone to 2.3.0, @walletconnect/ethereum-provider to 2.7.0 +- cdc387e: Added `ImToken` to `getInjectedName` list + +## 0.3.13 + +### Patch Changes + +- 2a21d27: Updated `@coinbase/wallet-sdk` to `3.6.4` + +## 0.3.12 + +### Patch Changes + +- 9bb22b6: Updated `@walletconnect/ethereum-provider` to `2.6.2`, relaxed `@web3modal/standalone` version requirement +- 0d7625b: Added Rabby to injected connector flags +- f63d7fd: Added correct error to switch network cause. + +## 0.3.11 + +### Patch Changes + +- 0778abc: Renamed `isTally` injected provider to `Taho` + +## 0.3.10 + +### Patch Changes + +- 4267020: Added `qrModalOptions` option to `WalletConnectConnector` +- e78fb0a: Pinned WalletConnect dependencies + +## 0.3.9 + +### Patch Changes + +- 5cd0afc: Added `isZerion` to `InjectedProviderFlags` and `getInjectedName` +- be4825e: Added GameStop Wallet to injected connector flags + +## 0.3.8 + +### Patch Changes + +- 11f3fe2: Fixed issue where `UNSTABLE_shimOnConnectSelectAccount` would not bubble up error for MetaMask if request to connect was already active. + +## 0.3.7 + +### Patch Changes + +- 04c0e47: Fixed issue switching chain after adding to MetaMask. + +## 0.3.6 + +### Patch Changes + +- 85330c1: Removed `InjectedConnector` `shimChainChangedDisconnect` shim (no longer necessary). + +## 0.3.5 + +### Patch Changes + +- 8b1a526: Added Dawn wallet flag + +## 0.3.4 + +### Patch Changes + +- 6b15d6f: Updated `@walletconnect/ethereum-provider` to `2.5.1`. +- 1f452e7: Added OKX Wallet to injected connector flags. +- a4d9083: Added Backpack wallet to injected connector flags. +- 6a4af48: Enabled support for programmatic chain switching on `LedgerConnector` & added `"ledger"` to the switch chain regex on `WalletConnectLegacyConnector`. + +## 0.3.3 + +### Patch Changes + +- f24ce0c: Updated @walletconnect/ethereum-provider to 2.4.8 +- e3a3fee: Added "uniswap wallet" to the regex that determines wallets allowed to switch chains in the WalletConnect legacy connector +- 641af48: Added name mapping for Bifrost Wallet +- 4d2c90a: Added name mapping for Phantom +- 3d276d0: Added Status as the name of the injected connector for the Status App + +## 0.3.2 + +### Patch Changes + +- 13a6a07: Updated `@walletconnect/ethereum-provider` to `2.4.7`. + +## 0.3.1 + +### Patch Changes + +- a23c40f: Added name mapping for [Frontier](https://frontier.xyz) Wallet +- d779fb3: Added name mapping for HyperPay. + +## 0.3.0 + +### Minor Changes + +- c4d5bb5: **Breaking:** Removed the `version` config option for `WalletConnectConnector`. + + `WalletConnectConnector` now uses WalletConnect v2 by default. WalletConnect v1 is now `WalletConnectLegacyConnector`. + + ### WalletConnect v2 + + ```diff + import { WalletConnectConnector } from '@wagmi/connectors/walletConnect' + + const connector = new WalletConnectConnector({ + options: { + - version: '2', + projectId: 'abc', + }, + }) + ``` + + ### WalletConnect v1 + + ```diff + -import { WalletConnectConnector } from '@wagmi/connectors/walletConnect' + +import { WalletConnectLegacyConnector } from '@wagmi/connectors/walletConnectLegacy' + + -const connector = new WalletConnectConnector({ + +const connector = new WalletConnectLegacyConnector({ + options: { + qrcode: true, + }, + }) + ``` + +## 0.2.7 + +### Patch Changes + +- 57f1226: Added name mapping for XDEFI + +## 0.2.6 + +### Patch Changes + +- bb1b88c: Added name mapping for Bitski injected wallet +- fcb5595: Fixed shim disconnect key to read from defined Connector ID. +- 49f8853: Fixed `SafeConnector` import type error that existed for specific build environments. + +## 0.2.5 + +### Patch Changes + +- 5d121f2: Added `isApexWallet` to injected `window.ethereum` flags. +- e3566eb: Updated `@web3modal/standalone` to `2.1.1` for WalletConnectConnector. + +## 0.2.4 + +### Patch Changes + +- a4f31bc: Added Connector for [Safe](https://safe.global) wallet +- d5e25d9: Locked ethers peer dependency version to >=5.5.1 <6 + +## 0.2.3 + +### Patch Changes + +- 6fa74dd: Updated `@walletconnect/universal-provider` + Added more signable methods to WC v2. + +## 0.2.2 + +### Patch Changes + +- 6b0725b: Fixed race condition between `switchNetwork` and mutation Hooks that use `chainId` (e.g. `sendTransaction`). + +## 0.2.1 + +### Patch Changes + +- 942fcde: Updated `@walletconnect/universal-provider` and `@web3modal/standalone` packages for WalletConnectConnector (v2). + + Improved initialization flow for `@walletconnect/universal-provider` for WalletConnectConnector (v2). + +## 0.2.0 + +### Minor Changes + +- be33c7d: Chains are now narrowed to their most specific type using the TypeScript [`satisfies`](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/#the-satisfies-operator) operator. + +## 0.1.10 + +### Patch Changes + +- d75e8d2: Fixed ABIType version mismatch between packages. + +## 0.1.9 + +### Patch Changes + +- 8c3fc00: Added public RPC URL to Connector fallback chains + +## 0.1.8 + +### Patch Changes + +- 5e6dc30: Replaced legacy qrcodemodal with web3modal for WalletConnect v2. + +## 0.1.7 + +### Patch Changes + +- be4add2: Added `isRainbow` flag to `InjectedConnector`. + +## 0.1.6 + +### Patch Changes + +- 3dfc558: Add `switchSigner` method to `MockProvider`. + +## 0.1.5 + +### Patch Changes + +- 7dce4b5: Bumped WalletConnect Universal Provider version. + +## 0.1.4 + +### Patch Changes + +- 4cec598: Added CJS escape hatch bundle under the "cjs" tag. + +## 0.1.3 + +### Patch Changes + +- 822bc88: The `WalletConnectConnector` now supports WalletConnect v2. + + It can be enabled by setting `version` to `'2'` and supplying a [WalletConnect Cloud `projectId`](https://cloud.walletconnect.com/sign-in). + +## 0.1.2 + +### Patch Changes + +- 5e5f37f: Fixed issue where connecting to MetaMask may return with a stale address + +## 0.1.1 + +### Patch Changes + +- 919790c: Updated `@ledgerhq/connect-kit-loader` to `1.0.1` + +## 0.1.0 + +### Minor Changes + +- 5db7cba: Added `LedgerConnector` +- 55a0ca2: Initial release of the `@wagmi/connectors` package – a collection of Connectors for wagmi. diff --git a/packages/connectors/README.md b/packages/connectors/README.md new file mode 100644 index 0000000000..05418ec15c --- /dev/null +++ b/packages/connectors/README.md @@ -0,0 +1,13 @@ +# @wagmi/connectors + +Collection of connectors for Wagmi + +## Installation + +```bash +pnpm add @wagmi/connectors @wagmi/core viem +``` + +## Documentation + +For documentation and guides, visit [wagmi.sh](https://wagmi.sh). diff --git a/packages/connectors/package.json b/packages/connectors/package.json new file mode 100644 index 0000000000..5ddcfaa107 --- /dev/null +++ b/packages/connectors/package.json @@ -0,0 +1,71 @@ +{ + "name": "@wagmi/connectors", + "description": "Collection of connectors for Wagmi", + "version": "5.8.3", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/wevm/wagmi.git", + "directory": "packages/connectors" + }, + "scripts": { + "build": "pnpm run clean && pnpm run build:esm+types", + "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", + "check:types": "tsc --noEmit", + "clean": "rm -rf dist tsconfig.tsbuildinfo", + "test:build": "publint --strict && attw --pack --ignore-rules cjs-resolves-to-esm" + }, + "files": [ + "dist/**", + "!dist/**/*.tsbuildinfo", + "src/**/*.ts", + "!src/**/*.test.ts", + "!src/**/*.test-d.ts" + ], + "sideEffects": false, + "type": "module", + "main": "./dist/esm/exports/index.js", + "types": "./dist/types/exports/index.d.ts", + "typings": "./dist/types/exports/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/exports/index.d.ts", + "default": "./dist/esm/exports/index.js" + }, + "./package.json": "./package.json" + }, + "peerDependencies": { + "@wagmi/core": "workspace:*", + "typescript": ">=5.0.4", + "viem": "2.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + }, + "dependencies": { + "@coinbase/wallet-sdk": "4.3.0", + "@metamask/sdk": "0.33.1", + "@safe-global/safe-apps-provider": "0.18.6", + "@safe-global/safe-apps-sdk": "9.1.0", + "@walletconnect/ethereum-provider": "2.20.2", + "cbw-sdk": "npm:@coinbase/wallet-sdk@3.9.3" + }, + "devDependencies": { + "@wagmi/core": "workspace:*", + "msw": "^2.4.9" + }, + "contributors": ["awkweb.eth ", "jxom.eth "], + "funding": "https://github.com/sponsors/wevm", + "keywords": [ + "react", + "hooks", + "eth", + "ethereum", + "dapps", + "wallet", + "web3", + "abi" + ] +} diff --git a/packages/connectors/src/coinbaseWallet.test.ts b/packages/connectors/src/coinbaseWallet.test.ts new file mode 100644 index 0000000000..99b141e49b --- /dev/null +++ b/packages/connectors/src/coinbaseWallet.test.ts @@ -0,0 +1,17 @@ +import { config } from '@wagmi/test' +import { expect, expectTypeOf, test } from 'vitest' + +import { coinbaseWallet } from './coinbaseWallet.js' + +test('setup', () => { + const connectorFn = coinbaseWallet({ appName: 'wagmi', version: '4' }) + const connector = config._internal.connectors.setup(connectorFn) + expect(connector.name).toEqual('Coinbase Wallet') + + type ConnectFnParameters = NonNullable< + Parameters<(typeof connector)['connect']>[0] + > + expectTypeOf().toMatchTypeOf< + boolean | undefined + >() +}) diff --git a/packages/connectors/src/coinbaseWallet.ts b/packages/connectors/src/coinbaseWallet.ts new file mode 100644 index 0000000000..630c91261e --- /dev/null +++ b/packages/connectors/src/coinbaseWallet.ts @@ -0,0 +1,546 @@ +import type { + Preference, + ProviderInterface, + createCoinbaseWalletSDK, +} from '@coinbase/wallet-sdk' +import { + ChainNotConfiguredError, + type Connector, + createConnector, +} from '@wagmi/core' +import type { Compute, Mutable, Omit } from '@wagmi/core/internal' +import type { + CoinbaseWalletProvider as CBW_Provider, + CoinbaseWalletSDK as CBW_SDK, +} from 'cbw-sdk' +import { + type AddEthereumChainParameter, + type Address, + type Hex, + type ProviderRpcError, + SwitchChainError, + UserRejectedRequestError, + getAddress, + numberToHex, +} from 'viem' + +type Version = '3' | '4' + +export type CoinbaseWalletParameters = + version extends '4' + ? Compute< + { + headlessMode?: false | undefined + /** Coinbase Wallet SDK version */ + version?: version | '3' | undefined + } & Version4Parameters + > + : Compute< + { + /** + * @deprecated `headlessMode` will be removed in the next major version. Upgrade to `version: '4'`. + */ + headlessMode?: true | undefined + /** + * Coinbase Wallet SDK version + * @deprecated Version 3 will be removed in the next major version. Upgrade to `version: '4'`. + * @default '4' + */ + version?: version | '4' | undefined + } & Version3Parameters + > + +coinbaseWallet.type = 'coinbaseWallet' as const +export function coinbaseWallet( + parameters: CoinbaseWalletParameters = {} as any, +): version extends '4' + ? ReturnType + : ReturnType { + if (parameters.version === '3' || parameters.headlessMode) + return version3(parameters as Version3Parameters) as any + return version4(parameters as Version4Parameters) as any +} + +type Version4Parameters = Mutable< + Omit< + Parameters[0], + | 'appChainIds' // set via wagmi config + | 'preference' + > & { + // TODO(v3): Remove `Preference['options']` + /** + * Preference for the type of wallet to display. + * @default 'all' + */ + preference?: Preference['options'] | Compute | undefined + } +> + +function version4(parameters: Version4Parameters) { + type Provider = ProviderInterface & { + // for backwards compatibility + close?(): void + } + type Properties = { + connect(parameters?: { + chainId?: number | undefined + instantOnboarding?: boolean | undefined + isReconnecting?: boolean | undefined + }): Promise<{ + accounts: readonly Address[] + chainId: number + }> + } + + let walletProvider: Provider | undefined + + let accountsChanged: Connector['onAccountsChanged'] | undefined + let chainChanged: Connector['onChainChanged'] | undefined + let disconnect: Connector['onDisconnect'] | undefined + + return createConnector((config) => ({ + id: 'coinbaseWalletSDK', + name: 'Coinbase Wallet', + rdns: 'com.coinbase.wallet', + type: coinbaseWallet.type, + async connect({ chainId, ...rest } = {}) { + try { + const provider = await this.getProvider() + const accounts = ( + (await provider.request({ + method: 'eth_requestAccounts', + params: + 'instantOnboarding' in rest && rest.instantOnboarding + ? [{ onboarding: 'instant' }] + : [], + })) as string[] + ).map((x) => getAddress(x)) + + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged) + } + if (!chainChanged) { + chainChanged = this.onChainChanged.bind(this) + provider.on('chainChanged', chainChanged) + } + if (!disconnect) { + disconnect = this.onDisconnect.bind(this) + provider.on('disconnect', disconnect) + } + + // Switch to chain if provided + let currentChainId = await this.getChainId() + if (chainId && currentChainId !== chainId) { + const chain = await this.switchChain!({ chainId }).catch((error) => { + if (error.code === UserRejectedRequestError.code) throw error + return { id: currentChainId } + }) + currentChainId = chain?.id ?? currentChainId + } + + return { accounts, chainId: currentChainId } + } catch (error) { + if ( + /(user closed modal|accounts received is empty|user denied account|request rejected)/i.test( + (error as Error).message, + ) + ) + throw new UserRejectedRequestError(error as Error) + throw error + } + }, + async disconnect() { + const provider = await this.getProvider() + + if (accountsChanged) { + provider.removeListener('accountsChanged', accountsChanged) + accountsChanged = undefined + } + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + + provider.disconnect() + provider.close?.() + }, + async getAccounts() { + const provider = await this.getProvider() + return ( + (await provider.request({ + method: 'eth_accounts', + })) as string[] + ).map((x) => getAddress(x)) + }, + async getChainId() { + const provider = await this.getProvider() + const chainId = (await provider.request({ + method: 'eth_chainId', + })) as Hex + return Number(chainId) + }, + async getProvider() { + if (!walletProvider) { + const preference = (() => { + if (typeof parameters.preference === 'string') + return { options: parameters.preference } + return { + ...parameters.preference, + options: parameters.preference?.options ?? 'all', + } + })() + + const { createCoinbaseWalletSDK } = await import('@coinbase/wallet-sdk') + const sdk = createCoinbaseWalletSDK({ + ...parameters, + appChainIds: config.chains.map((x) => x.id), + preference, + }) + + walletProvider = sdk.getProvider() + } + + return walletProvider + }, + async isAuthorized() { + try { + const accounts = await this.getAccounts() + return !!accounts.length + } catch { + return false + } + }, + async switchChain({ addEthereumChainParameter, chainId }) { + const chain = config.chains.find((chain) => chain.id === chainId) + if (!chain) throw new SwitchChainError(new ChainNotConfiguredError()) + + const provider = await this.getProvider() + + try { + await provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: numberToHex(chain.id) }], + }) + return chain + } catch (error) { + // Indicates chain is not added to provider + if ((error as ProviderRpcError).code === 4902) { + try { + let blockExplorerUrls: string[] | undefined + if (addEthereumChainParameter?.blockExplorerUrls) + blockExplorerUrls = addEthereumChainParameter.blockExplorerUrls + else + blockExplorerUrls = chain.blockExplorers?.default.url + ? [chain.blockExplorers?.default.url] + : [] + + let rpcUrls: readonly string[] + if (addEthereumChainParameter?.rpcUrls?.length) + rpcUrls = addEthereumChainParameter.rpcUrls + else rpcUrls = [chain.rpcUrls.default?.http[0] ?? ''] + + const addEthereumChain = { + blockExplorerUrls, + chainId: numberToHex(chainId), + chainName: addEthereumChainParameter?.chainName ?? chain.name, + iconUrls: addEthereumChainParameter?.iconUrls, + nativeCurrency: + addEthereumChainParameter?.nativeCurrency ?? + chain.nativeCurrency, + rpcUrls, + } satisfies AddEthereumChainParameter + + await provider.request({ + method: 'wallet_addEthereumChain', + params: [addEthereumChain], + }) + + return chain + } catch (error) { + throw new UserRejectedRequestError(error as Error) + } + } + + throw new SwitchChainError(error as Error) + } + }, + onAccountsChanged(accounts) { + if (accounts.length === 0) this.onDisconnect() + else + config.emitter.emit('change', { + accounts: accounts.map((x) => getAddress(x)), + }) + }, + onChainChanged(chain) { + const chainId = Number(chain) + config.emitter.emit('change', { chainId }) + }, + async onDisconnect(_error) { + config.emitter.emit('disconnect') + + const provider = await this.getProvider() + if (accountsChanged) { + provider.removeListener('accountsChanged', accountsChanged) + accountsChanged = undefined + } + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + }, + })) +} + +type Version3Parameters = Mutable< + Omit< + ConstructorParameters[0], + 'reloadOnDisconnect' // remove property since TSDoc says default is `true` + > +> & { + /** + * Fallback Ethereum JSON RPC URL + * @default "" + */ + jsonRpcUrl?: string | undefined + /** + * Fallback Ethereum Chain ID + * @default 1 + */ + chainId?: number | undefined + /** + * Whether or not to reload dapp automatically after disconnect. + * @default false + */ + reloadOnDisconnect?: boolean | undefined +} + +function version3(parameters: Version3Parameters) { + const reloadOnDisconnect = false + + type Provider = CBW_Provider + + let sdk: CBW_SDK | undefined + let walletProvider: Provider | undefined + + let accountsChanged: Connector['onAccountsChanged'] | undefined + let chainChanged: Connector['onChainChanged'] | undefined + let disconnect: Connector['onDisconnect'] | undefined + + return createConnector((config) => ({ + id: 'coinbaseWalletSDK', + name: 'Coinbase Wallet', + rdns: 'com.coinbase.wallet', + type: coinbaseWallet.type, + async connect({ chainId } = {}) { + try { + const provider = await this.getProvider() + const accounts = ( + (await provider.request({ + method: 'eth_requestAccounts', + })) as string[] + ).map((x) => getAddress(x)) + + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged) + } + if (!chainChanged) { + chainChanged = this.onChainChanged.bind(this) + provider.on('chainChanged', chainChanged) + } + if (!disconnect) { + disconnect = this.onDisconnect.bind(this) + provider.on('disconnect', disconnect) + } + + // Switch to chain if provided + let currentChainId = await this.getChainId() + if (chainId && currentChainId !== chainId) { + const chain = await this.switchChain!({ chainId }).catch((error) => { + if (error.code === UserRejectedRequestError.code) throw error + return { id: currentChainId } + }) + currentChainId = chain?.id ?? currentChainId + } + + return { accounts, chainId: currentChainId } + } catch (error) { + if ( + /(user closed modal|accounts received is empty|user denied account)/i.test( + (error as Error).message, + ) + ) + throw new UserRejectedRequestError(error as Error) + throw error + } + }, + async disconnect() { + const provider = await this.getProvider() + + if (accountsChanged) { + provider.removeListener('accountsChanged', accountsChanged) + accountsChanged = undefined + } + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + + provider.disconnect() + provider.close() + }, + async getAccounts() { + const provider = await this.getProvider() + return ( + await provider.request({ + method: 'eth_accounts', + }) + ).map((x) => getAddress(x)) + }, + async getChainId() { + const provider = await this.getProvider() + const chainId = await provider.request({ + method: 'eth_chainId', + }) + return Number(chainId) + }, + async getProvider() { + if (!walletProvider) { + // Unwrapping import for Vite compatibility. + // See: https://github.com/vitejs/vite/issues/9703 + const CoinbaseWalletSDK = await (async () => { + const { default: SDK } = await import('cbw-sdk') + if (typeof SDK !== 'function' && typeof SDK.default === 'function') + return SDK.default + return SDK as unknown as typeof SDK.default + })() + + sdk = new CoinbaseWalletSDK({ ...parameters, reloadOnDisconnect }) + + // Force types to retrieve private `walletExtension` method from the Coinbase Wallet SDK. + const walletExtensionChainId = ( + sdk as unknown as { + get walletExtension(): { getChainId(): number } | undefined + } + ).walletExtension?.getChainId() + + const chain = + config.chains.find((chain) => + parameters.chainId + ? chain.id === parameters.chainId + : chain.id === walletExtensionChainId, + ) || config.chains[0] + const chainId = parameters.chainId || chain?.id + const jsonRpcUrl = + parameters.jsonRpcUrl || chain?.rpcUrls.default.http[0] + + walletProvider = sdk.makeWeb3Provider(jsonRpcUrl, chainId) + } + + return walletProvider + }, + async isAuthorized() { + try { + const accounts = await this.getAccounts() + return !!accounts.length + } catch { + return false + } + }, + async switchChain({ addEthereumChainParameter, chainId }) { + const chain = config.chains.find((chain) => chain.id === chainId) + if (!chain) throw new SwitchChainError(new ChainNotConfiguredError()) + + const provider = await this.getProvider() + + try { + await provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: numberToHex(chain.id) }], + }) + return chain + } catch (error) { + // Indicates chain is not added to provider + if ((error as ProviderRpcError).code === 4902) { + try { + let blockExplorerUrls: string[] | undefined + if (addEthereumChainParameter?.blockExplorerUrls) + blockExplorerUrls = addEthereumChainParameter.blockExplorerUrls + else + blockExplorerUrls = chain.blockExplorers?.default.url + ? [chain.blockExplorers?.default.url] + : [] + + let rpcUrls: readonly string[] + if (addEthereumChainParameter?.rpcUrls?.length) + rpcUrls = addEthereumChainParameter.rpcUrls + else rpcUrls = [chain.rpcUrls.default?.http[0] ?? ''] + + const addEthereumChain = { + blockExplorerUrls, + chainId: numberToHex(chainId), + chainName: addEthereumChainParameter?.chainName ?? chain.name, + iconUrls: addEthereumChainParameter?.iconUrls, + nativeCurrency: + addEthereumChainParameter?.nativeCurrency ?? + chain.nativeCurrency, + rpcUrls, + } satisfies AddEthereumChainParameter + + await provider.request({ + method: 'wallet_addEthereumChain', + params: [addEthereumChain], + }) + + return chain + } catch (error) { + throw new UserRejectedRequestError(error as Error) + } + } + + throw new SwitchChainError(error as Error) + } + }, + onAccountsChanged(accounts) { + if (accounts.length === 0) this.onDisconnect() + else + config.emitter.emit('change', { + accounts: accounts.map((x) => getAddress(x)), + }) + }, + onChainChanged(chain) { + const chainId = Number(chain) + config.emitter.emit('change', { chainId }) + }, + async onDisconnect(_error) { + config.emitter.emit('disconnect') + + const provider = await this.getProvider() + if (accountsChanged) { + provider.removeListener('accountsChanged', accountsChanged) + accountsChanged = undefined + } + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + }, + })) +} diff --git a/packages/connectors/src/exports/index.test.ts b/packages/connectors/src/exports/index.test.ts new file mode 100644 index 0000000000..bcf100cb61 --- /dev/null +++ b/packages/connectors/src/exports/index.test.ts @@ -0,0 +1,17 @@ +import { expect, test } from 'vitest' + +import * as connectors from './index.js' + +test('exports', () => { + expect(Object.keys(connectors)).toMatchInlineSnapshot(` + [ + "injected", + "mock", + "coinbaseWallet", + "metaMask", + "safe", + "walletConnect", + "version", + ] + `) +}) diff --git a/packages/connectors/src/exports/index.ts b/packages/connectors/src/exports/index.ts new file mode 100644 index 0000000000..bac0975956 --- /dev/null +++ b/packages/connectors/src/exports/index.ts @@ -0,0 +1,23 @@ +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type InjectedParameters, + injected, + type MockParameters, + mock, +} from '@wagmi/core' + +export { + type CoinbaseWalletParameters, + coinbaseWallet, +} from '../coinbaseWallet.js' + +export { type MetaMaskParameters, metaMask } from '../metaMask.js' + +export { type SafeParameters, safe } from '../safe.js' + +export { + type WalletConnectParameters, + walletConnect, +} from '../walletConnect.js' + +export { version } from '../version.js' diff --git a/packages/connectors/src/metaMask.test.ts b/packages/connectors/src/metaMask.test.ts new file mode 100644 index 0000000000..40c3f0f7f9 --- /dev/null +++ b/packages/connectors/src/metaMask.test.ts @@ -0,0 +1,10 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { metaMask } from './metaMask.js' + +test('setup', () => { + const connectorFn = metaMask() + const connector = config._internal.connectors.setup(connectorFn) + expect(connector.name).toEqual('MetaMask') +}) diff --git a/packages/connectors/src/metaMask.ts b/packages/connectors/src/metaMask.ts new file mode 100644 index 0000000000..02ab4c3fb3 --- /dev/null +++ b/packages/connectors/src/metaMask.ts @@ -0,0 +1,505 @@ +import type { + MetaMaskSDK, + MetaMaskSDKOptions, + RPC_URLS_MAP, + SDKProvider, +} from '@metamask/sdk' +import { + ChainNotConfiguredError, + type Connector, + ProviderNotFoundError, + createConnector, + extractRpcUrls, +} from '@wagmi/core' +import type { + Compute, + ExactPartial, + OneOf, + RemoveUndefined, + UnionCompute, +} from '@wagmi/core/internal' +import { + type AddEthereumChainParameter, + type Address, + type Hex, + type ProviderConnectInfo, + type ProviderRpcError, + ResourceUnavailableRpcError, + type RpcError, + SwitchChainError, + UserRejectedRequestError, + getAddress, + hexToNumber, + numberToHex, + withRetry, + withTimeout, +} from 'viem' + +export type MetaMaskParameters = UnionCompute< + WagmiMetaMaskSDKOptions & + OneOf< + | { + /* Shortcut to connect and sign a message */ + connectAndSign?: string | undefined + } + | { + // TODO: Strongly type `method` and `params` + /* Allow `connectWith` any rpc method */ + connectWith?: { method: string; params: unknown[] } | undefined + } + > +> + +type WagmiMetaMaskSDKOptions = Compute< + ExactPartial< + Omit< + MetaMaskSDKOptions, + | '_source' + | 'forceDeleteProvider' + | 'forceInjectProvider' + | 'injectProvider' + | 'useDeeplink' + | 'readonlyRPCMap' + > + > & { + /** @deprecated */ + forceDeleteProvider?: MetaMaskSDKOptions['forceDeleteProvider'] + /** @deprecated */ + forceInjectProvider?: MetaMaskSDKOptions['forceInjectProvider'] + /** @deprecated */ + injectProvider?: MetaMaskSDKOptions['injectProvider'] + /** @deprecated */ + useDeeplink?: MetaMaskSDKOptions['useDeeplink'] + } +> + +metaMask.type = 'metaMask' as const +export function metaMask(parameters: MetaMaskParameters = {}) { + type Provider = SDKProvider + type Properties = { + onConnect(connectInfo: ProviderConnectInfo): void + onDisplayUri(uri: string): void + } + type Listener = Parameters[1] + + let sdk: MetaMaskSDK + let provider: Provider | undefined + let providerPromise: Promise + + let accountsChanged: Connector['onAccountsChanged'] | undefined + let chainChanged: Connector['onChainChanged'] | undefined + let connect: Connector['onConnect'] | undefined + let displayUri: ((uri: string) => void) | undefined + let disconnect: Connector['onDisconnect'] | undefined + + return createConnector((config) => ({ + id: 'metaMaskSDK', + name: 'MetaMask', + rdns: ['io.metamask', 'io.metamask.mobile'], + type: metaMask.type, + async setup() { + const provider = await this.getProvider() + if (provider?.on) { + if (!connect) { + connect = this.onConnect.bind(this) + provider.on('connect', connect as Listener) + } + + // We shouldn't need to listen for `'accountsChanged'` here since the `'connect'` event should suffice (and wallet shouldn't be connected yet). + // Some wallets, like MetaMask, do not implement the `'connect'` event and overload `'accountsChanged'` instead. + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged as Listener) + } + } + }, + async connect({ chainId, isReconnecting } = {}) { + const provider = await this.getProvider() + if (!displayUri) { + displayUri = this.onDisplayUri + provider.on('display_uri', displayUri as Listener) + } + + let accounts: readonly Address[] = [] + if (isReconnecting) accounts = await this.getAccounts().catch(() => []) + + try { + let signResponse: string | undefined + let connectWithResponse: unknown | undefined + if (!accounts?.length) { + if (parameters.connectAndSign || parameters.connectWith) { + if (parameters.connectAndSign) + signResponse = await sdk.connectAndSign({ + msg: parameters.connectAndSign, + }) + else if (parameters.connectWith) + connectWithResponse = await sdk.connectWith({ + method: parameters.connectWith.method, + params: parameters.connectWith.params, + }) + + accounts = await this.getAccounts() + } else { + const requestedAccounts = (await sdk.connect()) as string[] + accounts = requestedAccounts.map((x) => getAddress(x)) + } + } + // Switch to chain if provided + let currentChainId = (await this.getChainId()) as number + if (chainId && currentChainId !== chainId) { + const chain = await this.switchChain!({ chainId }).catch((error) => { + if (error.code === UserRejectedRequestError.code) throw error + return { id: currentChainId } + }) + currentChainId = chain?.id ?? currentChainId + } + + if (displayUri) { + provider.removeListener('display_uri', displayUri) + displayUri = undefined + } + + if (signResponse) + provider.emit('connectAndSign', { + accounts, + chainId: currentChainId, + signResponse, + }) + else if (connectWithResponse) + provider.emit('connectWith', { + accounts, + chainId: currentChainId, + connectWithResponse, + }) + + // Manage EIP-1193 event listeners + // https://eips.ethereum.org/EIPS/eip-1193#events + if (connect) { + provider.removeListener('connect', connect) + connect = undefined + } + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged as Listener) + } + if (!chainChanged) { + chainChanged = this.onChainChanged.bind(this) + provider.on('chainChanged', chainChanged as Listener) + } + if (!disconnect) { + disconnect = this.onDisconnect.bind(this) + provider.on('disconnect', disconnect as Listener) + } + + return { accounts, chainId: currentChainId } + } catch (err) { + const error = err as RpcError + if (error.code === UserRejectedRequestError.code) + throw new UserRejectedRequestError(error) + if (error.code === ResourceUnavailableRpcError.code) + throw new ResourceUnavailableRpcError(error) + throw error + } + }, + async disconnect() { + const provider = await this.getProvider() + + // Manage EIP-1193 event listeners + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + if (!connect) { + connect = this.onConnect.bind(this) + provider.on('connect', connect as Listener) + } + + await sdk.terminate() + }, + async getAccounts() { + const provider = await this.getProvider() + const accounts = (await provider.request({ + method: 'eth_accounts', + })) as string[] + return accounts.map((x) => getAddress(x)) + }, + async getChainId() { + const provider = await this.getProvider() + const chainId = + provider.getChainId() || + (await provider?.request({ method: 'eth_chainId' })) + return Number(chainId) + }, + async getProvider() { + async function initProvider() { + // Unwrapping import for Vite compatibility. + // See: https://github.com/vitejs/vite/issues/9703 + const MetaMaskSDK = await (async () => { + const { default: SDK } = await import('@metamask/sdk') + if (typeof SDK !== 'function' && typeof SDK.default === 'function') + return SDK.default + return SDK as unknown as typeof SDK.default + })() + + const readonlyRPCMap: RPC_URLS_MAP = {} + for (const chain of config.chains) + readonlyRPCMap[numberToHex(chain.id)] = extractRpcUrls({ + chain, + transports: config.transports, + })?.[0] + + sdk = new MetaMaskSDK({ + _source: 'wagmi', + forceDeleteProvider: false, + forceInjectProvider: false, + injectProvider: false, + // Workaround cast since MetaMask SDK does not support `'exactOptionalPropertyTypes'` + ...(parameters as RemoveUndefined), + readonlyRPCMap, + dappMetadata: { + ...parameters.dappMetadata, + // Test if name and url are set AND not empty + name: parameters.dappMetadata?.name + ? parameters.dappMetadata?.name + : 'wagmi', + url: parameters.dappMetadata?.url + ? parameters.dappMetadata?.url + : typeof window !== 'undefined' + ? window.location.origin + : 'https://wagmi.sh', + }, + useDeeplink: parameters.useDeeplink ?? true, + }) + const result = await sdk.init() + // On initial load, sometimes `sdk.getProvider` does not return provider. + // https://github.com/wevm/wagmi/issues/4367 + // Use result of `init` call if available. + const provider = (() => { + if (result?.activeProvider) return result.activeProvider + return sdk.getProvider() + })() + if (!provider) throw new ProviderNotFoundError() + return provider + } + + if (!provider) { + if (!providerPromise) providerPromise = initProvider() + provider = await providerPromise + } + return provider! + }, + async isAuthorized() { + try { + // MetaMask mobile provider sometimes fails to immediately resolve + // JSON-RPC requests on page load + const timeout = 200 + const accounts = await withRetry( + () => withTimeout(() => this.getAccounts(), { timeout }), + { + delay: timeout + 1, + retryCount: 3, + }, + ) + return !!accounts.length + } catch { + return false + } + }, + async switchChain({ addEthereumChainParameter, chainId }) { + const provider = await this.getProvider() + + const chain = config.chains.find((x) => x.id === chainId) + if (!chain) throw new SwitchChainError(new ChainNotConfiguredError()) + + try { + await provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: numberToHex(chainId) }], + }) + + // During `'wallet_switchEthereumChain'`, MetaMask makes a `'net_version'` RPC call to the target chain. + // If this request fails, MetaMask does not emit the `'chainChanged'` event, but will still switch the chain. + // To counter this behavior, we request and emit the current chain ID to confirm the chain switch either via + // this callback or an externally emitted `'chainChanged'` event. + // https://github.com/MetaMask/metamask-extension/issues/24247 + await waitForChainIdToSync() + await sendAndWaitForChangeEvent(chainId) + + return chain + } catch (err) { + const error = err as RpcError + + if (error.code === UserRejectedRequestError.code) + throw new UserRejectedRequestError(error) + + // Indicates chain is not added to provider + if ( + error.code === 4902 || + // Unwrapping for MetaMask Mobile + // https://github.com/MetaMask/metamask-mobile/issues/2944#issuecomment-976988719 + (error as ProviderRpcError<{ originalError?: { code: number } }>) + ?.data?.originalError?.code === 4902 + ) { + try { + await provider.request({ + method: 'wallet_addEthereumChain', + params: [ + { + blockExplorerUrls: (() => { + const { default: blockExplorer, ...blockExplorers } = + chain.blockExplorers ?? {} + if (addEthereumChainParameter?.blockExplorerUrls) + return addEthereumChainParameter.blockExplorerUrls + if (blockExplorer) + return [ + blockExplorer.url, + ...Object.values(blockExplorers).map((x) => x.url), + ] + return + })(), + chainId: numberToHex(chainId), + chainName: addEthereumChainParameter?.chainName ?? chain.name, + iconUrls: addEthereumChainParameter?.iconUrls, + nativeCurrency: + addEthereumChainParameter?.nativeCurrency ?? + chain.nativeCurrency, + rpcUrls: (() => { + if (addEthereumChainParameter?.rpcUrls?.length) + return addEthereumChainParameter.rpcUrls + return [chain.rpcUrls.default?.http[0] ?? ''] + })(), + } satisfies AddEthereumChainParameter, + ], + }) + + await waitForChainIdToSync() + await sendAndWaitForChangeEvent(chainId) + + return chain + } catch (err) { + const error = err as RpcError + if (error.code === UserRejectedRequestError.code) + throw new UserRejectedRequestError(error) + throw new SwitchChainError(error) + } + } + + throw new SwitchChainError(error) + } + + async function waitForChainIdToSync() { + // On mobile, there is a race condition between the result of `'wallet_addEthereumChain'` and `'eth_chainId'`. + // To avoid this, we wait for `'eth_chainId'` to return the expected chain ID with a retry loop. + await withRetry( + async () => { + const value = hexToNumber( + // `'eth_chainId'` is cached by the MetaMask SDK side to avoid unnecessary deeplinks + (await provider.request({ method: 'eth_chainId' })) as Hex, + ) + // `value` doesn't match expected `chainId`, throw to trigger retry + if (value !== chainId) + throw new Error('User rejected switch after adding network.') + return value + }, + { + delay: 50, + retryCount: 20, // android device encryption is slower + }, + ) + } + + async function sendAndWaitForChangeEvent(chainId: number) { + await new Promise((resolve) => { + const listener = ((data) => { + if ('chainId' in data && data.chainId === chainId) { + config.emitter.off('change', listener) + resolve() + } + }) satisfies Parameters[1] + config.emitter.on('change', listener) + config.emitter.emit('change', { chainId }) + }) + } + }, + async onAccountsChanged(accounts) { + // Disconnect if there are no accounts + if (accounts.length === 0) { + // ... and using browser extension + if (sdk.isExtensionActive()) this.onDisconnect() + // FIXME(upstream): Mobile app sometimes emits invalid `accountsChanged` event with empty accounts array + else return + } + // Connect if emitter is listening for connect event (e.g. is disconnected and connects through wallet interface) + else if (config.emitter.listenerCount('connect')) { + const chainId = (await this.getChainId()).toString() + this.onConnect({ chainId }) + } + // Regular change event + else + config.emitter.emit('change', { + accounts: accounts.map((x) => getAddress(x)), + }) + }, + onChainChanged(chain) { + const chainId = Number(chain) + config.emitter.emit('change', { chainId }) + }, + async onConnect(connectInfo) { + const accounts = await this.getAccounts() + if (accounts.length === 0) return + + const chainId = Number(connectInfo.chainId) + config.emitter.emit('connect', { accounts, chainId }) + + const provider = await this.getProvider() + if (connect) { + provider.removeListener('connect', connect) + connect = undefined + } + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged as Listener) + } + if (!chainChanged) { + chainChanged = this.onChainChanged.bind(this) + provider.on('chainChanged', chainChanged as Listener) + } + if (!disconnect) { + disconnect = this.onDisconnect.bind(this) + provider.on('disconnect', disconnect as Listener) + } + }, + async onDisconnect(error) { + const provider = await this.getProvider() + + // If MetaMask emits a `code: 1013` error, wait for reconnection before disconnecting + // https://github.com/MetaMask/providers/pull/120 + if (error && (error as RpcError<1013>).code === 1013) { + if (provider && !!(await this.getAccounts()).length) return + } + + config.emitter.emit('disconnect') + + // Manage EIP-1193 event listeners + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + if (!connect) { + connect = this.onConnect.bind(this) + provider.on('connect', connect as Listener) + } + }, + onDisplayUri(uri) { + config.emitter.emit('message', { type: 'display_uri', data: uri }) + }, + })) +} diff --git a/packages/connectors/src/safe.test.ts b/packages/connectors/src/safe.test.ts new file mode 100644 index 0000000000..0571115f36 --- /dev/null +++ b/packages/connectors/src/safe.test.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { safe } from './safe.js' + +/* + * To manually test the Safe connector: + * + * 1. Run the wagmi playground app (`pnpm dev`) + * 2. Add a custom Safe App with App URL set to `http://localhost:5173` (make sure there is a `manifest.json` file served by the playground) + * 3. Open the playground app at `https://app.safe.global/eth:0x4557B18E779944BFE9d78A672452331C186a9f48/apps?appUrl=http%3A%2F%2Flocalhost%3A5173` + * + * See https://docs.gnosis-safe.io/learn/safe-tools/sdks/safe-apps/releasing-your-safe-app for more info. + */ + +test('setup', () => { + const connectorFn = safe({ + allowedDomains: [/gnosis-safe.io$/, /app.safe.global$/], + debug: false, + }) + const connector = config._internal.connectors.setup(connectorFn) + expect(connector.name).toEqual('Safe') +}) diff --git a/packages/connectors/src/safe.ts b/packages/connectors/src/safe.ts new file mode 100644 index 0000000000..13153e106f --- /dev/null +++ b/packages/connectors/src/safe.ts @@ -0,0 +1,145 @@ +import type { SafeAppProvider } from '@safe-global/safe-apps-provider' +import type { Opts } from '@safe-global/safe-apps-sdk' +import { + type Connector, + ProviderNotFoundError, + createConnector, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { getAddress, withTimeout } from 'viem' + +export type SafeParameters = Compute< + Opts & { + /** + * Connector automatically connects when used as Safe App. + * + * This flag simulates the disconnect behavior by keeping track of connection status in storage + * and only autoconnecting when previously connected by user action (e.g. explicitly choosing to connect). + * + * @default false + */ + shimDisconnect?: boolean | undefined + /** + * Timeout in milliseconds for `getInfo` (from the Safe SDK) to resolve. + * + * `getInfo` does not resolve when not used in Safe App iFrame. This allows the connector to force a timeout. + * @default 10 + */ + unstable_getInfoTimeout?: number | undefined + } +> + +safe.type = 'safe' as const +export function safe(parameters: SafeParameters = {}) { + const { shimDisconnect = false } = parameters + + type Provider = SafeAppProvider | undefined + type Properties = Record + type StorageItem = { 'safe.disconnected': true } + + let provider_: Provider | undefined + + let disconnect: Connector['onDisconnect'] | undefined + + return createConnector((config) => ({ + id: 'safe', + name: 'Safe', + type: safe.type, + async connect() { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + + const accounts = await this.getAccounts() + const chainId = await this.getChainId() + + if (!disconnect) { + disconnect = this.onDisconnect.bind(this) + provider.on('disconnect', disconnect) + } + + // Remove disconnected shim if it exists + if (shimDisconnect) await config.storage?.removeItem('safe.disconnected') + + return { accounts, chainId } + }, + async disconnect() { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + + // Add shim signalling connector is disconnected + if (shimDisconnect) + await config.storage?.setItem('safe.disconnected', true) + }, + async getAccounts() { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + return (await provider.request({ method: 'eth_accounts' })).map( + getAddress, + ) + }, + async getProvider() { + // Only allowed in iframe context + const isIframe = + typeof window !== 'undefined' && window?.parent !== window + if (!isIframe) return + + if (!provider_) { + const { default: SDK } = await import('@safe-global/safe-apps-sdk') + const sdk = new SDK(parameters) + + // `getInfo` hangs when not used in Safe App iFrame + // https://github.com/safe-global/safe-apps-sdk/issues/263#issuecomment-1029835840 + const safe = await withTimeout(() => sdk.safe.getInfo(), { + timeout: parameters.unstable_getInfoTimeout ?? 10, + }) + if (!safe) throw new Error('Could not load Safe information') + // Unwrapping import for Vite compatibility. + // See: https://github.com/vitejs/vite/issues/9703 + const SafeAppProvider = await (async () => { + const Provider = await import('@safe-global/safe-apps-provider') + if ( + typeof Provider.SafeAppProvider !== 'function' && + typeof Provider.default.SafeAppProvider === 'function' + ) + return Provider.default.SafeAppProvider + return Provider.SafeAppProvider + })() + provider_ = new SafeAppProvider(safe, sdk) + } + return provider_ + }, + async getChainId() { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + return Number(provider.chainId) + }, + async isAuthorized() { + try { + const isDisconnected = + shimDisconnect && + // If shim exists in storage, connector is disconnected + (await config.storage?.getItem('safe.disconnected')) + if (isDisconnected) return false + + const accounts = await this.getAccounts() + return !!accounts.length + } catch { + return false + } + }, + onAccountsChanged() { + // Not relevant for Safe because changing account requires app reload. + }, + onChainChanged() { + // Not relevant for Safe because Safe smart contract wallets only exist on single chain. + }, + onDisconnect() { + config.emitter.emit('disconnect') + }, + })) +} diff --git a/packages/connectors/src/version.ts b/packages/connectors/src/version.ts new file mode 100644 index 0000000000..11f81d1c34 --- /dev/null +++ b/packages/connectors/src/version.ts @@ -0,0 +1 @@ +export const version = '5.8.3' diff --git a/packages/connectors/src/walletConnect.test.ts b/packages/connectors/src/walletConnect.test.ts new file mode 100644 index 0000000000..4e8a74ebfe --- /dev/null +++ b/packages/connectors/src/walletConnect.test.ts @@ -0,0 +1,67 @@ +import { config, walletConnectProjectId } from '@wagmi/test' +import { http, HttpResponse } from 'msw' +import { setupServer } from 'msw/node' +import { + afterAll, + afterEach, + beforeAll, + expect, + expectTypeOf, + test, + vi, +} from 'vitest' + +import { walletConnect } from './walletConnect.js' + +const handlers = [ + http.get('https://relay.walletconnect.com', async () => + HttpResponse.json( + { + topic: '222781e3-3fad-4184-acde-077796bf0d3d', + type: 'sub', + payload: '', + silent: true, + }, + { status: 200 }, + ), + ), +] + +const server = setupServer(...handlers) + +beforeAll(() => { + server.listen({ + onUnhandledRequest: 'warn', + }) + + const matchMedia = vi.fn().mockImplementation((query) => { + return { + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), // deprecated + removeListener: vi.fn(), // deprecated + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + } + }) + vi.stubGlobal('matchMedia', matchMedia) +}) + +afterEach(() => server.resetHandlers()) + +afterAll(() => server.close()) + +test('setup', () => { + const connectorFn = walletConnect({ projectId: walletConnectProjectId }) + const connector = config._internal.connectors.setup(connectorFn) + expect(connector.name).toEqual('WalletConnect') + + type ConnectFnParameters = NonNullable< + Parameters<(typeof connector)['connect']>[0] + > + expectTypeOf().toMatchTypeOf< + string | undefined + >() +}) diff --git a/packages/connectors/src/walletConnect.ts b/packages/connectors/src/walletConnect.ts new file mode 100644 index 0000000000..fc4f794c1f --- /dev/null +++ b/packages/connectors/src/walletConnect.ts @@ -0,0 +1,468 @@ +import { + ChainNotConfiguredError, + type Connector, + ProviderNotFoundError, + createConnector, + extractRpcUrls, +} from '@wagmi/core' +import type { Compute, ExactPartial, Omit } from '@wagmi/core/internal' +import type { EthereumProvider } from '@walletconnect/ethereum-provider' +import { + type AddEthereumChainParameter, + type Address, + type ProviderConnectInfo, + type ProviderRpcError, + type RpcError, + SwitchChainError, + UserRejectedRequestError, + getAddress, + numberToHex, +} from 'viem' + +type WalletConnectConnector = Connector & { + onDisplayUri(uri: string): void + onSessionDelete(data: { topic: string }): void +} + +type EthereumProviderOptions = Parameters<(typeof EthereumProvider)['init']>[0] + +export type WalletConnectParameters = Compute< + { + /** + * If a new chain is added to a previously existing configured connector `chains`, this flag + * will determine if that chain should be considered as stale. A stale chain is a chain that + * WalletConnect has yet to establish a relationship with (e.g. the user has not approved or + * rejected the chain). + * + * This flag mainly affects the behavior when a wallet does not support dynamic chain authorization + * with WalletConnect v2. + * + * If `true` (default), the new chain will be treated as a stale chain. If the user + * has yet to establish a relationship (approved/rejected) with this chain in their WalletConnect + * session, the connector will disconnect upon the dapp auto-connecting, and the user will have to + * reconnect to the dapp (revalidate the chain) in order to approve the newly added chain. + * This is the default behavior to avoid an unexpected error upon switching chains which may + * be a confusing user experience (e.g. the user will not know they have to reconnect + * unless the dapp handles these types of errors). + * + * If `false`, the new chain will be treated as a potentially valid chain. This means that if the user + * has yet to establish a relationship with the chain in their WalletConnect session, wagmi will successfully + * auto-connect the user. This comes with the trade-off that the connector will throw an error + * when attempting to switch to the unapproved chain if the wallet does not support dynamic session updates. + * This may be useful in cases where a dapp constantly + * modifies their configured chains, and they do not want to disconnect the user upon + * auto-connecting. If the user decides to switch to the unapproved chain, it is important that the + * dapp handles this error and prompts the user to reconnect to the dapp in order to approve + * the newly added chain. + * + * @default true + */ + isNewChainsStale?: boolean + } & Omit< + EthereumProviderOptions, + | 'chains' + | 'events' + | 'optionalChains' + | 'optionalEvents' + | 'optionalMethods' + | 'methods' + | 'rpcMap' + | 'showQrModal' + > & + ExactPartial> +> + +walletConnect.type = 'walletConnect' as const +export function walletConnect(parameters: WalletConnectParameters) { + const isNewChainsStale = parameters.isNewChainsStale ?? true + + type Provider = Awaited> + type Properties = { + connect(parameters?: { + chainId?: number | undefined + isReconnecting?: boolean | undefined + pairingTopic?: string | undefined + }): Promise<{ + accounts: readonly Address[] + chainId: number + }> + getNamespaceChainsIds(): number[] + getRequestedChainsIds(): Promise + isChainsStale(): Promise + onConnect(connectInfo: ProviderConnectInfo): void + onDisplayUri(uri: string): void + onSessionDelete(data: { topic: string }): void + setRequestedChainsIds(chains: number[]): void + requestedChainsStorageKey: `${string}.requestedChains` + } + type StorageItem = { + [_ in Properties['requestedChainsStorageKey']]: number[] + } + + let provider_: Provider | undefined + let providerPromise: Promise + const NAMESPACE = 'eip155' + + let accountsChanged: WalletConnectConnector['onAccountsChanged'] | undefined + let chainChanged: WalletConnectConnector['onChainChanged'] | undefined + let connect: WalletConnectConnector['onConnect'] | undefined + let displayUri: WalletConnectConnector['onDisplayUri'] | undefined + let sessionDelete: WalletConnectConnector['onSessionDelete'] | undefined + let disconnect: WalletConnectConnector['onDisconnect'] | undefined + + return createConnector((config) => ({ + id: 'walletConnect', + name: 'WalletConnect', + type: walletConnect.type, + async setup() { + const provider = await this.getProvider().catch(() => null) + if (!provider) return + if (!connect) { + connect = this.onConnect.bind(this) + provider.on('connect', connect) + } + if (!sessionDelete) { + sessionDelete = this.onSessionDelete.bind(this) + provider.on('session_delete', sessionDelete) + } + }, + async connect({ chainId, ...rest } = {}) { + try { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + if (!displayUri) { + displayUri = this.onDisplayUri + provider.on('display_uri', displayUri) + } + + let targetChainId = chainId + if (!targetChainId) { + const state = (await config.storage?.getItem('state')) ?? {} + const isChainSupported = config.chains.some( + (x) => x.id === state.chainId, + ) + if (isChainSupported) targetChainId = state.chainId + else targetChainId = config.chains[0]?.id + } + if (!targetChainId) throw new Error('No chains found on connector.') + + const isChainsStale = await this.isChainsStale() + // If there is an active session with stale chains, disconnect current session. + if (provider.session && isChainsStale) await provider.disconnect() + + // If there isn't an active session or chains are stale, connect. + if (!provider.session || isChainsStale) { + const optionalChains = config.chains + .filter((chain) => chain.id !== targetChainId) + .map((optionalChain) => optionalChain.id) + await provider.connect({ + optionalChains: [targetChainId, ...optionalChains], + ...('pairingTopic' in rest + ? { pairingTopic: rest.pairingTopic } + : {}), + }) + + this.setRequestedChainsIds(config.chains.map((x) => x.id)) + } + + // If session exists and chains are authorized, enable provider for required chain + const accounts = (await provider.enable()).map((x) => getAddress(x)) + const currentChainId = await this.getChainId() + + if (displayUri) { + provider.removeListener('display_uri', displayUri) + displayUri = undefined + } + if (connect) { + provider.removeListener('connect', connect) + connect = undefined + } + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged) + } + if (!chainChanged) { + chainChanged = this.onChainChanged.bind(this) + provider.on('chainChanged', chainChanged) + } + if (!disconnect) { + disconnect = this.onDisconnect.bind(this) + provider.on('disconnect', disconnect) + } + if (!sessionDelete) { + sessionDelete = this.onSessionDelete.bind(this) + provider.on('session_delete', sessionDelete) + } + + return { accounts, chainId: currentChainId } + } catch (error) { + if ( + /(user rejected|connection request reset)/i.test( + (error as ProviderRpcError)?.message, + ) + ) { + throw new UserRejectedRequestError(error as Error) + } + throw error + } + }, + async disconnect() { + const provider = await this.getProvider() + try { + await provider?.disconnect() + } catch (error) { + if (!/No matching key/i.test((error as Error).message)) throw error + } finally { + if (chainChanged) { + provider?.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider?.removeListener('disconnect', disconnect) + disconnect = undefined + } + if (!connect) { + connect = this.onConnect.bind(this) + provider?.on('connect', connect) + } + if (accountsChanged) { + provider?.removeListener('accountsChanged', accountsChanged) + accountsChanged = undefined + } + if (sessionDelete) { + provider?.removeListener('session_delete', sessionDelete) + sessionDelete = undefined + } + + this.setRequestedChainsIds([]) + } + }, + async getAccounts() { + const provider = await this.getProvider() + return provider.accounts.map((x) => getAddress(x)) + }, + async getProvider({ chainId } = {}) { + async function initProvider() { + const optionalChains = config.chains.map((x) => x.id) as [number] + if (!optionalChains.length) return + const { EthereumProvider } = await import( + '@walletconnect/ethereum-provider' + ) + return await EthereumProvider.init({ + ...parameters, + disableProviderPing: true, + optionalChains, + projectId: parameters.projectId, + rpcMap: Object.fromEntries( + config.chains.map((chain) => { + const [url] = extractRpcUrls({ + chain, + transports: config.transports, + }) + return [chain.id, url] + }), + ), + showQrModal: parameters.showQrModal ?? true, + }) + } + + if (!provider_) { + if (!providerPromise) providerPromise = initProvider() + provider_ = await providerPromise + provider_?.events.setMaxListeners(Number.POSITIVE_INFINITY) + } + if (chainId) await this.switchChain?.({ chainId }) + return provider_! + }, + async getChainId() { + const provider = await this.getProvider() + return provider.chainId + }, + async isAuthorized() { + try { + const [accounts, provider] = await Promise.all([ + this.getAccounts(), + this.getProvider(), + ]) + + // If an account does not exist on the session, then the connector is unauthorized. + if (!accounts.length) return false + + // If the chains are stale on the session, then the connector is unauthorized. + const isChainsStale = await this.isChainsStale() + if (isChainsStale && provider.session) { + await provider.disconnect().catch(() => {}) + return false + } + return true + } catch { + return false + } + }, + async switchChain({ addEthereumChainParameter, chainId }) { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + + const chain = config.chains.find((x) => x.id === chainId) + if (!chain) throw new SwitchChainError(new ChainNotConfiguredError()) + + try { + await Promise.all([ + new Promise((resolve) => { + const listener = ({ + chainId: currentChainId, + }: { chainId?: number | undefined }) => { + if (currentChainId === chainId) { + config.emitter.off('change', listener) + resolve() + } + } + config.emitter.on('change', listener) + }), + provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: numberToHex(chainId) }], + }), + ]) + + const requestedChains = await this.getRequestedChainsIds() + this.setRequestedChainsIds([...requestedChains, chainId]) + + return chain + } catch (err) { + const error = err as RpcError + + if (/(user rejected)/i.test(error.message)) + throw new UserRejectedRequestError(error) + + // Indicates chain is not added to provider + try { + let blockExplorerUrls: string[] | undefined + if (addEthereumChainParameter?.blockExplorerUrls) + blockExplorerUrls = addEthereumChainParameter.blockExplorerUrls + else + blockExplorerUrls = chain.blockExplorers?.default.url + ? [chain.blockExplorers?.default.url] + : [] + + let rpcUrls: readonly string[] + if (addEthereumChainParameter?.rpcUrls?.length) + rpcUrls = addEthereumChainParameter.rpcUrls + else rpcUrls = [...chain.rpcUrls.default.http] + + const addEthereumChain = { + blockExplorerUrls, + chainId: numberToHex(chainId), + chainName: addEthereumChainParameter?.chainName ?? chain.name, + iconUrls: addEthereumChainParameter?.iconUrls, + nativeCurrency: + addEthereumChainParameter?.nativeCurrency ?? chain.nativeCurrency, + rpcUrls, + } satisfies AddEthereumChainParameter + + await provider.request({ + method: 'wallet_addEthereumChain', + params: [addEthereumChain], + }) + + const requestedChains = await this.getRequestedChainsIds() + this.setRequestedChainsIds([...requestedChains, chainId]) + return chain + } catch (error) { + throw new UserRejectedRequestError(error as Error) + } + } + }, + onAccountsChanged(accounts) { + if (accounts.length === 0) this.onDisconnect() + else + config.emitter.emit('change', { + accounts: accounts.map((x) => getAddress(x)), + }) + }, + onChainChanged(chain) { + const chainId = Number(chain) + config.emitter.emit('change', { chainId }) + }, + async onConnect(connectInfo) { + const chainId = Number(connectInfo.chainId) + const accounts = await this.getAccounts() + config.emitter.emit('connect', { accounts, chainId }) + }, + async onDisconnect(_error) { + this.setRequestedChainsIds([]) + config.emitter.emit('disconnect') + + const provider = await this.getProvider() + if (accountsChanged) { + provider.removeListener('accountsChanged', accountsChanged) + accountsChanged = undefined + } + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + if (sessionDelete) { + provider.removeListener('session_delete', sessionDelete) + sessionDelete = undefined + } + if (!connect) { + connect = this.onConnect.bind(this) + provider.on('connect', connect) + } + }, + onDisplayUri(uri) { + config.emitter.emit('message', { type: 'display_uri', data: uri }) + }, + onSessionDelete() { + this.onDisconnect() + }, + getNamespaceChainsIds() { + if (!provider_) return [] + const chainIds = provider_.session?.namespaces[NAMESPACE]?.accounts?.map( + (account) => Number.parseInt(account.split(':')[1] || ''), + ) + return chainIds ?? [] + }, + async getRequestedChainsIds() { + return ( + (await config.storage?.getItem(this.requestedChainsStorageKey)) ?? [] + ) + }, + /** + * Checks if the target chains match the chains that were + * initially requested by the connector for the WalletConnect session. + * If there is a mismatch, this means that the chains on the connector + * are considered stale, and need to be revalidated at a later point (via + * connection). + * + * There may be a scenario where a dapp adds a chain to the + * connector later on, however, this chain will not have been approved or rejected + * by the wallet. In this case, the chain is considered stale. + */ + async isChainsStale() { + if (!isNewChainsStale) return false + + const connectorChains = config.chains.map((x) => x.id) + const namespaceChains = this.getNamespaceChainsIds() + if ( + namespaceChains.length && + !namespaceChains.some((id) => connectorChains.includes(id)) + ) + return false + + const requestedChains = await this.getRequestedChainsIds() + return !connectorChains.every((id) => requestedChains.includes(id)) + }, + async setRequestedChainsIds(chains) { + await config.storage?.setItem(this.requestedChainsStorageKey, chains) + }, + get requestedChainsStorageKey() { + return `${this.id}.requestedChains` as Properties['requestedChainsStorageKey'] + }, + })) +} diff --git a/packages/connectors/tsconfig.build.json b/packages/connectors/tsconfig.build.json new file mode 100644 index 0000000000..fbed2b1036 --- /dev/null +++ b/packages/connectors/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "src/**/*.test-d.ts"], + "compilerOptions": { + "sourceMap": true + } +} diff --git a/packages/connectors/tsconfig.json b/packages/connectors/tsconfig.json new file mode 100644 index 0000000000..bd33919ac3 --- /dev/null +++ b/packages/connectors/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.build.json", + "include": ["src/**/*.ts"], + "exclude": [] +} diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 6d7d0e8357..88b541e93e 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,1017 +1,3365 @@ -# @0xsequence/core +# @wagmi/core -## 2.0.0 +## 2.17.2 -### Major Changes +### Patch Changes + +- [`29297a48af72b537173d948ccd2fe37d39914c66`](https://github.com/wevm/wagmi/commit/29297a48af72b537173d948ccd2fe37d39914c66) Thanks [@jxom](https://github.com/jxom)! - Fixed `sendCalls` generics. + +- [`07370106d5fb6b8fe300992d93abf25b3d0eaf57`](https://github.com/wevm/wagmi/commit/07370106d5fb6b8fe300992d93abf25b3d0eaf57) Thanks [@jxom](https://github.com/jxom)! - Fixed propagation of `waitForCallsStatus` parameters. + +## 2.17.1 + +### Patch Changes + +- [#4649](https://github.com/wevm/wagmi/pull/4649) [`01f64e64fa4f85cdd30023903f972f4f9023681f`](https://github.com/wevm/wagmi/commit/01f64e64fa4f85cdd30023903f972f4f9023681f) Thanks [@jxom](https://github.com/jxom)! - Added `chainId` parameter to `getCapabilities`/`useCapabilities`. + +## 2.17.0 + +### Minor Changes + +- [#4638](https://github.com/wevm/wagmi/pull/4638) [`799ee4d4b23c2ecd64e3f3668e67634e81939719`](https://github.com/wevm/wagmi/commit/799ee4d4b23c2ecd64e3f3668e67634e81939719) Thanks [@jxom](https://github.com/jxom)! - Stabilized EIP-5792 Actions & Hooks. + +## 2.16.7 + +### Patch Changes + +- [`a4bd0623eed28e3761a27295831a60ad835f0ee0`](https://github.com/wevm/wagmi/commit/a4bd0623eed28e3761a27295831a60ad835f0ee0) Thanks [@jxom](https://github.com/jxom)! - **Experimental (EIP-5792):** Updated `id` parameter to be optional on `useWaitForCallsStatus`. -- changeset +## 2.16.6 ### Patch Changes -- Updated dependencies - - @0xsequence/abi@2.0.0 +- [#4586](https://github.com/wevm/wagmi/pull/4586) [`edf47477b2f6385a1c3ae01d36a8498c47f30a0b`](https://github.com/wevm/wagmi/commit/edf47477b2f6385a1c3ae01d36a8498c47f30a0b) Thanks [@jxom](https://github.com/jxom)! - **Experimental (EIP-5792):** Added `waitForCallsStatus` + `useWaitForCallsStatus`. -## 1.10.14 +## 2.16.5 ### Patch Changes -- network: add borne-testnet to allNetworks -- Updated dependencies - - @0xsequence/abi@1.10.14 +- [`d0c9a86921a4e939373cc6e763284e53f2a2e93c`](https://github.com/wevm/wagmi/commit/d0c9a86921a4e939373cc6e763284e53f2a2e93c) Thanks [@jxom](https://github.com/jxom)! - **Experimental (ERC-5792)**: Added support for `account: null` in `sendCalls` to cater for sending calls without a connected account (account will be filled by the wallet). -## 1.10.13 +## 2.16.4 ### Patch Changes -- network: add borne testnet -- Updated dependencies - - @0xsequence/abi@1.10.13 +- [`507f864d91238bfd423d0e36d3619eb9f6e52eec`](https://github.com/wevm/wagmi/commit/507f864d91238bfd423d0e36d3619eb9f6e52eec) Thanks [@jxom](https://github.com/jxom)! - Updated `@coinbase/wallet-sdk`. -## 1.10.12 +## 2.16.3 ### Patch Changes -- api: update bindings -- global/window -> globalThis -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.10.12 +- [#4480](https://github.com/wevm/wagmi/pull/4480) [`384a1d91597622eb59e1c05dc13ce25017c5b6d8`](https://github.com/wevm/wagmi/commit/384a1d91597622eb59e1c05dc13ce25017c5b6d8) Thanks [@RodeRickIsWatching](https://github.com/RodeRickIsWatching)! - Fixed invocation of default storage. -## 1.10.11 +## 2.16.2 ### Patch Changes -- waas: updated intent.gen without webrpc types, errors exported from authenticator.gen -- Updated dependencies - - @0xsequence/abi@1.10.11 +- [`012907032b532a438fce48f407470250cbc8f0c6`](https://github.com/wevm/wagmi/commit/012907032b532a438fce48f407470250cbc8f0c6) Thanks [@jxom](https://github.com/jxom)! - Fixed assignment in `getDefaultStorage`. -## 1.10.10 +## 2.16.1 ### Patch Changes -- metadata: update bindings with new contract collections api -- Updated dependencies - - @0xsequence/abi@1.10.10 +- [#4472](https://github.com/wevm/wagmi/pull/4472) [`3892ebd21c06beef4b28ece4e70d2a38807bce6f`](https://github.com/wevm/wagmi/commit/3892ebd21c06beef4b28ece4e70d2a38807bce6f) Thanks [@tmm](https://github.com/tmm)! - Added handling to default storage for `setItem` errors, like `QuotaExceededError`, `SecurityError`, etc. -## 1.10.9 +## 2.16.0 + +### Minor Changes + +- [#4453](https://github.com/wevm/wagmi/pull/4453) [`070e48480194c8d7f45bda1d7dd1346e6f5d7227`](https://github.com/wevm/wagmi/commit/070e48480194c8d7f45bda1d7dd1346e6f5d7227) Thanks [@tmm](https://github.com/tmm)! - Added narrowing to `config.connectors`. ### Patch Changes -- waas minor update -- Updated dependencies - - @0xsequence/abi@1.10.9 +- [`afea6b67822a7a2b96901ec851441d27ee0f7a52`](https://github.com/wevm/wagmi/commit/afea6b67822a7a2b96901ec851441d27ee0f7a52) Thanks [@tmm](https://github.com/tmm)! - Passed through parameters to `connector.connect` in `connect` action. -## 1.10.8 +## 2.15.2 ### Patch Changes -- update metadata bindings -- Updated dependencies - - @0xsequence/abi@1.10.8 +- [#4433](https://github.com/wevm/wagmi/pull/4433) [`06e186cd679b27fe195309110e766fcf46d4efbc`](https://github.com/wevm/wagmi/commit/06e186cd679b27fe195309110e766fcf46d4efbc) Thanks [@Aerilym](https://github.com/Aerilym)! - Bumped Metamask SDK version to `0.31.1`. -## 1.10.7 +## 2.15.1 ### Patch Changes -- minor fixes to waas client -- Updated dependencies - - @0xsequence/abi@1.10.7 +- [`b8bbb409f4934538e3dd6cac5aaf7346292d0693`](https://github.com/wevm/wagmi/commit/b8bbb409f4934538e3dd6cac5aaf7346292d0693) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where `null` gas would accidentally pass through. + +## 2.15.0 -## 1.10.6 +### Minor Changes + +- [#4417](https://github.com/wevm/wagmi/pull/4417) [`42e65ea4fea99c639817088bba915e0933d17141`](https://github.com/wevm/wagmi/commit/42e65ea4fea99c639817088bba915e0933d17141) Thanks [@jxom](https://github.com/jxom)! - Removed simulation in `writeContract` & `sendTransaction`. + +## 2.14.6 ### Patch Changes -- metadata: update bindings -- Updated dependencies - - @0xsequence/abi@1.10.6 +- [#4406](https://github.com/wevm/wagmi/pull/4406) [`a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3`](https://github.com/wevm/wagmi/commit/a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3) Thanks [@tmm](https://github.com/tmm)! - Added support for multiple connector `rdns` entries. -## 1.10.5 +## 2.14.5 ### Patch Changes -- network: ape-chain-testnet -> apechain-testnet -- Updated dependencies - - @0xsequence/abi@1.10.5 +- [#4400](https://github.com/wevm/wagmi/pull/4400) [`6b9bbacdc7bffd44fc2165362a5e65fd434e7646`](https://github.com/wevm/wagmi/commit/6b9bbacdc7bffd44fc2165362a5e65fd434e7646) Thanks [@AzzouQ](https://github.com/AzzouQ)! - Fixed `createWatchContractEvent` internal wiring, where `eventName` was incorrectly `functionName`. -## 1.10.4 +## 2.14.4 ### Patch Changes -- network: add b3-sepolia, ape-chain-testnet, blast, blast-sepolia -- Updated dependencies - - @0xsequence/abi@1.10.4 +- [#4311](https://github.com/wevm/wagmi/pull/4311) [`e08681c81fbdf475213e2d0f4c5517d0abf4e743`](https://github.com/wevm/wagmi/commit/e08681c81fbdf475213e2d0f4c5517d0abf4e743) Thanks [@chybisov](https://github.com/chybisov)! - Fixed `injected` connector race condition after calling `'wallet_addEthereumChain'` in `switchChain`. -## 1.10.3 +## 2.14.3 ### Patch Changes -- typing fix -- Updated dependencies - - @0xsequence/abi@1.10.3 +- [`cb7dd2ebb871d0be8f1a11a8cd8ce592cd74b7c7`](https://github.com/wevm/wagmi/commit/cb7dd2ebb871d0be8f1a11a8cd8ce592cd74b7c7) Thanks [@tmm](https://github.com/tmm)! - Removed unnecessary internal deep equal check in `structuralSharing`. -## 1.10.2 +## 2.14.2 ### Patch Changes -- - waas: add getIdToken method - - indexer: update api client -- Updated dependencies - - @0xsequence/abi@1.10.2 +- [#4339](https://github.com/wevm/wagmi/pull/4339) [`d0d0963bb5904a15cf0355862d62dd141ce0c31c`](https://github.com/wevm/wagmi/commit/d0d0963bb5904a15cf0355862d62dd141ce0c31c) Thanks [@AndriyAntonenko](https://github.com/AndriyAntonenko)! - Fixed bug in `waitForTransactionReceipt`, where transaction data wasn't passed to `'eth_call'` method as part of getting the revert reason. + +- [`ecac0ba36243d94c9199d0bd21937104c835d9a0`](https://github.com/wevm/wagmi/commit/ecac0ba36243d94c9199d0bd21937104c835d9a0) Thanks [@tmm](https://github.com/tmm)! - Fixed `getBalance` symbol error handling. -## 1.10.1 +## 2.14.1 ### Patch Changes -- metadata: update bindings -- Updated dependencies - - @0xsequence/abi@1.10.1 +- [`052e72e1f8c1c14fcbdce04a9f8fa7ec28d83702`](https://github.com/wevm/wagmi/commit/052e72e1f8c1c14fcbdce04a9f8fa7ec28d83702) Thanks [@tmm](https://github.com/tmm)! - Added `defaultConnected` feature to `mock` connector. -## 1.10.0 +- [#4349](https://github.com/wevm/wagmi/pull/4349) [`b250fc21ee577b2a75c5a34ff684f62fb4ad771a`](https://github.com/wevm/wagmi/commit/b250fc21ee577b2a75c5a34ff684f62fb4ad771a) Thanks [@tmm](https://github.com/tmm)! - Bumped internal deps. + +## 2.14.0 ### Minor Changes -- waas release v1.3.0 +- [#4343](https://github.com/wevm/wagmi/pull/4343) [`f43e074f473820b208a6295d7c97f847332f1a1d`](https://github.com/wevm/wagmi/commit/f43e074f473820b208a6295d7c97f847332f1a1d) Thanks [@tmm](https://github.com/tmm)! - Added `rdns` property to connector interface. This is used to filter out duplicate [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) injected providers when [`createConfig#multiInjectedProviderDiscovery`](https://wagmi.sh/core/api/createConfig#multiinjectedproviderdiscovery) is enabled and `createConfig#connectors` already matches EIP-6963 providers' `rdns` property. + +## 2.13.9 ### Patch Changes -- Updated dependencies - - @0xsequence/abi@1.10.0 +- [#4336](https://github.com/wevm/wagmi/pull/4336) [`c05caabc20c3ced9682cfc7ba1f3f7dcfece0703`](https://github.com/wevm/wagmi/commit/c05caabc20c3ced9682cfc7ba1f3f7dcfece0703) Thanks [@EdouardBougon](https://github.com/EdouardBougon)! - Added deprecation notice to `injected` target flags. -## 1.9.37 +## 2.13.8 ### Patch Changes -- network: adds nativeToken data to NetworkMetadata constants -- Updated dependencies - - @0xsequence/abi@1.9.37 +- [#4207](https://github.com/wevm/wagmi/pull/4207) [`56f2482508f2ba71bd6b0295c70c6abca7101e57`](https://github.com/wevm/wagmi/commit/56f2482508f2ba71bd6b0295c70c6abca7101e57) Thanks [@Smert](https://github.com/Smert)! - Updated chain switch listener for `injected` and `metaMask` to be more robust. -## 1.9.36 +## 2.13.7 ### Patch Changes -- guard: export client -- Updated dependencies - - @0xsequence/abi@1.9.36 +- [`be75c2d4ef636d7362420ab0a106bfdf63f5d1e6`](https://github.com/wevm/wagmi/commit/be75c2d4ef636d7362420ab0a106bfdf63f5d1e6) Thanks [@tmm](https://github.com/tmm)! - Added guard for missing `provider.on` for `injected` connector. -## 1.9.35 +## 2.13.6 ### Patch Changes -- guard: update bindings -- Updated dependencies - - @0xsequence/abi@1.9.35 +- [#4286](https://github.com/wevm/wagmi/pull/4286) [`edcbf5d6fbe92f639bead800502edda9e0aa39f1`](https://github.com/wevm/wagmi/commit/edcbf5d6fbe92f639bead800502edda9e0aa39f1) Thanks [@holic](https://github.com/holic)! - Removed duplicate code. -## 1.9.34 +## 2.13.5 ### Patch Changes -- waas: always use lowercase email -- Updated dependencies - - @0xsequence/abi@1.9.34 +- [#4259](https://github.com/wevm/wagmi/pull/4259) [`f47ce8f6d263e49fdff90b8edb3190142d2657bb`](https://github.com/wevm/wagmi/commit/f47ce8f6d263e49fdff90b8edb3190142d2657bb) Thanks [@tmm](https://github.com/tmm)! - Added guard to `getConnectorClient` when reconnecting to check if connector is fully restored. -## 1.9.33 +## 2.13.4 ### Patch Changes -- waas: umd build -- Updated dependencies - - @0xsequence/abi@1.9.33 +- [`b4c8971788c70b09479946ecfa998cff2f1b3953`](https://github.com/wevm/wagmi/commit/b4c8971788c70b09479946ecfa998cff2f1b3953) Thanks [@tmm](https://github.com/tmm)! - Made `serialize` and `deserialize` types more permissive. -## 1.9.32 +## 2.13.3 ### Patch Changes -- indexer: update bindings -- Updated dependencies - - @0xsequence/abi@1.9.32 +- [`871dbdbfe59ac8ad01d1ec6150ea7b091b7b7de4`](https://github.com/wevm/wagmi/commit/871dbdbfe59ac8ad01d1ec6150ea7b091b7b7de4) Thanks [@tmm](https://github.com/tmm)! - Added validation to internal state for persisted `chainId`. -## 1.9.31 +## 2.13.2 ### Patch Changes -- metadata: token directory changes -- Updated dependencies - - @0xsequence/abi@1.9.31 +- [`1b9b523fa9b9dfe839aecdf4b40caa9547d7e594`](https://github.com/wevm/wagmi/commit/1b9b523fa9b9dfe839aecdf4b40caa9547d7e594) Thanks [@tmm](https://github.com/tmm)! - Fixed built-in cookie storage `removeItem` working for all paths. -## 1.9.30 +## 2.13.1 ### Patch Changes -- update -- Updated dependencies - - @0xsequence/abi@1.9.30 +- [`07c1227f306d0efb9421d4bb77a774f92f5fcf45`](https://github.com/wevm/wagmi/commit/07c1227f306d0efb9421d4bb77a774f92f5fcf45) Thanks [@tmm](https://github.com/tmm)! - Fixed internal `extractRpcUrls` usage with `unstable_connector`. + +## 2.13.0 + +### Minor Changes + +- [#4162](https://github.com/wevm/wagmi/pull/4162) [`a73a7737b756886b388f120ae423e72cca53e8a0`](https://github.com/wevm/wagmi/commit/a73a7737b756886b388f120ae423e72cca53e8a0) Thanks [@jxom](https://github.com/jxom)! - Added functionality for consumer-defined RPC URLs (`config.transports`) to be propagated to the WalletConnect & MetaMask Connectors. -## 1.9.29 +## 2.12.2 ### Patch Changes -- disable gnosis chain -- Updated dependencies - - @0xsequence/abi@1.9.29 +- [`5bc8c8877810b2eec24a829df87dce40a51e6f20`](https://github.com/wevm/wagmi/commit/5bc8c8877810b2eec24a829df87dce40a51e6f20) Thanks [@tmm](https://github.com/tmm)! - Fixed reconnection when `status` is defined. -## 1.9.28 +## 2.12.1 ### Patch Changes -- add utils/merkletree -- Updated dependencies - - @0xsequence/abi@1.9.28 +- [#4146](https://github.com/wevm/wagmi/pull/4146) [`cc996e08e930c9e88cf753a1e874652059e81a3b`](https://github.com/wevm/wagmi/commit/cc996e08e930c9e88cf753a1e874652059e81a3b) Thanks [@jxom](https://github.com/jxom)! - Updated `@safe-global/safe-apps-sdk` + `@safe-global/safe-apps-provider` dependencies. + +## 2.12.0 + +### Minor Changes + +- [#4128](https://github.com/wevm/wagmi/pull/4128) [`5581a810ef70308e99c6f8b630cd4bca59f64afc`](https://github.com/wevm/wagmi/commit/5581a810ef70308e99c6f8b630cd4bca59f64afc) Thanks [@dalechyn](https://github.com/dalechyn)! - Added `watchAsset` action. -## 1.9.27 +## 2.11.8 ### Patch Changes -- network: optimistic -> optimism -- waas: remove defaults -- api, sessions: update bindings -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.9.27 +- [`b08013eaa9ce97c02f8a7128ea400e3da7ef74bb`](https://github.com/wevm/wagmi/commit/b08013eaa9ce97c02f8a7128ea400e3da7ef74bb) Thanks [@tmm](https://github.com/tmm)! - Fixed injected accounts ordering for `'wallet_requestPermissions'`. -## 1.9.26 +- [`d3814ab4b88f9f0e052b53bc3d458df87b43f01d`](https://github.com/wevm/wagmi/commit/d3814ab4b88f9f0e052b53bc3d458df87b43f01d) Thanks [@jxom](https://github.com/jxom)! - Updated `mipd` dependency. + +## 2.11.7 ### Patch Changes -- - add backend interfaces for pluggable interfaces - - introduce @0xsequence/react-native - - update pnpm to lockfile v9 -- Updated dependencies - - @0xsequence/abi@1.9.26 +- [`0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e`](https://github.com/wevm/wagmi/commit/0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e) Thanks [@tmm](https://github.com/tmm)! - Improved TypeScript `'exactOptionalPropertyTypes'` support. -## 1.9.25 +## 2.11.6 ### Patch Changes -- update webrpc clients with new error types -- Updated dependencies - - @0xsequence/abi@1.9.25 +- [#4060](https://github.com/wevm/wagmi/pull/4060) [`95965c1f19d480b97f2b297a077a9e607dee32ad`](https://github.com/wevm/wagmi/commit/95965c1f19d480b97f2b297a077a9e607dee32ad) Thanks [@dalechyn](https://github.com/dalechyn)! - Bumped Tanstack Query dependencies to fix typing issues between exported Wagmi query options and TanStack Query suspense query methods (due to [`direction` property in `QueryFunctionContext` being deprecated](https://github.com/TanStack/query/pull/7410)). -## 1.9.24 +## 2.11.5 ### Patch Changes -- waas: add memoryStore backend to localStore -- Updated dependencies - - @0xsequence/abi@1.9.24 +- [#4079](https://github.com/wevm/wagmi/pull/4079) [`04f2b846b113f3d300d82c9fa75212f1805817c5`](https://github.com/wevm/wagmi/commit/04f2b846b113f3d300d82c9fa75212f1805817c5) Thanks [@tmm](https://github.com/tmm)! - Added revalidation for config chain ID in SSR and migration. -## 1.9.23 +## 2.11.4 ### Patch Changes -- update api client bindings -- Updated dependencies - - @0xsequence/abi@1.9.23 +- [`9e8345cd56186b997b5e56deaa2cfc69b30d15f6`](https://github.com/wevm/wagmi/commit/9e8345cd56186b997b5e56deaa2cfc69b30d15f6) Thanks [@tmm](https://github.com/tmm)! - Switched `Register` to `interface` to fix declaration merging. -## 1.9.22 +## 2.11.3 ### Patch Changes -- update metadata client bindings -- Updated dependencies - - @0xsequence/abi@1.9.22 +- [#4065](https://github.com/wevm/wagmi/pull/4065) [`8974e6269bb5d7bfaa90db0246bc7d13e8bff798`](https://github.com/wevm/wagmi/commit/8974e6269bb5d7bfaa90db0246bc7d13e8bff798) Thanks [@alx-khramov](https://github.com/alx-khramov)! - Added timeout to internal call of `'wallet_revokePermissions'` request during `injected#disconnect` as some wallets that do not support this method hang. -## 1.9.21 +## 2.11.2 ### Patch Changes -- api client bindings -- Updated dependencies - - @0xsequence/abi@1.9.21 +- [#4042](https://github.com/wevm/wagmi/pull/4042) [`b4d9ef79deb554ee20fed6666a474be5e7cdd522`](https://github.com/wevm/wagmi/commit/b4d9ef79deb554ee20fed6666a474be5e7cdd522) Thanks [@tmm](https://github.com/tmm)! - Removed `injected` connector `isAuthorized` timeout. -## 1.9.20 +## 2.11.1 ### Patch Changes -- api client bindings update -- Updated dependencies - - @0xsequence/abi@1.9.20 +- [`9c862d8d63e3d692a22cef2a90782b74a9103f17`](https://github.com/wevm/wagmi/commit/9c862d8d63e3d692a22cef2a90782b74a9103f17) Thanks [@tmm](https://github.com/tmm)! - Reverted internal module loading utility. + +## 2.11.0 -## 1.9.19 +### Minor Changes + +- [#3816](https://github.com/wevm/wagmi/pull/3816) [`06bb598a7f04c7b167f5b7ff6d46bd15886a6a14`](https://github.com/wevm/wagmi/commit/06bb598a7f04c7b167f5b7ff6d46bd15886a6a14) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `deployContract` action. ### Patch Changes -- waas update -- Updated dependencies - - @0xsequence/abi@1.9.19 +- [`24a45b269bd0214a29d6f82a84ac66ef8c3f3822`](https://github.com/wevm/wagmi/commit/24a45b269bd0214a29d6f82a84ac66ef8c3f3822) Thanks [@tmm](https://github.com/tmm)! - Added `SameSite` default to `cookieStorage` -## 1.9.18 +## 2.10.6 ### Patch Changes -- provider: prohibit dangerous functions -- Updated dependencies - - @0xsequence/abi@1.9.18 +- [#4009](https://github.com/wevm/wagmi/pull/4009) [`f2a7cefab96691ebed8b8e45ffde071c47b58dbe`](https://github.com/wevm/wagmi/commit/f2a7cefab96691ebed8b8e45ffde071c47b58dbe) Thanks [@roninjin10](https://github.com/roninjin10)! - Marked `to` as optional for `sendTransaction`. -## 1.9.17 +- [#4023](https://github.com/wevm/wagmi/pull/4023) [`f0ea0b2a7fe193dadfeb49a4c8031ee451c638b5`](https://github.com/wevm/wagmi/commit/f0ea0b2a7fe193dadfeb49a4c8031ee451c638b5) Thanks [@jxom](https://github.com/jxom)! - Added chain check to `getConnectorClient` since it's possible for connection state chain ID to get out of sync with the connector (e.g. [wallet bug](https://github.com/MetaMask/metamask-extension/issues/25097)). + +## 2.10.5 ### Patch Changes -- network: add xr-sepolia -- Updated dependencies - - @0xsequence/abi@1.9.17 +- [#3970](https://github.com/wevm/wagmi/pull/3970) [`030c7c2cb380dfd67a2182f62e2aa7a6e1601898`](https://github.com/wevm/wagmi/commit/030c7c2cb380dfd67a2182f62e2aa7a6e1601898) Thanks [@nanxiaobei](https://github.com/nanxiaobei)! - Fixed `cookieStorage` not working across paths. -## 1.9.16 +## 2.10.4 ### Patch Changes -- waas: sequence.feeOptions -- Updated dependencies - - @0xsequence/abi@1.9.16 +- [#3984](https://github.com/wevm/wagmi/pull/3984) [`51fde8a0433b4fff357c1a8d7e08b41b4c86c968`](https://github.com/wevm/wagmi/commit/51fde8a0433b4fff357c1a8d7e08b41b4c86c968) Thanks [@tmm](https://github.com/tmm)! - Fixed `writeContract` query types for `value` property. -## 1.9.15 +## 2.10.3 ### Patch Changes -- metadata: collection external_link field name fix -- Updated dependencies - - @0xsequence/abi@1.9.15 +- [#3962](https://github.com/wevm/wagmi/pull/3962) [`2804a8a583b1874271154898b4bae38756ef581c`](https://github.com/wevm/wagmi/commit/2804a8a583b1874271154898b4bae38756ef581c) Thanks [@tmm](https://github.com/tmm)! - Added catch to `reconnect`. -## 1.9.14 +## 2.10.2 ### Patch Changes -- network: astar-zkatana -> astar-zkyoto -- network: deprecate polygon mumbai network -- network: add xai and polygon amoy -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.9.14 +- [#3940](https://github.com/wevm/wagmi/pull/3940) [`a5071f581dfdfb961718873643a2fc629101c72a`](https://github.com/wevm/wagmi/commit/a5071f581dfdfb961718873643a2fc629101c72a) Thanks [@jxom](https://github.com/jxom)! - Fixed usage of `metaMask` connector in Vite environments. -## 1.9.13 +## 2.10.1 ### Patch Changes -- waas: fix @0xsequence/network dependency -- Updated dependencies - - @0xsequence/abi@1.9.13 +- Bumped versions. + +## 2.10.0 -## 1.9.12 +### Minor Changes + +- [#3928](https://github.com/wevm/wagmi/pull/3928) [`3117e71825f9c58a0d718f3d1686f1a191fa9cb1`](https://github.com/wevm/wagmi/commit/3117e71825f9c58a0d718f3d1686f1a191fa9cb1) Thanks [@tmm](https://github.com/tmm)! - Updated the default Coinbase SDK in `coinbaseWallet` Connector to v4.x. + +## 2.9.8 ### Patch Changes -- indexer: update rpc bindings -- provider: signMessage: Serialize the BytesLike or string message into hexstring before sending -- waas: SessionAuthProof -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.9.12 +- [#3906](https://github.com/wevm/wagmi/pull/3906) [`32fcb4a31dde6b0206961d8ffe9c651f8a459c67`](https://github.com/wevm/wagmi/commit/32fcb4a31dde6b0206961d8ffe9c651f8a459c67) Thanks [@tmm](https://github.com/tmm)! - Added support for Vue. -## 1.9.11 +## 2.9.7 ### Patch Changes -- metdata, update rpc bindings -- Updated dependencies - - @0xsequence/abi@1.9.11 +- [#3924](https://github.com/wevm/wagmi/pull/3924) [`1f58734f88458e0f6adb05c99f0c90f36ab286b8`](https://github.com/wevm/wagmi/commit/1f58734f88458e0f6adb05c99f0c90f36ab286b8) Thanks [@jxom](https://github.com/jxom)! - Refactored `isChainsStale` logic in `walletConnect` connector. -## 1.9.10 +## 2.9.6 ### Patch Changes -- update metadata rpc bindings -- Updated dependencies - - @0xsequence/abi@1.9.10 +- [#3917](https://github.com/wevm/wagmi/pull/3917) [`05948fdad5bb4a56b08916d45b3dec2cb1e5f55b`](https://github.com/wevm/wagmi/commit/05948fdad5bb4a56b08916d45b3dec2cb1e5f55b) Thanks [@jxom](https://github.com/jxom)! - Updated `@metamask/sdk`. -## 1.9.9 +## 2.9.5 ### Patch Changes -- metadata, add SequenceCollections rpc client -- Updated dependencies - - @0xsequence/abi@1.9.9 +- [`4fecbbb66d0aacd03b8c62a6455d11a33cde8f85`](https://github.com/wevm/wagmi/commit/4fecbbb66d0aacd03b8c62a6455d11a33cde8f85) Thanks [@jxom](https://github.com/jxom)! - Fixed address comparison in `getConnectorClient`. -## 1.9.8 +## 2.9.4 ### Patch Changes -- waas client update -- Updated dependencies - - @0xsequence/abi@1.9.8 +- [#3910](https://github.com/wevm/wagmi/pull/3910) [`e6139a97c4b8804d734b1547b5e3921ce01fbe24`](https://github.com/wevm/wagmi/commit/e6139a97c4b8804d734b1547b5e3921ce01fbe24) Thanks [@tmm](https://github.com/tmm)! - Added experimental `wallet_revokePermissions` support to `injected`. -## 1.9.7 +## 2.9.3 ### Patch Changes -- update rpc client bindings for api, metadata and relayer -- Updated dependencies - - @0xsequence/abi@1.9.7 +- [#3904](https://github.com/wevm/wagmi/pull/3904) [`addca28ebc20f1a4367c35fe9ef786decff9c87e`](https://github.com/wevm/wagmi/commit/addca28ebc20f1a4367c35fe9ef786decff9c87e) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/ethereum-provider`. -## 1.9.6 +## 2.9.2 ### Patch Changes -- waas package update -- Updated dependencies - - @0xsequence/abi@1.9.6 +- [#3902](https://github.com/wevm/wagmi/pull/3902) [`204b7b624612405500ec098fb9e35facd3f74ca4`](https://github.com/wevm/wagmi/commit/204b7b624612405500ec098fb9e35facd3f74ca4) Thanks [@jxom](https://github.com/jxom)! - Made third-party SDK imports type-only. -## 1.9.5 +## 2.9.1 ### Patch Changes -- RpcRelayer prioritize project access key -- Updated dependencies - - @0xsequence/abi@1.9.5 +- [`cda6a5d5`](https://github.com/wevm/wagmi/commit/cda6a5d56328330fbde050b4ef40b01c58d2519a) Thanks [@jxom](https://github.com/jxom)! - Updated packages. + +## 2.9.0 -## 1.9.4 +### Minor Changes + +- [#3878](https://github.com/wevm/wagmi/pull/3878) [`017828fc`](https://github.com/wevm/wagmi/commit/017828fc027c7a84b54ea9d627e9389f4d60d6c2) Thanks [@jxom](https://github.com/jxom)! - Added experimental EIP-5792 Actions & Hooks. + +## 2.8.1 ### Patch Changes -- waas: fix network dependency -- Updated dependencies - - @0xsequence/abi@1.9.4 +- [#3869](https://github.com/wevm/wagmi/pull/3869) [`d4a78eb0`](https://github.com/wevm/wagmi/commit/d4a78eb07119d2e5617e52481ac7d6c6d1583ddc) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where `prepareTransactionRequest` would internally call unsupported wallet RPC methods. + +## 2.8.0 -## 1.9.3 +### Minor Changes + +- [#3868](https://github.com/wevm/wagmi/pull/3868) [`c2af20b8`](https://github.com/wevm/wagmi/commit/c2af20b88cf16970d087faaec10b463357a5836e) Thanks [@jxom](https://github.com/jxom)! - Added `supportsSimulation` property to connectors that indicates if the connector's wallet supports contract simulation. ### Patch Changes -- provider: don't append access key to RPC url if user has already provided it -- Updated dependencies - - @0xsequence/abi@1.9.3 +- [#3858](https://github.com/wevm/wagmi/pull/3858) [`0d141f17`](https://github.com/wevm/wagmi/commit/0d141f171d6ec44bcbfc9c876565b5e2fb8af6de) Thanks [@yulafezmesi](https://github.com/yulafezmesi)! - Fixed accessing reverted reason property inside `waitForTransactionReceipt`. + +## 2.7.0 + +### Minor Changes -## 1.9.2 +- [#3857](https://github.com/wevm/wagmi/pull/3857) [`d4274c03`](https://github.com/wevm/wagmi/commit/d4274c03a6af5f2d26d31432016ebc14950a330e) Thanks [@tmm](https://github.com/tmm)! - Added `addEthereumChainParameter` to `switchChain`-related methods. ### Patch Changes -- network: add xai-sepolia -- Updated dependencies - - @0xsequence/abi@1.9.2 +- [#3849](https://github.com/wevm/wagmi/pull/3849) [`4781a405`](https://github.com/wevm/wagmi/commit/4781a4056d4ffc2c74f96a75429e9b2cd2417ad8) Thanks [@tmm](https://github.com/tmm)! - Fixed `shimDisconnect: false` behavior. + +- [#3859](https://github.com/wevm/wagmi/pull/3859) [`400c960b`](https://github.com/wevm/wagmi/commit/400c960b30d701c134850c695ae903a382c29b5b) Thanks [@holic](https://github.com/holic)! - Added workaround to injected connector for MetaMask bug, where chain switching does not work if target chain RPC `'net_version'` request fails. -## 1.9.1 +## 2.6.19 ### Patch Changes -- analytics fix -- Updated dependencies - - @0xsequence/abi@1.9.1 +- [`e3c832a1`](https://github.com/wevm/wagmi/commit/e3c832a12c301f9b0ee129d877b3101d220ba8b2) Thanks [@jxom](https://github.com/jxom)! - Fixed undefined `navigator` issue in MetaMask connector. -## 1.9.0 +## 2.6.18 -### Minor Changes +### Patch Changes -- waas release +- [#3848](https://github.com/wevm/wagmi/pull/3848) [`dd40a41c`](https://github.com/wevm/wagmi/commit/dd40a41c526ab60a288aff2250ed8dba92a27b16) Thanks [@jxom](https://github.com/jxom)! - Updated MetaMask SDK. + +## 2.6.17 ### Patch Changes -- Updated dependencies - - @0xsequence/abi@1.9.0 +- [#3822](https://github.com/wevm/wagmi/pull/3822) [`a97bfbae`](https://github.com/wevm/wagmi/commit/a97bfbaeb615cfef04665e5e7348d85d17f960f0) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where Wagmi would not correctly rehydrate the active chain when a persisted store was being used. -## 1.8.8 +## 2.6.16 ### Patch Changes -- update metadata bindings -- Updated dependencies - - @0xsequence/abi@1.8.8 +- [#3788](https://github.com/wevm/wagmi/pull/3788) [`42ad380d`](https://github.com/wevm/wagmi/commit/42ad380d9a5d8bc0f61d73612142dea9d098de5e) Thanks [@tmm](https://github.com/tmm)! - Refactored connectors to remove unnecessarily event listeners. -## 1.8.7 +## 2.6.15 ### Patch Changes -- provider: update databeat to 0.9.1 -- Updated dependencies - - @0xsequence/abi@1.8.7 +- [#3782](https://github.com/wevm/wagmi/pull/3782) [`b907d5ac`](https://github.com/wevm/wagmi/commit/b907d5ac3a746bcbccc06d1fe78c5bd8f9a7d685) Thanks [@jxom](https://github.com/jxom)! - Refactored injected connector connection logic. -## 1.8.6 +## 2.6.14 ### Patch Changes -- guard: SignedOwnershipProof -- Updated dependencies - - @0xsequence/abi@1.8.6 +- [#3777](https://github.com/wevm/wagmi/pull/3777) [`b3b54ef1`](https://github.com/wevm/wagmi/commit/b3b54ef179c5fa0d1694d38d4b808549a0550409) Thanks [@desfero](https://github.com/desfero)! - Fixed `writeContract` to forward `chainIn` when simulating contract call + +- [#3779](https://github.com/wevm/wagmi/pull/3779) [`3da20bb8`](https://github.com/wevm/wagmi/commit/3da20bb80e7c3efeef8227ced66ad615370fc242) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `eth_requestAccounts` would be called upon reconnect instead of `eth_accounts`. -## 1.8.5 +- [`a3d1858f`](https://github.com/wevm/wagmi/commit/a3d1858fce448d2b70e36ee692ef1589b74e9d3f) Thanks [@jxom](https://github.com/jxom)! - Fixed hydration conditional in `createConfig`. + +## 2.6.13 ### Patch Changes -- guard: signOwnershipProof and isSignedOwnershipProof -- Updated dependencies - - @0xsequence/abi@1.8.5 +- [`b80236dc`](https://github.com/wevm/wagmi/commit/b80236dc623095fe8f1e1d10957d7776fb6ab48b) Thanks [@jxom](https://github.com/jxom)! - Removed unneeded `uniqueBy` check on connectors state. -## 1.8.4 +## 2.6.12 ### Patch Changes -- network: add homeverse to networks list -- Updated dependencies - - @0xsequence/abi@1.8.4 +- [#3763](https://github.com/wevm/wagmi/pull/3763) [`a59069e9`](https://github.com/wevm/wagmi/commit/a59069e9fab45dd606bb89a7f829fe94c51a5494) Thanks [@tmm](https://github.com/tmm)! - Fixed `getConnectorClient` internal address comparison. + +- [#3608](https://github.com/wevm/wagmi/pull/3608) [`0acd3132`](https://github.com/wevm/wagmi/commit/0acd31320f534993af566be5490c2978b6184f66) Thanks [@mqklin](https://github.com/mqklin)! - Disabled `wallet_requestPermissions` prompt when `shimDisconnect` is `false`. -## 1.8.3 +## 2.6.11 ### Patch Changes -- api: introduce basic linked wallet support -- Updated dependencies - - @0xsequence/abi@1.8.3 +- [`e1ca4e63`](https://github.com/wevm/wagmi/commit/e1ca4e637ae6cec7f5902b0a2c0e0efc3b751a1d) Thanks [@tmm](https://github.com/tmm)! - Deprecated `normalizeChainId`. Use `Number` instead. -## 1.8.2 +## 2.6.10 ### Patch Changes -- provider: don't initialize analytics unless explicitly requested -- Updated dependencies - - @0xsequence/abi@1.8.2 +- [`dbdca8fd`](https://github.com/wevm/wagmi/commit/dbdca8fd14b90c166222a66a373c1b33c06ce019) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where duplicate connectors could be instantiated if injected after page mount. -## 1.8.1 +## 2.6.9 ### Patch Changes -- update to analytics provider -- Updated dependencies - - @0xsequence/abi@1.8.1 +- [#3715](https://github.com/wevm/wagmi/pull/3715) [`d56edf4f`](https://github.com/wevm/wagmi/commit/d56edf4f27c52acc7a0f57114454b0d3e22cacd6) Thanks [@jxom](https://github.com/jxom)! - Fixed SSR hydration issues. -## 1.8.0 +## 2.6.8 -### Minor Changes +### Patch Changes + +- [#3643](https://github.com/wevm/wagmi/pull/3643) [`e46bcd47`](https://github.com/wevm/wagmi/commit/e46bcd4738a18da15b53f6612b614379c1985374) Thanks [@TateB](https://github.com/TateB)! - Fixed race condition arising from `reconnect`. + +## 2.6.7 -- provider: project analytics +### Patch Changes + +- [#3642](https://github.com/wevm/wagmi/pull/3642) [`b479b5e8`](https://github.com/wevm/wagmi/commit/b479b5e8a5866cba792862f22e6352c4fb566137) Thanks [@johanneskares](https://github.com/johanneskares)! - Fixed a bug where minification caused the wrong functions to be called on the client. + +- [`f5648dd2`](https://github.com/wevm/wagmi/commit/f5648dd28b3576b628f57732b89287f55acbb1c1) Thanks [@jxom](https://github.com/jxom)! - Updated `prepareTransactionRequest` types for `viem@2.8.0`. + +- [`1c1fee6a`](https://github.com/wevm/wagmi/commit/1c1fee6ab8f01f7734ac6ce05093fa8e388beb3e) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/ethereum-provider`. + +- [#3653](https://github.com/wevm/wagmi/pull/3653) [`88a2d744`](https://github.com/wevm/wagmi/commit/88a2d744a1315908c9e54156026df3ad2435ad44) Thanks [@tash-2s](https://github.com/tash-2s)! - Fixed error occurring when adding chains without explorers to MetaMask. + +## 2.6.6 ### Patch Changes -- Updated dependencies - - @0xsequence/abi@1.8.0 +- [#3644](https://github.com/wevm/wagmi/pull/3644) [`a91c0b64`](https://github.com/wevm/wagmi/commit/a91c0b64ba8b3e6537a560e69724eb601f26af27) Thanks [@nishuzumi](https://github.com/nishuzumi)! - Exported types -## 1.7.2 +## 2.6.5 ### Patch Changes -- 0xsequence: ChainId should not be exported as a type -- account, wallet: fix nonce selection -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.7.2 +- [#3580](https://github.com/wevm/wagmi/pull/3580) [`c677dcd2`](https://github.com/wevm/wagmi/commit/c677dcd245dccdf69289a3d66dded237b09570a2) Thanks [@tmm](https://github.com/tmm)! - Updated internals. -## 1.7.1 +## 2.6.4 ### Patch Changes -- network: add missing avalanche logoURI -- Updated dependencies - - @0xsequence/abi@1.7.1 +- [#3571](https://github.com/wevm/wagmi/pull/3571) [`7c6618e6`](https://github.com/wevm/wagmi/commit/7c6618e6a0eb1ff39cf8f66b34d3ddc14be538fe) Thanks [@tmm](https://github.com/tmm)! - Fixed `getClient` passthrough properties from `createConfig`. -## 1.7.0 +- [#3558](https://github.com/wevm/wagmi/pull/3558) [`895f28e8`](https://github.com/wevm/wagmi/commit/895f28e873af7c8eda5ca85734ff67c8979fd950) Thanks [@tmm](https://github.com/tmm)! - Fixed connector warnings. -### Minor Changes +## 2.6.3 + +### Patch Changes + +- [#3533](https://github.com/wevm/wagmi/pull/3533) [`9c3b85dd`](https://github.com/wevm/wagmi/commit/9c3b85dd0a9a4a593e1d7e029345275735330e32) Thanks [@tmm](https://github.com/tmm)! - Fixed `account` property passthrough for actions. + +- [`2a72214a`](https://github.com/wevm/wagmi/commit/2a72214a2901d6b6ddd39f80238aa0bd4db670a7) Thanks [@tmm](https://github.com/tmm)! - Shimmed EIP-1193 `removeListener` for injected since some wallets do not follow spec. + +## 2.6.2 + +### Patch Changes + +- [#3519](https://github.com/wevm/wagmi/pull/3519) [`414eb048`](https://github.com/wevm/wagmi/commit/414eb048af492caac70c0e874dfc87c30702804a) Thanks [@tmm](https://github.com/tmm)! - Fixed multicall passing through all properties to Viem method. + +- [#3518](https://github.com/wevm/wagmi/pull/3518) [`338e857d`](https://github.com/wevm/wagmi/commit/338e857d8cb2fe85e13d9207bef14cada1c1962d) Thanks [@tmm](https://github.com/tmm)! - Fixed internal store migration between versions. -- provider: projectAccessKey is now required +## 2.6.1 ### Patch Changes -- network: add NetworkMetadata.logoURI property for all networks -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.7.0 +- [#3510](https://github.com/wevm/wagmi/pull/3510) [`660ff80d`](https://github.com/wevm/wagmi/commit/660ff80d5b046967a446eba43ee54b8359a37d0d) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where connectors returning multiple addresses didn't checksum correctly. -## 1.6.3 +- [#3433](https://github.com/wevm/wagmi/pull/3433) [`101a7dd1`](https://github.com/wevm/wagmi/commit/101a7dd131b0cae2dc25579ecab9044290efd37b) Thanks [@tmm](https://github.com/tmm)! - Fixed `getClient` and `getPublicClient` throwing when used with unconfigured `chainId`. + +## 2.6.0 + +### Minor Changes + +- [#3496](https://github.com/wevm/wagmi/pull/3496) [`ba7f8a75`](https://github.com/wevm/wagmi/commit/ba7f8a758efb07664c6e401b5e7e325e7c62341b) Thanks [@tmm](https://github.com/tmm)! - Updated action internals to resolve Viem Client actions. + +## 2.5.0 + +### Minor Changes + +- [#3461](https://github.com/wevm/wagmi/pull/3461) [`ca98041d`](https://github.com/wevm/wagmi/commit/ca98041d1b39893d90246929485f4db0d1c6f9f7) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `getTransactionConfirmations` action. + +## 2.4.0 + +### Minor Changes + +- [#3427](https://github.com/wevm/wagmi/pull/3427) [`370f1b4a`](https://github.com/wevm/wagmi/commit/370f1b4a3f154d181acf381c31c2e7862e22c0e4) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `prepareTransactionRequest` action. + +## 2.3.1 ### Patch Changes -- network list update -- Updated dependencies - - @0xsequence/abi@1.6.3 +- [#3476](https://github.com/wevm/wagmi/pull/3476) [`3be5bb7b`](https://github.com/wevm/wagmi/commit/3be5bb7b0b38646e12e6da5c762ef74dff66bcc2) Thanks [@jxom](https://github.com/jxom)! - Modified persist strategy to only store "critical" properties that are needed before hydration. + +## 2.3.0 + +### Minor Changes -## 1.6.2 +- [#3459](https://github.com/wevm/wagmi/pull/3459) [`d950b666`](https://github.com/wevm/wagmi/commit/d950b666b56700ca039ce16cdfdf34564991e7f5) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `getEnsText` action. ### Patch Changes -- auth: projectAccessKey option -- wallet: use 12 bytes for random space -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.6.2 +- [#3467](https://github.com/wevm/wagmi/pull/3467) [`90ef39bb`](https://github.com/wevm/wagmi/commit/90ef39bb0f4ecb3c914d317875348e35ba0f4524) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where connectors that share the same provider instance could reconnect when they have never been connected before. -## 1.6.1 +- [`1cfb6e5a`](https://github.com/wevm/wagmi/commit/1cfb6e5a875e707abcee00dd5739e87da05e8c90) Thanks [@jxom](https://github.com/jxom)! - Bumped listener limit on WalletConnect connector. + +## 2.2.1 ### Patch Changes -- core: add simple config from subdigest support -- core: fix encode tree with subdigest -- account: implement buildOnChainSignature on Account -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.6.1 +- [#3447](https://github.com/wevm/wagmi/pull/3447) [`a02a26ad`](https://github.com/wevm/wagmi/commit/a02a26ad030d3afb78f744377d61b5c60b65d97a) Thanks [@tmm](https://github.com/tmm)! - Fixed account typing. + +- [#3443](https://github.com/wevm/wagmi/pull/3443) [`007024a6`](https://github.com/wevm/wagmi/commit/007024a684ddbecf924cdc06dd6a8854fc3d5eeb) Thanks [@jmrossy](https://github.com/jmrossy)! - Fixed invalid `chainId` parameter passed through actions to Viem. -## 1.6.0 +## 2.2.0 ### Minor Changes -- account, wallet: parallel transactions by default +- [#3434](https://github.com/wevm/wagmi/pull/3434) [`00bf10a4`](https://github.com/wevm/wagmi/commit/00bf10a428b0d1c5dac35ebf25b19571e033ac26) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `getBytecode` and `getStorageAt` actions. + +- [#3416](https://github.com/wevm/wagmi/pull/3416) [`64c073f6`](https://github.com/wevm/wagmi/commit/64c073f6c2720961e2d6aff986670b73dbfab9c3) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `getTransactionReceipt` action. + +- [#3408](https://github.com/wevm/wagmi/pull/3408) [`fb6c4148`](https://github.com/wevm/wagmi/commit/fb6c4148d9e9e2fccfbe74c8f343b444dc68dec5) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `getProof` action. + +## 2.1.2 + +### Patch Changes + +- [#3407](https://github.com/wevm/wagmi/pull/3407) [`e00b8205`](https://github.com/wevm/wagmi/commit/e00b82058685751637edfa9a6b2d196a12549fe7) Thanks [@jxom](https://github.com/jxom)! - Added a prelude gas estimate check to `sendTransaction`/`useSendTransaction`. + +## 2.1.1 ### Patch Changes -- provider: emit disconnect on sign out -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.6.0 +- [#3402](https://github.com/wevm/wagmi/pull/3402) [`64b82282`](https://github.com/wevm/wagmi/commit/64b82282c1e57e77c25aa0814673780e4d11edd4) Thanks [@Songkeys](https://github.com/Songkeys)! - Fixed SSR cookie support for cookies that have special characters, e.g. `=`. + +- [`ec0d8b41`](https://github.com/wevm/wagmi/commit/ec0d8b4112181fefb11025e436a94a6114761d37) Thanks [@tmm](https://github.com/tmm)! - Added note to `metaMask` connector. -## 1.5.0 +## 2.1.0 ### Minor Changes -- signhub: add 'signing' signer status +- [#3387](https://github.com/wevm/wagmi/pull/3387) [`c9cd302e`](https://github.com/wevm/wagmi/commit/c9cd302e1c65c980deaee2e12567c2a8ec08b399) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `call` action. + +## 2.0.2 + +### Patch Changes + +- [#3384](https://github.com/wevm/wagmi/pull/3384) [`ee868c33`](https://github.com/wevm/wagmi/commit/ee868c3385dae511230b6ddcb5627c1293cc1844) Thanks [@tmm](https://github.com/tmm)! - Fixed connectors not bubbling error when connecting with `chainId` and subsequent user rejection. + +## 2.0.1 + +### Major Changes + +- [#3333](https://github.com/wevm/wagmi/pull/3333) [`b3a0baaa`](https://github.com/wevm/wagmi/commit/b3a0baaaee7decf750d376aab2502cd33ca4825a) Thanks [@tmm](https://github.com/tmm)! - Wagmi Core 2.0 featuring: + + - Full TanStack Query support + queryKeys + - Connect multiple connectors + - Switch chains while disconnected + - EIP-6963 enabled + - Strongly typed chainId and chain properties + - Smaller bundle size + - Miscellaneous improvements and bug fixes + + [Breaking Changes & Migration Guide](https://wagmi.sh/core/guides/migrate-from-v1-to-v2) + +## 1.4.13 + +### Patch Changes + +- Updated dependencies [[`bbbbf587`](https://github.com/wevm/wagmi/commit/bbbbf587e41bae12b072b7a7c897d580fc07cd2b)]: + - @wagmi/connectors@3.1.11 + +## 1.4.12 + +### Patch Changes + +- [`53ca1f7e`](https://github.com/wevm/wagmi/commit/53ca1f7eb411d912e11fcce7e03bd61ed067959c) Thanks [@tmm](https://github.com/tmm)! - Removed LedgerConnector due to security vulnerability + +- Updated dependencies [[`53ca1f7e`](https://github.com/wevm/wagmi/commit/53ca1f7eb411d912e11fcce7e03bd61ed067959c)]: + - @wagmi/connectors@3.1.10 + +## 1.4.11 + +### Patch Changes + +- [#3299](https://github.com/wevm/wagmi/pull/3299) [`b02020b3`](https://github.com/wevm/wagmi/commit/b02020b3724e0228198f35817611bb063295906e) Thanks [@dasanra](https://github.com/dasanra)! - Fixed issue with [Safe SDK](https://github.com/wevm/viem/issues/579) by bumping `@safe-global/safe-apps-provider@0.18.1` + +- Updated dependencies [[`51eca0fb`](https://github.com/wevm/wagmi/commit/51eca0fbaea6932f31a5b8b4213f0252280053e2), [`b02020b3`](https://github.com/wevm/wagmi/commit/b02020b3724e0228198f35817611bb063295906e)]: + - @wagmi/connectors@3.1.9 + +## 1.4.10 ### Patch Changes -- auth: Session.open: onAccountAddress callback -- account: allow empty transaction bundles -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.5.0 +- Updated dependencies [[`e8f7bcbc`](https://github.com/wevm/wagmi/commit/e8f7bcbcd9c038a901c29e71769682c088efe2ac)]: + - @wagmi/connectors@3.1.8 ## 1.4.9 ### Patch Changes -- rename SequenceMetadataClient to SequenceMetadata -- Updated dependencies - - @0xsequence/abi@1.4.9 +- [#3276](https://github.com/wevm/wagmi/pull/3276) [`83223a06`](https://github.com/wevm/wagmi/commit/83223a0659e2f675d897a1d3374c7af752c16abf) Thanks [@glitch-txs](https://github.com/glitch-txs)! - Removed required namespaces from WalletConnect connector + +- Updated dependencies [[`83223a06`](https://github.com/wevm/wagmi/commit/83223a0659e2f675d897a1d3374c7af752c16abf)]: + - @wagmi/connectors@3.1.7 ## 1.4.8 ### Patch Changes -- account: Account.getSigners -- Updated dependencies - - @0xsequence/abi@1.4.8 +- Updated dependencies [[`cc7e18f2`](https://github.com/wevm/wagmi/commit/cc7e18f2e7f6b8b989f60f0b05aee70e996a9975), [`cc7e18f2`](https://github.com/wevm/wagmi/commit/cc7e18f2e7f6b8b989f60f0b05aee70e996a9975)]: + - @wagmi/connectors@3.1.6 ## 1.4.7 ### Patch Changes -- update indexer client bindings -- Updated dependencies - - @0xsequence/abi@1.4.7 +- Updated dependencies [[`a1950449`](https://github.com/wagmi-dev/wagmi/commit/a1950449127ddf72fff8ecd1fc34c3690befbb05)]: + - @wagmi/connectors@3.1.5 ## 1.4.6 ### Patch Changes -- - add sepolia networks, mark goerli as deprecated - - update indexer client bindings -- Updated dependencies - - @0xsequence/abi@1.4.6 +- Updated dependencies [[`4e6ec415`](https://github.com/wagmi-dev/wagmi/commit/4e6ec4151baece94e940e227e0e3711c7f8534d9)]: + - @wagmi/connectors@3.1.4 ## 1.4.5 ### Patch Changes -- indexer/metadata: update client bindings -- auth: selectWallet with new address -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.4.5 +- Updated dependencies [[`e78aa337`](https://github.com/wagmi-dev/wagmi/commit/e78aa337c454f04b41a3cbd381d25270dd4a0afd)]: + - @wagmi/connectors@3.1.3 ## 1.4.4 ### Patch Changes -- indexer: update bindings -- auth: handle jwt expiry -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.4.4 +- [#3125](https://github.com/wagmi-dev/wagmi/pull/3125) [`725e73fe`](https://github.com/wagmi-dev/wagmi/commit/725e73feb9143dbaa6d540bb76d2009cef29da0b) Thanks [@lukasrosario](https://github.com/lukasrosario)! - Fixed an issue where `dataSuffix` was not being passed down into viem's `simulateContract`, causing the data to be omitted from requests. ## 1.4.3 ### Patch Changes -- guard: return active status from GuardSigner.getAuthMethods -- Updated dependencies - - @0xsequence/abi@1.4.3 +- [#3076](https://github.com/wagmi-dev/wagmi/pull/3076) [`4c36831b`](https://github.com/wagmi-dev/wagmi/commit/4c36831b7aa44d03b5c0decf64dcd20faae28a67) Thanks [@jxom](https://github.com/jxom)! - Pass `chain` to viem `sendTransaction`/`writeContract`. + +- [#3006](https://github.com/wagmi-dev/wagmi/pull/3006) [`f2ddce23`](https://github.com/wagmi-dev/wagmi/commit/f2ddce23324aff0a91e066100918dac552dc3b4a) Thanks [@jxom](https://github.com/jxom)! - Changed `normalize` to a dynamic import. ## 1.4.2 ### Patch Changes -- guard: update bindings -- Updated dependencies - - @0xsequence/abi@1.4.2 +- Updated dependencies [[`3aaba328`](https://github.com/wagmi-dev/wagmi/commit/3aaba32808ddb4035ec885f96992c91078056715)]: + - @wagmi/connectors@3.1.2 ## 1.4.1 ### Patch Changes -- network: remove unused networks -- signhub: orchestrator interface -- guard: auth methods interface -- guard: update bindings for pin and totp -- guard: no more retry logic -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.4.1 +- Updated dependencies [[`bf831bb3`](https://github.com/wagmi-dev/wagmi/commit/bf831bb30df8037cc4312342d0fe3c045408c2fe)]: + - @wagmi/connectors@3.1.1 ## 1.4.0 ### Minor Changes -- project access key support +- [#2956](https://github.com/wagmi-dev/wagmi/pull/2956) [`2abeb285`](https://github.com/wagmi-dev/wagmi/commit/2abeb285674af3e539cc2550b1f5027b1eb0c895) Thanks [@tmm](https://github.com/tmm)! - Replaced `@wagmi/chains` with `viem/chains`. ### Patch Changes -- Updated dependencies - - @0xsequence/abi@1.4.0 +- Updated dependencies [[`2abeb285`](https://github.com/wagmi-dev/wagmi/commit/2abeb285674af3e539cc2550b1f5027b1eb0c895)]: + - @wagmi/connectors@3.1.0 -## 1.3.0 +## 1.3.10 -### Minor Changes +### Patch Changes -- signhub: account children +- [`557e6400`](https://github.com/wagmi-dev/wagmi/commit/557e6400b9cef3b2c5131739143956c37d7c934a) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +## 1.3.9 + +### Patch Changes + +- [`247c5d11`](https://github.com/wagmi-dev/wagmi/commit/247c5d113e83acf3a6894264c00d4b125d455107) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +## 1.3.8 ### Patch Changes -- guard: do not throw when building deploy transaction -- network: snowtrace.io -> subnets.avax.network/c-chain -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.3.0 +- [#2741](https://github.com/wagmi-dev/wagmi/pull/2741) [`5b1453d9`](https://github.com/wagmi-dev/wagmi/commit/5b1453d95973ed51f1c235a919fffb707eab9b70) Thanks [@jxom](https://github.com/jxom)! - Updated references -## 1.2.9 +## 1.3.7 ### Patch Changes -- account: AccountSigner.sendTransaction simulateForFeeOptions -- relayer: update bindings -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.2.9 +- [#2700](https://github.com/wagmi-dev/wagmi/pull/2700) [`30118e97`](https://github.com/wagmi-dev/wagmi/commit/30118e979b1b00302e035f31f58c15d1aed911d5) Thanks [@jxom](https://github.com/jxom)! - Updated references. -## 1.2.8 +## 1.3.6 ### Patch Changes -- rename X-Sequence-Token-Key header to X-Access-Key -- Updated dependencies - - @0xsequence/abi@1.2.8 +- [`7ad2fdb8`](https://github.com/wagmi-dev/wagmi/commit/7ad2fdb81c7734d0c8107670800c68390e3bad99) Thanks [@jxom](https://github.com/jxom)! - Updated references. -## 1.2.7 +## 1.3.5 ### Patch Changes -- add x-sequence-token-key to clients -- Updated dependencies - - @0xsequence/abi@1.2.7 +- [`aab63fc1`](https://github.com/wagmi-dev/wagmi/commit/aab63fc1f8949004573978ecd8574fada3360758) Thanks [@jxom](https://github.com/jxom)! - Updated references. -## 1.2.6 +## 1.3.4 ### Patch Changes -- Fix bind multicall provider -- Updated dependencies - - @0xsequence/abi@1.2.6 +- [`22246d98`](https://github.com/wagmi-dev/wagmi/commit/22246d9884277d28ccad6ca2d9529b96b67d47fc) Thanks [@jxom](https://github.com/jxom)! - Updated references. -## 1.2.5 +## 1.3.3 ### Patch Changes -- Multicall default configuration fixes -- Updated dependencies - - @0xsequence/abi@1.2.5 +- [`1946aa43`](https://github.com/wagmi-dev/wagmi/commit/1946aa43a65b684ef41b7b4c43c67bf29c13e854) Thanks [@jxom](https://github.com/jxom)! - Updated references -## 1.2.4 +## 1.3.2 ### Patch Changes -- provider: Adding missing payment provider types to PaymentProviderOption -- provider: WalletRequestHandler.notifyChainChanged -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.2.4 +- [`e86d0940`](https://github.com/wagmi-dev/wagmi/commit/e86d09409bb20b64d24e1263abcf0291314f03c7) Thanks [@jxom](https://github.com/jxom)! - Updated references -## 1.2.3 +## 1.3.1 ### Patch Changes -- auth, provider: connect to accept optional authorizeNonce -- Updated dependencies - - @0xsequence/abi@1.2.3 +- [`964042fa`](https://github.com/wagmi-dev/wagmi/commit/964042fa94d682977923c595820c58283fb9244a) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +## 1.3.0 + +### Minor Changes + +- [#2619](https://github.com/wagmi-dev/wagmi/pull/2619) [`0d79748c`](https://github.com/wagmi-dev/wagmi/commit/0d79748cec2b6ac2410ad2c9816cc662f2b70962) Thanks [@jxom](https://github.com/jxom)! - Updated references: + - Updated `@safe-global/safe-apps-sdk` to `^8.0.0` (the one with `viem` support) ## 1.2.2 ### Patch Changes -- provider: allow createContract calls -- core: check for explicit zero address in contract deployments -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.2.2 +- [#2611](https://github.com/wagmi-dev/wagmi/pull/2611) [`6d1ed7a1`](https://github.com/wagmi-dev/wagmi/commit/6d1ed7a156729b4df5d66fef3ae9a8b5762a2d34) Thanks [@jxom](https://github.com/jxom)! - Updated references. ## 1.2.1 ### Patch Changes -- auth: use sequence api chain id as reference chain id if available -- Updated dependencies - - @0xsequence/abi@1.2.1 +- [#2589](https://github.com/wagmi-dev/wagmi/pull/2589) [`9680c347`](https://github.com/wagmi-dev/wagmi/commit/9680c347476500d28ceca20d23eeaed7931cb6e0) Thanks [@jxom](https://github.com/jxom)! - Fixed `writeContract` parameters to be compatible with `prepareWriteContract`. + +- [#2587](https://github.com/wagmi-dev/wagmi/pull/2587) [`cfff9994`](https://github.com/wagmi-dev/wagmi/commit/cfff999459384ac644ff7e62f53a7b787cf37507) Thanks [@jxom](https://github.com/jxom)! - Updated references ## 1.2.0 ### Minor Changes -- split services from session, better local support +- [#2536](https://github.com/wagmi-dev/wagmi/pull/2536) [`85e9760a`](https://github.com/wagmi-dev/wagmi/commit/85e9760a140cb169ac6236d9466b96e2105dd193) Thanks [@tmm](https://github.com/tmm)! - Changed `Address` type import from ABIType to viem. ### Patch Changes -- Updated dependencies - - @0xsequence/abi@1.2.0 +- [#2539](https://github.com/wagmi-dev/wagmi/pull/2539) [`96319c64`](https://github.com/wagmi-dev/wagmi/commit/96319c640b9d07b375821c08a5c213355d8c290b) Thanks [@jxom](https://github.com/jxom)! - Updated references -## 1.1.15 +## 1.1.1 ### Patch Changes -- guard: remove error filtering -- Updated dependencies - - @0xsequence/abi@1.1.15 +- [`02b98a9f`](https://github.com/wagmi-dev/wagmi/commit/02b98a9f9b2c503a47af4a8967e0202b5db21787) Thanks [@jxom](https://github.com/jxom)! - Updated `viem` peer dependency. + +- [`02b98a9f`](https://github.com/wagmi-dev/wagmi/commit/02b98a9f9b2c503a47af4a8967e0202b5db21787) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +## 1.1.0 + +### Minor Changes -## 1.1.14 +- [#2482](https://github.com/wagmi-dev/wagmi/pull/2482) [`8764b54a`](https://github.com/wagmi-dev/wagmi/commit/8764b54aab68020063946112e8fe52aff650c99c) Thanks [@tmm](https://github.com/tmm)! - Bumped minimum TypeScript version to v5.0.4. ### Patch Changes -- guard: add GuardSigner.onError -- Updated dependencies - - @0xsequence/abi@1.1.14 +- [#2484](https://github.com/wagmi-dev/wagmi/pull/2484) [`3adf1f4f`](https://github.com/wagmi-dev/wagmi/commit/3adf1f4feab863cb7b5d52c81ad46f7e4eb56f09) Thanks [@jxom](https://github.com/jxom)! - Updated `abitype` to 0.8.7 + +- [#2484](https://github.com/wagmi-dev/wagmi/pull/2484) [`3adf1f4f`](https://github.com/wagmi-dev/wagmi/commit/3adf1f4feab863cb7b5d52c81ad46f7e4eb56f09) Thanks [@jxom](https://github.com/jxom)! - Updated references. -## 1.1.13 +## 1.0.8 ### Patch Changes -- provider: pass client version with connect options -- provider: removing large from BannerSize -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.1.13 +- [#2441](https://github.com/wagmi-dev/wagmi/pull/2441) [`326edee4`](https://github.com/wagmi-dev/wagmi/commit/326edee4bc85db84a7a4e3768e33785849ab8d8e) Thanks [@tmm](https://github.com/tmm)! - Fixed internal type issue -## 1.1.12 +## 1.0.7 ### Patch Changes -- provider: adding bannerSize to ConnectOptions -- Updated dependencies - - @0xsequence/abi@1.1.12 +- [#2433](https://github.com/wagmi-dev/wagmi/pull/2433) [`54fcff5f`](https://github.com/wagmi-dev/wagmi/commit/54fcff5f02f6933bbbe045ee0c83c5a78b6bba49) Thanks [@jxom](https://github.com/jxom)! - Added ability to pass an `account` to `writeContract`/`prepareWriteContract`. -## 1.1.11 +## 1.0.6 ### Patch Changes -- add homeverse configs -- Updated dependencies - - @0xsequence/abi@1.1.11 +- [`ca2e1e96`](https://github.com/wagmi-dev/wagmi/commit/ca2e1e96149b87a7dc42c9db07e1f1ad2bb02c4a) Thanks [@jxom](https://github.com/jxom)! - Updated references. -## 1.1.10 +- [#2401](https://github.com/wagmi-dev/wagmi/pull/2401) [`0f9dc875`](https://github.com/wagmi-dev/wagmi/commit/0f9dc875e90cfdd7a2028e04b7204caf9ea313b2) Thanks [@jxom](https://github.com/jxom)! - Exposed `account` on `readContract`/`useContractRead`. + +## 1.0.5 ### Patch Changes -- handle default EIP6492 on send -- Updated dependencies - - @0xsequence/abi@1.1.10 +- [`90e2b3b3`](https://github.com/wagmi-dev/wagmi/commit/90e2b3b39efe0585fe28645ac2264109be17362a) Thanks [@jxom](https://github.com/jxom)! - Updated references. -## 1.1.9 +## 1.0.4 ### Patch Changes -- Custom default EIP6492 on client -- Updated dependencies - - @0xsequence/abi@1.1.9 +- [#2344](https://github.com/wagmi-dev/wagmi/pull/2344) [`8a725458`](https://github.com/wagmi-dev/wagmi/commit/8a72545853ae1024acd9efd18c06142e8c6c5750) Thanks [@jxom](https://github.com/jxom)! - Added gas estimation back into `prepareSendTransaction`. -## 1.1.8 +## 1.0.3 ### Patch Changes -- metadata: searchMetadata: add types filter -- Updated dependencies - - @0xsequence/abi@1.1.8 +- [#2338](https://github.com/wagmi-dev/wagmi/pull/2338) [`92bfdc2c`](https://github.com/wagmi-dev/wagmi/commit/92bfdc2c744539558ba93c95f140b46ad331cee4) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where synchronous switch chain behavior (WalletConnect v2) would encounter chain id race conditions in `watchWalletClient`. -## 1.1.7 +## 1.0.2 ### Patch Changes -- adding signInWith connect settings option to allow dapps to automatically login their users with a certain provider optimizing the normal authentication flow -- Updated dependencies - - @0xsequence/abi@1.1.7 +- [#2304](https://github.com/wevm/wagmi/pull/2304) [`09a4fd38`](https://github.com/wevm/wagmi/commit/09a4fd38f44eb176797925fd85314be17b610cd4) Thanks [@jxom](https://github.com/jxom)! - Removed assert chain workaround. -## 1.1.6 +## 1.0.1 ### Patch Changes -- metadata: searchMetadata: add chainID and excludeTokenMetadata filters -- Updated dependencies - - @0xsequence/abi@1.1.6 +- [`ea651cd7`](https://github.com/wevm/wagmi/commit/ea651cd7fc75b7866272605467db11fd6e1d81af) Thanks [@jxom](https://github.com/jxom)! - Downgraded abitype. -## 1.1.5 +- Updated dependencies [[`ea651cd7`](https://github.com/wevm/wagmi/commit/ea651cd7fc75b7866272605467db11fd6e1d81af)]: + - @wagmi/connectors@1.0.1 + +## 1.0.0 + +### Major Changes + +- [#2235](https://github.com/wevm/wagmi/pull/2235) [`5be0655c`](https://github.com/wevm/wagmi/commit/5be0655c8e48b25d38009022461fbf611af54349) Thanks [@jxom](https://github.com/jxom)! - Released v1. Read [Migration Guide](https://next.wagmi.sh/react/migration-guide#1xx-breaking-changes). + +## 1.0.0-next.7 + +### Major Changes + +- [#2235](https://github.com/wevm/wagmi/pull/2235) [`708b2ce2`](https://github.com/wevm/wagmi/commit/708b2ce26efa8d3d910806a97cea5171dabc65de) Thanks [@jxom](https://github.com/jxom)! - Added `config.setPublicClient` & `config.setWebSocketPublicClient` + +- Updated references. ### Patch Changes -- account: re-compute meta-transaction id for wallet deployment transactions -- Updated dependencies - - @0xsequence/abi@1.1.5 +- Updated dependencies []: + - @wagmi/connectors@1.0.0-next.5 + +## 1.0.0-next.6 + +### Major Changes + +- [#2235](https://github.com/wevm/wagmi/pull/2235) [`708b2ce2`](https://github.com/wevm/wagmi/commit/708b2ce26efa8d3d910806a97cea5171dabc65de) Thanks [@jxom](https://github.com/jxom)! - Added `config.setPublicClient` & `config.setWebSocketPublicClient` -## 1.1.4 +- Added `config.setConnectors` ### Patch Changes -- network: rename base-mainnet to base -- provider: override isDefaultChain with ConnectOptions.networkId if provided -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.1.4 +- Updated dependencies []: + - @wagmi/connectors@1.0.0-next.6 + +## 1.0.0-next.5 + +### Major Changes + +- [#2235](https://github.com/wevm/wagmi/pull/2235) [`708b2ce2`](https://github.com/wevm/wagmi/commit/708b2ce26efa8d3d910806a97cea5171dabc65de) Thanks [@jxom](https://github.com/jxom)! - Added `config.setPublicClient` & `config.setWebSocketPublicClient` + +## 1.0.0-next.4 + +### Major Changes -## 1.1.3 +- Updated viem. + Removed `goerli` export from main entrypoint. ### Patch Changes -- provider: use network id from transport session -- provider: sign authorization using ConnectOptions.networkId if provided -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.1.3 +- Updated dependencies []: + - @wagmi/connectors@1.0.0-next.5 + +## 1.0.0-next.3 + +### Major Changes -## 1.1.2 +- Updated references. ### Patch Changes -- provider: jsonrpc chain id fixes -- Updated dependencies - - @0xsequence/abi@1.1.2 +- Updated dependencies []: + - @wagmi/connectors@1.0.0-next.4 -## 1.1.1 +## 1.0.0-next.2 + +### Major Changes + +- **Breaking:** Renamed `createClient` to `createConfig` +- **Breaking:** Renamed `getClient` to `getConfig` +- **Breaking:** Removed `request` as an argument to `prepareSendTransaction` & `sendTransaction`. Arguments now belong on the root level of the Action. ### Patch Changes -- network: add base mainnet and sepolia -- provider: reject toxic transaction requests -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.1.1 +- Updated dependencies []: + - @wagmi/chains@1.0.0-next.0 + - @wagmi/connectors@1.0.0-next.3 -## 1.1.0 +## 1.0.0-next.1 -### Minor Changes +### Major Changes -- Refactor dapp facing provider +- updated viem ### Patch Changes -- Updated dependencies - - @0xsequence/abi@1.1.0 +- Updated dependencies []: + - @wagmi/connectors@1.0.0-next.2 -## 1.0.5 +## 1.0.0-next.0 + +### Major Changes + +- [`a7dda00c`](https://github.com/wevm/wagmi/commit/a7dda00c5b546f8b2c42b527e4d9ac1b9e9ab1fb) Thanks [@jxom](https://github.com/jxom)! - Released v1. ### Patch Changes -- network: export network constants -- guard: use the correct global for fetch -- network: nova-explorer.arbitrum.io -> nova.arbiscan.io -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@1.0.5 +- Updated dependencies [[`a7dda00c`](https://github.com/wevm/wagmi/commit/a7dda00c5b546f8b2c42b527e4d9ac1b9e9ab1fb)]: + - @wagmi/connectors@1.0.0-next.1 -## 1.0.4 +## 0.10.11 ### Patch Changes -- provider: accept name or number for networkId -- Updated dependencies - - @0xsequence/abi@1.0.4 +- [#2270](https://github.com/wevm/wagmi/pull/2270) [`6d1fa9df`](https://github.com/wevm/wagmi/commit/6d1fa9df790287729c3b33d4f01fd23c2f8153f1) Thanks [@jxom](https://github.com/jxom)! - Updated references. -## 1.0.3 +- Updated dependencies []: + - @wagmi/connectors@0.3.19 + +## 0.10.10 ### Patch Changes -- Simpler isValidSignature helpers -- Updated dependencies - - @0xsequence/abi@1.0.3 +- [#2208](https://github.com/wevm/wagmi/pull/2208) [`cfc696d8`](https://github.com/wevm/wagmi/commit/cfc696d83c6f768a2e1a29c5197efeed7f1d40a1) Thanks [@bangtoven](https://github.com/bangtoven)! - Bumped references to apply coinbase wallet sdk updates -## 1.0.2 +- Updated dependencies []: + - @wagmi/connectors@0.3.16 + +## 0.10.9 ### Patch Changes -- add extra signature validation utils methods -- Updated dependencies - - @0xsequence/abi@1.0.2 +- [#2143](https://github.com/wevm/wagmi/pull/2143) [`26dc5326`](https://github.com/wevm/wagmi/commit/26dc53260fde1d3278018c0b20a6d48a093d9427) Thanks [@tmm](https://github.com/tmm)! - Exported Sepolia Chain. -## 1.0.1 +- [#2146](https://github.com/wevm/wagmi/pull/2146) [`21b6842e`](https://github.com/wevm/wagmi/commit/21b6842e8c296a0bbe71ebe0780d898abc4cf4a8) Thanks [@tmm](https://github.com/tmm)! - Bumped references + +- Updated dependencies []: + - @wagmi/connectors@0.3.12 + +## 0.10.8 ### Patch Changes -- add homeverse testnet -- Updated dependencies - - @0xsequence/abi@1.0.1 +- [#2099](https://github.com/wevm/wagmi/pull/2099) [`f1fee5b3`](https://github.com/wevm/wagmi/commit/f1fee5b30a1bd13b5e66118bf9cdc44b0dc003a1) Thanks [@jxom](https://github.com/jxom)! - Added chains: -## 1.0.0 + - `nexi` + - `polygonZkEvm` + - `xdc` + - `xdcTestnet` -### Major Changes +- [#2085](https://github.com/wevm/wagmi/pull/2085) [`7d64e3f5`](https://github.com/wevm/wagmi/commit/7d64e3f538a6149777bfa84ea9435769b2a7db58) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where multicall would not throw if the target chain was not configured on the wagmi client. + +- Updated dependencies []: + - @wagmi/connectors@0.3.10 + +## 0.10.7 + +### Patch Changes + +- [#2082](https://github.com/wevm/wagmi/pull/2082) [`2ccc8a25`](https://github.com/wevm/wagmi/commit/2ccc8a255e93f0a2bb7b22101656b3905ec59abd) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies []: + - @wagmi/connectors@0.3.10 + +## 0.10.6 + +### Patch Changes + +- [#2056](https://github.com/wevm/wagmi/pull/2056) [`944f6513`](https://github.com/wevm/wagmi/commit/944f6513adf09a6f0b3bd34f591d3bbd1f1ffd2e) Thanks [@tmm](https://github.com/tmm)! - Bumped references. + +- Updated dependencies []: + - @wagmi/connectors@0.3.8 + +## 0.10.5 + +### Patch Changes + +- [#2053](https://github.com/wevm/wagmi/pull/2053) [`665df1bf`](https://github.com/wevm/wagmi/commit/665df1bf2afccb533102069def395e19fb7194dd) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where you add a new chain to MetaMask, but the switch after is rejected. + +- Updated dependencies []: + - @wagmi/connectors@0.3.7 + +## 0.10.4 + +### Patch Changes + +- [#2046](https://github.com/wevm/wagmi/pull/2046) [`90d8e9b8`](https://github.com/wevm/wagmi/commit/90d8e9b87962b72c54311649537e91a953660f9b) Thanks [@tmm](https://github.com/tmm)! - Exported internal type. + +- Updated dependencies []: + - @wagmi/connectors@0.3.6 + +## 0.10.3 + +### Patch Changes + +- [#2039](https://github.com/wevm/wagmi/pull/2039) [`bac893ab`](https://github.com/wevm/wagmi/commit/bac893ab26012d4d8741c4f80e8b8813aee26f0c) Thanks [@tmm](https://github.com/tmm)! - Updated references. + +- [#2043](https://github.com/wevm/wagmi/pull/2043) [`49a58320`](https://github.com/wevm/wagmi/commit/49a58320ab5f1f13bc4de25abcc028c8335e98f0) Thanks [@tmm](https://github.com/tmm)! - Removed `InjectedConnector` `shimChainChangedDisconnect` shim (no longer necessary). + +- Updated dependencies []: + - @wagmi/connectors@0.3.6 + +## 0.10.2 + +### Patch Changes + +- [#2016](https://github.com/wevm/wagmi/pull/2016) [`06bf61de`](https://github.com/wevm/wagmi/commit/06bf61dee6d2920777bd9392491e6b7aedebe7ab) Thanks [@jxom](https://github.com/jxom)! - Added chains: + + - `boba` + - `chronos` + - `crossbell` + - `dfk` + - `dogechain` + - `flare` + - `flareTestnet` + - `klaytn` + - `scrollTestnet` + - `shardeumSphinx` + - `skaleCalypso` + - `skaleCalypsoTestnet` + - `skaleChaosTestnet` + - `skaleCryptoBlades` + - `skaleCryptoColosseum` + - `skaleEuropa` + - `skaleEuropaTestnet` + - `skaleExorde` + - `skaleHumanProtocol` + - `skaleNebula` + - `skaleNebulaTestnet` + - `skaleRazor` + - `skaleTitan` + - `skaleTitanTestnet` + - `songbird` + - `songbirdTestnet` + - `titan` + - `titanTestnet` + - `wanchain` + - `wanchainTestnet` + +- [#2016](https://github.com/wevm/wagmi/pull/2016) [`06bf61de`](https://github.com/wevm/wagmi/commit/06bf61dee6d2920777bd9392491e6b7aedebe7ab) Thanks [@jxom](https://github.com/jxom)! - Updated references/ submodule. + +- Updated dependencies []: + - @wagmi/connectors@0.3.4 + +## 0.10.0 + +### Minor Changes + +- [#1902](https://github.com/wevm/wagmi/pull/1902) [`0994e896`](https://github.com/wevm/wagmi/commit/0994e8966349b8811db0a5886db3831dafc99245) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** Removed the `version` config option for `WalletConnectConnector`. + + `WalletConnectConnector` now uses WalletConnect v2 by default. WalletConnect v1 is now `WalletConnectLegacyConnector`. + + ### WalletConnect v2 + + ```diff + import { WalletConnectConnector } from 'wagmi/connectors/walletConnect' + + const connector = new WalletConnectConnector({ + options: { + - version: '2', + projectId: 'abc', + }, + }) + ``` + + ### WalletConnect v1 + + ```diff + -import { WalletConnectConnector } from 'wagmi/connectors/walletConnect' + +import { WalletConnectConnector } from 'wagmi/connectors/walletConnectLegacy' + + -const connector = new WalletConnectConnector({ + +const connector = new WalletConnectLegacyConnector({ + options: { + qrcode: true, + }, + }) + ``` + +### Patch Changes + +- Updated dependencies []: + - @wagmi/connectors@0.3.2 + +## 0.9.7 + +### Patch Changes + +- [#1907](https://github.com/wevm/wagmi/pull/1907) [`cc4e74ee`](https://github.com/wevm/wagmi/commit/cc4e74ee19665eccb3767052dab6ab956ff4e676) Thanks [@jxom](https://github.com/jxom)! - Added the following chains to the `wagmi/chains` entrypoint: + + - `baseGoerli` + - `harmonyOne` + - `polygonZkEvmTestnet` + +- Updated dependencies []: + - @wagmi/connectors@0.2.7 + +## 0.9.6 + +### Patch Changes + +- [#1882](https://github.com/wevm/wagmi/pull/1882) [`282cc1b0`](https://github.com/wevm/wagmi/commit/282cc1b02003684d582cea411b11792a59c26fd0) Thanks [@tmm](https://github.com/tmm)! - Updated references. + +- Updated dependencies []: + - @wagmi/connectors@0.2.6 + +## 0.9.5 + +### Patch Changes + +- [#1812](https://github.com/wevm/wagmi/pull/1812) [`c7fd7fbd`](https://github.com/wevm/wagmi/commit/c7fd7fbde6f6c69a3a9a4f89d948c4dfb1d22679) Thanks [@jxom](https://github.com/jxom)! - Added the following chains to the `wagmi/chains` entrypoint: + + - `filecoinCalibration` + - `moonbaseAlpha` + - `moonbeam` + - `moonriver` + +- Updated dependencies []: + - @wagmi/connectors@0.2.5 + +## 0.9.4 + +### Patch Changes + +- [#1786](https://github.com/wevm/wagmi/pull/1786) [`b173a431`](https://github.com/wevm/wagmi/commit/b173a43165c7925a4e56ce1e0327a31917e7edc5) Thanks [@tmm](https://github.com/tmm)! - Locked ethers peer dependency version to >=5.5.1 <6 + +- [#1787](https://github.com/wevm/wagmi/pull/1787) [`f023fd8f`](https://github.com/wevm/wagmi/commit/f023fd8f66befb78b9a4df5ca971ceaa64e37ab4) Thanks [@tmm](https://github.com/tmm)! - Added `SafeConnector` + +- Updated dependencies []: + - @wagmi/connectors@0.2.4 + +## 0.9.3 + +### Patch Changes + +- [#1773](https://github.com/wevm/wagmi/pull/1773) [`9aaf1955`](https://github.com/wevm/wagmi/commit/9aaf195514d3b5f4d085c797fc5021d42a9efb6c) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/universal-provider` on `WalletConnectConnector` v2. + Added more signable methods to `WalletConnectConnector` v2. + +- [#1773](https://github.com/wevm/wagmi/pull/1773) [`9aaf1955`](https://github.com/wevm/wagmi/commit/9aaf195514d3b5f4d085c797fc5021d42a9efb6c) Thanks [@jxom](https://github.com/jxom)! - Added Telos to the `wagmi/chains` entrypoint. Thanks @donnyquixotic! + +- Updated dependencies []: + - @wagmi/connectors@0.2.3 + +## 0.9.2 + +### Patch Changes + +- [#1756](https://github.com/wevm/wagmi/pull/1756) [`31d06b8c`](https://github.com/wevm/wagmi/commit/31d06b8ce1e7af5e9d1a7ba57f1743b2dff7a53d) Thanks [@jxom](https://github.com/jxom)! - Added OKC Chain. Thanks @clark-cui! + +- [#1756](https://github.com/wevm/wagmi/pull/1756) [`31d06b8c`](https://github.com/wevm/wagmi/commit/31d06b8ce1e7af5e9d1a7ba57f1743b2dff7a53d) Thanks [@jxom](https://github.com/jxom)! - Fixed race condition between `switchNetwork` and mutation Actions that use `chainId` (e.g. `sendTransaction`). Thanks @DanInTheD4rk! + +- Updated dependencies []: + - @wagmi/connectors@0.2.2 + +## 0.9.1 + +### Patch Changes + +- [#1752](https://github.com/wevm/wagmi/pull/1752) [`144a0e76`](https://github.com/wevm/wagmi/commit/144a0e76ef4bb9ba0650b5ffb9c63f95329819a4) Thanks [@jxom](https://github.com/jxom)! - Improved `WalletConnectConnector` (v2) initialization & updated dependencies. + +- [#1752](https://github.com/wevm/wagmi/pull/1752) [`144a0e76`](https://github.com/wevm/wagmi/commit/144a0e76ef4bb9ba0650b5ffb9c63f95329819a4) Thanks [@jxom](https://github.com/jxom)! - Added the following chains to the `wagmi/chains` entrypoint: + + - Aurora – thanks @salil-naik + - Bronos – thanks @chedetinaveen + - Canto – thanks @tster + - Celo – thanks @aaronmgdr + +- Updated dependencies []: + - @wagmi/connectors@0.2.1 + +## 0.9.0 + +### Minor Changes + +- [#1732](https://github.com/wevm/wagmi/pull/1732) [`01e21897`](https://github.com/wevm/wagmi/commit/01e2189747a5c22dc758c6d719b4145adc2a643c) Thanks [@tmm](https://github.com/tmm)! - Bumped minimum TypeScript version to typescript@>=4.9.4. TypeScript 5.0 is coming soon and has some great features we are excited to bring into wagmi. To prepare for this, update your TypeScript version to 4.9.4 or higher. There are likely no [breaking changes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html#correctness-fixes-and-breaking-changes) if you are coming from typescript@4.7.x || typescript@4.8.x. + +## 0.8.19 + +### Patch Changes + +- [#1718](https://github.com/wevm/wagmi/pull/1718) [`e62b5ef8`](https://github.com/wevm/wagmi/commit/e62b5ef8aaa8063abb5264790768899ea35bbd31) Thanks [@tmm](https://github.com/tmm)! - Updated references + +## 0.8.18 + +### Patch Changes + +- [#1708](https://github.com/wevm/wagmi/pull/1708) [`07fc3801`](https://github.com/wevm/wagmi/commit/07fc3801fa13c2cb5f7cf9b86ba8320b05a6a135) Thanks [@jxom](https://github.com/jxom)! - Updated `references/` submodule. + +## 0.8.17 + +### Patch Changes + +- [#1705](https://github.com/wevm/wagmi/pull/1705) [`9ff797dc`](https://github.com/wevm/wagmi/commit/9ff797dcb979dc86b798a432b74c98598165430d) Thanks [@jxom](https://github.com/jxom)! - Added the following chains to the `@wagmi/core/chains` entrypoint: + + - `crossbell` (thanks @Songkeys) + - `filecoin` & `filecoinHyperspace` (thanks @neil0x46dc) + - `gnosisChiado` (thanks @theNvN) + - `metis` & `metisGoerli` (thanks @CookedCookee) + +## 0.8.16 + +### Patch Changes + +- [#1699](https://github.com/wevm/wagmi/pull/1699) [`2f1e7950`](https://github.com/wevm/wagmi/commit/2f1e7950e55550d9b50ef5ccb97cb609f4af39b1) Thanks [@tmm](https://github.com/tmm)! - Added public RPC URL property to Chain + +## 0.8.15 + +### Patch Changes + +- [#1685](https://github.com/wevm/wagmi/pull/1685) [`917f5bc1`](https://github.com/wevm/wagmi/commit/917f5bc1fad578e35a8c6ee787e339bfdc156bab) Thanks [@jxom](https://github.com/jxom)! - Replaced qrcodemodal with web3modal for the WalletConnect v2 Connector. + +## 0.8.14 + +### Patch Changes + +- [#1646](https://github.com/wevm/wagmi/pull/1646) [`fcdbe353`](https://github.com/wevm/wagmi/commit/fcdbe3531e6d05cda4a4a511bae1ad4c9e426d88) Thanks [@jxom](https://github.com/jxom)! - Upgraded `zustand` to v4.3.1. + +## 0.8.13 + +### Patch Changes + +- [#1639](https://github.com/wevm/wagmi/pull/1639) [`c6869f06`](https://github.com/wevm/wagmi/commit/c6869f0604fffb197752a08256f31db77f52e746) Thanks [@jxom](https://github.com/jxom)! - Added `isRainbow` flag to `InjectedConnector`. + +## 0.8.12 + +### Patch Changes + +- [#1636](https://github.com/wevm/wagmi/pull/1636) [`025f6771`](https://github.com/wevm/wagmi/commit/025f6771b32ff7eed22f527be81c5141ddaf9c3d) Thanks [@DanielSinclair](https://github.com/DanielSinclair)! - Added `isRainbow` flag to injected `window.ethereum` types. + +## 0.8.11 + +### Patch Changes + +- [#1621](https://github.com/wevm/wagmi/pull/1621) [`5812b590`](https://github.com/wevm/wagmi/commit/5812b5909277bf2862cb57a31d52465b47291410) Thanks [@tmm](https://github.com/tmm)! - Bumped @wagmi/connectors + +## 0.8.10 + +### Patch Changes + +- [#1598](https://github.com/wevm/wagmi/pull/1598) [`fc10ebe6`](https://github.com/wevm/wagmi/commit/fc10ebe659dd5f3b7a8e00581f094652280a779b) Thanks [@jxom](https://github.com/jxom)! - Fixed CJS dependency version range + +## 0.8.9 + +### Patch Changes + +- [#1593](https://github.com/wevm/wagmi/pull/1593) [`216d555c`](https://github.com/wevm/wagmi/commit/216d555c62bd95c3c7c8f8e20f7269f6c8504610) Thanks [@jxom](https://github.com/jxom)! - Added CJS escape hatch bundle under the "cjs" tag. + +## 0.8.8 + +### Patch Changes + +- [#1573](https://github.com/wevm/wagmi/pull/1573) [`ef380d9c`](https://github.com/wevm/wagmi/commit/ef380d9c6d51ae0495b9c35925d2843c75d97fd4) Thanks [@tmm](https://github.com/tmm)! - Updated internal types. + +## 0.8.7 + +### Patch Changes + +- [#1570](https://github.com/wevm/wagmi/pull/1570) [`216f585b`](https://github.com/wevm/wagmi/commit/216f585be8a9e3a56e3243f49ccd54d655b5a6dd) Thanks [@wslyvh](https://github.com/wslyvh)! - Added `watchPendingTransactions` + +- [#1470](https://github.com/wevm/wagmi/pull/1470) [`3a1a6c9f`](https://github.com/wevm/wagmi/commit/3a1a6c9fe5db5c360adfd116f9a03a1238b5720c) Thanks [@jxom](https://github.com/jxom)! - The `WalletConnectConnector` now supports WalletConnect v2. + + It can be enabled by setting `version` to `'2'` and supplying a [WalletConnect Cloud `projectId`](https://cloud.walletconnect.com/sign-in). + +## 0.8.6 + +### Patch Changes + +- [#1539](https://github.com/wevm/wagmi/pull/1539) [`732da004`](https://github.com/wevm/wagmi/commit/732da0042c7e28091b2e36a484ea8239971306f5) Thanks [@0xFlicker](https://github.com/0xFlicker)! - All Providers (ie. Alchemy, Infura, Public) now use the ENS Registry address on the wagmi `Chain` object (`chain.contracts.ensRegistry`). + +- [#1574](https://github.com/wevm/wagmi/pull/1574) [`ecde3d10`](https://github.com/wevm/wagmi/commit/ecde3d1029ccdf90e2853ba0e9ae4f5f4ebb9c4c) Thanks [@jxom](https://github.com/jxom)! - Added the following chains: + + - `iotex` + - `iotexTestnet` + - `zkSync` + - `zkSyncTestnet` + +## 0.8.5 + +### Patch Changes + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Added the following chains: + + - `evmos` + - `evmosTestnet` + - `gnosis` + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Updated Goerli symbol to `"ETH"`. + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Updated Arbitrum Goerli RPC and Block Explorer. + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where connecting to MetaMask may return with a stale address. + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Removed ENS registry for Sepolia. + +## 0.8.4 + +### Patch Changes + +- [#1508](https://github.com/wevm/wagmi/pull/1508) [`0b50b62f`](https://github.com/wevm/wagmi/commit/0b50b62f7389619e429509a3e337e451e823b059) Thanks [@jxom](https://github.com/jxom)! - Updated `@wagmi/chains` to `0.1.3`. + +- [#1504](https://github.com/wevm/wagmi/pull/1504) [`11b8b794`](https://github.com/wevm/wagmi/commit/11b8b794fbfd4a2b40f39962e2758e9fbf48cb54) Thanks [@tmm](https://github.com/tmm)! - Converted ethers custom "ACTION_REJECTED" error to standard RPC Error. + +## 0.8.3 + +### Patch Changes + +- [#1431](https://github.com/wevm/wagmi/pull/1431) [`af28f8f9`](https://github.com/wevm/wagmi/commit/af28f8f9cfc227e7c391927fdb934183edb5c2dc) Thanks [@jxom](https://github.com/jxom)! - Re-export connectors from `@wagmi/connectors` + +- [#1431](https://github.com/wevm/wagmi/pull/1431) [`af28f8f9`](https://github.com/wevm/wagmi/commit/af28f8f9cfc227e7c391927fdb934183edb5c2dc) Thanks [@jxom](https://github.com/jxom)! - Added `LedgerConnector` connector + +## 0.8.2 + +### Patch Changes + +- [#1442](https://github.com/wevm/wagmi/pull/1442) [`cde15289`](https://github.com/wevm/wagmi/commit/cde152899c758dea10787412b0aef669ed7202b2) Thanks [@0xproflupin](https://github.com/0xproflupin)! - Added Phantom wallet support to `InjectedConnector` + +- [#1448](https://github.com/wevm/wagmi/pull/1448) [`c6075f3a`](https://github.com/wevm/wagmi/commit/c6075f3a16885d850ad2656272351f9517c9f67b) Thanks [@tmm](https://github.com/tmm)! - Updated [ABIType](https://github.com/wevm/abitype) version. + +- [#1444](https://github.com/wevm/wagmi/pull/1444) [`310a8bc4`](https://github.com/wevm/wagmi/commit/310a8bc428ce4e7f68377f581b45dcdd64381cce) Thanks [@jxom](https://github.com/jxom)! - Assert that a `connector` exists before invoking the callback in `watchSigner`. + +- [#1434](https://github.com/wevm/wagmi/pull/1434) [`100e2a3b`](https://github.com/wevm/wagmi/commit/100e2a3b22f4602716554487b1d98738e053be76) Thanks [@tmm](https://github.com/tmm)! - Updated `MockConnector` `chainId` behavior to default to first chain from `chains` if not provided in `options`. + +## 0.8.1 + +### Patch Changes + +- [#1437](https://github.com/wevm/wagmi/pull/1437) [`c34a3dc6`](https://github.com/wevm/wagmi/commit/c34a3dc6396e6473d9f0505fad88ec910f8f5275) Thanks [@jxom](https://github.com/jxom)! - Omitted `"EIP712Domain"` type from `signTypedData` `types` arg since ethers throws an [internal error](https://github.com/ethers-io/ethers.js/blob/c80fcddf50a9023486e9f9acb1848aba4c19f7b6/packages/hash/src.ts/typed-data.ts#L466) if you include it. + +- [#1445](https://github.com/wevm/wagmi/pull/1445) [`51dd53cb`](https://github.com/wevm/wagmi/commit/51dd53cba3fe0f79fa1393270b738194577ddf54) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where the wagmi client wouldn't rehydrate the store in local storage when `autoConnect` is truthy. + +## 0.8.0 + +### Minor Changes + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: the shape of the `Chain` type has been modified. + + #### RPC URLs + + The `rpcUrls` shape has changed to include an array of URLs, and also the transport method (`http` or `webSocket`): + + ```diff + type Chain = { + ... + rpcUrls: { + - [key: string]: string + + [key: string]: { + + http: string[] + + webSocket: string[] + + } + } + ... + } + ``` + + Note that you will also need to ensure that usage is migrated: + + ```diff + - const rpcUrl = mainnet.rpcUrls.alchemy + + const rpcUrl = mainnet.rpcUrls.alchemy.http[0] + ``` + + #### Contracts + + The `multicall` and `ens` attributes have been moved into the `contracts` object: + + ```diff + type Contract = { + address: Address + blockCreated?: number + } + + type Chain = { + ... + - multicall: Contract + - ens: Contract + + contracts: { + + multicall3: Contract + + ensRegistry: Contract + + } + ... + } + ``` + + Note that you will also need to ensure that usage is migrated: + + ```diff + - const multicallContract = mainnet.multicall + + const multicallContract = mainnet.contracts.multicall3 + ``` + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: Upgraded `@coinbase/wallet-sdk` peer dependency to `3.6.0`. + + **Migration steps**: Update `@coinbase/wallet-sdk` to `^3.6.0`. + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: Removed the `wait` argument on `waitForTransaction`. Use the transaction `hash` instead. + + ```diff + const data = await waitForTransaction({ + - wait: transaction.wait + + hash: transaction.hash + }) + ``` + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: With the introduction of the [`@wagmi/core/chains` entrypoint](/core/chains), `@wagmi/core` no longer exports the following: + + - `chain` + - `allChains` + - `defaultChains` + - `defaultL2Chains` + - `chainId` + - `etherscanBlockExplorers` + - `alchemyRpcUrls`, `infuraRpcUrls`, `publicRpcUrls` + + Read below for migration steps. + + #### Removed `chain` + + The `chain` export has been removed. `@wagmi/core` now only exports the `mainnet` & `goerli` chains. If you need to use an alternative chain (`polygon`, `optimism`, etc), you will need to import it from the [`@wagmi/core/chains` entrypoint](/core/chains). + + ```diff + import { + - chain + configureChains + } from '@wagmi/core' + + import { mainnet, polygon, optimism } from '@wagmi/core/chains' + + const { ... } = configureChains( + - [chain.mainnet, chain.polygon, chain.optimism], + + [mainnet, polygon, optimism], + { + ... + } + ) + ``` + + #### Removed `allChains` + + The `allChains` export has been removed. If you need a list of all chains, you can utilize [`@wagmi/core/chains` entrypoint](/core/chains). + + ```diff + - import { allChains } from '@wagmi/core' + + import * as allChains from '@wagmi/core/chains' + + const { ... } = configureChains(allChains, ...) + ``` + + #### Removed `defaultChains` & `defaultL2Chains` + + The `defaultChains` & `defaultL2Chains` exports have been removed. If you still need the `defaultChains` or `defaultL2Chains` exports, you can build them yourself: + + ```diff + - import { defaultChains } from '@wagmi/core' + + import { mainnet, goerli } from '@wagmi/core/chains' + + + const defaultChains = [mainnet, goerli] + ``` + + > The `defaultChains` export was previously populated with `mainnet` & `goerli`. + + ```diff + - import { defaultL2Chains } from '@wagmi/core' + + import { + + arbitrum, + + arbitrumGoerli, + + polygon, + + polygonMumbai, + + optimism, + + optimismGoerli + + } from '@wagmi/core/chains' + + + const defaultL2Chains = [ + + arbitrum, + + arbitrumGoerli, + + polygon, + + polygonMumbai, + + optimism + + optimismGoerli + + ] + ``` + + > The `defaultL2Chains` export was previously populated with `arbitrum` & `optimism`. + + #### Removed `chainId` + + The `chainId` export has been removed. You can extract a chain ID from the chain itself. + + ```diff + - import { chainId } from '@wagmi/core' + + import { mainnet, polygon, optimism } from '@wagmi/core/chains' + + -const mainnetChainId = chainId.mainnet + -const polygonChainId = chainId.polygon + -const optimismChainId = chainId.optimism + +const mainnetChainId = mainnet.chainId + +const polygonChainId = polygon.chainId + +const optimismChainId = optimism.chainId + ``` + + #### Removed `etherscanBlockExplorers` + + The `etherscanBlockExplorers` export has been removed. You can extract a block explorer from the chain itself. + + ```diff + - import { etherscanBlockExplorers } from '@wagmi/core' + + import { mainnet, polygon, optimism } from '@wagmi/core/chains' + + -const mainnetEtherscanBlockExplorer = etherscanBlockExplorers.mainnet + -const polygonEtherscanBlockExplorer = etherscanBlockExplorers.polygon + -const optimismEtherscanBlockExplorer = etherscanBlockExplorers.optimism + +const mainnetEtherscanBlockExplorer = mainnet.blockExplorer + +const polygonEtherscanBlockExplorer = polygon.blockExplorer + +const optimismEtherscanBlockExplorer = optimism.blockExplorer + ``` + + #### Removed `alchemyRpcUrls`, `infuraRpcUrls` & `publicRpcUrls` + + The `alchemyRpcUrls`, `infuraRpcUrls` & `publicRpcUrls` exports have been removed. You can extract a RPC URL from the chain itself. + + ```diff + - import { alchemyRpcUrls, infuraRpcUrls, publicRpcUrls } from '@wagmi/core' + + import { mainnet } from '@wagmi/core/chains' + + -const mainnetAlchemyRpcUrl = alchemyRpcUrls.mainnet + -const mainnetInfuraRpcUrl = infuraRpcUrls.mainnet + -const mainnetOptimismRpcUrl = publicRpcUrls.mainnet + +const mainnetAlchemyRpcUrl = mainnet.rpcUrls.alchemy + +const mainnetInfuraRpcUrl = mainnet.rpcUrls.infura + +const mainnetOptimismRpcUrl = mainnet.rpcUrls.optimism + ``` + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: Changed `waitForTransaction` behavior to throw an error if the transaction reverted. + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - Updated errors to use `cause` instead of `internal` + +### Patch Changes + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - `waitForTransaction` now respects repriced (sped up) transactions. + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - Export `getClient` + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - `waitForTransaction` now throws an error for cancelled or replaced transactions. + +## 0.7.9 + +### Patch Changes + +- [#1411](https://github.com/wevm/wagmi/pull/1411) [`659be184`](https://github.com/wevm/wagmi/commit/659be1840c613ce9f7aca9ac96694c4f60da4a66) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where block invalidation was not properly disabled when setting `enabled: false`. + +## 0.7.8 + +### Patch Changes + +- [#1406](https://github.com/wevm/wagmi/pull/1406) [`4f18c450`](https://github.com/wevm/wagmi/commit/4f18c450a4d7952bfcfa6c533348ffbe55893d3c) Thanks [@tmm](https://github.com/tmm)! - Function for selecting the [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) Ethereum Provider to target. Defaults to `() => typeof window !== 'undefined' ? window.ethereum : undefined`. + + ```ts + import { InjectedConnector } from "@wagmi/core/connectors/injected"; + + const connector = new InjectedConnector({ + options: { + name: "My Injected Wallet", + getProvider: () => + typeof window !== "undefined" ? window.myInjectedWallet : undefined, + }, + }); + ``` + +## 0.7.7 + +### Patch Changes + +- [#1386](https://github.com/wevm/wagmi/pull/1386) [`206a2adb`](https://github.com/wevm/wagmi/commit/206a2adbb4ee5149a364543b34612050ccf78c21) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `persister` would still use `window.localStorage` instead of the wagmi `storage`. + +- [#1376](https://github.com/wevm/wagmi/pull/1376) [`a70a9528`](https://github.com/wevm/wagmi/commit/a70a9528f93f4d7fea28b7652751dfef2dcacf9b) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where `switchChain` on `WalletConnectConnector` would not resolve. + +- [#1386](https://github.com/wevm/wagmi/pull/1386) [`206a2adb`](https://github.com/wevm/wagmi/commit/206a2adbb4ee5149a364543b34612050ccf78c21) Thanks [@jxom](https://github.com/jxom)! - Added `serialize`/`deserialize` as config options to `createStorage`. + +- [#1392](https://github.com/wevm/wagmi/pull/1392) [`88afc849`](https://github.com/wevm/wagmi/commit/88afc84978afe9689ab7364633e4422ecd7699ea) Thanks [@tmm](https://github.com/tmm)! - Added check for active connector when connecting + +## 0.7.6 + +### Patch Changes + +- [#1384](https://github.com/wevm/wagmi/pull/1384) [`027e88d6`](https://github.com/wevm/wagmi/commit/027e88d6e5f8d028d46ee78aec8500701e0173d9) Thanks [@tmm](https://github.com/tmm)! - Fixed issue reconnecting after disconnect with `MetaMaskConnector` in MetaMask mobile browser. + +## 0.7.5 + +### Patch Changes + +- [`1169914a`](https://github.com/wevm/wagmi/commit/1169914a0f0ad2810ca1c536b1f1bc6c20f2c1be) Thanks [@jxom](https://github.com/jxom)! - Use `get_accounts` for `getSigner` in InjectedConnector + +## 0.7.4 + +### Patch Changes + +- [#1309](https://github.com/wevm/wagmi/pull/1309) [`1f4a4261`](https://github.com/wevm/wagmi/commit/1f4a4261247b1d3a90e3123157bc851a35d49b9c) Thanks [@tmm](https://github.com/tmm)! - Fixed internal type + +## 0.7.3 + +### Patch Changes + +- [#1294](https://github.com/wevm/wagmi/pull/1294) [`b2f88949`](https://github.com/wevm/wagmi/commit/b2f88949f32aabaf13f318472648cd51a8b7f2e7) Thanks [@tmm](https://github.com/tmm)! - Set `abi` return type value for `prepareContractWrite` as more permissive when not inferrable as `Abi`. + +## 0.7.2 + +### Patch Changes + +- [`e9f806b6`](https://github.com/wevm/wagmi/commit/e9f806b652ba62effb3ddac464815e447fc287f6) Thanks [@tmm](https://github.com/tmm)! - Bumped abitype and zustand versions. + +## 0.7.1 + +### Patch Changes + +- [#1272](https://github.com/wevm/wagmi/pull/1272) [`1f7fc41`](https://github.com/wevm/wagmi/commit/1f7fc419f7960bbdc51dfa85c2f33b89f1ecc1bf) Thanks [@tmm](https://github.com/tmm)! - Fixed ethers import path + +## 0.7.0 + +### Minor Changes + +- [#1202](https://github.com/wevm/wagmi/pull/1202) [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Removed the following deprecated chains: + + - `ropsten` + - `rinkeby` + - `kovan` + - `optimismKovan` + - `arbitrumRinkeby` + + If you feel you still need to include one of these testnets in your application, you will have to define it manually: + + ```diff + -import { rinkeby } from 'wagmi' + +import { Chain } from 'wagmi' + + +export const rinkeby: Chain = { + + id: 4, + + name: 'Rinkeby', + + network: 'rinkeby', + + nativeCurrency: { name: 'Rinkeby Ether', symbol: 'ETH', decimals: 18 }, + + rpcUrls: { + + alchemy: 'https://eth-rinkeby.alchemyapi.io/v2', + + default: 'https://rpc.ankr.com/eth_rinkeby', + + infura: 'https://rinkeby.infura.io/v3', + + public: 'https://rpc.ankr.com/eth_rinkeby', + + }, + + blockExplorers: { + + etherscan: 'https://rinkeby.etherscan.io', + + default: 'https://rinkeby.etherscan.io', + + }, + + ens: { + + address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + + }, + + multicall: { + + address: '0xca11bde05977b3631167028862be2a173976ca11', + + blockCreated: 10299530, + + }, + + testnet: true, + } + ``` + + You can reference these removed chains [here](https://github.com/wevm/wagmi/blob/389765f7d9af063ab0df07389a2bbfbc10a41060/packages/core/src/constants/chains.ts). + +- [#1202](https://github.com/wevm/wagmi/pull/1202) [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `addressOrName` renamed to `address` for `fetchBalance` and `fetchEnsAvatar`. + + ```diff + const result = await fetchBalance({ + - addressOrName: '0x…', + + address: '0x…', + }) + ``` + + If you were using an ENS name instead of an address, you can resolve the name to an address before passing it to the action. + + ```diff + + const { data: address } = await fetchEnsAddress({ name: 'example.eth' }) + const result = await fetchBalance({ + - addressOrName: 'example.eth', + + address, + }) + ``` + +- [#1202](https://github.com/wevm/wagmi/pull/1202) [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Made `apiKey` required on `infuraProvider` and `alchemyProvider`. + + ```diff + import { configureChains } from 'wagmi' + + const config = configureChains(defaultChains, [ + - alchemyProvider(), + + alchemyProvider({ apiKey: process.env.ALCHEMY_API_KEY }) + ]) + ``` + + You can find your Alchemy API key from the [Alchemy Dashboard](https://dashboard.alchemyapi.io/), or your Infura API key from the [Infura Dashboard](https://infura.io/login). + +- [#1202](https://github.com/wevm/wagmi/pull/1202) [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2) Thanks [@tmm](https://github.com/tmm)! - Removed CommonJS support + +## 0.6.12 + +### Patch Changes + +- [#1250](https://github.com/wevm/wagmi/pull/1250) [`ce2e0f4`](https://github.com/wevm/wagmi/commit/ce2e0f4a46b8fd1c509ead552012ef4c072a525b) Thanks [@tmm](https://github.com/tmm)! - Added support for Trust Wallet browser extension. + +## 0.6.11 + +### Patch Changes + +- [#1234](https://github.com/wevm/wagmi/pull/1234) [`3ff9303`](https://github.com/wevm/wagmi/commit/3ff930349250f62137cca4ca3b382522882abf8a) Thanks [@tmm](https://github.com/tmm)! - Fixed issue with adding chain to wallet without block explorer URL. + +## 0.6.10 + +### Patch Changes + +- [#1232](https://github.com/wevm/wagmi/pull/1232) [`c0ca509`](https://github.com/wevm/wagmi/commit/c0ca509506dcf6d98b058df549dc761c9a5f3d1c) Thanks [@tmm](https://github.com/tmm)! - Added validation to check that chain is configured for connector when accessing `Signer`. + +## 0.6.9 + +### Patch Changes + +- [#1207](https://github.com/wevm/wagmi/pull/1207) [`c73d463`](https://github.com/wevm/wagmi/commit/c73d463d65c9dbfcfe709187e47323a769589741) Thanks [@lvshaoping007](https://github.com/lvshaoping007)! - Added Kucoin wallet support to `InjectedConnector` + +## 0.6.8 + +### Patch Changes + +- [#1132](https://github.com/wevm/wagmi/pull/1132) [`d41c0d6`](https://github.com/wevm/wagmi/commit/d41c0d650f8c0e54145758685b7604b8909d7ae0) Thanks [@toniocodo](https://github.com/toniocodo)! - Added ERC-4626 ABI + +- [#1201](https://github.com/wevm/wagmi/pull/1201) [`9a07efa`](https://github.com/wevm/wagmi/commit/9a07efaa397d3ba03f2edbe527c359f21e22139a) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where non-checksum addresses did not resolve with an ENS name + +## 0.6.7 + +### Patch Changes + +- [#1174](https://github.com/wevm/wagmi/pull/1174) [`196a458`](https://github.com/wevm/wagmi/commit/196a458f64141e8a9f39c1b1e1af5937f692cb39) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `client.chains` (active connector chains) would be populated when there is no active connector (disconnected user). + +- [#1176](https://github.com/wevm/wagmi/pull/1176) [`389765f`](https://github.com/wevm/wagmi/commit/389765f7d9af063ab0df07389a2bbfbc10a41060) Thanks [@jxom](https://github.com/jxom)! - Migrate away from Alchemy RPC URLs in the public RPC URL list + +## 0.6.6 + +### Patch Changes + +- [`81ce9e6`](https://github.com/wevm/wagmi/commit/81ce9e64d85f7d01370324c1a529988a0919894f) Thanks [@jxom](https://github.com/jxom)! - Add `isPortal` to injected MetaMask flags. + +- [`c2c0109`](https://github.com/wevm/wagmi/commit/c2c01096ef4cd0ffadbb49062969c208604c6194) Thanks [@jxom](https://github.com/jxom)! - Add etherscan block explorer to Optimism Goerli + +## 0.6.5 + +### Patch Changes + +- [#1162](https://github.com/wevm/wagmi/pull/1162) [`30335b3`](https://github.com/wevm/wagmi/commit/30335b3199fb425e398e9c492b50c68d5e2ade7e) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where non-indexed event parameter types were set to `null`. + +- [#1162](https://github.com/wevm/wagmi/pull/1162) [`30335b3`](https://github.com/wevm/wagmi/commit/30335b3199fb425e398e9c492b50c68d5e2ade7e) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where `useContractReads` and `useContractInfiniteReads` types were slowing down TypeScript compiler. + +## 0.6.4 + +### Patch Changes + +- [#1103](https://github.com/wevm/wagmi/pull/1103) [`651eda0`](https://github.com/wevm/wagmi/commit/651eda06384bd0955268427f898e9337b2dc5a31) Thanks [@tmm](https://github.com/tmm)! - Bumped `abitype` dependency. + +## 0.6.3 + +### Patch Changes + +- [#1086](https://github.com/wevm/wagmi/pull/1086) [`4e28d2a`](https://github.com/wevm/wagmi/commit/4e28d2ad4c2e6b3479b728563040b9529463cbcf) Thanks [@tmm](https://github.com/tmm)! - Exposed module types. + +## 0.6.2 + +### Patch Changes + +- [#1080](https://github.com/wevm/wagmi/pull/1080) [`3be5e8b`](https://github.com/wevm/wagmi/commit/3be5e8b01e58ed40cc9dab7ef9533c0197cb74d0) Thanks [@tmm](https://github.com/tmm)! - Added `abitype` to `dependencies` so types ship correctly. + +## 0.6.1 + +### Patch Changes + +- [#1074](https://github.com/wevm/wagmi/pull/1074) [`8db807f`](https://github.com/wevm/wagmi/commit/8db807f16149aa278c2a7db9ee5245431db12173) Thanks [@IljaDaderko](https://github.com/IljaDaderko)! - Exported `EventListener` type + +## 0.6.0 + +### Minor Changes + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: `watchSigner` now requires an arguments object (that accepts an optional `chainId`) as it's first parameter. + + ```diff + import { watchSigner } from `@wagmi/core` + + -watchSigner(signer => { + +watchSigner({}, signer => { + console.log('new signer!', signer) + }) + ``` + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: `prepareSendTransaction` now throws when a `chainId` is specified and the end-user is on a different chain id (the wrong network). + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `addressOrName` and `contractInterface` renamed to `address` and `abi` respectively for contract actions: `getContract`, `multicall`, `prepareWriteContract`, `readContract`, `readContracts`, `watchContractEvent`, `watchMulticall`, `watchReadContract`, `watchReadContracts`, `writeContract`. + + ```diff + import { readContract } from '@wagmi/core' + + const result = await readContract({ + - addressOrName: '0x…', + + address: '0x…', + - contractInterface: […] as const, + + abi: […] as const, + functionName: 'balanceOf', + args: ['0x…'], + }) + ``` + + If you were using an ENS name instead of an address, you can resolve the name to an address before passing it to the action. + + ```diff + - import { readContract } from '@wagmi/core' + + import { fetchEnsAddress, readContract } from '@wagmi/core' + + + const address = await fetchEnsAddress('example.eth') + const result = await readContract({ + - addressOrName: 'example.eth', + + address, + abi: […] as const, + functionName: 'balanceOf', + args: ['0x…'], + }) + ``` + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: `prepareWriteContract` now throws when a `chainId` is specified and the end-user is on a different chain id (the wrong network). + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: `prepareSendTransaction` now only accepts a `signer` instead of `signerOrProvider`. + + This is to reach parity with `prepareWriteContract`. + + If no `signer` is provided, wagmi will use the signer that is currently connected. If no user is connected, then `prepareWriteContract` will throw an error. + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `args` config option must now be an array for the following actions: `readContract`, `writeContract`, `prepareWriteContract`, `multicall`, `readContracts`, `watchMulticall`, and `watchReadContracts`. + + ```diff + import { readContract } from '@wagmi/core' + + const result = await readContract({ + address: '0x…', + abi: […], + functionName: 'balanceOf', + - args: '0x…', + + args: ['0x…'], + }) + ``` + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `watchContractEvent` now accepts a configuration object and callback instead of positional arguments. + + ```diff + import { watchContractEvent } from '@wagmi/core' + + - const unsubscribe = watchContractEvent( + - { + - address: '0x…', + - abi: […], + - }, + - 'Transfer', + - (from, to, tokenId) => { + - // ... + - }, + - { once: true }, + - ) + + const unsubscribe = watchContractEvent( + + { + + address: '0x…', + + abi: […], + + eventName: 'Transfer', + + once: true, + + }, + + (from, to, tokenId) => { + + // ... + + }, + + ) + ``` + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Updated TypeScript version to `typescript@>=4.7.4`. + + `@wagmi/core` can now infer types based on [ABI](https://docs.soliditylang.org/en/v0.8.15/abi-spec.html#json) and [EIP-712](https://eips.ethereum.org/EIPS/eip-712) Typed Data definitions, giving you full end-to-end type-safety from your contracts to your frontend and incredible developer experience (e.g. autocomplete contract function names and catch misspellings, type contract function arguments, etc.). + + For this to work, you must upgrade to `typescript@>=4.7.4`. Why is TypeScript v4.7.4 or greater necessary? TypeScript 4.7.4 introduced the ability to [extend constraints on inferred type variables](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#extends-constraints-on-infer-type-variables), which is used extensively to help narrow types for ABIs. Good news! When upgrading TypeScript from 4.6 to 4.7 there are likely no [breaking changes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#breaking-changes) for your set up. + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Updated TypeScript generics for contract interaction and typed data actions. + + Adding a [const assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) to `abi` allows TypeScript to infer `functionName`, `args`, `overrides`, and return types for functions, and `eventName` and `listener` types for events. + + ```diff + import { readContract } from '@wagmi/core' + + const result = await readContract({ + address: '0x…', + - abi: […], + + abi: […] as const, + functionName: 'balanceOf', // will autocomplete and catch typos + args: ['0x…'], // inferred based on `functionName` + }) + result // inferred based on `functionName` + ``` + + This works for the following actions: `readContract`, `writeContract`, `prepareWriteContract`, `multicall`, `readContracts`, `watchMulticall`, `watchReadContracts`, and `watchContractEvent`. + + Adding a [const assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) to `signTypedData`'s config option, `types`, allows TypeScript to infer `value`. + + ```diff + import { signTypedData } from '@wagmi/core' + + const result = await signTypedData({ + domain: { + name: 'Ether Mail', + version: '1', + chainId: 1, + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', + }, + types: { + Person: [ + { name: 'name', type: 'string' }, + { name: 'wallet', type: 'address' }, + ], + Mail: [ + { name: 'from', type: 'Person' }, + { name: 'to', type: 'Person' }, + { name: 'contents', type: 'string' }, + ], + - }, + + } as const, + value: { // `value` is inferred based on `types` + from: { + name: 'Cow', + wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + }, + to: { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', + }, + contents: 'Hello, Bob!', + }, + }) + ``` + +### Patch Changes + +- [#1061](https://github.com/wevm/wagmi/pull/1061) [`a4ffe8b`](https://github.com/wevm/wagmi/commit/a4ffe8b25516d5504685ae94579da4cd8c409329) Thanks [@alecananian](https://github.com/alecananian)! - Added Arbitrum Goerli Arbiscan block explorer + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - The `fetchSigner` action now accepts an optional `chainId` to use for signer initialization as an argument. + + ```tsx + import { fetchSigner } from "@wagmi/core"; + import { optimism } from "@wagmi/core/chains"; + + // ... + + fetchSigner({ chainId: optimism.id }); + ``` + +- [#1048](https://github.com/wevm/wagmi/pull/1048) [`ed13074`](https://github.com/wevm/wagmi/commit/ed130747c0f28c1d9980a1328883e4000a60455e) Thanks [@Max-3-7](https://github.com/Max-3-7)! - Added support for Avalanche core wallet + +- [#1046](https://github.com/wevm/wagmi/pull/1046) [`ab9ecaa`](https://github.com/wevm/wagmi/commit/ab9ecaa74dfa4324279e167dd7e348319ef7d35d) Thanks [@jxom](https://github.com/jxom)! - make ethers block format validator compatible with Celo + +- [#1050](https://github.com/wevm/wagmi/pull/1050) [`73d4d47`](https://github.com/wevm/wagmi/commit/73d4d47bc679f4f9a1cf46010fe2bf858c9d0b5c) Thanks [@jxom](https://github.com/jxom)! - update dependencies + + - `zustand@4.1.1` + +## 0.5.8 + +### Patch Changes + +- [`8cb07462`](https://github.com/wevm/wagmi/commit/8cb07462acc3c5637398d11d2451f8b8e330d553) Thanks [@jxom](https://github.com/jxom)! - Added `chainId` as an argument to `watchBlockNumber`. + +* [`53c1a474`](https://github.com/wevm/wagmi/commit/53c1a4747d03b685e8cfbf55361fc2a56777fb06) Thanks [@tmm](https://github.com/tmm)! - Added missing `decimals` option to `Connector` `watchAsset` + +- [`4d74dd4f`](https://github.com/wevm/wagmi/commit/4d74dd4ff827ba5c43c3546a218f38cee45ea76a) Thanks [@jxom](https://github.com/jxom)! - Support ERC20 contracts that represent strings as bytes32 + +## 0.5.7 + +### Patch Changes + +- [`aa51bc4d`](https://github.com/wevm/wagmi/commit/aa51bc4dc5683bf0178597d2fdb8f2e9d82e7970) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue in `CoinbaseWalletConnector` where the browser extension would unintendedly reset the network when the browser is refreshed. + +* [#955](https://github.com/wevm/wagmi/pull/955) [`e326cd80`](https://github.com/wevm/wagmi/commit/e326cd80fe65267db623eb6c80ccdd75572914cf) Thanks [@0xFlicker](https://github.com/0xFlicker)! - Added Infura RPC URL for Sepolia + +- [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `useProvider` & `getProvider` were not returning referentially equal providers. + +* [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where the `watch` option was not respecting the neighboring `chainId` option in `useBlockNumber`. + +- [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where block listeners (via `watch`) were firing excessively on L2 chains. + +## 0.5.6 + +### Patch Changes + +- [#936](https://github.com/wevm/wagmi/pull/936) [`3329d1f`](https://github.com/wevm/wagmi/commit/3329d1f5880431566e14ac1640f48d0975aec4c2) Thanks [@jxom](https://github.com/jxom)! - Added the ability to provide a custom logger to override how logs are broadcasted to the consumer in wagmi. + + A custom logger can be provided to the wagmi client via `logger`. + + ### API + + ```tsx + logger?: { + warn: typeof console.warn | null + } + ``` + + ### Examples + + **Passing in a custom logger** + + You can pass in a function to define your own custom logger. + + ```diff + + import { logWarn } from './logger'; + + const client = createClient({ + ... + + logger: { + + warn: message => logWarn(message) + + } + ... + }) + ``` + + **Disabling a logger** + + You can disable a logger by passing `null` as the value. + + ```diff + const client = createClient({ + ... + + logger: { + + warn: null + + } + ... + }) + ``` + +* [#889](https://github.com/wevm/wagmi/pull/889) [`27788ed`](https://github.com/wevm/wagmi/commit/27788ed989b5dc26849c7945fb91a92e56766018) Thanks [@jxom](https://github.com/jxom)! - Make multicall & readContracts more error robust + +## 0.5.5 + +### Patch Changes + +- [#912](https://github.com/wevm/wagmi/pull/912) [`e529e12`](https://github.com/wevm/wagmi/commit/e529e125c713ed3ef24a59c6bf226fe4deee7ac9) Thanks [@zouhangwithsweet](https://github.com/zouhangwithsweet)! - Added BitKeep to injected flags + +- [#912](https://github.com/wevm/wagmi/pull/910) Thanks [@mytangying](https://github.com/zouhangwithsweet)! - Added MathWallet to injected flags + +- [#904](https://github.com/wevm/wagmi/pull/904) [`c231058`](https://github.com/wevm/wagmi/commit/c23105850f335f8798031e14c7098b7dee8c2975) Thanks [@jxom](https://github.com/jxom)! - Minimized contract interface returned from `prepareWriteContract`. + +## 0.5.4 + +### Patch Changes + +- [#852](https://github.com/wevm/wagmi/pull/852) [`c3192d0`](https://github.com/wevm/wagmi/commit/c3192d0663aa332ae9edfd9dd49b333454013ab7) Thanks [@skeithc](https://github.com/skeithc)! - Added support for the Sepolia testnet + +## 0.5.3 + +### Patch Changes + +- [#835](https://github.com/wevm/wagmi/pull/835) [`1b85e54`](https://github.com/wevm/wagmi/commit/1b85e54ae654e2564cf5bc2dae6411fe0a25875c) Thanks [@jxom](https://github.com/jxom)! - Update `@coinbase/wallet-sdk` to `3.4.1` + +* [#834](https://github.com/wevm/wagmi/pull/834) [`9655879`](https://github.com/wevm/wagmi/commit/96558793b0319df47aefafa6b7b9c959068d491b) Thanks [@jxom](https://github.com/jxom)! - Update zustand to `4.0.0` + +## 0.5.2 + +### Patch Changes + +- [#823](https://github.com/wevm/wagmi/pull/823) [`10b8b78`](https://github.com/wevm/wagmi/commit/10b8b78605b7246b2c55b8d69f96663906e5cd20) Thanks [@tmm](https://github.com/tmm)! - Add Optimism Goerli to `chain` lookup. + +## 0.5.1 + +### Patch Changes + +- [#767](https://github.com/wevm/wagmi/pull/767) [`e9392f3`](https://github.com/wevm/wagmi/commit/e9392f396e48e928bd9d2522e3ad671c589f08cb) Thanks [@klyap](https://github.com/klyap)! - Add Optimism Goerli chain ahead of [Kovan deprecation](https://dev.optimism.io/kovan-to-goerli). + +* [#817](https://github.com/wevm/wagmi/pull/817) [`7e5cac7`](https://github.com/wevm/wagmi/commit/7e5cac75815dcd8aa563462342a4853fc5207735) Thanks [@alecananian](https://github.com/alecananian)! - Added custom name mapping for 1inch Wallet injected provider + +- [#806](https://github.com/wevm/wagmi/pull/806) [`0b34e56`](https://github.com/wevm/wagmi/commit/0b34e56db97e6dcdb71088e0149b2d55ebc604a5) Thanks [@vmichalik](https://github.com/vmichalik)! - Fix canonical testnet native asset symbols by changing them to ETH + +* [#778](https://github.com/wevm/wagmi/pull/778) [`0892908`](https://github.com/wevm/wagmi/commit/08929084eeeba1a3a55aa098fa9d92a243685ad5) Thanks [@0xcadams](https://github.com/0xcadams)! - Add Arbitrum Goerli chain. + +## 0.5.0 + +### Minor Changes + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The configuration passed to the `sendTransaction` action now needs to be: + + - prepared with the `prepareSendTransaction` action **(new functionality)**, or + - recklessly unprepared **(previous functionality)** + + > Why? [Read here](https://wagmi.sh/docs/prepare-hooks) + + ### Prepared usage + + ```diff + import { prepareSendTransaction, sendTransaction } from '@wagmi/core' + + +const config = await prepareSendTransaction({ + + request: { + + to: 'moxey.eth', + + value: parseEther('1'), + + } + +}) + + const result = await sendTransaction({ + - request: { + - to: 'moxey.eth', + - value: parseEther('1') + - } + + ...config + }) + ``` + + ### Recklessly unprepared usage + + It is possible to use `sendTransaction` without preparing the configuration first by passing `mode: 'recklesslyUnprepared'`. + + ```diff + import { sendTransaction } from '@wagmi/core' + + const result = await sendTransaction({ + + mode: 'recklesslyUnprepared', + request: { + to: 'moxey.eth', + value: parseEther('1'), + } + }) + ``` + +* [#760](https://github.com/wevm/wagmi/pull/760) [`d8af6bf`](https://github.com/wevm/wagmi/commit/d8af6bf50885aec110ae4d64716642453aa27896) Thanks [@tmm](https://github.com/tmm)! - **Breaking:** `alchemyProvider` and `infuraProvider` now use a generic `apiKey` configuration option instead of `alchemyId` and `infuraId`. + + ```diff + import { alchemyProvider } from '@wagmi/core/providers/alchemy' + import { infuraProvider } from '@wagmi/core/providers/infura' + + alchemyProvider({ + - alchemyId: 'yourAlchemyApiKey', + + apiKey: 'yourAlchemyApiKey', + }) + + infuraProvider({ + - infuraId: 'yourInfuraApiKey', + + apiKey: 'yourInfuraApiKey', + }) + ``` + +- [#727](https://github.com/wevm/wagmi/pull/727) [`ac3b9b8`](https://github.com/wevm/wagmi/commit/ac3b9b87f80cb45b65d003f09d916d7d1427a62e) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Moved the `pollingInterval` config option from the chain provider config to `configureChains` config. + + ```diff + const { chains, provider } = configureChains( + [chain.mainnet, chain.polygon], + [ + - alchemyProvider({ apiKey, pollingInterval: 5000 }), + - publicProvider({ pollingInterval: 5000 }) + + alchemyProvider({ apiKey }), + + publicProvider() + ], + + { pollingInterval: 5000 } + ) + ``` + +* [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** The `sendTransaction` action now returns an object only consisting of `hash` & `wait`, and not the full [`TransactionResponse`](https://docs.ethers.io/v5/api/providers/types/#providers-TransactionResponse). + + If you require the full `TransactionResponse`, you can use `fetchTransaction`: + + ```diff + import { sendTransaction, fetchTransaction } from '@wagmi/core' + + const { + hash, + wait, + - ...transaction + } = sendTransaction(...) + + +const transaction = fetchTransaction({ hash }) + ``` + + > Why? The old implementation of `sendTransaction` created a long-running async task, causing [UX pitfalls](https://wagmi.sh/docs/prepare-hooks#ux-pitfalls-without-prepare-hooks) when invoked in a click handler. + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: If a `chainId` is passed to `writeContract` or `sendTransaction`, it will no longer attempt to switch chain before sending the transaction. Instead, it will throw an error if the user is on the wrong chain. + + > Why? + > + > - Eagerly prompting to switch chain in these actions created a long-running async task that that makes [iOS App Links](https://wagmi.sh/docs/prepare-hooks#ios-app-link-constraints) vulnerable. + > - Not all wallets support programmatic chain switching. + +### Patch Changes + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The configuration passed to the `writeContract` action now needs to be: + + - prepared with the `prepareWriteContract` action **(new functionality)**, or + - recklessly unprepared **(previous functionality)** + + > Why? [Read here](https://wagmi.sh/docs/prepare-hooks) + + ### Prepared usage + + ```diff + import { prepareWriteContract, writeContract } from '@wagmi/core' + + const tokenId = 69 + + +const config = await prepareWriteContract({ + + addressOrName: '0x...', + + contractInterface: wagmiAbi, + + functionName: 'mint', + + args: [tokenId] + +}) + + const result = await writeContract({ + - addressOrName: '0x...', + - contractInterface: wagmiAbi, + - functionName: 'mint', + - args: [tokenId], + + ...config + }) + ``` + + ### Recklessly unprepared usage + + It is possible to use `writeContract` without preparing the configuration first by passing `mode: 'recklesslyUnprepared'`. + + ```diff + import { writeContract } from '@wagmi/core' + + const tokenId = 69 + + const result = await writeContract({ + + mode: 'recklesslyUnprepared', + addressOrName: '0x...', + contractInterface: wagmiAbi, + functionName: 'mint', + args: [tokenId], + }) + ``` + +* [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - Added the `prepareSendTransaction` hook that prepares the parameters required for sending a transaction. + + It returns config to be passed through to `sendTransaction`. + + ```ts + import { prepareSendTransaction, sendTransaction } from "@wagmi/core"; + + const config = await prepareSendTransaction({ + request: { + to: "moxey.eth", + value: parseEther("1"), + }, + }); + const result = await sendTransaction(config); + ``` + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - Added the `prepareWriteContract` hook that prepares the parameters required for a contract write transaction. + + It returns config to be passed through to `writeContract`. + + Example: + + ```tsx + import { prepareWriteContract, writeContract } from "@wagmi/core"; + + const config = await prepareWriteContract({ + addressOrName: "0x...", + contractInterface: wagmiAbi, + functionName: "mint", + }); + const result = await writeContract(config); + ``` + +* [#739](https://github.com/wevm/wagmi/pull/739) [`c2295a5`](https://github.com/wevm/wagmi/commit/c2295a56cc86d02cc6602e2b4557b8ab9a091a3f) Thanks [@tmm](https://github.com/tmm)! - Fix balance formatting for tokens that do not have 18 decimals. + +- [#759](https://github.com/wevm/wagmi/pull/759) [`959953d`](https://github.com/wevm/wagmi/commit/959953d1f5b3e8189bac56de245c62333470d18e) Thanks [@tmm](https://github.com/tmm)! - Added `fetchTransaction` action: + + ```ts + import { fetchTransaction } from "@wagmi/core"; + + const transaction = await fetchTransaction({ + hash: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060", + }); + ``` + +## 0.4.9 + +### Patch Changes + +- [#721](https://github.com/tmm/wagmi/pull/721) [`abea25f`](https://github.com/tmm/wagmi/commit/abea25fd15d81d1ecaec9d3fbd687042ab29b1e6) Thanks [@tmm](https://github.com/tmm)! - Stay connected to existing `client.connector` when `connect` action fails to connect to new connector. + +* [#721](https://github.com/tmm/wagmi/pull/721) [`abea25f`](https://github.com/tmm/wagmi/commit/abea25fd15d81d1ecaec9d3fbd687042ab29b1e6) Thanks [@tmm](https://github.com/tmm)! - Switch `fetchToken` action to multicall and add `name` output property. + +## 0.4.8 + +### Patch Changes + +- [#693](https://github.com/tmm/wagmi/pull/693) [`56e468c`](https://github.com/tmm/wagmi/commit/56e468c3617ec222527bb3c02eadec3ebeff923a) Thanks [@markdalgleish](https://github.com/markdalgleish)! - Fix import errors with Coinbase Wallet SDK in Vite + +## 0.4.7 + +### Patch Changes + +- [#677](https://github.com/tmm/wagmi/pull/677) [`35e4219`](https://github.com/tmm/wagmi/commit/35e42199af9dd346549c1718e144728f55b8d7dd) Thanks [@jxom](https://github.com/jxom)! - Move `parseContractResult` to `@wagmi/core` + +## 0.4.6 + +### Patch Changes + +- [#670](https://github.com/tmm/wagmi/pull/670) [`29a0d21`](https://github.com/tmm/wagmi/commit/29a0d21ee83995559f63542778dfa805f15e7441) Thanks [@tmm](https://github.com/tmm)! - Added ethers-compatible `deepEqual` function. + +## 0.4.5 + +### Patch Changes + +- [#654](https://github.com/tmm/wagmi/pull/654) [`e66530b`](https://github.com/tmm/wagmi/commit/e66530bf4881b3533c528f8c5a5f41be0eab0a64) Thanks [@jxom](https://github.com/jxom)! - fix `multicall` returning nullish data for all calls unexpectedly + +## 0.4.4 + +### Patch Changes + +- [#616](https://github.com/tmm/wagmi/pull/616) [`7a7a17a`](https://github.com/tmm/wagmi/commit/7a7a17a46d4c9e6465cc46a111b5fe8a56109f1b) Thanks [@tmm](https://github.com/tmm)! - Adds `UNSTABLE_shimOnConnectSelectAccount` flag. With this flag and "disconnected" with `shimDisconnect` enabled, the user is prompted to select a different MetaMask account (than the currently connected account) when trying to connect (e.g. `useConnect`/`connect` action). + +## 0.4.3 + +### Patch Changes + +- [#631](https://github.com/tmm/wagmi/pull/631) [`a780e32`](https://github.com/tmm/wagmi/commit/a780e32e91a0072c795fa0b5a6111302768e2a01) Thanks [@tmm](https://github.com/tmm)! - Fix WalletConnect stale session + +## 0.4.2 + +### Patch Changes + +- [#624](https://github.com/tmm/wagmi/pull/624) [`416fa7e`](https://github.com/tmm/wagmi/commit/416fa7ee1f8019ab86e33fb93783ffddecc02c49) Thanks [@jxom](https://github.com/jxom)! - Fix broken `WebSocketProvider` type defs + +## 0.4.1 + +### Patch Changes + +- [#622](https://github.com/tmm/wagmi/pull/622) [`d171581`](https://github.com/tmm/wagmi/commit/d171581464891dd870d97b6232205da0cb152d9b) Thanks [@tmm](https://github.com/tmm)! - Use `domain.chainId` to validate and switch chain before signing in `signTypedData`. + +* [#618](https://github.com/tmm/wagmi/pull/618) [`a5138e8`](https://github.com/tmm/wagmi/commit/a5138e82a00e4d9469ad78c97b2d34200d7f1fbe) Thanks [@tmm](https://github.com/tmm)! - Fix adding chains when using MetaMask mobile app, add `publicRpcUrls` constant, and default to public endpoint when adding chain. + +## 0.4.0 + +### Minor Changes + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `provider` config option is now required on `createClient`. It is recommended to pass the [`provider` given from `configureChains`](https://wagmi.sh/docs/providers/configuring-chains). + + ```diff + import { + createClient, + + defaultChains, + + configureChains + } from 'wagmi' + +import { publicProvider } from 'wagmi/providers/publicProvider' + + +const { provider } = configureChains(defaultChains, [ + + publicProvider + +]) + + const client = createClient({ + + provider + }) + ``` + + If you previously used an ethers.js Provider, you now need to provide your `chains` on the Provider instance: + + ```diff + import { + createClient, + + defaultChains + } from 'wagmi' + import ethers from 'ethers' + + const client = createClient({ + - provider: getDefaultProvider() + + provider: Object.assign(getDefaultProvider(), { chains: defaultChains }) + }) + ``` + +* [`4f8f3c0`](https://github.com/tmm/wagmi/commit/4f8f3c0d65383bd8bbdfc3f1033adfdb11d80ebb) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Use ethereum-lists chains symbols + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** Removed the `chainId` parameter from `connectors` function on `createClient`. + + ```diff + const client = createClient({ + - connectors({ chainId }) { + + connectors() { + ... + } + }) + ``` + + If you previously derived RPC URLs from the `chainId` on `connectors`, you can now remove that logic as `wagmi` now handles RPC URLs internally when used with `configureChains`. + + ```diff + import { + chain, + + configureChains, + createClient + } from 'wagmi'; + + +import { publicProvider } from 'wagmi/providers/public' + + import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet' + import { InjectedConnector } from 'wagmi/connectors/injected' + import { MetaMaskConnector } from 'wagmi/connectors/metaMask' + import { WalletConnectConnector } from 'wagmi/connectors/walletConnect' + + +const { chains } = configureChains( + + [chain.mainnet], + + [publicProvider()] + +); + + const client = createClient({ + - connectors({ chainId }) { + - const chain = chains.find((x) => x.id === chainId) ?? defaultChain + - const rpcUrl = chain.rpcUrls.alchemy + - ? `${chain.rpcUrls.alchemy}/${alchemyId}` + - : chain.rpcUrls.default + - return [ + + connectors: [ + new MetaMaskConnector({ chains }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: 'wagmi', + - chainId: chain.id, + - jsonRpcUrl: rpcUrl, + }, + }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + - rpc: { [chain.id]: rpcUrl }, + }, + }), + new InjectedConnector({ + chains, + options: { name: 'Injected' }, + }), + ] + - }, + }) + ``` + +* [#611](https://github.com/tmm/wagmi/pull/611) [`3089c34`](https://github.com/tmm/wagmi/commit/3089c34196d4034acabac031e0a2f7ee63ae30cc) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `Connector`s `getProvider` method no longer supports the `create` config parameter. Use the `chainId` config option instead. + +- [#596](https://github.com/tmm/wagmi/pull/596) [`a770af7`](https://github.com/tmm/wagmi/commit/a770af7d2cb214b6620d5341115f1e938e1e77ff) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `TypedDataDomain` and `TypedDataField` types were removed and incorporated into `SignTypedDataArgs`. + +* [`4f8f3c0`](https://github.com/tmm/wagmi/commit/4f8f3c0d65383bd8bbdfc3f1033adfdb11d80ebb) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Update symbols to match ethereum-lists/chains + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `writeContract` function parameters have been consolidated into a singular config parameter. + + Before: + + ```tsx + writeContract( + { + addressOrName: "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + contractInterface: wagmigotchiABI, + }, + "feed", + ); + ``` + + After: + + ```tsx + readContract({ + addressOrName: "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + contractInterface: wagmigotchiABI, + functionName: "feed", + }); + ``` + +### Patch Changes + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `readContract` & `watchReadContract` function parameters have been consolidated into a singular config parameter. + + Before: + + ```tsx + readContract( + { + addressOrName: "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + contractInterface: wagmigotchiABI, + }, + "getHunger", + { args: [0] }, + ); + + watchReadContract( + { + addressOrName: "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + contractInterface: wagmigotchiABI, + }, + "getHunger", + { args: [0] }, + (result) => {}, + ); + ``` + + After: + + ```tsx + readContract({ + addressOrName: "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + contractInterface: wagmigotchiABI, + functionName: "getHunger", + args: [0], + }); + + watchReadContract( + { + addressOrName: "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + contractInterface: wagmigotchiABI, + functionName: "getHunger", + args: [0], + }, + (result) => {}, + ); + ``` + +* [#598](https://github.com/tmm/wagmi/pull/598) [`fef26bf`](https://github.com/tmm/wagmi/commit/fef26bf8aef76fc9621e3cd54d4e0ca8f69abb38) Thanks [@markdalgleish](https://github.com/markdalgleish)! - Update `@coinbase/wallet-sdk` peer dependency to `>=3.3.0` to fix errors when connecting with older versions of the Coinbase Wallet extension and mobile app. + +- [#611](https://github.com/tmm/wagmi/pull/611) [`3089c34`](https://github.com/tmm/wagmi/commit/3089c34196d4034acabac031e0a2f7ee63ae30cc) Thanks [@tmm](https://github.com/tmm)! - Added `chainId` config parameter for `writeContract` and `sendTransaction`. + + If `chainId` is provided, the connector will validate that `chainId` is the active chain before sending a transaction (and switch to `chainId` if necessary). + +* [#582](https://github.com/tmm/wagmi/pull/582) [`b03830a`](https://github.com/tmm/wagmi/commit/b03830a54465215c2526f9509543fe2c978bfe70) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where the wagmi client's `status` would not update from `"disconnected"` to `"connecting" -> "connected"` when the `connect` action is invoked. + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - Added a `multicall` & `watchMulticall` action that provides multicall support. + + Internally uses the [`multicall3` contract](https://github.com/mds1/multicall). + + [See example usage](https://github.com/tmm/wagmi/blob/194866032985fdd3f49bc46bf1b14181d7cb61d1/packages/core/src/actions/contracts/multicall.test.ts) + +* [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - Added a `readContracts` & `watchReadContracts` action that provides the ability to batch up multiple ethers Contract read-only methods. + +## 0.3.8 + +### Patch Changes + +- [#570](https://github.com/tmm/wagmi/pull/570) [`0e3fe15`](https://github.com/tmm/wagmi/commit/0e3fe15445377f35d6f4142b49bf1c96bfeb62cd) Thanks [@tmm](https://github.com/tmm)! - adds chain for [Foundry](https://github.com/foundry-rs) + +## 0.3.7 + +### Patch Changes + +- [#550](https://github.com/tmm/wagmi/pull/550) [`2a5313e`](https://github.com/tmm/wagmi/commit/2a5313e8cbc9ba6335e8e4b85e43862c9b711bd3) Thanks [@tmm](https://github.com/tmm)! - fix `CoinbaseWalletConnector` possible type error + +* [#548](https://github.com/tmm/wagmi/pull/548) [`0c48719`](https://github.com/tmm/wagmi/commit/0c487199f2421f042abc1f1d139468ccbbc5646a) Thanks [@dohaki](https://github.com/dohaki)! - add ensAddress to Chain type + +- [#549](https://github.com/tmm/wagmi/pull/549) [`89b3a74`](https://github.com/tmm/wagmi/commit/89b3a74ead4234daacd0dcf8506659887ebf0553) Thanks [@tmm](https://github.com/tmm)! - Turns on [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess=) and [`strictNullChecks`](https://www.typescriptlang.org/tsconfig#strictNullChecks=) for better runtime safety. + +## 0.3.6 + +### Patch Changes + +- [#526](https://github.com/tmm/wagmi/pull/526) [`e95c5f9`](https://github.com/tmm/wagmi/commit/e95c5f91859e57d079b962a72d06b93dce004d2f) Thanks [@jxom](https://github.com/jxom)! - Added `shimChainChangedDisconnect` option to `InjectedConnector`. Defaults to `true` for `MetaMaskConnector`. + +* [#526](https://github.com/tmm/wagmi/pull/526) [`e95c5f9`](https://github.com/tmm/wagmi/commit/e95c5f91859e57d079b962a72d06b93dce004d2f) Thanks [@jxom](https://github.com/jxom)! - Added `lastUsedChainId` property to the wagmi `Client`. + +- [#526](https://github.com/tmm/wagmi/pull/526) [`e95c5f9`](https://github.com/tmm/wagmi/commit/e95c5f91859e57d079b962a72d06b93dce004d2f) Thanks [@jxom](https://github.com/jxom)! - Added `chainId` config option to the `connect` action. + + Example: + + ```tsx + import { connect } from "@wagmi/core"; + + await connect({ chainId: 69 }); + ``` + +## 0.3.5 + +### Patch Changes + +- [#543](https://github.com/tmm/wagmi/pull/543) [`4d489fd`](https://github.com/tmm/wagmi/commit/4d489fd630dd8c00440bdaf4d646de662c41ff52) Thanks [@tmm](https://github.com/tmm)! - fix fee data formatting for null values + +## 0.3.4 + +### Patch Changes + +- [`c4deb66`](https://github.com/tmm/wagmi/commit/c4deb6655a52e4cc4e5b3fd82202db11d6106848) Thanks [@jxom](https://github.com/jxom)! - infer `options.chainId` config from `chains` on WalletConnectConnector + +## 0.3.3 + +### Patch Changes + +- [#486](https://github.com/tmm/wagmi/pull/486) [`dbfe3dd`](https://github.com/tmm/wagmi/commit/dbfe3dd320d178d6854a8096101200c1508786bb) Thanks [@tmm](https://github.com/tmm)! - add chains entrypoint + +## 0.3.2 + +### Patch Changes + +- [`17212da`](https://github.com/tmm/wagmi/commit/17212da601640110d2835300e6433d1433db212e) Thanks [@jxom](https://github.com/jxom)! - Made the `defaultChains` type generic in `configureChains`. + +## 0.3.1 + +### Patch Changes + +- [#484](https://github.com/tmm/wagmi/pull/484) [`1b9a503`](https://github.com/tmm/wagmi/commit/1b9a5033d51c6655b4f6570c490da6e0e9a29da9) Thanks [@tmm](https://github.com/tmm)! - export React Context + +## 0.3.0 + +### Minor Changes + +- [#408](https://github.com/tmm/wagmi/pull/408) [`bfcc3a5`](https://github.com/tmm/wagmi/commit/bfcc3a51bbb1551753e3ccde6af134e9fd4fec9a) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** The `connectors` option on `createClient` no longer reacts to chain switching. + + **Passing a function to `connectors` has been deprecated.** + + If you previously derived an RPC URL from the `chainId` in `connectors`, you will need to migrate to use the [`configureChains` API](https://wagmi.sh/docs/providers/configuring-chains). + + ### Before + + ```tsx + import { providers } from "ethers"; + import { chain, createClient, defaultChains } from "wagmi"; + import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet"; + import { InjectedConnector } from "wagmi/connectors/injected"; + import { MetaMaskConnector } from "wagmi/connectors/metaMask"; + import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; + + const alchemyId = process.env.ALCHEMY_ID; + + const chains = defaultChains; + const defaultChain = chain.mainnet; + + const client = createClient({ + autoConnect: true, + connectors({ chainId }) { + const chain = chains.find((x) => x.id === chainId) ?? defaultChain; + const rpcUrl = chain.rpcUrls.alchemy + ? `${chain.rpcUrls.alchemy}/${alchemyId}` + : chain.rpcUrls.default; + return [ + new MetaMaskConnector({ chains }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: "wagmi", + chainId: chain.id, + jsonRpcUrl: rpcUrl, + }, + }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + rpc: { [chain.id]: rpcUrl }, + }, + }), + new InjectedConnector({ + chains, + options: { + name: "Injected", + shimDisconnect: true, + }, + }), + ]; + }, + }); + ``` + + ### After + + ```tsx + import { chain, createClient, defaultChains } from "wagmi"; + + import { alchemyProvider } from "wagmi/providers/alchemy"; + import { publicProvider } from "wagmi/providers/public"; + + import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet"; + import { InjectedConnector } from "wagmi/connectors/injected"; + import { MetaMaskConnector } from "wagmi/connectors/metaMask"; + import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; + + const alchemyId = process.env.ALCHEMY_ID; + + const { chains } = configureChains(defaultChains, [ + alchemyProvider({ alchemyId }), + publicProvider(), + ]); + + const client = createClient({ + autoConnect: true, + connectors: [ + new MetaMaskConnector({ chains }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: "wagmi", + }, + }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + }, + }), + new InjectedConnector({ + chains, + options: { + name: "Injected", + shimDisconnect: true, + }, + }), + ], + }); + ``` + +* [#468](https://github.com/tmm/wagmi/pull/468) [`44a884b`](https://github.com/tmm/wagmi/commit/44a884b84171c418f57701e80ef8de972948ef0b) Thanks [@tmm](https://github.com/tmm)! - **Breaking:** Duplicate exports with different names and the same functionality were removed to simplify the public API. In addition, confusing exports were renamed to be more descriptive. + + - `createWagmiClient` alias was removed. Use `createClient` instead. + - `useWagmiClient` alias was removed. Use `useClient` instead. + - `WagmiClient` alias was removed. Use `Client` instead. + - `createWagmiStorage` alias was removed. Use `createStorage` instead. + - `Provider` was renamed and `WagmiProvider` alias was removed. Use `WagmiConfig` instead. + +- [#408](https://github.com/tmm/wagmi/pull/408) [`bfcc3a5`](https://github.com/tmm/wagmi/commit/bfcc3a51bbb1551753e3ccde6af134e9fd4fec9a) Thanks [@jxom](https://github.com/jxom)! - Add `configureChains` API. + + The `configureChains` function allows you to configure your chains with a selected provider (Alchemy, Infura, JSON RPC, Public RPC URLs). This means you don't have to worry about deriving your own RPC URLs for each chain, or instantiating a Ethereum Provider. + + `configureChains` accepts 3 parameters: an array of chains, and an array of providers, and a config object. + + [Learn more about configuring chains & providers.](https://wagmi.sh/docs/providers/configuring-chains) + + ### Before + + ```tsx + import { providers } from "ethers"; + import { chain, createClient, defaultChains } from "wagmi"; + import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet"; + import { InjectedConnector } from "wagmi/connectors/injected"; + import { MetaMaskConnector } from "wagmi/connectors/metaMask"; + import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; + + const alchemyId = process.env.ALCHEMY_ID; + + const chains = defaultChains; + const defaultChain = chain.mainnet; + + const client = createClient({ + autoConnect: true, + connectors({ chainId }) { + const chain = chains.find((x) => x.id === chainId) ?? defaultChain; + const rpcUrl = chain.rpcUrls.alchemy + ? `${chain.rpcUrls.alchemy}/${alchemyId}` + : chain.rpcUrls.default; + return [ + new MetaMaskConnector({ chains }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: "wagmi", + chainId: chain.id, + jsonRpcUrl: rpcUrl, + }, + }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + rpc: { [chain.id]: rpcUrl }, + }, + }), + new InjectedConnector({ + chains, + options: { + name: "Injected", + shimDisconnect: true, + }, + }), + ]; + }, + provider: ({ chainId }) => + new providers.AlchemyProvider(chainId, alchemyId), + }); + ``` + + ### After + + ```tsx + import { chain, createClient, defaultChains } from "wagmi"; + + import { alchemyProvider } from "wagmi/providers/alchemy"; + import { publicProvider } from "wagmi/providers/public"; + + import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet"; + import { InjectedConnector } from "wagmi/connectors/injected"; + import { MetaMaskConnector } from "wagmi/connectors/metaMask"; + import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; + + const alchemyId = process.env.ALCHEMY_ID; + + const { chains, provider, webSocketProvider } = configureChains( + defaultChains, + [alchemyProvider({ alchemyId }), publicProvider()], + ); + + const client = createClient({ + autoConnect: true, + connectors: [ + new MetaMaskConnector({ chains }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: "wagmi", + }, + }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + }, + }), + new InjectedConnector({ + chains, + options: { + name: "Injected", + shimDisconnect: true, + }, + }), + ], + provider, + webSocketProvider, + }); + ``` + +### Patch Changes + +- [#404](https://github.com/tmm/wagmi/pull/404) [`f81c156`](https://github.com/tmm/wagmi/commit/f81c15665e2e71534f84ada3fa705f2d78627472) Thanks [@holic](https://github.com/holic)! - Add `ProviderRpcError` and `RpcError` error classes. + + Certain wagmi-standardized errors may wrap `ProviderRpcError` or `RpcError`. For these cases, you can access the original provider rpc or rpc error value using the `internal` property. + +* [#459](https://github.com/tmm/wagmi/pull/459) [`72dcf7c`](https://github.com/tmm/wagmi/commit/72dcf7c09e814261b2e43a8fa364c57675c472de) Thanks [@tmm](https://github.com/tmm)! - update dependencies + +- [#473](https://github.com/tmm/wagmi/pull/473) [`a54f3e2`](https://github.com/tmm/wagmi/commit/a54f3e23ea385ed8aa4ad188128d7089ba20f83e) Thanks [@cesargdm](https://github.com/cesargdm)! - Add workaround for CoinbaseWalletSDK import on esbuild + +* [#447](https://github.com/tmm/wagmi/pull/447) [`b9ebf78`](https://github.com/tmm/wagmi/commit/b9ebf782e0900725bcb76483686b30f09d357ebd) Thanks [@tmm](https://github.com/tmm)! - Fix case where connector disconnected while app was closed and stale data was returned when autoconnecting. For example, [MetaMask was locked](https://github.com/tmm/wagmi/issues/444) when page was closed. + +## 0.2.5 + +### Patch Changes + +- [`4e03666`](https://github.com/tmm/wagmi/commit/4e03666428d42fc9186c617001b5eb356229677e) Thanks [@tmm](https://github.com/tmm)! - bump dependencies #429 + add imToken support for WC switch chains #432 + fix MetaMask and Brave Wallet collision #436 + +## 0.2.4 + +### Patch Changes + +- [#421](https://github.com/tmm/wagmi/pull/421) [`a232b3f`](https://github.com/tmm/wagmi/commit/a232b3ff5cc41e882c4d2a34c599a8cb670edd2b) Thanks [@tmm](https://github.com/tmm)! - fix erc721 abi + +## 0.2.3 + +### Patch Changes + +- [#412](https://github.com/tmm/wagmi/pull/412) [`80bef4f`](https://github.com/tmm/wagmi/commit/80bef4ff3f714b0b8f896f1b4b658acc7266299b) Thanks [@markdalgleish](https://github.com/markdalgleish)! - Import providers from `ethers` peer dependency rather than `@ethersproject/providers` to avoid multiple conflicting versions being installed + +## 0.2.2 + +### Patch Changes + +- [`018c2a1`](https://github.com/tmm/wagmi/commit/018c2a11b22ee513571cc7f83fd63f7eb169ee70) Thanks [@tmm](https://github.com/tmm)! - - warn and fallback to default client #380 + + - remove signerOrProvider option from read contract #390 + + - MetaMaskConnector #391 + +## 0.2.1 + +### Patch Changes + +- [`afc4607`](https://github.com/tmm/wagmi/commit/afc46071e91601ab8a2b465524da796cd60b6ad4) Thanks [@tmm](https://github.com/tmm)! - - Fix time scaling e9593df + - Use fully-specified path for use-sync-external-store import 7b235c1 + - Update serialize 236fc17 + +## 0.2.0 + +### Minor Changes + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - don't persist account data when `autoConnect` is falsy + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - - fix(@wagmi/core): persist connector chains to local storage + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - - Favour `message` event over `connecting` event to conform to EIP-1193 + - Export `useWaitForTransaction` + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - Initial 0.3.0 release + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - Add `cacheOnBlock` config for `useContractRead` + Update `react-query` to v4 + Fix `watchBlockNumber` listener leak + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - add `connecting` event to connectors + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - Fix issue where `getProvider` was not being awaited in `getSigner` + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - - remove storage persistence of `connector` + - add `chains` to client state + +### Patch Changes + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - add chainId to actions and hooks + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - showtime + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - improve type support for ethers providers + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - update zustand + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - update babel target + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - update block explorers and rpc urls structure + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - keep previous data when watching + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - republish + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - fix stale connectors when switching chains + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - last beta + +## 0.2.0-next.18 + +### Patch Changes + +- showtime + +## 0.2.0-next.17 + +### Patch Changes + +- update block explorers and rpc urls structure + +## 0.2.0-next.16 + +### Patch Changes + +- last beta + +## 0.2.0-next.15 + +### Patch Changes + +- update zustand + +## 0.2.0-next.14 + +### Minor Changes + +- Add `cacheOnBlock` config for `useContractRead` +- Update `react-query` to v4 +- Fix `watchBlockNumber` listener leak + +## 0.2.0-next.13 + +### Patch Changes + +- keep previous data when watching + +## 0.2.0-next.12 + +### Patch Changes + +- add chainId to actions and hooks + +## 0.2.0-next.11 + +### Patch Changes + +- fix stale connectors when switching chains + +## 0.2.0-next.10 + +### Patch Changes + +- republish + +## 0.2.0-next.9 + +### Patch Changes + +- improve type support for ethers providers + +## 0.2.0-next.8 + +### Patch Changes + +- update babel target + +## 0.2.0-next.7 + +### Minor Changes + +- - Favour `message` event over `connecting` event to conform to EIP-1193 + - Export `useWaitForTransaction` + +## 0.2.0-next.6 + +### Minor Changes + +- add `connecting` event to connectors + +## 0.2.0-next.5 + +### Minor Changes + +- don't persist account data when `autoConnect` is falsy + +## 0.2.0-next.4 + +### Minor Changes + +- remove storage persistence of `connector` +- add `chains` to client state + +## 0.2.0-next.3 + +### Minor Changes + +- Fix issue where `getProvider` was not being awaited in `getSigner` + +## 0.2.0-next.2 + +### Minor Changes + +- fix: persist connector chains to local storage + +## 0.2.0-next.1 + +### Minor Changes + +- Initial 0.3.0 release + +## 0.1.22 + +### Patch Changes + +- [`747d895`](https://github.com/tmm/wagmi/commit/747d895a54b562958afde34b1d34e81ab5039e2c) Thanks [@tmm](https://github.com/tmm)! - add warning to WalletLinkConnector + +## 0.1.21 + +### Patch Changes + +- [`c858c51`](https://github.com/tmm/wagmi/commit/c858c51b44d9039f1d0db5bcf016639f47d1931f) Thanks [@tmm](https://github.com/tmm)! - update coinbase connector + +## 0.1.20 + +### Patch Changes + +- [#326](https://github.com/tmm/wagmi/pull/326) [`36e6989`](https://github.com/tmm/wagmi/commit/36e69894f4c27aaad7fb6d678476c8bb870244bb) Thanks [@0xGabi](https://github.com/0xGabi)! - Add Gnosis Chain + +## 0.1.19 + +### Patch Changes + +- [`d467df6`](https://github.com/tmm/wagmi/commit/d467df6374210dbc4b016788b4beb4fded54cb4d) Thanks [@tmm](https://github.com/tmm)! - fix global type leaking + +## 0.1.18 + +### Patch Changes + +- [#294](https://github.com/tmm/wagmi/pull/294) [`1d253f3`](https://github.com/tmm/wagmi/commit/1d253f3a59b61d24c88d25c99decd84a6c734e5d) Thanks [@tmm](https://github.com/tmm)! - change babel target + +## 0.1.17 + +### Patch Changes + +- [#292](https://github.com/tmm/wagmi/pull/292) [`53c9be1`](https://github.com/tmm/wagmi/commit/53c9be17ee0c2ae6b8f34f2351b8858257b3f5f2) Thanks [@tmm](https://github.com/tmm)! - fix private fields + +## 0.1.16 + +### Patch Changes + +- [`79a2499`](https://github.com/tmm/wagmi/commit/79a249989029f818c32c0e84c0dd2c75e8aa990a) Thanks [@tmm](https://github.com/tmm)! - update build target to es2021 + +## 0.1.15 + +### Patch Changes + +- [`f9790b5`](https://github.com/tmm/wagmi/commit/f9790b55600df09c77bb8ca349c5a3457df1b07c) Thanks [@tmm](https://github.com/tmm)! - fix WalletConnect issue + +## 0.1.14 + +### Patch Changes + +- [#236](https://github.com/tmm/wagmi/pull/236) [`53bad61`](https://github.com/tmm/wagmi/commit/53bad615788764e31121678083c382c1bd042fe8) Thanks [@markdalgleish](https://github.com/markdalgleish)! - Updated `@walletconnect/ethereum-provider` to [v1.7.4](https://github.com/WalletConnect/walletconnect-monorepo/releases/tag/1.7.4) + +## 0.1.13 + +### Patch Changes + +- [`8e9412a`](https://github.com/tmm/wagmi/commit/8e9412af71958301ae2f9748febb936e79900aa0) Thanks [@tmm](https://github.com/tmm)! - bump walletlink + +## 0.1.12 + +### Patch Changes + +- [#210](https://github.com/tmm/wagmi/pull/210) [`684468a`](https://github.com/tmm/wagmi/commit/684468aee3e42a1ce2b4b599f3f17d1819213de8) Thanks [@tmm](https://github.com/tmm)! - update chains to match chainslist.org + +## 0.1.11 + +### Patch Changes + +- [#195](https://github.com/tmm/wagmi/pull/195) [`25b6083`](https://github.com/tmm/wagmi/commit/25b6083a662a0236794d1765343467691421c14b) Thanks [@tmm](https://github.com/tmm)! - rename wagmi-private to wagmi-core + +## 0.1.10 + +### Patch Changes + +- [#192](https://github.com/tmm/wagmi/pull/192) [`428cedb`](https://github.com/tmm/wagmi/commit/428cedb3dec4e3e4b9f4559c8e65932e05f94e05) Thanks [@tmm](https://github.com/tmm)! - rename core and testing packages + +## 0.1.9 + +### Patch Changes + +- [#190](https://github.com/tmm/wagmi/pull/190) [`7034bb8`](https://github.com/tmm/wagmi/commit/7034bb868412b9f481b206371280e84c2d52706d) Thanks [@tmm](https://github.com/tmm)! - add shim for metamask chain changed to prevent disconnect + +## 0.1.8 + +### Patch Changes + +- [#137](https://github.com/tmm/wagmi/pull/137) [`dceeb43`](https://github.com/tmm/wagmi/commit/dceeb430d9021fbf98366859cb1cd0149e80c55c) Thanks [@tmm](https://github.com/tmm)! - add siwe guide + +## 0.1.7 + +### Patch Changes + +- [#127](https://github.com/tmm/wagmi/pull/127) [`f05b031`](https://github.com/tmm/wagmi/commit/f05b0310f7f7e6447e9b6c81cedbb27dcf2f3649) Thanks [@tmm](https://github.com/tmm)! - update switch chain return type + +## 0.1.6 + +### Patch Changes + +- [`1412eed`](https://github.com/tmm/wagmi/commit/1412eed0d1494bb4f8c6845a0e890f79e4e68e03) Thanks [@tmm](https://github.com/tmm)! - add frame to injected + +## 0.1.5 + +### Patch Changes + +- [`e338c3b`](https://github.com/tmm/wagmi/commit/e338c3b6cc255742be6a67593aa5da6c17e90fbd) Thanks [@tmm](https://github.com/tmm)! - checksum connector address on change events + + add shim to injected connector for simulating disconnect + +## 0.1.4 + +### Patch Changes + +- [`0176c4e`](https://github.com/tmm/wagmi/commit/0176c4e83fb0c5f159c3c802a1da3d6deb2184ae) Thanks [@tmm](https://github.com/tmm)! - added switchChain to WalletConnect and WalletLink connectors + +## 0.1.3 + +### Patch Changes + +- [`071d7fb`](https://github.com/tmm/wagmi/commit/071d7fbca35ec4832700b5343661ceb2dae20598) Thanks [@tmm](https://github.com/tmm)! - add hardhat chain + +## 0.1.2 + +### Patch Changes + +- [`78bade9`](https://github.com/tmm/wagmi/commit/78bade9d0da97ab38a7e6594c34e3841ec1c8fe6) Thanks [@tmm](https://github.com/tmm)! - add type definitions + +## 0.1.1 + +### Patch Changes + +- [#56](https://github.com/tmm/wagmi/pull/56) [`2ebfd8e`](https://github.com/tmm/wagmi/commit/2ebfd8e85b560f25cd46cff04619c84643cab297) Thanks [@tmm](https://github.com/tmm)! - add chain support status + +## 0.1.0 + +### Minor Changes + +- [#52](https://github.com/tmm/wagmi/pull/52) [`da7a3a6`](https://github.com/tmm/wagmi/commit/da7a3a615def2443f65c041999100ce35e9774cc) Thanks [@tmm](https://github.com/tmm)! - Moves connectors to their own entrypoints to reduce bundle size. + + ```ts + // old - WalletLinkConnector unused, but still in final bundle + import { InjectedConnector, WalletConnectConnector } from "wagmi"; + + // new - WalletLinkConnector not in final bundle + import { InjectedConnector } from "wagmi/connectors/injected"; + import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; + ``` + +## 0.0.17 + +### Patch Changes + +- [#25](https://github.com/tmm/wagmi/pull/25) [`9a7dab7`](https://github.com/tmm/wagmi/commit/9a7dab78b3518658bc7d85dc397990f0d28da175) Thanks [@tmm](https://github.com/tmm)! - update response types + +## 0.0.16 + +### Patch Changes + +- [`d1574cf`](https://github.com/tmm/wagmi/commit/d1574cf5f7a578ccd480889c2e375134145a4aba) Thanks [@tmm](https://github.com/tmm)! - add better type information for contract results + +## 0.0.15 + +### Patch Changes + +- [`3909624`](https://github.com/tmm/wagmi/commit/39096249c1fa9516beabb11735beb67c94032879) Thanks [@tmm](https://github.com/tmm)! - make contract read and write execute overrides param optional + +## 0.0.14 + +### Patch Changes + +- [`63312e2`](https://github.com/tmm/wagmi/commit/63312e2b06b8d835abc2908cba399d941ca79408) Thanks [@tmm](https://github.com/tmm)! - add once to contract event + +## 0.0.13 + +### Patch Changes + +- [`6f890b0`](https://github.com/tmm/wagmi/commit/6f890b0dabbdbea913ec91cb8bfc970c05ed0a93) Thanks [@tmm](https://github.com/tmm)! - update readme + +## 0.0.12 + +### Patch Changes + +- [#19](https://github.com/tmm/wagmi/pull/19) [`7bc1c47`](https://github.com/tmm/wagmi/commit/7bc1c47875e9ef24e9c79cfafc6b23e7a838b5bc) Thanks [@tmm](https://github.com/tmm)! - remove console log from walletlink connector + +## 0.0.11 + +### Patch Changes + +- [#17](https://github.com/tmm/wagmi/pull/17) [`571648b`](https://github.com/tmm/wagmi/commit/571648b754f7f538536bafc9387bd3104657ea49) Thanks [@tmm](https://github.com/tmm)! - standardize connector provider + +## 0.0.10 + +### Patch Changes + +- [#15](https://github.com/tmm/wagmi/pull/15) [`5f7675c`](https://github.com/tmm/wagmi/commit/5f7675c3ffd848522d4117c07c1f62b17dfc6616) Thanks [@tmm](https://github.com/tmm)! - read and write contract functions + +## 0.0.9 + +### Patch Changes + +- [#13](https://github.com/tmm/wagmi/pull/13) [`e5545f5`](https://github.com/tmm/wagmi/commit/e5545f5565cf0bbf5e62ec7ccab3051705b1d313) Thanks [@tmm](https://github.com/tmm)! - add testing package + +## 0.0.8 + +### Patch Changes + +- [`5332500`](https://github.com/tmm/wagmi/commit/5332500918ac240d29ffe4d2aed8566a8ac001e4) Thanks [@tmm](https://github.com/tmm)! - update signing + +## 0.0.7 + +### Patch Changes + +- [`0bff89a`](https://github.com/tmm/wagmi/commit/0bff89ab2ad28b2cb9b346d1ac870e859d9278bc) Thanks [@tmm](https://github.com/tmm)! - update injected connector + +## 0.0.6 + +### Patch Changes + +- [`37d39d1`](https://github.com/tmm/wagmi/commit/37d39d174ddfa122462bbe2d02141cd61eb9db4a) Thanks [@tmm](https://github.com/tmm)! - add message signing + +## 0.0.5 + +### Patch Changes + +- [`d7d94f0`](https://github.com/tmm/wagmi/commit/d7d94f06f7d30468e5e39d64db63124c6315cf82) Thanks [@tmm](https://github.com/tmm)! - fix injected connector name + +## 0.0.4 + +### Patch Changes + +- [`29fbe29`](https://github.com/tmm/wagmi/commit/29fbe2920046b9e87a34faa04500ccf3c4f83748) Thanks [@tmm](https://github.com/tmm)! - fix external deps + +## 0.0.3 + +### Patch Changes + +- [#6](https://github.com/tmm/wagmi/pull/6) [`8dc3a5d`](https://github.com/tmm/wagmi/commit/8dc3a5d5f418813b09663534fe585d9bcf94dbeb) Thanks [@tmm](https://github.com/tmm)! - clean up deps -- https://sequence.xyz/blog/sequence-wallet-light-state-sync-full-merkle-wallets +## 0.0.2 ### Patch Changes -- Updated dependencies - - @0xsequence/abi@1.0.0 +- [#4](https://github.com/tmm/wagmi/pull/4) [`2fbd821`](https://github.com/tmm/wagmi/commit/2fbd8216379bd03c9cc5c06b10b75637e75cb7d8) Thanks [@tmm](https://github.com/tmm)! - init changesets diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000000..b46e39c559 --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,13 @@ +# @wagmi/core + +VanillaJS library for Ethereum + +## Installation + +```bash +pnpm add @wagmi/core viem +``` + +## Documentation + +For documentation and guides, visit [wagmi.sh](https://wagmi.sh). diff --git a/packages/core/package.json b/packages/core/package.json index 9f79c70b6d..00589ee839 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,30 +1,100 @@ { - "name": "@0xsequence/core", - "version": "2.0.0", - "description": "core primitives for interacting with the sequence wallet contracts", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/core", - "source": "src/index.ts", - "main": "dist/0xsequence-core.cjs.js", - "module": "dist/0xsequence-core.esm.js", - "author": "Horizon Blockchain Games", - "license": "Apache-2.0", + "name": "@wagmi/core", + "description": "VanillaJS library for Ethereum", + "version": "2.17.2", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/wevm/wagmi.git", + "directory": "packages/core" + }, "scripts": { - "test": "pnpm test:file tests/**/*.spec.ts", - "test:file": "TS_NODE_PROJECT=../../tsconfig.test.json mocha -r ts-node/register --timeout 30000", - "test:coverage": "nyc yarn test" + "build": "pnpm run clean && pnpm run build:esm+types", + "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", + "check:types": "tsc --noEmit", + "clean": "rm -rf dist tsconfig.tsbuildinfo actions chains codegen experimental internal query", + "test:build": "publint --strict && attw --pack --ignore-rules cjs-resolves-to-esm" + }, + "files": [ + "dist/**", + "!dist/**/*.tsbuildinfo", + "src/**/*.ts", + "!src/**/*.test.ts", + "!src/**/*.test-d.ts", + "/actions", + "/chains", + "/experimental", + "/internal", + "/query" + ], + "sideEffects": false, + "type": "module", + "main": "./dist/esm/exports/index.js", + "types": "./dist/types/exports/index.d.ts", + "typings": "./dist/types/exports/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/exports/index.d.ts", + "default": "./dist/esm/exports/index.js" + }, + "./actions": { + "types": "./dist/types/exports/actions.d.ts", + "default": "./dist/esm/exports/actions.js" + }, + "./chains": { + "types": "./dist/types/exports/chains.d.ts", + "default": "./dist/esm/exports/chains.js" + }, + "./codegen": { + "types": "./dist/types/exports/codegen.d.ts", + "default": "./dist/esm/exports/codegen.js" + }, + "./experimental": { + "types": "./dist/types/exports/experimental.d.ts", + "default": "./dist/esm/exports/experimental.js" + }, + "./internal": { + "types": "./dist/types/exports/internal.d.ts", + "default": "./dist/esm/exports/internal.js" + }, + "./query": { + "types": "./dist/types/exports/query.d.ts", + "default": "./dist/esm/exports/query.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "actions": ["./dist/types/exports/actions.d.ts"], + "chains": ["./dist/types/exports/chains.d.ts"], + "codegen": ["./dist/types/exports/codegen.d.ts"], + "experimental": ["./dist/types/exports/experimental.d.ts"], + "internal": ["./dist/types/exports/internal.d.ts"], + "query": ["./dist/types/exports/query.d.ts"] + } }, "peerDependencies": { - "ethers": ">=5.5" + "@tanstack/query-core": ">=5.0.0", + "typescript": ">=5.0.4", + "viem": "2.x" }, - "devDependencies": { - "@istanbuljs/nyc-config-typescript": "^1.0.2", - "nyc": "^15.1.0" + "peerDependenciesMeta": { + "@tanstack/query-core": { + "optional": true + }, + "typescript": { + "optional": true + } }, - "files": [ - "src", - "dist" - ], "dependencies": { - "@0xsequence/abi": "workspace:*" - } + "eventemitter3": "5.0.1", + "mipd": "0.0.7", + "zustand": "5.0.0" + }, + "devDependencies": { + "@tanstack/query-core": "catalog:" + }, + "contributors": ["awkweb.eth ", "jxom.eth "], + "funding": "https://github.com/sponsors/wevm", + "keywords": ["wagmi", "eth", "ethereum", "dapps", "wallet", "web3"] } diff --git a/packages/core/src/actions/call.test.ts b/packages/core/src/actions/call.test.ts new file mode 100644 index 0000000000..2ef01160da --- /dev/null +++ b/packages/core/src/actions/call.test.ts @@ -0,0 +1,149 @@ +import { accounts, address, config } from '@wagmi/test' +import { parseEther, parseGwei } from 'viem' +import { expect, test } from 'vitest' + +import { call } from './call.js' + +const name4bytes = '0x06fdde03' +const mint4bytes = '0x1249c58b' +const mintWithParams4bytes = '0xa0712d68' +const fourTwenty = + '00000000000000000000000000000000000000000000000000000000000001a4' + +const account = accounts[0] + +test('default', async () => { + await expect( + call(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + }), + ).resolves.toMatchInlineSnapshot(` + { + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000057761676d69000000000000000000000000000000000000000000000000000000", + } + `) +}) + +test('zero data', async () => { + await expect( + call(config, { + account, + data: mint4bytes, + to: address.wagmiMintExample, + }), + ).resolves.toMatchInlineSnapshot(` + { + "data": undefined, + } + `) +}) + +// TODO: Re-enable +test.skip('parameters: blockNumber', async () => { + await expect( + call(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + blockNumber: 16280770n, + }), + ).resolves.toMatchInlineSnapshot(` + { + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000057761676d69000000000000000000000000000000000000000000000000000000", + } + `) +}) + +test('insufficient funds', async () => { + await expect( + call(config, { + account, + to: accounts[1], + value: parseEther('100000'), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [CallExecutionError: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account. + + This error could arise when the account does not have enough funds to: + - pay for the total gas fee, + - pay for the value to send. + + The cost of the transaction is calculated as \`gas * gas fee + value\`, where: + - \`gas\` is the amount of gas needed for transaction to execute, + - \`gas fee\` is the gas fee, + - \`value\` is the amount of ether to send to the recipient. + + Raw Call Arguments: + from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + to: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 + value: 100000 ETH + + Details: Insufficient funds for gas * price + value + Version: viem@2.29.2] + `) +}) + +test('maxFeePerGas less than maxPriorityFeePerGas', async () => { + await expect( + call(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + maxFeePerGas: parseGwei('20'), + maxPriorityFeePerGas: parseGwei('22'), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [CallExecutionError: The provided tip (\`maxPriorityFeePerGas\` = 22 gwei) cannot be higher than the fee cap (\`maxFeePerGas\` = 20 gwei). + + Raw Call Arguments: + from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + to: 0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2 + data: 0x06fdde03 + maxFeePerGas: 20 gwei + maxPriorityFeePerGas: 22 gwei + + Version: viem@2.29.2] + `) +}) + +test('contract revert (contract error)', async () => { + await expect( + call(config, { + account, + data: `${mintWithParams4bytes}${fourTwenty}`, + to: address.wagmiMintExample, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [CallExecutionError: Execution reverted with reason: Token ID is taken. + + Raw Call Arguments: + from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + to: 0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2 + data: 0xa0712d6800000000000000000000000000000000000000000000000000000000000001a4 + + Details: execution reverted: Token ID is taken + Version: viem@2.29.2] + `) +}) + +test('contract revert (insufficient params)', async () => { + await expect( + call(config, { + account, + data: mintWithParams4bytes, + to: address.wagmiMintExample, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [CallExecutionError: Execution reverted for an unknown reason. + + Raw Call Arguments: + from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + to: 0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2 + data: 0xa0712d68 + + Details: execution reverted + Version: viem@2.29.2] + `) +}) diff --git a/packages/core/src/actions/call.ts b/packages/core/src/actions/call.ts new file mode 100644 index 0000000000..90e6c1ae20 --- /dev/null +++ b/packages/core/src/actions/call.ts @@ -0,0 +1,27 @@ +import type { + CallErrorType as viem_CallErrorType, + CallParameters as viem_CallParameters, + CallReturnType as viem_CallReturnType, +} from 'viem' +import { call as viem_call } from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import { getAction } from '../utils/getAction.js' + +export type CallParameters = + viem_CallParameters & ChainIdParameter + +export type CallReturnType = viem_CallReturnType + +export type CallErrorType = viem_CallErrorType + +export async function call( + config: config, + parameters: CallParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_call, 'call') + return action(rest) +} diff --git a/packages/core/src/actions/codegen/createReadContract.test-d.ts b/packages/core/src/actions/codegen/createReadContract.test-d.ts new file mode 100644 index 0000000000..f5c9dd302b --- /dev/null +++ b/packages/core/src/actions/codegen/createReadContract.test-d.ts @@ -0,0 +1,130 @@ +import { abi, config, mainnet, optimism } from '@wagmi/test' +import { assertType, expectTypeOf, test } from 'vitest' + +import { createReadContract } from './createReadContract.js' + +test('default', async () => { + const readErc20 = createReadContract({ + abi: abi.erc20, + address: '0x', + }) + + const result = await readErc20(config, { + functionName: 'balanceOf', + args: ['0x'], + chainId: 1, + }) + expectTypeOf(result).toEqualTypeOf() +}) + +test('multichain address', async () => { + const readErc20 = createReadContract({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + const result = await readErc20(config, { + functionName: 'balanceOf', + args: ['0x'], + chainId: mainnet.id, + // ^? + }) + assertType(result) + + readErc20(config, { + functionName: 'balanceOf', + args: ['0x'], + // @ts-expect-error chain id must match address keys + chainId: 420, + }) + + readErc20(config, { + functionName: 'balanceOf', + args: ['0x'], + // @ts-expect-error address not allowed + address: '0x', + }) +}) + +test('overloads', async () => { + const readViewOverloads = createReadContract({ + abi: abi.viewOverloads, + address: '0x', + }) + + const result1 = await readViewOverloads(config, { + functionName: 'foo', + }) + assertType(result1) + + const result2 = await readViewOverloads(config, { + functionName: 'foo', + args: [], + }) + assertType(result2) + + const result3 = await readViewOverloads(config, { + functionName: 'foo', + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3) + + const result4 = await readViewOverloads(config, { + functionName: 'foo', + args: ['0x', '0x'], + }) + assertType<{ + foo: `0x${string}` + bar: `0x${string}` + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + }>(result4) +}) + +test('functionName', async () => { + const readErc20BalanceOf = createReadContract({ + abi: abi.erc20, + address: '0x', + functionName: 'balanceOf', + }) + + const result = await readErc20BalanceOf(config, { + args: ['0x'], + chainId: 1, + }) + expectTypeOf(result).toEqualTypeOf() +}) + +test('functionName with overloads', async () => { + const readViewOverloads = createReadContract({ + abi: abi.viewOverloads, + address: '0x', + functionName: 'foo', + }) + + const result1 = await readViewOverloads(config, {}) + assertType(result1) + + const result2 = await readViewOverloads(config, { + args: [], + }) + assertType(result2) + + const result3 = await readViewOverloads(config, { + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3) + + const result4 = await readViewOverloads(config, { + args: ['0x', '0x'], + }) + assertType<{ + foo: `0x${string}` + bar: `0x${string}` + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + }>(result4) +}) diff --git a/packages/core/src/actions/codegen/createReadContract.test.ts b/packages/core/src/actions/codegen/createReadContract.test.ts new file mode 100644 index 0000000000..9de7ae718f --- /dev/null +++ b/packages/core/src/actions/codegen/createReadContract.test.ts @@ -0,0 +1,50 @@ +import { abi, address, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { createReadContract } from './createReadContract.js' + +test('default', async () => { + const readWagmiMintExample = createReadContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + }) + + await expect( + readWagmiMintExample(config, { + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }), + ).resolves.toMatchInlineSnapshot('4n') +}) + +test('multichain', async () => { + const readWagmiMintExample = createReadContract({ + address: { + [chain.mainnet.id]: address.wagmiMintExample, + [chain.mainnet2.id]: address.wagmiMintExample, + }, + abi: abi.wagmiMintExample, + }) + + await expect( + readWagmiMintExample(config, { + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + chainId: chain.mainnet2.id, + }), + ).resolves.toMatchInlineSnapshot('4n') +}) + +test('functionName', async () => { + const readWagmiMintExampleBalanceOf = createReadContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + }) + + await expect( + readWagmiMintExampleBalanceOf(config, { + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }), + ).resolves.toMatchInlineSnapshot('4n') +}) diff --git a/packages/core/src/actions/codegen/createReadContract.ts b/packages/core/src/actions/codegen/createReadContract.ts new file mode 100644 index 0000000000..f7dbfbed45 --- /dev/null +++ b/packages/core/src/actions/codegen/createReadContract.ts @@ -0,0 +1,100 @@ +import type { + Abi, + Address, + ContractFunctionArgs, + ContractFunctionName, +} from 'viem' + +import type { Config } from '../../createConfig.js' +import type { UnionCompute, UnionStrictOmit } from '../../types/utils.js' +import { getAccount } from '../getAccount.js' +import { getChainId } from '../getChainId.js' +import { + type ReadContractParameters, + type ReadContractReturnType, + readContract, +} from '../readContract.js' + +type stateMutability = 'pure' | 'view' + +export type CreateReadContractParameters< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +> = { + abi: abi | Abi | readonly unknown[] + address?: address | Address | Record | undefined + functionName?: + | functionName + | ContractFunctionName + | undefined +} + +export type CreateReadContractReturnType< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined, + functionName extends ContractFunctionName | undefined, + /// + omittedProperties extends 'abi' | 'address' | 'chainId' | 'functionName' = + | 'abi' + | (address extends undefined ? never : 'address') + | (address extends Record ? 'chainId' : never) + | (functionName extends undefined ? never : 'functionName'), +> = < + config extends Config, + name extends functionName extends ContractFunctionName + ? functionName + : ContractFunctionName, + args extends ContractFunctionArgs, +>( + config: config, + parameters: UnionCompute< + UnionStrictOmit< + ReadContractParameters, + omittedProperties + > + > & + (address extends Record + ? { chainId?: keyof address | undefined } + : unknown), +) => Promise> + +export function createReadContract< + const abi extends Abi | readonly unknown[], + const address extends + | Address + | Record + | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +>( + c: CreateReadContractParameters, +): CreateReadContractReturnType { + if (c.address !== undefined && typeof c.address === 'object') + return (config, parameters) => { + const configChainId = getChainId(config) + const account = getAccount(config) + const chainId = + (parameters as { chainId?: number })?.chainId ?? + account.chainId ?? + configChainId + return readContract(config, { + ...(parameters as any), + ...(c.functionName ? { functionName: c.functionName } : {}), + address: c.address?.[chainId], + abi: c.abi, + }) + } + + return (config, parameters) => { + return readContract(config, { + ...(parameters as any), + ...(c.address ? { address: c.address } : {}), + ...(c.functionName ? { functionName: c.functionName } : {}), + abi: c.abi, + }) + } +} diff --git a/packages/core/src/actions/codegen/createSimulateContract.test-d.ts b/packages/core/src/actions/codegen/createSimulateContract.test-d.ts new file mode 100644 index 0000000000..91e5998977 --- /dev/null +++ b/packages/core/src/actions/codegen/createSimulateContract.test-d.ts @@ -0,0 +1,211 @@ +import { abi, config, mainnet, optimism } from '@wagmi/test' +import { http, type Address } from 'viem' +import { celo } from 'viem/chains' +import { assertType, expectTypeOf, test } from 'vitest' + +import { createConfig } from '../../createConfig.js' +import { createSimulateContract } from './createSimulateContract.js' + +test('default', async () => { + const simulateErc20 = createSimulateContract({ + abi: abi.erc20, + address: '0x', + }) + + const result = await simulateErc20(config, { + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + }) + + expectTypeOf(result).toMatchTypeOf<{ + result: boolean + request: { + chainId: 1 + abi: readonly [ + { + readonly name: 'transferFrom' + readonly type: 'function' + readonly stateMutability: 'nonpayable' + readonly inputs: readonly [ + { readonly type: 'address'; readonly name: 'sender' }, + { readonly type: 'address'; readonly name: 'recipient' }, + { readonly type: 'uint256'; readonly name: 'amount' }, + ] + readonly outputs: readonly [{ type: 'bool' }] + }, + ] + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + } + }>() +}) + +test('multichain address', async () => { + const simulateErc20 = createSimulateContract({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + const result = await simulateErc20(config, { + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: optimism.id, + }) + expectTypeOf(result.result).toEqualTypeOf() + + simulateErc20(config, { + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error chain id must match address keys + chainId: 420, + }) + + simulateErc20(config, { + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error address not allowed + address: '0x', + }) +}) + +test('overloads', async () => { + const simulateWriteOverloads = createSimulateContract({ + abi: abi.writeOverloads, + address: '0x', + }) + + const result1 = await simulateWriteOverloads(config, { + functionName: 'foo', + }) + assertType(result1.result) + + const result2 = await simulateWriteOverloads(config, { + functionName: 'foo', + args: [], + }) + assertType(result2.result) + + const result3 = await simulateWriteOverloads(config, { + functionName: 'foo', + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3.result) + + const result4 = await simulateWriteOverloads(config, { + functionName: 'foo', + args: ['0x', '0x'], + }) + assertType< + | { + foo: `0x${string}` + bar: `0x${string}` + } + | undefined + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + >(result4.result) +}) + +test('functionName', async () => { + const simulateErc20 = createSimulateContract({ + abi: abi.erc20, + address: '0x', + functionName: 'transferFrom', + }) + + const result = await simulateErc20(config, { + args: ['0x', '0x', 123n], + chainId: 1, + }) + + expectTypeOf(result).toMatchTypeOf<{ + result: boolean + request: { + chainId: 1 + abi: readonly [ + { + readonly name: 'transferFrom' + readonly type: 'function' + readonly stateMutability: 'nonpayable' + readonly inputs: readonly [ + { readonly type: 'address'; readonly name: 'sender' }, + { readonly type: 'address'; readonly name: 'recipient' }, + { readonly type: 'uint256'; readonly name: 'amount' }, + ] + readonly outputs: readonly [{ type: 'bool' }] + }, + ] + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + } + }>() +}) + +test('functionName with overloads', async () => { + const simulateWriteOverloads = createSimulateContract({ + abi: abi.writeOverloads, + address: '0x', + functionName: 'foo', + }) + + const result1 = await simulateWriteOverloads(config, {}) + assertType(result1.result) + + const result2 = await simulateWriteOverloads(config, { + args: [], + }) + assertType(result2.result) + + const result3 = await simulateWriteOverloads(config, { + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3.result) + + const result4 = await simulateWriteOverloads(config, { + args: ['0x', '0x'], + }) + assertType< + | { + foo: `0x${string}` + bar: `0x${string}` + } + | undefined + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + >(result4.result) +}) + +test('chain formatters', async () => { + const simulateErc20 = createSimulateContract({ + abi: abi.erc20, + address: '0x', + }) + + const config = createConfig({ + chains: [celo, mainnet], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + const response = await simulateErc20(config, { + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + }) + if (response.chainId === celo.id) { + expectTypeOf(response.request.feeCurrency).toEqualTypeOf< + `0x${string}` | undefined + >() + } + + const response2 = await simulateErc20(config, { + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: celo.id, + }) + expectTypeOf(response2.request.feeCurrency).toEqualTypeOf< + `0x${string}` | undefined + >() +}) diff --git a/packages/core/src/actions/codegen/createSimulateContract.test.ts b/packages/core/src/actions/codegen/createSimulateContract.test.ts new file mode 100644 index 0000000000..bd37d13770 --- /dev/null +++ b/packages/core/src/actions/codegen/createSimulateContract.test.ts @@ -0,0 +1,137 @@ +import { abi, address, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from '../connect.js' +import { disconnect } from '../disconnect.js' +import { createSimulateContract } from './createSimulateContract.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const simulateWagmiMintExample = createSimulateContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + }) + + await expect( + simulateWagmiMintExample(config, { + functionName: 'mint', + }), + ).resolves.toMatchInlineSnapshot(` + { + "chainId": 1, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": undefined, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + } + `) + + await disconnect(config, { connector }) +}) + +test('multichain', async () => { + await connect(config, { connector }) + + const simulateWagmiMintExample = createSimulateContract({ + address: { + [chain.mainnet.id]: address.wagmiMintExample, + [chain.mainnet2.id]: address.wagmiMintExample, + }, + abi: abi.wagmiMintExample, + }) + + await expect( + simulateWagmiMintExample(config, { + functionName: 'mint', + chainId: chain.mainnet2.id, + }), + ).resolves.toMatchInlineSnapshot(` + { + "chainId": 456, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": 456, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + } + `) + + await disconnect(config, { connector }) +}) + +test('functionName', async () => { + await connect(config, { connector }) + + const simulateWagmiMintExample = createSimulateContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'mint', + }) + + await expect( + simulateWagmiMintExample(config, {}), + ).resolves.toMatchInlineSnapshot(` + { + "chainId": 1, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": undefined, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + } + `) + + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/codegen/createSimulateContract.ts b/packages/core/src/actions/codegen/createSimulateContract.ts new file mode 100644 index 0000000000..016384e245 --- /dev/null +++ b/packages/core/src/actions/codegen/createSimulateContract.ts @@ -0,0 +1,122 @@ +import type { + Abi, + Account, + Address, + Chain, + ContractFunctionArgs, + ContractFunctionName, + SimulateContractParameters as viem_SimulateContractParameters, +} from 'viem' + +import type { Config } from '../../createConfig.js' +import type { SelectChains } from '../../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../../types/properties.js' +import type { UnionCompute, UnionStrictOmit } from '../../types/utils.js' +import { getAccount } from '../getAccount.js' +import { getChainId } from '../getChainId.js' +import { + type SimulateContractReturnType, + simulateContract, +} from '../simulateContract.js' + +type stateMutability = 'nonpayable' | 'payable' + +export type CreateSimulateContractParameters< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +> = { + abi: abi | Abi | readonly unknown[] + address?: address | Address | Record | undefined + functionName?: + | functionName + | ContractFunctionName + | undefined +} + +export type CreateSimulateContractReturnType< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined, + functionName extends ContractFunctionName | undefined, +> = < + config extends Config, + name extends functionName extends ContractFunctionName + ? functionName + : ContractFunctionName, + args extends ContractFunctionArgs, + chainId extends config['chains'][number]['id'] | undefined = undefined, + /// + chains extends readonly Chain[] = SelectChains, +>( + config: config, + parameters: { + [key in keyof chains]: UnionCompute< + UnionStrictOmit< + viem_SimulateContractParameters< + abi, + name, + args, + chains[key], + chains[key], + Account | Address + >, + | 'abi' + | 'chain' + | (address extends undefined ? never : 'address') + | (functionName extends undefined ? never : 'functionName') + > + > & + ChainIdParameter & + ConnectorParameter & { + chainId?: address extends Record + ? + | keyof address + | (chainId extends keyof address ? chainId : never) + | undefined + : chainId | number | undefined + } + }[number], +) => Promise> + +export function createSimulateContract< + const abi extends Abi | readonly unknown[], + const address extends + | Address + | Record + | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +>( + c: CreateSimulateContractParameters, +): CreateSimulateContractReturnType { + if (c.address !== undefined && typeof c.address === 'object') + return (config, parameters) => { + const configChainId = getChainId(config) + const account = getAccount(config) + const chainId = + (parameters as { chainId?: number })?.chainId ?? + account.chainId ?? + configChainId + return simulateContract(config, { + ...(parameters as any), + ...(c.functionName ? { functionName: c.functionName } : {}), + address: c.address?.[chainId], + abi: c.abi, + }) + } + + return (config, parameters) => { + return simulateContract(config, { + ...(parameters as any), + ...(c.address ? { address: c.address } : {}), + ...(c.functionName ? { functionName: c.functionName } : {}), + abi: c.abi, + }) + } +} diff --git a/packages/core/src/actions/codegen/createWatchContractEvent.test-d.ts b/packages/core/src/actions/codegen/createWatchContractEvent.test-d.ts new file mode 100644 index 0000000000..6c0485f39d --- /dev/null +++ b/packages/core/src/actions/codegen/createWatchContractEvent.test-d.ts @@ -0,0 +1,123 @@ +import { abi, config, mainnet, optimism } from '@wagmi/test' +import { http, webSocket } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../../createConfig.js' +import { createWatchContractEvent } from './createWatchContractEvent.js' + +test('default', () => { + const watchErc20Event = createWatchContractEvent({ + abi: abi.erc20, + }) + + watchErc20Event(config, { + eventName: 'Transfer', + chainId: 1, + onLogs(logs) { + expectTypeOf(logs[0]!.eventName).toEqualTypeOf<'Transfer'>() + expectTypeOf(logs[0]!.args).toEqualTypeOf<{ + from?: `0x${string}` | undefined + to?: `0x${string}` | undefined + value?: bigint | undefined + }>() + }, + }) +}) + +test('multichain address', () => { + const watchErc20Event = createWatchContractEvent({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + watchErc20Event(config, { + eventName: 'Transfer', + chainId: mainnet.id, + // ^? + onLogs() {}, + }) + + watchErc20Event(config, { + eventName: 'Transfer', + // @ts-expect-error chain id must match address keys + chainId: 420, + onLogs() {}, + }) + + watchErc20Event(config, { + eventName: 'Transfer', + // @ts-expect-error chain id must match address keys + address: '0x', + onLogs() {}, + }) +}) + +test('differing transports', () => { + const watchErc20Event = createWatchContractEvent({ + abi: abi.erc20, + }) + + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + watchErc20Event(config, { + poll: false, + address: '0x', + onLogs() {}, + }) + + watchErc20Event(config, { + chainId: mainnet.id, + poll: true, + address: '0x', + onLogs() {}, + }) + watchErc20Event(config, { + config, + chainId: mainnet.id, + // @ts-expect-error poll required since http transport + poll: false, + address: '0x', + onLogs() {}, + }) + + watchErc20Event(config, { + chainId: optimism.id, + poll: true, + address: '0x', + onLogs() {}, + }) + watchErc20Event(config, { + chainId: optimism.id, + poll: false, + address: '0x', + onLogs() {}, + }) +}) + +test('eventName', () => { + const watchErc20Event = createWatchContractEvent({ + abi: abi.erc20, + eventName: 'Transfer', + }) + + watchErc20Event(config, { + chainId: 1, + onLogs(logs) { + expectTypeOf(logs[0]!.eventName).toEqualTypeOf<'Transfer'>() + expectTypeOf(logs[0]!.args).toEqualTypeOf<{ + from?: `0x${string}` | undefined + to?: `0x${string}` | undefined + value?: bigint | undefined + }>() + }, + }) +}) diff --git a/packages/core/src/actions/codegen/createWatchContractEvent.test.ts b/packages/core/src/actions/codegen/createWatchContractEvent.test.ts new file mode 100644 index 0000000000..19ba09b2a2 --- /dev/null +++ b/packages/core/src/actions/codegen/createWatchContractEvent.test.ts @@ -0,0 +1,41 @@ +import { abi, address, chain, config } from '@wagmi/test' +import type { WatchEventOnLogsParameter } from 'viem' +import { test } from 'vitest' + +import { createWatchContractEvent } from './createWatchContractEvent.js' + +test('default', async () => { + const watchErc20Event = createWatchContractEvent({ + address: address.usdc, + abi: abi.wagmiMintExample, + }) + + let logs: WatchEventOnLogsParameter = [] + const unwatch = watchErc20Event(config, { + eventName: 'Transfer', + onLogs(next) { + logs = logs.concat(next) + }, + }) + unwatch() +}) + +test('multichain', async () => { + const watchErc20Event = createWatchContractEvent({ + address: { + [chain.mainnet.id]: address.usdc, + [chain.mainnet2.id]: address.usdc, + }, + abi: abi.wagmiMintExample, + }) + + let logs: WatchEventOnLogsParameter = [] + const unwatch = watchErc20Event(config, { + eventName: 'Transfer', + chainId: chain.mainnet2.id, + onLogs(next) { + logs = logs.concat(next) + }, + }) + unwatch() +}) diff --git a/packages/core/src/actions/codegen/createWatchContractEvent.ts b/packages/core/src/actions/codegen/createWatchContractEvent.ts new file mode 100644 index 0000000000..2817efc704 --- /dev/null +++ b/packages/core/src/actions/codegen/createWatchContractEvent.ts @@ -0,0 +1,88 @@ +import type { Abi, Address, ContractEventName } from 'viem' + +import type { Config } from '../../createConfig.js' +import type { UnionCompute, UnionStrictOmit } from '../../types/utils.js' +import { getAccount } from '../getAccount.js' +import { getChainId } from '../getChainId.js' +import { + type WatchContractEventParameters, + type WatchContractEventReturnType, + watchContractEvent, +} from '../watchContractEvent.js' + +export type CreateWatchContractEventParameters< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined = undefined, + eventName extends ContractEventName | undefined = undefined, +> = { + abi: abi | Abi | readonly unknown[] + address?: address | Address | Record | undefined + eventName?: eventName | ContractEventName | undefined +} + +export type CreateWatchContractEventReturnType< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined, + eventName extends ContractEventName | undefined, + /// + omittedProperties extends 'abi' | 'address' | 'chainId' | 'eventName' = + | 'abi' + | (address extends undefined ? never : 'address') + | (address extends Record ? 'chainId' : never) + | (eventName extends undefined ? never : 'eventName'), +> = < + config extends Config, + name extends eventName extends ContractEventName + ? eventName + : ContractEventName, + strict extends boolean | undefined = undefined, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + config: config, + parameters: UnionCompute< + UnionStrictOmit< + WatchContractEventParameters, + omittedProperties + > + > & + (address extends Record + ? { chainId?: keyof address | undefined } + : unknown), +) => WatchContractEventReturnType + +export function createWatchContractEvent< + const abi extends Abi | readonly unknown[], + const address extends + | Address + | Record + | undefined = undefined, + eventName extends ContractEventName | undefined = undefined, +>( + c: CreateWatchContractEventParameters, +): CreateWatchContractEventReturnType { + if (c.address !== undefined && typeof c.address === 'object') + return (config, parameters) => { + const configChainId = getChainId(config) + const account = getAccount(config) + const chainId = + (parameters as { chainId?: number })?.chainId ?? + account.chainId ?? + configChainId + return watchContractEvent(config, { + ...(parameters as any), + ...(c.eventName ? { eventName: c.eventName } : {}), + address: c.address?.[chainId], + abi: c.abi, + }) + } + + return (config, parameters) => { + return watchContractEvent(config, { + ...(parameters as any), + ...(c.address ? { address: c.address } : {}), + ...(c.eventName ? { eventName: c.eventName } : {}), + abi: c.abi, + }) + } +} diff --git a/packages/core/src/actions/codegen/createWriteContract.test-d.ts b/packages/core/src/actions/codegen/createWriteContract.test-d.ts new file mode 100644 index 0000000000..9c69ede5bf --- /dev/null +++ b/packages/core/src/actions/codegen/createWriteContract.test-d.ts @@ -0,0 +1,129 @@ +import { abi, config, mainnet, optimism } from '@wagmi/test' +import { test } from 'vitest' + +import { simulateContract } from '../simulateContract.js' +import { createWriteContract } from './createWriteContract.js' + +test('default', () => { + const writeErc20 = createWriteContract({ + abi: abi.erc20, + address: '0x', + }) + + writeErc20(config, { + functionName: 'transfer', + args: ['0x', 123n], + }) +}) + +test('multichain address', () => { + const writeErc20 = createWriteContract({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + writeErc20(config, { + functionName: 'transfer', + args: ['0x', 123n], + chainId: mainnet.id, + // ^? + }) + + writeErc20(config, { + functionName: 'transfer', + args: ['0x', 123n], + // @ts-expect-error chain id must match address keys + chainId: 420, + }) + + writeErc20(config, { + // @ts-expect-error address not allowed + address: '0x', + functionName: 'transfer', + args: ['0x', 123n], + }) +}) + +test('overloads', () => { + const writeOverloads = createWriteContract({ + abi: abi.writeOverloads, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + writeOverloads(config, { + functionName: 'foo', + args: [], + }) + + writeOverloads(config, { + functionName: 'foo', + args: ['0x'], + }) + + writeOverloads(config, { + functionName: 'foo', + args: ['0x', '0x'], + }) +}) + +test('useSimulateContract', async () => { + const writeErc20 = createWriteContract({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + const { request } = await simulateContract(config, { + account: '0x', + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + }) + + writeErc20(config, request) +}) + +test('functionName', () => { + const writeErc20 = createWriteContract({ + abi: abi.erc20, + address: '0x', + functionName: 'transfer', + }) + + writeErc20(config, { + args: ['0x', 123n], + }) +}) + +test('functionName with overloads', () => { + const writeOverloads = createWriteContract({ + abi: abi.writeOverloads, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + functionName: 'foo', + }) + + writeOverloads(config, { + args: [], + }) + + writeOverloads(config, { + args: ['0x'], + }) + + writeOverloads(config, { + args: ['0x', '0x'], + }) +}) diff --git a/packages/core/src/actions/codegen/createWriteContract.test.ts b/packages/core/src/actions/codegen/createWriteContract.test.ts new file mode 100644 index 0000000000..04511d13ab --- /dev/null +++ b/packages/core/src/actions/codegen/createWriteContract.test.ts @@ -0,0 +1,11 @@ +import { abi } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { createWriteContract } from './createWriteContract.js' + +test('default', () => { + const writeErc20 = createWriteContract({ + abi: abi.erc20, + }) + expect(writeErc20).toBeDefined() +}) diff --git a/packages/core/src/actions/codegen/createWriteContract.ts b/packages/core/src/actions/codegen/createWriteContract.ts new file mode 100644 index 0000000000..6c1d891a3f --- /dev/null +++ b/packages/core/src/actions/codegen/createWriteContract.ts @@ -0,0 +1,145 @@ +import type { + Abi, + Account, + Address, + Chain, + ContractFunctionArgs, + ContractFunctionName, + WriteContractParameters as viem_WriteContractParameters, +} from 'viem' + +import type { Config } from '../../createConfig.js' +import type { SelectChains } from '../../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../../types/properties.js' +import type { + Compute, + UnionCompute, + UnionStrictOmit, +} from '../../types/utils.js' +import { getAccount } from '../getAccount.js' +import { getChainId } from '../getChainId.js' +import { + type WriteContractReturnType, + writeContract, +} from '../writeContract.js' + +type stateMutability = 'nonpayable' | 'payable' + +export type CreateWriteContractParameters< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +> = { + abi: abi | Abi | readonly unknown[] + address?: address | Address | Record | undefined + functionName?: + | functionName + | ContractFunctionName + | undefined +} + +export type CreateWriteContractReturnType< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined, + functionName extends ContractFunctionName | undefined, +> = < + config extends Config, + name extends functionName extends ContractFunctionName + ? functionName + : ContractFunctionName, + args extends ContractFunctionArgs, + chainId extends config['chains'][number]['id'], + /// + allFunctionNames = ContractFunctionName, + chains extends readonly Chain[] = SelectChains, + omittedProperties extends 'abi' | 'address' | 'functionName' = + | 'abi' + | (address extends undefined ? never : 'address') + | (functionName extends undefined ? never : 'functionName'), +>( + config: config, + parameters: UnionCompute< + { + [key in keyof chains]: UnionStrictOmit< + viem_WriteContractParameters< + abi, + name, + args, + chains[key], + Account, + chains[key], + allFunctionNames + >, + omittedProperties | 'chain' + > + }[number] & + (address extends Record + ? { + chainId?: + | keyof address + | (chainId extends keyof address ? chainId : never) + | undefined + } + : Compute>) & + ConnectorParameter & { + /** @deprecated */ + __mode?: 'prepared' + } + >, +) => Promise + +export function createWriteContract< + const abi extends Abi | readonly unknown[], + const address extends + | Address + | Record + | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +>( + c: CreateWriteContractParameters, +): CreateWriteContractReturnType { + if (c.address !== undefined && typeof c.address === 'object') + return (config, parameters) => { + const configChainId = getChainId(config) + const account = getAccount(config) + + let chainId: number | undefined + if (parameters.chainId) chainId = parameters.chainId + else if ( + (parameters as unknown as { account: Address | Account | undefined }) + .account && + (parameters as unknown as { account: Address | Account | undefined }) + .account === account.address + ) + chainId = account.chainId + else if ( + (parameters as unknown as { account: Address | Account | undefined }) + .account === undefined + ) + chainId = account.chainId + else chainId = configChainId + + return writeContract(config, { + ...(parameters as any), + ...(c.functionName ? { functionName: c.functionName } : {}), + address: chainId ? c.address?.[chainId] : undefined, + abi: c.abi, + }) + } + + return (config, parameters) => { + return writeContract(config, { + ...(parameters as any), + ...(c.address ? { address: c.address } : {}), + ...(c.functionName ? { functionName: c.functionName } : {}), + abi: c.abi, + }) + } +} diff --git a/packages/core/src/actions/connect.test-d.ts b/packages/core/src/actions/connect.test-d.ts new file mode 100644 index 0000000000..ad790b1209 --- /dev/null +++ b/packages/core/src/actions/connect.test-d.ts @@ -0,0 +1,48 @@ +import { accounts } from '@wagmi/test' +import { http } from 'viem' +import { mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import type { CreateConnectorFn } from '../connectors/createConnector.js' +import { mock } from '../connectors/mock.js' +import { type Connector, createConfig } from '../createConfig.js' +import { connect } from './connect.js' + +const config = createConfig({ + chains: [mainnet], + transports: { [mainnet.id]: http() }, +}) + +test('parameters: connector (ConnectorFn)', () => { + const connectorFn = mock({ accounts }) + + connect(config, { + connector: connectorFn, + foo: 'bar', + }) + expectTypeOf< + typeof connectorFn extends CreateConnectorFn ? true : false + >().toEqualTypeOf() + + type Result = NonNullable< + Parameters>[1] + > + expectTypeOf().toEqualTypeOf() +}) + +test('parameters: connector (Connector)', () => { + const connector = config._internal.connectors.setup(mock({ accounts })) + + connect(config, { + connector, + foo: 'bar', + }) + expectTypeOf< + typeof connector extends Connector ? true : false + >().toEqualTypeOf() + + type Result = NonNullable< + Parameters>[1] + > + expectTypeOf().toEqualTypeOf() +}) diff --git a/packages/core/src/actions/connect.test.ts b/packages/core/src/actions/connect.test.ts new file mode 100644 index 0000000000..eece8c3a4d --- /dev/null +++ b/packages/core/src/actions/connect.test.ts @@ -0,0 +1,71 @@ +import { accounts, chain, config } from '@wagmi/test' +import { beforeEach, expect, test } from 'vitest' + +import { mock } from '../connectors/mock.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' + +const connector = config._internal.connectors.setup(mock({ accounts })) + +beforeEach(async () => { + if (config.state.current === connector.uid) + await disconnect(config, { connector }) +}) + +test('default', async () => { + await expect(connect(config, { connector })).resolves.toMatchObject( + expect.objectContaining({ + accounts: expect.any(Array), + chainId: expect.any(Number), + }), + ) +}) + +test('parameters: chainId', async () => { + const chainId = chain.mainnet2.id + await expect(connect(config, { connector, chainId })).resolves.toMatchObject( + expect.objectContaining({ + accounts: expect.any(Array), + chainId, + }), + ) +}) + +test('parameters: connector', async () => { + const connector_ = config._internal.connectors.setup(mock({ accounts })) + await expect( + connect(config, { connector: connector_ }), + ).resolves.toMatchObject( + expect.objectContaining({ + accounts: expect.any(Array), + chainId: expect.any(Number), + }), + ) + await disconnect(config, { connector: connector_ }) +}) + +test('behavior: user rejected request', async () => { + const connector_ = config._internal.connectors.setup( + mock({ + accounts, + features: { connectError: true }, + }), + ) + await expect( + connect(config, { connector: connector_ }), + ).rejects.toMatchInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to connect. + Version: viem@2.29.2] + `) +}) + +test('behavior: already connected', async () => { + await connect(config, { connector }) + await expect(connect(config, { connector })).rejects.toMatchInlineSnapshot(` + [ConnectorAlreadyConnectedError: Connector already connected. + + Version: @wagmi/core@x.y.z] + `) +}) diff --git a/packages/core/src/actions/connect.ts b/packages/core/src/actions/connect.ts new file mode 100644 index 0000000000..f69809e504 --- /dev/null +++ b/packages/core/src/actions/connect.ts @@ -0,0 +1,110 @@ +import type { + Address, + ResourceUnavailableRpcErrorType, + UserRejectedRequestErrorType, +} from 'viem' + +import type { CreateConnectorFn } from '../connectors/createConnector.js' +import type { Config, Connector } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import { + ConnectorAlreadyConnectedError, + type ConnectorAlreadyConnectedErrorType, +} from '../errors/config.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' + +export type ConnectParameters< + config extends Config = Config, + connector extends Connector | CreateConnectorFn = + | Connector + | CreateConnectorFn, + /// + parameters extends unknown | undefined = + | (connector extends CreateConnectorFn + ? Omit< + NonNullable['connect']>[0]>, + 'isReconnecting' + > + : never) + | (connector extends Connector + ? Omit< + NonNullable[0]>, + 'isReconnecting' + > + : never), +> = Compute< + ChainIdParameter & { + connector: connector | CreateConnectorFn + } +> & + parameters + +export type ConnectReturnType = { + accounts: readonly [Address, ...Address[]] + chainId: + | config['chains'][number]['id'] + | (number extends config['chains'][number]['id'] ? number : number & {}) +} + +export type ConnectErrorType = + | ConnectorAlreadyConnectedErrorType + // connector.connect() + | UserRejectedRequestErrorType + | ResourceUnavailableRpcErrorType + // base + | BaseErrorType + | ErrorType + +/** https://wagmi.sh/core/api/actions/connect */ +export async function connect< + config extends Config, + connector extends Connector | CreateConnectorFn, +>( + config: config, + parameters: ConnectParameters, +): Promise> { + // "Register" connector if not already created + let connector: Connector + if (typeof parameters.connector === 'function') { + connector = config._internal.connectors.setup(parameters.connector) + } else connector = parameters.connector + + // Check if connector is already connected + if (connector.uid === config.state.current) + throw new ConnectorAlreadyConnectedError() + + try { + config.setState((x) => ({ ...x, status: 'connecting' })) + connector.emitter.emit('message', { type: 'connecting' }) + + const { connector: _, ...rest } = parameters + const data = await connector.connect(rest) + const accounts = data.accounts as readonly [Address, ...Address[]] + + connector.emitter.off('connect', config._internal.events.connect) + connector.emitter.on('change', config._internal.events.change) + connector.emitter.on('disconnect', config._internal.events.disconnect) + + await config.storage?.setItem('recentConnectorId', connector.id) + config.setState((x) => ({ + ...x, + connections: new Map(x.connections).set(connector.uid, { + accounts, + chainId: data.chainId, + connector: connector, + }), + current: connector.uid, + status: 'connected', + })) + + return { accounts, chainId: data.chainId } + } catch (error) { + config.setState((x) => ({ + ...x, + // Keep existing connector connected in case of error + status: x.current ? 'connected' : 'disconnected', + })) + throw error + } +} diff --git a/packages/core/src/actions/deployContract.test-d.ts b/packages/core/src/actions/deployContract.test-d.ts new file mode 100644 index 0000000000..b7a6a897d3 --- /dev/null +++ b/packages/core/src/actions/deployContract.test-d.ts @@ -0,0 +1,71 @@ +import { abi, bytecode, config } from '@wagmi/test' +import { http } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type DeployContractParameters, + deployContract, +} from './deployContract.js' + +test('default', async () => { + await deployContract(config, { + abi: abi.bayc, + bytecode: bytecode.bayc, + args: ['Bored Ape Wagmi Club', 'BAYC', 69420n, 0n], + chainId: mainnet.id, + }) +}) + +test('chain formatters', () => { + const config = createConfig({ + chains: [mainnet, celo], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = DeployContractParameters + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + deployContract(config, { + abi: abi.bayc, + bytecode: bytecode.bayc, + args: ['Bored Ape Wagmi Club', 'BAYC', 69420n, 0n], + feeCurrency: '0x', + }) + + type Result2 = DeployContractParameters< + typeof abi.bayc, + typeof config, + typeof celo.id + > + expectTypeOf().toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + deployContract(config, { + chainId: celo.id, + abi: abi.bayc, + bytecode: bytecode.bayc, + args: ['Bored Ape Wagmi Club', 'BAYC', 69420n, 0n], + feeCurrency: '0x', + }) + + type Result3 = DeployContractParameters< + typeof abi.bayc, + typeof config, + typeof mainnet.id + > + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + deployContract(config, { + chainId: mainnet.id, + abi: abi.bayc, + bytecode: bytecode.bayc, + args: ['Bored Ape Wagmi Club', 'BAYC', 69420n, 0n], + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/core/src/actions/deployContract.test.ts b/packages/core/src/actions/deployContract.test.ts new file mode 100644 index 0000000000..bebf42342e --- /dev/null +++ b/packages/core/src/actions/deployContract.test.ts @@ -0,0 +1,67 @@ +import { + abi, + bytecode, + config, + testClient, + transactionHashRegex, +} from '@wagmi/test' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { deployContract } from './deployContract.js' +import { disconnect } from './disconnect.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + await expect( + deployContract(config, { + abi: abi.bayc, + bytecode: bytecode.bayc, + args: ['Bored Ape Wagmi Club', 'BAYC', 69420n, 0n], + }), + ).resolves.toMatch(transactionHashRegex) + await disconnect(config, { connector }) +}) + +test('behavior: no funds', async () => { + const data = await connect(config, { connector }) + const connectedAddress = data.accounts[0] + + await testClient.mainnet.setBalance({ + address: connectedAddress, + value: parseEther('0'), + }) + + await expect( + deployContract(config, { + chainId: testClient.mainnet.chain.id, + abi: abi.bayc, + bytecode: bytecode.bayc, + args: ['Bored Ape Wagmi Club', 'BAYC', 69420n, 0n], + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [TransactionExecutionError: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account. + + This error could arise when the account does not have enough funds to: + - pay for the total gas fee, + - pay for the value to send. + + The cost of the transaction is calculated as \`gas * gas fee + value\`, where: + - \`gas\` is the amount of gas needed for transaction to execute, + - \`gas fee\` is the gas fee, + - \`value\` is the amount of ether to send to the recipient. + + Request Arguments: + chain: undefined (id: 1) + from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + data: 0x608060405260405180602001604052806000815250600b90805190602001906200002b92919062000484565b506000600f60006101000a81548160ff0219169083151502179055503480156200005457600080fd5b50604051620046d0380380620046d0833981810160405260808110156200007a57600080fd5b81019080805160405193929190846401000000008211156200009b57600080fd5b83820191506020820185811115620000b257600080fd5b8251866001820283011164010000000082111715620000d057600080fd5b8083526020830192505050908051906020019080838360005b8381101562000106578082015181840152602081019050620000e9565b50505050905090810190601f168015620001345780820380516001836020036101000a031916815260200191505b50604052602001805160405193929190846401000000008211156200015857600080fd5b838201915060208201858111156200016f57600080fd5b82518660018202830111640100000000821117156200018d57600080fd5b8083526020830192505050908051906020019080838360005b83811015620001c3578082015181840152602081019050620001a6565b50505050905090810190601f168015620001f15780820380516001836020036101000a031916815260200191505b5060405260200180519060200190929190805190602001909291905050508383620002296301ffc9a760e01b6200037360201b60201c565b81600690805190602001906200024192919062000484565b5080600790805190602001906200025a92919062000484565b50620002736380ac58cd60e01b6200037360201b60201c565b6200028b635b5e139f60e01b6200037360201b60201c565b620002a363780e9d6360e01b6200037360201b60201c565b50506000620002b76200047c60201b60201c565b905080600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35081600e81905550620bdd808101601081905550505050506200052a565b63ffffffff60e01b817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141562000410576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4552433136353a20696e76616c696420696e746572666163652069640000000081525060200191505060405180910390fd5b6001600080837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b600033905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620004c757805160ff1916838001178555620004f8565b82800160010185558215620004f8579182015b82811115620004f7578251825591602001919060010190620004da565b5b5090506200050791906200050b565b5090565b5b80821115620005265760008160009055506001016200050c565b5090565b614196806200053a6000396000f3fe60806040526004361061021a5760003560e01c80636c0360eb11610123578063b0f67427116100ab578063e36d64981161006f578063e36d649814610ddf578063e985e9c514610e0a578063e986655014610e91578063eb8d244414610ea8578063f2fde38b14610ed55761021a565b8063b0f6742714610bac578063b88d4fde14610bc3578063bb8a16bd14610cd5578063c87b56dd14610d00578063cb774d4714610db45761021a565b80637d17fcbe116100f25780637d17fcbe14610a395780638da5cb5b14610a5057806395d89b4114610a91578063a22cb46514610b21578063a723533e14610b7e5761021a565b80636c0360eb1461090257806370a0823114610992578063715018a6146109f75780637a3f451e14610a0e5761021a565b80632f745c59116101a65780634f6ccce7116101755780634f6ccce7146106cb57806355f804b31461071a578063571dff3b146107e2578063607e20e31461080d5780636352211e1461089d5761021a565b80632f745c59146105b357806334918dfd146106225780633ccfd60b1461063957806342842e0e146106505761021a565b8063095ea7b3116101ed578063095ea7b3146103bf578063109695231461041a57806318160ddd146104e257806318e20a381461050d57806323b872dd146105385761021a565b8063018a2c371461021f57806301ffc9a71461025a57806306fdde03146102ca578063081812fc1461035a575b600080fd5b34801561022b57600080fd5b506102586004803603602081101561024257600080fd5b8101908080359060200190929190505050610f26565b005b34801561026657600080fd5b506102b26004803603602081101561027d57600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610fdf565b60405180821515815260200191505060405180910390f35b3480156102d657600080fd5b506102df611046565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561031f578082015181840152602081019050610304565b50505050905090810190601f16801561034c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561036657600080fd5b506103936004803603602081101561037d57600080fd5b81019080803590602001909291905050506110e8565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156103cb57600080fd5b50610418600480360360408110156103e257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611183565b005b34801561042657600080fd5b506104e06004803603602081101561043d57600080fd5b810190808035906020019064010000000081111561045a57600080fd5b82018360208201111561046c57600080fd5b8035906020019184600183028401116401000000008311171561048e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506112c7565b005b3480156104ee57600080fd5b506104f7611390565b6040518082815260200191505060405180910390f35b34801561051957600080fd5b506105226113a1565b6040518082815260200191505060405180910390f35b34801561054457600080fd5b506105b16004803603606081101561055b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506113a7565b005b3480156105bf57600080fd5b5061060c600480360360408110156105d657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061141d565b6040518082815260200191505060405180910390f35b34801561062e57600080fd5b50610637611478565b005b34801561064557600080fd5b5061064e611553565b005b34801561065c57600080fd5b506106c96004803603606081101561067357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611651565b005b3480156106d757600080fd5b50610704600480360360208110156106ee57600080fd5b8101908080359060200190929190505050611671565b6040518082815260200191505060405180910390f35b34801561072657600080fd5b506107e06004803603602081101561073d57600080fd5b810190808035906020019064010000000081111561075a57600080fd5b82018360208201111561076c57600080fd5b8035906020019184600183028401116401000000008311171561078e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611694565b005b3480156107ee57600080fd5b506107f761174f565b6040518082815260200191505060405180910390f35b34801561081957600080fd5b50610822611754565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610862578082015181840152602081019050610847565b50505050905090810190601f16801561088f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156108a957600080fd5b506108d6600480360360208110156108c057600080fd5b81019080803590602001909291905050506117f2565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561090e57600080fd5b50610917611829565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561095757808201518184015260208101905061093c565b50505050905090810190601f1680156109845780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561099e57600080fd5b506109e1600480360360208110156109b557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506118cb565b6040518082815260200191505060405180910390f35b348015610a0357600080fd5b50610a0c6119a0565b005b348015610a1a57600080fd5b50610a23611b10565b6040518082815260200191505060405180910390f35b348015610a4557600080fd5b50610a4e611b1c565b005b348015610a5c57600080fd5b50610a65611c4c565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610a9d57600080fd5b50610aa6611c76565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610ae6578082015181840152602081019050610acb565b50505050905090810190601f168015610b135780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b348015610b2d57600080fd5b50610b7c60048036036040811015610b4457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803515159060200190929190505050611d18565b005b610baa60048036036020811015610b9457600080fd5b8101908080359060200190929190505050611ece565b005b348015610bb857600080fd5b50610bc1612127565b005b348015610bcf57600080fd5b50610cd360048036036080811015610be657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190640100000000811115610c4d57600080fd5b820183602082011115610c5f57600080fd5b80359060200191846001830284011164010000000083111715610c8157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061220b565b005b348015610ce157600080fd5b50610cea612283565b6040518082815260200191505060405180910390f35b348015610d0c57600080fd5b50610d3960048036036020811015610d2357600080fd5b8101908080359060200190929190505050612289565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610d79578082015181840152602081019050610d5e565b50505050905090810190601f168015610da65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b348015610dc057600080fd5b50610dc961255a565b6040518082815260200191505060405180910390f35b348015610deb57600080fd5b50610df4612560565b6040518082815260200191505060405180910390f35b348015610e1657600080fd5b50610e7960048036036040811015610e2d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612566565b60405180821515815260200191505060405180910390f35b348015610e9d57600080fd5b50610ea66125fa565b005b348015610eb457600080fd5b50610ebd612764565b60405180821515815260200191505060405180910390f35b348015610ee157600080fd5b50610f2460048036036020811015610ef857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612777565b005b610f2e61296c565b73ffffffffffffffffffffffffffffffffffffffff16610f4c611c4c565b73ffffffffffffffffffffffffffffffffffffffff1614610fd5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b8060108190555050565b6000806000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900460ff169050919050565b606060068054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156110de5780601f106110b3576101008083540402835291602001916110de565b820191906000526020600020905b8154815290600101906020018083116110c157829003601f168201915b5050505050905090565b60006110f382612974565b611148576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061408b602c913960400191505060405180910390fd5b6004600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b600061118e826117f2565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061410f6021913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661123461296c565b73ffffffffffffffffffffffffffffffffffffffff16148061126357506112628161125d61296c565b612566565b5b6112b8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526038815260200180613f956038913960400191505060405180910390fd5b6112c28383612991565b505050565b6112cf61296c565b73ffffffffffffffffffffffffffffffffffffffff166112ed611c4c565b73ffffffffffffffffffffffffffffffffffffffff1614611376576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b80600b908051906020019061138c929190613de6565b5050565b600061139c6002612a4a565b905090565b60105481565b6113b86113b261296c565b82612a5f565b61140d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260318152602001806141306031913960400191505060405180910390fd5b611418838383612b53565b505050565b600061147082600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020612d9690919063ffffffff16565b905092915050565b61148061296c565b73ffffffffffffffffffffffffffffffffffffffff1661149e611c4c565b73ffffffffffffffffffffffffffffffffffffffff1614611527576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600f60009054906101000a900460ff1615600f60006101000a81548160ff021916908315150217905550565b61155b61296c565b73ffffffffffffffffffffffffffffffffffffffff16611579611c4c565b73ffffffffffffffffffffffffffffffffffffffff1614611602576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60004790503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561164d573d6000803e3d6000fd5b5050565b61166c8383836040518060200160405280600081525061220b565b505050565b600080611688836002612db090919063ffffffff16565b50905080915050919050565b61169c61296c565b73ffffffffffffffffffffffffffffffffffffffff166116ba611c4c565b73ffffffffffffffffffffffffffffffffffffffff1614611743576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61174c81612ddc565b50565b601481565b600b8054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156117ea5780601f106117bf576101008083540402835291602001916117ea565b820191906000526020600020905b8154815290600101906020018083116117cd57829003601f168201915b505050505081565b600061182282604051806060016040528060298152602001613ff7602991396002612df69092919063ffffffff16565b9050919050565b606060098054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156118c15780601f10611896576101008083540402835291602001916118c1565b820191906000526020600020905b8154815290600101906020018083116118a457829003601f168201915b5050505050905090565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611952576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180613fcd602a913960400191505060405180910390fd5b611999600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020612e15565b9050919050565b6119a861296c565b73ffffffffffffffffffffffffffffffffffffffff166119c6611c4c565b73ffffffffffffffffffffffffffffffffffffffff1614611a4f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36000600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b67011c37937e08000081565b611b2461296c565b73ffffffffffffffffffffffffffffffffffffffff16611b42611c4c565b73ffffffffffffffffffffffffffffffffffffffff1614611bcb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b6000600d5414611c43576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f5374617274696e6720696e64657820697320616c72656164792073657400000081525060200191505060405180910390fd5b43600c81905550565b6000600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b606060078054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611d0e5780601f10611ce357610100808354040283529160200191611d0e565b820191906000526020600020905b815481529060010190602001808311611cf157829003601f168201915b5050505050905090565b611d2061296c565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611dc1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4552433732313a20617070726f766520746f2063616c6c65720000000000000081525060200191505060405180910390fd5b8060056000611dce61296c565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff16611e7b61296c565b73ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c318360405180821515815260200191505060405180910390a35050565b600f60009054906101000a900460ff16611f50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f53616c65206d7573742062652061637469766520746f206d696e74204170650081525060200191505060405180910390fd5b6014811115611faa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180613f746021913960400191505060405180910390fd5b600e54611fc782611fb9611390565b612e2a90919063ffffffff16565b111561201e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260288152602001806140426028913960400191505060405180910390fd5b3461203a8267011c37937e080000612eb290919063ffffffff16565b11156120ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f45746865722076616c75652073656e74206973206e6f7420636f72726563740081525060200191505060405180910390fd5b60005b818110156120ef5760006120c3611390565b9050600e546120d0611390565b10156120e1576120e03382612f38565b5b5080806001019150506120b1565b506000600c541480156121175750600e54612108611390565b148061211657506010544210155b5b156121245743600c819055505b50565b61212f61296c565b73ffffffffffffffffffffffffffffffffffffffff1661214d611c4c565b73ffffffffffffffffffffffffffffffffffffffff16146121d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b60006121e0611390565b905060005b601e811015612207576121fa33828401612f38565b80806001019150506121e5565b5050565b61221c61221661296c565b83612a5f565b612271576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260318152602001806141306031913960400191505060405180910390fd5b61227d84848484612f56565b50505050565b600e5481565b606061229482612974565b6122e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f8152602001806140e0602f913960400191505060405180910390fd5b6060600860008481526020019081526020016000208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156123925780601f1061236757610100808354040283529160200191612392565b820191906000526020600020905b81548152906001019060200180831161237557829003601f168201915b5050505050905060606123a3611829565b90506000815114156123b9578192505050612555565b60008251111561248a5780826040516020018083805190602001908083835b602083106123fb57805182526020820191506020810190506020830392506123d8565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b6020831061244c5780518252602082019150602081019050602083039250612429565b6001836020036101000a0380198251168184511680821785525050505050509050019250505060405160208183030381529060405292505050612555565b8061249485612fc8565b6040516020018083805190602001908083835b602083106124ca57805182526020820191506020810190506020830392506124a7565b6001836020036101000a03801982511681845116808217855250505050505090500182805190602001908083835b6020831061251b57805182526020820191506020810190506020830392506124f8565b6001836020036101000a03801982511681845116808217855250505050505090500192505050604051602081830303815290604052925050505b919050565b600d5481565b600c5481565b6000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000600d5414612672576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f5374617274696e6720696e64657820697320616c72656164792073657400000081525060200191505060405180910390fd5b6000600c5414156126eb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f5374617274696e6720696e64657820626c6f636b206d7573742062652073657481525060200191505060405180910390fd5b600e54600c544060001c816126fc57fe5b06600d8190555060ff61271a600c544361310f90919063ffffffff16565b111561273a57600e54600143034060001c8161273257fe5b06600d819055505b6000600d5414156127625761275b6001600d54612e2a90919063ffffffff16565b600d819055505b565b600f60009054906101000a900460ff1681565b61277f61296c565b73ffffffffffffffffffffffffffffffffffffffff1661279d611c4c565b73ffffffffffffffffffffffffffffffffffffffff1614612826576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156128ac576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180613ed86026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16600a60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600a60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600033905090565b600061298a82600261319290919063ffffffff16565b9050919050565b816004600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16612a04836117f2565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000612a58826000016131ac565b9050919050565b6000612a6a82612974565b612abf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180613f48602c913960400191505060405180910390fd5b6000612aca836117f2565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480612b3957508373ffffffffffffffffffffffffffffffffffffffff16612b21846110e8565b73ffffffffffffffffffffffffffffffffffffffff16145b80612b4a5750612b498185612566565b5b91505092915050565b8273ffffffffffffffffffffffffffffffffffffffff16612b73826117f2565b73ffffffffffffffffffffffffffffffffffffffff1614612bdf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806140b76029913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415612c65576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526024815260200180613efe6024913960400191505060405180910390fd5b612c708383836131bd565b612c7b600082612991565b612ccc81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206131c290919063ffffffff16565b50612d1e81600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206131dc90919063ffffffff16565b50612d35818360026131f69092919063ffffffff16565b50808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4505050565b6000612da5836000018361322b565b60001c905092915050565b600080600080612dc386600001866132ae565b915091508160001c8160001c9350935050509250929050565b8060099080519060200190612df2929190613de6565b5050565b6000612e09846000018460001b84613347565b60001c90509392505050565b6000612e238260000161343d565b9050919050565b600080828401905083811015612ea8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600080831415612ec55760009050612f32565b6000828402905082848281612ed657fe5b0414612f2d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061406a6021913960400191505060405180910390fd5b809150505b92915050565b612f5282826040518060200160405280600081525061344e565b5050565b612f61848484612b53565b612f6d848484846134bf565b612fc2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526032815260200180613ea66032913960400191505060405180910390fd5b50505050565b60606000821415613010576040518060400160405280600181526020017f3000000000000000000000000000000000000000000000000000000000000000815250905061310a565b600082905060005b6000821461303a578080600101915050600a828161303257fe5b049150613018565b60608167ffffffffffffffff8111801561305357600080fd5b506040519080825280601f01601f1916602001820160405280156130865781602001600182028036833780820191505090505b50905060006001830390508593505b6000841461310257600a84816130a757fe5b0660300160f81b828280600190039350815181106130c157fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600a84816130fa57fe5b049350613095565b819450505050505b919050565b600082821115613187576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525060200191505060405180910390fd5b818303905092915050565b60006131a4836000018360001b6136d8565b905092915050565b600081600001805490509050919050565b505050565b60006131d4836000018360001b6136fb565b905092915050565b60006131ee836000018360001b6137e3565b905092915050565b6000613222846000018460001b8473ffffffffffffffffffffffffffffffffffffffff1660001b613853565b90509392505050565b60008183600001805490501161328c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180613e846022913960400191505060405180910390fd5b82600001828154811061329b57fe5b9060005260206000200154905092915050565b60008082846000018054905011613310576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806140206022913960400191505060405180910390fd5b600084600001848154811061332157fe5b906000526020600020906002020190508060000154816001015492509250509250929050565b6000808460010160008581526020019081526020016000205490506000811415839061340e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156133d35780820151818401526020810190506133b8565b50505050905090810190601f1680156134005780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5084600001600182038154811061342157fe5b9060005260206000209060020201600101549150509392505050565b600081600001805490509050919050565b613458838361392f565b61346560008484846134bf565b6134ba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526032815260200180613ea66032913960400191505060405180910390fd5b505050565b60006134e08473ffffffffffffffffffffffffffffffffffffffff16613b23565b6134ed57600190506136d0565b606061365763150b7a0260e01b61350261296c565b888787604051602401808573ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff16815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561358657808201518184015260208101905061356b565b50505050905090810190601f1680156135b35780820380516001836020036101000a031916815260200191505b5095505050505050604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051806060016040528060328152602001613ea6603291398773ffffffffffffffffffffffffffffffffffffffff16613b369092919063ffffffff16565b9050600081806020019051602081101561367057600080fd5b8101908080519060200190929190505050905063150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614925050505b949350505050565b600080836001016000848152602001908152602001600020541415905092915050565b600080836001016000848152602001908152602001600020549050600081146137d7576000600182039050600060018660000180549050039050600086600001828154811061374657fe5b906000526020600020015490508087600001848154811061376357fe5b906000526020600020018190555060018301876001016000838152602001908152602001600020819055508660000180548061379b57fe5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506137dd565b60009150505b92915050565b60006137ef8383613b4e565b61384857826000018290806001815401808255809150506001900390600052602060002001600090919091909150558260000180549050836001016000848152602001908152602001600020819055506001905061384d565b600090505b92915050565b60008084600101600085815260200190815260200160002054905060008114156138fa57846000016040518060400160405280868152602001858152509080600181540180825580915050600190039060005260206000209060020201600090919091909150600082015181600001556020820151816001015550508460000180549050856001016000868152602001908152602001600020819055506001915050613928565b8285600001600183038154811061390d57fe5b90600052602060002090600202016001018190555060009150505b9392505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156139d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4552433732313a206d696e7420746f20746865207a65726f206164647265737381525060200191505060405180910390fd5b6139db81612974565b15613a4e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000081525060200191505060405180910390fd5b613a5a600083836131bd565b613aab81600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206131dc90919063ffffffff16565b50613ac2818360026131f69092919063ffffffff16565b50808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a45050565b600080823b905060008111915050919050565b6060613b458484600085613b71565b90509392505050565b600080836001016000848152602001908152602001600020541415905092915050565b606082471015613bcc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180613f226026913960400191505060405180910390fd5b613bd585613b23565b613c47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601d8152602001807f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000081525060200191505060405180910390fd5b600060608673ffffffffffffffffffffffffffffffffffffffff1685876040518082805190602001908083835b60208310613c975780518252602082019150602081019050602083039250613c74565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114613cf9576040519150601f19603f3d011682016040523d82523d6000602084013e613cfe565b606091505b5091509150613d0e828286613d1a565b92505050949350505050565b60608315613d2a57829050613ddf565b600083511115613d3d5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613da4578082015181840152602081019050613d89565b50505050905090810190601f168015613dd15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b9392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613e2757805160ff1916838001178555613e55565b82800160010185558215613e55579182015b82811115613e54578251825591602001919060010190613e39565b5b509050613e629190613e66565b5090565b5b80821115613e7f576000816000905550600101613e67565b509056fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e64734552433732313a207472616e7366657220746f206e6f6e20455243373231526563656976657220696d706c656d656e7465724f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734552433732313a207472616e7366657220746f20746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c4552433732313a206f70657261746f7220717565727920666f72206e6f6e6578697374656e7420746f6b656e43616e206f6e6c79206d696e7420323020746f6b656e7320617420612074696d654552433732313a20617070726f76652063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f76656420666f7220616c6c4552433732313a2062616c616e636520717565727920666f7220746865207a65726f20616464726573734552433732313a206f776e657220717565727920666f72206e6f6e6578697374656e7420746f6b656e456e756d657261626c654d61703a20696e646578206f7574206f6620626f756e6473507572636861736520776f756c6420657863656564206d617820737570706c79206f662041706573536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774552433732313a20617070726f76656420717565727920666f72206e6f6e6578697374656e7420746f6b656e4552433732313a207472616e73666572206f6620746f6b656e2074686174206973206e6f74206f776e4552433732314d657461646174613a2055524920717565727920666f72206e6f6e6578697374656e7420746f6b656e4552433732313a20617070726f76616c20746f2063757272656e74206f776e65724552433732313a207472616e736665722063616c6c6572206973206e6f74206f776e6572206e6f7220617070726f766564a2646970667358221220b0e64d1fa6c4dbeb9c6f54607d7e1996943fe27624a80652f57b53fda084621b64736f6c63430007000033000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000002710000000000000000000000000000000000000000000000000000000006080e6d70000000000000000000000000000000000000000000000000000000000000011426f7265644170655961636874436c756200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044241594300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000010f2c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014426f72656420417065205761676d6920436c756200000000000000000000000000000000000000000000000000000000000000000000000000000000000000044241594300000000000000000000000000000000000000000000000000000000 + + Details: Insufficient funds for gas * price + value + Version: viem@2.29.2] + `) + + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/deployContract.ts b/packages/core/src/actions/deployContract.ts new file mode 100644 index 0000000000..a688dd7cfd --- /dev/null +++ b/packages/core/src/actions/deployContract.ts @@ -0,0 +1,87 @@ +import type { Abi, Account, Chain, Client, ContractConstructorArgs } from 'viem' +import { + type DeployContractErrorType as viem_DeployContractErrorType, + type DeployContractParameters as viem_DeployContractParameters, + type DeployContractReturnType as viem_DeployContractReturnType, + deployContract as viem_deployContract, +} from 'viem/actions' +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type DeployContractParameters< + abi extends Abi | readonly unknown[] = Abi, + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + allArgs = ContractConstructorArgs, + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + viem_DeployContractParameters< + abi, + chains[key], + Account, + chains[key], + allArgs + >, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type DeployContractReturnType = viem_DeployContractReturnType + +export type DeployContractErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_DeployContractErrorType + +/** https://wagmi.sh/core/api/actions/deployContract */ +export async function deployContract< + config extends Config, + const abi extends Abi | readonly unknown[], + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: DeployContractParameters, +): Promise { + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, viem_deployContract, 'deployContract') + const hash = await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) + + return hash +} diff --git a/packages/core/src/actions/disconnect.test.ts b/packages/core/src/actions/disconnect.test.ts new file mode 100644 index 0000000000..03d63db7de --- /dev/null +++ b/packages/core/src/actions/disconnect.test.ts @@ -0,0 +1,33 @@ +import { accounts, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { mock } from '../connectors/mock.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' + +const connector = config._internal.connectors.setup(mock({ accounts })) + +test('default', async () => { + await connect(config, { connector }) + expect(config.state.status).toEqual('connected') + await disconnect(config) + expect(config.state.status).toEqual('disconnected') +}) + +test('parameters: connector', async () => { + await connect(config, { connector }) + expect(config.state.status).toEqual('connected') + await disconnect(config, { connector }) + expect(config.state.status).toEqual('disconnected') +}) + +test('behavior: uses next connector on disconnect', async () => { + const connector_ = config._internal.connectors.setup(mock({ accounts })) + await connect(config, { connector: connector_ }) + await connect(config, { connector }) + + expect(config.state.status).toEqual('connected') + await disconnect(config, { connector }) + expect(config.state.status).toEqual('connected') + await disconnect(config, { connector: connector_ }) +}) diff --git a/packages/core/src/actions/disconnect.ts b/packages/core/src/actions/disconnect.ts new file mode 100644 index 0000000000..6efb4c790c --- /dev/null +++ b/packages/core/src/actions/disconnect.ts @@ -0,0 +1,71 @@ +import type { Config, Connection, Connector } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { + ConnectorNotConnectedErrorType, + ConnectorNotFoundErrorType, +} from '../errors/config.js' +import type { ConnectorParameter } from '../types/properties.js' + +export type DisconnectParameters = ConnectorParameter + +export type DisconnectReturnType = void + +export type DisconnectErrorType = + | ConnectorNotFoundErrorType + | ConnectorNotConnectedErrorType + // base + | BaseErrorType + | ErrorType + +/** https://wagmi.sh/core/api/actions/disconnect */ +export async function disconnect( + config: Config, + parameters: DisconnectParameters = {}, +): Promise { + let connector: Connector | undefined + if (parameters.connector) connector = parameters.connector + else { + const { connections, current } = config.state + const connection = connections.get(current!) + connector = connection?.connector + } + + const connections = config.state.connections + + if (connector) { + await connector.disconnect() + connector.emitter.off('change', config._internal.events.change) + connector.emitter.off('disconnect', config._internal.events.disconnect) + connector.emitter.on('connect', config._internal.events.connect) + + connections.delete(connector.uid) + } + + config.setState((x) => { + // if no connections exist, move to disconnected state + if (connections.size === 0) + return { + ...x, + connections: new Map(), + current: null, + status: 'disconnected', + } + + // switch over to another connection + const nextConnection = connections.values().next().value as Connection + return { + ...x, + connections: new Map(connections), + current: nextConnection.connector.uid, + } + }) + + // Set recent connector if exists + { + const current = config.state.current + if (!current) return + const connector = config.state.connections.get(current)?.connector + if (!connector) return + await config.storage?.setItem('recentConnectorId', connector.id) + } +} diff --git a/packages/core/src/actions/estimateFeesPerGas.test-d.ts b/packages/core/src/actions/estimateFeesPerGas.test-d.ts new file mode 100644 index 0000000000..dada2ea311 --- /dev/null +++ b/packages/core/src/actions/estimateFeesPerGas.test-d.ts @@ -0,0 +1,41 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { estimateFeesPerGas } from './estimateFeesPerGas.js' + +test('types', async () => { + const default_ = await estimateFeesPerGas(config) + expectTypeOf(default_).toMatchTypeOf<{ + gasPrice?: undefined + maxFeePerGas: bigint + maxPriorityFeePerGas: bigint + formatted: { + gasPrice?: undefined + maxFeePerGas: string + maxPriorityFeePerGas: string + } + }>() + + const legacy = await estimateFeesPerGas(config, { type: 'legacy' }) + expectTypeOf(legacy).toMatchTypeOf<{ + gasPrice: bigint + maxFeePerGas?: undefined + maxPriorityFeePerGas?: undefined + formatted: { + gasPrice: string + maxFeePerGas?: undefined + maxPriorityFeePerGas?: undefined + } + }>() + + const eip1559 = await estimateFeesPerGas(config, { type: 'eip1559' }) + expectTypeOf(eip1559).toMatchTypeOf<{ + gasPrice?: undefined + maxFeePerGas: bigint + maxPriorityFeePerGas: bigint + formatted: { + gasPrice?: undefined + maxFeePerGas: string + maxPriorityFeePerGas: string + } + }>() +}) diff --git a/packages/core/src/actions/estimateFeesPerGas.test.ts b/packages/core/src/actions/estimateFeesPerGas.test.ts new file mode 100644 index 0000000000..4c5d668b83 --- /dev/null +++ b/packages/core/src/actions/estimateFeesPerGas.test.ts @@ -0,0 +1,16 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { estimateFeesPerGas } from './estimateFeesPerGas.js' + +test('default', async () => { + const result = await estimateFeesPerGas(config) + expect(Object.keys(result)).toMatchInlineSnapshot(` + [ + "formatted", + "gasPrice", + "maxFeePerGas", + "maxPriorityFeePerGas", + ] + `) +}) diff --git a/packages/core/src/actions/estimateFeesPerGas.ts b/packages/core/src/actions/estimateFeesPerGas.ts new file mode 100644 index 0000000000..66915f010a --- /dev/null +++ b/packages/core/src/actions/estimateFeesPerGas.ts @@ -0,0 +1,87 @@ +import { + type Chain, + type FeeValuesEIP1559, + type FeeValuesLegacy, + type FeeValuesType, + formatUnits, +} from 'viem' +import { + type EstimateFeesPerGasErrorType as viem_EstimateFeesPerGasErrorType, + type EstimateFeesPerGasParameters as viem_EstimateFeesPerGasParameters, + type EstimateFeesPerGasReturnType as viem_EstimateFeesPerGasReturnType, + estimateFeesPerGas as viem_estimateFeesPerGas, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Unit } from '../types/unit.js' +import type { Compute } from '../types/utils.js' +import type { UnionCompute, UnionLooseOmit } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { getUnit } from '../utils/getUnit.js' + +export type EstimateFeesPerGasParameters< + type extends FeeValuesType = FeeValuesType, + config extends Config = Config, +> = UnionCompute< + UnionLooseOmit< + viem_EstimateFeesPerGasParameters, + 'chain' + > & + ChainIdParameter & { + /** @deprecated */ + formatUnits?: Unit | undefined + } +> + +export type EstimateFeesPerGasReturnType< + type extends FeeValuesType = FeeValuesType, +> = Compute< + viem_EstimateFeesPerGasReturnType & { + /** @deprecated */ + formatted: UnionCompute< + | (type extends 'legacy' ? FeeValuesLegacy : never) + | (type extends 'eip1559' ? FeeValuesEIP1559 : never) + > + } +> + +export type EstimateFeesPerGasErrorType = viem_EstimateFeesPerGasErrorType + +export async function estimateFeesPerGas< + config extends Config, + type extends FeeValuesType = 'eip1559', +>( + config: config, + parameters: EstimateFeesPerGasParameters = {}, +): Promise> { + const { chainId, formatUnits: units = 'gwei', ...rest } = parameters + + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_estimateFeesPerGas, + 'estimateFeesPerGas', + ) + + const { gasPrice, maxFeePerGas, maxPriorityFeePerGas } = await action({ + ...rest, + chain: client.chain, + }) + + const unit = getUnit(units) + const formatted = { + gasPrice: gasPrice ? formatUnits(gasPrice, unit) : undefined, + maxFeePerGas: maxFeePerGas ? formatUnits(maxFeePerGas, unit) : undefined, + maxPriorityFeePerGas: maxPriorityFeePerGas + ? formatUnits(maxPriorityFeePerGas, unit) + : undefined, + } + + return { + formatted, + gasPrice, + maxFeePerGas, + maxPriorityFeePerGas, + } as EstimateFeesPerGasReturnType +} diff --git a/packages/core/src/actions/estimateGas.test-d.ts b/packages/core/src/actions/estimateGas.test-d.ts new file mode 100644 index 0000000000..5fc66c0ec0 --- /dev/null +++ b/packages/core/src/actions/estimateGas.test-d.ts @@ -0,0 +1,47 @@ +import { http, parseEther } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { type EstimateGasParameters, estimateGas } from './estimateGas.js' + +test('chain formatters', () => { + const config = createConfig({ + chains: [mainnet, celo], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = EstimateGasParameters + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + estimateGas(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + feeCurrency: '0x', + }) + + type Result2 = EstimateGasParameters + expectTypeOf().toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + estimateGas(config, { + chainId: celo.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + feeCurrency: '0x', + }) + + type Result3 = EstimateGasParameters + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + estimateGas(config, { + chainId: mainnet.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/core/src/actions/estimateGas.test.ts b/packages/core/src/actions/estimateGas.test.ts new file mode 100644 index 0000000000..f0154c55fa --- /dev/null +++ b/packages/core/src/actions/estimateGas.test.ts @@ -0,0 +1,47 @@ +import { accounts, config } from '@wagmi/test' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { mock } from '../connectors/mock.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { estimateGas } from './estimateGas.js' + +const connector = config._internal.connectors.setup(mock({ accounts })) + +test('parameters: account', async () => { + await expect( + estimateGas(config, { + account: accounts[0], + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ).resolves.toMatchInlineSnapshot('21000n') +}) + +test('parameters: connector', async () => { + await connect(config, { connector }) + + await expect( + estimateGas(config, { + connector, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ).resolves.toMatchInlineSnapshot('21000n') + + await disconnect(config, { connector }) +}) + +test('behavior: no account and not connected', async () => { + await expect( + estimateGas(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) +}) diff --git a/packages/core/src/actions/estimateGas.ts b/packages/core/src/actions/estimateGas.ts new file mode 100644 index 0000000000..c049fa42f8 --- /dev/null +++ b/packages/core/src/actions/estimateGas.ts @@ -0,0 +1,73 @@ +import type { Account, Address, Chain } from 'viem' +import { + type EstimateGasErrorType as viem_EstimateGasErrorType, + type EstimateGasParameters as viem_EstimateGasParameters, + type EstimateGasReturnType as viem_EstimateGasReturnType, + estimateGas as viem_estimateGas, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { UnionCompute, UnionLooseOmit } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type EstimateGasParameters< + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: UnionCompute< + UnionLooseOmit, 'chain'> & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type EstimateGasReturnType = viem_EstimateGasReturnType + +export type EstimateGasErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_EstimateGasErrorType + +/** https://wagmi.sh/core/api/actions/estimateGas */ +export async function estimateGas< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +>( + config: config, + parameters: EstimateGasParameters, +): Promise { + const { chainId, connector, ...rest } = parameters + + let account: Address | Account + if (parameters.account) account = parameters.account + else { + const connectorClient = await getConnectorClient(config, { + account: parameters.account, + chainId, + connector, + }) + account = connectorClient.account + } + + const client = config.getClient({ chainId }) + const action = getAction(client, viem_estimateGas, 'estimateGas') + return action({ ...(rest as viem_EstimateGasParameters), account }) +} diff --git a/packages/core/src/actions/estimateMaxPriorityFeePerGas.test.ts b/packages/core/src/actions/estimateMaxPriorityFeePerGas.test.ts new file mode 100644 index 0000000000..deb969ebec --- /dev/null +++ b/packages/core/src/actions/estimateMaxPriorityFeePerGas.test.ts @@ -0,0 +1,16 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { estimateMaxPriorityFeePerGas } from './estimateMaxPriorityFeePerGas.js' + +test('default', async () => { + await expect(estimateMaxPriorityFeePerGas(config)).resolves.toBeDefined() +}) + +test('parameters: chainId', async () => { + await expect( + estimateMaxPriorityFeePerGas(config, { + chainId: chain.mainnet2.id, + }), + ).resolves.toBeDefined() +}) diff --git a/packages/core/src/actions/estimateMaxPriorityFeePerGas.ts b/packages/core/src/actions/estimateMaxPriorityFeePerGas.ts new file mode 100644 index 0000000000..06378d84fd --- /dev/null +++ b/packages/core/src/actions/estimateMaxPriorityFeePerGas.ts @@ -0,0 +1,49 @@ +import type { Chain } from 'viem' +import { + type EstimateMaxPriorityFeePerGasErrorType as viem_EstimateMaxPriorityFeePerGasErrorType, + type EstimateMaxPriorityFeePerGasParameters as viem_EstimateMaxPriorityFeePerGasParameters, + type EstimateMaxPriorityFeePerGasReturnType as viem_EstimateMaxPriorityFeePerGasReturnType, + estimateMaxPriorityFeePerGas as viem_estimateMaxPriorityFeePerGas, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute, UnionLooseOmit } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type EstimateMaxPriorityFeePerGasParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + UnionLooseOmit< + viem_EstimateMaxPriorityFeePerGasParameters & + ChainIdParameter, + 'chain' + > +> + +export type EstimateMaxPriorityFeePerGasReturnType = + viem_EstimateMaxPriorityFeePerGasReturnType + +export type EstimateMaxPriorityFeePerGasErrorType = + viem_EstimateMaxPriorityFeePerGasErrorType + +/** https://wagmi.sh/core/api/actions/estimateMaxPriorityFeePerGas */ +export async function estimateMaxPriorityFeePerGas< + config extends Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + config: config, + parameters: EstimateMaxPriorityFeePerGasParameters = {}, +): Promise { + const { chainId } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_estimateMaxPriorityFeePerGas, + 'estimateMaxPriorityFeePerGas', + ) + return action({ chain: client.chain }) +} diff --git a/packages/core/src/actions/getAccount.test-d.ts b/packages/core/src/actions/getAccount.test-d.ts new file mode 100644 index 0000000000..728468039b --- /dev/null +++ b/packages/core/src/actions/getAccount.test-d.ts @@ -0,0 +1,69 @@ +import { config } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import type { Connector } from '../createConfig.js' +import { getAccount } from './getAccount.js' + +test('states', () => { + const result = getAccount(config) + + switch (result.status) { + case 'reconnecting': { + expectTypeOf(result).toMatchTypeOf<{ + address: Address | undefined + chain: (typeof config)['chains'][number] | undefined + chainId: number | undefined + connector: Connector | undefined + isConnected: boolean + isConnecting: false + isDisconnected: false + isReconnecting: true + status: 'reconnecting' + }>() + break + } + case 'connecting': { + expectTypeOf(result).toMatchTypeOf<{ + address: Address | undefined + chain: (typeof config)['chains'][number] | undefined + chainId: number | undefined + connector: Connector | undefined + isConnected: false + isReconnecting: false + isConnecting: true + isDisconnected: false + status: 'connecting' + }>() + break + } + case 'connected': { + expectTypeOf(result).toMatchTypeOf<{ + address: Address + chain: (typeof config)['chains'][number] | undefined + chainId: number + connector: Connector + isConnected: true + isConnecting: false + isDisconnected: false + isReconnecting: false + status: 'connected' + }>() + break + } + case 'disconnected': { + expectTypeOf(result).toMatchTypeOf<{ + address: undefined + chain: undefined + chainId: undefined + connector: undefined + isConnected: false + isReconnecting: false + isConnecting: false + isDisconnected: true + status: 'disconnected' + }>() + break + } + } +}) diff --git a/packages/core/src/actions/getAccount.test.ts b/packages/core/src/actions/getAccount.test.ts new file mode 100644 index 0000000000..a538357875 --- /dev/null +++ b/packages/core/src/actions/getAccount.test.ts @@ -0,0 +1,37 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getAccount } from './getAccount.js' + +test('default', () => { + expect(getAccount(config)).toMatchInlineSnapshot(` + { + "address": undefined, + "addresses": undefined, + "chain": undefined, + "chainId": undefined, + "connector": undefined, + "isConnected": false, + "isConnecting": false, + "isDisconnected": true, + "isReconnecting": false, + "status": "disconnected", + } + `) +}) + +test('behavior: connected', async () => { + let result = getAccount(config) + expect(result.status).toEqual('disconnected') + + await connect(config, { connector: config.connectors[0]! }) + result = getAccount(config) + expect(result.address).toBeDefined() + expect(result.status).toEqual('connected') + + await disconnect(config) + result = getAccount(config) + expect(result.status).toEqual('disconnected') +}) diff --git a/packages/core/src/actions/getAccount.ts b/packages/core/src/actions/getAccount.ts new file mode 100644 index 0000000000..af5daea028 --- /dev/null +++ b/packages/core/src/actions/getAccount.ts @@ -0,0 +1,126 @@ +import type { Address, Chain } from 'viem' + +import type { Config, Connector } from '../createConfig.js' + +export type GetAccountReturnType< + config extends Config = Config, + /// + chain = Config extends config ? Chain : config['chains'][number], +> = + | { + address: Address + addresses: readonly [Address, ...Address[]] + chain: chain | undefined + chainId: number + connector: Connector + isConnected: true + isConnecting: false + isDisconnected: false + isReconnecting: false + status: 'connected' + } + | { + address: Address | undefined + addresses: readonly Address[] | undefined + chain: chain | undefined + chainId: number | undefined + connector: Connector | undefined + isConnected: boolean + isConnecting: false + isDisconnected: false + isReconnecting: true + status: 'reconnecting' + } + | { + address: Address | undefined + addresses: readonly Address[] | undefined + chain: chain | undefined + chainId: number | undefined + connector: Connector | undefined + isConnected: false + isReconnecting: false + isConnecting: true + isDisconnected: false + status: 'connecting' + } + | { + address: undefined + addresses: undefined + chain: undefined + chainId: undefined + connector: undefined + isConnected: false + isReconnecting: false + isConnecting: false + isDisconnected: true + status: 'disconnected' + } + +/** https://wagmi.sh/core/api/actions/getAccount */ +export function getAccount( + config: config, +): GetAccountReturnType { + const uid = config.state.current! + const connection = config.state.connections.get(uid) + const addresses = connection?.accounts + const address = addresses?.[0] + const chain = config.chains.find( + (chain) => chain.id === connection?.chainId, + ) as GetAccountReturnType['chain'] + const status = config.state.status + + switch (status) { + case 'connected': + return { + address: address!, + addresses: addresses!, + chain, + chainId: connection?.chainId!, + connector: connection?.connector!, + isConnected: true, + isConnecting: false, + isDisconnected: false, + isReconnecting: false, + status, + } + case 'reconnecting': + return { + address, + addresses, + chain, + chainId: connection?.chainId, + connector: connection?.connector, + isConnected: !!address, + isConnecting: false, + isDisconnected: false, + isReconnecting: true, + status, + } + case 'connecting': + return { + address, + addresses, + chain, + chainId: connection?.chainId, + connector: connection?.connector, + isConnected: false, + isConnecting: true, + isDisconnected: false, + isReconnecting: false, + status, + } + case 'disconnected': + return { + address: undefined, + addresses: undefined, + chain: undefined, + chainId: undefined, + connector: undefined, + isConnected: false, + isConnecting: false, + isDisconnected: true, + isReconnecting: false, + status, + } + } +} diff --git a/packages/core/src/actions/getBalance.test.ts b/packages/core/src/actions/getBalance.test.ts new file mode 100644 index 0000000000..954c09c2e6 --- /dev/null +++ b/packages/core/src/actions/getBalance.test.ts @@ -0,0 +1,102 @@ +import { accounts, chain, config, testClient } from '@wagmi/test' +import { parseEther } from 'viem' +import { beforeEach, expect, test } from 'vitest' + +import { getBalance } from './getBalance.js' + +const address = accounts[0] + +beforeEach(async () => { + await testClient.mainnet.setBalance({ + address, + value: parseEther('10000'), + }) + await testClient.mainnet.mine({ blocks: 1 }) + await testClient.mainnet2.setBalance({ + address, + value: parseEther('420'), + }) + await testClient.mainnet2.mine({ blocks: 1 }) +}) + +test('default', async () => { + await expect(getBalance(config, { address })).resolves.toMatchInlineSnapshot(` + { + "decimals": 18, + "formatted": "10000", + "symbol": "ETH", + "value": 10000000000000000000000n, + } + `) + + await testClient.mainnet.setBalance({ + address, + value: parseEther('6969.12222215666'), + }) + await expect(getBalance(config, { address })).resolves.toMatchInlineSnapshot(` + { + "decimals": 18, + "formatted": "6969.12222215666", + "symbol": "ETH", + "value": 6969122222156660000000n, + } + `) +}) + +test('parameters: chainId', async () => { + await expect( + getBalance(config, { address, chainId: chain.mainnet2.id }), + ).resolves.toMatchInlineSnapshot(` + { + "decimals": 18, + "formatted": "420", + "symbol": "WAG", + "value": 420000000000000000000n, + } + `) +}) + +test('parameters: token', async () => { + await expect( + getBalance(config, { + address: '0x4557B18E779944BFE9d78A672452331C186a9f48', + token: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + }), + ).resolves.toMatchInlineSnapshot(` + { + "decimals": 18, + "formatted": "0.559062564299199392", + "symbol": "DAI", + "value": 559062564299199392n, + } + `) +}) + +test('parameters: token (bytes32 symbol)', async () => { + await expect( + getBalance(config, { + address: '0x4557B18E779944BFE9d78A672452331C186a9f48', + token: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', + }), + ).resolves.toMatchInlineSnapshot(` + { + "decimals": 18, + "formatted": "0", + "symbol": "MKR", + "value": 0n, + } + `) +}) + +test('parameters: unit', async () => { + await expect( + getBalance(config, { address, unit: 'wei' }), + ).resolves.toMatchInlineSnapshot(` + { + "decimals": 18, + "formatted": "10000000000000000000000", + "symbol": "ETH", + "value": 10000000000000000000000n, + } + `) +}) diff --git a/packages/core/src/actions/getBalance.ts b/packages/core/src/actions/getBalance.ts new file mode 100644 index 0000000000..1aae5667b1 --- /dev/null +++ b/packages/core/src/actions/getBalance.ts @@ -0,0 +1,149 @@ +import { type Address, type Hex, formatUnits, hexToString, trim } from 'viem' +import { + type GetBalanceErrorType as viem_GetBalanceErrorType, + type GetBalanceParameters as viem_GetBalanceParameters, + getBalance as viem_getBalance, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Unit } from '../types/unit.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { getUnit } from '../utils/getUnit.js' +import { type ReadContractsErrorType, readContracts } from './readContracts.js' + +export type GetBalanceParameters = Compute< + ChainIdParameter & + viem_GetBalanceParameters & { + /** @deprecated */ + token?: Address | undefined + /** @deprecated */ + unit?: Unit | undefined + } +> + +export type GetBalanceReturnType = { + decimals: number + /** @deprecated */ + formatted: string + symbol: string + value: bigint +} + +export type GetBalanceErrorType = viem_GetBalanceErrorType + +/** https://wagmi.sh/core/api/actions/getBalance */ +export async function getBalance( + config: config, + parameters: GetBalanceParameters, +): Promise { + const { + address, + blockNumber, + blockTag, + chainId, + token: tokenAddress, + unit = 'ether', + } = parameters + + if (tokenAddress) { + try { + return await getTokenBalance(config, { + balanceAddress: address, + chainId, + symbolType: 'string', + tokenAddress, + }) + } catch (error) { + // In the chance that there is an error upon decoding the contract result, + // it could be likely that the contract data is represented as bytes32 instead + // of a string. + if ( + (error as ReadContractsErrorType).name === + 'ContractFunctionExecutionError' + ) { + const balance = await getTokenBalance(config, { + balanceAddress: address, + chainId, + symbolType: 'bytes32', + tokenAddress, + }) + const symbol = hexToString( + trim(balance.symbol as Hex, { dir: 'right' }), + ) + return { ...balance, symbol } + } + throw error + } + } + + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getBalance, 'getBalance') + const value = await action( + blockNumber ? { address, blockNumber } : { address, blockTag }, + ) + const chain = config.chains.find((x) => x.id === chainId) ?? client.chain! + return { + decimals: chain.nativeCurrency.decimals, + formatted: formatUnits(value, getUnit(unit)), + symbol: chain.nativeCurrency.symbol, + value, + } +} + +type GetTokenBalanceParameters = { + balanceAddress: Address + chainId?: number | undefined + symbolType: 'bytes32' | 'string' + tokenAddress: Address + unit?: Unit | undefined +} + +async function getTokenBalance( + config: Config, + parameters: GetTokenBalanceParameters, +) { + const { balanceAddress, chainId, symbolType, tokenAddress, unit } = parameters + const contract = { + abi: [ + { + type: 'function', + name: 'balanceOf', + stateMutability: 'view', + inputs: [{ type: 'address' }], + outputs: [{ type: 'uint256' }], + }, + { + type: 'function', + name: 'decimals', + stateMutability: 'view', + inputs: [], + outputs: [{ type: 'uint8' }], + }, + { + type: 'function', + name: 'symbol', + stateMutability: 'view', + inputs: [], + outputs: [{ type: symbolType }], + }, + ], + address: tokenAddress, + } as const + const [value, decimals, symbol] = await readContracts(config, { + allowFailure: false, + contracts: [ + { + ...contract, + functionName: 'balanceOf', + args: [balanceAddress], + chainId, + }, + { ...contract, functionName: 'decimals', chainId }, + { ...contract, functionName: 'symbol', chainId }, + ] as const, + }) + const formatted = formatUnits(value ?? '0', getUnit(unit ?? decimals)) + return { decimals, formatted, symbol, value } +} diff --git a/packages/core/src/actions/getBlock.test-d.ts b/packages/core/src/actions/getBlock.test-d.ts new file mode 100644 index 0000000000..2344c50267 --- /dev/null +++ b/packages/core/src/actions/getBlock.test-d.ts @@ -0,0 +1,35 @@ +import { http, type Hex } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { getBlock } from './getBlock.js' + +test('chain formatters', async () => { + const config = createConfig({ + chains: [celo, mainnet], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + const result = await getBlock(config) + if (result.chainId === celo.id) { + expectTypeOf(result.difficulty).toEqualTypeOf() + expectTypeOf(result.gasLimit).toEqualTypeOf() + expectTypeOf(result.mixHash).toEqualTypeOf() + expectTypeOf(result.nonce).toEqualTypeOf<`0x${string}`>() + expectTypeOf(result.uncles).toEqualTypeOf() + } +}) + +test('chainId', async () => { + const config = createConfig({ + chains: [celo, mainnet], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + const result = await getBlock(config, { + chainId: celo.id, + }) + expectTypeOf(result.difficulty).toEqualTypeOf() + expectTypeOf(result.gasLimit).toEqualTypeOf() + expectTypeOf(result.mixHash).toEqualTypeOf() + expectTypeOf(result.nonce).toEqualTypeOf<`0x${string}`>() +}) diff --git a/packages/core/src/actions/getBlock.test.ts b/packages/core/src/actions/getBlock.test.ts new file mode 100644 index 0000000000..b1e92ba364 --- /dev/null +++ b/packages/core/src/actions/getBlock.test.ts @@ -0,0 +1,153 @@ +import { config, mainnet } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBlock } from './getBlock.js' + +test('default', async () => { + await expect(getBlock(config)).resolves.toBeDefined() +}) + +test('args: blockNumber', async () => { + const { transactions, ...block } = await getBlock(config, { + blockNumber: mainnet.fork.blockNumber, + }) + expect(transactions).toMatchObject( + expect.arrayContaining([expect.any(String)]), + ) + expect(block).toMatchInlineSnapshot(` + { + "baseFeePerGas": 24076814055n, + "blobGasUsed": undefined, + "chainId": 1, + "difficulty": 0n, + "excessBlobGas": undefined, + "extraData": "0x546974616e2028746974616e6275696c6465722e78797a29", + "gasLimit": 30000000n, + "gasUsed": 26325393n, + "hash": "0xcfa5df46abf1521f68ae72a7f7c4661949f4fb08a3d1296fe8082f6580a414e0", + "logsBloom": "0x2df3b5a24d2d57e7d73f96dbfea3577b1d5fbaacfcb9b5fb86db74d2e4ffd1e48bba050c33edada84fe477213937158c1e95d3da9f457f6f36e3ff0afdffcb667c5ee5f9e3ddffa9db1af6bbf15fcbbca5139717d5eedab4daa63cd8bb7dfa3e976b1e7023e2dc4586cef3caa0b73d6ff2ba3afb989c9f58f6b67bb4ed596c5aeb78cef51f69ad3675df70ffbd2aa6576d7c9e3debd00cccec3b69fc617b8568bfe588f7e126ef591f34ddd0d8b68c28b7ed45b46af3a7bb75c0e2fe4bec54fb772c87ae6f7efcdfb13139b758cfda4d98dffe426fef6d1c2e55f36b5bb1f0a2aef7bcbdf83d31ea646cf6ef3fe9d8b9af2ad4197f7ea2de462bd029fdef7e6f", + "miner": "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97", + "mixHash": "0x92ac9cd6e57bacd7c7d3e9b087c3907b1c085e284eec2dce7379a847cb4c9940", + "nonce": "0x0000000000000000", + "number": 19258213n, + "parentHash": "0x40cb7885ad596d0397d664a4dc9ef5c2011c09e9a62b386f838f5f5362582ebb", + "receiptsRoot": "0x910a69ba396ab4f59c2c77aa413e941fc4da97a021b8d8bbf12c125bfc42d9d3", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": 158252n, + "stateRoot": "0x6e27207d219b0251dbc2fde71f3cde8e33703261f032056453c27275500dddbc", + "timestamp": 1708302299n, + "totalDifficulty": null, + "transactionsRoot": "0x897dba26a3a940b62f86da6e5fec5f71312ad7c871a4031db79dee67442c9d1e", + "uncles": [], + "withdrawals": [ + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x112da72", + "index": "0x21ec946", + "validatorIndex": "0x5cd8e", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x1119345", + "index": "0x21ec947", + "validatorIndex": "0x5cd8f", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x110e2ca", + "index": "0x21ec948", + "validatorIndex": "0x5cd90", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x1119245", + "index": "0x21ec949", + "validatorIndex": "0x5cd91", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x1115a03", + "index": "0x21ec94a", + "validatorIndex": "0x5cd92", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x111cf3f", + "index": "0x21ec94b", + "validatorIndex": "0x5cd93", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x1106006", + "index": "0x21ec94c", + "validatorIndex": "0x5cd94", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x1115bb3", + "index": "0x21ec94d", + "validatorIndex": "0x5cd95", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x111e0d9", + "index": "0x21ec94e", + "validatorIndex": "0x5cd96", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x110829f", + "index": "0x21ec94f", + "validatorIndex": "0x5cd97", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x11029ab", + "index": "0x21ec950", + "validatorIndex": "0x5cd98", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x11140a6", + "index": "0x21ec951", + "validatorIndex": "0x5cd99", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x111396c", + "index": "0x21ec952", + "validatorIndex": "0x5cd9a", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x110de16", + "index": "0x21ec953", + "validatorIndex": "0x5cd9b", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x1121062", + "index": "0x21ec954", + "validatorIndex": "0x5cd9c", + }, + { + "address": "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + "amount": "0x11188bc", + "index": "0x21ec955", + "validatorIndex": "0x5cd9d", + }, + ], + "withdrawalsRoot": "0x26638497bd55075025ac2362d92bd789ac1232fd50c4b3866565280318027950", + } + `) +}) + +test('args: includeTransactions', async () => { + const { transactions } = await getBlock(config, { + includeTransactions: true, + blockNumber: mainnet.fork.blockNumber, + }) + expect(transactions).toMatchObject( + expect.arrayContaining([expect.any(Object)]), + ) +}) diff --git a/packages/core/src/actions/getBlock.ts b/packages/core/src/actions/getBlock.ts new file mode 100644 index 0000000000..babb52cd41 --- /dev/null +++ b/packages/core/src/actions/getBlock.ts @@ -0,0 +1,74 @@ +import type { BlockTag, Chain } from 'viem' +import { + type GetBlockErrorType as viem_GetBlockErrorType, + type GetBlockParameters as viem_GetBlockParameters, + type GetBlockReturnType as viem_GetBlockReturnType, + getBlock as viem_getBlock, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute, IsNarrowable } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetBlockParameters< + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + viem_GetBlockParameters & + ChainIdParameter +> + +export type GetBlockReturnType< + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = Compute< + { + [key in keyof chains]: viem_GetBlockReturnType< + IsNarrowable extends true ? chains[key] : undefined, + includeTransactions, + blockTag + > & { chainId: chains[key]['id'] } + }[number] +> + +export type GetBlockErrorType = viem_GetBlockErrorType + +/** https://wagmi.sh/core/actions/getBlock */ +export async function getBlock< + config extends Config, + chainId extends config['chains'][number]['id'], + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', +>( + config: config, + parameters: GetBlockParameters< + includeTransactions, + blockTag, + config, + chainId + > = {}, +): Promise> { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getBlock, 'getBlock') + const block = await action(rest) + return { + ...(block as unknown as GetBlockReturnType< + includeTransactions, + blockTag, + config, + chainId + >), + chainId: client.chain.id, + } +} diff --git a/packages/core/src/actions/getBlockNumber.test.ts b/packages/core/src/actions/getBlockNumber.test.ts new file mode 100644 index 0000000000..7e18744e14 --- /dev/null +++ b/packages/core/src/actions/getBlockNumber.test.ts @@ -0,0 +1,8 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBlockNumber } from './getBlockNumber.js' + +test('default', async () => { + await expect(getBlockNumber(config)).resolves.toBeDefined() +}) diff --git a/packages/core/src/actions/getBlockNumber.ts b/packages/core/src/actions/getBlockNumber.ts new file mode 100644 index 0000000000..ecad7c0f67 --- /dev/null +++ b/packages/core/src/actions/getBlockNumber.ts @@ -0,0 +1,36 @@ +import { + type GetBlockNumberErrorType as viem_GetBlockNumberErrorType, + type GetBlockNumberParameters as viem_GetBlockNumberParameters, + type GetBlockNumberReturnType as viem_GetBlockNumberReturnType, + getBlockNumber as viem_getBlockNumber, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetBlockNumberParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute> + +export type GetBlockNumberReturnType = viem_GetBlockNumberReturnType + +export type GetBlockNumberErrorType = viem_GetBlockNumberErrorType + +/** https://wagmi.sh/core/api/actions/getBlockNumber */ +export function getBlockNumber< + config extends Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + config: config, + parameters: GetBlockNumberParameters = {}, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getBlockNumber, 'getBlockNumber') + return action(rest) +} diff --git a/packages/core/src/actions/getBlockTransactionCount.test.ts b/packages/core/src/actions/getBlockTransactionCount.test.ts new file mode 100644 index 0000000000..d8a593aa00 --- /dev/null +++ b/packages/core/src/actions/getBlockTransactionCount.test.ts @@ -0,0 +1,61 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBlockTransactionCount } from './getBlockTransactionCount.js' + +test('default', async () => { + await expect(getBlockTransactionCount(config)).resolves.toBeTypeOf('number') +}) + +test('parameters: chainId', async () => { + await expect( + getBlockTransactionCount(config, { chainId: chain.mainnet2.id }), + ).resolves.toBeTypeOf('number') +}) + +test('parameters: blockNumber', async () => { + await expect( + getBlockTransactionCount(config, { blockNumber: 13677382n }), + ).resolves.toBeTypeOf('number') +}) + +test('parameters: blockHash', async () => { + await expect( + getBlockTransactionCount(config, { + blockHash: + '0x6201f37a245850d1f11e4be3ac45bc51bd9d43ee4a127192cad550f351cfa575', + }), + ).resolves.toBeTypeOf('number') +}) + +test('parameters: blockTag', async () => { + await expect( + getBlockTransactionCount(config, { + blockTag: 'earliest', + }), + ).resolves.toBeTypeOf('number') + + await expect( + getBlockTransactionCount(config, { + blockTag: 'finalized', + }), + ).resolves.toBeTypeOf('number') + + await expect( + getBlockTransactionCount(config, { + blockTag: 'latest', + }), + ).resolves.toBeTypeOf('number') + + await expect( + getBlockTransactionCount(config, { + blockTag: 'pending', + }), + ).resolves.toBeTypeOf('number') + + await expect( + getBlockTransactionCount(config, { + blockTag: 'safe', + }), + ).resolves.toBeTypeOf('number') +}) diff --git a/packages/core/src/actions/getBlockTransactionCount.ts b/packages/core/src/actions/getBlockTransactionCount.ts new file mode 100644 index 0000000000..e30aca976a --- /dev/null +++ b/packages/core/src/actions/getBlockTransactionCount.ts @@ -0,0 +1,44 @@ +import { + type GetBlockTransactionCountErrorType as viem_GetBlockTransactionCountErrorType, + type GetBlockTransactionCountParameters as viem_GetBlockTransactionCountParameters, + type GetBlockTransactionCountReturnType as viem_GetBlockTransactionCountReturnType, + getBlockTransactionCount as viem_getBlockTransactionCount, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { UnionCompute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetBlockTransactionCountParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = UnionCompute< + viem_GetBlockTransactionCountParameters & ChainIdParameter +> + +export type GetBlockTransactionCountReturnType = + viem_GetBlockTransactionCountReturnType + +export type GetBlockTransactionCountErrorType = + viem_GetBlockTransactionCountErrorType + +/** https://wagmi.sh/core/api/actions/getBlockTransactionCount */ +export function getBlockTransactionCount< + config extends Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + config: config, + parameters: GetBlockTransactionCountParameters = {}, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_getBlockTransactionCount, + 'getBlockTransactionCount', + ) + return action(rest) +} diff --git a/packages/core/src/actions/getBytecode.test.ts b/packages/core/src/actions/getBytecode.test.ts new file mode 100644 index 0000000000..1d5b6bdff9 --- /dev/null +++ b/packages/core/src/actions/getBytecode.test.ts @@ -0,0 +1,45 @@ +import { address, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBytecode } from './getBytecode.js' + +test('default', async () => { + await expect( + getBytecode(config, { + address: '0x0000000000000000000000000000000000000000', + }), + ).resolves.toBe(undefined) + + await expect( + getBytecode(config, { + address: address.wagmiMintExample, + }), + ).resolves.toMatch(/^0x.*/) +}) + +test('parameters: blockNumber', async () => { + await expect( + getBytecode(config, { + address: address.wagmiMintExample, + blockNumber: 15564163n, + }), + ).resolves.toBe(undefined) +}) + +test('parameters: blockTag', async () => { + await expect( + getBytecode(config, { + address: address.wagmiMintExample, + blockTag: 'earliest', + }), + ).resolves.toBe(undefined) +}) + +test('parameters: chainId', async () => { + await expect( + getBytecode(config, { + address: address.wagmiMintExample, + chainId: chain.optimism.id, + }), + ).resolves.toBe(undefined) +}) diff --git a/packages/core/src/actions/getBytecode.ts b/packages/core/src/actions/getBytecode.ts new file mode 100644 index 0000000000..2ece822a86 --- /dev/null +++ b/packages/core/src/actions/getBytecode.ts @@ -0,0 +1,30 @@ +import { + type GetBytecodeErrorType as viem_GetBytecodeErrorType, + type GetBytecodeParameters as viem_GetBytecodeParameters, + type GetBytecodeReturnType as viem_GetBytecodeReturnType, + getBytecode as viem_getBytecode, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetBytecodeParameters = Compute< + viem_GetBytecodeParameters & ChainIdParameter +> + +export type GetBytecodeReturnType = viem_GetBytecodeReturnType + +export type GetBytecodeErrorType = viem_GetBytecodeErrorType + +/** https://wagmi.sh/core/api/actions/getBytecode */ +export async function getBytecode( + config: config, + parameters: GetBytecodeParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getBytecode, 'getBytecode') + return action(rest) +} diff --git a/packages/core/src/actions/getCallsStatus.test.ts b/packages/core/src/actions/getCallsStatus.test.ts new file mode 100644 index 0000000000..72978a3c7f --- /dev/null +++ b/packages/core/src/actions/getCallsStatus.test.ts @@ -0,0 +1,70 @@ +import { accounts, config, testClient } from '@wagmi/test' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getCallsStatus } from './getCallsStatus.js' +import { sendCalls } from './sendCalls.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + const { id } = await sendCalls(config, { + calls: [ + { + data: '0xdeadbeef', + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }) + await testClient.mainnet.mine({ blocks: 1 }) + const { receipts, status } = await getCallsStatus(config, { + id, + }) + + expect(status).toBe('success') + expect( + receipts?.map((x) => ({ ...x, blockHash: undefined })), + ).toMatchInlineSnapshot( + ` + [ + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21064n, + "logs": [], + "status": "success", + "transactionHash": "0x13c53b2d4d9da424835525349cd66e553330f323d6fb19458b801ae1f7989a41", + }, + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21000n, + "logs": [], + "status": "success", + "transactionHash": "0xd8397b3e82b061c26a0c2093f1ceca0c3662a512614f7d6370349e89d0eea007", + }, + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21000n, + "logs": [], + "status": "success", + "transactionHash": "0x4d26e346593d9ea265bb164b115e89aa92df43b0b8778ac75d4ad28e2a22b101", + }, + ] + `, + ) + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/getCallsStatus.ts b/packages/core/src/actions/getCallsStatus.ts new file mode 100644 index 0000000000..85f7a592c5 --- /dev/null +++ b/packages/core/src/actions/getCallsStatus.ts @@ -0,0 +1,27 @@ +import { + type GetCallsStatusErrorType as viem_GetCallsStatusErrorType, + type GetCallsStatusParameters as viem_GetCallsStatusParameters, + type GetCallsStatusReturnType as viem_GetCallsStatusReturnType, + getCallsStatus as viem_getCallsStatus, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ConnectorParameter } from '../types/properties.js' +import { getConnectorClient } from './getConnectorClient.js' + +export type GetCallsStatusParameters = viem_GetCallsStatusParameters & + ConnectorParameter + +export type GetCallsStatusReturnType = viem_GetCallsStatusReturnType + +export type GetCallsStatusErrorType = viem_GetCallsStatusErrorType + +/** https://wagmi.sh/core/api/actions/getCallsStatus */ +export async function getCallsStatus( + config: config, + parameters: GetCallsStatusParameters, +): Promise { + const { connector, id } = parameters + const client = await getConnectorClient(config, { connector }) + return viem_getCallsStatus(client, { id }) +} diff --git a/packages/core/src/actions/getCapabilities.test.ts b/packages/core/src/actions/getCapabilities.test.ts new file mode 100644 index 0000000000..e7c02ec444 --- /dev/null +++ b/packages/core/src/actions/getCapabilities.test.ts @@ -0,0 +1,64 @@ +import { accounts, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getCapabilities } from './getCapabilities.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + const capabilities = await getCapabilities(config) + expect(capabilities).toMatchInlineSnapshot(` + { + "8453": { + "paymasterService": { + "supported": true, + }, + "sessionKeys": { + "supported": true, + }, + }, + "84532": { + "paymasterService": { + "supported": true, + }, + }, + } + `) + await disconnect(config, { connector }) +}) + +test('args: account', async () => { + await connect(config, { connector }) + const capabilities = await getCapabilities(config, { + account: accounts[1], + }) + expect(capabilities).toMatchInlineSnapshot(` + { + "8453": { + "paymasterService": { + "supported": false, + }, + "sessionKeys": { + "supported": true, + }, + }, + "84532": { + "paymasterService": { + "supported": false, + }, + }, + } + `) + await disconnect(config, { connector }) +}) + +test('behavior: not connected', async () => { + await expect(getCapabilities(config)).rejects.toMatchInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) +}) diff --git a/packages/core/src/actions/getCapabilities.ts b/packages/core/src/actions/getCapabilities.ts new file mode 100644 index 0000000000..ab8ea82bfb --- /dev/null +++ b/packages/core/src/actions/getCapabilities.ts @@ -0,0 +1,39 @@ +import type { Account } from 'viem' +import { + type GetCapabilitiesErrorType as viem_GetCapabilitiesErrorType, + type GetCapabilitiesParameters as viem_GetCapabilitiesParameters, + type GetCapabilitiesReturnType as viem_GetCapabilitiesReturnType, + getCapabilities as viem_getCapabilities, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ConnectorParameter } from '../types/properties.js' +import { getConnectorClient } from './getConnectorClient.js' + +export type GetCapabilitiesParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +> = viem_GetCapabilitiesParameters & ConnectorParameter + +export type GetCapabilitiesReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +> = viem_GetCapabilitiesReturnType + +export type GetCapabilitiesErrorType = viem_GetCapabilitiesErrorType + +/** https://wagmi.sh/core/api/actions/getCapabilities */ +export async function getCapabilities< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +>( + config: config, + parameters: GetCapabilitiesParameters = {}, +): Promise> { + const { account, chainId, connector } = parameters + const client = await getConnectorClient(config, { account, connector }) + return viem_getCapabilities(client as any, { + account: account as Account, + chainId, + }) +} diff --git a/packages/core/src/actions/getChainId.test.ts b/packages/core/src/actions/getChainId.test.ts new file mode 100644 index 0000000000..3dcedcf085 --- /dev/null +++ b/packages/core/src/actions/getChainId.test.ts @@ -0,0 +1,10 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getChainId } from './getChainId.js' + +test('default', async () => { + expect(getChainId(config)).toEqual(chain.mainnet.id) + config.setState((x) => ({ ...x, chainId: chain.mainnet2.id })) + expect(getChainId(config)).toEqual(chain.mainnet2.id) +}) diff --git a/packages/core/src/actions/getChainId.ts b/packages/core/src/actions/getChainId.ts new file mode 100644 index 0000000000..208602e05b --- /dev/null +++ b/packages/core/src/actions/getChainId.ts @@ -0,0 +1,11 @@ +import type { Config } from '../createConfig.js' + +export type GetChainIdReturnType = + config['chains'][number]['id'] + +/** https://wagmi.sh/core/api/actions/getChainId */ +export function getChainId( + config: config, +): GetChainIdReturnType { + return config.state.chainId +} diff --git a/packages/core/src/actions/getChains.test-d.ts b/packages/core/src/actions/getChains.test-d.ts new file mode 100644 index 0000000000..cd5f04c9c5 --- /dev/null +++ b/packages/core/src/actions/getChains.test-d.ts @@ -0,0 +1,12 @@ +import { type chain, config } from '@wagmi/test' +import type { Chain } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { getChains } from './getChains.js' + +test('default', async () => { + const chains = getChains(config) + expectTypeOf(chains[0]).toEqualTypeOf() + expectTypeOf(chains[2]).toEqualTypeOf() + expectTypeOf(chains[3]).toEqualTypeOf() +}) diff --git a/packages/core/src/actions/getChains.test.ts b/packages/core/src/actions/getChains.test.ts new file mode 100644 index 0000000000..fbaeae7645 --- /dev/null +++ b/packages/core/src/actions/getChains.test.ts @@ -0,0 +1,14 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getChains } from './getChains.js' + +test('default', async () => { + expect(getChains(config)).toEqual([ + chain.mainnet, + chain.mainnet2, + chain.optimism, + ]) + config._internal.chains.setState([chain.mainnet, chain.mainnet2]) + expect(getChains(config)).toEqual([chain.mainnet, chain.mainnet2]) +}) diff --git a/packages/core/src/actions/getChains.ts b/packages/core/src/actions/getChains.ts new file mode 100644 index 0000000000..a91e6e89b4 --- /dev/null +++ b/packages/core/src/actions/getChains.ts @@ -0,0 +1,21 @@ +import type { Chain } from 'viem' +import type { Config } from '../createConfig.js' +import { deepEqual } from '../utils/deepEqual.js' + +export type GetChainsReturnType = readonly [ + ...config['chains'], + ...Chain[], +] + +let previousChains: readonly Chain[] = [] + +/** https://wagmi.sh/core/api/actions/getChains */ +export function getChains( + config: config, +): GetChainsReturnType { + const chains = config.chains + if (deepEqual(previousChains, chains)) + return previousChains as GetChainsReturnType + previousChains = chains + return chains as unknown as GetChainsReturnType +} diff --git a/packages/core/src/actions/getClient.test-d.ts b/packages/core/src/actions/getClient.test-d.ts new file mode 100644 index 0000000000..f64cbae2ce --- /dev/null +++ b/packages/core/src/actions/getClient.test-d.ts @@ -0,0 +1,27 @@ +import { chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { getClient } from './getClient.js' + +test('default', () => { + const client = getClient(config) + expectTypeOf(client.chain).toEqualTypeOf<(typeof config)['chains'][number]>() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = getClient(config, { + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('behavior: unconfigured chain', () => { + const client = getClient(config, { + // @ts-expect-error + chainId: 123456, + }) + expectTypeOf(client).toEqualTypeOf() +}) diff --git a/packages/core/src/actions/getClient.test.ts b/packages/core/src/actions/getClient.test.ts new file mode 100644 index 0000000000..9eb0fa574b --- /dev/null +++ b/packages/core/src/actions/getClient.test.ts @@ -0,0 +1,17 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getClient } from './getClient.js' + +test('default', () => { + expect(getClient(config)).toBeDefined() +}) + +test('behavior: unconfigured chain', () => { + expect( + getClient(config, { + // @ts-expect-error + chainId: 123456, + }), + ).toBeUndefined() +}) diff --git a/packages/core/src/actions/getClient.ts b/packages/core/src/actions/getClient.ts new file mode 100644 index 0000000000..82f1c6c171 --- /dev/null +++ b/packages/core/src/actions/getClient.ts @@ -0,0 +1,52 @@ +import type { Client } from 'viem' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute, IsNarrowable } from '../types/utils.js' + +export type GetClientParameters< + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | number + | undefined = config['chains'][number]['id'], +> = ChainIdParameter + +export type GetClientReturnType< + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], + /// + resolvedChainId extends + | config['chains'][number]['id'] + | undefined = IsNarrowable< + config['chains'][number]['id'], + number + > extends true + ? IsNarrowable extends true + ? chainId + : config['chains'][number]['id'] + : config['chains'][number]['id'] | undefined, +> = resolvedChainId extends config['chains'][number]['id'] + ? Compute< + Client< + config['_internal']['transports'][resolvedChainId], + Extract + > + > + : undefined + +export function getClient< + config extends Config, + chainId extends config['chains'][number]['id'] | number | undefined, +>( + config: config, + parameters: GetClientParameters = {}, +): GetClientReturnType { + let client = undefined + try { + client = config.getClient(parameters) + } catch {} + return client as GetClientReturnType +} diff --git a/packages/core/src/actions/getConnections.test.ts b/packages/core/src/actions/getConnections.test.ts new file mode 100644 index 0000000000..22e6748cf9 --- /dev/null +++ b/packages/core/src/actions/getConnections.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getConnections } from './getConnections.js' + +test('default', async () => { + const connector = config.connectors[0]! + expect(getConnections(config)).toEqual([]) + await connect(config, { connector }) + expect(getConnections(config).length).toEqual(1) + await disconnect(config, { connector }) + expect(getConnections(config)).toEqual([]) +}) diff --git a/packages/core/src/actions/getConnections.ts b/packages/core/src/actions/getConnections.ts new file mode 100644 index 0000000000..72cdbc27d7 --- /dev/null +++ b/packages/core/src/actions/getConnections.ts @@ -0,0 +1,16 @@ +import type { Config, Connection } from '../createConfig.js' +import type { Compute } from '../types/utils.js' +import { deepEqual } from '../utils/deepEqual.js' + +export type GetConnectionsReturnType = Compute[] + +let previousConnections: Connection[] = [] + +/** https://wagmi.sh/core/api/actions/getConnections */ +export function getConnections(config: Config): GetConnectionsReturnType { + const connections = [...config.state.connections.values()] + if (config.state.status === 'reconnecting') return previousConnections + if (deepEqual(previousConnections, connections)) return previousConnections + previousConnections = connections + return connections +} diff --git a/packages/core/src/actions/getConnectorClient.test-d.ts b/packages/core/src/actions/getConnectorClient.test-d.ts new file mode 100644 index 0000000000..c4d980a7d5 --- /dev/null +++ b/packages/core/src/actions/getConnectorClient.test-d.ts @@ -0,0 +1,19 @@ +import { chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { getConnectorClient } from './getConnectorClient.js' + +test('default', async () => { + const client = await getConnectorClient(config) + expectTypeOf(client.chain).toEqualTypeOf<(typeof config)['chains'][number]>() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', async () => { + const client = await getConnectorClient(config, { + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) diff --git a/packages/core/src/actions/getConnectorClient.test.ts b/packages/core/src/actions/getConnectorClient.test.ts new file mode 100644 index 0000000000..a9d60f5142 --- /dev/null +++ b/packages/core/src/actions/getConnectorClient.test.ts @@ -0,0 +1,106 @@ +import { address, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import type { Connector } from '../createConfig.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getConnectorClient } from './getConnectorClient.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + await expect(getConnectorClient(config)).resolves.toBeDefined() + await disconnect(config, { connector }) +}) + +test('parameters: connector', async () => { + const connector2 = config.connectors[1]! + await connect(config, { connector }) + await connect(config, { connector: connector2 }) + await expect(getConnectorClient(config, { connector })).resolves.toBeDefined() + await disconnect(config, { connector }) + await disconnect(config, { connector: connector2 }) +}) + +test.todo('custom connector client') + +test('behavior: account address is checksummed', async () => { + await connect(config, { connector }) + const account = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' + const client = await getConnectorClient(config, { account }) + expect(client.account.address).toMatchInlineSnapshot( + '"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"', + ) + expect(client.account.address).not.toBe(account) + await disconnect(config, { connector }) +}) + +test('behavior: not connected', async () => { + await expect( + getConnectorClient(config), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) +}) + +test('behavior: connector is on different chain', async () => { + await connect(config, { chainId: 1, connector }) + config.setState((state) => { + const uid = state.current! + const connection = state.connections.get(uid)! + return { + ...state, + connections: new Map(state.connections).set(uid, { + ...connection, + chainId: 456, + }), + } + }) + await expect( + getConnectorClient(config, { account: address.usdcHolder }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorChainMismatchError: The current chain of the connector (id: 1) does not match the connection's chain (id: 456). + + Current Chain ID: 1 + Expected Chain ID: 456 + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) + +test('behavior: account does not exist on connector', async () => { + await connect(config, { connector }) + await expect( + getConnectorClient(config, { account: address.usdcHolder }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorAccountNotFoundError: Account "0x5414d89a8bF7E99d732BC52f3e6A3Ef461c0C078" not found for connector "Mock Connector". + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) + +test('behavior: reconnecting', async () => { + config.setState((state) => ({ ...state, status: 'reconnecting' })) + const { id, name, type, uid } = connector + await expect( + getConnectorClient(config, { + connector: { + id, + name, + type, + uid, + } as unknown as Connector, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorUnavailableReconnectingError: Connector "Mock Connector" unavailable while reconnecting. + + Details: During the reconnection step, the only connector methods guaranteed to be available are: \`id\`, \`name\`, \`type\`, \`uid\`. All other methods are not guaranteed to be available until reconnection completes and connectors are fully restored. This error commonly occurs for connectors that asynchronously inject after reconnection has already started. + Version: @wagmi/core@x.y.z] + `) + config.setState((state) => ({ ...state, status: 'disconnected' })) +}) diff --git a/packages/core/src/actions/getConnectorClient.ts b/packages/core/src/actions/getConnectorClient.ts new file mode 100644 index 0000000000..534ba76bc6 --- /dev/null +++ b/packages/core/src/actions/getConnectorClient.ts @@ -0,0 +1,147 @@ +import { + type Account, + type Address, + type BaseErrorType, + type Client, + createClient, + custom, +} from 'viem' +import { getAddress, parseAccount } from 'viem/utils' + +import type { Config, Connection } from '../createConfig.js' +import type { ErrorType } from '../errors/base.js' +import { + ConnectorAccountNotFoundError, + type ConnectorAccountNotFoundErrorType, + ConnectorChainMismatchError, + type ConnectorChainMismatchErrorType, + ConnectorNotConnectedError, + type ConnectorNotConnectedErrorType, + ConnectorUnavailableReconnectingError, + type ConnectorUnavailableReconnectingErrorType, +} from '../errors/config.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { Compute } from '../types/utils.js' + +export type GetConnectorClientParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + ChainIdParameter & + ConnectorParameter & { + /** + * Account to use for the client. + * + * - `Account | Address`: An Account MUST exist on the connector. + * - `null`: Account MAY NOT exist on the connector. This is useful for + * actions that can infer the account from the connector (e.g. sending a + * call without a connected account – the user will be prompted to select + * an account within the wallet). + */ + account?: Address | Account | null | undefined + } +> + +export type GetConnectorClientReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + Client< + config['_internal']['transports'][chainId], + Extract, + Account + > +> + +export type GetConnectorClientErrorType = + | ConnectorAccountNotFoundErrorType + | ConnectorChainMismatchErrorType + | ConnectorNotConnectedErrorType + | ConnectorUnavailableReconnectingErrorType + // base + | BaseErrorType + | ErrorType + +/** https://wagmi.sh/core/api/actions/getConnectorClient */ +export async function getConnectorClient< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: GetConnectorClientParameters = {}, +): Promise> { + // Get connection + let connection: Connection | undefined + if (parameters.connector) { + const { connector } = parameters + if ( + config.state.status === 'reconnecting' && + !connector.getAccounts && + !connector.getChainId + ) + throw new ConnectorUnavailableReconnectingError({ connector }) + + const [accounts, chainId] = await Promise.all([ + connector.getAccounts().catch((e) => { + if (parameters.account === null) return [] + throw e + }), + connector.getChainId(), + ]) + connection = { + accounts: accounts as readonly [Address, ...Address[]], + chainId, + connector, + } + } else connection = config.state.connections.get(config.state.current!) + if (!connection) throw new ConnectorNotConnectedError() + + const chainId = parameters.chainId ?? connection.chainId + + // Check connector using same chainId as connection + const connectorChainId = await connection.connector.getChainId() + if (connectorChainId !== connection.chainId) + throw new ConnectorChainMismatchError({ + connectionChainId: connection.chainId, + connectorChainId, + }) + + // If connector has custom `getClient` implementation + type Return = GetConnectorClientReturnType + const connector = connection.connector + if (connector.getClient) + return connector.getClient({ chainId }) as unknown as Return + + // Default using `custom` transport + const account = parseAccount(parameters.account ?? connection.accounts[0]!) + if (account) account.address = getAddress(account.address) // TODO: Checksum address as part of `parseAccount`? + + // If account was provided, check that it exists on the connector + if ( + parameters.account && + !connection.accounts.some( + (x) => x.toLowerCase() === account.address.toLowerCase(), + ) + ) + throw new ConnectorAccountNotFoundError({ + address: account.address, + connector, + }) + + const chain = config.chains.find((chain) => chain.id === chainId) + const provider = (await connection.connector.getProvider({ chainId })) as { + request(...args: any): Promise + } + + return createClient({ + account, + chain, + name: 'Connector Client', + transport: (opts) => custom(provider)({ ...opts, retryCount: 0 }), + }) as Return +} diff --git a/packages/core/src/actions/getConnectors.test.ts b/packages/core/src/actions/getConnectors.test.ts new file mode 100644 index 0000000000..d15f5fbb08 --- /dev/null +++ b/packages/core/src/actions/getConnectors.test.ts @@ -0,0 +1,8 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getConnectors } from './getConnectors.js' + +test('default', () => { + expect(getConnectors(config)).toEqual(config.connectors) +}) diff --git a/packages/core/src/actions/getConnectors.ts b/packages/core/src/actions/getConnectors.ts new file mode 100644 index 0000000000..439362d3f4 --- /dev/null +++ b/packages/core/src/actions/getConnectors.ts @@ -0,0 +1,17 @@ +import type { Config, Connector } from '../createConfig.js' +import { deepEqual } from '../utils/deepEqual.js' + +export type GetConnectorsReturnType = + config['connectors'] + +let previousConnectors: readonly Connector[] = [] + +/** https://wagmi.sh/core/api/actions/getConnectors */ +export function getConnectors( + config: config, +): GetConnectorsReturnType { + const connectors = config.connectors + if (deepEqual(previousConnectors, connectors)) return previousConnectors + previousConnectors = connectors + return connectors +} diff --git a/packages/core/src/actions/getEnsAddress.test.ts b/packages/core/src/actions/getEnsAddress.test.ts new file mode 100644 index 0000000000..d120c82ef5 --- /dev/null +++ b/packages/core/src/actions/getEnsAddress.test.ts @@ -0,0 +1,12 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsAddress } from './getEnsAddress.js' + +test('default', async () => { + await expect( + getEnsAddress(config, { name: 'wevm.eth' }), + ).resolves.toMatchInlineSnapshot( + '"0xd2135CfB216b74109775236E36d4b433F1DF507B"', + ) +}) diff --git a/packages/core/src/actions/getEnsAddress.ts b/packages/core/src/actions/getEnsAddress.ts new file mode 100644 index 0000000000..5f882999be --- /dev/null +++ b/packages/core/src/actions/getEnsAddress.ts @@ -0,0 +1,30 @@ +import { + type GetEnsAddressErrorType as viem_GetEnsAddressErrorType, + type GetEnsAddressParameters as viem_GetEnsAddressParameters, + type GetEnsAddressReturnType as viem_GetEnsAddressReturnType, + getEnsAddress as viem_getEnsAddress, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetEnsAddressParameters = Compute< + viem_GetEnsAddressParameters & ChainIdParameter +> + +export type GetEnsAddressReturnType = viem_GetEnsAddressReturnType + +export type GetEnsAddressErrorType = viem_GetEnsAddressErrorType + +/** https://wagmi.sh/core/api/actions/getEnsAddress */ +export function getEnsAddress( + config: config, + parameters: GetEnsAddressParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getEnsAddress, 'getEnsAddress') + return action(rest) +} diff --git a/packages/core/src/actions/getEnsAvatar.test.ts b/packages/core/src/actions/getEnsAvatar.test.ts new file mode 100644 index 0000000000..ed1e830481 --- /dev/null +++ b/packages/core/src/actions/getEnsAvatar.test.ts @@ -0,0 +1,12 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsAvatar } from './getEnsAvatar.js' + +test('default', async () => { + await expect( + getEnsAvatar(config, { + name: 'wevm.eth', + }), + ).resolves.toMatchInlineSnapshot('"https://euc.li/wevm.eth"') +}) diff --git a/packages/core/src/actions/getEnsAvatar.ts b/packages/core/src/actions/getEnsAvatar.ts new file mode 100644 index 0000000000..e6c3855d13 --- /dev/null +++ b/packages/core/src/actions/getEnsAvatar.ts @@ -0,0 +1,30 @@ +import { + type GetEnsAvatarErrorType as viem_GetEnsAvatarErrorType, + type GetEnsAvatarParameters as viem_GetEnsAvatarParameters, + type GetEnsAvatarReturnType as viem_GetEnsAvatarReturnType, + getEnsAvatar as viem_getEnsAvatar, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetEnsAvatarParameters = Compute< + viem_GetEnsAvatarParameters & ChainIdParameter +> + +export type GetEnsAvatarReturnType = viem_GetEnsAvatarReturnType + +export type GetEnsAvatarErrorType = viem_GetEnsAvatarErrorType + +/** https://wagmi.sh/core/api/actions/getEnsAvatar */ +export function getEnsAvatar( + config: config, + parameters: GetEnsAvatarParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getEnsAvatar, 'getEnsAvatar') + return action(rest) +} diff --git a/packages/core/src/actions/getEnsName.test.ts b/packages/core/src/actions/getEnsName.test.ts new file mode 100644 index 0000000000..38d1bae97b --- /dev/null +++ b/packages/core/src/actions/getEnsName.test.ts @@ -0,0 +1,12 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsName } from './getEnsName.js' + +test('default', async () => { + await expect( + getEnsName(config, { + address: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + }), + ).resolves.toMatchInlineSnapshot('"wevm.eth"') +}) diff --git a/packages/core/src/actions/getEnsName.ts b/packages/core/src/actions/getEnsName.ts new file mode 100644 index 0000000000..e6ab338db4 --- /dev/null +++ b/packages/core/src/actions/getEnsName.ts @@ -0,0 +1,30 @@ +import { + type GetEnsNameErrorType as viem_GetEnsNameErrorType, + type GetEnsNameParameters as viem_GetEnsNameParameters, + type GetEnsNameReturnType as viem_GetEnsNameReturnType, + getEnsName as viem_getEnsName, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetEnsNameParameters = Compute< + viem_GetEnsNameParameters & ChainIdParameter +> + +export type GetEnsNameReturnType = viem_GetEnsNameReturnType + +export type GetEnsNameErrorType = viem_GetEnsNameErrorType + +/** https://wagmi.sh/core/api/actions/getEnsName */ +export function getEnsName( + config: config, + parameters: GetEnsNameParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getEnsName, 'getEnsName') + return action(rest) +} diff --git a/packages/core/src/actions/getEnsResolver.test.ts b/packages/core/src/actions/getEnsResolver.test.ts new file mode 100644 index 0000000000..4bc30be55b --- /dev/null +++ b/packages/core/src/actions/getEnsResolver.test.ts @@ -0,0 +1,14 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsResolver } from './getEnsResolver.js' + +test('default', async () => { + await expect( + getEnsResolver(config, { + name: 'wevm.eth', + }), + ).resolves.toMatchInlineSnapshot( + '"0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41"', + ) +}) diff --git a/packages/core/src/actions/getEnsResolver.ts b/packages/core/src/actions/getEnsResolver.ts new file mode 100644 index 0000000000..ab59b7d76c --- /dev/null +++ b/packages/core/src/actions/getEnsResolver.ts @@ -0,0 +1,30 @@ +import { + type GetEnsResolverErrorType as viem_GetEnsResolverErrorType, + type GetEnsResolverParameters as viem_GetEnsResolverParameters, + type GetEnsResolverReturnType as viem_GetEnsResolverReturnType, + getEnsResolver as viem_getEnsResolver, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetEnsResolverParameters = Compute< + viem_GetEnsResolverParameters & ChainIdParameter +> + +export type GetEnsResolverReturnType = viem_GetEnsResolverReturnType + +export type GetEnsResolverErrorType = viem_GetEnsResolverErrorType + +/** https://wagmi.sh/core/api/actions/getEnsResolver */ +export function getEnsResolver( + config: config, + parameters: GetEnsResolverParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getEnsResolver, 'getEnsResolver') + return action(rest) +} diff --git a/packages/core/src/actions/getEnsText.test.ts b/packages/core/src/actions/getEnsText.test.ts new file mode 100644 index 0000000000..63801747d5 --- /dev/null +++ b/packages/core/src/actions/getEnsText.test.ts @@ -0,0 +1,13 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsText } from './getEnsText.js' + +test('default', async () => { + await expect( + getEnsText(config, { + name: 'wevm.eth', + key: 'com.twitter', + }), + ).resolves.toMatchInlineSnapshot('"wevm_dev"') +}) diff --git a/packages/core/src/actions/getEnsText.ts b/packages/core/src/actions/getEnsText.ts new file mode 100644 index 0000000000..d786f72b78 --- /dev/null +++ b/packages/core/src/actions/getEnsText.ts @@ -0,0 +1,30 @@ +import { + type GetEnsTextErrorType as viem_GetEnsTextErrorType, + type GetEnsTextParameters as viem_GetEnsTextParameters, + type GetEnsTextReturnType as viem_GetEnsTextReturnType, + getEnsText as viem_getEnsText, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetEnsTextParameters = Compute< + viem_GetEnsTextParameters & ChainIdParameter +> + +export type GetEnsTextReturnType = viem_GetEnsTextReturnType + +export type GetEnsTextErrorType = viem_GetEnsTextErrorType + +/** https://wagmi.sh/core/api/actions/getEnsText */ +export function getEnsText( + config: config, + parameters: GetEnsTextParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getEnsText, 'getEnsText') + return action(rest) +} diff --git a/packages/core/src/actions/getFeeHistory.test.ts b/packages/core/src/actions/getFeeHistory.test.ts new file mode 100644 index 0000000000..2630381556 --- /dev/null +++ b/packages/core/src/actions/getFeeHistory.test.ts @@ -0,0 +1,63 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getFeeHistory } from './getFeeHistory.js' + +test('default', async () => { + await expect( + getFeeHistory(config, { + blockCount: 4, + rewardPercentiles: [25, 75], + }), + ).resolves.toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) +}) + +test('parameters: chainId', async () => { + await expect( + getFeeHistory(config, { + blockCount: 4, + rewardPercentiles: [25, 75], + chainId: chain.mainnet2.id, + }), + ).resolves.toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) +}) + +test('parameters: blockNumber', async () => { + await expect( + getFeeHistory(config, { + blockCount: 4, + rewardPercentiles: [25, 75], + blockNumber: 18677379n, + }), + ).resolves.toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) +}) + +test('parameters: blockTag', async () => { + await expect( + getFeeHistory(config, { + blockCount: 4, + rewardPercentiles: [25, 75], + blockTag: 'safe', + }), + ).resolves.toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) +}) diff --git a/packages/core/src/actions/getFeeHistory.ts b/packages/core/src/actions/getFeeHistory.ts new file mode 100644 index 0000000000..9588214139 --- /dev/null +++ b/packages/core/src/actions/getFeeHistory.ts @@ -0,0 +1,36 @@ +import { + type GetFeeHistoryErrorType as viem_GetFeeHistoryErrorType, + type GetFeeHistoryParameters as viem_GetFeeHistoryParameters, + type GetFeeHistoryReturnType as viem_GetFeeHistoryReturnType, + getFeeHistory as viem_getFeeHistory, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetFeeHistoryParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute> + +export type GetFeeHistoryReturnType = viem_GetFeeHistoryReturnType + +export type GetFeeHistoryErrorType = viem_GetFeeHistoryErrorType + +/** https://wagmi.sh/core/api/actions/getFeeHistory */ +export function getFeeHistory< + config extends Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + config: config, + parameters: GetFeeHistoryParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getFeeHistory, 'getFeeHistory') + return action(rest) +} diff --git a/packages/core/src/actions/getGasPrice.test.ts b/packages/core/src/actions/getGasPrice.test.ts new file mode 100644 index 0000000000..64b7ba6fd5 --- /dev/null +++ b/packages/core/src/actions/getGasPrice.test.ts @@ -0,0 +1,21 @@ +import { chain, config, testClient } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getGasPrice } from './getGasPrice.js' + +test('default', async () => { + await testClient.mainnet.setNextBlockBaseFeePerGas({ + baseFeePerGas: 2_000_000_000n, + }) + await expect(getGasPrice(config)).resolves.toBe(3000000000n) +}) + +test('parameters: chainId', async () => { + await testClient.mainnet2.setNextBlockBaseFeePerGas({ + baseFeePerGas: 1_000_000_000n, + }) + await testClient.mainnet2.mine({ blocks: 1 }) + await expect( + getGasPrice(config, { chainId: chain.mainnet2.id }), + ).resolves.toBe(1875000000n) +}) diff --git a/packages/core/src/actions/getGasPrice.ts b/packages/core/src/actions/getGasPrice.ts new file mode 100644 index 0000000000..c6482c44a2 --- /dev/null +++ b/packages/core/src/actions/getGasPrice.ts @@ -0,0 +1,35 @@ +import { + type GetGasPriceErrorType as viem_GetGasPriceErrorType, + type GetGasPriceReturnType as viem_GetGasPriceReturnType, + getGasPrice as viem_getGasPrice, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetGasPriceParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute> + +export type GetGasPriceReturnType = viem_GetGasPriceReturnType + +export type GetGasPriceErrorType = viem_GetGasPriceErrorType + +/** https://wagmi.sh/core/api/actions/getGasPrice */ +export function getGasPrice< + config extends Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + config: config, + parameters: GetGasPriceParameters = {}, +): Promise { + const { chainId } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getGasPrice, 'getGasPrice') + return action({}) +} diff --git a/packages/core/src/actions/getProof.test.ts b/packages/core/src/actions/getProof.test.ts new file mode 100644 index 0000000000..5ff0af2828 --- /dev/null +++ b/packages/core/src/actions/getProof.test.ts @@ -0,0 +1,16 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getProof } from './getProof.js' + +test('default', async () => { + await expect( + getProof(config, { + chainId: chain.optimism.id, + address: '0x4200000000000000000000000000000000000016', + storageKeys: [ + '0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99', + ], + }), + ).resolves.toBeDefined() +}) diff --git a/packages/core/src/actions/getProof.ts b/packages/core/src/actions/getProof.ts new file mode 100644 index 0000000000..ee9ec218d3 --- /dev/null +++ b/packages/core/src/actions/getProof.ts @@ -0,0 +1,30 @@ +import { + type GetProofErrorType as viem_GetProofErrorType, + type GetProofParameters as viem_GetProofParameters, + type GetProofReturnType as viem_GetProofReturnType, + getProof as viem_getProof, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetProofParameters = Compute< + viem_GetProofParameters & ChainIdParameter +> + +export type GetProofReturnType = viem_GetProofReturnType + +export type GetProofErrorType = viem_GetProofErrorType + +/** https://wagmi.sh/core/api/actions/getProof */ +export async function getProof( + config: config, + parameters: GetProofParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getProof, 'getProof') + return action(rest) +} diff --git a/packages/core/src/actions/getPublicClient.test-d.ts b/packages/core/src/actions/getPublicClient.test-d.ts new file mode 100644 index 0000000000..711f11a29f --- /dev/null +++ b/packages/core/src/actions/getPublicClient.test-d.ts @@ -0,0 +1,27 @@ +import { chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { getPublicClient } from './getPublicClient.js' + +test('default', () => { + const client = getPublicClient(config) + expectTypeOf(client.chain).toEqualTypeOf<(typeof config)['chains'][number]>() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = getPublicClient(config, { + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('behavior: unconfigured chain', () => { + const client = getPublicClient(config, { + // @ts-expect-error + chainId: 123456, + }) + expectTypeOf(client).toEqualTypeOf() +}) diff --git a/packages/core/src/actions/getPublicClient.test.ts b/packages/core/src/actions/getPublicClient.test.ts new file mode 100644 index 0000000000..c77d0bfb94 --- /dev/null +++ b/packages/core/src/actions/getPublicClient.test.ts @@ -0,0 +1,17 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getPublicClient } from './getPublicClient.js' + +test('default', () => { + expect(getPublicClient(config)).toBeDefined() +}) + +test('behavior: unconfigured chain', () => { + expect( + getPublicClient(config, { + // @ts-expect-error + chainId: 123456, + }), + ).toBeUndefined() +}) diff --git a/packages/core/src/actions/getPublicClient.ts b/packages/core/src/actions/getPublicClient.ts new file mode 100644 index 0000000000..1fbd53ed30 --- /dev/null +++ b/packages/core/src/actions/getPublicClient.ts @@ -0,0 +1,52 @@ +import { type Client, type PublicClient, publicActions } from 'viem' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute, IsNarrowable } from '../types/utils.js' +import { getClient } from './getClient.js' + +export type GetPublicClientParameters< + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], +> = ChainIdParameter + +export type GetPublicClientReturnType< + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], + /// + resolvedChainId extends + | config['chains'][number]['id'] + | undefined = IsNarrowable< + config['chains'][number]['id'], + number + > extends true + ? IsNarrowable extends true + ? chainId + : config['chains'][number]['id'] + : config['chains'][number]['id'] | undefined, +> = resolvedChainId extends config['chains'][number]['id'] + ? Compute< + PublicClient< + config['_internal']['transports'][resolvedChainId], + Extract + > + > + : undefined + +export function getPublicClient< + config extends Config, + chainId extends config['chains'][number]['id'] | number | undefined, +>( + config: config, + parameters: GetPublicClientParameters = {}, +): GetPublicClientReturnType { + const client = getClient(config, parameters) + return (client as Client)?.extend(publicActions) as GetPublicClientReturnType< + config, + chainId + > +} diff --git a/packages/core/src/actions/getStorageAt.test.ts b/packages/core/src/actions/getStorageAt.test.ts new file mode 100644 index 0000000000..bc612fe91b --- /dev/null +++ b/packages/core/src/actions/getStorageAt.test.ts @@ -0,0 +1,59 @@ +import { address, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getStorageAt } from './getStorageAt.js' + +test('default', async () => { + await expect( + getStorageAt(config, { + address: address.wagmiMintExample, + slot: '0x0', + }), + ).resolves.toBe( + '0x7761676d6900000000000000000000000000000000000000000000000000000a', + ) + await expect( + getStorageAt(config, { + address: address.wagmiMintExample, + slot: '0x1', + }), + ).resolves.toBe( + '0x5741474d4900000000000000000000000000000000000000000000000000000a', + ) +}) + +test('parameters: blockNumber', async () => { + await expect( + getStorageAt(config, { + address: address.wagmiMintExample, + blockNumber: 16280770n, + slot: '0x0', + }), + ).resolves.toBe( + '0x7761676d6900000000000000000000000000000000000000000000000000000a', + ) +}) + +test('parameters: blockTag', async () => { + await expect( + getStorageAt(config, { + address: address.wagmiMintExample, + blockTag: 'safe', + slot: '0x0', + }), + ).resolves.toBe( + '0x7761676d6900000000000000000000000000000000000000000000000000000a', + ) +}) + +test('parameters: chainId', async () => { + await expect( + getStorageAt(config, { + address: address.wagmiMintExample, + chainId: chain.optimism.id, + slot: '0x0', + }), + ).resolves.toBe( + '0x0000000000000000000000000000000000000000000000000000000000000000', + ) +}) diff --git a/packages/core/src/actions/getStorageAt.ts b/packages/core/src/actions/getStorageAt.ts new file mode 100644 index 0000000000..a07ec081b7 --- /dev/null +++ b/packages/core/src/actions/getStorageAt.ts @@ -0,0 +1,30 @@ +import { + type GetStorageAtErrorType as viem_GetStorageAtErrorType, + type GetStorageAtParameters as viem_GetStorageAtParameters, + type GetStorageAtReturnType as viem_GetStorageAtReturnType, + getStorageAt as viem_getStorageAt, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetStorageAtParameters = Compute< + viem_GetStorageAtParameters & ChainIdParameter +> + +export type GetStorageAtReturnType = viem_GetStorageAtReturnType + +export type GetStorageAtErrorType = viem_GetStorageAtErrorType + +/** https://wagmi.sh/core/api/actions/getStorageAt */ +export async function getStorageAt( + config: config, + parameters: GetStorageAtParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getStorageAt, 'getStorageAt') + return action(rest) +} diff --git a/packages/core/src/actions/getToken.test.ts b/packages/core/src/actions/getToken.test.ts new file mode 100644 index 0000000000..ed8903f3dd --- /dev/null +++ b/packages/core/src/actions/getToken.test.ts @@ -0,0 +1,84 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getToken } from './getToken.js' + +test('default', async () => { + await expect( + getToken(config, { + address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', + }), + ).resolves.toMatchInlineSnapshot(` + { + "address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", + "decimals": 18, + "name": "Uniswap", + "symbol": "UNI", + "totalSupply": { + "formatted": "1000000000", + "value": 1000000000000000000000000000n, + }, + } + `) +}) + +test('parameters: formatUnits', async () => { + await expect( + getToken(config, { + address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', + formatUnits: 'gwei', + }), + ).resolves.toMatchInlineSnapshot(` + { + "address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", + "decimals": 18, + "name": "Uniswap", + "symbol": "UNI", + "totalSupply": { + "formatted": "1000000000000000000", + "value": 1000000000000000000000000000n, + }, + } + `) +}) + +test('behavior: bytes32 token', async () => { + await expect( + getToken(config, { + address: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', + }), + ).resolves.toMatchInlineSnapshot(` + { + "address": "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2", + "decimals": 18, + "name": "Maker", + "symbol": "MKR", + "totalSupply": { + "formatted": "977631.036950888222010062", + "value": 977631036950888222010062n, + }, + } + `) +}) + +test('behavior: bogus token', async () => { + await expect( + getToken(config, { + address: '0xa0cf798816d4b9b9866b5330eea46a18382f251e', + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ContractFunctionExecutionError: The contract function "decimals" returned no data ("0x"). + + This could be due to any of the following: + - The contract does not have the function "decimals", + - The parameters passed to the contract function may be invalid, or + - The address is not a contract. + + Contract Call: + address: 0xa0cf798816d4b9b9866b5330eea46a18382f251e + function: decimals() + + Docs: https://viem.sh/docs/contract/multicall + Version: viem@2.29.2] + `) +}) diff --git a/packages/core/src/actions/getToken.ts b/packages/core/src/actions/getToken.ts new file mode 100644 index 0000000000..480a742820 --- /dev/null +++ b/packages/core/src/actions/getToken.ts @@ -0,0 +1,141 @@ +import type { Address, Hex } from 'viem' +import { + ContractFunctionExecutionError, + formatUnits, + hexToString, + trim, +} from 'viem' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Unit } from '../types/unit.js' +import type { Compute } from '../types/utils.js' +import { getUnit } from '../utils/getUnit.js' +import { type ReadContractsErrorType, readContracts } from './readContracts.js' + +export type GetTokenParameters = Compute< + ChainIdParameter & { + address: Address + formatUnits?: Unit | undefined + } +> + +export type GetTokenReturnType = { + address: Address + decimals: number + name: string | undefined + symbol: string | undefined + totalSupply: { + formatted: string + value: bigint + } +} + +export type GetTokenErrorType = ReadContractsErrorType + +/** @deprecated */ +export async function getToken( + config: config, + parameters: GetTokenParameters, +): Promise { + const { address, chainId, formatUnits: unit = 18 } = parameters + + function getAbi(type: type) { + return [ + { + type: 'function', + name: 'decimals', + stateMutability: 'view', + inputs: [], + outputs: [{ type: 'uint8' }], + }, + { + type: 'function', + name: 'name', + stateMutability: 'view', + inputs: [], + outputs: [{ type }], + }, + { + type: 'function', + name: 'symbol', + stateMutability: 'view', + inputs: [], + outputs: [{ type }], + }, + { + type: 'function', + name: 'totalSupply', + stateMutability: 'view', + inputs: [], + outputs: [{ type: 'uint256' }], + }, + ] as const + } + + try { + const abi = getAbi('string') + const contractConfig = { address, abi, chainId } as const + const [decimals, name, symbol, totalSupply] = await readContracts(config, { + allowFailure: true, + contracts: [ + { ...contractConfig, functionName: 'decimals' }, + { ...contractConfig, functionName: 'name' }, + { ...contractConfig, functionName: 'symbol' }, + { ...contractConfig, functionName: 'totalSupply' }, + ] as const, + }) + + // throw if `name` or `symbol` failed + if (name.error instanceof ContractFunctionExecutionError) throw name.error + if (symbol.error instanceof ContractFunctionExecutionError) + throw symbol.error + + // `decimals` and `totalSupply` are required + if (decimals.error) throw decimals.error + if (totalSupply.error) throw totalSupply.error + + return { + address, + decimals: decimals.result, + name: name.result, + symbol: symbol.result, + totalSupply: { + formatted: formatUnits(totalSupply.result!, getUnit(unit)), + value: totalSupply.result, + }, + } + } catch (error) { + // In the chance that there is an error upon decoding the contract result, + // it could be likely that the contract data is represented as bytes32 instead + // of a string. + if (error instanceof ContractFunctionExecutionError) { + const abi = getAbi('bytes32') + const contractConfig = { address, abi, chainId } as const + const [decimals, name, symbol, totalSupply] = await readContracts( + config, + { + allowFailure: false, + contracts: [ + { ...contractConfig, functionName: 'decimals' }, + { ...contractConfig, functionName: 'name' }, + { ...contractConfig, functionName: 'symbol' }, + { ...contractConfig, functionName: 'totalSupply' }, + ] as const, + }, + ) + return { + address, + decimals, + name: hexToString(trim(name as Hex, { dir: 'right' })), + symbol: hexToString(trim(symbol as Hex, { dir: 'right' })), + totalSupply: { + formatted: formatUnits(totalSupply, getUnit(unit)), + value: totalSupply, + }, + } + } + + throw error + } +} diff --git a/packages/core/src/actions/getTransaction.test-d.ts b/packages/core/src/actions/getTransaction.test-d.ts new file mode 100644 index 0000000000..9476b781c1 --- /dev/null +++ b/packages/core/src/actions/getTransaction.test-d.ts @@ -0,0 +1,29 @@ +import { http } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { getTransaction } from './getTransaction.js' + +test('chain formatters', async () => { + const config = createConfig({ + chains: [celo, mainnet], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + const result = await getTransaction(config, { hash: '0x123' }) + if (result.chainId === celo.id) { + expectTypeOf(result.feeCurrency).toEqualTypeOf<`0x${string}` | null>() + } +}) + +test('chainId', async () => { + const config = createConfig({ + chains: [celo, mainnet], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + const result = await getTransaction(config, { + hash: '0x123', + chainId: celo.id, + }) + expectTypeOf(result.feeCurrency).toEqualTypeOf<`0x${string}` | null>() +}) diff --git a/packages/core/src/actions/getTransaction.test.ts b/packages/core/src/actions/getTransaction.test.ts new file mode 100644 index 0000000000..3615e6a0d9 --- /dev/null +++ b/packages/core/src/actions/getTransaction.test.ts @@ -0,0 +1,36 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getTransaction } from './getTransaction.js' + +test('default', async () => { + await expect( + getTransaction(config, { + hash: '0xa559259bd2d0e8372421e222ff3545f705b5da60005bd787a23c2e68d6d7fefd', + }), + ).resolves.toMatchInlineSnapshot(` + { + "accessList": [], + "blockHash": "0x61c4e868008b465addd7c0a5da03db28bb9911597c58e239a85dd14dd43fd56a", + "blockNumber": 17488642n, + "chainId": 1, + "from": "0xd2135cfb216b74109775236e36d4b433f1df507b", + "gas": 53671n, + "gasPrice": 15806335296n, + "hash": "0xa559259bd2d0e8372421e222ff3545f705b5da60005bd787a23c2e68d6d7fefd", + "input": "0xa9059cbb0000000000000000000000006acbe090725d8b1cd59fe5f3e0c9c3685ebb77af00000000000000000000000000000000000000000000000000000002540be400", + "maxFeePerGas": 19000000000n, + "maxPriorityFeePerGas": 1000000000n, + "nonce": 29, + "r": "0x60a19c4a708571d2a7c661dc5494542fa2c6ddd8e7dc218e4c4795b6ba7969f5", + "s": "0x7ef2778cc21f5c12861208d0c030e77193a234273e32a1dd5066d7d677aa1ef2", + "to": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "transactionIndex": 58, + "type": "eip1559", + "typeHex": "0x2", + "v": 1n, + "value": 0n, + "yParity": 1, + } + `) +}) diff --git a/packages/core/src/actions/getTransaction.ts b/packages/core/src/actions/getTransaction.ts new file mode 100644 index 0000000000..0148282811 --- /dev/null +++ b/packages/core/src/actions/getTransaction.ts @@ -0,0 +1,51 @@ +import type { Chain } from 'viem' +import { + type GetTransactionErrorType as viem_GetTransactionErrorType, + type GetTransactionParameters as viem_GetTransactionParameters, + type GetTransactionReturnType as viem_GetTransactionReturnType, + getTransaction as viem_getTransaction, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute, IsNarrowable } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetTransactionParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute> + +export type GetTransactionReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = Compute< + { + [key in keyof chains]: viem_GetTransactionReturnType< + IsNarrowable extends true ? chains[key] : undefined + > & { chainId: chains[key]['id'] } + }[number] +> + +export type GetTransactionErrorType = viem_GetTransactionErrorType + +/** https://wagmi.sh/core/api/actions/getTransaction */ +export function getTransaction< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: GetTransactionParameters, +): Promise> { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_getTransaction, 'getTransaction') + return action(rest) as unknown as Promise< + GetTransactionReturnType + > +} diff --git a/packages/core/src/actions/getTransactionConfirmations.test-d.ts b/packages/core/src/actions/getTransactionConfirmations.test-d.ts new file mode 100644 index 0000000000..fd9168dfa2 --- /dev/null +++ b/packages/core/src/actions/getTransactionConfirmations.test-d.ts @@ -0,0 +1,85 @@ +import { config } from '@wagmi/test' +import { mainnet, zkSync } from 'viem/chains' +import { test } from 'vitest' + +import { http } from 'viem' +import { createConfig } from '../createConfig.js' +import { getTransactionConfirmations } from './getTransactionConfirmations.js' + +test('default', async () => { + getTransactionConfirmations(config, { + transactionReceipt: { + blockHash: '0x', + blockNumber: 1n, + contractAddress: '0x', + cumulativeGasUsed: 1n, + effectiveGasPrice: 1n, + from: '0x', + gasUsed: 1n, + l1Fee: 1n, + logs: [], + logsBloom: '0x', + status: 'success', + to: '0x', + transactionHash: '0x', + transactionIndex: 1, + type: 'eip1559', + }, + }) +}) + +test('chain formatters', async () => { + const config = createConfig({ + chains: [mainnet, zkSync], + transports: { [mainnet.id]: http(), [zkSync.id]: http() }, + }) + + const transactionReceipt = { + blockHash: '0x', + blockNumber: 1n, + contractAddress: '0x', + cumulativeGasUsed: 1n, + effectiveGasPrice: 1n, + from: '0x', + gasUsed: 1n, + logsBloom: '0x', + status: 'success', + to: '0x', + transactionHash: '0x', + transactionIndex: 1, + type: 'eip1559', + } as const + + getTransactionConfirmations(config, { + transactionReceipt: { + ...transactionReceipt, + l1BatchNumber: 1n, + l1BatchTxIndex: 1n, + logs: [], + l2ToL1Logs: [], + }, + }) + + getTransactionConfirmations(config, { + chainId: zkSync.id, + transactionReceipt: { + ...transactionReceipt, + l1BatchNumber: 1n, + l1BatchTxIndex: 1n, + logs: [], + l2ToL1Logs: [], + }, + }) + + getTransactionConfirmations(config, { + chainId: mainnet.id, + transactionReceipt: { + ...transactionReceipt, + // @ts-expect-error + l1BatchNumber: 1n, + l1BatchTxIndex: 1n, + logs: [], + l2ToL1Logs: [], + }, + }) +}) diff --git a/packages/core/src/actions/getTransactionConfirmations.test.ts b/packages/core/src/actions/getTransactionConfirmations.test.ts new file mode 100644 index 0000000000..a2f47d6a42 --- /dev/null +++ b/packages/core/src/actions/getTransactionConfirmations.test.ts @@ -0,0 +1,25 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getTransactionConfirmations } from './getTransactionConfirmations.js' +import { getTransactionReceipt } from './getTransactionReceipt.js' + +test('default', async () => { + await expect( + getTransactionConfirmations(config, { + hash: '0xa559259bd2d0e8372421e222ff3545f705b5da60005bd787a23c2e68d6d7fefd', + }), + ).resolves.toBeTypeOf('bigint') +}) + +test('parameters: transactionReceipt', async () => { + const transactionReceipt = await getTransactionReceipt(config, { + hash: '0xa559259bd2d0e8372421e222ff3545f705b5da60005bd787a23c2e68d6d7fefd', + }) + + await expect( + getTransactionConfirmations(config, { + transactionReceipt, + }), + ).resolves.toBeTypeOf('bigint') +}) diff --git a/packages/core/src/actions/getTransactionConfirmations.ts b/packages/core/src/actions/getTransactionConfirmations.ts new file mode 100644 index 0000000000..8baa88cf16 --- /dev/null +++ b/packages/core/src/actions/getTransactionConfirmations.ts @@ -0,0 +1,52 @@ +import type { Chain } from 'viem' +import { + type GetTransactionConfirmationsErrorType as viem_GetTransactionConfirmationsErrorType, + type GetTransactionConfirmationsParameters as viem_GetTransactionConfirmationsParameters, + type GetTransactionConfirmationsReturnType as viem_GetTransactionConfirmationsReturnType, + getTransactionConfirmations as viem_getTransactionConfirmations, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { ChainIdParameter } from '../types/properties.js' +import { getAction } from '../utils/getAction.js' + +export type GetTransactionConfirmationsParameters< + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: viem_GetTransactionConfirmationsParameters< + chains[key] + > & + ChainIdParameter +}[number] + +export type GetTransactionConfirmationsReturnType = + viem_GetTransactionConfirmationsReturnType + +export type GetTransactionConfirmationsErrorType = + viem_GetTransactionConfirmationsErrorType + +/** https://wagmi.sh/core/api/actions/getTransactionConfirmations */ +export function getTransactionConfirmations< + config extends Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], +>( + config: config, + parameters: GetTransactionConfirmationsParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_getTransactionConfirmations, + 'getTransactionConfirmations', + ) + return action(rest as viem_GetTransactionConfirmationsParameters) +} diff --git a/packages/core/src/actions/getTransactionCount.test.ts b/packages/core/src/actions/getTransactionCount.test.ts new file mode 100644 index 0000000000..95f0e6ddee --- /dev/null +++ b/packages/core/src/actions/getTransactionCount.test.ts @@ -0,0 +1,50 @@ +import { accounts, chain, config, testClient } from '@wagmi/test' +import { expect, test } from 'vitest' + +import type { BlockTag } from 'viem' +import { getTransactionCount } from './getTransactionCount.js' + +const address = accounts[0] + +test('default', async () => { + await expect(getTransactionCount(config, { address })).resolves.toBeTypeOf( + 'number', + ) +}) + +test('parameters: chainId', async () => { + await testClient.mainnet2.setNonce({ + address, + nonce: 6969, + }) + await testClient.mainnet2.mine({ blocks: 1 }) + await expect( + getTransactionCount(config, { address, chainId: chain.mainnet2.id }), + ).resolves.toBeTypeOf('number') +}) + +test('parameters: blockNumber', async () => { + await expect( + getTransactionCount(config, { address, blockNumber: 13677382n }), + ).resolves.toBeTypeOf('number') +}) + +test.each([ + { blockTag: 'earliest' }, + { blockTag: 'finalized' }, + { blockTag: 'latest' }, + { blockTag: 'pending' }, + { blockTag: 'safe' }, +] as { blockTag: BlockTag; expected: number }[])( + 'parameters: blockTag $blockTag', + async ({ blockTag }) => { + await testClient.mainnet.restart() + + await expect( + getTransactionCount(config, { + address, + blockTag, + }), + ).resolves.toBeTypeOf('number') + }, +) diff --git a/packages/core/src/actions/getTransactionCount.ts b/packages/core/src/actions/getTransactionCount.ts new file mode 100644 index 0000000000..6872e6ede6 --- /dev/null +++ b/packages/core/src/actions/getTransactionCount.ts @@ -0,0 +1,34 @@ +import { + type GetTransactionCountErrorType as viem_GetTransactionCountErrorType, + type GetTransactionCountParameters as viem_GetTransactionCountParameters, + type GetTransactionCountReturnType as viem_GetTransactionCountReturnType, + getTransactionCount as viem_getTransactionCount, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetTransactionCountParameters = + Compute & viem_GetTransactionCountParameters> + +export type GetTransactionCountReturnType = viem_GetTransactionCountReturnType + +export type GetTransactionCountErrorType = viem_GetTransactionCountErrorType + +/** https://wagmi.sh/core/api/actions/getTransactionCount */ +export async function getTransactionCount( + config: config, + parameters: GetTransactionCountParameters, +): Promise { + const { address, blockNumber, blockTag, chainId } = parameters + + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_getTransactionCount, + 'getTransactionCount', + ) + return action(blockNumber ? { address, blockNumber } : { address, blockTag }) +} diff --git a/packages/core/src/actions/getTransactionReceipt.test-d.ts b/packages/core/src/actions/getTransactionReceipt.test-d.ts new file mode 100644 index 0000000000..e9850eacc7 --- /dev/null +++ b/packages/core/src/actions/getTransactionReceipt.test-d.ts @@ -0,0 +1,36 @@ +import { http } from 'viem' +import { mainnet, zkSync } from 'viem/chains' +import type { ZkSyncL2ToL1Log, ZkSyncLog } from 'viem/zksync' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { getTransactionReceipt } from './getTransactionReceipt.js' + +test('chain formatters', async () => { + const config = createConfig({ + chains: [mainnet, zkSync], + transports: { [mainnet.id]: http(), [zkSync.id]: http() }, + }) + const result = await getTransactionReceipt(config, { hash: '0x123' }) + if (result.chainId === zkSync.id) { + expectTypeOf(result.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result.l1BatchTxIndex).toEqualTypeOf() + expectTypeOf(result.logs).toEqualTypeOf() + expectTypeOf(result.l2ToL1Logs).toEqualTypeOf() + } +}) + +test('chainId', async () => { + const config = createConfig({ + chains: [zkSync], + transports: { [zkSync.id]: http() }, + }) + const result = await getTransactionReceipt(config, { + hash: '0x123', + chainId: zkSync.id, + }) + expectTypeOf(result.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result.l1BatchTxIndex).toEqualTypeOf() + expectTypeOf(result.logs).toEqualTypeOf() + expectTypeOf(result.l2ToL1Logs).toEqualTypeOf() +}) diff --git a/packages/core/src/actions/getTransactionReceipt.test.ts b/packages/core/src/actions/getTransactionReceipt.test.ts new file mode 100644 index 0000000000..82fee0b11f --- /dev/null +++ b/packages/core/src/actions/getTransactionReceipt.test.ts @@ -0,0 +1,35 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getTransaction } from './getTransaction.js' +import { getTransactionReceipt } from './getTransactionReceipt.js' + +test('default', async () => { + const transaction = await getTransaction(config, { + blockNumber: 16280769n, + index: 0, + }) + + await expect( + getTransactionReceipt(config, { + hash: transaction.hash, + }), + ).resolves.toMatchInlineSnapshot(` + { + "blockHash": "0xb932f77cf770d1d1c8f861153eec1e990f5d56b6ffdb4ac06aef3cca51ef37d4", + "blockNumber": 16280769n, + "contractAddress": null, + "cumulativeGasUsed": 21000n, + "effectiveGasPrice": 33427926161n, + "from": "0x043022ef9fca1066024d19d681e2ccf44ff90de3", + "gasUsed": 21000n, + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": "success", + "to": "0x318a5fb4f1604fc46375a1db9a9018b6e423b345", + "transactionHash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + "transactionIndex": 0, + "type": "legacy", + } + `) +}) diff --git a/packages/core/src/actions/getTransactionReceipt.ts b/packages/core/src/actions/getTransactionReceipt.ts new file mode 100644 index 0000000000..8c06e36ba6 --- /dev/null +++ b/packages/core/src/actions/getTransactionReceipt.ts @@ -0,0 +1,57 @@ +import type { Chain } from 'viem' +import { + type GetTransactionReceiptErrorType as viem_GetTransactionReceiptErrorType, + type GetTransactionReceiptParameters as viem_GetTransactionReceiptParameters, + type GetTransactionReceiptReturnType as viem_GetTransactionReceiptReturnType, + getTransactionReceipt as viem_getTransactionReceipt, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute, IsNarrowable } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type GetTransactionReceiptParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + viem_GetTransactionReceiptParameters & ChainIdParameter +> + +export type GetTransactionReceiptReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = Compute< + { + [key in keyof chains]: viem_GetTransactionReceiptReturnType< + IsNarrowable extends true ? chains[key] : undefined + > & { chainId: chains[key]['id'] } + }[number] +> + +export type GetTransactionReceiptErrorType = viem_GetTransactionReceiptErrorType + +/** https://wagmi.sh/core/api/actions/getTransactionReceipt */ +export async function getTransactionReceipt< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: GetTransactionReceiptParameters, +): Promise> { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_getTransactionReceipt, + 'getTransactionReceipt', + ) + return action(rest) as unknown as Promise< + GetTransactionReceiptReturnType + > +} diff --git a/packages/core/src/actions/getWalletClient.test-d.ts b/packages/core/src/actions/getWalletClient.test-d.ts new file mode 100644 index 0000000000..d1d87f5e97 --- /dev/null +++ b/packages/core/src/actions/getWalletClient.test-d.ts @@ -0,0 +1,22 @@ +import { chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import type { Account } from 'viem' +import { getWalletClient } from './getWalletClient.js' + +test('default', async () => { + const client = await getWalletClient(config) + expectTypeOf(client.chain).toEqualTypeOf<(typeof config)['chains'][number]>() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() + expectTypeOf(client.account).toEqualTypeOf() +}) + +test('parameters: chainId', async () => { + const client = await getWalletClient(config, { + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() + expectTypeOf(client.account).toEqualTypeOf() +}) diff --git a/packages/core/src/actions/getWalletClient.test.ts b/packages/core/src/actions/getWalletClient.test.ts new file mode 100644 index 0000000000..2350f81b3e --- /dev/null +++ b/packages/core/src/actions/getWalletClient.test.ts @@ -0,0 +1,24 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getWalletClient } from './getWalletClient.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + await expect(getWalletClient(config)).resolves.toBeDefined() + await disconnect(config, { connector }) +}) + +test('behavior: not connected', async () => { + await expect( + getWalletClient(config), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) +}) diff --git a/packages/core/src/actions/getWalletClient.ts b/packages/core/src/actions/getWalletClient.ts new file mode 100644 index 0000000000..bf49668701 --- /dev/null +++ b/packages/core/src/actions/getWalletClient.ts @@ -0,0 +1,50 @@ +import { type Account, type WalletClient, walletActions } from 'viem' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { Compute } from '../types/utils.js' +import { + type GetConnectorClientErrorType, + type GetConnectorClientParameters, + getConnectorClient, +} from './getConnectorClient.js' + +export type GetWalletClientParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = GetConnectorClientParameters + +export type GetWalletClientReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + WalletClient< + config['_internal']['transports'][chainId], + Extract, + Account + > +> + +export type GetWalletClientErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + +export async function getWalletClient< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: GetWalletClientParameters = {}, +): Promise> { + const client = await getConnectorClient(config, parameters) + // @ts-ignore + return client.extend(walletActions) as unknown as GetWalletClientReturnType< + config, + chainId + > +} diff --git a/packages/core/src/actions/multicall.test-d.ts b/packages/core/src/actions/multicall.test-d.ts new file mode 100644 index 0000000000..bb70db9989 --- /dev/null +++ b/packages/core/src/actions/multicall.test-d.ts @@ -0,0 +1,106 @@ +import { abi, config } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { multicall } from './multicall.js' + +test('default', async () => { + const result = await multicall(config, { + chainId: 1, + contracts: [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ], + }) + expectTypeOf(result).toEqualTypeOf< + [ + ( + | { error: Error; result?: undefined; status: 'failure' } + | { error?: undefined; result: bigint; status: 'success' } + ), + ( + | { error: Error; result?: undefined; status: 'failure' } + | { error?: undefined; result: string; status: 'success' } + ), + ] + >() +}) + +test('allowFailure', async () => { + const result = await multicall(config, { + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ], + }) + expectTypeOf(result).toEqualTypeOf<[bigint, string]>() +}) + +test('MulticallParameters', async () => { + type Result = Parameters< + typeof multicall< + typeof config, + [ + { + address: '0x' + abi: typeof abi.viewOverloads + functionName: 'foo' + }, + ] + > + >[1]['contracts'][0] + expectTypeOf().toEqualTypeOf<'foo' | 'bar'>() + expectTypeOf().toEqualTypeOf< + readonly [] | readonly [Address] | readonly [Address, Address] | undefined + >() +}) + +test('overloads', async () => { + const res = await multicall(config, { + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + }, + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x'], + }, + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x', '0x'], + }, + ], + }) + + expectTypeOf(res).toEqualTypeOf< + [number, string, { foo: Address; bar: Address }] + >() +}) diff --git a/packages/core/src/actions/multicall.test.ts b/packages/core/src/actions/multicall.test.ts new file mode 100644 index 0000000000..cf2d7c0ddf --- /dev/null +++ b/packages/core/src/actions/multicall.test.ts @@ -0,0 +1,46 @@ +import { abi, address, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { multicall } from './multicall.js' + +test('default', async () => { + await expect( + multicall(config, { + contracts: [ + { + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + ], + }), + ).resolves.toMatchInlineSnapshot(` + [ + { + "result": 4n, + "status": "success", + }, + ] + `) +}) + +test('allowFailure', async () => { + await expect( + multicall(config, { + allowFailure: false, + contracts: [ + { + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + ], + }), + ).resolves.toMatchInlineSnapshot(` + [ + 4n, + ] + `) +}) diff --git a/packages/core/src/actions/multicall.ts b/packages/core/src/actions/multicall.ts new file mode 100644 index 0000000000..528bb0118e --- /dev/null +++ b/packages/core/src/actions/multicall.ts @@ -0,0 +1,42 @@ +import type { + ContractFunctionParameters, + MulticallErrorType as viem_MulticallErrorType, + MulticallParameters as viem_MulticallParameters, + MulticallReturnType as viem_MulticallReturnType, +} from 'viem' +import { multicall as viem_multicall } from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import { getAction } from '../utils/getAction.js' + +export type MulticallParameters< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + allowFailure extends boolean = true, + config extends Config = Config, +> = viem_MulticallParameters & ChainIdParameter + +export type MulticallReturnType< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + allowFailure extends boolean = true, +> = viem_MulticallReturnType + +export type MulticallErrorType = viem_MulticallErrorType + +export async function multicall< + config extends Config, + const contracts extends readonly ContractFunctionParameters[], + allowFailure extends boolean = true, +>( + config: config, + parameters: MulticallParameters, +): Promise> { + const { allowFailure = true, chainId, contracts, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_multicall, 'multicall') + return action({ + allowFailure, + contracts, + ...rest, + }) as Promise> +} diff --git a/packages/core/src/actions/prepareTransactionRequest.test-d.ts b/packages/core/src/actions/prepareTransactionRequest.test-d.ts new file mode 100644 index 0000000000..a8a0091157 --- /dev/null +++ b/packages/core/src/actions/prepareTransactionRequest.test-d.ts @@ -0,0 +1,80 @@ +import { accounts, config } from '@wagmi/test' +import { http, parseEther } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type PrepareTransactionRequestParameters, + prepareTransactionRequest, +} from './prepareTransactionRequest.js' + +const targetAccount = accounts[1] + +test('default', async () => { + const response = await prepareTransactionRequest(config, { + chainId: 1, + to: '0x', + value: parseEther('1'), + }) + const { nonce: _nonce, ...request } = response + request.to + request.chainId + + expectTypeOf(response).toMatchTypeOf<{ + chainId: 1 + }>() +}) + +test('chain formatters', async () => { + const config = createConfig({ + chains: [celo, mainnet], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = PrepareTransactionRequestParameters + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + const request = await prepareTransactionRequest(config, { + to: targetAccount, + value: parseEther('0.01'), + feeCurrency: '0x', + }) + if (request.chainId === celo.id) { + expectTypeOf(request.chainId).toEqualTypeOf(celo.id) + expectTypeOf(request.feeCurrency).toEqualTypeOf<`0x${string}` | undefined>() + } + + type Result2 = PrepareTransactionRequestParameters< + typeof config, + typeof celo.id + > + expectTypeOf().toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + const request2 = await prepareTransactionRequest(config, { + chainId: celo.id, + to: targetAccount, + value: parseEther('0.01'), + feeCurrency: '0x', + }) + expectTypeOf(request2.chainId).toEqualTypeOf(celo.id) + expectTypeOf(request2.feeCurrency).toEqualTypeOf<`0x${string}` | undefined>() + + type Result3 = PrepareTransactionRequestParameters< + typeof config, + typeof mainnet.id + > + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + prepareTransactionRequest(config, { + chainId: mainnet.id, + to: targetAccount, + value: parseEther('0.01'), + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/core/src/actions/prepareTransactionRequest.test.ts b/packages/core/src/actions/prepareTransactionRequest.test.ts new file mode 100644 index 0000000000..271037af8f --- /dev/null +++ b/packages/core/src/actions/prepareTransactionRequest.test.ts @@ -0,0 +1,108 @@ +import { accounts, config, privateKey } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { parseEther } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { prepareTransactionRequest } from './prepareTransactionRequest.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const request = await prepareTransactionRequest(config, { + to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + value: parseEther('1'), + }) + + const { + gas: _gas, + maxFeePerGas: _mfpg, + maxPriorityFeePerGas: _mpfpg, + nonce: _nonce, + ...rest + } = request + expect(rest).toMatchInlineSnapshot(` + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "type": "eip1559", + "value": 1000000000000000000n, + } + `) + + await disconnect(config, { connector }) +}) + +test('parameters: account', async () => { + await connect(config, { connector }) + + const request = await prepareTransactionRequest(config, { + account: accounts[0], + to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + value: parseEther('1'), + }) + + const { + gas: _gas, + maxFeePerGas: _mfpg, + maxPriorityFeePerGas: _mpfpg, + nonce: _nonce, + ...rest + } = request + expect(rest).toMatchInlineSnapshot(` + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "type": "eip1559", + "value": 1000000000000000000n, + } + `) + + await disconnect(config, { connector }) +}) + +test('behavior: local account', async () => { + const account = privateKeyToAccount(privateKey) + + const request = await prepareTransactionRequest(config, { + account, + to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + value: parseEther('1'), + }) + + const { + gas: _gas, + maxFeePerGas: _mfpg, + maxPriorityFeePerGas: _mpfpg, + nonce: _nonce, + ...rest + } = request + expect(rest).toMatchInlineSnapshot(` + { + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "nonceManager": undefined, + "publicKey": "0x048318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5", + "sign": [Function], + "signAuthorization": [Function], + "signMessage": [Function], + "signTransaction": [Function], + "signTypedData": [Function], + "source": "privateKey", + "type": "local", + }, + "chainId": 1, + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "type": "eip1559", + "value": 1000000000000000000n, + } + `) +}) diff --git a/packages/core/src/actions/prepareTransactionRequest.ts b/packages/core/src/actions/prepareTransactionRequest.ts new file mode 100644 index 0000000000..36ed81f774 --- /dev/null +++ b/packages/core/src/actions/prepareTransactionRequest.ts @@ -0,0 +1,125 @@ +import type { + Account, + Address, + Chain, + PrepareTransactionRequestErrorType as viem_PrepareTransactionRequestErrorType, + PrepareTransactionRequestParameters as viem_PrepareTransactionRequestParameters, + PrepareTransactionRequestRequest as viem_PrepareTransactionRequestRequest, + PrepareTransactionRequestReturnType as viem_PrepareTransactionRequestReturnType, +} from 'viem' +import { prepareTransactionRequest as viem_prepareTransactionRequest } from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { + Compute, + IsNarrowable, + UnionCompute, + UnionStrictOmit, +} from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { getAccount } from './getAccount.js' + +export type PrepareTransactionRequestParameters< + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + > = viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: UnionCompute< + UnionStrictOmit< + viem_PrepareTransactionRequestParameters< + chains[key], + Account, + chains[key], + Account | Address, + request extends viem_PrepareTransactionRequestRequest< + chains[key], + chains[key] + > + ? request + : never + >, + 'chain' + > & + ChainIdParameter & { + to: Address + } + > +}[number] + +export type PrepareTransactionRequestReturnType< + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + > = viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + viem_PrepareTransactionRequestReturnType< + IsNarrowable extends true ? chains[key] : undefined, + Account, + chains[key], + Account, + request extends viem_PrepareTransactionRequestRequest< + IsNarrowable extends true ? chains[key] : undefined, + chains[key] + > + ? request + : never + > + > & { + chainId: chains[key]['id'] + } +}[number] + +export type PrepareTransactionRequestErrorType = + viem_PrepareTransactionRequestErrorType + +/** https://wagmi.sh/core/api/actions/prepareTransactionRequest */ +export async function prepareTransactionRequest< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, + const request extends viem_PrepareTransactionRequestRequest< + SelectChains['0'], + SelectChains['0'] + >, +>( + config: config, + parameters: PrepareTransactionRequestParameters, +): Promise> { + const { account: account_, chainId, ...rest } = parameters + + const account = account_ ?? getAccount(config).address + const client = config.getClient({ chainId }) + + const action = getAction( + client, + viem_prepareTransactionRequest, + 'prepareTransactionRequest', + ) + return action({ + ...rest, + ...(account ? { account } : {}), + } as unknown as viem_PrepareTransactionRequestParameters) as unknown as Promise< + PrepareTransactionRequestReturnType + > +} diff --git a/packages/core/src/actions/readContract.test-d.ts b/packages/core/src/actions/readContract.test-d.ts new file mode 100644 index 0000000000..a667ec03e8 --- /dev/null +++ b/packages/core/src/actions/readContract.test-d.ts @@ -0,0 +1,74 @@ +import { abi, config } from '@wagmi/test' +import { assertType, expectTypeOf, test } from 'vitest' + +import { readContract } from './readContract.js' + +test('default', async () => { + const result = await readContract(config, { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }) + expectTypeOf(result).toEqualTypeOf() +}) + +test('overloads', async () => { + const result1 = await readContract(config, { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + }) + assertType(result1) + + const result2 = await readContract(config, { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: [], + }) + assertType(result2) + + const result3 = await readContract(config, { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3) + + const result4 = await readContract(config, { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x', '0x'], + }) + assertType<{ + foo: `0x${string}` + bar: `0x${string}` + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + }>(result4) +}) + +test('deployless read (bytecode)', async () => { + const result = await readContract(config, { + code: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }) + expectTypeOf(result).toEqualTypeOf() +}) + +test('deployless read (factory)', async () => { + const result = await readContract(config, { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + factory: '0x', + factoryData: '0x', + }) + expectTypeOf(result).toEqualTypeOf() +}) diff --git a/packages/core/src/actions/readContract.test.ts b/packages/core/src/actions/readContract.test.ts new file mode 100644 index 0000000000..37f0db7e0c --- /dev/null +++ b/packages/core/src/actions/readContract.test.ts @@ -0,0 +1,37 @@ +import { abi, address, bytecode, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { readContract } from './readContract.js' + +test('default', async () => { + await expect( + readContract(config, { + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }), + ).resolves.toMatchInlineSnapshot('4n') +}) + +test('parameters: chainId', async () => { + await expect( + readContract(config, { + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + chainId: chain.mainnet2.id, + }), + ).resolves.toMatchInlineSnapshot('4n') +}) + +test('parameters: deployless read (bytecode)', async () => { + await expect( + readContract(config, { + abi: abi.wagmiMintExample, + functionName: 'name', + code: bytecode.wagmiMintExample, + }), + ).resolves.toMatchInlineSnapshot(`"wagmi"`) +}) diff --git a/packages/core/src/actions/readContract.ts b/packages/core/src/actions/readContract.ts new file mode 100644 index 0000000000..e01e74e9f2 --- /dev/null +++ b/packages/core/src/actions/readContract.ts @@ -0,0 +1,58 @@ +import type { Abi } from 'viem' +import type { ContractFunctionArgs, ContractFunctionName } from 'viem' +import { + type ReadContractErrorType as viem_ReadContractErrorType, + type ReadContractParameters as viem_ReadContractParameters, + type ReadContractReturnType as viem_ReadContractReturnType, + readContract as viem_readContract, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import { getAction } from '../utils/getAction.js' + +export type ReadContractParameters< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'pure' | 'view' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'pure' | 'view', + functionName + > = ContractFunctionArgs, + config extends Config = Config, +> = viem_ReadContractParameters & + ChainIdParameter + +export type ReadContractReturnType< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'pure' | 'view' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'pure' | 'view', + functionName + > = ContractFunctionArgs, +> = viem_ReadContractReturnType + +export type ReadContractErrorType = viem_ReadContractErrorType + +/** https://wagmi.sh/core/api/actions/readContract */ +export function readContract< + config extends Config, + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, +>( + config: config, + parameters: ReadContractParameters, +): Promise> { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_readContract, 'readContract') + return action(rest as any) +} diff --git a/packages/core/src/actions/readContracts.test-d.ts b/packages/core/src/actions/readContracts.test-d.ts new file mode 100644 index 0000000000..a68b2acab6 --- /dev/null +++ b/packages/core/src/actions/readContracts.test-d.ts @@ -0,0 +1,118 @@ +import { abi, config } from '@wagmi/test' +import { assertType, expectTypeOf, test } from 'vitest' + +import { readContracts } from './readContracts.js' + +test('default', async () => { + const result = await readContracts(config, { + contracts: [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + chainId: 1, + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ], + }) + expectTypeOf(result).toEqualTypeOf< + [ + ( + | { error: Error; result?: undefined; status: 'failure' } + | { error?: undefined; result: bigint; status: 'success' } + ), + ( + | { error: Error; result?: undefined; status: 'failure' } + | { error?: undefined; result: string; status: 'success' } + ), + ] + >() +}) + +test('allowFailure', async () => { + const result = await readContracts(config, { + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ], + }) + expectTypeOf(result).toEqualTypeOf<[bigint, string]>() +}) + +test('overloads', async () => { + const result1 = await readContracts(config, { + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + }, + ], + }) + assertType<[number] | undefined>(result1) + + const result2 = await readContracts(config, { + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: [], + }, + ], + }) + assertType<[number] | undefined>(result2) + + const result3 = await readContracts(config, { + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x'], + }, + ], + }) + assertType<[string] | undefined>(result3) + + const result4 = await readContracts(config, { + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x', '0x'], + }, + ], + }) + assertType< + | [ + { + foo: `0x${string}` + bar: `0x${string}` + }, + ] + | undefined + >(result4) +}) diff --git a/packages/core/src/actions/readContracts.test.ts b/packages/core/src/actions/readContracts.test.ts new file mode 100644 index 0000000000..4bc33f60da --- /dev/null +++ b/packages/core/src/actions/readContracts.test.ts @@ -0,0 +1,678 @@ +import { abi, address, chain } from '@wagmi/test' +import { http, type MulticallResponse } from 'viem' +import { expect, expectTypeOf, test, vi } from 'vitest' + +import { createConfig } from '../createConfig.js' +import * as multicall from './multicall.js' +import * as readContract from './readContract.js' +import { readContracts } from './readContracts.js' + +const contracts = [ + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + functionName: 'love', + args: ['0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + functionName: 'love', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + functionName: 'getAlive', + }, + { + abi: abi.mloot, + address: address.mloot, + functionName: 'tokenOfOwnerByIndex', + args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', 0n], + }, +] + +const { mainnet, mainnet2, optimism } = chain + +const config = createConfig({ + chains: [ + { ...mainnet, contracts: { multicall3: undefined } }, + { ...mainnet2, contracts: { multicall3: undefined } }, + ], + transports: { + [mainnet.id]: http(), + [mainnet2.id]: http(), + }, +}) + +test('default', async () => { + const spy = vi.spyOn(multicall, 'multicall') + const config = createConfig({ + chains: [mainnet, mainnet2], + transports: { + [mainnet.id]: http(), + [mainnet2.id]: http(), + }, + }) + const results = await readContracts(config, { contracts }) + + expect(spy).toHaveBeenCalledWith(config, { + allowFailure: true, + contracts, + chainId: mainnet.id, + }) + expect(results).toMatchInlineSnapshot(` + [ + { + "result": 2n, + "status": "success", + }, + { + "result": 1n, + "status": "success", + }, + { + "result": false, + "status": "success", + }, + { + "result": 370395n, + "status": "success", + }, + ] + `) +}) + +test.skip('falls back to readContract if multicall is not available', async () => { + const spy = vi.spyOn(readContract, 'readContract') + const config = createConfig({ + chains: [mainnet, { ...mainnet2, contracts: { multicall3: undefined } }], + transports: { + [mainnet.id]: http(), + [mainnet2.id]: http(), + }, + }) + const chainId = mainnet2.id + const contracts = [ + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet2.id, + functionName: 'love', + args: ['0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet2.id, + functionName: 'love', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet2.id, + functionName: 'getAlive', + }, + { + abi: abi.mloot, + address: address.mloot, + chainId: mainnet2.id, + functionName: 'tokenOfOwnerByIndex', + args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', 0n], + }, + ] as const + const results = await readContracts(config, { contracts }) + expectTypeOf(results).toEqualTypeOf< + [ + MulticallResponse, + MulticallResponse, + MulticallResponse, + MulticallResponse, + ] + >() + + for (const contract of contracts) { + expect(spy).toBeCalledWith(config, { ...contract, chainId }) + } + expect(results).toMatchInlineSnapshot(` + [ + { + "result": 2n, + "status": "success", + }, + { + "result": 1n, + "status": "success", + }, + { + "result": false, + "status": "success", + }, + { + "result": 370395n, + "status": "success", + }, + ] + `) +}) + +test.skip('multichain', async () => { + const config = createConfig({ + chains: [mainnet, mainnet2, optimism], + transports: { + [mainnet.id]: http(), + [mainnet2.id]: http(), + [optimism.id]: http(), + }, + }) + + const spy = vi.spyOn(multicall, 'multicall') + const mainnetContracts = [ + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet.id, + functionName: 'love', + args: ['0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet.id, + functionName: 'love', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet.id, + functionName: 'love', + args: ['0xd2135CfB216b74109775236E36d4b433F1DF507B'], + }, + ] as const + const mainnet2Contracts = [ + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet2.id, + functionName: 'getAlive', + }, + { + abi: abi.mloot, + address: address.mloot, + chainId: mainnet2.id, + functionName: 'tokenOfOwnerByIndex', + args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', 0n], + }, + ] as const + const optimismContracts = [ + { + abi: abi.erc20, + address: address.optimism.usdc, + chainId: optimism.id, + functionName: 'symbol', + }, + { + abi: abi.erc20, + address: address.optimism.usdc, + chainId: optimism.id, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + ] as const + const results = await readContracts(config, { + contracts: [ + mainnetContracts[0]!, + optimismContracts[0]!, + mainnetContracts[1]!, + mainnet2Contracts[0]!, + optimismContracts[1]!, + mainnet2Contracts[1]!, + mainnetContracts[2]!, + ], + }) + expectTypeOf(results).toEqualTypeOf< + [ + MulticallResponse, + MulticallResponse, + MulticallResponse, + MulticallResponse, + MulticallResponse, + MulticallResponse, + MulticallResponse, + ] + >() + + expect(spy).toHaveBeenCalledWith(config, { + allowFailure: true, + contracts: mainnetContracts, + chainId: mainnet.id, + overrides: undefined, + }) + expect(spy).toHaveBeenCalledWith(config, { + allowFailure: true, + contracts: mainnet2Contracts, + chainId: mainnet2.id, + overrides: undefined, + }) + expect(results).toMatchInlineSnapshot(` + [ + { + "result": 2n, + "status": "success", + }, + { + "result": "USDC", + "status": "success", + }, + { + "result": 1n, + "status": "success", + }, + { + "result": false, + "status": "success", + }, + { + "result": 10959340n, + "status": "success", + }, + { + "result": 370395n, + "status": "success", + }, + { + "result": 0n, + "status": "success", + }, + ] + `) +}) + +test('multi-chain: falls back to readContract if multicall is not available', async () => { + const config = createConfig({ + chains: [mainnet, { ...optimism, contracts: { multicall3: undefined } }], + transports: { + [mainnet.id]: http(), + [optimism.id]: http(), + }, + }) + + const spy = vi.spyOn(readContract, 'readContract') + const mainnetContracts = [ + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet.id, + functionName: 'love', + args: ['0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet.id, + functionName: 'love', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + ] as const + const optimismContracts = [ + { + abi: abi.erc20, + address: address.optimism.usdc, + chainId: optimism.id, + functionName: 'symbol', + }, + { + abi: abi.erc20, + address: address.optimism.usdc, + chainId: optimism.id, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + ] as const + const results = await readContracts(config, { + contracts: [...mainnetContracts, ...optimismContracts], + }) + expectTypeOf(results).toEqualTypeOf< + [ + MulticallResponse, + MulticallResponse, + MulticallResponse, + MulticallResponse, + ] + >() + + for (const contract of mainnetContracts) { + expect(spy).toBeCalledWith(config, { ...contract, chainId: mainnet.id }) + } + for (const contract of optimismContracts) { + expect(spy).toBeCalledWith(config, { ...contract, chainId: optimism.id }) + } + expect(results).toMatchInlineSnapshot(` + [ + { + "result": 2n, + "status": "success", + }, + { + "result": 1n, + "status": "success", + }, + { + "result": "USDC", + "status": "success", + }, + { + "result": 10959340n, + "status": "success", + }, + ] + `) +}) + +test('throws if allowFailure=false & a contract method fails', async () => { + await expect( + readContracts(config, { + allowFailure: false, + contracts: [ + ...contracts, + { + abi: abi.mloot, + address: address.mloot, + functionName: 'tokenOfOwnerByIndex', + args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', 69420n], + }, + ], + }), + ).rejects.toThrowErrorMatchingInlineSnapshot( + ` + [ContractFunctionExecutionError: The contract function "tokenOfOwnerByIndex" reverted with the following reason: + ERC721Enumerable: owner index out of bounds + + Contract Call: + address: 0x1dfe7ca09e99d10835bf73044a23b73fc20623df + function: tokenOfOwnerByIndex(address owner, uint256 index) + args: (0xA0Cf798816D4b9b9866b5330EEa46a18382f251e, 69420) + + Docs: https://viem.sh/docs/contract/readContract + Version: viem@2.29.2] + `, + ) +}) + +test('allowFailure=true & a contract method fails', async () => { + expect( + await readContracts(config, { + allowFailure: true, + contracts: [ + ...contracts, + { + abi: abi.mloot, + address: address.mloot, + functionName: 'tokenOfOwnerByIndex', + args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', 69420n], + }, + { + abi: abi.mloot, + address: address.mloot, + functionName: 'tokenOfOwnerByIndex', + args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', 69421n], + }, + ], + }), + ).toMatchInlineSnapshot(` + [ + { + "result": 2n, + "status": "success", + }, + { + "result": 1n, + "status": "success", + }, + { + "result": false, + "status": "success", + }, + { + "result": 370395n, + "status": "success", + }, + { + "error": [ContractFunctionExecutionError: The contract function "tokenOfOwnerByIndex" reverted with the following reason: + ERC721Enumerable: owner index out of bounds + + Contract Call: + address: 0x1dfe7ca09e99d10835bf73044a23b73fc20623df + function: tokenOfOwnerByIndex(address owner, uint256 index) + args: (0xA0Cf798816D4b9b9866b5330EEa46a18382f251e, 69420) + + Docs: https://viem.sh/docs/contract/readContract + Version: viem@2.29.2], + "result": undefined, + "status": "failure", + }, + { + "error": [ContractFunctionExecutionError: The contract function "tokenOfOwnerByIndex" reverted with the following reason: + ERC721Enumerable: owner index out of bounds + + Contract Call: + address: 0x1dfe7ca09e99d10835bf73044a23b73fc20623df + function: tokenOfOwnerByIndex(address owner, uint256 index) + args: (0xA0Cf798816D4b9b9866b5330EEa46a18382f251e, 69421) + + Docs: https://viem.sh/docs/contract/readContract + Version: viem@2.29.2], + "result": undefined, + "status": "failure", + }, + ] + `) +}) + +test('throws if allowFailure=false & encoding contract function data fails', async () => { + await expect( + readContracts(config, { + allowFailure: false, + contracts: [ + ...contracts, + { + abi: abi.mloot, + functionName: 'ownerOf', + // address is not the mloot contract + address: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', + args: [10e30], + }, + ], + }), + ).rejects.toThrowErrorMatchingInlineSnapshot( + ` + [ContractFunctionExecutionError: The contract function "ownerOf" returned no data ("0x"). + + This could be due to any of the following: + - The contract does not have the function "ownerOf", + - The parameters passed to the contract function may be invalid, or + - The address is not a contract. + + Contract Call: + address: 0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC + function: ownerOf(uint256 tokenId) + args: (1e+31) + + Docs: https://viem.sh/docs/contract/readContract + Version: viem@2.29.2] + `, + ) +}) + +test('allowFailure=true & encoding contract function data fails', async () => { + expect( + await readContracts(config, { + allowFailure: true, + contracts: [ + ...contracts, + { + abi: abi.mloot, + functionName: 'ownerOf', + // address is not the mloot contract + address: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', + args: [10e30], + }, + { + abi: abi.mloot, + functionName: 'ownerOf', + // address is not the mloot contract + address: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', + args: [10e30], + }, + ], + }), + ).toMatchInlineSnapshot(` + [ + { + "result": 2n, + "status": "success", + }, + { + "result": 1n, + "status": "success", + }, + { + "result": false, + "status": "success", + }, + { + "result": 370395n, + "status": "success", + }, + { + "error": [ContractFunctionExecutionError: The contract function "ownerOf" returned no data ("0x"). + + This could be due to any of the following: + - The contract does not have the function "ownerOf", + - The parameters passed to the contract function may be invalid, or + - The address is not a contract. + + Contract Call: + address: 0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC + function: ownerOf(uint256 tokenId) + args: (1e+31) + + Docs: https://viem.sh/docs/contract/readContract + Version: viem@2.29.2], + "result": undefined, + "status": "failure", + }, + { + "error": [ContractFunctionExecutionError: The contract function "ownerOf" returned no data ("0x"). + + This could be due to any of the following: + - The contract does not have the function "ownerOf", + - The parameters passed to the contract function may be invalid, or + - The address is not a contract. + + Contract Call: + address: 0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC + function: ownerOf(uint256 tokenId) + args: (1e+31) + + Docs: https://viem.sh/docs/contract/readContract + Version: viem@2.29.2], + "result": undefined, + "status": "failure", + }, + ] + `) +}) + +test('should throw if allowFailure=false & a contract has no response', async () => { + await expect( + readContracts(config, { + allowFailure: false, + contracts: [ + ...contracts, + { + abi: abi.wagmigotchi, + functionName: 'love', + // address is not the wagmigotchi contract + address: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + ], + }), + ).rejects.toThrowErrorMatchingInlineSnapshot( + ` + [ContractFunctionExecutionError: The contract function "love" returned no data ("0x"). + + This could be due to any of the following: + - The contract does not have the function "love", + - The parameters passed to the contract function may be invalid, or + - The address is not a contract. + + Contract Call: + address: 0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC + function: love(address) + args: (0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC) + + Docs: https://viem.sh/docs/contract/readContract + Version: viem@2.29.2] + `, + ) +}) + +test('allowFailure=true & a contract has no response', async () => { + expect( + await readContracts(config, { + allowFailure: true, + contracts: [ + ...contracts, + { + abi: abi.wagmigotchi, + functionName: 'love', + // address is not the wagmigotchi contract + address: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + ], + }), + ).toMatchInlineSnapshot(` + [ + { + "result": 2n, + "status": "success", + }, + { + "result": 1n, + "status": "success", + }, + { + "result": false, + "status": "success", + }, + { + "result": 370395n, + "status": "success", + }, + { + "error": [ContractFunctionExecutionError: The contract function "love" returned no data ("0x"). + + This could be due to any of the following: + - The contract does not have the function "love", + - The parameters passed to the contract function may be invalid, or + - The address is not a contract. + + Contract Call: + address: 0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC + function: love(address) + args: (0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC) + + Docs: https://viem.sh/docs/contract/readContract + Version: viem@2.29.2], + "result": undefined, + "status": "failure", + }, + ] + `) +}) diff --git a/packages/core/src/actions/readContracts.ts b/packages/core/src/actions/readContracts.ts new file mode 100644 index 0000000000..45f2a0cef4 --- /dev/null +++ b/packages/core/src/actions/readContracts.ts @@ -0,0 +1,96 @@ +import type { + ContractFunctionParameters, + MulticallParameters as viem_MulticallParameters, + MulticallReturnType as viem_MulticallReturnType, +} from 'viem' +import { ContractFunctionExecutionError } from 'viem' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import { type MulticallErrorType, multicall } from './multicall.js' +import { type ReadContractErrorType, readContract } from './readContract.js' + +export type ReadContractsParameters< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + allowFailure extends boolean = true, + config extends Config = Config, +> = viem_MulticallParameters< + contracts, + allowFailure, + { properties: ChainIdParameter } +> + +export type ReadContractsReturnType< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + allowFailure extends boolean = true, +> = viem_MulticallReturnType + +export type ReadContractsErrorType = MulticallErrorType | ReadContractErrorType + +export async function readContracts< + config extends Config, + const contracts extends readonly ContractFunctionParameters[], + allowFailure extends boolean = true, +>( + config: config, + parameters: ReadContractsParameters, +): Promise> { + const { allowFailure = true, blockNumber, blockTag, ...rest } = parameters + const contracts = parameters.contracts as (ContractFunctionParameters & { + chainId?: number | undefined + })[] + + try { + const contractsByChainId: { + [chainId: number]: { + contract: ContractFunctionParameters + index: number + }[] + } = {} + for (const [index, contract] of contracts.entries()) { + const chainId = contract.chainId ?? config.state.chainId + if (!contractsByChainId[chainId]) contractsByChainId[chainId] = [] + contractsByChainId[chainId]?.push({ contract, index }) + } + const promises = () => + Object.entries(contractsByChainId).map(([chainId, contracts]) => + multicall(config, { + ...rest, + allowFailure, + blockNumber, + blockTag, + chainId: Number.parseInt(chainId), + contracts: contracts.map(({ contract }) => contract), + }), + ) + + const multicallResults = (await Promise.all(promises())).flat() + // Reorder the contract results back to the order they were + // provided in. + const resultIndexes = Object.values(contractsByChainId).flatMap( + (contracts) => contracts.map(({ index }) => index), + ) + return multicallResults.reduce((results, result, index) => { + if (results) (results as unknown[])[resultIndexes[index]!] = result + return results + }, [] as unknown[]) as ReadContractsReturnType + } catch (error) { + if (error instanceof ContractFunctionExecutionError) throw error + + const promises = () => + contracts.map((contract) => + readContract(config, { ...contract, blockNumber, blockTag }), + ) + if (allowFailure) + return (await Promise.allSettled(promises())).map((result) => { + if (result.status === 'fulfilled') + return { result: result.value, status: 'success' } + return { error: result.reason, result: undefined, status: 'failure' } + }) as ReadContractsReturnType + + return (await Promise.all(promises())) as ReadContractsReturnType< + contracts, + allowFailure + > + } +} diff --git a/packages/core/src/actions/reconnect.test.ts b/packages/core/src/actions/reconnect.test.ts new file mode 100644 index 0000000000..910d16e304 --- /dev/null +++ b/packages/core/src/actions/reconnect.test.ts @@ -0,0 +1,119 @@ +import { accounts, config, mainnet } from '@wagmi/test' +import { http } from 'viem' +import { afterEach, expect, test, vi } from 'vitest' + +import { mock } from '../connectors/mock.js' +import { createConfig } from '../createConfig.js' +import { createStorage } from '../createStorage.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { reconnect } from './reconnect.js' + +const connector = config._internal.connectors.setup( + mock({ + accounts, + features: { reconnect: true }, + }), +) + +afterEach(async () => { + if (config.state.current === connector.uid) + await disconnect(config, { connector }) + else if (config.state.current) await disconnect(config) +}) + +test('default', async () => { + await expect( + reconnect(config, { connectors: [connector] }), + ).resolves.toStrictEqual([]) + expect(config.state.status).toEqual('disconnected') +}) + +test('parameters: connectors (Connector)', async () => { + await connect(config, { connector }) + await expect( + reconnect(config, { connectors: [connector] }), + ).resolves.toMatchObject( + expect.arrayContaining([ + expect.objectContaining({ + accounts: expect.any(Array), + chainId: expect.any(Number), + }), + ]), + ) + expect(config.state.status).toEqual('connected') +}) + +test('parameters: connectors (CreateConnectorFn)', async () => { + const connector = mock({ + accounts, + features: { reconnect: true }, + }) + await connect(config, { connector }) + await expect( + reconnect(config, { connectors: [connector] }), + ).resolves.toMatchObject( + expect.arrayContaining([ + expect.objectContaining({ + accounts: expect.any(Array), + chainId: expect.any(Number), + }), + ]), + ) + expect(config.state.status).toEqual('connected') +}) + +test("behavior: doesn't reconnect if already reconnecting", async () => { + const previousStatus = config.state.status + config.setState((x) => ({ ...x, status: 'reconnecting' })) + await expect( + reconnect(config, { connectors: [connector] }), + ).resolves.toStrictEqual([]) + config.setState((x) => ({ ...x, status: previousStatus })) +}) + +test('behavior: recovers from invalid state', async () => { + const state = { + 'wagmi.store': JSON.stringify({ + state: { + status: 'connected', // <-- invalid - `status` should not be kept in storage + chainId: 1, + current: '983b8aca245', + }, + version: Number.NaN, // mocked version is `'x.y.z'`, which will get interpreted as `NaN` + }), + } as Record + Object.defineProperty(window, 'localStorage', { + value: { + getItem: vi.fn((key) => state[key] ?? null), + removeItem: vi.fn((key) => state.delete?.[key]), + setItem: vi.fn((key, value) => { + state[key] = value + }), + }, + writable: true, + }) + + const storage = createStorage<{ store: object }>({ + storage: window.localStorage, + }) + + const config = createConfig({ + chains: [mainnet], + storage, + transports: { + [mainnet.id]: http(), + }, + }) + + await reconnect(config, { connectors: [connector] }) + + expect(config.state).toMatchInlineSnapshot(` + { + "chainId": 1, + "connections": Map {}, + "current": null, + "status": "disconnected", + } + `) +}) diff --git a/packages/core/src/actions/reconnect.ts b/packages/core/src/actions/reconnect.ts new file mode 100644 index 0000000000..2234c934d4 --- /dev/null +++ b/packages/core/src/actions/reconnect.ts @@ -0,0 +1,127 @@ +import type { Address } from 'viem' + +import type { CreateConnectorFn } from '../connectors/createConnector.js' +import type { Config, Connection, Connector } from '../createConfig.js' +import type { ErrorType } from '../errors/base.js' +import type { Compute } from '../types/utils.js' + +export type ReconnectParameters = { + /** Connectors to attempt reconnect with */ + connectors?: readonly (CreateConnectorFn | Connector)[] | undefined +} + +export type ReconnectReturnType = Compute[] + +export type ReconnectErrorType = ErrorType + +let isReconnecting = false + +/** https://wagmi.sh/core/api/actions/reconnect */ +export async function reconnect( + config: Config, + parameters: ReconnectParameters = {}, +): Promise { + // If already reconnecting, do nothing + if (isReconnecting) return [] + isReconnecting = true + + config.setState((x) => ({ + ...x, + status: x.current ? 'reconnecting' : 'connecting', + })) + + const connectors: Connector[] = [] + if (parameters.connectors?.length) { + for (const connector_ of parameters.connectors) { + let connector: Connector + // "Register" connector if not already created + if (typeof connector_ === 'function') + connector = config._internal.connectors.setup(connector_) + else connector = connector_ + connectors.push(connector) + } + } else connectors.push(...config.connectors) + + // Try recently-used connectors first + let recentConnectorId: string | null | undefined + try { + recentConnectorId = await config.storage?.getItem('recentConnectorId') + } catch {} + const scores: Record = {} + for (const [, connection] of config.state.connections) { + scores[connection.connector.id] = 1 + } + if (recentConnectorId) scores[recentConnectorId] = 0 + const sorted = + Object.keys(scores).length > 0 + ? // .toSorted() + [...connectors].sort( + (a, b) => (scores[a.id] ?? 10) - (scores[b.id] ?? 10), + ) + : connectors + + // Iterate through each connector and try to connect + let connected = false + const connections: Connection[] = [] + const providers: unknown[] = [] + for (const connector of sorted) { + const provider = await connector.getProvider().catch(() => undefined) + if (!provider) continue + + // If we already have an instance of this connector's provider, + // then we have already checked it (ie. injected connectors can + // share the same `window.ethereum` instance, so we don't want to + // connect to it again). + if (providers.some((x) => x === provider)) continue + + const isAuthorized = await connector.isAuthorized() + if (!isAuthorized) continue + + const data = await connector + .connect({ isReconnecting: true }) + .catch(() => null) + if (!data) continue + + connector.emitter.off('connect', config._internal.events.connect) + connector.emitter.on('change', config._internal.events.change) + connector.emitter.on('disconnect', config._internal.events.disconnect) + + config.setState((x) => { + const connections = new Map(connected ? x.connections : new Map()).set( + connector.uid, + { accounts: data.accounts, chainId: data.chainId, connector }, + ) + return { + ...x, + current: connected ? x.current : connector.uid, + connections, + } + }) + connections.push({ + accounts: data.accounts as readonly [Address, ...Address[]], + chainId: data.chainId, + connector, + }) + providers.push(provider) + connected = true + } + + // Prevent overwriting connected status from race condition + if ( + config.state.status === 'reconnecting' || + config.state.status === 'connecting' + ) { + // If connecting didn't succeed, set to disconnected + if (!connected) + config.setState((x) => ({ + ...x, + connections: new Map(), + current: null, + status: 'disconnected', + })) + else config.setState((x) => ({ ...x, status: 'connected' })) + } + + isReconnecting = false + return connections +} diff --git a/packages/core/src/actions/sendCalls.test.ts b/packages/core/src/actions/sendCalls.test.ts new file mode 100644 index 0000000000..cb25e2b0ba --- /dev/null +++ b/packages/core/src/actions/sendCalls.test.ts @@ -0,0 +1,121 @@ +import { accounts, config } from '@wagmi/test' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { sendCalls } from './sendCalls.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + await expect( + sendCalls(config, { + calls: [ + { + data: '0xdeadbeef', + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }), + ).resolves.toMatchInlineSnapshot( + ` + { + "id": "0x5dedb5a4ff8968db37459b52b83cbdc92b01c9c709c9cff26e345ef5cf27f92e", + } + `, + ) + await disconnect(config, { connector }) +}) + +test('behavior: not connected', async () => { + await expect( + sendCalls(config, { + calls: [ + { + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) +}) + +test('behavior: nullish account (account filled by wallet)', async () => { + await expect( + sendCalls(config, { + account: null, + connector, + calls: [ + { + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }), + ).resolves.toMatchInlineSnapshot( + ` + { + "id": "0x035b56a56a5b2fea10e194bae4c846b415de48a8288c7eb704ba7880edcc29a0", + } + `, + ) +}) + +test('behavior: account does not exist on connector', async () => { + await connect(config, { connector }) + await expect( + sendCalls(config, { + account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + calls: [ + { + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorAccountNotFoundError: Account "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e" not found for connector "Mock Connector". + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/sendCalls.ts b/packages/core/src/actions/sendCalls.ts new file mode 100644 index 0000000000..6139455d60 --- /dev/null +++ b/packages/core/src/actions/sendCalls.ts @@ -0,0 +1,74 @@ +import type { Account, Chain } from 'viem' +import { + type SendCallsErrorType as viem_SendCallsErrorType, + type SendCallsParameters as viem_SendCallsParameters, + type SendCallsReturnType as viem_SendCallsReturnType, + sendCalls as viem_sendCalls, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type SendCallsParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + calls extends readonly unknown[] = readonly unknown[], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + viem_SendCallsParameters, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type SendCallsReturnType = viem_SendCallsReturnType + +export type SendCallsErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_SendCallsErrorType + +/** https://wagmi.sh/core/api/actions/sendCalls */ +export async function sendCalls< + const calls extends readonly unknown[], + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: SendCallsParameters, +): Promise { + const { account, chainId, connector, calls, ...rest } = parameters + + const client = await getConnectorClient(config, { + account, + chainId, + connector, + }) + + return viem_sendCalls(client, { + ...(rest as any), + ...(typeof account !== 'undefined' ? { account } : {}), + calls, + chain: chainId ? { id: chainId } : undefined, + }) +} diff --git a/packages/core/src/actions/sendTransaction.test-d.ts b/packages/core/src/actions/sendTransaction.test-d.ts new file mode 100644 index 0000000000..54ce62a947 --- /dev/null +++ b/packages/core/src/actions/sendTransaction.test-d.ts @@ -0,0 +1,50 @@ +import { http, parseEther } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type SendTransactionParameters, + sendTransaction, +} from './sendTransaction.js' + +test('chain formatters', () => { + const config = createConfig({ + chains: [mainnet, celo], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = SendTransactionParameters + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + sendTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + feeCurrency: '0x', + }) + + type Result2 = SendTransactionParameters + expectTypeOf().toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + sendTransaction(config, { + chainId: celo.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + feeCurrency: '0x', + }) + + type Result3 = SendTransactionParameters + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + sendTransaction(config, { + chainId: mainnet.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/core/src/actions/sendTransaction.test.ts b/packages/core/src/actions/sendTransaction.test.ts new file mode 100644 index 0000000000..e263d96670 --- /dev/null +++ b/packages/core/src/actions/sendTransaction.test.ts @@ -0,0 +1,105 @@ +import { config, privateKey, transactionHashRegex } from '@wagmi/test' +import { parseEther } from 'viem' +import { beforeEach, expect, test } from 'vitest' + +import { privateKeyToAccount } from 'viem/accounts' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { sendTransaction } from './sendTransaction.js' + +const connector = config.connectors[0]! + +beforeEach(async () => { + if (config.state.current === connector.uid) + await disconnect(config, { connector }) +}) + +test('default', async () => { + const result = await connect(config, { connector }) + config.state.connections.set(connector.uid, { + ...result, + // Switch up the current account because the default one is running out of funds somewhere + accounts: result.accounts.slice(1) as unknown as typeof result.accounts, + connector, + }) + await expect( + sendTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.0001'), + }), + ).resolves.toMatch(transactionHashRegex) + await disconnect(config, { connector }) +}) + +test('behavior: connector not connected', async () => { + await connect(config, { connector }) + await expect( + sendTransaction(config, { + connector: config.connectors[1], + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.0001'), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) + +test('behavior: account does not exist on connector', async () => { + await connect(config, { connector }) + await expect( + sendTransaction(config, { + account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.0001'), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorAccountNotFoundError: Account "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e" not found for connector "Mock Connector". + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) + +test('behavior: value exceeds balance', async () => { + await connect(config, { connector }) + await expect( + sendTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('99999'), + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [TransactionExecutionError: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account. + + This error could arise when the account does not have enough funds to: + - pay for the total gas fee, + - pay for the value to send. + + The cost of the transaction is calculated as \`gas * gas fee + value\`, where: + - \`gas\` is the amount of gas needed for transaction to execute, + - \`gas fee\` is the gas fee, + - \`value\` is the amount of ether to send to the recipient. + + Request Arguments: + from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 + to: 0xd2135CfB216b74109775236E36d4b433F1DF507B + value: 99999 ETH + + Details: Insufficient funds for gas * price + value + Version: viem@2.29.2] + `) + await disconnect(config, { connector }) +}) + +test('behavior: local account', async () => { + const account = privateKeyToAccount(privateKey) + await expect( + sendTransaction(config, { + account, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.000001'), + }), + ).resolves.toMatch(transactionHashRegex) +}) diff --git a/packages/core/src/actions/sendTransaction.ts b/packages/core/src/actions/sendTransaction.ts new file mode 100644 index 0000000000..76bc3a420a --- /dev/null +++ b/packages/core/src/actions/sendTransaction.ts @@ -0,0 +1,86 @@ +import type { + Account, + Chain, + Client, + TransactionRequest, + SendTransactionErrorType as viem_SendTransactionErrorType, + SendTransactionParameters as viem_SendTransactionParameters, + SendTransactionReturnType as viem_SendTransactionReturnType, +} from 'viem' +import { sendTransaction as viem_sendTransaction } from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type SendTransactionParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + viem_SendTransactionParameters, + 'chain' | 'gas' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] & { + /** Gas provided for transaction execution. */ + gas?: TransactionRequest['gas'] | null +} + +export type SendTransactionReturnType = viem_SendTransactionReturnType + +export type SendTransactionErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_SendTransactionErrorType + +/** https://wagmi.sh/core/api/actions/sendTransaction */ +export async function sendTransaction< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: SendTransactionParameters, +): Promise { + const { account, chainId, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, viem_sendTransaction, 'sendTransaction') + const hash = await action({ + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + gas: rest.gas ?? undefined, + }) + + return hash +} diff --git a/packages/core/src/actions/showCallsStatus.test.ts b/packages/core/src/actions/showCallsStatus.test.ts new file mode 100644 index 0000000000..5822242382 --- /dev/null +++ b/packages/core/src/actions/showCallsStatus.test.ts @@ -0,0 +1,36 @@ +import { accounts, config, testClient } from '@wagmi/test' +import { parseEther } from 'viem' +import { test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { sendCalls } from './sendCalls.js' +import { showCallsStatus } from './showCallsStatus.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + const { id } = await sendCalls(config, { + calls: [ + { + data: '0xdeadbeef', + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }) + await testClient.mainnet.mine({ blocks: 1 }) + await showCallsStatus(config, { + id, + }) + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/showCallsStatus.ts b/packages/core/src/actions/showCallsStatus.ts new file mode 100644 index 0000000000..e3c6ae067d --- /dev/null +++ b/packages/core/src/actions/showCallsStatus.ts @@ -0,0 +1,27 @@ +import { + type ShowCallsStatusErrorType as viem_ShowCallsStatusErrorType, + type ShowCallsStatusParameters as viem_ShowCallsStatusParameters, + type ShowCallsStatusReturnType as viem_ShowCallsStatusReturnType, + showCallsStatus as viem_showCallsStatus, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ConnectorParameter } from '../types/properties.js' +import { getConnectorClient } from './getConnectorClient.js' + +export type ShowCallsStatusParameters = viem_ShowCallsStatusParameters & + ConnectorParameter + +export type ShowCallsStatusReturnType = viem_ShowCallsStatusReturnType + +export type ShowCallsStatusErrorType = viem_ShowCallsStatusErrorType + +/** https://wagmi.sh/core/api/actions/showCallsStatus */ +export async function showCallsStatus( + config: config, + parameters: ShowCallsStatusParameters, +): Promise { + const { connector, id } = parameters + const client = await getConnectorClient(config, { connector }) + return viem_showCallsStatus(client, { id }) +} diff --git a/packages/core/src/actions/signMessage.test.ts b/packages/core/src/actions/signMessage.test.ts new file mode 100644 index 0000000000..5b87c7d652 --- /dev/null +++ b/packages/core/src/actions/signMessage.test.ts @@ -0,0 +1,67 @@ +import { accounts, config, privateKey } from '@wagmi/test' +import { recoverMessageAddress } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { expect, test } from 'vitest' + +import { mock } from '../connectors/mock.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getAccount } from './getAccount.js' +import { signMessage } from './signMessage.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + const signature = await signMessage(config, { message: 'foo bar baz' }) + await expect( + recoverMessageAddress({ + message: 'foo bar baz', + signature, + }), + ).resolves.toEqual(getAccount(config).address) + await disconnect(config, { connector }) +}) + +test('behavior: local account', async () => { + const account = privateKeyToAccount(privateKey) + const signature = await signMessage(config, { + account, + message: 'foo bar baz', + }) + await expect( + recoverMessageAddress({ + message: 'foo bar baz', + signature, + }), + ).resolves.toEqual(account.address) +}) + +test('behavior: user rejected request', async () => { + const connector_ = config._internal.connectors.setup( + mock({ + accounts, + features: { signMessageError: true }, + }), + ) + await connect(config, { connector: connector_ }) + await expect( + signMessage(config, { message: 'foo bar baz' }), + ).rejects.toMatchInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to sign message. + Version: viem@2.29.2] + `) + await disconnect(config, { connector: connector_ }) +}) + +test('behavior: not connected', async () => { + await expect( + signMessage(config, { message: 'foo bar baz' }), + ).rejects.toMatchInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) +}) diff --git a/packages/core/src/actions/signMessage.ts b/packages/core/src/actions/signMessage.ts new file mode 100644 index 0000000000..67f0c293a6 --- /dev/null +++ b/packages/core/src/actions/signMessage.ts @@ -0,0 +1,51 @@ +import type { Account, Client } from 'viem' +import { + type SignMessageErrorType as viem_SignMessageErrorType, + type SignMessageParameters as viem_SignMessageParameters, + type SignMessageReturnType as viem_SignMessageReturnType, + signMessage as viem_signMessage, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { ConnectorParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type SignMessageParameters = Compute< + viem_SignMessageParameters & ConnectorParameter +> + +export type SignMessageReturnType = viem_SignMessageReturnType + +export type SignMessageErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_SignMessageErrorType + +/** https://wagmi.sh/core/api/actions/signMessage */ +export async function signMessage( + config: Config, + parameters: SignMessageParameters, +): Promise { + const { account, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account.type === 'local') + client = config.getClient() + else client = await getConnectorClient(config, { account, connector }) + + const action = getAction(client, viem_signMessage, 'signMessage') + return action({ + ...rest, + ...(account ? { account } : {}), + } as viem_SignMessageParameters) +} diff --git a/packages/core/src/actions/signTypedData.test-d.ts b/packages/core/src/actions/signTypedData.test-d.ts new file mode 100644 index 0000000000..a502d26e91 --- /dev/null +++ b/packages/core/src/actions/signTypedData.test-d.ts @@ -0,0 +1,31 @@ +import { config, typedData } from '@wagmi/test' +import { test } from 'vitest' + +import { signTypedData } from './signTypedData.js' + +test('default', async () => { + signTypedData(config, { + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + }) +}) + +test('domain', async () => { + signTypedData(config, { + primaryType: 'EIP712Domain', + domain: {}, + }) +}) + +test('custom domain', async () => { + signTypedData(config, { + types: { + EIP712Domain: [{ type: 'uint256', name: 'chainId' }], + }, + primaryType: 'EIP712Domain', + domain: { + chainId: 123n, + }, + }) +}) diff --git a/packages/core/src/actions/signTypedData.test.ts b/packages/core/src/actions/signTypedData.test.ts new file mode 100644 index 0000000000..b72ecab8cb --- /dev/null +++ b/packages/core/src/actions/signTypedData.test.ts @@ -0,0 +1,85 @@ +import { accounts, config, privateKey, typedData } from '@wagmi/test' +import { recoverTypedDataAddress } from 'viem' +import { expect, test } from 'vitest' + +import { privateKeyToAccount } from 'viem/accounts' +import { mock } from '../connectors/mock.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getAccount } from './getAccount.js' +import { signTypedData } from './signTypedData.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + const signature = await signTypedData(config, { + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + }) + await expect( + recoverTypedDataAddress({ + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + signature, + }), + ).resolves.toBe(getAccount(config).address) + await disconnect(config, { connector }) +}) + +test('behavior: user rejected request', async () => { + const connector_ = config._internal.connectors.setup( + mock({ + accounts, + features: { signTypedDataError: true }, + }), + ) + await connect(config, { connector: connector_ }) + await expect( + signTypedData(config, { + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + }), + ).rejects.toMatchInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to sign typed data. + Version: viem@2.29.2] + `) + await disconnect(config, { connector: connector_ }) +}) + +test('behavior: not connected', async () => { + await expect( + signTypedData(config, { + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + }), + ).rejects.toMatchInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) +}) + +test('behavior: local account', async () => { + const account = privateKeyToAccount(privateKey) + const signature = await signTypedData(config, { + account, + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + }) + await expect( + recoverTypedDataAddress({ + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + signature, + }), + ).resolves.toBe(account.address) +}) diff --git a/packages/core/src/actions/signTypedData.ts b/packages/core/src/actions/signTypedData.ts new file mode 100644 index 0000000000..22cea12662 --- /dev/null +++ b/packages/core/src/actions/signTypedData.ts @@ -0,0 +1,60 @@ +import type { Account, Client, TypedData } from 'viem' +import { + type SignMessageErrorType as viem_SignMessageErrorType, + type SignTypedDataParameters as viem_SignTypedDataParameters, + type SignTypedDataReturnType as viem_SignTypedDataReturnType, + signTypedData as viem_signTypedData, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { ConnectorParameter } from '../types/properties.js' +import type { UnionCompute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type SignTypedDataParameters< + typedData extends TypedData | Record = TypedData, + primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData, + /// + primaryTypes = typedData extends TypedData ? keyof typedData : string, +> = UnionCompute< + viem_SignTypedDataParameters & + ConnectorParameter +> + +export type SignTypedDataReturnType = viem_SignTypedDataReturnType + +export type SignTypedDataErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_SignMessageErrorType + +/** https://wagmi.sh/core/api/actions/signTypedData */ +export async function signTypedData< + const typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', +>( + config: Config, + parameters: SignTypedDataParameters, +): Promise { + const { account, connector, ...rest } = parameters + + let client: Client + if (typeof account === 'object' && account.type === 'local') + client = config.getClient() + else client = await getConnectorClient(config, { account, connector }) + + const action = getAction(client, viem_signTypedData, 'signTypedData') + return action({ + ...rest, + ...(account ? { account } : {}), + } as unknown as viem_SignTypedDataParameters) +} diff --git a/packages/core/src/actions/simulateContract.test-d.ts b/packages/core/src/actions/simulateContract.test-d.ts new file mode 100644 index 0000000000..3f893ff8f8 --- /dev/null +++ b/packages/core/src/actions/simulateContract.test-d.ts @@ -0,0 +1,160 @@ +import { abi, config } from '@wagmi/test' +import { http, type Address } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type SimulateContractParameters, + type SimulateContractReturnType, + simulateContract, +} from './simulateContract.js' + +test('default', async () => { + const response = await simulateContract(config, { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + }) + + expectTypeOf(response).toMatchTypeOf<{ + result: boolean + request: { + chainId: 1 + abi: readonly [ + { + readonly name: 'transferFrom' + readonly type: 'function' + readonly stateMutability: 'nonpayable' + readonly inputs: readonly [ + { readonly type: 'address'; readonly name: 'sender' }, + { readonly type: 'address'; readonly name: 'recipient' }, + { readonly type: 'uint256'; readonly name: 'amount' }, + ] + readonly outputs: readonly [{ type: 'bool' }] + }, + ] + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + } + }>() +}) + +test('chain formatters', async () => { + const config = createConfig({ + chains: [celo, mainnet], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = SimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config + > + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + const response = await simulateContract(config, { + account: '0x', + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + feeCurrency: '0x', + }) + + if (response.chainId === celo.id) { + expectTypeOf(response.chainId).toEqualTypeOf(celo.id) + expectTypeOf(response.request.feeCurrency).toEqualTypeOf< + `0x${string}` | undefined + >() + } + + type Result2 = SimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof celo.id + > + expectTypeOf().toMatchTypeOf<{ + functionName: 'approve' | 'transfer' | 'transferFrom' + args: readonly [Address, Address, bigint] + feeCurrency?: `0x${string}` | undefined + }>() + const response2 = await simulateContract(config, { + chainId: celo.id, + account: '0x', + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + feeCurrency: '0x', + }) + expectTypeOf(response2.chainId).toEqualTypeOf(celo.id) + expectTypeOf(response2.request.feeCurrency).toEqualTypeOf< + `0x${string}` | undefined + >() + + type Result3 = SimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof mainnet.id + > + expectTypeOf().toMatchTypeOf<{ + functionName: 'approve' | 'transfer' | 'transferFrom' + args: readonly [Address, Address, bigint] + }>() + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + simulateContract(config, { + chainId: mainnet.id, + account: '0x', + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('SimulateContractParameters', () => { + type Result = SimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + (typeof config)['chains'][number]['id'] + > + expectTypeOf().toMatchTypeOf<{ + chainId?: (typeof config)['chains'][number]['id'] | undefined + functionName: 'approve' | 'transfer' | 'transferFrom' + args: readonly [Address, Address, bigint] + }>() +}) + +test('SimulateContractReturnType', () => { + type Result = SimulateContractReturnType< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + (typeof config)['chains'][number]['id'] + > + expectTypeOf().toMatchTypeOf<{ + result: boolean + request: { + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + chainId: (typeof config)['chains'][number]['id'] + } + }>() +}) diff --git a/packages/core/src/actions/simulateContract.test.ts b/packages/core/src/actions/simulateContract.test.ts new file mode 100644 index 0000000000..a52cbd5568 --- /dev/null +++ b/packages/core/src/actions/simulateContract.test.ts @@ -0,0 +1,84 @@ +import { abi, accounts, address, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { simulateContract } from './simulateContract.js' + +const connector = config.connectors[0]! + +test('parameters: account', async () => { + await expect( + simulateContract(config, { + account: accounts[0], + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'mint', + }), + ).resolves.toMatchInlineSnapshot(` + { + "chainId": 1, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": undefined, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + } + `) +}) + +test('parameters: connector', async () => { + await connect(config, { connector }) + + await expect( + simulateContract(config, { + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'mint', + connector, + }), + ).resolves.toMatchInlineSnapshot(` + { + "chainId": 1, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": undefined, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + } + `) + + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/simulateContract.ts b/packages/core/src/actions/simulateContract.ts new file mode 100644 index 0000000000..e5fe5655ff --- /dev/null +++ b/packages/core/src/actions/simulateContract.ts @@ -0,0 +1,166 @@ +import type { + Abi, + Account, + Address, + Chain, + ContractFunctionArgs, + ContractFunctionName, +} from 'viem' +import { + type SimulateContractErrorType as viem_SimulateContractErrorType, + type SimulateContractParameters as viem_SimulateContractParameters, + type SimulateContractReturnType as viem_SimulateContractReturnType, + simulateContract as viem_simulateContract, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { + Compute, + PartialBy, + UnionCompute, + UnionStrictOmit, +} from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type SimulateContractParameters< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'nonpayable' | 'payable' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + > = ContractFunctionArgs, + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: UnionCompute< + UnionStrictOmit< + viem_SimulateContractParameters< + abi, + functionName, + args, + chains[key], + chains[key], + Account | Address + >, + 'chain' + > + > & + ChainIdParameter & + ConnectorParameter +}[number] + +export type SimulateContractReturnType< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'nonpayable' | 'payable' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + > = ContractFunctionArgs, + config extends Config = Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: viem_SimulateContractReturnType< + abi, + functionName, + args, + chains[key], + Account, + chains[key] + > & { + chainId: chains[key]['id'] + request: Compute< + PartialBy< + { chainId: chainId; chain: chains[key] }, + chainId extends config['chains'][number]['id'] ? never : 'chainId' + > + > + } +}[number] + +export type SimulateContractErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_SimulateContractErrorType + +/** https://wagmi.sh/core/api/actions/simulateContract */ +export async function simulateContract< + config extends Config, + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + chainId extends config['chains'][number]['id'] | undefined = undefined, +>( + config: config, + parameters: SimulateContractParameters< + abi, + functionName, + args, + config, + chainId + >, +): Promise< + SimulateContractReturnType +> { + const { abi, chainId, connector, ...rest } = + parameters as SimulateContractParameters + + let account: Address | Account + if (parameters.account) account = parameters.account + else { + const connectorClient = await getConnectorClient(config, { + chainId, + connector, + }) + account = connectorClient.account + } + + const client = config.getClient({ chainId }) + const action = getAction(client, viem_simulateContract, 'simulateContract') + const { result, request } = await action({ ...rest, abi, account }) + + return { + chainId: client.chain.id, + result, + request: { ...request, chainId }, + } as unknown as SimulateContractReturnType< + abi, + functionName, + args, + config, + chainId + > +} diff --git a/packages/core/src/actions/switchAccount.test.ts b/packages/core/src/actions/switchAccount.test.ts new file mode 100644 index 0000000000..97d0e84b37 --- /dev/null +++ b/packages/core/src/actions/switchAccount.test.ts @@ -0,0 +1,32 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getAccount } from './getAccount.js' +import { switchAccount } from './switchAccount.js' + +const connector1 = config.connectors[0]! +const connector2 = config.connectors[1]! + +test('default', async () => { + await connect(config, { connector: connector2 }) + await connect(config, { connector: connector1 }) + + const address1 = getAccount(config).address + + await switchAccount(config, { connector: connector2 }) + + const address2 = getAccount(config).address + expect(address2).toBeDefined() + expect(address1).not.toBe(address2) + + await switchAccount(config, { connector: connector1 }) + + const address3 = getAccount(config).address + expect(address3).toBeDefined() + expect(address1).toBe(address3) + + await disconnect(config, { connector: connector1 }) + await disconnect(config, { connector: connector2 }) +}) diff --git a/packages/core/src/actions/switchAccount.ts b/packages/core/src/actions/switchAccount.ts new file mode 100644 index 0000000000..ec577a57c7 --- /dev/null +++ b/packages/core/src/actions/switchAccount.ts @@ -0,0 +1,45 @@ +import type { Address } from 'viem' + +import type { Config, Connector } from '../createConfig.js' +import type { BaseError, ErrorType } from '../errors/base.js' +import { + ConnectorNotConnectedError, + type ConnectorNotConnectedErrorType, +} from '../errors/config.js' + +export type SwitchAccountParameters = { + connector: Connector +} + +export type SwitchAccountReturnType = { + accounts: readonly [Address, ...Address[]] + chainId: + | config['chains'][number]['id'] + | (number extends config['chains'][number]['id'] ? number : number & {}) +} + +export type SwitchAccountErrorType = + | ConnectorNotConnectedErrorType + | BaseError + | ErrorType + +/** https://wagmi.sh/core/api/actions/switchAccount */ +export async function switchAccount( + config: config, + parameters: SwitchAccountParameters, +): Promise> { + const { connector } = parameters + + const connection = config.state.connections.get(connector.uid) + if (!connection) throw new ConnectorNotConnectedError() + + await config.storage?.setItem('recentConnectorId', connector.id) + config.setState((x) => ({ + ...x, + current: connector.uid, + })) + return { + accounts: connection.accounts, + chainId: connection.chainId, + } +} diff --git a/packages/core/src/actions/switchChain.test.ts b/packages/core/src/actions/switchChain.test.ts new file mode 100644 index 0000000000..466d77824f --- /dev/null +++ b/packages/core/src/actions/switchChain.test.ts @@ -0,0 +1,73 @@ +import { accounts, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { mock } from '../connectors/mock.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getAccount } from './getAccount.js' +import { switchChain } from './switchChain.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const chainId1 = getAccount(config).chainId + + await switchChain(config, { chainId: chain.mainnet2.id }) + + const chainId2 = getAccount(config).chainId + expect(chainId2).toBeDefined() + expect(chainId1).not.toBe(chainId2) + + await switchChain(config, { chainId: chain.mainnet.id }) + + const chainId3 = getAccount(config).chainId + expect(chainId3).toBeDefined() + expect(chainId1).toBe(chainId3) + + await disconnect(config, { connector }) +}) + +test('behavior: user rejected request', async () => { + const connector_ = config._internal.connectors.setup( + mock({ + accounts, + features: { switchChainError: true }, + }), + ) + await connect(config, { connector: connector_ }) + await expect( + switchChain(config, { chainId: chain.mainnet.id }), + ).rejects.toMatchInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to switch chain. + Version: viem@2.29.2] + `) + await disconnect(config, { connector: connector_ }) +}) + +test('behavior: not supported', async () => { + const { switchChain: _, ...connector_ } = config._internal.connectors.setup( + mock({ accounts }), + ) + await connect(config, { connector: connector_ }) + await expect( + switchChain(config, { chainId: chain.mainnet.id }), + ).rejects.toMatchInlineSnapshot(` + [SwitchChainNotSupportedError: "Mock Connector" does not support programmatic chain switching. + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector: connector_ }) +}) + +test('behavior: not connected', async () => { + const chainId = config.state.chainId + expect(config.state.chainId).toMatchInlineSnapshot('1') + await switchChain(config, { chainId: chain.mainnet2.id }) + expect(config.state.chainId).toMatchInlineSnapshot('456') + await switchChain(config, { chainId }) + expect(config.state.chainId).toMatchInlineSnapshot('1') +}) diff --git a/packages/core/src/actions/switchChain.ts b/packages/core/src/actions/switchChain.ts new file mode 100644 index 0000000000..59f337d141 --- /dev/null +++ b/packages/core/src/actions/switchChain.ts @@ -0,0 +1,83 @@ +import type { + AddEthereumChainParameter, + UserRejectedRequestErrorType, + SwitchChainErrorType as viem_SwitchChainErrorType, +} from 'viem' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import { + ChainNotConfiguredError, + type ChainNotConfiguredErrorType, +} from '../errors/config.js' +import { + type ProviderNotFoundErrorType, + SwitchChainNotSupportedError, + type SwitchChainNotSupportedErrorType, +} from '../errors/connector.js' +import type { ConnectorParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' + +export type SwitchChainParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + ConnectorParameter & { + chainId: chainId | config['chains'][number]['id'] + addEthereumChainParameter?: + | Compute>> + | undefined + } +> + +export type SwitchChainReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Extract< + config['chains'][number], + { id: Config extends config ? number : chainId } +> + +export type SwitchChainErrorType = + | SwitchChainNotSupportedErrorType + | ChainNotConfiguredErrorType + // connector.switchChain() + | ProviderNotFoundErrorType + | UserRejectedRequestErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_SwitchChainErrorType + +/** https://wagmi.sh/core/api/actions/switchChain */ +export async function switchChain< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: SwitchChainParameters, +): Promise> { + const { addEthereumChainParameter, chainId } = parameters + + const connection = config.state.connections.get( + parameters.connector?.uid ?? config.state.current!, + ) + if (connection) { + const connector = connection.connector + if (!connector.switchChain) + throw new SwitchChainNotSupportedError({ connector }) + const chain = await connector.switchChain({ + addEthereumChainParameter, + chainId, + }) + return chain as SwitchChainReturnType + } + + const chain = config.chains.find((x) => x.id === chainId) + if (!chain) throw new ChainNotConfiguredError() + config.setState((x) => ({ ...x, chainId })) + return chain as SwitchChainReturnType +} diff --git a/packages/core/src/actions/verifyMessage.test.ts b/packages/core/src/actions/verifyMessage.test.ts new file mode 100644 index 0000000000..f2766cbea4 --- /dev/null +++ b/packages/core/src/actions/verifyMessage.test.ts @@ -0,0 +1,72 @@ +import { accounts, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { verifyMessage } from './verifyMessage.js' + +const eoaAddress = accounts[0] +const smartAccountAddress = '0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145' + +test('smart account: valid signature', async () => { + expect( + await verifyMessage(config, { + address: smartAccountAddress, + message: 'This is a test message for viem!', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toBe(true) +}) + +test('smart account: invalid signature', async () => { + expect( + await verifyMessage(config, { + address: smartAccountAddress, + message: 'This is a test message for viem!', + signature: '0xdead', + }), + ).toBe(false) +}) + +test('smart account: account not deployed', async () => { + expect( + await verifyMessage(config, { + blockNumber: 1234567890n, + address: smartAccountAddress, + message: 'This is a test message for viem!', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toBe(false) +}) + +test('eoa: valid signature', async () => { + expect( + await verifyMessage(config, { + address: eoaAddress, + message: 'This is a test message for viem!', + signature: + '0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b', + }), + ).toBe(true) +}) + +test('eoa: invalid signature', async () => { + expect( + await verifyMessage(config, { + address: eoaAddress, + message: 'This is a test message for viem!', + signature: '0xdead', + }), + ).toBe(false) +}) + +test('eoa: raw message', async () => { + expect( + await verifyMessage(config, { + address: eoaAddress, + message: { raw: '0x68656c6c6f20776f726c64' }, + signature: + '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b', + }), + ).toBe(true) +}) diff --git a/packages/core/src/actions/verifyMessage.ts b/packages/core/src/actions/verifyMessage.ts new file mode 100644 index 0000000000..851f8589fa --- /dev/null +++ b/packages/core/src/actions/verifyMessage.ts @@ -0,0 +1,30 @@ +import { + type VerifyMessageErrorType as viem_VerifyMessageErrorType, + type VerifyMessageParameters as viem_VerifyMessageParameters, + type VerifyMessageReturnType as viem_VerifyMessageReturnType, + verifyMessage as viem_verifyMessage, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type VerifyMessageParameters = Compute< + viem_VerifyMessageParameters & ChainIdParameter +> + +export type VerifyMessageReturnType = viem_VerifyMessageReturnType + +export type VerifyMessageErrorType = viem_VerifyMessageErrorType + +/** https://wagmi.sh/core/api/actions/verifyMessage */ +export async function verifyMessage( + config: config, + parameters: VerifyMessageParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_verifyMessage, 'verifyMessage') + return action(rest) +} diff --git a/packages/core/src/actions/verifyTypedData.test.ts b/packages/core/src/actions/verifyTypedData.test.ts new file mode 100644 index 0000000000..c4b98c68dc --- /dev/null +++ b/packages/core/src/actions/verifyTypedData.test.ts @@ -0,0 +1,42 @@ +import { config, typedData } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { verifyTypedData } from './verifyTypedData.js' + +const smartAccountAddress = '0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145' +const notDeployedAddress = '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' + +test('valid signature', async () => { + expect( + await verifyTypedData(config, { + ...typedData.basic, + primaryType: 'Mail', + address: smartAccountAddress, + signature: + '0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c', + }), + ).toBe(true) +}) + +test('invalid signature', async () => { + expect( + await verifyTypedData(config, { + ...typedData.basic, + primaryType: 'Mail', + address: smartAccountAddress, + signature: '0xdead', + }), + ).toBe(false) +}) + +test('account not deployed', async () => { + expect( + await verifyTypedData(config, { + ...typedData.basic, + primaryType: 'Mail', + address: notDeployedAddress, + signature: + '0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c', + }), + ).toBe(false) +}) diff --git a/packages/core/src/actions/verifyTypedData.ts b/packages/core/src/actions/verifyTypedData.ts new file mode 100644 index 0000000000..4fac5463f5 --- /dev/null +++ b/packages/core/src/actions/verifyTypedData.ts @@ -0,0 +1,40 @@ +import type { TypedData } from 'viem' +import { + type VerifyTypedDataErrorType as viem_VerifyTypedDataErrorType, + type VerifyTypedDataParameters as viem_VerifyTypedDataParameters, + type VerifyTypedDataReturnType as viem_VerifyTypedDataReturnType, + verifyTypedData as viem_verifyTypedData, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type VerifyTypedDataParameters< + typedData extends TypedData | Record = TypedData, + primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData, + config extends Config = Config, +> = Compute< + viem_VerifyTypedDataParameters & + ChainIdParameter +> + +export type VerifyTypedDataReturnType = viem_VerifyTypedDataReturnType + +export type VerifyTypedDataErrorType = viem_VerifyTypedDataErrorType + +/** https://wagmi.sh/core/api/actions/verifyTypedData */ +export async function verifyTypedData< + config extends Config, + const typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', +>( + config: config, + parameters: VerifyTypedDataParameters, +): Promise { + const { chainId, ...rest } = parameters + const client = config.getClient({ chainId }) + const action = getAction(client, viem_verifyTypedData, 'verifyTypedData') + return action(rest as viem_VerifyTypedDataParameters) +} diff --git a/packages/core/src/actions/waitForCallsStatus.test.ts b/packages/core/src/actions/waitForCallsStatus.test.ts new file mode 100644 index 0000000000..338fe02616 --- /dev/null +++ b/packages/core/src/actions/waitForCallsStatus.test.ts @@ -0,0 +1,77 @@ +import { accounts, config, testClient, wait } from '@wagmi/test' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { sendCalls } from './sendCalls.js' +import { waitForCallsStatus } from './waitForCallsStatus.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { id } = await sendCalls(config, { + calls: [ + { + data: '0xdeadbeef', + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }) + + const [{ receipts, status }] = await Promise.all([ + waitForCallsStatus(config, { + id, + }), + (async () => { + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + })(), + ]) + + expect(status).toBe('success') + expect( + receipts?.map((x) => ({ ...x, blockHash: undefined })), + ).toMatchInlineSnapshot( + ` + [ + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21064n, + "logs": [], + "status": "success", + "transactionHash": "0x13c53b2d4d9da424835525349cd66e553330f323d6fb19458b801ae1f7989a41", + }, + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21000n, + "logs": [], + "status": "success", + "transactionHash": "0xd8397b3e82b061c26a0c2093f1ceca0c3662a512614f7d6370349e89d0eea007", + }, + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21000n, + "logs": [], + "status": "success", + "transactionHash": "0x4d26e346593d9ea265bb164b115e89aa92df43b0b8778ac75d4ad28e2a22b101", + }, + ] + `, + ) + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/waitForCallsStatus.ts b/packages/core/src/actions/waitForCallsStatus.ts new file mode 100644 index 0000000000..a1c5764d32 --- /dev/null +++ b/packages/core/src/actions/waitForCallsStatus.ts @@ -0,0 +1,27 @@ +import { + type WaitForCallsStatusErrorType as viem_WaitForCallsStatusErrorType, + type WaitForCallsStatusParameters as viem_WaitForCallsStatusParameters, + type WaitForCallsStatusReturnType as viem_WaitForCallsStatusReturnType, + waitForCallsStatus as viem_waitForCallsStatus, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { ConnectorParameter } from '../types/properties.js' +import { getConnectorClient } from './getConnectorClient.js' + +export type WaitForCallsStatusParameters = viem_WaitForCallsStatusParameters & + ConnectorParameter + +export type WaitForCallsStatusReturnType = viem_WaitForCallsStatusReturnType + +export type WaitForCallsStatusErrorType = viem_WaitForCallsStatusErrorType + +/** https://wagmi.sh/core/api/actions/waitForCallsStatus */ +export async function waitForCallsStatus( + config: config, + parameters: WaitForCallsStatusParameters, +): Promise { + const { connector } = parameters + const client = await getConnectorClient(config, { connector }) + return viem_waitForCallsStatus(client, parameters) +} diff --git a/packages/core/src/actions/waitForTransactionReceipt.test-d.ts b/packages/core/src/actions/waitForTransactionReceipt.test-d.ts new file mode 100644 index 0000000000..0cce58d1e4 --- /dev/null +++ b/packages/core/src/actions/waitForTransactionReceipt.test-d.ts @@ -0,0 +1,36 @@ +import { http } from 'viem' +import { mainnet, zkSync } from 'viem/chains' +import type { ZkSyncL2ToL1Log, ZkSyncLog } from 'viem/zksync' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { waitForTransactionReceipt } from './waitForTransactionReceipt.js' + +test('chain formatters', async () => { + const config = createConfig({ + chains: [mainnet, zkSync], + transports: { [mainnet.id]: http(), [zkSync.id]: http() }, + }) + const result = await waitForTransactionReceipt(config, { hash: '0x123' }) + if (result.chainId === zkSync.id) { + expectTypeOf(result.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result.l1BatchTxIndex).toEqualTypeOf() + expectTypeOf(result.logs).toEqualTypeOf() + expectTypeOf(result.l2ToL1Logs).toEqualTypeOf() + } +}) + +test('chainId', async () => { + const config = createConfig({ + chains: [zkSync], + transports: { [zkSync.id]: http() }, + }) + const result = await waitForTransactionReceipt(config, { + hash: '0x123', + chainId: zkSync.id, + }) + expectTypeOf(result.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result.l1BatchTxIndex).toEqualTypeOf() + expectTypeOf(result.logs).toEqualTypeOf() + expectTypeOf(result.l2ToL1Logs).toEqualTypeOf() +}) diff --git a/packages/core/src/actions/waitForTransactionReceipt.test.ts b/packages/core/src/actions/waitForTransactionReceipt.test.ts new file mode 100644 index 0000000000..c060e82665 --- /dev/null +++ b/packages/core/src/actions/waitForTransactionReceipt.test.ts @@ -0,0 +1,58 @@ +import { config, testClient, wait } from '@wagmi/test' +import { parseEther } from 'viem' +import { beforeEach, expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { sendTransaction } from './sendTransaction.js' +import { waitForTransactionReceipt } from './waitForTransactionReceipt.js' + +const connector = config.connectors[0]! + +beforeEach(async () => { + if (config.state.current === connector.uid) + await disconnect(config, { connector }) +}) + +test('default', async () => { + await connect(config, { connector }) + + const hash = await sendTransaction(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }) + + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + + await expect( + waitForTransactionReceipt(config, { hash }), + ).resolves.toMatchObject({ + chainId: 1, + transactionHash: hash, + }) + + await disconnect(config, { connector }) +}) + +test('behavior: transaction reverted', async () => { + await expect( + waitForTransactionReceipt(config, { + hash: '0x745367f76807d411b7fa4c3a552a62e3e45303ef40145fff04d84b867c2575d3', + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [CallExecutionError: Execution reverted with reason: PartyBid::claim: contribution already claimed. + + Raw Call Arguments: + to: 0xf1332f21487e74612ed3a0fb36da729b73f1ae19 + value: 0 ETH + data: 0x1e83409a000000000000000000000000a0cf798816d4b9b9866b5330eea46a18382f251e + gas: 128730 + maxFeePerGas: 43.307900987 gwei + maxPriorityFeePerGas: 1.5 gwei + nonce: 43 + + Details: execution reverted: PartyBid::claim: contribution already claimed + Version: viem@2.29.2] + `) +}) diff --git a/packages/core/src/actions/waitForTransactionReceipt.ts b/packages/core/src/actions/waitForTransactionReceipt.ts new file mode 100644 index 0000000000..5ac8fcdb20 --- /dev/null +++ b/packages/core/src/actions/waitForTransactionReceipt.ts @@ -0,0 +1,86 @@ +import type { Chain } from 'viem' +import { hexToString } from 'viem' +import { + call, + getTransaction, + type WaitForTransactionReceiptErrorType as viem_WaitForTransactionReceiptErrorType, + type WaitForTransactionReceiptParameters as viem_WaitForTransactionReceiptParameters, + type WaitForTransactionReceiptReturnType as viem_WaitForTransactionReceiptReturnType, + waitForTransactionReceipt as viem_waitForTransactionReceipt, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { Compute, IsNarrowable } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type WaitForTransactionReceiptParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + viem_WaitForTransactionReceiptParameters & ChainIdParameter +> + +export type WaitForTransactionReceiptReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = Compute< + { + [key in keyof chains]: viem_WaitForTransactionReceiptReturnType< + IsNarrowable extends true ? chains[key] : undefined + > & { chainId: chains[key]['id'] } + }[number] +> + +export type WaitForTransactionReceiptErrorType = + viem_WaitForTransactionReceiptErrorType + +export async function waitForTransactionReceipt< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: WaitForTransactionReceiptParameters, +): Promise> { + const { chainId, timeout = 0, ...rest } = parameters + + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_waitForTransactionReceipt, + 'waitForTransactionReceipt', + ) + const receipt = await action({ ...rest, timeout }) + + if (receipt.status === 'reverted') { + const action_getTransaction = getAction( + client, + getTransaction, + 'getTransaction', + ) + const txn = await action_getTransaction({ hash: receipt.transactionHash }) + const action_call = getAction(client, call, 'call') + const code = await action_call({ + ...(txn as any), + data: txn.input, + gasPrice: txn.type !== 'eip1559' ? txn.gasPrice : undefined, + maxFeePerGas: txn.type === 'eip1559' ? txn.maxFeePerGas : undefined, + maxPriorityFeePerGas: + txn.type === 'eip1559' ? txn.maxPriorityFeePerGas : undefined, + }) + const reason = code?.data + ? hexToString(`0x${code.data.substring(138)}`) + : 'unknown reason' + throw new Error(reason) + } + + return { + ...receipt, + chainId: client.chain.id, + } as WaitForTransactionReceiptReturnType +} diff --git a/packages/core/src/actions/watchAccount.test.ts b/packages/core/src/actions/watchAccount.test.ts new file mode 100644 index 0000000000..c803165888 --- /dev/null +++ b/packages/core/src/actions/watchAccount.test.ts @@ -0,0 +1,38 @@ +import { config } from '@wagmi/test' +import type { Address } from 'viem' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { watchAccount } from './watchAccount.js' + +test('default', async () => { + const accounts: { address: Address | undefined; status: string }[] = [] + const unwatch = watchAccount(config, { + onChange(data) { + accounts.push({ address: data.address, status: data.status }) + }, + }) + + await connect(config, { connector: config.connectors[0]! }) + await disconnect(config) + + expect(accounts).toMatchInlineSnapshot(` + [ + { + "address": undefined, + "status": "connecting", + }, + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "status": "connected", + }, + { + "address": undefined, + "status": "disconnected", + }, + ] + `) + + unwatch() +}) diff --git a/packages/core/src/actions/watchAccount.ts b/packages/core/src/actions/watchAccount.ts new file mode 100644 index 0000000000..dfa8ae4908 --- /dev/null +++ b/packages/core/src/actions/watchAccount.ts @@ -0,0 +1,33 @@ +import type { Config } from '../createConfig.js' +import { deepEqual } from '../utils/deepEqual.js' +import { type GetAccountReturnType, getAccount } from './getAccount.js' + +export type WatchAccountParameters = { + onChange( + account: GetAccountReturnType, + prevAccount: GetAccountReturnType, + ): void +} + +export type WatchAccountReturnType = () => void + +/** https://wagmi.sh/core/api/actions/watchAccount */ +export function watchAccount( + config: config, + parameters: WatchAccountParameters, +): WatchAccountReturnType { + const { onChange } = parameters + + return config.subscribe(() => getAccount(config), onChange, { + equalityFn(a, b) { + const { connector: aConnector, ...aRest } = a + const { connector: bConnector, ...bRest } = b + return ( + deepEqual(aRest, bRest) && + // check connector separately + aConnector?.id === bConnector?.id && + aConnector?.uid === bConnector?.uid + ) + }, + }) +} diff --git a/packages/core/src/actions/watchAsset.test.ts b/packages/core/src/actions/watchAsset.test.ts new file mode 100644 index 0000000000..6d4cd86b58 --- /dev/null +++ b/packages/core/src/actions/watchAsset.test.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { watchAsset } from './watchAsset.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + await expect( + watchAsset(config, { + type: 'ERC20', + options: { + address: '0x0000000000000000000000000000000000000000', + symbol: 'NULL', + decimals: 18, + }, + }), + ).resolves.toMatchInlineSnapshot('true') + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/watchAsset.ts b/packages/core/src/actions/watchAsset.ts new file mode 100644 index 0000000000..cd5690bf57 --- /dev/null +++ b/packages/core/src/actions/watchAsset.ts @@ -0,0 +1,44 @@ +import { + type WatchAssetErrorType as viem_WatchAssetErrorType, + type WatchAssetParameters as viem_WatchAssetParameters, + type WatchAssetReturnType as viem_WatchAssetReturnType, + watchAsset as viem_watchAsset, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { ConnectorParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type WatchAssetParameters = Compute< + viem_WatchAssetParameters & ConnectorParameter +> + +export type WatchAssetReturnType = viem_WatchAssetReturnType + +export type WatchAssetErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_WatchAssetErrorType + +/** https://wagmi.sh/core/api/actions/watchAsset */ +export async function watchAsset( + config: Config, + parameters: WatchAssetParameters, +): Promise { + const { connector, ...rest } = parameters + + const client = await getConnectorClient(config, { connector }) + + const action = getAction(client, viem_watchAsset, 'watchAsset') + return action(rest as viem_WatchAssetParameters) +} diff --git a/packages/core/src/actions/watchBlockNumber.test-d.ts b/packages/core/src/actions/watchBlockNumber.test-d.ts new file mode 100644 index 0000000000..ae63ed8af6 --- /dev/null +++ b/packages/core/src/actions/watchBlockNumber.test-d.ts @@ -0,0 +1,56 @@ +import { http, webSocket } from 'viem' +import { mainnet, optimism } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type WatchBlockNumberParameters, + watchBlockNumber, +} from './watchBlockNumber.js' + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + type Result = WatchBlockNumberParameters< + typeof config, + typeof mainnet.id | typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + watchBlockNumber(config, { + poll: false, + onBlockNumber() {}, + }) + + type Result2 = WatchBlockNumberParameters + expectTypeOf().toEqualTypeOf() + watchBlockNumber(config, { + chainId: mainnet.id, + poll: true, + onBlockNumber() {}, + }) + watchBlockNumber(config, { + chainId: mainnet.id, + // @ts-expect-error + poll: false, + onBlockNumber() {}, + }) + + type Result3 = WatchBlockNumberParameters + expectTypeOf().toEqualTypeOf() + watchBlockNumber(config, { + chainId: optimism.id, + poll: true, + onBlockNumber() {}, + }) + watchBlockNumber(config, { + chainId: optimism.id, + poll: false, + onBlockNumber() {}, + }) +}) diff --git a/packages/core/src/actions/watchBlockNumber.test.ts b/packages/core/src/actions/watchBlockNumber.test.ts new file mode 100644 index 0000000000..0a4299db58 --- /dev/null +++ b/packages/core/src/actions/watchBlockNumber.test.ts @@ -0,0 +1,27 @@ +import { config, testClient, wait } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { watchBlockNumber } from './watchBlockNumber.js' + +test('default', async () => { + const blockNumbers: bigint[] = [] + const unwatch = watchBlockNumber(config, { + onBlockNumber(blockNumber) { + blockNumbers.push(blockNumber) + }, + }) + + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + + expect(blockNumbers.length).toBe(3) + expect( + blockNumbers.map((blockNumber) => blockNumber - blockNumbers[0]!), + ).toEqual([0n, 1n, 2n]) + + unwatch() +}) diff --git a/packages/core/src/actions/watchBlockNumber.ts b/packages/core/src/actions/watchBlockNumber.ts new file mode 100644 index 0000000000..712849080a --- /dev/null +++ b/packages/core/src/actions/watchBlockNumber.ts @@ -0,0 +1,78 @@ +import { + type WatchBlockNumberParameters as viem_WatchBlockNumberParameters, + type WatchBlockNumberReturnType as viem_WatchBlockNumberReturnType, + watchBlockNumber as viem_watchBlockNumber, +} from 'viem/actions' + +import type { Chain, Transport, WebSocketTransport } from 'viem' +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + SyncConnectedChainParameter, +} from '../types/properties.js' +import type { UnionCompute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type WatchBlockNumberParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: UnionCompute< + viem_WatchBlockNumberParameters< + config['_internal']['transports'][chains[key]['id']] extends infer transport extends + Transport + ? Transport extends transport + ? WebSocketTransport + : transport + : WebSocketTransport + > & + ChainIdParameter & + SyncConnectedChainParameter + > +}[number] + +export type WatchBlockNumberReturnType = viem_WatchBlockNumberReturnType + +// TODO: wrap in viem's `observe` to avoid duplicate invocations. +/** https://wagmi.sh/core/api/actions/watchBlockNumber */ +export function watchBlockNumber< + config extends Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + config: config, + parameters: WatchBlockNumberParameters, +): WatchBlockNumberReturnType { + const { syncConnectedChain = config._internal.syncConnectedChain, ...rest } = + parameters as WatchBlockNumberParameters + + let unwatch: WatchBlockNumberReturnType | undefined + const listener = (chainId: number | undefined) => { + if (unwatch) unwatch() + + const client = config.getClient({ chainId }) + const action = getAction(client, viem_watchBlockNumber, 'watchBlockNumber') + unwatch = action(rest as viem_WatchBlockNumberParameters) + return unwatch + } + + // set up listener for block number changes + const unlisten = listener(parameters.chainId) + + // set up subscriber for connected chain changes + let unsubscribe: (() => void) | undefined + if (syncConnectedChain && !parameters.chainId) + unsubscribe = config.subscribe( + ({ chainId }) => chainId, + async (chainId) => listener(chainId), + ) + + return () => { + unlisten?.() + unsubscribe?.() + } +} diff --git a/packages/core/src/actions/watchBlocks.test-d.ts b/packages/core/src/actions/watchBlocks.test-d.ts new file mode 100644 index 0000000000..1899dcb1ae --- /dev/null +++ b/packages/core/src/actions/watchBlocks.test-d.ts @@ -0,0 +1,59 @@ +import { http, webSocket } from 'viem' +import { mainnet, optimism } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { type WatchBlocksParameters, watchBlocks } from './watchBlocks.js' + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + type Result = WatchBlocksParameters< + false, + 'latest', + typeof config, + typeof mainnet.id | typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + watchBlocks(config, { + poll: false, + onBlock() {}, + }) + + type Result2 = WatchBlocksParameters< + false, + 'latest', + typeof config, + typeof mainnet.id + > + expectTypeOf().toEqualTypeOf() + watchBlocks(config, { + chainId: mainnet.id, + poll: true, + onBlock() {}, + }) + + type Result3 = WatchBlocksParameters< + false, + 'latest', + typeof config, + typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + watchBlocks(config, { + chainId: optimism.id, + poll: true, + onBlock() {}, + }) + watchBlocks(config, { + chainId: optimism.id, + poll: false, + onBlock() {}, + }) +}) diff --git a/packages/core/src/actions/watchBlocks.test.ts b/packages/core/src/actions/watchBlocks.test.ts new file mode 100644 index 0000000000..caf4c0dc61 --- /dev/null +++ b/packages/core/src/actions/watchBlocks.test.ts @@ -0,0 +1,30 @@ +import { config, testClient, wait } from '@wagmi/test' +import { expect, test } from 'vitest' + +import type { Block } from 'viem' +import { watchBlocks } from './watchBlocks.js' + +test('default', async () => { + const blocks: Block[] = [] + const unwatch = watchBlocks(config, { + onBlock(block) { + blocks.push(block) + }, + }) + + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + + expect(blocks.length).toBe(3) + expect(blocks.map((block) => block.number! - blocks[0]?.number!)).toEqual([ + 0n, + 1n, + 2n, + ]) + + unwatch() +}) diff --git a/packages/core/src/actions/watchBlocks.ts b/packages/core/src/actions/watchBlocks.ts new file mode 100644 index 0000000000..c6f3225dc9 --- /dev/null +++ b/packages/core/src/actions/watchBlocks.ts @@ -0,0 +1,90 @@ +import { + type WatchBlocksParameters as viem_WatchBlocksParameters, + type WatchBlocksReturnType as viem_WatchBlocksReturnType, + watchBlocks as viem_watchBlocks, +} from 'viem/actions' + +import type { BlockTag, Chain, Transport, WebSocketTransport } from 'viem' +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + SyncConnectedChainParameter, +} from '../types/properties.js' +import type { IsNarrowable, UnionCompute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type WatchBlocksParameters< + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: UnionCompute< + viem_WatchBlocksParameters< + config['_internal']['transports'][chains[key]['id']] extends infer transport extends + Transport + ? Transport extends transport + ? WebSocketTransport + : transport + : WebSocketTransport, + IsNarrowable extends true ? chains[key] : undefined, + includeTransactions, + blockTag + > & + ChainIdParameter & + SyncConnectedChainParameter + > +}[number] + +export type WatchBlocksReturnType = viem_WatchBlocksReturnType + +// TODO: wrap in viem's `observe` to avoid duplicate invocations. +/** https://wagmi.sh/core/actions/watchBlocks */ +export function watchBlocks< + config extends Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', +>( + config: config, + parameters: WatchBlocksParameters< + includeTransactions, + blockTag, + config, + chainId + >, +): WatchBlocksReturnType { + const { syncConnectedChain = config._internal.syncConnectedChain, ...rest } = + parameters as WatchBlocksParameters + + let unwatch: WatchBlocksReturnType | undefined + const listener = (chainId: number | undefined) => { + if (unwatch) unwatch() + + const client = config.getClient({ chainId }) + const action = getAction(client, viem_watchBlocks, 'watchBlocks') + unwatch = action(rest as viem_WatchBlocksParameters) + return unwatch + } + + // set up listener for block number changes + const unlisten = listener(parameters.chainId) + + // set up subscriber for connected chain changes + let unsubscribe: (() => void) | undefined + if (syncConnectedChain && !parameters.chainId) + unsubscribe = config.subscribe( + ({ chainId }) => chainId, + async (chainId) => listener(chainId), + ) + + return () => { + unlisten?.() + unsubscribe?.() + } +} diff --git a/packages/core/src/actions/watchChainId.test.ts b/packages/core/src/actions/watchChainId.test.ts new file mode 100644 index 0000000000..9e27ba7edd --- /dev/null +++ b/packages/core/src/actions/watchChainId.test.ts @@ -0,0 +1,26 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { watchChainId } from './watchChainId.js' + +test('default', async () => { + const chainIds: number[] = [] + const unwatch = watchChainId(config, { + onChange(chainId) { + chainIds.push(chainId) + }, + }) + config.setState((x) => ({ ...x, chainId: chain.mainnet2.id })) + config.setState((x) => ({ ...x, chainId: chain.mainnet.id })) + config.setState((x) => ({ ...x, chainId: chain.mainnet2.id })) + + expect(chainIds).toMatchInlineSnapshot(` + [ + 456, + 1, + 456, + ] + `) + + unwatch() +}) diff --git a/packages/core/src/actions/watchChainId.ts b/packages/core/src/actions/watchChainId.ts new file mode 100644 index 0000000000..e3d4f010a4 --- /dev/null +++ b/packages/core/src/actions/watchChainId.ts @@ -0,0 +1,20 @@ +import type { Config } from '../createConfig.js' +import type { GetChainIdReturnType } from './getChainId.js' + +export type WatchChainIdParameters = { + onChange( + chainId: GetChainIdReturnType, + prevChainId: GetChainIdReturnType, + ): void +} + +export type WatchChainIdReturnType = () => void + +/** https://wagmi.sh/core/api/actions/watchChainId */ +export function watchChainId( + config: config, + parameters: WatchChainIdParameters, +): WatchChainIdReturnType { + const { onChange } = parameters + return config.subscribe((state) => state.chainId, onChange) +} diff --git a/packages/core/src/actions/watchChains.test.ts b/packages/core/src/actions/watchChains.test.ts new file mode 100644 index 0000000000..d7d586a833 --- /dev/null +++ b/packages/core/src/actions/watchChains.test.ts @@ -0,0 +1,37 @@ +import { chain, config } from '@wagmi/test' +import type { Chain } from 'viem' +import { expect, test } from 'vitest' + +import { watchChains } from './watchChains.js' + +test('default', async () => { + let chains: readonly [Chain, ...Chain[]] = config.chains + const unwatch = watchChains(config, { + onChange(nextChains) { + chains = nextChains + }, + }) + + config._internal.chains.setState([chain.mainnet, chain.mainnet2]) + expect(chains.map((x) => x.id)).toMatchInlineSnapshot(` + [ + 1, + 456, + ] + `) + + config._internal.chains.setState([ + chain.mainnet, + chain.mainnet2, + chain.optimism, + ]) + expect(chains.map((x) => x.id)).toMatchInlineSnapshot(` + [ + 1, + 456, + 10, + ] + `) + + unwatch() +}) diff --git a/packages/core/src/actions/watchChains.ts b/packages/core/src/actions/watchChains.ts new file mode 100644 index 0000000000..db5c496d5a --- /dev/null +++ b/packages/core/src/actions/watchChains.ts @@ -0,0 +1,29 @@ +import type { Config } from '../createConfig.js' +import type { GetChainsReturnType } from './getChains.js' + +export type WatchChainsParameters = { + onChange( + chains: GetChainsReturnType, + prevChains: GetChainsReturnType, + ): void +} + +export type WatchChainsReturnType = () => void + +/** + * @internal + * We don't expose this because as far as consumers know, you can't chainge (lol) `config.chains` at runtime. + * Setting `config.chains` via `config._internal.chains.setState(...)` is an extremely advanced use case that's not worth documenting or supporting in the public API at this time. + */ +export function watchChains( + config: config, + parameters: WatchChainsParameters, +): WatchChainsReturnType { + const { onChange } = parameters + return config._internal.chains.subscribe((chains, prevChains) => { + onChange( + chains as unknown as GetChainsReturnType, + prevChains as unknown as GetChainsReturnType, + ) + }) +} diff --git a/packages/core/src/actions/watchClient.test-d.ts b/packages/core/src/actions/watchClient.test-d.ts new file mode 100644 index 0000000000..42830f02c3 --- /dev/null +++ b/packages/core/src/actions/watchClient.test-d.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { watchClient } from './watchClient.js' + +test('default', () => { + watchClient(config, { + onChange(client) { + expectTypeOf(client.chain).toEqualTypeOf< + (typeof config)['chains'][number] + >() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() + }, + }) +}) diff --git a/packages/core/src/actions/watchClient.test.ts b/packages/core/src/actions/watchClient.test.ts new file mode 100644 index 0000000000..9cb5f447d2 --- /dev/null +++ b/packages/core/src/actions/watchClient.test.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import type { Client } from 'viem' +import { expect, test } from 'vitest' + +import { switchChain } from './switchChain.js' +import { watchClient } from './watchClient.js' + +test('default', async () => { + const clients: Client[] = [] + const unwatch = watchClient(config, { + onChange(client) { + clients.push(client) + }, + }) + + switchChain(config, { chainId: 456 }) + switchChain(config, { chainId: 10 }) + switchChain(config, { chainId: 1 }) + + expect(clients.length).toBe(3) + + unwatch() +}) diff --git a/packages/core/src/actions/watchClient.ts b/packages/core/src/actions/watchClient.ts new file mode 100644 index 0000000000..4247353537 --- /dev/null +++ b/packages/core/src/actions/watchClient.ts @@ -0,0 +1,35 @@ +import type { Config } from '../createConfig.js' +import { type GetClientReturnType, getClient } from './getClient.js' + +export type WatchClientParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = { + onChange( + publicClient: GetClientReturnType, + prevClient: GetClientReturnType, + ): void +} + +export type WatchClientReturnType = () => void + +/** https://wagmi.sh/core/api/actions/watchClient */ +export function watchClient< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: WatchClientParameters, +): WatchClientReturnType { + const { onChange } = parameters + return config.subscribe( + () => getClient(config) as GetClientReturnType, + onChange, + { + equalityFn(a, b) { + return a?.uid === b?.uid + }, + }, + ) +} diff --git a/packages/core/src/actions/watchConnections.test.ts b/packages/core/src/actions/watchConnections.test.ts new file mode 100644 index 0000000000..9ddf4444ab --- /dev/null +++ b/packages/core/src/actions/watchConnections.test.ts @@ -0,0 +1,25 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import type { Connection } from '../createConfig.js' +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { watchConnections } from './watchConnections.js' + +test('default', async () => { + const connections: Connection[][] = [] + const unwatch = watchConnections(config, { + onChange(connection) { + connections.push(connection) + }, + }) + + const connector = config.connectors[0]! + expect(connections).toEqual([]) + await connect(config, { connector }) + expect(connections[0]?.length).toEqual(1) + await disconnect(config, { connector }) + expect(connections[1]).toEqual([]) + + unwatch() +}) diff --git a/packages/core/src/actions/watchConnections.ts b/packages/core/src/actions/watchConnections.ts new file mode 100644 index 0000000000..56b94fd455 --- /dev/null +++ b/packages/core/src/actions/watchConnections.ts @@ -0,0 +1,26 @@ +import type { Config } from '../createConfig.js' +import { deepEqual } from '../utils/deepEqual.js' +import { + type GetConnectionsReturnType, + getConnections, +} from './getConnections.js' + +export type WatchConnectionsParameters = { + onChange( + connections: GetConnectionsReturnType, + prevConnections: GetConnectionsReturnType, + ): void +} + +export type WatchConnectionsReturnType = () => void + +/** https://wagmi.sh/core/api/actions/watchConnections */ +export function watchConnections( + config: Config, + parameters: WatchConnectionsParameters, +): WatchConnectionsReturnType { + const { onChange } = parameters + return config.subscribe(() => getConnections(config), onChange, { + equalityFn: deepEqual, + }) +} diff --git a/packages/core/src/actions/watchConnectors.test.ts b/packages/core/src/actions/watchConnectors.test.ts new file mode 100644 index 0000000000..6e66f75f93 --- /dev/null +++ b/packages/core/src/actions/watchConnectors.test.ts @@ -0,0 +1,27 @@ +import { accounts, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { mock } from '../connectors/mock.js' +import type { Connector } from '../createConfig.js' +import { watchConnectors } from './watchConnectors.js' + +test('default', async () => { + const connectors: (readonly Connector[])[] = [] + const unwatch = watchConnectors(config, { + onChange(connector) { + connectors.push(connector) + }, + }) + + const count = config.connectors.length + expect(config.connectors).toEqual(config.connectors) + + config._internal.connectors.setState(() => [ + ...config.connectors, + config._internal.connectors.setup(mock({ accounts })), + ]) + + expect(config.connectors.length).toBe(count + 1) + + unwatch() +}) diff --git a/packages/core/src/actions/watchConnectors.ts b/packages/core/src/actions/watchConnectors.ts new file mode 100644 index 0000000000..e463ab52fa --- /dev/null +++ b/packages/core/src/actions/watchConnectors.ts @@ -0,0 +1,22 @@ +import type { Config } from '../createConfig.js' +import type { GetConnectorsReturnType } from './getConnectors.js' + +export type WatchConnectorsParameters = { + onChange( + connections: GetConnectorsReturnType, + prevConnectors: GetConnectorsReturnType, + ): void +} + +export type WatchConnectorsReturnType = () => void + +/** https://wagmi.sh/core/api/actions/watchConnectors */ +export function watchConnectors( + config: config, + parameters: WatchConnectorsParameters, +): WatchConnectorsReturnType { + const { onChange } = parameters + return config._internal.connectors.subscribe((connectors, prevConnectors) => { + onChange(Object.values(connectors), prevConnectors) + }) +} diff --git a/packages/core/src/actions/watchContractEvent.test-d.ts b/packages/core/src/actions/watchContractEvent.test-d.ts new file mode 100644 index 0000000000..e6624fff57 --- /dev/null +++ b/packages/core/src/actions/watchContractEvent.test-d.ts @@ -0,0 +1,142 @@ +import { abi, config } from '@wagmi/test' +import { http, webSocket } from 'viem' +import { mainnet, optimism } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type WatchContractEventParameters, + watchContractEvent, +} from './watchContractEvent.js' + +test('default', () => { + watchContractEvent(config, { + address: '0x', + abi: abi.erc20, + eventName: 'Transfer', + args: { + from: '0x', + to: '0x', + }, + onLogs(logs) { + expectTypeOf(logs[0]!.eventName).toEqualTypeOf<'Transfer'>() + expectTypeOf(logs[0]!.args).toEqualTypeOf<{ + from?: `0x${string}` | undefined + to?: `0x${string}` | undefined + value?: bigint | undefined + }>() + }, + }) +}) + +test('behavior: no eventName', () => { + type Result = WatchContractEventParameters< + typeof abi.erc20, + undefined, + true, + typeof config + > + expectTypeOf().toEqualTypeOf< + | { + from?: `0x${string}` | `0x${string}`[] | null | undefined + to?: `0x${string}` | `0x${string}`[] | null | undefined + } + | { + owner?: `0x${string}` | `0x${string}`[] | null | undefined + spender?: `0x${string}` | `0x${string}`[] | null | undefined + } + | undefined + >() + + watchContractEvent(config, { + address: '0x', + abi: abi.erc20, + args: { + // TODO: Figure out why this is not working + // @ts-ignore + from: '0x', + to: '0x', + }, + onLogs(logs) { + expectTypeOf(logs[0]!.eventName).toEqualTypeOf<'Transfer' | 'Approval'>() + expectTypeOf(logs[0]!.args).toEqualTypeOf< + | Record + | readonly unknown[] + | { + from?: `0x${string}` | undefined + to?: `0x${string}` | undefined + value?: bigint | undefined + } + | { + owner?: `0x${string}` | undefined + spender?: `0x${string}` | undefined + value?: bigint | undefined + } + >() + }, + }) +}) + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + type Result = WatchContractEventParameters< + typeof abi.erc20, + 'Transfer' | 'Approval', + true, + typeof config, + typeof mainnet.id | typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + watchContractEvent(config, { + poll: false, + address: '0x', + abi: abi.erc20, + onLogs() {}, + }) + + type Result2 = WatchContractEventParameters< + typeof abi.erc20, + 'Transfer' | 'Approval', + true, + typeof config, + typeof mainnet.id + > + expectTypeOf().toEqualTypeOf() + watchContractEvent(config, { + chainId: mainnet.id, + poll: true, + address: '0x', + abi: abi.erc20, + onLogs() {}, + }) + + type Result3 = WatchContractEventParameters< + typeof abi.erc20, + 'Transfer' | 'Approval', + true, + typeof config, + typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + watchContractEvent(config, { + chainId: optimism.id, + poll: true, + address: '0x', + abi: abi.erc20, + onLogs() {}, + }) + watchContractEvent(config, { + chainId: optimism.id, + poll: false, + address: '0x', + abi: abi.erc20, + onLogs() {}, + }) +}) diff --git a/packages/core/src/actions/watchContractEvent.test.ts b/packages/core/src/actions/watchContractEvent.test.ts new file mode 100644 index 0000000000..759a21dd19 --- /dev/null +++ b/packages/core/src/actions/watchContractEvent.test.ts @@ -0,0 +1,96 @@ +import { + abi, + accounts, + address, + config, + testClient, + transactionHashRegex, + wait, +} from '@wagmi/test' +import { http, createWalletClient, parseEther } from 'viem' +import type { WatchEventOnLogsParameter } from 'viem/actions' +import { beforeEach, expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { getBalance } from './getBalance.js' +import { watchContractEvent } from './watchContractEvent.js' +import { writeContract } from './writeContract.js' + +const connector = config.connectors[0]! + +// TODO: Some test does not call disconnect after finishing. Remove once fixing it. +beforeEach(async () => { + if (config.state.current) { + const connection = config.state.connections.get(config.state.current)! + const connector = connection.connector + await disconnect(config, { connector }) + } +}) + +test('default', async () => { + const data = await connect(config, { connector }) + const connectedAddress = data.accounts[0] + + // impersonate usdc holder account and transfer usdc to connected account + await testClient.mainnet.impersonateAccount({ address: address.usdcHolder }) + await testClient.mainnet.setBalance({ + address: address.usdcHolder, + value: 10000000000000000000000n, + }) + await createWalletClient({ + account: address.usdcHolder, + chain: testClient.mainnet.chain, + transport: http(), + }).writeContract({ + address: address.usdc, + abi: abi.erc20, + functionName: 'transfer', + args: [connectedAddress, parseEther('10', 'gwei')], + }) + await testClient.mainnet.mine({ blocks: 1 }) + await testClient.mainnet.stopImpersonatingAccount({ + address: address.usdcHolder, + }) + + const balance = await getBalance(config, { + address: connectedAddress, + token: address.usdc, + }) + expect(balance.value).toBeGreaterThan(0n) + + // start watching transfer events + let logs: WatchEventOnLogsParameter = [] + const unwatch = watchContractEvent(config, { + address: address.usdc, + abi: abi.erc20, + eventName: 'Transfer', + onLogs(next) { + logs = logs.concat(next) + }, + }) + + await writeContract(config, { + address: address.usdc, + abi: abi.erc20, + functionName: 'transfer', + args: [accounts[1], parseEther('1', 'gwei')], + }) + + await writeContract(config, { + address: address.usdc, + abi: abi.erc20, + functionName: 'transfer', + args: [accounts[3], parseEther('1', 'gwei')], + }) + + await testClient.mainnet.mine({ blocks: 1 }) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(1000) // wait for events to be emitted + + unwatch() + expect(logs.length).toBe(2) + expect(logs[0]?.transactionHash).toMatch(transactionHashRegex) + + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/watchContractEvent.ts b/packages/core/src/actions/watchContractEvent.ts new file mode 100644 index 0000000000..ddc15741da --- /dev/null +++ b/packages/core/src/actions/watchContractEvent.ts @@ -0,0 +1,102 @@ +import type { + Abi, + Chain, + ContractEventName, + Transport, + WebSocketTransport, +} from 'viem' +import { + type WatchContractEventParameters as viem_WatchContractEventParameters, + type WatchContractEventReturnType as viem_WatchContractEventReturnType, + watchContractEvent as viem_watchContractEvent, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + SyncConnectedChainParameter, +} from '../types/properties.js' +import type { UnionCompute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type WatchContractEventParameters< + abi extends Abi | readonly unknown[] = Abi, + eventName extends ContractEventName | undefined = ContractEventName, + strict extends boolean | undefined = undefined, + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: UnionCompute< + viem_WatchContractEventParameters< + abi, + eventName, + strict, + config['_internal']['transports'][chains[key]['id']] extends infer transport extends + Transport + ? Transport extends transport + ? WebSocketTransport + : transport + : WebSocketTransport + > & + ChainIdParameter & + SyncConnectedChainParameter + > +}[number] + +export type WatchContractEventReturnType = viem_WatchContractEventReturnType + +// TODO: wrap in viem's `observe` to avoid duplicate invocations. +/** https://wagmi.sh/core/api/actions/watchContractEvent */ +export function watchContractEvent< + config extends Config, + chainId extends config['chains'][number]['id'], + const abi extends Abi | readonly unknown[], + eventName extends ContractEventName | undefined, + strict extends boolean | undefined = undefined, +>( + config: config, + parameters: WatchContractEventParameters< + abi, + eventName, + strict, + config, + chainId + >, +) { + const { syncConnectedChain = config._internal.syncConnectedChain, ...rest } = + parameters + + let unwatch: WatchContractEventReturnType | undefined + const listener = (chainId: number | undefined) => { + if (unwatch) unwatch() + + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_watchContractEvent, + 'watchContractEvent', + ) + unwatch = action(rest as unknown as viem_WatchContractEventParameters) + return unwatch + } + + // set up listener for transaction changes + const unlisten = listener(parameters.chainId) + + // set up subscriber for connected chain changes + let unsubscribe: (() => void) | undefined + if (syncConnectedChain && !parameters.chainId) + unsubscribe = config.subscribe( + ({ chainId }) => chainId, + async (chainId) => listener(chainId), + ) + + return () => { + unlisten?.() + unsubscribe?.() + } +} diff --git a/packages/core/src/actions/watchPendingTransactions.test-d.ts b/packages/core/src/actions/watchPendingTransactions.test-d.ts new file mode 100644 index 0000000000..0af40a24c5 --- /dev/null +++ b/packages/core/src/actions/watchPendingTransactions.test-d.ts @@ -0,0 +1,56 @@ +import { http, webSocket } from 'viem' +import { mainnet, optimism } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type WatchPendingTransactionsParameters, + watchPendingTransactions, +} from './watchPendingTransactions.js' + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + type Result = WatchPendingTransactionsParameters< + typeof config, + typeof mainnet.id | typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + watchPendingTransactions(config, { + poll: false, + onTransactions() {}, + }) + + type Result2 = WatchPendingTransactionsParameters< + typeof config, + typeof mainnet.id + > + expectTypeOf().toEqualTypeOf() + watchPendingTransactions(config, { + chainId: mainnet.id, + poll: true, + onTransactions() {}, + }) + + type Result3 = WatchPendingTransactionsParameters< + typeof config, + typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + watchPendingTransactions(config, { + chainId: optimism.id, + poll: true, + onTransactions() {}, + }) + watchPendingTransactions(config, { + chainId: optimism.id, + poll: false, + onTransactions() {}, + }) +}) diff --git a/packages/core/src/actions/watchPendingTransactions.test.ts b/packages/core/src/actions/watchPendingTransactions.test.ts new file mode 100644 index 0000000000..510b9acce3 --- /dev/null +++ b/packages/core/src/actions/watchPendingTransactions.test.ts @@ -0,0 +1,49 @@ +import { + accounts, + config, + testClient, + transactionHashRegex, + wait, +} from '@wagmi/test' +import { parseEther } from 'viem' +import type { OnTransactionsParameter } from 'viem/actions' +import { expect, test } from 'vitest' + +import { connect } from './connect.js' +import { disconnect } from './disconnect.js' +import { sendTransaction } from './sendTransaction.js' +import { watchPendingTransactions } from './watchPendingTransactions.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + let transactions: OnTransactionsParameter = [] + const unwatch = watchPendingTransactions(config, { + onTransactions(next) { + transactions = [...transactions, ...next] + }, + }) + await wait(500) + + await sendTransaction(config, { + to: accounts[1], + value: parseEther('1'), + }) + await wait(100) + + await sendTransaction(config, { + to: accounts[3], + value: parseEther('1'), + }) + await wait(100) + + await testClient.mainnet.mine({ blocks: 1 }) + + unwatch() + expect(transactions.length).toBe(2) + expect(transactions[0]).toMatch(transactionHashRegex) + + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/actions/watchPendingTransactions.ts b/packages/core/src/actions/watchPendingTransactions.ts new file mode 100644 index 0000000000..29f0354501 --- /dev/null +++ b/packages/core/src/actions/watchPendingTransactions.ts @@ -0,0 +1,82 @@ +import type { Chain, Transport, WebSocketTransport } from 'viem' +import { + type WatchPendingTransactionsParameters as viem_WatchPendingTransactionsParameters, + type WatchPendingTransactionsReturnType as viem_WatchPendingTransactionsReturnType, + watchPendingTransactions as viem_watchPendingTransactions, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + SyncConnectedChainParameter, +} from '../types/properties.js' +import type { UnionCompute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' + +export type WatchPendingTransactionsParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: UnionCompute< + viem_WatchPendingTransactionsParameters< + config['_internal']['transports'][chains[key]['id']] extends infer transport extends + Transport + ? Transport extends transport + ? WebSocketTransport + : transport + : WebSocketTransport + > & + ChainIdParameter & + SyncConnectedChainParameter + > +}[number] + +export type WatchPendingTransactionsReturnType = + viem_WatchPendingTransactionsReturnType + +// TODO: wrap in viem's `observe` to avoid duplicate invocations. +/** https://wagmi.sh/core/api/actions/watchPendingTransactions */ +export function watchPendingTransactions< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: WatchPendingTransactionsParameters, +) { + const { syncConnectedChain = config._internal.syncConnectedChain, ...rest } = + parameters + + let unwatch: WatchPendingTransactionsReturnType | undefined + const listener = (chainId: number | undefined) => { + if (unwatch) unwatch() + + const client = config.getClient({ chainId }) + const action = getAction( + client, + viem_watchPendingTransactions, + 'watchPendingTransactions', + ) + unwatch = action(rest as viem_WatchPendingTransactionsParameters) + return unwatch + } + + // set up listener for transaction changes + const unlisten = listener(parameters.chainId) + + // set up subscriber for connected chain changes + let unsubscribe: (() => void) | undefined + if (syncConnectedChain && !parameters.chainId) + unsubscribe = config.subscribe( + ({ chainId }) => chainId, + async (chainId) => listener(chainId), + ) + + return () => { + unlisten?.() + unsubscribe?.() + } +} diff --git a/packages/core/src/actions/watchPublicClient.test-d.ts b/packages/core/src/actions/watchPublicClient.test-d.ts new file mode 100644 index 0000000000..a7353636cb --- /dev/null +++ b/packages/core/src/actions/watchPublicClient.test-d.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { watchPublicClient } from './watchPublicClient.js' + +test('default', () => { + watchPublicClient(config, { + onChange(client) { + expectTypeOf(client.chain).toEqualTypeOf< + (typeof config)['chains'][number] + >() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() + }, + }) +}) diff --git a/packages/core/src/actions/watchPublicClient.test.ts b/packages/core/src/actions/watchPublicClient.test.ts new file mode 100644 index 0000000000..3d9594002c --- /dev/null +++ b/packages/core/src/actions/watchPublicClient.test.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import type { Client } from 'viem' +import { expect, test } from 'vitest' + +import { switchChain } from './switchChain.js' +import { watchPublicClient } from './watchPublicClient.js' + +test('default', async () => { + const clients: Client[] = [] + const unwatch = watchPublicClient(config, { + onChange(client) { + clients.push(client) + }, + }) + + switchChain(config, { chainId: 456 }) + switchChain(config, { chainId: 10 }) + switchChain(config, { chainId: 1 }) + + expect(clients.length).toBe(3) + + unwatch() +}) diff --git a/packages/core/src/actions/watchPublicClient.ts b/packages/core/src/actions/watchPublicClient.ts new file mode 100644 index 0000000000..b7d854c2ad --- /dev/null +++ b/packages/core/src/actions/watchPublicClient.ts @@ -0,0 +1,38 @@ +import type { Config } from '../createConfig.js' +import { + type GetPublicClientReturnType, + getPublicClient, +} from './getPublicClient.js' + +export type WatchPublicClientParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = { + onChange( + publicClient: GetPublicClientReturnType, + prevPublicClient: GetPublicClientReturnType, + ): void +} + +export type WatchPublicClientReturnType = () => void + +/** https://wagmi.sh/core/api/actions/watchPublicClient */ +export function watchPublicClient< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: WatchPublicClientParameters, +): WatchPublicClientReturnType { + const { onChange } = parameters + return config.subscribe( + () => getPublicClient(config) as GetPublicClientReturnType, + onChange, + { + equalityFn(a, b) { + return a?.uid === b?.uid + }, + }, + ) +} diff --git a/packages/core/src/actions/writeContract.test-d.ts b/packages/core/src/actions/writeContract.test-d.ts new file mode 100644 index 0000000000..68009a3773 --- /dev/null +++ b/packages/core/src/actions/writeContract.test-d.ts @@ -0,0 +1,152 @@ +import { abi, config } from '@wagmi/test' +import { http, type Address, parseAbi } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { simulateContract } from './simulateContract.js' +import { type WriteContractParameters, writeContract } from './writeContract.js' + +test('default', async () => { + await writeContract(config, { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + }) +}) + +test('simulateContract', async () => { + const { request } = await simulateContract(config, { + account: '0x', + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + }) + await writeContract(config, request) + await writeContract(config, { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + }) +}) + +test('chain formatters', () => { + const config = createConfig({ + chains: [mainnet, celo], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = WriteContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config + > + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + writeContract(config, { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + feeCurrency: '0x', + }) + + type Result2 = WriteContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof celo.id + > + expectTypeOf().toMatchTypeOf<{ + functionName: 'approve' | 'transfer' | 'transferFrom' + args: readonly [Address, Address, bigint] + feeCurrency?: `0x${string}` | undefined + }>() + writeContract(config, { + chainId: celo.id, + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + feeCurrency: '0x', + }) + + type Result3 = WriteContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof mainnet.id + > + expectTypeOf().toMatchTypeOf<{ + functionName: 'approve' | 'transfer' | 'transferFrom' + args: readonly [Address, Address, bigint] + }>() + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + writeContract(config, { + chainId: mainnet.id, + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('overloads', async () => { + const abi = parseAbi([ + 'function foo() returns (int8)', + 'function foo(address) returns (string)', + 'function foo(address, address) returns ((address foo, address bar))', + 'function bar(uint256) returns (int8)', + ]) + + type Result = WriteContractParameters + expectTypeOf().toEqualTypeOf<'foo' | 'bar'>() + expectTypeOf().toEqualTypeOf< + | readonly [] + | readonly [`0x${string}`] + | readonly [`0x${string}`, `0x${string}`] + | undefined + >() + writeContract(config, { + address: '0x', + abi, + functionName: 'foo', + }) + writeContract(config, { + address: '0x', + abi, + functionName: 'foo', + args: ['0x'], + }) + writeContract(config, { + address: '0x', + abi, + functionName: 'foo', + args: ['0x', '0x'], + }) + writeContract(config, { + address: '0x', + abi, + functionName: 'foo', + // @ts-expect-error + args: ['0x', 123n], + }) + + type Result2 = WriteContractParameters + expectTypeOf().toEqualTypeOf<'foo' | 'bar'>() + expectTypeOf().toEqualTypeOf() +}) diff --git a/packages/core/src/actions/writeContract.ts b/packages/core/src/actions/writeContract.ts new file mode 100644 index 0000000000..5fb6d87104 --- /dev/null +++ b/packages/core/src/actions/writeContract.ts @@ -0,0 +1,114 @@ +import type { + Abi, + Account, + Chain, + Client, + ContractFunctionArgs, + ContractFunctionName, +} from 'viem' +import { + type WriteContractErrorType as viem_WriteContractErrorType, + type WriteContractParameters as viem_WriteContractParameters, + type WriteContractReturnType as viem_WriteContractReturnType, + writeContract as viem_writeContract, +} from 'viem/actions' + +import type { Config } from '../createConfig.js' +import type { BaseErrorType, ErrorType } from '../errors/base.js' +import type { SelectChains } from '../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../types/properties.js' +import type { Compute, UnionCompute } from '../types/utils.js' +import { getAction } from '../utils/getAction.js' +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from './getConnectorClient.js' + +export type WriteContractParameters< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'nonpayable' | 'payable' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + > = ContractFunctionArgs, + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + allFunctionNames = ContractFunctionName, + chains extends readonly Chain[] = SelectChains, +> = UnionCompute< + { + // TODO: Should use `UnionStrictOmit<..., 'chain'>` on `viem_WriteContractParameters` result instead + // temp workaround that doesn't affect runtime behavior for for https://github.com/wevm/wagmi/issues/3981 + [key in keyof chains]: viem_WriteContractParameters< + abi, + functionName, + args, + chains[key], + Account, + chains[key], + allFunctionNames + > + }[number] & + Compute> & + ConnectorParameter & { + /** @deprecated */ + __mode?: 'prepared' + } +> + +export type WriteContractReturnType = viem_WriteContractReturnType + +export type WriteContractErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_WriteContractErrorType + +/** https://wagmi.sh/core/api/actions/writeContract */ +export async function writeContract< + config extends Config, + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: WriteContractParameters, +): Promise { + const { account, chainId, connector, ...request } = parameters + + let client: Client + if (typeof account === 'object' && account?.type === 'local') + client = config.getClient({ chainId }) + else + client = await getConnectorClient(config, { + account: account ?? undefined, + chainId, + connector, + }) + + const action = getAction(client, viem_writeContract, 'writeContract') + const hash = await action({ + ...(request as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : null, + }) + + return hash +} diff --git a/packages/core/src/connectors/createConnector.test.ts b/packages/core/src/connectors/createConnector.test.ts new file mode 100644 index 0000000000..d1ff9f20af --- /dev/null +++ b/packages/core/src/connectors/createConnector.test.ts @@ -0,0 +1,31 @@ +import type { Address } from 'viem' +import { test } from 'vitest' +import { createConnector } from './createConnector.js' + +test('default', () => { + createConnector(() => { + return { + id: 'test', + name: 'Test Connector', + type: 'test', + async setup() {}, + async connect() { + return { accounts: [] as Address[], chainId: 123 } + }, + async disconnect() {}, + async getAccounts() { + return [] + }, + async getChainId() { + return 123 + }, + async isAuthorized() { + return true + }, + onAccountsChanged() {}, + onChainChanged() {}, + async onDisconnect(_error) {}, + async getProvider() {}, + } + }) +}) diff --git a/packages/core/src/connectors/createConnector.ts b/packages/core/src/connectors/createConnector.ts new file mode 100644 index 0000000000..54b4d9955c --- /dev/null +++ b/packages/core/src/connectors/createConnector.ts @@ -0,0 +1,93 @@ +import type { + AddEthereumChainParameter, + Address, + Chain, + Client, + ProviderConnectInfo, + ProviderMessage, +} from 'viem' + +import type { Transport } from '../createConfig.js' +import type { Emitter } from '../createEmitter.js' +import type { Storage } from '../createStorage.js' +import type { Compute, ExactPartial, StrictOmit } from '../types/utils.js' + +export type ConnectorEventMap = { + change: { + accounts?: readonly Address[] | undefined + chainId?: number | undefined + } + connect: { accounts: readonly Address[]; chainId: number } + disconnect: never + error: { error: Error } + message: { type: string; data?: unknown | undefined } +} + +export type CreateConnectorFn< + provider = unknown, + properties extends Record = Record, + storageItem extends Record = Record, +> = (config: { + chains: readonly [Chain, ...Chain[]] + emitter: Emitter + storage?: Compute> | null | undefined + transports?: Record | undefined +}) => Compute< + { + readonly icon?: string | undefined + readonly id: string + readonly name: string + readonly rdns?: string | readonly string[] | undefined + /** @deprecated */ + readonly supportsSimulation?: boolean | undefined + readonly type: string + + setup?(): Promise + connect( + parameters?: + | { chainId?: number | undefined; isReconnecting?: boolean | undefined } + | undefined, + ): Promise<{ + accounts: readonly Address[] + chainId: number + }> + disconnect(): Promise + getAccounts(): Promise + getChainId(): Promise + getProvider( + parameters?: { chainId?: number | undefined } | undefined, + ): Promise + getClient?( + parameters?: { chainId?: number | undefined } | undefined, + ): Promise + isAuthorized(): Promise + switchChain?( + parameters: Compute<{ + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + }>, + ): Promise + + onAccountsChanged(accounts: string[]): void + onChainChanged(chainId: string): void + onConnect?(connectInfo: ProviderConnectInfo): void + onDisconnect(error?: Error | undefined): void + onMessage?(message: ProviderMessage): void + } & properties +> + +export function createConnector< + provider, + properties extends Record = Record, + storageItem extends Record = Record, + /// + createConnectorFn extends CreateConnectorFn< + provider, + properties, + storageItem + > = CreateConnectorFn, +>(createConnectorFn: createConnectorFn) { + return createConnectorFn +} diff --git a/packages/core/src/connectors/injected.test.ts b/packages/core/src/connectors/injected.test.ts new file mode 100644 index 0000000000..9188a53bae --- /dev/null +++ b/packages/core/src/connectors/injected.test.ts @@ -0,0 +1,25 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { injected } from './injected.js' + +test('setup', () => { + const connectorFn = injected() + const connector = config._internal.connectors.setup(connectorFn) + expect(connector.name).toEqual('Injected') +}) + +test.each([ + { wallet: undefined, expected: 'Injected' }, + { wallet: 'coinbaseWallet', expected: 'Coinbase Wallet' }, + { wallet: 'metaMask', expected: 'MetaMask' }, + { wallet: 'phantom', expected: 'Phantom' }, + { wallet: 'rainbow', expected: 'Rainbow' }, +] as const satisfies readonly { + wallet: string | undefined + expected: string +}[])('injected({ wallet: $wallet })', ({ wallet, expected }) => { + const connectorFn = injected({ target: wallet }) + const connector = config._internal.connectors.setup(connectorFn) + expect(connector.name).toEqual(expected) +}) diff --git a/packages/core/src/connectors/injected.ts b/packages/core/src/connectors/injected.ts new file mode 100644 index 0000000000..d686d0aa29 --- /dev/null +++ b/packages/core/src/connectors/injected.ts @@ -0,0 +1,697 @@ +import { + type AddEthereumChainParameter, + type Address, + type EIP1193Provider, + type ProviderConnectInfo, + type ProviderRpcError, + ResourceUnavailableRpcError, + type RpcError, + SwitchChainError, + UserRejectedRequestError, + getAddress, + numberToHex, + withRetry, + withTimeout, +} from 'viem' + +import type { Connector } from '../createConfig.js' +import { ChainNotConfiguredError } from '../errors/config.js' +import { ProviderNotFoundError } from '../errors/connector.js' +import type { Compute } from '../types/utils.js' +import { createConnector } from './createConnector.js' + +export type InjectedParameters = { + /** + * Some injected providers do not support programmatic disconnect. + * This flag simulates the disconnect behavior by keeping track of connection status in storage. + * @default true + */ + shimDisconnect?: boolean | undefined + /** + * [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) Ethereum Provider to target + */ + target?: TargetId | Target | (() => Target | undefined) | undefined + unstable_shimAsyncInject?: boolean | number | undefined +} + +injected.type = 'injected' as const +export function injected(parameters: InjectedParameters = {}) { + const { shimDisconnect = true, unstable_shimAsyncInject } = parameters + + function getTarget(): Compute { + const target = parameters.target + if (typeof target === 'function') { + const result = target() + if (result) return result + } + + if (typeof target === 'object') return target + + if (typeof target === 'string') + return { + ...(targetMap[target as keyof typeof targetMap] ?? { + id: target, + name: `${target[0]!.toUpperCase()}${target.slice(1)}`, + provider: `is${target[0]!.toUpperCase()}${target.slice(1)}`, + }), + } + + return { + id: 'injected', + name: 'Injected', + provider(window) { + return window?.ethereum + }, + } + } + + type Provider = WalletProvider | undefined + type Properties = { + onConnect(connectInfo: ProviderConnectInfo): void + } + type StorageItem = { + [_ in 'injected.connected' | `${string}.disconnected`]: true + } + + let accountsChanged: Connector['onAccountsChanged'] | undefined + let chainChanged: Connector['onChainChanged'] | undefined + let connect: Connector['onConnect'] | undefined + let disconnect: Connector['onDisconnect'] | undefined + + return createConnector((config) => ({ + get icon() { + return getTarget().icon + }, + get id() { + return getTarget().id + }, + get name() { + return getTarget().name + }, + /** @deprecated */ + get supportsSimulation() { + return true + }, + type: injected.type, + async setup() { + const provider = await this.getProvider() + // Only start listening for events if `target` is set, otherwise `injected()` will also receive events + if (provider?.on && parameters.target) { + if (!connect) { + connect = this.onConnect.bind(this) + provider.on('connect', connect) + } + + // We shouldn't need to listen for `'accountsChanged'` here since the `'connect'` event should suffice (and wallet shouldn't be connected yet). + // Some wallets, like MetaMask, do not implement the `'connect'` event and overload `'accountsChanged'` instead. + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged) + } + } + }, + async connect({ chainId, isReconnecting } = {}) { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + + let accounts: readonly Address[] = [] + if (isReconnecting) accounts = await this.getAccounts().catch(() => []) + else if (shimDisconnect) { + // Attempt to show another prompt for selecting account if `shimDisconnect` flag is enabled + try { + const permissions = await provider.request({ + method: 'wallet_requestPermissions', + params: [{ eth_accounts: {} }], + }) + accounts = (permissions[0]?.caveats?.[0]?.value as string[])?.map( + (x) => getAddress(x), + ) + // `'wallet_requestPermissions'` can return a different order of accounts than `'eth_accounts'` + // switch to `'eth_accounts'` ordering if more than one account is connected + // https://github.com/wevm/wagmi/issues/4140 + if (accounts.length > 0) { + const sortedAccounts = await this.getAccounts() + accounts = sortedAccounts + } + } catch (err) { + const error = err as RpcError + // Not all injected providers support `wallet_requestPermissions` (e.g. MetaMask iOS). + // Only bubble up error if user rejects request + if (error.code === UserRejectedRequestError.code) + throw new UserRejectedRequestError(error) + // Or prompt is already open + if (error.code === ResourceUnavailableRpcError.code) throw error + } + } + + try { + if (!accounts?.length && !isReconnecting) { + const requestedAccounts = await provider.request({ + method: 'eth_requestAccounts', + }) + accounts = requestedAccounts.map((x) => getAddress(x)) + } + + // Manage EIP-1193 event listeners + // https://eips.ethereum.org/EIPS/eip-1193#events + if (connect) { + provider.removeListener('connect', connect) + connect = undefined + } + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged) + } + if (!chainChanged) { + chainChanged = this.onChainChanged.bind(this) + provider.on('chainChanged', chainChanged) + } + if (!disconnect) { + disconnect = this.onDisconnect.bind(this) + provider.on('disconnect', disconnect) + } + + // Switch to chain if provided + let currentChainId = await this.getChainId() + if (chainId && currentChainId !== chainId) { + const chain = await this.switchChain!({ chainId }).catch((error) => { + if (error.code === UserRejectedRequestError.code) throw error + return { id: currentChainId } + }) + currentChainId = chain?.id ?? currentChainId + } + + // Remove disconnected shim if it exists + if (shimDisconnect) + await config.storage?.removeItem(`${this.id}.disconnected`) + + // Add connected shim if no target exists + if (!parameters.target) + await config.storage?.setItem('injected.connected', true) + + return { accounts, chainId: currentChainId } + } catch (err) { + const error = err as RpcError + if (error.code === UserRejectedRequestError.code) + throw new UserRejectedRequestError(error) + if (error.code === ResourceUnavailableRpcError.code) + throw new ResourceUnavailableRpcError(error) + throw error + } + }, + async disconnect() { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + + // Manage EIP-1193 event listeners + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + if (!connect) { + connect = this.onConnect.bind(this) + provider.on('connect', connect) + } + + // Experimental support for MetaMask disconnect + // https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-2.md + try { + // Adding timeout as not all wallets support this method and can hang + // https://github.com/wevm/wagmi/issues/4064 + await withTimeout( + () => + // TODO: Remove explicit type for viem@3 + provider.request<{ + Method: 'wallet_revokePermissions' + Parameters: [permissions: { eth_accounts: Record }] + ReturnType: null + }>({ + // `'wallet_revokePermissions'` added in `viem@2.10.3` + method: 'wallet_revokePermissions', + params: [{ eth_accounts: {} }], + }), + { timeout: 100 }, + ) + } catch {} + + // Add shim signalling connector is disconnected + if (shimDisconnect) { + await config.storage?.setItem(`${this.id}.disconnected`, true) + } + + if (!parameters.target) + await config.storage?.removeItem('injected.connected') + }, + async getAccounts() { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + const accounts = await provider.request({ method: 'eth_accounts' }) + return accounts.map((x) => getAddress(x)) + }, + async getChainId() { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + const hexChainId = await provider.request({ method: 'eth_chainId' }) + return Number(hexChainId) + }, + async getProvider() { + if (typeof window === 'undefined') return undefined + + let provider: Provider + const target = getTarget() + if (typeof target.provider === 'function') + provider = target.provider(window as Window | undefined) + else if (typeof target.provider === 'string') + provider = findProvider(window, target.provider) + else provider = target.provider + + // Some wallets do not conform to EIP-1193 (e.g. Trust Wallet) + // https://github.com/wevm/wagmi/issues/3526#issuecomment-1912683002 + if (provider && !provider.removeListener) { + // Try using `off` handler if it exists, otherwise noop + if ('off' in provider && typeof provider.off === 'function') + provider.removeListener = + provider.off as typeof provider.removeListener + else provider.removeListener = () => {} + } + + return provider + }, + async isAuthorized() { + try { + const isDisconnected = + shimDisconnect && + // If shim exists in storage, connector is disconnected + (await config.storage?.getItem(`${this.id}.disconnected`)) + if (isDisconnected) return false + + // Don't allow injected connector to connect if no target is set and it hasn't already connected + // (e.g. flag in storage is not set). This prevents a targetless injected connector from connecting + // automatically whenever there is a targeted connector configured. + if (!parameters.target) { + const connected = await config.storage?.getItem('injected.connected') + if (!connected) return false + } + + const provider = await this.getProvider() + if (!provider) { + if ( + unstable_shimAsyncInject !== undefined && + unstable_shimAsyncInject !== false + ) { + // If no provider is found, check for async injection + // https://github.com/wevm/references/issues/167 + // https://github.com/MetaMask/detect-provider + const handleEthereum = async () => { + if (typeof window !== 'undefined') + window.removeEventListener( + 'ethereum#initialized', + handleEthereum, + ) + const provider = await this.getProvider() + return !!provider + } + const timeout = + typeof unstable_shimAsyncInject === 'number' + ? unstable_shimAsyncInject + : 1_000 + const res = await Promise.race([ + ...(typeof window !== 'undefined' + ? [ + new Promise((resolve) => + window.addEventListener( + 'ethereum#initialized', + () => resolve(handleEthereum()), + { once: true }, + ), + ), + ] + : []), + new Promise((resolve) => + setTimeout(() => resolve(handleEthereum()), timeout), + ), + ]) + if (res) return true + } + + throw new ProviderNotFoundError() + } + + // Use retry strategy as some injected wallets (e.g. MetaMask) fail to + // immediately resolve JSON-RPC requests on page load. + const accounts = await withRetry(() => this.getAccounts()) + return !!accounts.length + } catch { + return false + } + }, + async switchChain({ addEthereumChainParameter, chainId }) { + const provider = await this.getProvider() + if (!provider) throw new ProviderNotFoundError() + + const chain = config.chains.find((x) => x.id === chainId) + if (!chain) throw new SwitchChainError(new ChainNotConfiguredError()) + + const promise = new Promise((resolve) => { + const listener = ((data) => { + if ('chainId' in data && data.chainId === chainId) { + config.emitter.off('change', listener) + resolve() + } + }) satisfies Parameters[1] + config.emitter.on('change', listener) + }) + + try { + await Promise.all([ + provider + .request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: numberToHex(chainId) }], + }) + // During `'wallet_switchEthereumChain'`, MetaMask makes a `'net_version'` RPC call to the target chain. + // If this request fails, MetaMask does not emit the `'chainChanged'` event, but will still switch the chain. + // To counter this behavior, we request and emit the current chain ID to confirm the chain switch either via + // this callback or an externally emitted `'chainChanged'` event. + // https://github.com/MetaMask/metamask-extension/issues/24247 + .then(async () => { + const currentChainId = await this.getChainId() + if (currentChainId === chainId) + config.emitter.emit('change', { chainId }) + }), + promise, + ]) + return chain + } catch (err) { + const error = err as RpcError + + // Indicates chain is not added to provider + if ( + error.code === 4902 || + // Unwrapping for MetaMask Mobile + // https://github.com/MetaMask/metamask-mobile/issues/2944#issuecomment-976988719 + (error as ProviderRpcError<{ originalError?: { code: number } }>) + ?.data?.originalError?.code === 4902 + ) { + try { + const { default: blockExplorer, ...blockExplorers } = + chain.blockExplorers ?? {} + let blockExplorerUrls: string[] | undefined + if (addEthereumChainParameter?.blockExplorerUrls) + blockExplorerUrls = addEthereumChainParameter.blockExplorerUrls + else if (blockExplorer) + blockExplorerUrls = [ + blockExplorer.url, + ...Object.values(blockExplorers).map((x) => x.url), + ] + + let rpcUrls: readonly string[] + if (addEthereumChainParameter?.rpcUrls?.length) + rpcUrls = addEthereumChainParameter.rpcUrls + else rpcUrls = [chain.rpcUrls.default?.http[0] ?? ''] + + const addEthereumChain = { + blockExplorerUrls, + chainId: numberToHex(chainId), + chainName: addEthereumChainParameter?.chainName ?? chain.name, + iconUrls: addEthereumChainParameter?.iconUrls, + nativeCurrency: + addEthereumChainParameter?.nativeCurrency ?? + chain.nativeCurrency, + rpcUrls, + } satisfies AddEthereumChainParameter + + await Promise.all([ + provider + .request({ + method: 'wallet_addEthereumChain', + params: [addEthereumChain], + }) + .then(async () => { + const currentChainId = await this.getChainId() + if (currentChainId === chainId) + config.emitter.emit('change', { chainId }) + else + throw new UserRejectedRequestError( + new Error('User rejected switch after adding network.'), + ) + }), + promise, + ]) + + return chain + } catch (error) { + throw new UserRejectedRequestError(error as Error) + } + } + + if (error.code === UserRejectedRequestError.code) + throw new UserRejectedRequestError(error) + throw new SwitchChainError(error) + } + }, + async onAccountsChanged(accounts) { + // Disconnect if there are no accounts + if (accounts.length === 0) this.onDisconnect() + // Connect if emitter is listening for connect event (e.g. is disconnected and connects through wallet interface) + else if (config.emitter.listenerCount('connect')) { + const chainId = (await this.getChainId()).toString() + this.onConnect({ chainId }) + // Remove disconnected shim if it exists + if (shimDisconnect) + await config.storage?.removeItem(`${this.id}.disconnected`) + } + // Regular change event + else + config.emitter.emit('change', { + accounts: accounts.map((x) => getAddress(x)), + }) + }, + onChainChanged(chain) { + const chainId = Number(chain) + config.emitter.emit('change', { chainId }) + }, + async onConnect(connectInfo) { + const accounts = await this.getAccounts() + if (accounts.length === 0) return + + const chainId = Number(connectInfo.chainId) + config.emitter.emit('connect', { accounts, chainId }) + + // Manage EIP-1193 event listeners + const provider = await this.getProvider() + if (provider) { + if (connect) { + provider.removeListener('connect', connect) + connect = undefined + } + if (!accountsChanged) { + accountsChanged = this.onAccountsChanged.bind(this) + provider.on('accountsChanged', accountsChanged) + } + if (!chainChanged) { + chainChanged = this.onChainChanged.bind(this) + provider.on('chainChanged', chainChanged) + } + if (!disconnect) { + disconnect = this.onDisconnect.bind(this) + provider.on('disconnect', disconnect) + } + } + }, + async onDisconnect(error) { + const provider = await this.getProvider() + + // If MetaMask emits a `code: 1013` error, wait for reconnection before disconnecting + // https://github.com/MetaMask/providers/pull/120 + if (error && (error as RpcError<1013>).code === 1013) { + if (provider && !!(await this.getAccounts()).length) return + } + + // No need to remove `${this.id}.disconnected` from storage because `onDisconnect` is typically + // only called when the wallet is disconnected through the wallet's interface, meaning the wallet + // actually disconnected and we don't need to simulate it. + config.emitter.emit('disconnect') + + // Manage EIP-1193 event listeners + if (provider) { + if (chainChanged) { + provider.removeListener('chainChanged', chainChanged) + chainChanged = undefined + } + if (disconnect) { + provider.removeListener('disconnect', disconnect) + disconnect = undefined + } + if (!connect) { + connect = this.onConnect.bind(this) + provider.on('connect', connect) + } + } + }, + })) +} + +const targetMap = { + coinbaseWallet: { + id: 'coinbaseWallet', + name: 'Coinbase Wallet', + provider(window) { + if (window?.coinbaseWalletExtension) return window.coinbaseWalletExtension + return findProvider(window, 'isCoinbaseWallet') + }, + }, + metaMask: { + id: 'metaMask', + name: 'MetaMask', + provider(window) { + return findProvider(window, (provider) => { + if (!provider.isMetaMask) return false + // Brave tries to make itself look like MetaMask + // Could also try RPC `web3_clientVersion` if following is unreliable + if (provider.isBraveWallet && !provider._events && !provider._state) + return false + // Other wallets that try to look like MetaMask + const flags = [ + 'isApexWallet', + 'isAvalanche', + 'isBitKeep', + 'isBlockWallet', + 'isKuCoinWallet', + 'isMathWallet', + 'isOkxWallet', + 'isOKExWallet', + 'isOneInchIOSWallet', + 'isOneInchAndroidWallet', + 'isOpera', + 'isPhantom', + 'isPortal', + 'isRabby', + 'isTokenPocket', + 'isTokenary', + 'isUniswapWallet', + 'isZerion', + ] satisfies WalletProviderFlags[] + for (const flag of flags) if (provider[flag]) return false + return true + }) + }, + }, + phantom: { + id: 'phantom', + name: 'Phantom', + provider(window) { + if (window?.phantom?.ethereum) return window.phantom?.ethereum + return findProvider(window, 'isPhantom') + }, + }, +} as const satisfies TargetMap + +type TargetMap = { [_ in TargetId]?: Target | undefined } + +type Target = { + icon?: string | undefined + id: string + name: string + provider: + | WalletProviderFlags + | WalletProvider + | ((window?: Window | undefined) => WalletProvider | undefined) +} + +/** @deprecated */ +type TargetId = Compute extends `is${infer name}` + ? name extends `${infer char}${infer rest}` + ? `${Lowercase}${rest}` + : never + : never + +/** + * @deprecated As of 2024/10/16, we are no longer accepting new provider flags as EIP-6963 should be used instead. + */ +type WalletProviderFlags = + | 'isApexWallet' + | 'isAvalanche' + | 'isBackpack' + | 'isBifrost' + | 'isBitKeep' + | 'isBitski' + | 'isBlockWallet' + | 'isBraveWallet' + | 'isCoinbaseWallet' + | 'isDawn' + | 'isEnkrypt' + | 'isExodus' + | 'isFrame' + | 'isFrontier' + | 'isGamestop' + | 'isHyperPay' + | 'isImToken' + | 'isKuCoinWallet' + | 'isMathWallet' + | 'isMetaMask' + | 'isOkxWallet' + | 'isOKExWallet' + | 'isOneInchAndroidWallet' + | 'isOneInchIOSWallet' + | 'isOpera' + | 'isPhantom' + | 'isPortal' + | 'isRabby' + | 'isRainbow' + | 'isStatus' + | 'isTally' + | 'isTokenPocket' + | 'isTokenary' + | 'isTrust' + | 'isTrustWallet' + | 'isUniswapWallet' + | 'isXDEFI' + | 'isZerion' + +type WalletProvider = Compute< + EIP1193Provider & { + [key in WalletProviderFlags]?: true | undefined + } & { + providers?: WalletProvider[] | undefined + /** Only exists in MetaMask as of 2022/04/03 */ + _events?: { connect?: (() => void) | undefined } | undefined + /** Only exists in MetaMask as of 2022/04/03 */ + _state?: + | { + accounts?: string[] + initialized?: boolean + isConnected?: boolean + isPermanentlyDisconnected?: boolean + isUnlocked?: boolean + } + | undefined + } +> + +type Window = { + coinbaseWalletExtension?: WalletProvider | undefined + ethereum?: WalletProvider | undefined + phantom?: { ethereum: WalletProvider } | undefined +} + +function findProvider( + window: globalThis.Window | Window | undefined, + select?: WalletProviderFlags | ((provider: WalletProvider) => boolean), +) { + function isProvider(provider: WalletProvider) { + if (typeof select === 'function') return select(provider) + if (typeof select === 'string') return provider[select] + return true + } + + const ethereum = (window as Window).ethereum + if (ethereum?.providers) + return ethereum.providers.find((provider) => isProvider(provider)) + if (ethereum && isProvider(ethereum)) return ethereum + return undefined +} diff --git a/packages/core/src/connectors/mock.test.ts b/packages/core/src/connectors/mock.test.ts new file mode 100644 index 0000000000..d8a812a3d7 --- /dev/null +++ b/packages/core/src/connectors/mock.test.ts @@ -0,0 +1,112 @@ +import { accounts, config } from '@wagmi/test' +import { expect, expectTypeOf, test } from 'vitest' + +import type { Connector } from '../createConfig.js' +import type { CreateConnectorFn } from './createConnector.js' +import { mock } from './mock.js' + +test('setup', () => { + const connectorFn = mock({ accounts }) + const connector = config._internal.connectors.setup(connectorFn) + expect(connector.name).toEqual('Mock Connector') + + expectTypeOf< + typeof connectorFn extends CreateConnectorFn ? true : false + >().toEqualTypeOf() + expectTypeOf< + typeof connector extends Connector ? true : false + >().toEqualTypeOf() + + type ConnectFnParameters = NonNullable< + Parameters<(typeof connector)['connect']>[0] + > + expectTypeOf().toMatchTypeOf() +}) + +test('behavior: features.connectError', () => { + const connectorFn = mock({ accounts, features: { connectError: true } }) + const connector = config._internal.connectors.setup(connectorFn) + expect(() => connector.connect()).rejects.toThrowErrorMatchingInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to connect. + Version: viem@2.29.2] + `) +}) + +test('behavior: connector.getProvider request errors', async () => { + const connectorFn = mock({ + accounts, + features: { + signMessageError: true, + signTypedDataError: true, + switchChainError: true, + watchAssetError: true, + }, + }) + const connector = config._internal.connectors.setup( + connectorFn, + ) as ReturnType + const provider = await connector.getProvider() + + expect( + provider.request({ + method: 'eth_signTypedData_v4', + params: [] as any, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to sign typed data. + Version: viem@2.29.2] + `) + + expect( + provider.request({ + method: 'wallet_switchEthereumChain', + params: [] as any, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to switch chain. + Version: viem@2.29.2] + `) + + expect( + provider.request({ + method: 'wallet_watchAsset', + params: [] as any, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to switch chain. + Version: viem@2.29.2] + `) + + expect( + provider.request({ + method: 'personal_sign', + params: [] as any, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [UserRejectedRequestError: User rejected the request. + + Details: Failed to sign message. + Version: viem@2.29.2] + `) +}) + +test('behavior: reconnect', async () => { + const connectorFn = mock({ + accounts, + features: { + defaultConnected: true, + reconnect: true, + }, + }) + const connector = config._internal.connectors.setup(connectorFn) + + await expect(connector.isAuthorized()).resolves.toBeTruthy() +}) diff --git a/packages/core/src/connectors/mock.ts b/packages/core/src/connectors/mock.ts new file mode 100644 index 0000000000..9d4dc9d846 --- /dev/null +++ b/packages/core/src/connectors/mock.ts @@ -0,0 +1,315 @@ +import { + type Address, + type EIP1193RequestFn, + type Hex, + RpcRequestError, + SwitchChainError, + type Transport, + UserRejectedRequestError, + type WalletCallReceipt, + type WalletGetCallsStatusReturnType, + type WalletRpcSchema, + custom, + fromHex, + getAddress, + keccak256, + numberToHex, + stringToHex, +} from 'viem' +import { rpc } from 'viem/utils' + +import { + ChainNotConfiguredError, + ConnectorNotConnectedError, +} from '../errors/config.js' +import { createConnector } from './createConnector.js' + +export type MockParameters = { + accounts: readonly [Address, ...Address[]] + features?: + | { + defaultConnected?: boolean | undefined + connectError?: boolean | Error | undefined + switchChainError?: boolean | Error | undefined + signMessageError?: boolean | Error | undefined + signTypedDataError?: boolean | Error | undefined + reconnect?: boolean | undefined + watchAssetError?: boolean | Error | undefined + } + | undefined +} + +mock.type = 'mock' as const +export function mock(parameters: MockParameters) { + const transactionCache = new Map() + const features = + parameters.features ?? + ({ defaultConnected: false } satisfies MockParameters['features']) + + type Provider = ReturnType< + Transport<'custom', unknown, EIP1193RequestFn> + > + type Properties = { + connect(parameters?: { + chainId?: number | undefined + isReconnecting?: boolean | undefined + foo?: string | undefined + }): Promise<{ + accounts: readonly Address[] + chainId: number + }> + } + let connected = features.defaultConnected + let connectedChainId: number + + return createConnector((config) => ({ + id: 'mock', + name: 'Mock Connector', + type: mock.type, + async setup() { + connectedChainId = config.chains[0].id + }, + async connect({ chainId } = {}) { + if (features.connectError) { + if (typeof features.connectError === 'boolean') + throw new UserRejectedRequestError(new Error('Failed to connect.')) + throw features.connectError + } + + const provider = await this.getProvider() + const accounts = await provider.request({ + method: 'eth_requestAccounts', + }) + + let currentChainId = await this.getChainId() + if (chainId && currentChainId !== chainId) { + const chain = await this.switchChain!({ chainId }) + currentChainId = chain.id + } + + connected = true + + return { + accounts: accounts.map((x) => getAddress(x)), + chainId: currentChainId, + } + }, + async disconnect() { + connected = false + }, + async getAccounts() { + if (!connected) throw new ConnectorNotConnectedError() + const provider = await this.getProvider() + const accounts = await provider.request({ method: 'eth_accounts' }) + return accounts.map((x) => getAddress(x)) + }, + async getChainId() { + const provider = await this.getProvider() + const hexChainId = await provider.request({ method: 'eth_chainId' }) + return fromHex(hexChainId, 'number') + }, + async isAuthorized() { + if (!features.reconnect) return false + if (!connected) return false + const accounts = await this.getAccounts() + return !!accounts.length + }, + async switchChain({ chainId }) { + const provider = await this.getProvider() + const chain = config.chains.find((x) => x.id === chainId) + if (!chain) throw new SwitchChainError(new ChainNotConfiguredError()) + + await provider.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: numberToHex(chainId) }], + }) + return chain + }, + onAccountsChanged(accounts) { + if (accounts.length === 0) this.onDisconnect() + else + config.emitter.emit('change', { + accounts: accounts.map((x) => getAddress(x)), + }) + }, + onChainChanged(chain) { + const chainId = Number(chain) + config.emitter.emit('change', { chainId }) + }, + async onDisconnect(_error) { + config.emitter.emit('disconnect') + connected = false + }, + async getProvider({ chainId } = {}) { + const chain = + config.chains.find((x) => x.id === chainId) ?? config.chains[0] + const url = chain.rpcUrls.default.http[0]! + + const request: EIP1193RequestFn = async ({ method, params }) => { + // eth methods + if (method === 'eth_chainId') return numberToHex(connectedChainId) + if (method === 'eth_requestAccounts') return parameters.accounts + if (method === 'eth_signTypedData_v4') + if (features.signTypedDataError) { + if (typeof features.signTypedDataError === 'boolean') + throw new UserRejectedRequestError( + new Error('Failed to sign typed data.'), + ) + throw features.signTypedDataError + } + + // wallet methods + if (method === 'wallet_switchEthereumChain') { + if (features.switchChainError) { + if (typeof features.switchChainError === 'boolean') + throw new UserRejectedRequestError( + new Error('Failed to switch chain.'), + ) + throw features.switchChainError + } + type Params = [{ chainId: Hex }] + connectedChainId = fromHex((params as Params)[0].chainId, 'number') + this.onChainChanged(connectedChainId.toString()) + return + } + + if (method === 'wallet_watchAsset') { + if (features.watchAssetError) { + if (typeof features.watchAssetError === 'boolean') + throw new UserRejectedRequestError( + new Error('Failed to switch chain.'), + ) + throw features.watchAssetError + } + return connected + } + + if (method === 'wallet_getCapabilities') + return { + '0x2105': { + paymasterService: { + supported: + (params as [Hex])[0] === + '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + }, + sessionKeys: { + supported: true, + }, + }, + '0x14A34': { + paymasterService: { + supported: + (params as [Hex])[0] === + '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + }, + }, + } + + if (method === 'wallet_sendCalls') { + const hashes = [] + const calls = (params as any)[0].calls + for (const call of calls) { + const { result, error } = await rpc.http(url, { + body: { + method: 'eth_sendTransaction', + params: [call], + }, + }) + if (error) + throw new RpcRequestError({ + body: { method, params }, + error, + url, + }) + hashes.push(result) + } + const id = keccak256(stringToHex(JSON.stringify(calls))) + transactionCache.set(id, hashes) + return { id } + } + + if (method === 'wallet_getCallsStatus') { + const hashes = transactionCache.get((params as any)[0]) + if (!hashes) + return { + atomic: false, + chainId: '0x1', + id: (params as any)[0], + status: 100, + receipts: [], + version: '2.0.0', + } satisfies WalletGetCallsStatusReturnType + + const receipts = await Promise.all( + hashes.map(async (hash) => { + const { result, error } = await rpc.http(url, { + body: { + method: 'eth_getTransactionReceipt', + params: [hash], + id: 0, + }, + }) + if (error) + throw new RpcRequestError({ + body: { method, params }, + error, + url, + }) + if (!result) return null + return { + blockHash: result.blockHash, + blockNumber: result.blockNumber, + gasUsed: result.gasUsed, + logs: result.logs, + status: result.status, + transactionHash: result.transactionHash, + } satisfies WalletCallReceipt + }), + ) + const receipts_ = receipts.filter((x) => x !== null) + if (receipts_.length === 0) + return { + atomic: false, + chainId: '0x1', + id: (params as any)[0], + status: 100, + receipts: [], + version: '2.0.0', + } satisfies WalletGetCallsStatusReturnType + return { + atomic: false, + chainId: '0x1', + id: (params as any)[0], + status: 200, + receipts: receipts_, + version: '2.0.0', + } satisfies WalletGetCallsStatusReturnType + } + + if (method === 'wallet_showCallsStatus') return + + // other methods + if (method === 'personal_sign') { + if (features.signMessageError) { + if (typeof features.signMessageError === 'boolean') + throw new UserRejectedRequestError( + new Error('Failed to sign message.'), + ) + throw features.signMessageError + } + // Change `personal_sign` to `eth_sign` and swap params + method = 'eth_sign' + type Params = [data: Hex, address: Address] + params = [(params as Params)[1], (params as Params)[0]] + } + + const body = { method, params } + const { error, result } = await rpc.http(url, { body }) + if (error) throw new RpcRequestError({ body, error, url }) + + return result + } + return custom({ request })({ retryCount: 0 }) + }, + })) +} diff --git a/packages/core/src/createConfig.test-d.ts b/packages/core/src/createConfig.test-d.ts new file mode 100644 index 0000000000..3daa57bd5f --- /dev/null +++ b/packages/core/src/createConfig.test-d.ts @@ -0,0 +1,110 @@ +import { accounts } from '@wagmi/test' +import { http, createClient, webSocket } from 'viem' +import { mainnet, sepolia } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { mock } from './connectors/mock.js' +import { type CreateConfigParameters, createConfig } from './createConfig.js' + +test('high-level config', () => { + // Create config without needing to import viem modules. + const config = createConfig({ + cacheTime: 100, + chains: [mainnet, sepolia], + connectors: [mock({ accounts })], + batch: { multicall: true }, + pollingInterval: { [mainnet.id]: 100 }, + transports: { + [mainnet.id]: webSocket(), + [sepolia.id]: http(), + }, + }) + const client = config.getClient({ chainId: mainnet.id }) + expectTypeOf(client.chain).toEqualTypeOf(mainnet) + expectTypeOf(client.transport.type).toEqualTypeOf<'webSocket'>() +}) + +test('low-level config', () => { + // Create a "multi chain" config using viem modules. + const config = createConfig({ + chains: [mainnet, sepolia], + connectors: [mock({ accounts })], + client({ chain }) { + return createClient({ chain, transport: http() }) + }, + }) + const client = config.getClient({ chainId: mainnet.id }) + expectTypeOf(client.chain).toEqualTypeOf(mainnet) +}) + +test('behavior: `chains` must have at least one chain', () => { + createConfig({ + // @ts-expect-error + chains: [], + connectors: [mock({ accounts })], + transports: { + [mainnet.id]: http(), + }, + }) + createConfig({ + // @ts-expect-error + chains: [], + connectors: [mock({ accounts })], + client: ({ chain }) => + createClient({ + chain, + transport: http(), + }), + }) +}) + +test('behavior: missing transport for chain', () => { + createConfig({ + chains: [mainnet, sepolia], + connectors: [mock({ accounts })], + // @ts-expect-error + transports: { + [mainnet.id]: http(), + }, + }) + createConfig({ + chains: [mainnet, sepolia], + connectors: [mock({ accounts })], + transports: { + [mainnet.id]: http(), + // @ts-expect-error + [123]: http(), + }, + }) +}) + +test('behavior: parameters should not include certain client config properties', () => { + type Result = keyof CreateConfigParameters + expectTypeOf<'account' extends Result ? true : false>().toEqualTypeOf() + expectTypeOf<'chain' extends Result ? true : false>().toEqualTypeOf() + expectTypeOf< + 'transport' extends Result ? true : false + >().toEqualTypeOf() +}) + +test('infer connectors', () => { + const connectorFn = mock({ accounts }) + const config = createConfig({ + chains: [mainnet, sepolia], + connectors: [connectorFn], + transports: { + [mainnet.id]: webSocket(), + [sepolia.id]: http(), + }, + }) + + const connector = config.connectors[0]! + expectTypeOf(connector).toEqualTypeOf( + config._internal.connectors.setup(connectorFn), + ) + + type ConnectFnParameters = NonNullable< + Parameters<(typeof connector)['connect']>[0] + > + expectTypeOf().toMatchTypeOf() +}) diff --git a/packages/core/src/createConfig.test.ts b/packages/core/src/createConfig.test.ts new file mode 100644 index 0000000000..a6ae41e280 --- /dev/null +++ b/packages/core/src/createConfig.test.ts @@ -0,0 +1,440 @@ +import { accounts, chain, wait } from '@wagmi/test' +import { + type EIP1193Provider, + type EIP6963ProviderDetail, + announceProvider, +} from 'mipd' +import { http } from 'viem' +import { expect, test, vi } from 'vitest' + +import { connect } from './actions/connect.js' +import { disconnect } from './actions/disconnect.js' +import { switchChain } from './actions/switchChain.js' +import { createConnector } from './connectors/createConnector.js' +import { mock } from './connectors/mock.js' +import { createConfig } from './createConfig.js' +import { createStorage } from './createStorage.js' + +const { mainnet, optimism } = chain + +vi.mock(import('mipd'), async (importOriginal) => { + const mod = await importOriginal() + + let cache: typeof mod | undefined + if (!cache) + cache = { + ...mod, + createStore() { + const store = mod.createStore() + return { + ...store, + getProviders() { + return [ + getProviderDetail({ name: 'Example', rdns: 'com.example' }), + getProviderDetail({ name: 'Mock', rdns: 'com.mock' }), + ] + }, + } + }, + } + return cache +}) + +test('default', () => { + const config = createConfig({ + chains: [mainnet], + connectors: [mock({ accounts })], + transports: { + [mainnet.id]: http(), + }, + }) + expect(config).toBeDefined() +}) + +test('getClient', () => { + const config = createConfig({ + chains: [mainnet, optimism], + connectors: [mock({ accounts })], + syncConnectedChain: true, + transports: { + [mainnet.id]: http(), + [optimism.id]: http(), + }, + }) + + { + const client = config.getClient() + expect(client.chain.id).toBe(mainnet.id) + } + + { + const client = config.getClient({ chainId: mainnet.id }) + expect(client.chain.id).toBe(mainnet.id) + } + + expect(() => + config.getClient({ + // @ts-expect-error + chainId: 123456, + }), + ).toThrowErrorMatchingInlineSnapshot(` + [ChainNotConfiguredError: Chain not configured. + + Version: @wagmi/core@x.y.z] + `) + + expect(() => { + // @ts-expect-error + config.state.chainId = 123456 + config.getClient() + }).toThrowErrorMatchingInlineSnapshot(` + [ChainNotConfiguredError: Chain not configured. + + Version: @wagmi/core@x.y.z] + `) +}) + +test('behavior: syncConnectedChain', async () => { + const config = createConfig({ + chains: [mainnet, optimism], + connectors: [mock({ accounts })], + syncConnectedChain: true, + transports: { + [mainnet.id]: http(), + [optimism.id]: http(), + }, + }) + // defaults to first chain in `createConfig({ chains })` + expect(config.getClient().chain.id).toBe(mainnet.id) + + // switches to optimism + await switchChain(config, { chainId: optimism.id }) + expect(config.getClient().chain.id).toBe(optimism.id) + + // connects to mainnet + await connect(config, { + chainId: mainnet.id, + connector: config.connectors[0]!, + }) + expect(config.getClient().chain.id).toBe(mainnet.id) + + // switches to optimism + await switchChain(config, { chainId: optimism.id }) + expect(config.getClient().chain.id).toBe(optimism.id) + + // disconnects, still connected to optimism + await disconnect(config) + expect(config.getClient().chain.id).toBe(optimism.id) +}) + +test('behavior: migrate for current version', async () => { + const state = { + 'wagmi.store': JSON.stringify({ + state: { + connections: { + __type: 'Map', + value: [ + [ + '983b8aca245', + { + accounts: [ + '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + '0xd2135CfB216b74109775236E36d4b433F1DF507B', + ], + chainId: 1, + connector: { + id: 'io.metamask', + name: 'MetaMask', + type: 'injected', + uid: '983b8aca245', + }, + }, + ], + ], + }, + chainId: 1, + current: '983b8aca245', + }, + version: Number.NaN, // mocked version is `'x.y.z'`, which will get interpreted as `NaN` + }), + } as Record + Object.defineProperty(window, 'localStorage', { + value: { + getItem: vi.fn((key) => state[key] ?? null), + removeItem: vi.fn((key) => state.delete?.[key]), + setItem: vi.fn((key, value) => { + state[key] = value + }), + }, + writable: true, + }) + + const storage = createStorage<{ store: object }>({ + storage: window.localStorage, + }) + + const config = createConfig({ + chains: [mainnet], + storage, + transports: { [mainnet.id]: http() }, + }) + + await wait(100) + + expect(config.state).toMatchInlineSnapshot(` + { + "chainId": 1, + "connections": Map { + "983b8aca245" => { + "accounts": [ + "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e", + "0xd2135CfB216b74109775236E36d4b433F1DF507B", + ], + "chainId": 1, + "connector": { + "id": "io.metamask", + "name": "MetaMask", + "type": "injected", + "uid": "983b8aca245", + }, + }, + }, + "current": "983b8aca245", + "status": "disconnected", + } + `) +}) + +test('behavior: migrate chainId', async () => { + const state = { + 'wagmi.store': JSON.stringify({ + state: { chainId: 10 }, + version: 1, + }), + } as Record + Object.defineProperty(window, 'localStorage', { + value: { + getItem: vi.fn((key) => state[key] ?? null), + removeItem: vi.fn((key) => state.delete?.[key]), + setItem: vi.fn((key, value) => { + state[key] = value + }), + }, + writable: true, + }) + + const storage = createStorage<{ store: object }>({ + storage: window.localStorage, + }) + + const config = createConfig({ + chains: [mainnet, optimism], + storage, + transports: { + [mainnet.id]: http(), + [optimism.id]: http(), + }, + }) + + await wait(100) + + expect(config.state).toMatchInlineSnapshot(` + { + "chainId": 10, + "connections": Map {}, + "current": null, + "status": "disconnected", + } + `) +}) + +test('behavior: properties passed through to Viem Client via getClient', () => { + { + const properties = { + batch: { + multicall: { + batchSize: 102_400, + }, + }, + cacheTime: 5_000, + pollingInterval: 1_000, + } + + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: http(), + }, + ...properties, + }) + + const { + account: _a, + chain: _c, + extend: _e, + key: _k, + name: _n, + request: _r, + transport: _tr, + uid: _u, + type: _ty, + ...rest + } = config.getClient() + expect(rest).toEqual(properties) + } + + { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: http(), + }, + batch: { + [mainnet.id]: { + multicall: { + batchSize: 1024, + }, + }, + }, + }) + + const client = config.getClient() + expect(client.batch).toMatchInlineSnapshot(` + { + "multicall": { + "batchSize": 1024, + }, + } + `) + + const client2 = config.getClient({ chainId: optimism.id }) + expect(client2.batch).toMatchInlineSnapshot(` + { + "multicall": true, + } + `) + } +}) + +test('behavior: restore unconfigured chainId', () => { + const state = { + 'wagmi.store': JSON.stringify({ + state: { chainId: 10 }, + version: 1, + }), + } as Record + Object.defineProperty(window, 'localStorage', { + value: { + getItem: vi.fn((key) => state[key] ?? null), + removeItem: vi.fn((key) => state.delete?.[key]), + setItem: vi.fn((key, value) => { + state[key] = value + }), + }, + writable: true, + }) + + const storage = createStorage<{ store: object }>({ + storage: window.localStorage, + }) + + const config = createConfig({ + chains: [mainnet], + storage, + transports: { + [mainnet.id]: http(), + }, + }) + + expect(config.state).toMatchInlineSnapshot(` + { + "chainId": 1, + "connections": Map {}, + "current": null, + "status": "disconnected", + } + `) + + const client = config.getClient() + expect(client.chain.id).toBe(mainnet.id) +}) + +test('behavior: setup connector', async () => { + const config = createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + }) + + const connectorFn = mock({ accounts }) + const connector = config._internal.connectors.setup(connectorFn) + config._internal.connectors.setState((x) => [...x, connector]) + + await connect(config, { + chainId: mainnet.id, + connector: config.connectors.find((x) => x.uid === connector.uid)!, + }) + + expect(config.state.current).toBe(connector.uid) + + await disconnect(config) +}) + +test('behavior: eip 6963 providers', async () => { + const detail_1 = getProviderDetail({ name: 'Foo Wallet', rdns: 'com.foo' }) + const detail_2 = getProviderDetail({ name: 'Bar Wallet', rdns: 'com.bar' }) + const detail_3 = getProviderDetail({ name: 'Mock', rdns: 'com.mock' }) + + const config = createConfig({ + chains: [mainnet], + connectors: [ + createConnector((c) => { + return { + ...mock({ accounts })(c), + rdns: ['com.mock', 'com.baz'], + } + }), + ], + transports: { + [mainnet.id]: http(), + }, + }) + + await wait(100) + announceProvider(detail_1)() + await wait(100) + announceProvider(detail_1)() + await wait(100) + announceProvider(detail_2)() + await wait(100) + announceProvider(detail_3)() + await wait(100) + + expect( + config.connectors.flatMap((x) => x.rdns ?? x.id), + ).toMatchInlineSnapshot(` + [ + "com.mock", + "com.baz", + "com.example", + "com.foo", + "com.bar", + ] + `) +}) + +function getProviderDetail( + info: Pick, +): EIP6963ProviderDetail { + return { + info: { + icon: 'data:image/svg+xml,', + uuid: crypto.randomUUID(), + ...info, + }, + provider: `` as unknown as EIP1193Provider, + } +} diff --git a/packages/core/src/createConfig.ts b/packages/core/src/createConfig.ts new file mode 100644 index 0000000000..154eef5f6d --- /dev/null +++ b/packages/core/src/createConfig.ts @@ -0,0 +1,653 @@ +import { + type EIP6963ProviderDetail, + type Store as MipdStore, + createStore as createMipd, +} from 'mipd' +import { + type Address, + type Chain, + type Client, + type EIP1193RequestFn, + createClient, + type ClientConfig as viem_ClientConfig, + type Transport as viem_Transport, +} from 'viem' +import { persist, subscribeWithSelector } from 'zustand/middleware' +import { type Mutate, type StoreApi, createStore } from 'zustand/vanilla' + +import type { + ConnectorEventMap, + CreateConnectorFn, +} from './connectors/createConnector.js' +import { injected } from './connectors/injected.js' +import { type Emitter, type EventData, createEmitter } from './createEmitter.js' +import { + type Storage, + createStorage, + getDefaultStorage, +} from './createStorage.js' +import { ChainNotConfiguredError } from './errors/config.js' +import type { + Compute, + ExactPartial, + LooseOmit, + OneOf, + RemoveUndefined, +} from './types/utils.js' +import { uid } from './utils/uid.js' +import { version } from './version.js' + +export function createConfig< + const chains extends readonly [Chain, ...Chain[]], + transports extends Record, + const connectorFns extends readonly CreateConnectorFn[], +>( + parameters: CreateConfigParameters, +): Config { + const { + multiInjectedProviderDiscovery = true, + storage = createStorage({ + storage: getDefaultStorage(), + }), + syncConnectedChain = true, + ssr = false, + ...rest + } = parameters + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Set up connectors, clients, etc. + ///////////////////////////////////////////////////////////////////////////////////////////////// + + const mipd = + typeof window !== 'undefined' && multiInjectedProviderDiscovery + ? createMipd() + : undefined + + const chains = createStore(() => rest.chains) + const connectors = createStore(() => { + const collection = [] + const rdnsSet = new Set() + for (const connectorFns of rest.connectors ?? []) { + const connector = setup(connectorFns) + collection.push(connector) + if (!ssr && connector.rdns) { + const rdnsValues = + typeof connector.rdns === 'string' ? [connector.rdns] : connector.rdns + for (const rdns of rdnsValues) { + rdnsSet.add(rdns) + } + } + } + if (!ssr && mipd) { + const providers = mipd.getProviders() + for (const provider of providers) { + if (rdnsSet.has(provider.info.rdns)) continue + collection.push(setup(providerDetailToConnector(provider))) + } + } + return collection + }) + function setup(connectorFn: CreateConnectorFn): Connector { + // Set up emitter with uid and add to connector so they are "linked" together. + const emitter = createEmitter(uid()) + const connector = { + ...connectorFn({ + emitter, + chains: chains.getState(), + storage, + transports: rest.transports, + }), + emitter, + uid: emitter.uid, + } + + // Start listening for `connect` events on connector setup + // This allows connectors to "connect" themselves without user interaction (e.g. MetaMask's "Manually connect to current site") + emitter.on('connect', connect) + connector.setup?.() + + return connector + } + function providerDetailToConnector(providerDetail: EIP6963ProviderDetail) { + const { info } = providerDetail + const provider = providerDetail.provider as any + return injected({ target: { ...info, id: info.rdns, provider } }) + } + + const clients = new Map>() + function getClient( + config: { chainId?: chainId | chains[number]['id'] | undefined } = {}, + ): Client> { + const chainId = config.chainId ?? store.getState().chainId + const chain = chains.getState().find((x) => x.id === chainId) + + // chainId specified and not configured + if (config.chainId && !chain) throw new ChainNotConfiguredError() + + // If the target chain is not configured, use the client of the current chain. + type Return = Client> + { + const client = clients.get(store.getState().chainId) + if (client && !chain) return client as Return + if (!chain) throw new ChainNotConfiguredError() + } + + // If a memoized client exists for a chain id, use that. + { + const client = clients.get(chainId) + if (client) return client as Return + } + + let client: Client + if (rest.client) client = rest.client({ chain }) + else { + const chainId = chain.id as chains[number]['id'] + const chainIds = chains.getState().map((x) => x.id) + // Grab all properties off `rest` and resolve for use in `createClient` + const properties: Partial = {} + const entries = Object.entries(rest) as [keyof typeof rest, any][] + + for (const [key, value] of entries) { + if ( + key === 'chains' || + key === 'client' || + key === 'connectors' || + key === 'transports' + ) + continue + + if (typeof value === 'object') { + // check if value is chainId-specific since some values can be objects + // e.g. { batch: { multicall: { batchSize: 1024 } } } + if (chainId in value) properties[key] = value[chainId] + else { + // check if value is chainId-specific, but does not have value for current chainId + const hasChainSpecificValue = chainIds.some((x) => x in value) + if (hasChainSpecificValue) continue + properties[key] = value + } + } else properties[key] = value + } + + client = createClient({ + ...properties, + chain, + batch: properties.batch ?? { multicall: true }, + transport: (parameters) => + rest.transports[chainId]({ ...parameters, connectors }), + }) + } + + clients.set(chainId, client) + return client as Return + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Create store + ///////////////////////////////////////////////////////////////////////////////////////////////// + + function getInitialState(): State { + return { + chainId: chains.getState()[0].id, + connections: new Map(), + current: null, + status: 'disconnected', + } + } + + let currentVersion: number + const prefix = '0.0.0-canary-' + if (version.startsWith(prefix)) + currentVersion = Number.parseInt(version.replace(prefix, '')) + // use package major version to version store + else currentVersion = Number.parseInt(version.split('.')[0] ?? '0') + + const store = createStore( + subscribeWithSelector( + // only use persist middleware if storage exists + storage + ? persist(getInitialState, { + migrate(persistedState, version) { + if (version === currentVersion) return persistedState as State + + const initialState = getInitialState() + const chainId = validatePersistedChainId( + persistedState, + initialState.chainId, + ) + return { ...initialState, chainId } + }, + name: 'store', + partialize(state) { + // Only persist "critical" store properties to preserve storage size. + return { + connections: { + __type: 'Map', + value: Array.from(state.connections.entries()).map( + ([key, connection]) => { + const { id, name, type, uid } = connection.connector + const connector = { id, name, type, uid } + return [key, { ...connection, connector }] + }, + ), + } as unknown as PartializedState['connections'], + chainId: state.chainId, + current: state.current, + } satisfies PartializedState + }, + merge(persistedState, currentState) { + // `status` should not be persisted as it messes with reconnection + if ( + typeof persistedState === 'object' && + persistedState && + 'status' in persistedState + ) + delete persistedState.status + // Make sure persisted `chainId` is valid + const chainId = validatePersistedChainId( + persistedState, + currentState.chainId, + ) + return { + ...currentState, + ...(persistedState as object), + chainId, + } + }, + skipHydration: ssr, + storage: storage as Storage>, + version: currentVersion, + }) + : getInitialState, + ), + ) + store.setState(getInitialState()) + + function validatePersistedChainId( + persistedState: unknown, + defaultChainId: number, + ) { + return persistedState && + typeof persistedState === 'object' && + 'chainId' in persistedState && + typeof persistedState.chainId === 'number' && + chains.getState().some((x) => x.id === persistedState.chainId) + ? persistedState.chainId + : defaultChainId + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Subscribe to changes + ///////////////////////////////////////////////////////////////////////////////////////////////// + + // Update default chain when connector chain changes + if (syncConnectedChain) + store.subscribe( + ({ connections, current }) => + current ? connections.get(current)?.chainId : undefined, + (chainId) => { + // If chain is not configured, then don't switch over to it. + const isChainConfigured = chains + .getState() + .some((x) => x.id === chainId) + if (!isChainConfigured) return + + return store.setState((x) => ({ + ...x, + chainId: chainId ?? x.chainId, + })) + }, + ) + + // EIP-6963 subscribe for new wallet providers + mipd?.subscribe((providerDetails) => { + const connectorIdSet = new Set() + const connectorRdnsSet = new Set() + for (const connector of connectors.getState()) { + connectorIdSet.add(connector.id) + if (connector.rdns) { + const rdnsValues = + typeof connector.rdns === 'string' ? [connector.rdns] : connector.rdns + for (const rdns of rdnsValues) { + connectorRdnsSet.add(rdns) + } + } + } + + const newConnectors: Connector[] = [] + for (const providerDetail of providerDetails) { + if (connectorRdnsSet.has(providerDetail.info.rdns)) continue + const connector = setup(providerDetailToConnector(providerDetail)) + if (connectorIdSet.has(connector.id)) continue + newConnectors.push(connector) + } + + if (storage && !store.persist.hasHydrated()) return + connectors.setState((x) => [...x, ...newConnectors], true) + }) + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Emitter listeners + ///////////////////////////////////////////////////////////////////////////////////////////////// + + function change(data: EventData) { + store.setState((x) => { + const connection = x.connections.get(data.uid) + if (!connection) return x + return { + ...x, + connections: new Map(x.connections).set(data.uid, { + accounts: + (data.accounts as readonly [Address, ...Address[]]) ?? + connection.accounts, + chainId: data.chainId ?? connection.chainId, + connector: connection.connector, + }), + } + }) + } + function connect(data: EventData) { + // Disable handling if reconnecting/connecting + if ( + store.getState().status === 'connecting' || + store.getState().status === 'reconnecting' + ) + return + + store.setState((x) => { + const connector = connectors.getState().find((x) => x.uid === data.uid) + if (!connector) return x + + if (connector.emitter.listenerCount('connect')) + connector.emitter.off('connect', change) + if (!connector.emitter.listenerCount('change')) + connector.emitter.on('change', change) + if (!connector.emitter.listenerCount('disconnect')) + connector.emitter.on('disconnect', disconnect) + + return { + ...x, + connections: new Map(x.connections).set(data.uid, { + accounts: data.accounts as readonly [Address, ...Address[]], + chainId: data.chainId, + connector: connector, + }), + current: data.uid, + status: 'connected', + } + }) + } + function disconnect(data: EventData) { + store.setState((x) => { + const connection = x.connections.get(data.uid) + if (connection) { + const connector = connection.connector + if (connector.emitter.listenerCount('change')) + connection.connector.emitter.off('change', change) + if (connector.emitter.listenerCount('disconnect')) + connection.connector.emitter.off('disconnect', disconnect) + if (!connector.emitter.listenerCount('connect')) + connection.connector.emitter.on('connect', connect) + } + + x.connections.delete(data.uid) + + if (x.connections.size === 0) + return { + ...x, + connections: new Map(), + current: null, + status: 'disconnected', + } + + const nextConnection = x.connections.values().next().value as Connection + return { + ...x, + connections: new Map(x.connections), + current: nextConnection.connector.uid, + } + }) + } + + return { + get chains() { + return chains.getState() as chains + }, + get connectors() { + return connectors.getState() as Connector[] + }, + storage, + + getClient, + get state() { + return store.getState() as unknown as State + }, + setState(value) { + let newState: State + if (typeof value === 'function') newState = value(store.getState() as any) + else newState = value + + // Reset state if it got set to something not matching the base state + const initialState = getInitialState() + if (typeof newState !== 'object') newState = initialState + const isCorrupt = Object.keys(initialState).some((x) => !(x in newState)) + if (isCorrupt) newState = initialState + + store.setState(newState, true) + }, + subscribe(selector, listener, options) { + return store.subscribe( + selector as unknown as (state: State) => any, + listener, + options + ? ({ + ...options, + fireImmediately: options.emitImmediately, + // Workaround cast since Zustand does not support `'exactOptionalPropertyTypes'` + } as RemoveUndefined) + : undefined, + ) + }, + + _internal: { + mipd, + store, + ssr: Boolean(ssr), + syncConnectedChain, + transports: rest.transports as transports, + chains: { + setState(value) { + const nextChains = ( + typeof value === 'function' ? value(chains.getState()) : value + ) as chains + if (nextChains.length === 0) return + return chains.setState(nextChains, true) + }, + subscribe(listener) { + return chains.subscribe(listener) + }, + }, + connectors: { + providerDetailToConnector, + setup: setup as ( + connectorFn: connectorFn, + ) => Connector, + setState(value) { + return connectors.setState( + typeof value === 'function' ? value(connectors.getState()) : value, + true, + ) + }, + subscribe(listener) { + return connectors.subscribe(listener) + }, + }, + events: { change, connect, disconnect }, + }, + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// Types +///////////////////////////////////////////////////////////////////////////////////////////////// + +export type CreateConfigParameters< + chains extends readonly [Chain, ...Chain[]] = readonly [Chain, ...Chain[]], + transports extends Record = Record< + chains[number]['id'], + Transport + >, + connectorFns extends + readonly CreateConnectorFn[] = readonly CreateConnectorFn[], +> = Compute< + { + chains: chains + connectors?: connectorFns | undefined + multiInjectedProviderDiscovery?: boolean | undefined + storage?: Storage | null | undefined + ssr?: boolean | undefined + syncConnectedChain?: boolean | undefined + } & OneOf< + | ({ transports: transports } & { + [key in keyof ClientConfig]?: + | ClientConfig[key] + | { [_ in chains[number]['id']]?: ClientConfig[key] | undefined } + | undefined + }) + | { + client(parameters: { chain: chains[number] }): Client< + transports[chains[number]['id']], + chains[number] + > + } + > +> + +export type Config< + chains extends readonly [Chain, ...Chain[]] = readonly [Chain, ...Chain[]], + transports extends Record = Record< + chains[number]['id'], + Transport + >, + connectorFns extends + readonly CreateConnectorFn[] = readonly CreateConnectorFn[], +> = { + readonly chains: chains + readonly connectors: readonly Connector[] + readonly storage: Storage | null + + readonly state: State + setState( + value: State | ((state: State) => State), + ): void + subscribe( + selector: (state: State) => state, + listener: (state: state, previousState: state) => void, + options?: + | { + emitImmediately?: boolean | undefined + equalityFn?: ((a: state, b: state) => boolean) | undefined + } + | undefined, + ): () => void + + getClient(parameters?: { + chainId?: chainId | chains[number]['id'] | undefined + }): Client> + + /** + * Not part of versioned API, proceed with caution. + * @internal + */ + _internal: Internal +} + +type Internal< + chains extends readonly [Chain, ...Chain[]] = readonly [Chain, ...Chain[]], + transports extends Record = Record< + chains[number]['id'], + Transport + >, +> = { + readonly mipd: MipdStore | undefined + readonly store: Mutate, [['zustand/persist', any]]> + readonly ssr: boolean + readonly syncConnectedChain: boolean + readonly transports: transports + + chains: { + setState( + value: + | readonly [Chain, ...Chain[]] + | (( + state: readonly [Chain, ...Chain[]], + ) => readonly [Chain, ...Chain[]]), + ): void + subscribe( + listener: ( + state: readonly [Chain, ...Chain[]], + prevState: readonly [Chain, ...Chain[]], + ) => void, + ): () => void + } + connectors: { + providerDetailToConnector( + providerDetail: EIP6963ProviderDetail, + ): CreateConnectorFn + setup( + connectorFn: connectorFn, + ): Connector + setState(value: Connector[] | ((state: Connector[]) => Connector[])): void + subscribe( + listener: (state: Connector[], prevState: Connector[]) => void, + ): () => void + } + events: { + change(data: EventData): void + connect(data: EventData): void + disconnect(data: EventData): void + } +} + +export type State< + chains extends readonly [Chain, ...Chain[]] = readonly [Chain, ...Chain[]], +> = { + chainId: chains[number]['id'] + connections: Map + current: string | null + status: 'connected' | 'connecting' | 'disconnected' | 'reconnecting' +} + +export type PartializedState = Compute< + ExactPartial> +> + +export type Connection = { + accounts: readonly [Address, ...Address[]] + chainId: number + connector: Connector +} + +export type Connector< + createConnectorFn extends CreateConnectorFn = CreateConnectorFn, +> = ReturnType & { + emitter: Emitter + uid: string +} + +export type Transport< + type extends string = string, + rpcAttributes = Record, + eip1193RequestFn extends EIP1193RequestFn = EIP1193RequestFn, +> = ( + params: Parameters< + viem_Transport + >[0] & { + connectors?: StoreApi | undefined + }, +) => ReturnType> + +type ClientConfig = LooseOmit< + viem_ClientConfig, + 'account' | 'chain' | 'key' | 'name' | 'transport' | 'type' +> diff --git a/packages/core/src/createEmitter.test.ts b/packages/core/src/createEmitter.test.ts new file mode 100644 index 0000000000..5eb453bb51 --- /dev/null +++ b/packages/core/src/createEmitter.test.ts @@ -0,0 +1,19 @@ +import { expect, test, vi } from 'vitest' + +import type { ConnectorEventMap } from './connectors/createConnector.js' +import { createEmitter } from './createEmitter.js' +import { uid } from './utils/uid.js' + +test('default', () => { + const emitter = createEmitter(uid()) + + const onMessage = vi.fn() + emitter.on('message', onMessage) + emitter.emit('message', { type: 'bar', data: 'baz' }) + + expect(onMessage).toHaveBeenCalledWith({ + type: 'bar', + data: 'baz', + uid: emitter.uid, + }) +}) diff --git a/packages/core/src/createEmitter.ts b/packages/core/src/createEmitter.ts new file mode 100644 index 0000000000..20bf4c5a88 --- /dev/null +++ b/packages/core/src/createEmitter.ts @@ -0,0 +1,68 @@ +import { EventEmitter } from 'eventemitter3' + +type EventMap = Record +type EventKey = string & keyof eventMap +type EventFn = ( + ...parameters: parameters +) => void +export type EventData< + eventMap extends EventMap, + eventName extends keyof eventMap, +> = (eventMap[eventName] extends [never] ? unknown : eventMap[eventName]) & { + uid: string +} + +export class Emitter { + _emitter = new EventEmitter() + + constructor(public uid: string) {} + + on>( + eventName: key, + fn: EventFn< + eventMap[key] extends [never] + ? [{ uid: string }] + : [data: eventMap[key] & { uid: string }] + >, + ) { + this._emitter.on(eventName, fn as EventFn) + } + + once>( + eventName: key, + fn: EventFn< + eventMap[key] extends [never] + ? [{ uid: string }] + : [data: eventMap[key] & { uid: string }] + >, + ) { + this._emitter.once(eventName, fn as EventFn) + } + + off>( + eventName: key, + fn: EventFn< + eventMap[key] extends [never] + ? [{ uid: string }] + : [data: eventMap[key] & { uid: string }] + >, + ) { + this._emitter.off(eventName, fn as EventFn) + } + + emit>( + eventName: key, + ...params: eventMap[key] extends [never] ? [] : [data: eventMap[key]] + ) { + const data = params[0] + this._emitter.emit(eventName, { uid: this.uid, ...data }) + } + + listenerCount>(eventName: key) { + return this._emitter.listenerCount(eventName) + } +} + +export function createEmitter(uid: string) { + return new Emitter(uid) +} diff --git a/packages/core/src/createStorage.test-d.ts b/packages/core/src/createStorage.test-d.ts new file mode 100644 index 0000000000..6bb4c7f300 --- /dev/null +++ b/packages/core/src/createStorage.test-d.ts @@ -0,0 +1,74 @@ +import { expectTypeOf, test } from 'vitest' +import { createStorage } from './createStorage.js' + +import type { Connection } from './createConfig.js' + +test('getItem', () => { + const storage = createStorage({ storage: localStorage }) + + expectTypeOf(storage.getItem('recentConnectorId')).toEqualTypeOf< + string | null | Promise + >() + expectTypeOf(storage.getItem('recentConnectorId', 'foo')).toEqualTypeOf< + string | Promise + >() + expectTypeOf(storage.getItem('foo')).toEqualTypeOf() + // @ts-expect-error incorrect argument type + storage.getItem('recentConnectorId', 1n) + + expectTypeOf(storage.getItem('state')).toEqualTypeOf< + | { + chainId?: number | undefined + connections?: Map | undefined + current?: string | null | undefined + status?: + | 'connected' + | 'connecting' + | 'reconnecting' + | 'disconnected' + | undefined + } + | null + | Promise<{ + chainId?: number | undefined + connections?: Map | undefined + current?: string | null | undefined + status?: + | 'connected' + | 'connecting' + | 'reconnecting' + | 'disconnected' + | undefined + } | null> + >() + + const customStorage = createStorage<{ foo: number }>({ + storage: localStorage, + }) + expectTypeOf(customStorage.getItem('foo')).toEqualTypeOf< + number | null | Promise + >() + expectTypeOf(customStorage.getItem('foo', 1)).toEqualTypeOf< + number | Promise + >() +}) + +test('setItem', () => { + const storage = createStorage({ storage: localStorage }) + + storage.setItem('recentConnectorId', 'foo') + // @ts-expect-error incorrect argument type + storage.setItem('recentConnectorId', 1n) +}) + +test('serialize/deserialize types', () => { + createStorage({ + deserialize(value) { + return value + }, + serialize(value) { + return value + }, + storage: localStorage, + }) +}) diff --git a/packages/core/src/createStorage.test.ts b/packages/core/src/createStorage.test.ts new file mode 100644 index 0000000000..06aa0f89d6 --- /dev/null +++ b/packages/core/src/createStorage.test.ts @@ -0,0 +1,45 @@ +import { expect, test, vi } from 'vitest' + +import { createStorage } from './createStorage.js' + +Object.defineProperty(window, 'localStorage', { + value: { + getItem: vi.fn(() => null), + removeItem: vi.fn(() => null), + setItem: vi.fn(() => null), + }, + writable: true, +}) + +test('inits', () => { + const storage = createStorage({ storage: window.localStorage }) + expect(storage).toBeDefined() +}) + +test('getItem', () => { + const storage = createStorage({ storage: window.localStorage }) + storage.getItem('recentConnectorId') + expect(window.localStorage.getItem).toHaveBeenCalledTimes(1) + expect(window.localStorage.getItem).toHaveBeenCalledWith( + 'wagmi.recentConnectorId', + ) +}) + +test('setItem', () => { + const storage = createStorage({ storage: window.localStorage }) + storage.setItem('recentConnectorId', 'bar') + expect(window.localStorage.setItem).toHaveBeenCalledTimes(1) + expect(window.localStorage.setItem).toHaveBeenCalledWith( + 'wagmi.recentConnectorId', + '"bar"', + ) +}) + +test('removeItem', () => { + const storage = createStorage({ storage: window.localStorage }) + storage.removeItem('recentConnectorId') + expect(window.localStorage.removeItem).toHaveBeenCalledTimes(1) + expect(window.localStorage.removeItem).toHaveBeenCalledWith( + 'wagmi.recentConnectorId', + ) +}) diff --git a/packages/core/src/createStorage.ts b/packages/core/src/createStorage.ts new file mode 100644 index 0000000000..8995ca3202 --- /dev/null +++ b/packages/core/src/createStorage.ts @@ -0,0 +1,112 @@ +import type { PartializedState } from './createConfig.js' +import type { Compute } from './types/utils.js' +import { deserialize as deserialize_ } from './utils/deserialize.js' +import { serialize as serialize_ } from './utils/serialize.js' + +// key-values for loose autocomplete and typing +export type StorageItemMap = { + recentConnectorId: string + state: PartializedState +} + +export type Storage< + itemMap extends Record = Record, + /// + storageItemMap extends StorageItemMap = StorageItemMap & itemMap, +> = { + key: string + getItem< + key extends keyof storageItemMap, + value extends storageItemMap[key], + defaultValue extends value | null | undefined, + >( + key: key, + defaultValue?: defaultValue | undefined, + ): + | (defaultValue extends null ? value | null : value) + | Promise + setItem< + key extends keyof storageItemMap, + value extends storageItemMap[key] | null, + >(key: key, value: value): void | Promise + removeItem(key: keyof storageItemMap): void | Promise +} + +export type BaseStorage = { + getItem( + key: string, + ): string | null | undefined | Promise + setItem(key: string, value: string): void | Promise + removeItem(key: string): void | Promise +} + +export type CreateStorageParameters = { + deserialize?: ((value: string) => type | unknown) | undefined + key?: string | undefined + serialize?: ((value: type | any) => string) | undefined + storage?: Compute | undefined +} + +export function createStorage< + itemMap extends Record = Record, + storageItemMap extends StorageItemMap = StorageItemMap & itemMap, +>(parameters: CreateStorageParameters): Compute> { + const { + deserialize = deserialize_, + key: prefix = 'wagmi', + serialize = serialize_, + storage = noopStorage, + } = parameters + + function unwrap(value: type): type | Promise { + if (value instanceof Promise) return value.then((x) => x).catch(() => null) + return value + } + + return { + ...storage, + key: prefix, + async getItem(key, defaultValue) { + const value = storage.getItem(`${prefix}.${key as string}`) + const unwrapped = await unwrap(value) + if (unwrapped) return deserialize(unwrapped) ?? null + return (defaultValue ?? null) as any + }, + async setItem(key, value) { + const storageKey = `${prefix}.${key as string}` + if (value === null) await unwrap(storage.removeItem(storageKey)) + else await unwrap(storage.setItem(storageKey, serialize(value))) + }, + async removeItem(key) { + await unwrap(storage.removeItem(`${prefix}.${key as string}`)) + }, + } +} + +export const noopStorage = { + getItem: () => null, + setItem: () => {}, + removeItem: () => {}, +} satisfies BaseStorage + +export function getDefaultStorage() { + const storage = (() => { + if (typeof window !== 'undefined' && window.localStorage) + return window.localStorage + return noopStorage + })() + return { + getItem(key) { + return storage.getItem(key) + }, + removeItem(key) { + storage.removeItem(key) + }, + setItem(key, value) { + try { + storage.setItem(key, value) + // silence errors by default (QuotaExceededError, SecurityError, etc.) + } catch {} + }, + } satisfies BaseStorage +} diff --git a/packages/core/src/errors/base.test.ts b/packages/core/src/errors/base.test.ts new file mode 100644 index 0000000000..443b1ea3cf --- /dev/null +++ b/packages/core/src/errors/base.test.ts @@ -0,0 +1,155 @@ +import { expect, test } from 'vitest' + +import { BaseError } from './base.js' + +test('BaseError', () => { + expect(new BaseError('An error occurred.')).toMatchInlineSnapshot(` + [WagmiCoreError: An error occurred. + + Version: @wagmi/core@x.y.z] + `) + + expect( + new BaseError('An error occurred.', { details: 'details' }), + ).toMatchInlineSnapshot(` + [WagmiCoreError: An error occurred. + + Details: details + Version: @wagmi/core@x.y.z] + `) + + expect(new BaseError('', { details: 'details' })).toMatchInlineSnapshot(` + [WagmiCoreError: An error occurred. + + Details: details + Version: @wagmi/core@x.y.z] + `) +}) + +test('BaseError (w/ docsPath)', () => { + expect( + new BaseError('An error occurred.', { + details: 'details', + docsPath: '/lol', + }), + ).toMatchInlineSnapshot(` + [WagmiCoreError: An error occurred. + + Docs: https://wagmi.sh/core/lol.html + Details: details + Version: @wagmi/core@x.y.z] + `) + expect( + new BaseError('An error occurred.', { + cause: new BaseError('error', { docsPath: '/docs' }), + }), + ).toMatchInlineSnapshot(` + [WagmiCoreError: An error occurred. + + Docs: https://wagmi.sh/core/docs.html + Version: @wagmi/core@x.y.z] + `) + expect( + new BaseError('An error occurred.', { + cause: new BaseError('error'), + docsPath: '/lol', + }), + ).toMatchInlineSnapshot(` + [WagmiCoreError: An error occurred. + + Docs: https://wagmi.sh/core/lol.html + Version: @wagmi/core@x.y.z] + `) + expect( + new BaseError('An error occurred.', { + details: 'details', + docsPath: '/lol', + docsSlug: 'test', + }), + ).toMatchInlineSnapshot(` + [WagmiCoreError: An error occurred. + + Docs: https://wagmi.sh/core/lol.html#test + Details: details + Version: @wagmi/core@x.y.z] + `) +}) + +test('BaseError (w/ metaMessages)', () => { + expect( + new BaseError('An error occurred.', { + details: 'details', + metaMessages: ['Reason: idk', 'Cause: lol'], + }), + ).toMatchInlineSnapshot(` + [WagmiCoreError: An error occurred. + + Reason: idk + Cause: lol + + Details: details + Version: @wagmi/core@x.y.z] + `) +}) + +test('inherited BaseError', () => { + const err = new BaseError('An error occurred.', { + details: 'details', + docsPath: '/lol', + }) + expect( + new BaseError('An internal error occurred.', { + cause: err, + }), + ).toMatchInlineSnapshot(` + [WagmiCoreError: An internal error occurred. + + Docs: https://wagmi.sh/core/lol.html + Details: details + Version: @wagmi/core@x.y.z] + `) +}) + +test('inherited Error', () => { + const err = new Error('details') + expect( + new BaseError('An internal error occurred.', { + cause: err, + docsPath: '/lol', + }), + ).toMatchInlineSnapshot(` + [WagmiCoreError: An internal error occurred. + + Docs: https://wagmi.sh/core/lol.html + Details: details + Version: @wagmi/core@x.y.z] + `) +}) + +test('walk: no predicate fn (walks to leaf)', () => { + class FooError extends BaseError {} + class BarError extends BaseError {} + + const err = new BaseError('test1', { + cause: new FooError('test2', { cause: new BarError('test3') }), + }) + expect(err.walk()).toMatchInlineSnapshot(` + [WagmiCoreError: test3 + + Version: @wagmi/core@x.y.z] + `) +}) + +test('walk: predicate fn', () => { + class FooError extends BaseError {} + class BarError extends BaseError {} + + const err = new BaseError('test1', { + cause: new FooError('test2', { cause: new BarError('test3') }), + }) + expect(err.walk((err) => err instanceof FooError)).toMatchInlineSnapshot(` + [WagmiCoreError: test2 + + Version: @wagmi/core@x.y.z] + `) +}) diff --git a/packages/core/src/errors/base.ts b/packages/core/src/errors/base.ts new file mode 100644 index 0000000000..8540c1da7b --- /dev/null +++ b/packages/core/src/errors/base.ts @@ -0,0 +1,74 @@ +import type { Compute, OneOf } from '../types/utils.js' +import { getVersion } from '../utils/getVersion.js' + +export type ErrorType = Error & { name: name } + +type BaseErrorOptions = Compute< + OneOf<{ details?: string | undefined } | { cause: BaseError | Error }> & { + docsPath?: string | undefined + docsSlug?: string | undefined + metaMessages?: string[] | undefined + } +> + +export type BaseErrorType = BaseError & { name: 'WagmiCoreError' } +export class BaseError extends Error { + details: string + docsPath?: string | undefined + metaMessages?: string[] | undefined + shortMessage: string + + override name = 'WagmiCoreError' + get docsBaseUrl() { + return 'https://wagmi.sh/core' + } + get version() { + return getVersion() + } + + constructor(shortMessage: string, options: BaseErrorOptions = {}) { + super() + + const details = + options.cause instanceof BaseError + ? options.cause.details + : options.cause?.message + ? options.cause.message + : options.details! + const docsPath = + options.cause instanceof BaseError + ? options.cause.docsPath || options.docsPath + : options.docsPath + + this.message = [ + shortMessage || 'An error occurred.', + '', + ...(options.metaMessages ? [...options.metaMessages, ''] : []), + ...(docsPath + ? [ + `Docs: ${this.docsBaseUrl}${docsPath}.html${ + options.docsSlug ? `#${options.docsSlug}` : '' + }`, + ] + : []), + ...(details ? [`Details: ${details}`] : []), + `Version: ${this.version}`, + ].join('\n') + + if (options.cause) this.cause = options.cause + this.details = details + this.docsPath = docsPath + this.metaMessages = options.metaMessages + this.shortMessage = shortMessage + } + + walk(fn?: (err: unknown) => boolean) { + return this.#walk(this, fn) + } + + #walk(err: unknown, fn?: (err: unknown) => boolean): unknown { + if (fn?.(err)) return err + if ((err as Error).cause) return this.#walk((err as Error).cause, fn) + return err + } +} diff --git a/packages/core/src/errors/config.test.ts b/packages/core/src/errors/config.test.ts new file mode 100644 index 0000000000..1ec07fd107 --- /dev/null +++ b/packages/core/src/errors/config.test.ts @@ -0,0 +1,68 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { + ChainNotConfiguredError, + ConnectorAccountNotFoundError, + ConnectorAlreadyConnectedError, + ConnectorChainMismatchError, + ConnectorNotConnectedError, + ConnectorNotFoundError, + ConnectorUnavailableReconnectingError, +} from './config.js' + +test('constructors', () => { + expect(new ChainNotConfiguredError()).toMatchInlineSnapshot(` + [ChainNotConfiguredError: Chain not configured. + + Version: @wagmi/core@x.y.z] + `) + expect(new ConnectorAlreadyConnectedError()).toMatchInlineSnapshot(` + [ConnectorAlreadyConnectedError: Connector already connected. + + Version: @wagmi/core@x.y.z] + `) + expect(new ConnectorNotConnectedError()).toMatchInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) + expect(new ConnectorNotFoundError()).toMatchInlineSnapshot(` + [ConnectorNotFoundError: Connector not found. + + Version: @wagmi/core@x.y.z] + `) + expect( + new ConnectorAccountNotFoundError({ + address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + connector: config.connectors[0]!, + }), + ).toMatchInlineSnapshot(` + [ConnectorAccountNotFoundError: Account "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e" not found for connector "Mock Connector". + + Version: @wagmi/core@x.y.z] + `) + expect( + new ConnectorChainMismatchError({ + connectionChainId: 1, + connectorChainId: 123, + }), + ).toMatchInlineSnapshot(` + [ConnectorChainMismatchError: The current chain of the connector (id: 123) does not match the connection's chain (id: 1). + + Current Chain ID: 123 + Expected Chain ID: 1 + + Version: @wagmi/core@x.y.z] + `) + expect( + new ConnectorUnavailableReconnectingError({ + connector: { name: 'Rabby Wallet' }, + }), + ).toMatchInlineSnapshot(` + [ConnectorUnavailableReconnectingError: Connector "Rabby Wallet" unavailable while reconnecting. + + Details: During the reconnection step, the only connector methods guaranteed to be available are: \`id\`, \`name\`, \`type\`, \`uid\`. All other methods are not guaranteed to be available until reconnection completes and connectors are fully restored. This error commonly occurs for connectors that asynchronously inject after reconnection has already started. + Version: @wagmi/core@x.y.z] + `) +}) diff --git a/packages/core/src/errors/config.ts b/packages/core/src/errors/config.ts new file mode 100644 index 0000000000..2956f0132c --- /dev/null +++ b/packages/core/src/errors/config.ts @@ -0,0 +1,103 @@ +import type { Address } from 'viem' + +import type { Connector } from '../createConfig.js' +import { BaseError } from './base.js' + +export type ChainNotConfiguredErrorType = ChainNotConfiguredError & { + name: 'ChainNotConfiguredError' +} +export class ChainNotConfiguredError extends BaseError { + override name = 'ChainNotConfiguredError' + constructor() { + super('Chain not configured.') + } +} + +export type ConnectorAlreadyConnectedErrorType = + ConnectorAlreadyConnectedError & { + name: 'ConnectorAlreadyConnectedError' + } +export class ConnectorAlreadyConnectedError extends BaseError { + override name = 'ConnectorAlreadyConnectedError' + constructor() { + super('Connector already connected.') + } +} + +export type ConnectorNotConnectedErrorType = ConnectorNotConnectedError & { + name: 'ConnectorNotConnectedError' +} +export class ConnectorNotConnectedError extends BaseError { + override name = 'ConnectorNotConnectedError' + constructor() { + super('Connector not connected.') + } +} + +export type ConnectorNotFoundErrorType = ConnectorNotFoundError & { + name: 'ConnectorNotFoundError' +} +export class ConnectorNotFoundError extends BaseError { + override name = 'ConnectorNotFoundError' + constructor() { + super('Connector not found.') + } +} + +export type ConnectorAccountNotFoundErrorType = + ConnectorAccountNotFoundError & { + name: 'ConnectorAccountNotFoundError' + } +export class ConnectorAccountNotFoundError extends BaseError { + override name = 'ConnectorAccountNotFoundError' + constructor({ + address, + connector, + }: { + address: Address + connector: Connector + }) { + super(`Account "${address}" not found for connector "${connector.name}".`) + } +} + +export type ConnectorChainMismatchErrorType = ConnectorAccountNotFoundError & { + name: 'ConnectorChainMismatchError' +} +export class ConnectorChainMismatchError extends BaseError { + override name = 'ConnectorChainMismatchError' + constructor({ + connectionChainId, + connectorChainId, + }: { + connectionChainId: number + connectorChainId: number + }) { + super( + `The current chain of the connector (id: ${connectorChainId}) does not match the connection's chain (id: ${connectionChainId}).`, + { + metaMessages: [ + `Current Chain ID: ${connectorChainId}`, + `Expected Chain ID: ${connectionChainId}`, + ], + }, + ) + } +} + +export type ConnectorUnavailableReconnectingErrorType = + ConnectorUnavailableReconnectingError & { + name: 'ConnectorUnavailableReconnectingError' + } +export class ConnectorUnavailableReconnectingError extends BaseError { + override name = 'ConnectorUnavailableReconnectingError' + constructor({ connector }: { connector: { name: string } }) { + super(`Connector "${connector.name}" unavailable while reconnecting.`, { + details: [ + 'During the reconnection step, the only connector methods guaranteed to be available are: `id`, `name`, `type`, `uid`.', + 'All other methods are not guaranteed to be available until reconnection completes and connectors are fully restored.', + 'This error commonly occurs for connectors that asynchronously inject after reconnection has already started.', + ].join(' '), + }) + } +} diff --git a/packages/core/src/errors/connector.test.ts b/packages/core/src/errors/connector.test.ts new file mode 100644 index 0000000000..258ef834cd --- /dev/null +++ b/packages/core/src/errors/connector.test.ts @@ -0,0 +1,24 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { + ProviderNotFoundError, + SwitchChainNotSupportedError, +} from './connector.js' + +test('constructors', () => { + expect(new ProviderNotFoundError()).toMatchInlineSnapshot(` + [ProviderNotFoundError: Provider not found. + + Version: @wagmi/core@x.y.z] + `) + expect( + new SwitchChainNotSupportedError({ + connector: config.connectors[0]!, + }), + ).toMatchInlineSnapshot(` + [SwitchChainNotSupportedError: "Mock Connector" does not support programmatic chain switching. + + Version: @wagmi/core@x.y.z] + `) +}) diff --git a/packages/core/src/errors/connector.ts b/packages/core/src/errors/connector.ts new file mode 100644 index 0000000000..c6c30ef1a5 --- /dev/null +++ b/packages/core/src/errors/connector.ts @@ -0,0 +1,23 @@ +import type { Connector } from '../createConfig.js' +import { BaseError } from './base.js' + +export type ProviderNotFoundErrorType = ProviderNotFoundError & { + name: 'ProviderNotFoundError' +} +export class ProviderNotFoundError extends BaseError { + override name = 'ProviderNotFoundError' + constructor() { + super('Provider not found.') + } +} + +export type SwitchChainNotSupportedErrorType = SwitchChainNotSupportedError & { + name: 'SwitchChainNotSupportedError' +} +export class SwitchChainNotSupportedError extends BaseError { + override name = 'SwitchChainNotSupportedError' + + constructor({ connector }: { connector: Connector }) { + super(`"${connector.name}" does not support programmatic chain switching.`) + } +} diff --git a/packages/core/src/experimental/actions/writeContracts.test.ts b/packages/core/src/experimental/actions/writeContracts.test.ts new file mode 100644 index 0000000000..b74cd780ef --- /dev/null +++ b/packages/core/src/experimental/actions/writeContracts.test.ts @@ -0,0 +1,99 @@ +import { abi, address, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connect } from '../../actions/connect.js' +import { disconnect } from '../../actions/disconnect.js' +import { writeContracts } from './writeContracts.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + await expect( + writeContracts(config, { + contracts: [ + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + ], + }), + ).resolves.toMatchInlineSnapshot( + ` + { + "id": "0x8913636bd97cf4bcc0a6343c730905a27ead0f7480ff82190072e916439eb212", + } + `, + ) + await disconnect(config, { connector }) +}) + +test('behavior: not connected', async () => { + await expect( + writeContracts(config, { + contracts: [ + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + ], + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorNotConnectedError: Connector not connected. + + Version: @wagmi/core@x.y.z] + `) +}) + +test('behavior: account does not exist on connector', async () => { + await connect(config, { connector }) + await expect( + writeContracts(config, { + account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', + contracts: [ + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + ], + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ConnectorAccountNotFoundError: Account "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e" not found for connector "Mock Connector". + + Version: @wagmi/core@x.y.z] + `) + await disconnect(config, { connector }) +}) diff --git a/packages/core/src/experimental/actions/writeContracts.ts b/packages/core/src/experimental/actions/writeContracts.ts new file mode 100644 index 0000000000..06e6687592 --- /dev/null +++ b/packages/core/src/experimental/actions/writeContracts.ts @@ -0,0 +1,78 @@ +import type { Account, Chain, ContractFunctionParameters } from 'viem' +import { + type WriteContractsErrorType as viem_WriteContractsErrorType, + type WriteContractsParameters as viem_WriteContractsParameters, + type WriteContractsReturnType as viem_WriteContractsReturnType, + writeContracts as viem_writeContracts, +} from 'viem/experimental' + +import { + type GetConnectorClientErrorType, + getConnectorClient, +} from '../../actions/getConnectorClient.js' +import type { Config } from '../../createConfig.js' +import type { BaseErrorType, ErrorType } from '../../errors/base.js' +import type { SelectChains } from '../../types/chain.js' +import type { + ChainIdParameter, + ConnectorParameter, +} from '../../types/properties.js' +import type { Compute } from '../../types/utils.js' + +export type WriteContractsParameters< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + /// + chains extends readonly Chain[] = SelectChains, +> = { + [key in keyof chains]: Compute< + Omit< + viem_WriteContractsParameters< + contracts, + chains[key], + Account, + chains[key] + >, + 'chain' + > & + ChainIdParameter & + ConnectorParameter + > +}[number] + +export type WriteContractsReturnType = viem_WriteContractsReturnType + +export type WriteContractsErrorType = + // getConnectorClient() + | GetConnectorClientErrorType + // base + | BaseErrorType + | ErrorType + // viem + | viem_WriteContractsErrorType + +/** https://wagmi.sh/core/api/actions/writeContracts */ +export async function writeContracts< + const contracts extends readonly unknown[], + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + parameters: WriteContractsParameters, +): Promise { + const { account, chainId, connector, ...rest } = parameters + + const client = await getConnectorClient(config, { + account, + chainId, + connector, + }) + + return viem_writeContracts(client, { + ...(rest as any), + ...(account ? { account } : {}), + chain: chainId ? { id: chainId } : undefined, + }) +} diff --git a/packages/core/src/experimental/query/writeContracts.test.ts b/packages/core/src/experimental/query/writeContracts.test.ts new file mode 100644 index 0000000000..5f4a1f28dd --- /dev/null +++ b/packages/core/src/experimental/query/writeContracts.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { writeContractsMutationOptions } from './writeContracts.js' + +test('default', () => { + expect(writeContractsMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "writeContracts", + ], + } + `) +}) diff --git a/packages/core/src/experimental/query/writeContracts.ts b/packages/core/src/experimental/query/writeContracts.ts new file mode 100644 index 0000000000..192a842ca0 --- /dev/null +++ b/packages/core/src/experimental/query/writeContracts.ts @@ -0,0 +1,70 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import type { Config } from '../../createConfig.js' +import type { Compute } from '../../types/utils.js' +import { + type WriteContractsErrorType, + type WriteContractsParameters, + type WriteContractsReturnType, + writeContracts, +} from '../actions/writeContracts.js' + +export function writeContractsMutationOptions< + const contracts extends readonly unknown[], + config extends Config, +>(config: config) { + return { + mutationFn(variables) { + return writeContracts(config, variables as any) as any + }, + mutationKey: ['writeContracts'], + } as const satisfies MutationOptions< + WriteContractsData, + WriteContractsErrorType, + WriteContractsVariables + > +} + +export type WriteContractsData = Compute + +export type WriteContractsVariables< + contracts extends readonly unknown[], + config extends Config, + chainId extends config['chains'][number]['id'], +> = WriteContractsParameters + +export type WriteContractsMutate< + contracts extends readonly unknown[], + config extends Config, + context = unknown, +> = ( + variables: WriteContractsVariables, + options?: + | Compute< + MutateOptions< + WriteContractsData, + WriteContractsErrorType, + Compute>, + context + > + > + | undefined, +) => void + +export type WriteContractsMutateAsync< + contracts extends readonly unknown[], + config extends Config, + context = unknown, +> = ( + variables: WriteContractsVariables, + options?: + | Compute< + MutateOptions< + WriteContractsData, + WriteContractsErrorType, + Compute>, + context + > + > + | undefined, +) => Promise diff --git a/packages/core/src/exports/actions.test.ts b/packages/core/src/exports/actions.test.ts new file mode 100644 index 0000000000..eaaedba14f --- /dev/null +++ b/packages/core/src/exports/actions.test.ts @@ -0,0 +1,86 @@ +import { expect, test } from 'vitest' + +import * as actions from './actions.js' + +test('exports', () => { + expect(Object.keys(actions)).toMatchInlineSnapshot(` + [ + "call", + "connect", + "deployContract", + "disconnect", + "estimateGas", + "estimateFeesPerGas", + "estimateMaxPriorityFeePerGas", + "getAccount", + "getBalance", + "fetchBalance", + "getBlock", + "getBlockNumber", + "fetchBlockNumber", + "getBlockTransactionCount", + "getBytecode", + "getCallsStatus", + "getCapabilities", + "getChainId", + "getChains", + "getClient", + "getConnections", + "getConnectors", + "getConnectorClient", + "getEnsAddress", + "fetchEnsAddress", + "getEnsAvatar", + "fetchEnsAvatar", + "getEnsName", + "fetchEnsName", + "getEnsResolver", + "fetchEnsResolver", + "getEnsText", + "getFeeHistory", + "getGasPrice", + "getProof", + "getPublicClient", + "getStorageAt", + "getToken", + "fetchToken", + "getTransaction", + "fetchTransaction", + "getTransactionConfirmations", + "getTransactionCount", + "getTransactionReceipt", + "getWalletClient", + "multicall", + "prepareTransactionRequest", + "readContract", + "readContracts", + "reconnect", + "sendCalls", + "sendTransaction", + "showCallsStatus", + "signMessage", + "signTypedData", + "simulateContract", + "switchAccount", + "switchChain", + "switchNetwork", + "verifyMessage", + "verifyTypedData", + "waitForCallsStatus", + "watchAccount", + "watchAsset", + "watchBlocks", + "watchBlockNumber", + "watchChainId", + "watchClient", + "watchConnections", + "watchConnectors", + "watchContractEvent", + "watchPendingTransactions", + "watchPublicClient", + "waitForTransactionReceipt", + "waitForTransaction", + "writeContract", + ] + `) +}) diff --git a/packages/core/src/exports/actions.ts b/packages/core/src/exports/actions.ts new file mode 100644 index 0000000000..d03c2adb76 --- /dev/null +++ b/packages/core/src/exports/actions.ts @@ -0,0 +1,460 @@ +//////////////////////////////////////////////////////////////////////////////// +// Actions +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type CallErrorType, + type CallParameters, + type CallReturnType, + call, +} from '../actions/call.js' + +export { + type ConnectErrorType, + type ConnectParameters, + type ConnectReturnType, + connect, +} from '../actions/connect.js' + +export { + type DeployContractErrorType, + type DeployContractParameters, + type DeployContractReturnType, + deployContract, +} from '../actions/deployContract.js' + +export { + type DisconnectErrorType, + type DisconnectParameters, + type DisconnectReturnType, + disconnect, +} from '../actions/disconnect.js' + +export { + type EstimateGasErrorType, + type EstimateGasParameters, + type EstimateGasReturnType, + estimateGas, +} from '../actions/estimateGas.js' + +export { + type EstimateFeesPerGasErrorType, + type EstimateFeesPerGasParameters, + type EstimateFeesPerGasReturnType, + estimateFeesPerGas, +} from '../actions/estimateFeesPerGas.js' + +export { + type EstimateMaxPriorityFeePerGasErrorType, + type EstimateMaxPriorityFeePerGasParameters, + type EstimateMaxPriorityFeePerGasReturnType, + estimateMaxPriorityFeePerGas, +} from '../actions/estimateMaxPriorityFeePerGas.js' + +export { + type GetAccountReturnType, + getAccount, +} from '../actions/getAccount.js' + +export { + type GetBalanceParameters, + type GetBalanceReturnType, + type GetBalanceErrorType, + getBalance, + /** @deprecated use `getBalance` instead */ + getBalance as fetchBalance, +} from '../actions/getBalance.js' + +export { + type GetBlockErrorType, + type GetBlockParameters, + type GetBlockReturnType, + getBlock, +} from '../actions/getBlock.js' + +export { + type GetBlockNumberErrorType, + type GetBlockNumberParameters, + type GetBlockNumberReturnType, + getBlockNumber, + /** @deprecated use `getBlockNumber` instead */ + getBlockNumber as fetchBlockNumber, +} from '../actions/getBlockNumber.js' + +export { + type GetBlockTransactionCountErrorType, + type GetBlockTransactionCountParameters, + type GetBlockTransactionCountReturnType, + getBlockTransactionCount, +} from '../actions/getBlockTransactionCount.js' + +export { + type GetBytecodeErrorType, + type GetBytecodeParameters, + type GetBytecodeReturnType, + getBytecode, +} from '../actions/getBytecode.js' + +export { + type GetCallsStatusErrorType, + type GetCallsStatusParameters, + type GetCallsStatusReturnType, + getCallsStatus, +} from '../actions/getCallsStatus.js' + +export { + type GetCapabilitiesErrorType, + type GetCapabilitiesParameters, + type GetCapabilitiesReturnType, + getCapabilities, +} from '../actions/getCapabilities.js' + +export { + type GetChainIdReturnType, + getChainId, +} from '../actions/getChainId.js' + +export { + type GetChainsReturnType, + getChains, +} from '../actions/getChains.js' + +export { + type GetClientParameters, + type GetClientReturnType, + getClient, +} from '../actions/getClient.js' + +export { + type GetConnectionsReturnType, + getConnections, +} from '../actions/getConnections.js' + +export { + type GetConnectorsReturnType, + getConnectors, +} from '../actions/getConnectors.js' + +export { + type GetConnectorClientErrorType, + type GetConnectorClientParameters, + type GetConnectorClientReturnType, + getConnectorClient, +} from '../actions/getConnectorClient.js' + +export { + type GetEnsAddressErrorType, + type GetEnsAddressParameters, + type GetEnsAddressReturnType, + getEnsAddress, + /** @deprecated use `getEnsAddress` instead */ + getEnsAddress as fetchEnsAddress, +} from '../actions/getEnsAddress.js' + +export { + type GetEnsAvatarErrorType, + type GetEnsAvatarParameters, + type GetEnsAvatarReturnType, + getEnsAvatar, + /** @deprecated use `getEnsAvatar` instead */ + getEnsAvatar as fetchEnsAvatar, +} from '../actions/getEnsAvatar.js' + +export { + type GetEnsNameErrorType, + type GetEnsNameParameters, + type GetEnsNameReturnType, + getEnsName, + /** @deprecated */ + getEnsName as fetchEnsName, +} from '../actions/getEnsName.js' + +export { + type GetEnsResolverErrorType, + type GetEnsResolverParameters, + type GetEnsResolverReturnType, + getEnsResolver, + /** @deprecated use `getEnsResolver` instead */ + getEnsResolver as fetchEnsResolver, +} from '../actions/getEnsResolver.js' + +export { + type GetEnsTextErrorType, + type GetEnsTextParameters, + type GetEnsTextReturnType, + getEnsText, +} from '../actions/getEnsText.js' + +export { + type GetFeeHistoryErrorType, + type GetFeeHistoryParameters, + type GetFeeHistoryReturnType, + getFeeHistory, +} from '../actions/getFeeHistory.js' + +export { + type GetGasPriceErrorType, + type GetGasPriceParameters, + type GetGasPriceReturnType, + getGasPrice, +} from '../actions/getGasPrice.js' + +export { + type GetProofErrorType, + type GetProofParameters, + type GetProofReturnType, + getProof, +} from '../actions/getProof.js' + +export { + type GetPublicClientParameters, + type GetPublicClientReturnType, + getPublicClient, +} from '../actions/getPublicClient.js' + +export { + type GetStorageAtErrorType, + type GetStorageAtParameters, + type GetStorageAtReturnType, + getStorageAt, +} from '../actions/getStorageAt.js' + +export { + type GetTokenErrorType, + type GetTokenParameters, + type GetTokenReturnType, + getToken, + /** @deprecated use `getToken` instead */ + getToken as fetchToken, +} from '../actions/getToken.js' + +export { + type GetTransactionErrorType, + type GetTransactionParameters, + type GetTransactionReturnType, + getTransaction, + /** @deprecated use `getTransaction` instead */ + getTransaction as fetchTransaction, +} from '../actions/getTransaction.js' + +export { + type GetTransactionConfirmationsErrorType, + type GetTransactionConfirmationsParameters, + type GetTransactionConfirmationsReturnType, + getTransactionConfirmations, +} from '../actions/getTransactionConfirmations.js' + +export { + type GetTransactionCountErrorType, + type GetTransactionCountParameters, + type GetTransactionCountReturnType, + getTransactionCount, +} from '../actions/getTransactionCount.js' + +export { + type GetTransactionReceiptErrorType, + type GetTransactionReceiptParameters, + type GetTransactionReceiptReturnType, + getTransactionReceipt, +} from '../actions/getTransactionReceipt.js' + +export { + type GetWalletClientErrorType, + type GetWalletClientParameters, + type GetWalletClientReturnType, + getWalletClient, +} from '../actions/getWalletClient.js' + +export { + type MulticallParameters, + type MulticallReturnType, + multicall, +} from '../actions/multicall.js' + +export { + type PrepareTransactionRequestParameters, + type PrepareTransactionRequestReturnType, + type PrepareTransactionRequestErrorType, + prepareTransactionRequest, +} from '../actions/prepareTransactionRequest.js' + +export { + type ReadContractParameters, + type ReadContractReturnType, + type ReadContractErrorType, + readContract, +} from '../actions/readContract.js' + +export { + type ReadContractsParameters, + type ReadContractsReturnType, + type ReadContractsErrorType, + readContracts, +} from '../actions/readContracts.js' + +export { + type ReconnectErrorType, + type ReconnectParameters, + type ReconnectReturnType, + reconnect, +} from '../actions/reconnect.js' + +export { + type SendCallsErrorType, + type SendCallsParameters, + type SendCallsReturnType, + sendCalls, +} from '../actions/sendCalls.js' + +export { + type SendTransactionErrorType, + type SendTransactionParameters, + type SendTransactionReturnType, + sendTransaction, +} from '../actions/sendTransaction.js' + +export { + type ShowCallsStatusErrorType, + type ShowCallsStatusParameters, + type ShowCallsStatusReturnType, + showCallsStatus, +} from '../actions/showCallsStatus.js' + +export { + type SignMessageErrorType, + type SignMessageParameters, + type SignMessageReturnType, + signMessage, +} from '../actions/signMessage.js' + +export { + type SignTypedDataErrorType, + type SignTypedDataParameters, + type SignTypedDataReturnType, + signTypedData, +} from '../actions/signTypedData.js' + +export { + type SimulateContractErrorType, + type SimulateContractParameters, + type SimulateContractReturnType, + simulateContract, +} from '../actions/simulateContract.js' + +export { + type SwitchAccountErrorType, + type SwitchAccountParameters, + type SwitchAccountReturnType, + switchAccount, +} from '../actions/switchAccount.js' + +export { + type SwitchChainErrorType, + type SwitchChainParameters, + type SwitchChainReturnType, + switchChain, + /** @deprecated use `switchChain` instead */ + switchChain as switchNetwork, +} from '../actions/switchChain.js' + +export { + type VerifyMessageParameters, + type VerifyMessageReturnType, + verifyMessage, +} from '../actions/verifyMessage.js' + +export { + type VerifyTypedDataParameters, + type VerifyTypedDataReturnType, + verifyTypedData, +} from '../actions/verifyTypedData.js' + +export { + type WaitForCallsStatusErrorType, + type WaitForCallsStatusParameters, + type WaitForCallsStatusReturnType, + waitForCallsStatus, +} from '../actions/waitForCallsStatus.js' + +export { + type WatchAccountParameters, + type WatchAccountReturnType, + watchAccount, +} from '../actions/watchAccount.js' + +export { + type WatchAssetParameters, + type WatchAssetReturnType, + watchAsset, +} from '../actions/watchAsset.js' + +export { + type WatchBlocksParameters, + type WatchBlocksReturnType, + watchBlocks, +} from '../actions/watchBlocks.js' + +export { + type WatchBlockNumberParameters, + type WatchBlockNumberReturnType, + watchBlockNumber, +} from '../actions/watchBlockNumber.js' + +export { + type WatchChainIdParameters, + type WatchChainIdReturnType, + watchChainId, +} from '../actions/watchChainId.js' + +export { + type WatchClientParameters, + type WatchClientReturnType, + watchClient, +} from '../actions/watchClient.js' + +export { + type WatchConnectionsParameters, + type WatchConnectionsReturnType, + watchConnections, +} from '../actions/watchConnections.js' + +export { + type WatchConnectorsParameters, + type WatchConnectorsReturnType, + watchConnectors, +} from '../actions/watchConnectors.js' + +export { + type WatchContractEventParameters, + type WatchContractEventReturnType, + watchContractEvent, +} from '../actions/watchContractEvent.js' + +export { + type WatchPendingTransactionsParameters, + type WatchPendingTransactionsReturnType, + watchPendingTransactions, +} from '../actions/watchPendingTransactions.js' + +export { + type WatchPublicClientParameters, + type WatchPublicClientReturnType, + watchPublicClient, +} from '../actions/watchPublicClient.js' + +export { + type WaitForTransactionReceiptErrorType, + type WaitForTransactionReceiptParameters, + type WaitForTransactionReceiptReturnType, + waitForTransactionReceipt, + /** @deprecated use `waitForTransactionReceipt` instead */ + waitForTransactionReceipt as waitForTransaction, +} from '../actions/waitForTransactionReceipt.js' + +export { + type WriteContractErrorType, + type WriteContractParameters, + type WriteContractReturnType, + writeContract, +} from '../actions/writeContract.js' diff --git a/packages/core/src/exports/chains.ts b/packages/core/src/exports/chains.ts new file mode 100644 index 0000000000..1fca7f537f --- /dev/null +++ b/packages/core/src/exports/chains.ts @@ -0,0 +1,7 @@ +//////////////////////////////////////////////////////////////////////////////// +// viem/chains +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +// biome-ignore lint/performance/noReExportAll: entrypoint module +export * from 'viem/chains' diff --git a/packages/core/src/exports/codegen.test.ts b/packages/core/src/exports/codegen.test.ts new file mode 100644 index 0000000000..c947b7d188 --- /dev/null +++ b/packages/core/src/exports/codegen.test.ts @@ -0,0 +1,14 @@ +import { expect, test } from 'vitest' + +import * as codegen from './codegen.js' + +test('exports', () => { + expect(Object.keys(codegen)).toMatchInlineSnapshot(` + [ + "createSimulateContract", + "createReadContract", + "createWatchContractEvent", + "createWriteContract", + ] + `) +}) diff --git a/packages/core/src/exports/codegen.ts b/packages/core/src/exports/codegen.ts new file mode 100644 index 0000000000..bed8aa1cc7 --- /dev/null +++ b/packages/core/src/exports/codegen.ts @@ -0,0 +1,24 @@ +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type CreateSimulateContractParameters, + type CreateSimulateContractReturnType, + createSimulateContract, +} from '../actions/codegen/createSimulateContract.js' + +export { + type CreateReadContractParameters, + type CreateReadContractReturnType, + createReadContract, +} from '../actions/codegen/createReadContract.js' + +export { + type CreateWatchContractEventParameters, + type CreateWatchContractEventReturnType, + createWatchContractEvent, +} from '../actions/codegen/createWatchContractEvent.js' + +export { + type CreateWriteContractParameters, + type CreateWriteContractReturnType, + createWriteContract, +} from '../actions/codegen/createWriteContract.js' diff --git a/packages/core/src/exports/experimental.ts b/packages/core/src/exports/experimental.ts new file mode 100644 index 0000000000..f74baa82cb --- /dev/null +++ b/packages/core/src/exports/experimental.ts @@ -0,0 +1,158 @@ +//////////////////////////////////////////////////////////////////////////////// +// Actions +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + /** @deprecated This is no longer experimental – use `import type { GetCallsStatusErrorType } from '@wagmi/core'` instead. */ + type GetCallsStatusErrorType, + /** @deprecated This is no longer experimental – use `import type { GetCallsStatusParameters } from '@wagmi/core'` instead. */ + type GetCallsStatusParameters, + /** @deprecated This is no longer experimental – use `import type { GetCallsStatusReturnType } from '@wagmi/core'` instead. */ + type GetCallsStatusReturnType, + /** @deprecated This is no longer experimental – use `import { getCallsStatus } from '@wagmi/core'` instead. */ + getCallsStatus, +} from '../actions/getCallsStatus.js' + +export { + /** @deprecated This is no longer experimental – use `import type { GetCapabilitiesErrorType } from '@wagmi/core'` instead. */ + type GetCapabilitiesErrorType, + /** @deprecated This is no longer experimental – use `import type { GetCapabilitiesParameters } from '@wagmi/core'` instead. */ + type GetCapabilitiesParameters, + /** @deprecated This is no longer experimental – use `import type { GetCapabilitiesReturnType } from '@wagmi/core'` instead. */ + type GetCapabilitiesReturnType, + /** @deprecated This is no longer experimental – use `import { getCapabilities } from '@wagmi/core'` instead. */ + getCapabilities, +} from '../actions/getCapabilities.js' + +export { + /** @deprecated This is no longer experimental – use `import type { SendCallsErrorType } from '@wagmi/core'` instead. */ + type SendCallsErrorType, + /** @deprecated This is no longer experimental – use `import type { SendCallsParameters } from '@wagmi/core'` instead. */ + type SendCallsParameters, + /** @deprecated This is no longer experimental – use `import type { SendCallsReturnType } from '@wagmi/core'` instead. */ + type SendCallsReturnType, + /** @deprecated This is no longer experimental – use `import { sendCalls } from '@wagmi/core'` instead. */ + sendCalls, +} from '../actions/sendCalls.js' + +export { + /** @deprecated This is no longer experimental – use `import type { ShowCallsStatusErrorType } from '@wagmi/core'` instead. */ + type ShowCallsStatusErrorType, + /** @deprecated This is no longer experimental – use `import type { ShowCallsStatusParameters } from '@wagmi/core'` instead. */ + type ShowCallsStatusParameters, + /** @deprecated This is no longer experimental – use `import type { ShowCallsStatusReturnType } from '@wagmi/core'` instead. */ + type ShowCallsStatusReturnType, + /** @deprecated This is no longer experimental – use `import { showCallsStatus } from '@wagmi/core'` instead. */ + showCallsStatus, +} from '../actions/showCallsStatus.js' + +export { + /** @deprecated This is no longer experimental – use `import type { WaitForCallsStatusErrorType } from '@wagmi/core'` instead. */ + type WaitForCallsStatusErrorType, + /** @deprecated This is no longer experimental – use `import type { WaitForCallsStatusParameters } from '@wagmi/core'` instead. */ + type WaitForCallsStatusParameters, + /** @deprecated This is no longer experimental – use `import type { WaitForCallsStatusReturnType } from '@wagmi/core'` instead. */ + type WaitForCallsStatusReturnType, + /** @deprecated This is no longer experimental – use `import { waitForCallsStatus } from '@wagmi/core'` instead. */ + waitForCallsStatus, +} from '../actions/waitForCallsStatus.js' + +export { + /** @deprecated Use `SendCallsErrorType` instead. */ + type WriteContractsErrorType, + /** @deprecated Use `SendCallsParameters` instead. */ + type WriteContractsParameters, + /** @deprecated Use `SendCallsReturnType` instead. */ + type WriteContractsReturnType, + /** @deprecated Use `sendCalls` instead. */ + writeContracts, +} from '../experimental/actions/writeContracts.js' + +//////////////////////////////////////////////////////////////////////////////// +// Tanstack Query +//////////////////////////////////////////////////////////////////////////////// + +export { + /** @deprecated This is no longer experimental – use `import type { GetCallsStatusData } from '@wagmi/core/query'` instead. */ + type GetCallsStatusData, + /** @deprecated This is no longer experimental – use `import type { GetCallsStatusOptions } from '@wagmi/core/query'` instead. */ + type GetCallsStatusOptions, + /** @deprecated This is no longer experimental – use `import type { GetCallsStatusQueryFnData } from '@wagmi/core/query'` instead. */ + type GetCallsStatusQueryFnData, + /** @deprecated This is no longer experimental – use `import type { GetCallsStatusQueryKey } from '@wagmi/core/query'` instead. */ + type GetCallsStatusQueryKey, + /** @deprecated This is no longer experimental – use `import { getCallsStatusQueryOptions } from '@wagmi/core/query'` instead. */ + getCallsStatusQueryOptions, + /** @deprecated This is no longer experimental – use `import { getCallsStatusQueryKey } from '@wagmi/core/query'` instead. */ + getCallsStatusQueryKey, +} from '../query/getCallsStatus.js' + +export { + /** @deprecated This is no longer experimental – use `import type { GetCapabilitiesData } from '@wagmi/core/query'` instead. */ + type GetCapabilitiesData, + /** @deprecated This is no longer experimental – use `import type { GetCapabilitiesOptions } from '@wagmi/core/query'` instead. */ + type GetCapabilitiesOptions, + /** @deprecated This is no longer experimental – use `import type { GetCapabilitiesQueryFnData } from '@wagmi/core/query'` instead. */ + type GetCapabilitiesQueryFnData, + /** @deprecated This is no longer experimental – use `import type { GetCapabilitiesQueryKey } from '@wagmi/core/query'` instead. */ + type GetCapabilitiesQueryKey, + /** @deprecated This is no longer experimental – use `import { getCapabilitiesQueryOptions } from '@wagmi/core/query'` instead. */ + getCapabilitiesQueryOptions, + /** @deprecated This is no longer experimental – use `import { getCapabilitiesQueryKey } from '@wagmi/core/query'` instead. */ + getCapabilitiesQueryKey, +} from '../query/getCapabilities.js' + +export { + /** @deprecated This is no longer experimental – use `import type { SendCallsData } from '@wagmi/core/query'` instead. */ + type SendCallsData, + /** @deprecated This is no longer experimental – use `import type { SendCallsMutate } from '@wagmi/core/query'` instead. */ + type SendCallsMutate, + /** @deprecated This is no longer experimental – use `import type { SendCallsMutateAsync } from '@wagmi/core/query'` instead. */ + type SendCallsMutateAsync, + /** @deprecated This is no longer experimental – use `import type { SendCallsVariables } from '@wagmi/core/query'` instead. */ + type SendCallsVariables, + /** @deprecated This is no longer experimental – use `import { sendCallsMutationOptions } from '@wagmi/core/query'` instead. */ + sendCallsMutationOptions, +} from '../query/sendCalls.js' + +export { + /** @deprecated This is no longer experimental – use `import type { ShowCallsStatusData } from '@wagmi/core/query'` instead. */ + type ShowCallsStatusData, + /** @deprecated This is no longer experimental – use `import type { ShowCallsStatusMutate } from '@wagmi/core/query'` instead. */ + type ShowCallsStatusMutate, + /** @deprecated This is no longer experimental – use `import type { ShowCallsStatusMutateAsync } from '@wagmi/core/query'` instead. */ + type ShowCallsStatusMutateAsync, + /** @deprecated This is no longer experimental – use `import type { ShowCallsStatusVariables } from '@wagmi/core/query'` instead. */ + type ShowCallsStatusVariables, + /** @deprecated This is no longer experimental – use `import { showCallsStatusMutationOptions } from '@wagmi/core/query'` instead. */ + showCallsStatusMutationOptions, +} from '../query/showCallsStatus.js' + +export { + /** @deprecated This is no longer experimental – use `import type { WaitForCallsStatusData } from '@wagmi/core/query'` instead. */ + type WaitForCallsStatusData, + /** @deprecated This is no longer experimental – use `import type { WaitForCallsStatusOptions } from '@wagmi/core/query'` instead. */ + type WaitForCallsStatusOptions, + /** @deprecated This is no longer experimental – use `import type { WaitForCallsStatusQueryFnData } from '@wagmi/core/query'` instead. */ + type WaitForCallsStatusQueryFnData, + /** @deprecated This is no longer experimental – use `import type { WaitForCallsStatusQueryKey } from '@wagmi/core/query'` instead. */ + type WaitForCallsStatusQueryKey, + /** @deprecated This is no longer experimental – use `import { waitForCallsStatusQueryKey } from '@wagmi/core/query'` instead. */ + waitForCallsStatusQueryKey, + /** @deprecated This is no longer experimental – use `import { waitForCallsStatusQueryOptions } from '@wagmi/core/query'` instead. */ + waitForCallsStatusQueryOptions, +} from '../query/waitForCallsStatus.js' + +export { + /** @deprecated Use `SendCallsData` instead. */ + type WriteContractsData, + /** @deprecated Use `SendCallsMutate` instead. */ + type WriteContractsMutate, + /** @deprecated Use `SendCallsMutateAsync` instead. */ + type WriteContractsMutateAsync, + /** @deprecated Use `SendCallsVariables` instead. */ + type WriteContractsVariables, + /** @deprecated Use `sendCallsMutationOptions` instead. */ + writeContractsMutationOptions, +} from '../experimental/query/writeContracts.js' diff --git a/packages/core/src/exports/index.test.ts b/packages/core/src/exports/index.test.ts new file mode 100644 index 0000000000..55f3885806 --- /dev/null +++ b/packages/core/src/exports/index.test.ts @@ -0,0 +1,117 @@ +import { expect, test } from 'vitest' + +import * as core from './index.js' + +test('exports', () => { + expect(Object.keys(core)).toMatchInlineSnapshot(` + [ + "call", + "connect", + "deployContract", + "disconnect", + "estimateGas", + "estimateFeesPerGas", + "estimateMaxPriorityFeePerGas", + "getAccount", + "getBalance", + "fetchBalance", + "getBlock", + "getBlockNumber", + "fetchBlockNumber", + "getBlockTransactionCount", + "getBytecode", + "getCallsStatus", + "getCapabilities", + "getChainId", + "getChains", + "getClient", + "getConnections", + "getConnectors", + "getConnectorClient", + "getEnsAddress", + "fetchEnsAddress", + "getEnsAvatar", + "fetchEnsAvatar", + "getEnsName", + "fetchEnsName", + "getEnsResolver", + "fetchEnsResolver", + "getEnsText", + "getFeeHistory", + "getGasPrice", + "getProof", + "getPublicClient", + "getStorageAt", + "getToken", + "fetchToken", + "getTransaction", + "fetchTransaction", + "getTransactionConfirmations", + "getTransactionCount", + "getTransactionReceipt", + "getWalletClient", + "multicall", + "prepareTransactionRequest", + "readContract", + "readContracts", + "reconnect", + "sendCalls", + "sendTransaction", + "showCallsStatus", + "signMessage", + "signTypedData", + "simulateContract", + "switchAccount", + "switchChain", + "switchNetwork", + "verifyMessage", + "verifyTypedData", + "waitForCallsStatus", + "watchAccount", + "watchAsset", + "watchBlocks", + "watchBlockNumber", + "watchChainId", + "watchClient", + "watchConnections", + "watchConnectors", + "watchContractEvent", + "watchPendingTransactions", + "watchPublicClient", + "waitForTransactionReceipt", + "waitForTransaction", + "writeContract", + "createConnector", + "injected", + "mock", + "createConfig", + "createStorage", + "noopStorage", + "hydrate", + "BaseError", + "ChainNotConfiguredError", + "ConnectorNotConnectedError", + "ConnectorAlreadyConnectedError", + "ConnectorNotFoundError", + "ConnectorAccountNotFoundError", + "ConnectorChainMismatchError", + "ConnectorUnavailableReconnectingError", + "ProviderNotFoundError", + "SwitchChainNotSupportedError", + "custom", + "http", + "webSocket", + "unstable_connector", + "fallback", + "cookieStorage", + "cookieToInitialState", + "parseCookie", + "deepEqual", + "deserialize", + "extractRpcUrls", + "normalizeChainId", + "serialize", + "version", + ] + `) +}) diff --git a/packages/core/src/exports/index.ts b/packages/core/src/exports/index.ts new file mode 100644 index 0000000000..1e0e210ea2 --- /dev/null +++ b/packages/core/src/exports/index.ts @@ -0,0 +1,594 @@ +//////////////////////////////////////////////////////////////////////////////// +// Actions +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type CallErrorType, + type CallParameters, + type CallReturnType, + call, +} from '../actions/call.js' + +export { + type ConnectErrorType, + type ConnectParameters, + type ConnectReturnType, + connect, +} from '../actions/connect.js' + +export { + type DeployContractErrorType, + type DeployContractParameters, + type DeployContractReturnType, + deployContract, +} from '../actions/deployContract.js' + +export { + type DisconnectErrorType, + type DisconnectParameters, + type DisconnectReturnType, + disconnect, +} from '../actions/disconnect.js' + +export { + type EstimateGasErrorType, + type EstimateGasParameters, + type EstimateGasReturnType, + estimateGas, +} from '../actions/estimateGas.js' + +export { + type EstimateFeesPerGasErrorType, + type EstimateFeesPerGasParameters, + type EstimateFeesPerGasReturnType, + estimateFeesPerGas, +} from '../actions/estimateFeesPerGas.js' + +export { + type EstimateMaxPriorityFeePerGasErrorType, + type EstimateMaxPriorityFeePerGasParameters, + type EstimateMaxPriorityFeePerGasReturnType, + estimateMaxPriorityFeePerGas, +} from '../actions/estimateMaxPriorityFeePerGas.js' + +export { + type GetAccountReturnType, + getAccount, +} from '../actions/getAccount.js' + +export { + type GetBalanceParameters, + type GetBalanceReturnType, + type GetBalanceErrorType, + getBalance, + /** @deprecated use `getBalance` instead */ + getBalance as fetchBalance, +} from '../actions/getBalance.js' + +export { + type GetBlockErrorType, + type GetBlockParameters, + type GetBlockReturnType, + getBlock, +} from '../actions/getBlock.js' + +export { + type GetBlockNumberErrorType, + type GetBlockNumberParameters, + type GetBlockNumberReturnType, + getBlockNumber, + /** @deprecated use `getBlockNumber` instead */ + getBlockNumber as fetchBlockNumber, +} from '../actions/getBlockNumber.js' + +export { + type GetBlockTransactionCountErrorType, + type GetBlockTransactionCountParameters, + type GetBlockTransactionCountReturnType, + getBlockTransactionCount, +} from '../actions/getBlockTransactionCount.js' + +export { + type GetBytecodeErrorType, + type GetBytecodeParameters, + type GetBytecodeReturnType, + getBytecode, +} from '../actions/getBytecode.js' + +export { + type GetCallsStatusErrorType, + type GetCallsStatusParameters, + type GetCallsStatusReturnType, + getCallsStatus, +} from '../actions/getCallsStatus.js' + +export { + type GetCapabilitiesErrorType, + type GetCapabilitiesParameters, + type GetCapabilitiesReturnType, + getCapabilities, +} from '../actions/getCapabilities.js' + +export { + type GetChainIdReturnType, + getChainId, +} from '../actions/getChainId.js' + +export { + type GetChainsReturnType, + getChains, +} from '../actions/getChains.js' + +export { + type GetClientParameters, + type GetClientReturnType, + getClient, +} from '../actions/getClient.js' + +export { + type GetConnectionsReturnType, + getConnections, +} from '../actions/getConnections.js' + +export { + type GetConnectorsReturnType, + getConnectors, +} from '../actions/getConnectors.js' + +export { + type GetConnectorClientErrorType, + type GetConnectorClientParameters, + type GetConnectorClientReturnType, + getConnectorClient, +} from '../actions/getConnectorClient.js' + +export { + type GetEnsAddressErrorType, + type GetEnsAddressParameters, + type GetEnsAddressReturnType, + getEnsAddress, + /** @deprecated use `getEnsAddress` instead */ + getEnsAddress as fetchEnsAddress, +} from '../actions/getEnsAddress.js' + +export { + type GetEnsAvatarErrorType, + type GetEnsAvatarParameters, + type GetEnsAvatarReturnType, + getEnsAvatar, + /** @deprecated use `getEnsAvatar` instead */ + getEnsAvatar as fetchEnsAvatar, +} from '../actions/getEnsAvatar.js' + +export { + type GetEnsNameErrorType, + type GetEnsNameParameters, + type GetEnsNameReturnType, + getEnsName, + /** @deprecated */ + getEnsName as fetchEnsName, +} from '../actions/getEnsName.js' + +export { + type GetEnsResolverErrorType, + type GetEnsResolverParameters, + type GetEnsResolverReturnType, + getEnsResolver, + /** @deprecated use `getEnsResolver` instead */ + getEnsResolver as fetchEnsResolver, +} from '../actions/getEnsResolver.js' + +export { + type GetEnsTextErrorType, + type GetEnsTextParameters, + type GetEnsTextReturnType, + getEnsText, +} from '../actions/getEnsText.js' + +export { + type GetFeeHistoryErrorType, + type GetFeeHistoryParameters, + type GetFeeHistoryReturnType, + getFeeHistory, +} from '../actions/getFeeHistory.js' + +export { + type GetGasPriceErrorType, + type GetGasPriceParameters, + type GetGasPriceReturnType, + getGasPrice, +} from '../actions/getGasPrice.js' + +export { + type GetProofErrorType, + type GetProofParameters, + type GetProofReturnType, + getProof, +} from '../actions/getProof.js' + +export { + type GetPublicClientParameters, + type GetPublicClientReturnType, + getPublicClient, +} from '../actions/getPublicClient.js' + +export { + type GetStorageAtErrorType, + type GetStorageAtParameters, + type GetStorageAtReturnType, + getStorageAt, +} from '../actions/getStorageAt.js' + +export { + type GetTokenErrorType, + type GetTokenParameters, + type GetTokenReturnType, + getToken, + /** @deprecated use `getToken` instead */ + getToken as fetchToken, +} from '../actions/getToken.js' + +export { + type GetTransactionErrorType, + type GetTransactionParameters, + type GetTransactionReturnType, + getTransaction, + /** @deprecated use `getTransaction` instead */ + getTransaction as fetchTransaction, +} from '../actions/getTransaction.js' + +export { + type GetTransactionConfirmationsErrorType, + type GetTransactionConfirmationsParameters, + type GetTransactionConfirmationsReturnType, + getTransactionConfirmations, +} from '../actions/getTransactionConfirmations.js' + +export { + type GetTransactionCountErrorType, + type GetTransactionCountParameters, + type GetTransactionCountReturnType, + getTransactionCount, +} from '../actions/getTransactionCount.js' + +export { + type GetTransactionReceiptErrorType, + type GetTransactionReceiptParameters, + type GetTransactionReceiptReturnType, + getTransactionReceipt, +} from '../actions/getTransactionReceipt.js' + +export { + type GetWalletClientErrorType, + type GetWalletClientParameters, + type GetWalletClientReturnType, + getWalletClient, +} from '../actions/getWalletClient.js' + +export { + type MulticallParameters, + type MulticallReturnType, + multicall, +} from '../actions/multicall.js' + +export { + type PrepareTransactionRequestErrorType, + type PrepareTransactionRequestParameters, + type PrepareTransactionRequestReturnType, + prepareTransactionRequest, +} from '../actions/prepareTransactionRequest.js' + +export { + type ReadContractParameters, + type ReadContractReturnType, + type ReadContractErrorType, + readContract, +} from '../actions/readContract.js' + +export { + type ReadContractsParameters, + type ReadContractsReturnType, + type ReadContractsErrorType, + readContracts, +} from '../actions/readContracts.js' + +export { + type ReconnectErrorType, + type ReconnectParameters, + type ReconnectReturnType, + reconnect, +} from '../actions/reconnect.js' + +export { + type SendCallsErrorType, + type SendCallsParameters, + type SendCallsReturnType, + sendCalls, +} from '../actions/sendCalls.js' + +export { + type SendTransactionErrorType, + type SendTransactionParameters, + type SendTransactionReturnType, + sendTransaction, +} from '../actions/sendTransaction.js' + +export { + type ShowCallsStatusErrorType, + type ShowCallsStatusParameters, + type ShowCallsStatusReturnType, + showCallsStatus, +} from '../actions/showCallsStatus.js' + +export { + type SignMessageErrorType, + type SignMessageParameters, + type SignMessageReturnType, + signMessage, +} from '../actions/signMessage.js' + +export { + type SignTypedDataErrorType, + type SignTypedDataParameters, + type SignTypedDataReturnType, + signTypedData, +} from '../actions/signTypedData.js' + +export { + type SimulateContractErrorType, + type SimulateContractParameters, + type SimulateContractReturnType, + simulateContract, +} from '../actions/simulateContract.js' + +export { + type SwitchAccountErrorType, + type SwitchAccountParameters, + type SwitchAccountReturnType, + switchAccount, +} from '../actions/switchAccount.js' + +export { + type SwitchChainErrorType, + type SwitchChainParameters, + type SwitchChainReturnType, + switchChain, + /** @deprecated use `switchChain` instead */ + switchChain as switchNetwork, +} from '../actions/switchChain.js' + +export { + type VerifyMessageErrorType, + type VerifyMessageParameters, + type VerifyMessageReturnType, + verifyMessage, +} from '../actions/verifyMessage.js' + +export { + type VerifyTypedDataErrorType, + type VerifyTypedDataParameters, + type VerifyTypedDataReturnType, + verifyTypedData, +} from '../actions/verifyTypedData.js' + +export { + type WaitForCallsStatusErrorType, + type WaitForCallsStatusParameters, + type WaitForCallsStatusReturnType, + waitForCallsStatus, +} from '../actions/waitForCallsStatus.js' + +export { + type WatchAccountParameters, + type WatchAccountReturnType, + watchAccount, +} from '../actions/watchAccount.js' + +export { + type WatchAssetParameters, + type WatchAssetErrorType, + type WatchAssetReturnType, + watchAsset, +} from '../actions/watchAsset.js' + +export { + type WatchBlocksParameters, + type WatchBlocksReturnType, + watchBlocks, +} from '../actions/watchBlocks.js' + +export { + type WatchBlockNumberParameters, + type WatchBlockNumberReturnType, + watchBlockNumber, +} from '../actions/watchBlockNumber.js' + +export { + type WatchChainIdParameters, + type WatchChainIdReturnType, + watchChainId, +} from '../actions/watchChainId.js' + +export { + type WatchClientParameters, + type WatchClientReturnType, + watchClient, +} from '../actions/watchClient.js' + +export { + type WatchConnectionsParameters, + type WatchConnectionsReturnType, + watchConnections, +} from '../actions/watchConnections.js' + +export { + type WatchConnectorsParameters, + type WatchConnectorsReturnType, + watchConnectors, +} from '../actions/watchConnectors.js' + +export { + type WatchContractEventParameters, + type WatchContractEventReturnType, + watchContractEvent, +} from '../actions/watchContractEvent.js' + +export { + type WatchPendingTransactionsParameters, + type WatchPendingTransactionsReturnType, + watchPendingTransactions, +} from '../actions/watchPendingTransactions.js' + +export { + type WatchPublicClientParameters, + type WatchPublicClientReturnType, + watchPublicClient, +} from '../actions/watchPublicClient.js' + +export { + type WaitForTransactionReceiptErrorType, + type WaitForTransactionReceiptParameters, + type WaitForTransactionReceiptReturnType, + waitForTransactionReceipt, + /** @deprecated use `waitForTransactionReceipt` instead */ + waitForTransactionReceipt as waitForTransaction, +} from '../actions/waitForTransactionReceipt.js' + +export { + type WriteContractErrorType, + type WriteContractParameters, + type WriteContractReturnType, + writeContract, +} from '../actions/writeContract.js' + +//////////////////////////////////////////////////////////////////////////////// +// Connectors +//////////////////////////////////////////////////////////////////////////////// + +export { + type ConnectorEventMap, + type CreateConnectorFn, + createConnector, +} from '../connectors/createConnector.js' + +export { + type InjectedParameters, + injected, +} from '../connectors/injected.js' + +export { + type MockParameters, + mock, +} from '../connectors/mock.js' + +//////////////////////////////////////////////////////////////////////////////// +// createConfig +//////////////////////////////////////////////////////////////////////////////// + +export { + type Connection, + type Connector, + type Config, + type CreateConfigParameters, + type PartializedState, + type State, + type Transport, + createConfig, +} from '../createConfig.js' + +//////////////////////////////////////////////////////////////////////////////// +// createStorage +//////////////////////////////////////////////////////////////////////////////// + +export { + type CreateStorageParameters, + type Storage, + type StorageItemMap, + createStorage, + noopStorage, +} from '../createStorage.js' + +//////////////////////////////////////////////////////////////////////////////// +// Hydrate +//////////////////////////////////////////////////////////////////////////////// + +export { hydrate } from '../hydrate.js' + +//////////////////////////////////////////////////////////////////////////////// +// Errors +//////////////////////////////////////////////////////////////////////////////// + +export { BaseError } from '../errors/base.js' + +export { + type ChainNotConfiguredErrorType, + ChainNotConfiguredError, + type ConnectorNotConnectedErrorType, + ConnectorNotConnectedError, + type ConnectorAlreadyConnectedErrorType, + ConnectorAlreadyConnectedError, + type ConnectorNotFoundErrorType, + ConnectorNotFoundError, + type ConnectorAccountNotFoundErrorType, + ConnectorAccountNotFoundError, + type ConnectorChainMismatchErrorType, + ConnectorChainMismatchError, + type ConnectorUnavailableReconnectingErrorType, + ConnectorUnavailableReconnectingError, +} from '../errors/config.js' + +export { + type ProviderNotFoundErrorType, + ProviderNotFoundError, + type SwitchChainNotSupportedErrorType, + SwitchChainNotSupportedError, +} from '../errors/connector.js' + +//////////////////////////////////////////////////////////////////////////////// +// Transports +//////////////////////////////////////////////////////////////////////////////// + +export { custom, http, webSocket } from 'viem' + +export { + type ConnectorTransportConfig, + type ConnectorTransport, + unstable_connector, +} from '../transports/connector.js' + +export { fallback } from '../transports/fallback.js' + +//////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////// + +export type { SelectChains } from '../types/chain.js' + +export type { Register, ResolvedRegister } from '../types/register.js' + +//////////////////////////////////////////////////////////////////////////////// +// Utilities +//////////////////////////////////////////////////////////////////////////////// + +export { + cookieStorage, + cookieToInitialState, + parseCookie, +} from '../utils/cookie.js' + +export { deepEqual } from '../utils/deepEqual.js' + +export { deserialize } from '../utils/deserialize.js' + +export { extractRpcUrls } from '../utils/extractRpcUrls.js' + +export { normalizeChainId } from '../utils/normalizeChainId.js' + +export { serialize } from '../utils/serialize.js' + +//////////////////////////////////////////////////////////////////////////////// +// Version +//////////////////////////////////////////////////////////////////////////////// + +export { version } from '../version.js' diff --git a/packages/core/src/exports/internal.test.ts b/packages/core/src/exports/internal.test.ts new file mode 100644 index 0000000000..425a1b4eba --- /dev/null +++ b/packages/core/src/exports/internal.test.ts @@ -0,0 +1,15 @@ +import { expect, test } from 'vitest' + +import * as internal from './internal.js' + +test('exports', () => { + expect(Object.keys(internal)).toMatchInlineSnapshot(` + [ + "watchChains", + "Emitter", + "createEmitter", + "deepEqual", + "uid", + ] + `) +}) diff --git a/packages/core/src/exports/internal.ts b/packages/core/src/exports/internal.ts new file mode 100644 index 0000000000..670420d89a --- /dev/null +++ b/packages/core/src/exports/internal.ts @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// Actions +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type WatchChainsParameters, + type WatchChainsReturnType, + watchChains, +} from '../actions/watchChains.js' + +//////////////////////////////////////////////////////////////////////////////// +// Emitter +//////////////////////////////////////////////////////////////////////////////// + +export { + type EventData, + Emitter, + createEmitter, +} from '../createEmitter.js' + +//////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////// + +export type { SelectChains } from '../types/chain.js' + +export type { + ChainIdParameter, + ConnectorParameter, + ScopeKeyParameter, +} from '../types/properties.js' + +export type { + Compute, + ExactPartial, + Mutable, + StrictOmit as Omit, + OneOf, + RemoveUndefined, + UnionCompute, + UnionStrictOmit, + UnionExactPartial, +} from '../types/utils.js' + +//////////////////////////////////////////////////////////////////////////////// +// Utilities +//////////////////////////////////////////////////////////////////////////////// + +export { deepEqual } from '../utils/deepEqual.js' + +export { uid } from '../utils/uid.js' diff --git a/packages/core/src/exports/query.test.ts b/packages/core/src/exports/query.test.ts new file mode 100644 index 0000000000..cbdaf925de --- /dev/null +++ b/packages/core/src/exports/query.test.ts @@ -0,0 +1,97 @@ +import { expect, test } from 'vitest' + +import * as query from './query.js' + +test('exports', () => { + expect(Object.keys(query)).toMatchInlineSnapshot(` + [ + "callQueryKey", + "callQueryOptions", + "connectMutationOptions", + "deployContractMutationOptions", + "disconnectMutationOptions", + "estimateFeesPerGasQueryKey", + "estimateFeesPerGasQueryOptions", + "estimateGasQueryKey", + "estimateGasQueryOptions", + "estimateMaxPriorityFeePerGasQueryKey", + "estimateMaxPriorityFeePerGasQueryOptions", + "getBalanceQueryKey", + "getBalanceQueryOptions", + "getBlockQueryKey", + "getBlockQueryOptions", + "getBlockNumberQueryKey", + "getBlockNumberQueryOptions", + "getBlockTransactionCountQueryKey", + "getBlockTransactionCountQueryOptions", + "getBytecodeQueryKey", + "getBytecodeQueryOptions", + "getCallsStatusQueryKey", + "getCallsStatusQueryOptions", + "getCapabilitiesQueryKey", + "getCapabilitiesQueryOptions", + "getConnectorClientQueryKey", + "getConnectorClientQueryOptions", + "getEnsAddressQueryKey", + "getEnsAddressQueryOptions", + "getEnsAvatarQueryKey", + "getEnsAvatarQueryOptions", + "getEnsNameQueryKey", + "getEnsNameQueryOptions", + "getEnsResolverQueryKey", + "getEnsResolverQueryOptions", + "getEnsTextQueryKey", + "getEnsTextQueryOptions", + "getFeeHistoryQueryKey", + "getFeeHistoryQueryOptions", + "getGasPriceQueryKey", + "getGasPriceQueryOptions", + "getProofQueryKey", + "getProofQueryOptions", + "getStorageAtQueryKey", + "getStorageAtQueryOptions", + "getTokenQueryKey", + "getTokenQueryOptions", + "getTransactionQueryKey", + "getTransactionQueryOptions", + "getTransactionConfirmationsQueryKey", + "getTransactionConfirmationsQueryOptions", + "getTransactionCountQueryKey", + "getTransactionCountQueryOptions", + "getTransactionReceiptQueryKey", + "getTransactionReceiptQueryOptions", + "getWalletClientQueryKey", + "getWalletClientQueryOptions", + "infiniteReadContractsQueryKey", + "infiniteReadContractsQueryOptions", + "prepareTransactionRequestQueryKey", + "prepareTransactionRequestQueryOptions", + "readContractQueryKey", + "readContractQueryOptions", + "readContractsQueryKey", + "readContractsQueryOptions", + "reconnectMutationOptions", + "sendCallsMutationOptions", + "showCallsStatusMutationOptions", + "sendTransactionMutationOptions", + "signMessageMutationOptions", + "signTypedDataMutationOptions", + "switchAccountMutationOptions", + "simulateContractQueryKey", + "simulateContractQueryOptions", + "switchChainMutationOptions", + "verifyMessageQueryKey", + "verifyMessageQueryOptions", + "verifyTypedDataQueryKey", + "verifyTypedDataQueryOptions", + "waitForCallsStatusQueryKey", + "waitForCallsStatusQueryOptions", + "waitForTransactionReceiptQueryKey", + "waitForTransactionReceiptQueryOptions", + "watchAssetMutationOptions", + "writeContractMutationOptions", + "hashFn", + "structuralSharing", + ] + `) +}) diff --git a/packages/core/src/exports/query.ts b/packages/core/src/exports/query.ts new file mode 100644 index 0000000000..d689f558d5 --- /dev/null +++ b/packages/core/src/exports/query.ts @@ -0,0 +1,434 @@ +//////////////////////////////////////////////////////////////////////////////// +// Tanstack Query +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type CallData, + type CallOptions, + type CallQueryFnData, + type CallQueryKey, + callQueryKey, + callQueryOptions, +} from '../query/call.js' + +export { + type ConnectData, + type ConnectVariables, + type ConnectMutate, + type ConnectMutateAsync, + connectMutationOptions, +} from '../query/connect.js' + +export { + type DeployContractData, + type DeployContractVariables, + type DeployContractMutate, + type DeployContractMutateAsync, + deployContractMutationOptions, +} from '../query/deployContract.js' + +export { + type DisconnectData, + type DisconnectVariables, + type DisconnectMutate, + type DisconnectMutateAsync, + disconnectMutationOptions, +} from '../query/disconnect.js' + +export { + type EstimateFeesPerGasData, + type EstimateFeesPerGasOptions, + type EstimateFeesPerGasQueryFnData, + type EstimateFeesPerGasQueryKey, + estimateFeesPerGasQueryKey, + estimateFeesPerGasQueryOptions, +} from '../query/estimateFeesPerGas.js' + +export { + type EstimateGasData, + type EstimateGasOptions, + type EstimateGasQueryFnData, + type EstimateGasQueryKey, + estimateGasQueryKey, + estimateGasQueryOptions, +} from '../query/estimateGas.js' + +export { + type EstimateMaxPriorityFeePerGasData, + type EstimateMaxPriorityFeePerGasOptions, + type EstimateMaxPriorityFeePerGasQueryFnData, + type EstimateMaxPriorityFeePerGasQueryKey, + estimateMaxPriorityFeePerGasQueryKey, + estimateMaxPriorityFeePerGasQueryOptions, +} from '../query/estimateMaxPriorityFeePerGas.js' + +export { + type GetBalanceData, + type GetBalanceOptions, + type GetBalanceQueryFnData, + type GetBalanceQueryKey, + getBalanceQueryKey, + getBalanceQueryOptions, +} from '../query/getBalance.js' + +export { + type GetBlockData, + type GetBlockOptions, + type GetBlockQueryFnData, + type GetBlockQueryKey, + getBlockQueryKey, + getBlockQueryOptions, +} from '../query/getBlock.js' + +export { + type GetBlockNumberData, + type GetBlockNumberOptions, + type GetBlockNumberQueryFnData, + type GetBlockNumberQueryKey, + getBlockNumberQueryKey, + getBlockNumberQueryOptions, +} from '../query/getBlockNumber.js' + +export { + type GetBlockTransactionCountData, + type GetBlockTransactionCountOptions, + type GetBlockTransactionCountQueryFnData, + type GetBlockTransactionCountQueryKey, + getBlockTransactionCountQueryKey, + getBlockTransactionCountQueryOptions, +} from '../query/getBlockTransactionCount.js' + +export { + type GetBytecodeData, + type GetBytecodeOptions, + type GetBytecodeQueryFnData, + type GetBytecodeQueryKey, + getBytecodeQueryKey, + getBytecodeQueryOptions, +} from '../query/getBytecode.js' + +export { + type GetCallsStatusData, + type GetCallsStatusOptions, + type GetCallsStatusQueryFnData, + type GetCallsStatusQueryKey, + getCallsStatusQueryKey, + getCallsStatusQueryOptions, +} from '../query/getCallsStatus.js' + +export { + type GetCapabilitiesData, + type GetCapabilitiesOptions, + type GetCapabilitiesQueryFnData, + type GetCapabilitiesQueryKey, + getCapabilitiesQueryKey, + getCapabilitiesQueryOptions, +} from '../query/getCapabilities.js' + +export { + type GetConnectorClientData, + type GetConnectorClientOptions, + type GetConnectorClientQueryFnData, + type GetConnectorClientQueryKey, + getConnectorClientQueryKey, + getConnectorClientQueryOptions, +} from '../query/getConnectorClient.js' + +export { + type GetEnsAddressData, + type GetEnsAddressOptions, + type GetEnsAddressQueryFnData, + type GetEnsAddressQueryKey, + getEnsAddressQueryKey, + getEnsAddressQueryOptions, +} from '../query/getEnsAddress.js' + +export { + type GetEnsAvatarData, + type GetEnsAvatarOptions, + type GetEnsAvatarQueryFnData, + type GetEnsAvatarQueryKey, + getEnsAvatarQueryKey, + getEnsAvatarQueryOptions, +} from '../query/getEnsAvatar.js' + +export { + type GetEnsNameData, + type GetEnsNameOptions, + type GetEnsNameQueryFnData, + type GetEnsNameQueryKey, + getEnsNameQueryKey, + getEnsNameQueryOptions, +} from '../query/getEnsName.js' + +export { + type GetEnsResolverData, + type GetEnsResolverOptions, + type GetEnsResolverQueryFnData, + type GetEnsResolverQueryKey, + getEnsResolverQueryKey, + getEnsResolverQueryOptions, +} from '../query/getEnsResolver.js' + +export { + type GetEnsTextData, + type GetEnsTextOptions, + type GetEnsTextQueryFnData, + type GetEnsTextQueryKey, + getEnsTextQueryKey, + getEnsTextQueryOptions, +} from '../query/getEnsText.js' + +export { + type GetFeeHistoryData, + type GetFeeHistoryOptions, + type GetFeeHistoryQueryFnData, + type GetFeeHistoryQueryKey, + getFeeHistoryQueryKey, + getFeeHistoryQueryOptions, +} from '../query/getFeeHistory.js' + +export { + type GetGasPriceData, + type GetGasPriceOptions, + type GetGasPriceQueryFnData, + type GetGasPriceQueryKey, + getGasPriceQueryKey, + getGasPriceQueryOptions, +} from '../query/getGasPrice.js' + +export { + type GetProofData, + type GetProofOptions, + type GetProofQueryFnData, + type GetProofQueryKey, + getProofQueryKey, + getProofQueryOptions, +} from '../query/getProof.js' + +export { + type GetStorageAtData, + type GetStorageAtOptions, + type GetStorageAtQueryFnData, + type GetStorageAtQueryKey, + getStorageAtQueryKey, + getStorageAtQueryOptions, +} from '../query/getStorageAt.js' + +export { + type GetTokenData, + type GetTokenOptions, + type GetTokenQueryFnData, + type GetTokenQueryKey, + getTokenQueryKey, + getTokenQueryOptions, +} from '../query/getToken.js' + +export { + type GetTransactionData, + type GetTransactionOptions, + type GetTransactionQueryFnData, + type GetTransactionQueryKey, + getTransactionQueryKey, + getTransactionQueryOptions, +} from '../query/getTransaction.js' + +export { + type GetTransactionConfirmationsData, + type GetTransactionConfirmationsOptions, + type GetTransactionConfirmationsQueryFnData, + type GetTransactionConfirmationsQueryKey, + getTransactionConfirmationsQueryKey, + getTransactionConfirmationsQueryOptions, +} from '../query/getTransactionConfirmations.js' + +export { + type GetTransactionCountData, + type GetTransactionCountOptions, + type GetTransactionCountQueryFnData, + type GetTransactionCountQueryKey, + getTransactionCountQueryKey, + getTransactionCountQueryOptions, +} from '../query/getTransactionCount.js' + +export { + type GetTransactionReceiptData, + type GetTransactionReceiptOptions, + type GetTransactionReceiptQueryFnData, + type GetTransactionReceiptQueryKey, + getTransactionReceiptQueryKey, + getTransactionReceiptQueryOptions, +} from '../query/getTransactionReceipt.js' + +export { + type GetWalletClientData, + type GetWalletClientOptions, + type GetWalletClientQueryFnData, + type GetWalletClientQueryKey, + getWalletClientQueryKey, + getWalletClientQueryOptions, +} from '../query/getWalletClient.js' + +export { + type InfiniteReadContractsData, + type InfiniteReadContractsOptions, + type InfiniteReadContractsQueryFnData, + type InfiniteReadContractsQueryKey, + infiniteReadContractsQueryKey, + infiniteReadContractsQueryOptions, +} from '../query/infiniteReadContracts.js' + +export { + type PrepareTransactionRequestData, + type PrepareTransactionRequestOptions, + type PrepareTransactionRequestQueryFnData, + type PrepareTransactionRequestQueryKey, + prepareTransactionRequestQueryKey, + prepareTransactionRequestQueryOptions, +} from '../query/prepareTransactionRequest.js' + +export { + type ReadContractData, + type ReadContractOptions, + type ReadContractQueryFnData, + type ReadContractQueryKey, + readContractQueryKey, + readContractQueryOptions, +} from '../query/readContract.js' + +export { + type ReadContractsData, + type ReadContractsOptions, + type ReadContractsQueryFnData, + type ReadContractsQueryKey, + readContractsQueryKey, + readContractsQueryOptions, +} from '../query/readContracts.js' + +export { + type ReconnectData, + type ReconnectVariables, + type ReconnectMutate, + type ReconnectMutateAsync, + reconnectMutationOptions, +} from '../query/reconnect.js' + +export { + type SendCallsData, + type SendCallsVariables, + type SendCallsMutate, + type SendCallsMutateAsync, + sendCallsMutationOptions, +} from '../query/sendCalls.js' + +export { + type ShowCallsStatusData, + type ShowCallsStatusVariables, + type ShowCallsStatusMutate, + type ShowCallsStatusMutateAsync, + showCallsStatusMutationOptions, +} from '../query/showCallsStatus.js' + +export { + type SendTransactionData, + type SendTransactionVariables, + type SendTransactionMutate, + type SendTransactionMutateAsync, + sendTransactionMutationOptions, +} from '../query/sendTransaction.js' + +export { + type SignMessageData, + type SignMessageVariables, + type SignMessageMutate, + type SignMessageMutateAsync, + signMessageMutationOptions, +} from '../query/signMessage.js' + +export { + type SignTypedDataData, + type SignTypedDataVariables, + type SignTypedDataMutate, + type SignTypedDataMutateAsync, + signTypedDataMutationOptions, +} from '../query/signTypedData.js' + +export { + type SwitchAccountData, + type SwitchAccountVariables, + type SwitchAccountMutate, + type SwitchAccountMutateAsync, + switchAccountMutationOptions, +} from '../query/switchAccount.js' + +export { + type SimulateContractData, + type SimulateContractOptions, + type SimulateContractQueryFnData, + type SimulateContractQueryKey, + simulateContractQueryKey, + simulateContractQueryOptions, +} from '../query/simulateContract.js' + +export { + type SwitchChainData, + type SwitchChainVariables, + type SwitchChainMutate, + type SwitchChainMutateAsync, + switchChainMutationOptions, +} from '../query/switchChain.js' + +export { + type VerifyMessageData, + type VerifyMessageOptions, + type VerifyMessageQueryFnData, + type VerifyMessageQueryKey, + verifyMessageQueryKey, + verifyMessageQueryOptions, +} from '../query/verifyMessage.js' + +export { + type VerifyTypedDataData, + type VerifyTypedDataOptions, + type VerifyTypedDataQueryFnData, + type VerifyTypedDataQueryKey, + verifyTypedDataQueryKey, + verifyTypedDataQueryOptions, +} from '../query/verifyTypedData.js' + +export { + type WaitForCallsStatusData, + type WaitForCallsStatusOptions, + type WaitForCallsStatusQueryFnData, + type WaitForCallsStatusQueryKey, + waitForCallsStatusQueryKey, + waitForCallsStatusQueryOptions, +} from '../query/waitForCallsStatus.js' + +export { + type WaitForTransactionReceiptData, + type WaitForTransactionReceiptOptions, + type WaitForTransactionReceiptQueryFnData, + type WaitForTransactionReceiptQueryKey, + waitForTransactionReceiptQueryKey, + waitForTransactionReceiptQueryOptions, +} from '../query/waitForTransactionReceipt.js' + +export { + type WatchAssetData, + type WatchAssetVariables, + type WatchAssetMutate, + type WatchAssetMutateAsync, + watchAssetMutationOptions, +} from '../query/watchAsset.js' + +export { + type WriteContractData, + type WriteContractVariables, + type WriteContractMutate, + type WriteContractMutateAsync, + writeContractMutationOptions, +} from '../query/writeContract.js' + +export { hashFn, structuralSharing } from '../query/utils.js' diff --git a/packages/core/src/hydrate.test.ts b/packages/core/src/hydrate.test.ts new file mode 100644 index 0000000000..3dc6e28549 --- /dev/null +++ b/packages/core/src/hydrate.test.ts @@ -0,0 +1,114 @@ +import { accounts, config, wait } from '@wagmi/test' +import type { EIP1193Provider } from 'mipd' +import { http } from 'viem' +import { mainnet } from 'viem/chains' +import { expect, test, vi } from 'vitest' + +import { createConnector } from './connectors/createConnector.js' +import { mock } from './connectors/mock.js' +import { createConfig } from './createConfig.js' +import { createStorage } from './createStorage.js' +import { hydrate } from './hydrate.js' +import { cookieStorage } from './utils/cookie.js' + +vi.mock(import('mipd'), async (importOriginal) => { + const mod = await importOriginal() + + let cache: typeof mod | undefined + if (!cache) + cache = { + ...mod, + createStore() { + const store = mod.createStore() + return { + ...store, + getProviders() { + const info = { + icon: 'data:image/svg+xml,', + uuid: crypto.randomUUID(), + } as const + const provider = '' as unknown as EIP1193Provider + return [ + { info: { ...info, name: 'Foo', rdns: 'com.foo' }, provider }, + { info: { ...info, name: 'Bar', rdns: 'com.bar' }, provider }, + { info: { ...info, name: 'Mock', rdns: 'com.mock' }, provider }, + ] + }, + } + }, + } + return cache +}) + +test('default', () => { + const { onMount } = hydrate(config, { + initialState: undefined, + reconnectOnMount: false, + }) + onMount() + + expect(onMount).toBeDefined() +}) + +test('initialState', () => { + const config = createConfig({ + chains: [mainnet], + transports: { [mainnet.id]: http() }, + ssr: true, + storage: createStorage({ storage: cookieStorage }), + }) + + const { onMount } = hydrate(config, { + initialState: { + chainId: 1, + current: null, + connections: new Map(), + status: 'disconnected', + }, + reconnectOnMount: true, + }) + onMount() + + expect(onMount).toBeDefined() +}) + +test('ssr', async () => { + const config = createConfig({ + chains: [mainnet], + connectors: [ + createConnector((c) => { + return { + ...mock({ accounts })(c), + rdns: 'com.mock', + } + }), + ], + ssr: true, + storage: createStorage({ storage: cookieStorage }), + transports: { [mainnet.id]: http() }, + }) + + const { onMount } = hydrate(config, { + initialState: { + chainId: 10, + current: null, + connections: new Map(), + status: 'disconnected', + }, + reconnectOnMount: false, + }) + onMount() + expect(onMount).toBeDefined() + expect(config.chains[0].id).toBe(1) + + await wait(100) + expect(config.connectors.map((x) => x.rdns ?? x.id)).toMatchInlineSnapshot( + ` + [ + "com.mock", + "com.foo", + "com.bar", + ] + `, + ) +}) diff --git a/packages/core/src/hydrate.ts b/packages/core/src/hydrate.ts new file mode 100644 index 0000000000..70bc2199e6 --- /dev/null +++ b/packages/core/src/hydrate.ts @@ -0,0 +1,62 @@ +import { reconnect } from './actions/reconnect.js' +import type { Config, State } from './createConfig.js' + +type HydrateParameters = { + initialState?: State | undefined + reconnectOnMount?: boolean | undefined +} + +export function hydrate(config: Config, parameters: HydrateParameters) { + const { initialState, reconnectOnMount } = parameters + + if (initialState && !config._internal.store.persist.hasHydrated()) + config.setState({ + ...initialState, + chainId: config.chains.some((x) => x.id === initialState.chainId) + ? initialState.chainId + : config.chains[0].id, + connections: reconnectOnMount ? initialState.connections : new Map(), + status: reconnectOnMount ? 'reconnecting' : 'disconnected', + }) + + return { + async onMount() { + if (config._internal.ssr) { + await config._internal.store.persist.rehydrate() + if (config._internal.mipd) { + config._internal.connectors.setState((connectors) => { + const rdnsSet = new Set() + for (const connector of connectors ?? []) { + if (connector.rdns) { + const rdnsValues = Array.isArray(connector.rdns) + ? connector.rdns + : [connector.rdns] + for (const rdns of rdnsValues) { + rdnsSet.add(rdns) + } + } + } + const mipdConnectors = [] + const providers = config._internal.mipd?.getProviders() ?? [] + for (const provider of providers) { + if (rdnsSet.has(provider.info.rdns)) continue + const connectorFn = + config._internal.connectors.providerDetailToConnector(provider) + const connector = config._internal.connectors.setup(connectorFn) + mipdConnectors.push(connector) + } + return [...connectors, ...mipdConnectors] + }) + } + } + + if (reconnectOnMount) reconnect(config) + else if (config.storage) + // Reset connections that may have been hydrated from storage. + config.setState((x) => ({ + ...x, + connections: new Map(), + })) + }, + } +} diff --git a/packages/core/src/query/call.test.ts b/packages/core/src/query/call.test.ts new file mode 100644 index 0000000000..1e9ee03f7a --- /dev/null +++ b/packages/core/src/query/call.test.ts @@ -0,0 +1,306 @@ +import { accounts, address, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { parseEther, parseGwei } from 'viem' +import { callQueryOptions } from './call.js' + +const name4bytes = '0x06fdde03' +const account = accounts[0] + +test('default', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: accessList', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + accessList: [ + { + address: '0x1', + storageKeys: ['0x1'], + }, + ], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "accessList": [ + { + "address": "0x1", + "storageKeys": [ + "0x1", + ], + }, + ], + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: blockNumber', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + blockNumber: 1234567890n, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockNumber": 1234567890n, + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: blockTag', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + blockTag: 'safe', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockTag": "safe", + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + chainId: chain.mainnet2.id, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 456, + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: gas', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + gas: 100000n, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "gas": 100000n, + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: gasPrice', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + gasPrice: parseGwei('20'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "gasPrice": 20000000000n, + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: maxFeePerGas', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + maxFeePerGas: parseGwei('20'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "maxFeePerGas": 20000000000n, + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: maxPriorityFeePerGas', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + maxPriorityFeePerGas: parseGwei('2'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "maxPriorityFeePerGas": 2000000000n, + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: nonce', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + nonce: 123, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "nonce": 123, + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: type', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + type: 'eip1559', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "type": "eip1559", + }, + ], + } + `) +}) + +test('parameters: value', () => { + expect( + callQueryOptions(config, { + account, + data: name4bytes, + to: address.wagmiMintExample, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "value": 1000000000000000000n, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/call.ts b/packages/core/src/query/call.ts new file mode 100644 index 0000000000..2ca4491b86 --- /dev/null +++ b/packages/core/src/query/call.ts @@ -0,0 +1,51 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type CallErrorType, + type CallParameters, + type CallReturnType, + call, +} from '../actions/call.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type CallOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function callQueryOptions( + config: config, + options: CallOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + const data = await call(config, { + ...parameters, + } as CallParameters) + return data ?? null + }, + queryKey: callQueryKey(options), + } as const satisfies QueryOptions< + CallQueryFnData, + CallErrorType, + CallData, + CallQueryKey + > +} + +export type CallQueryFnData = CallReturnType + +export type CallData = CallQueryFnData + +export function callQueryKey( + options: CallOptions, +) { + return ['call', filterQueryOptions(options)] as const +} + +export type CallQueryKey = ReturnType< + typeof callQueryKey +> diff --git a/packages/core/src/query/connect.test.ts b/packages/core/src/query/connect.test.ts new file mode 100644 index 0000000000..b8300ee0ee --- /dev/null +++ b/packages/core/src/query/connect.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { connectMutationOptions } from './connect.js' + +test('default', () => { + expect(connectMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "connect", + ], + } + `) +}) diff --git a/packages/core/src/query/connect.ts b/packages/core/src/query/connect.ts new file mode 100644 index 0000000000..f521512789 --- /dev/null +++ b/packages/core/src/query/connect.ts @@ -0,0 +1,70 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import { + type ConnectErrorType, + type ConnectParameters, + type ConnectReturnType, + connect, +} from '../actions/connect.js' +import type { Config, Connector } from '../createConfig.js' + +import type { CreateConnectorFn } from '../connectors/createConnector.js' +import type { Compute } from '../types/utils.js' + +export function connectMutationOptions(config: config) { + return { + mutationFn(variables) { + return connect(config, variables) + }, + mutationKey: ['connect'], + } as const satisfies MutationOptions< + ConnectData, + ConnectErrorType, + ConnectVariables + > +} + +export type ConnectData = ConnectReturnType + +export type ConnectVariables< + config extends Config, + connector extends Connector | CreateConnectorFn, +> = ConnectParameters + +export type ConnectMutate = < + connector extends + | config['connectors'][number] + | Connector + | CreateConnectorFn, +>( + variables: ConnectVariables, + options?: + | Compute< + MutateOptions< + ConnectData, + ConnectErrorType, + Compute>, + context + > + > + | undefined, +) => void + +export type ConnectMutateAsync = < + connector extends + | config['connectors'][number] + | Connector + | CreateConnectorFn, +>( + variables: ConnectVariables, + options?: + | Compute< + MutateOptions< + ConnectData, + ConnectErrorType, + Compute>, + context + > + > + | undefined, +) => Promise> diff --git a/packages/core/src/query/deployContract.test.ts b/packages/core/src/query/deployContract.test.ts new file mode 100644 index 0000000000..51157c03df --- /dev/null +++ b/packages/core/src/query/deployContract.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { deployContractMutationOptions } from './deployContract.js' + +test('default', () => { + expect(deployContractMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "deployContract", + ], + } + `) +}) diff --git a/packages/core/src/query/deployContract.ts b/packages/core/src/query/deployContract.ts new file mode 100644 index 0000000000..d4fac04531 --- /dev/null +++ b/packages/core/src/query/deployContract.ts @@ -0,0 +1,73 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' +import type { Abi, ContractConstructorArgs } from 'viem' + +import { + type DeployContractErrorType, + type DeployContractParameters, + type DeployContractReturnType, + deployContract, +} from '../actions/deployContract.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function deployContractMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return deployContract(config, variables) + }, + mutationKey: ['deployContract'], + } as const satisfies MutationOptions< + DeployContractData, + DeployContractErrorType, + DeployContractVariables + > +} + +export type DeployContractData = Compute + +export type DeployContractVariables< + abi extends Abi | readonly unknown[], + config extends Config, + chainId extends config['chains'][number]['id'], + /// + allArgs = ContractConstructorArgs, +> = DeployContractParameters + +export type DeployContractMutate = < + abi extends Abi | readonly unknown[], + chainId extends config['chains'][number]['id'], +>( + variables: DeployContractVariables, + options?: + | Compute< + MutateOptions< + DeployContractData, + DeployContractErrorType, + Compute>, + context + > + > + | undefined, +) => void + +export type DeployContractMutateAsync< + config extends Config, + context = unknown, +> = < + abi extends Abi | readonly unknown[], + chainId extends config['chains'][number]['id'], +>( + variables: DeployContractVariables, + options?: + | Compute< + MutateOptions< + DeployContractData, + DeployContractErrorType, + Compute>, + context + > + > + | undefined, +) => Promise diff --git a/packages/core/src/query/disconnect.test.ts b/packages/core/src/query/disconnect.test.ts new file mode 100644 index 0000000000..4637c7e88f --- /dev/null +++ b/packages/core/src/query/disconnect.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { disconnectMutationOptions } from './disconnect.js' + +test('default', () => { + expect(disconnectMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "disconnect", + ], + } + `) +}) diff --git a/packages/core/src/query/disconnect.ts b/packages/core/src/query/disconnect.ts new file mode 100644 index 0000000000..018873da03 --- /dev/null +++ b/packages/core/src/query/disconnect.ts @@ -0,0 +1,43 @@ +import type { MutationOptions } from '@tanstack/query-core' + +import { + type DisconnectErrorType, + type DisconnectParameters, + type DisconnectReturnType, + disconnect, +} from '../actions/disconnect.js' +import type { Config } from '../createConfig.js' +import type { Mutate, MutateAsync } from './types.js' + +export function disconnectMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return disconnect(config, variables) + }, + mutationKey: ['disconnect'], + } as const satisfies MutationOptions< + DisconnectData, + DisconnectErrorType, + DisconnectVariables + > +} + +export type DisconnectData = DisconnectReturnType + +export type DisconnectVariables = DisconnectParameters | undefined + +export type DisconnectMutate = Mutate< + DisconnectData, + DisconnectErrorType, + DisconnectVariables, + context +> + +export type DisconnectMutateAsync = MutateAsync< + DisconnectData, + DisconnectErrorType, + DisconnectVariables, + context +> diff --git a/packages/core/src/query/estimateFeesPerGas.test.ts b/packages/core/src/query/estimateFeesPerGas.test.ts new file mode 100644 index 0000000000..6b9965e651 --- /dev/null +++ b/packages/core/src/query/estimateFeesPerGas.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { estimateFeesPerGasQueryOptions } from './estimateFeesPerGas.js' + +test('default', () => { + expect(estimateFeesPerGasQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "estimateFeesPerGas", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + estimateFeesPerGasQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "estimateFeesPerGas", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/estimateFeesPerGas.ts b/packages/core/src/query/estimateFeesPerGas.ts new file mode 100644 index 0000000000..7cf2f607c8 --- /dev/null +++ b/packages/core/src/query/estimateFeesPerGas.ts @@ -0,0 +1,56 @@ +import type { QueryOptions } from '@tanstack/query-core' +import type { FeeValuesType } from 'viem' + +import { + type EstimateFeesPerGasErrorType, + type EstimateFeesPerGasParameters, + type EstimateFeesPerGasReturnType, + estimateFeesPerGas, +} from '../actions/estimateFeesPerGas.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type EstimateFeesPerGasOptions< + type extends FeeValuesType, + config extends Config, +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function estimateFeesPerGasQueryOptions< + config extends Config, + type extends FeeValuesType = 'eip1559', +>(config: config, options: EstimateFeesPerGasOptions = {}) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + return estimateFeesPerGas(config, parameters) + }, + queryKey: estimateFeesPerGasQueryKey(options), + } as const satisfies QueryOptions< + EstimateFeesPerGasQueryFnData, + EstimateFeesPerGasErrorType, + EstimateFeesPerGasData, + EstimateFeesPerGasQueryKey + > +} + +export type EstimateFeesPerGasQueryFnData = + EstimateFeesPerGasReturnType + +export type EstimateFeesPerGasData = + EstimateFeesPerGasQueryFnData + +export function estimateFeesPerGasQueryKey< + config extends Config, + type extends FeeValuesType = 'eip1559', +>(options: EstimateFeesPerGasOptions = {}) { + return ['estimateFeesPerGas', filterQueryOptions(options)] as const +} + +export type EstimateFeesPerGasQueryKey< + config extends Config, + type extends FeeValuesType, +> = ReturnType> diff --git a/packages/core/src/query/estimateGas.test-d.ts b/packages/core/src/query/estimateGas.test-d.ts new file mode 100644 index 0000000000..e48c4b089a --- /dev/null +++ b/packages/core/src/query/estimateGas.test-d.ts @@ -0,0 +1,55 @@ +import { http, type Address, parseEther } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type EstimateGasOptions, + estimateGasQueryOptions, +} from './estimateGas.js' + +test('chain formatters', () => { + const config = createConfig({ + chains: [mainnet, celo], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = EstimateGasOptions< + typeof config, + (typeof config)['chains'][number]['id'] + > + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + estimateGasQueryOptions(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + feeCurrency: '0x', + }) + + type Result2 = EstimateGasOptions + expectTypeOf().toMatchTypeOf<{ + functionName?: 'approve' | 'transfer' | 'transferFrom' | undefined + args?: readonly [Address, Address, bigint] | undefined + feeCurrency?: `0x${string}` | undefined + }>() + estimateGasQueryOptions(config, { + chainId: celo.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + feeCurrency: '0x', + }) + + type Result3 = EstimateGasOptions + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + estimateGasQueryOptions(config, { + chainId: mainnet.id, + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/core/src/query/estimateGas.test.ts b/packages/core/src/query/estimateGas.test.ts new file mode 100644 index 0000000000..6c31cadbc1 --- /dev/null +++ b/packages/core/src/query/estimateGas.test.ts @@ -0,0 +1,25 @@ +import { config } from '@wagmi/test' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { estimateGasQueryOptions } from './estimateGas.js' + +test('default', () => { + expect( + estimateGasQueryOptions(config, { + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "estimateGas", + { + "to": "0xd2135CfB216b74109775236E36d4b433F1DF507B", + "value": 10000000000000000n, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/estimateGas.ts b/packages/core/src/query/estimateGas.ts new file mode 100644 index 0000000000..749002f92c --- /dev/null +++ b/packages/core/src/query/estimateGas.ts @@ -0,0 +1,56 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type EstimateGasErrorType, + type EstimateGasParameters, + type EstimateGasReturnType, + estimateGas, +} from '../actions/estimateGas.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { UnionExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type EstimateGasOptions< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, +> = UnionExactPartial> & + ScopeKeyParameter + +export function estimateGasQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: EstimateGasOptions = {} as any) { + return { + async queryFn({ queryKey }) { + const { connector } = options + const { account, scopeKey: _, ...parameters } = queryKey[1] + if (!account && !connector) + throw new Error('account or connector is required') + return estimateGas(config, { account, connector, ...(parameters as any) }) + }, + queryKey: estimateGasQueryKey(options), + } as const satisfies QueryOptions< + EstimateGasQueryFnData, + EstimateGasErrorType, + EstimateGasData, + EstimateGasQueryKey + > +} + +export type EstimateGasQueryFnData = EstimateGasReturnType + +export type EstimateGasData = EstimateGasQueryFnData + +export function estimateGasQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, +>(options: EstimateGasOptions = {} as any) { + const { connector: _, ...rest } = options + return ['estimateGas', filterQueryOptions(rest)] as const +} + +export type EstimateGasQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, +> = ReturnType> diff --git a/packages/core/src/query/estimateMaxPriorityFeePerGas.test.ts b/packages/core/src/query/estimateMaxPriorityFeePerGas.test.ts new file mode 100644 index 0000000000..38bcde076d --- /dev/null +++ b/packages/core/src/query/estimateMaxPriorityFeePerGas.test.ts @@ -0,0 +1,36 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { estimateMaxPriorityFeePerGasQueryOptions } from './estimateMaxPriorityFeePerGas.js' + +test('default', () => { + expect( + estimateMaxPriorityFeePerGasQueryOptions(config), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "estimateMaxPriorityFeePerGas", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + estimateMaxPriorityFeePerGasQueryOptions(config, { + chainId: chain.mainnet.id, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "estimateMaxPriorityFeePerGas", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/estimateMaxPriorityFeePerGas.ts b/packages/core/src/query/estimateMaxPriorityFeePerGas.ts new file mode 100644 index 0000000000..cb58e65a79 --- /dev/null +++ b/packages/core/src/query/estimateMaxPriorityFeePerGas.ts @@ -0,0 +1,51 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type EstimateMaxPriorityFeePerGasErrorType, + type EstimateMaxPriorityFeePerGasParameters, + type EstimateMaxPriorityFeePerGasReturnType, + estimateMaxPriorityFeePerGas, +} from '../actions/estimateMaxPriorityFeePerGas.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type EstimateMaxPriorityFeePerGasOptions = + Compute< + ExactPartial> & + ScopeKeyParameter + > + +export function estimateMaxPriorityFeePerGasQueryOptions( + config: config, + options: EstimateMaxPriorityFeePerGasOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + return estimateMaxPriorityFeePerGas(config, parameters) + }, + queryKey: estimateMaxPriorityFeePerGasQueryKey(options), + } as const satisfies QueryOptions< + EstimateMaxPriorityFeePerGasQueryFnData, + EstimateMaxPriorityFeePerGasErrorType, + EstimateMaxPriorityFeePerGasData, + EstimateMaxPriorityFeePerGasQueryKey + > +} + +export type EstimateMaxPriorityFeePerGasQueryFnData = + EstimateMaxPriorityFeePerGasReturnType + +export type EstimateMaxPriorityFeePerGasData = + EstimateMaxPriorityFeePerGasQueryFnData + +export function estimateMaxPriorityFeePerGasQueryKey( + options: EstimateMaxPriorityFeePerGasOptions = {}, +) { + return ['estimateMaxPriorityFeePerGas', filterQueryOptions(options)] as const +} + +export type EstimateMaxPriorityFeePerGasQueryKey = + ReturnType> diff --git a/packages/core/src/query/getBalance.test.ts b/packages/core/src/query/getBalance.test.ts new file mode 100644 index 0000000000..1f9e1ecda9 --- /dev/null +++ b/packages/core/src/query/getBalance.test.ts @@ -0,0 +1,63 @@ +import { accounts, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBalanceQueryOptions } from './getBalance.js' + +const address = accounts[0] + +test('default', () => { + expect(getBalanceQueryOptions(config, { address })).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "balance", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getBalanceQueryOptions(config, { address, chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "balance", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + }, + ], + } + `) +}) + +test.todo('parameters: token') + +test('parameters: unit', () => { + expect( + getBalanceQueryOptions(config, { + address, + chainId: chain.mainnet.id, + token: '0x0000000000000000000000000000000000000000', + unit: 'gwei', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "balance", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "token": "0x0000000000000000000000000000000000000000", + "unit": "gwei", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getBalance.ts b/packages/core/src/query/getBalance.ts new file mode 100644 index 0000000000..e1dd287633 --- /dev/null +++ b/packages/core/src/query/getBalance.ts @@ -0,0 +1,53 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetBalanceErrorType, + type GetBalanceParameters, + type GetBalanceReturnType, + getBalance, +} from '../actions/getBalance.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, PartialBy } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetBalanceOptions = Compute< + PartialBy, 'address'> & ScopeKeyParameter +> + +export function getBalanceQueryOptions( + config: config, + options: GetBalanceOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { address, scopeKey: _, ...parameters } = queryKey[1] + if (!address) throw new Error('address is required') + const balance = await getBalance(config, { + ...(parameters as GetBalanceParameters), + address, + }) + return balance ?? null + }, + queryKey: getBalanceQueryKey(options), + } as const satisfies QueryOptions< + GetBalanceQueryFnData, + GetBalanceErrorType, + GetBalanceData, + GetBalanceQueryKey + > +} + +export type GetBalanceQueryFnData = Compute + +export type GetBalanceData = GetBalanceQueryFnData + +export function getBalanceQueryKey( + options: GetBalanceOptions = {}, +) { + return ['balance', filterQueryOptions(options)] as const +} + +export type GetBalanceQueryKey = ReturnType< + typeof getBalanceQueryKey +> diff --git a/packages/core/src/query/getBlock.test.ts b/packages/core/src/query/getBlock.test.ts new file mode 100644 index 0000000000..f31fb8c26e --- /dev/null +++ b/packages/core/src/query/getBlock.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBlockQueryOptions } from './getBlock.js' + +test('default', () => { + expect(getBlockQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "block", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getBlockQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "block", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getBlock.ts b/packages/core/src/query/getBlock.ts new file mode 100644 index 0000000000..f8f4db8417 --- /dev/null +++ b/packages/core/src/query/getBlock.ts @@ -0,0 +1,84 @@ +import type { QueryOptions } from '@tanstack/query-core' +import type { BlockTag } from 'viem' + +import { + type GetBlockErrorType, + type GetBlockParameters, + type GetBlockReturnType, + getBlock, +} from '../actions/getBlock.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetBlockOptions< + includeTransactions extends boolean, + blockTag extends BlockTag, + config extends Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = Compute< + ExactPartial< + GetBlockParameters + > & + ScopeKeyParameter +> + +export function getBlockQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], + includeTransactions extends boolean, + blockTag extends BlockTag, +>( + config: config, + options: GetBlockOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + const block = await getBlock(config, parameters) + return (block ?? null) as any + }, + queryKey: getBlockQueryKey(options), + } as const satisfies QueryOptions< + GetBlockQueryFnData, + GetBlockErrorType, + GetBlockData, + GetBlockQueryKey + > +} + +export type GetBlockQueryFnData< + includeTransactions extends boolean, + blockTag extends BlockTag, + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetBlockReturnType + +export type GetBlockData< + includeTransactions extends boolean, + blockTag extends BlockTag, + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetBlockQueryFnData + +export function getBlockQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', +>( + options: GetBlockOptions = {}, +) { + return ['block', filterQueryOptions(options)] as const +} + +export type GetBlockQueryKey< + includeTransactions extends boolean, + blockTag extends BlockTag, + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType< + typeof getBlockQueryKey +> diff --git a/packages/core/src/query/getBlockNumber.test.ts b/packages/core/src/query/getBlockNumber.test.ts new file mode 100644 index 0000000000..b2ff0c2d14 --- /dev/null +++ b/packages/core/src/query/getBlockNumber.test.ts @@ -0,0 +1,34 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBlockNumberQueryOptions } from './getBlockNumber.js' + +test('default', () => { + expect(getBlockNumberQueryOptions(config)).toMatchInlineSnapshot(` + { + "gcTime": 0, + "queryFn": [Function], + "queryKey": [ + "blockNumber", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getBlockNumberQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "gcTime": 0, + "queryFn": [Function], + "queryKey": [ + "blockNumber", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getBlockNumber.ts b/packages/core/src/query/getBlockNumber.ts new file mode 100644 index 0000000000..9585068a92 --- /dev/null +++ b/packages/core/src/query/getBlockNumber.ts @@ -0,0 +1,55 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetBlockNumberErrorType, + type GetBlockNumberParameters, + type GetBlockNumberReturnType, + getBlockNumber, +} from '../actions/getBlockNumber.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetBlockNumberOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getBlockNumberQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: GetBlockNumberOptions = {}) { + return { + gcTime: 0, + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + const blockNumber = await getBlockNumber(config, parameters) + return blockNumber ?? null + }, + queryKey: getBlockNumberQueryKey(options), + } as const satisfies QueryOptions< + GetBlockNumberQueryFnData, + GetBlockNumberErrorType, + GetBlockNumberData, + GetBlockNumberQueryKey + > +} + +export type GetBlockNumberQueryFnData = GetBlockNumberReturnType + +export type GetBlockNumberData = GetBlockNumberQueryFnData + +export function getBlockNumberQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetBlockNumberOptions = {}) { + return ['blockNumber', filterQueryOptions(options)] as const +} + +export type GetBlockNumberQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/getBlockTransactionCount.test.ts b/packages/core/src/query/getBlockTransactionCount.test.ts new file mode 100644 index 0000000000..d6c45ee09b --- /dev/null +++ b/packages/core/src/query/getBlockTransactionCount.test.ts @@ -0,0 +1,50 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBlockTransactionCountQueryOptions } from './getBlockTransactionCount.js' + +test('default', () => { + expect(getBlockTransactionCountQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "blockTransactionCount", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getBlockTransactionCountQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "blockTransactionCount", + { + "chainId": 1, + }, + ], + } + `) +}) + +test('parameters: blockTag', () => { + expect( + getBlockTransactionCountQueryOptions(config, { + blockTag: 'earliest', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "blockTransactionCount", + { + "blockTag": "earliest", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getBlockTransactionCount.ts b/packages/core/src/query/getBlockTransactionCount.ts new file mode 100644 index 0000000000..453e95e001 --- /dev/null +++ b/packages/core/src/query/getBlockTransactionCount.ts @@ -0,0 +1,62 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetBlockTransactionCountErrorType, + type GetBlockTransactionCountParameters, + type GetBlockTransactionCountReturnType, + getBlockTransactionCount, +} from '../actions/getBlockTransactionCount.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { ExactPartial, UnionCompute } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetBlockTransactionCountOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = UnionCompute< + ExactPartial> & + ScopeKeyParameter +> + +export function getBlockTransactionCountQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + options: GetBlockTransactionCountOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + const blockTransactionCount = await getBlockTransactionCount( + config, + parameters, + ) + return blockTransactionCount ?? null + }, + queryKey: getBlockTransactionCountQueryKey(options), + } as const satisfies QueryOptions< + GetBlockTransactionCountQueryFnData, + GetBlockTransactionCountErrorType, + GetBlockTransactionCountData, + GetBlockTransactionCountQueryKey + > +} + +export type GetBlockTransactionCountQueryFnData = + GetBlockTransactionCountReturnType + +export type GetBlockTransactionCountData = GetBlockTransactionCountQueryFnData + +export function getBlockTransactionCountQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetBlockTransactionCountOptions = {}) { + return ['blockTransactionCount', filterQueryOptions(options)] as const +} + +export type GetBlockTransactionCountQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/getBytecode.test.ts b/packages/core/src/query/getBytecode.test.ts new file mode 100644 index 0000000000..83f22ebf2e --- /dev/null +++ b/packages/core/src/query/getBytecode.test.ts @@ -0,0 +1,82 @@ +import { address, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getBytecodeQueryOptions } from './getBytecode.js' + +test('default', () => { + expect( + getBytecodeQueryOptions(config, { + address: address.wagmiMintExample, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getBytecodeQueryOptions(config, { + address: address.wagmiMintExample, + chainId: chain.mainnet2.id, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 456, + }, + ], + } + `) +}) + +test('parameters: blockNumber', () => { + expect( + getBytecodeQueryOptions(config, { + address: address.wagmiMintExample, + blockNumber: 1234567890n, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "blockNumber": 1234567890n, + }, + ], + } + `) +}) + +test('parameters: blockTag', () => { + expect( + getBytecodeQueryOptions(config, { + address: address.wagmiMintExample, + blockTag: 'safe', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "blockTag": "safe", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getBytecode.ts b/packages/core/src/query/getBytecode.ts new file mode 100644 index 0000000000..7000c50eaa --- /dev/null +++ b/packages/core/src/query/getBytecode.ts @@ -0,0 +1,49 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetBytecodeErrorType, + type GetBytecodeParameters, + type GetBytecodeReturnType, + getBytecode, +} from '../actions/getBytecode.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetBytecodeOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getBytecodeQueryOptions( + config: config, + options: GetBytecodeOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { address, scopeKey: _, ...parameters } = queryKey[1] + if (!address) throw new Error('address is required') + const bytecode = await getBytecode(config, { ...parameters, address }) + return (bytecode ?? null) as any + }, + queryKey: getBytecodeQueryKey(options), + } as const satisfies QueryOptions< + GetBytecodeQueryFnData, + GetBytecodeErrorType, + GetBytecodeData, + GetBytecodeQueryKey + > +} +export type GetBytecodeQueryFnData = GetBytecodeReturnType + +export type GetBytecodeData = GetBytecodeQueryFnData + +export function getBytecodeQueryKey( + options: GetBytecodeOptions, +) { + return ['getBytecode', filterQueryOptions(options)] as const +} + +export type GetBytecodeQueryKey = ReturnType< + typeof getBytecodeQueryKey +> diff --git a/packages/core/src/query/getCallsStatus.test.ts b/packages/core/src/query/getCallsStatus.test.ts new file mode 100644 index 0000000000..fe834ecc79 --- /dev/null +++ b/packages/core/src/query/getCallsStatus.test.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getCallsStatusQueryOptions } from './getCallsStatus.js' + +test('default', () => { + expect( + getCallsStatusQueryOptions(config, { + id: '0x0000000000000000000000000000000000000000', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "callsStatus", + { + "id": "0x0000000000000000000000000000000000000000", + }, + ], + "retry": [Function], + } + `) +}) diff --git a/packages/core/src/query/getCallsStatus.ts b/packages/core/src/query/getCallsStatus.ts new file mode 100644 index 0000000000..869263eef0 --- /dev/null +++ b/packages/core/src/query/getCallsStatus.ts @@ -0,0 +1,50 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetCallsStatusErrorType, + type GetCallsStatusParameters, + type GetCallsStatusReturnType, + getCallsStatus, +} from '../actions/getCallsStatus.js' +import type { Config } from '../createConfig.js' +import { ConnectorNotConnectedError } from '../errors/config.js' +import { filterQueryOptions } from '../query/utils.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute } from '../types/utils.js' + +export type GetCallsStatusOptions = Compute< + GetCallsStatusParameters & ScopeKeyParameter +> + +export function getCallsStatusQueryOptions( + config: config, + options: GetCallsStatusOptions, +) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + const status = await getCallsStatus(config, parameters) + return status + }, + queryKey: getCallsStatusQueryKey(options), + retry(failureCount, error) { + if (error instanceof ConnectorNotConnectedError) return false + return failureCount < 3 + }, + } as const satisfies QueryOptions< + GetCallsStatusQueryFnData, + GetCallsStatusErrorType, + GetCallsStatusData, + GetCallsStatusQueryKey + > +} + +export type GetCallsStatusQueryFnData = GetCallsStatusReturnType + +export type GetCallsStatusData = GetCallsStatusQueryFnData + +export function getCallsStatusQueryKey(options: GetCallsStatusOptions) { + return ['callsStatus', filterQueryOptions(options)] as const +} + +export type GetCallsStatusQueryKey = ReturnType diff --git a/packages/core/src/query/getCapabilities.test.ts b/packages/core/src/query/getCapabilities.test.ts new file mode 100644 index 0000000000..942eb37e04 --- /dev/null +++ b/packages/core/src/query/getCapabilities.test.ts @@ -0,0 +1,36 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getCapabilitiesQueryOptions } from './getCapabilities.js' + +test('default', () => { + expect(getCapabilitiesQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "capabilities", + {}, + ], + "retry": [Function], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getCapabilitiesQueryOptions(config, { + account: '0x0000000000000000000000000000000000000000', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "capabilities", + { + "account": "0x0000000000000000000000000000000000000000", + }, + ], + "retry": [Function], + } + `) +}) diff --git a/packages/core/src/query/getCapabilities.ts b/packages/core/src/query/getCapabilities.ts new file mode 100644 index 0000000000..754b77b551 --- /dev/null +++ b/packages/core/src/query/getCapabilities.ts @@ -0,0 +1,65 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetCapabilitiesErrorType, + type GetCapabilitiesParameters, + type GetCapabilitiesReturnType, + getCapabilities, +} from '../actions/getCapabilities.js' +import type { Config } from '../createConfig.js' +import { ConnectorNotConnectedError } from '../errors/config.js' +import { filterQueryOptions } from '../query/utils.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' + +export type GetCapabilitiesOptions< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getCapabilitiesQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +>(config: config, options: GetCapabilitiesOptions = {}) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + const capabilities = await getCapabilities(config, parameters) + return capabilities + }, + queryKey: getCapabilitiesQueryKey(options), + retry(failureCount, error) { + if (error instanceof ConnectorNotConnectedError) return false + return failureCount < 3 + }, + } as const satisfies QueryOptions< + GetCapabilitiesQueryFnData, + GetCapabilitiesErrorType, + GetCapabilitiesData, + GetCapabilitiesQueryKey + > +} + +export type GetCapabilitiesQueryFnData< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +> = GetCapabilitiesReturnType + +export type GetCapabilitiesData< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +> = GetCapabilitiesQueryFnData + +export function getCapabilitiesQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +>(options: GetCapabilitiesOptions = {}) { + return ['capabilities', filterQueryOptions(options)] as const +} + +export type GetCapabilitiesQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +> = ReturnType> diff --git a/packages/core/src/query/getConnectorClient.test.ts b/packages/core/src/query/getConnectorClient.test.ts new file mode 100644 index 0000000000..fdec57969d --- /dev/null +++ b/packages/core/src/query/getConnectorClient.test.ts @@ -0,0 +1,37 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getConnectorClientQueryOptions } from './getConnectorClient.js' + +test('default', () => { + expect(getConnectorClientQueryOptions(config)).toMatchInlineSnapshot(` + { + "gcTime": 0, + "queryFn": [Function], + "queryKey": [ + "connectorClient", + { + "connectorUid": undefined, + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getConnectorClientQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "gcTime": 0, + "queryFn": [Function], + "queryKey": [ + "connectorClient", + { + "chainId": 1, + "connectorUid": undefined, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getConnectorClient.ts b/packages/core/src/query/getConnectorClient.ts new file mode 100644 index 0000000000..7bc65029f0 --- /dev/null +++ b/packages/core/src/query/getConnectorClient.ts @@ -0,0 +1,69 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetConnectorClientErrorType, + type GetConnectorClientParameters, + type GetConnectorClientReturnType, + getConnectorClient, +} from '../actions/getConnectorClient.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetConnectorClientOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute< + ExactPartial> & + ScopeKeyParameter +> + +export function getConnectorClientQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: GetConnectorClientOptions = {}) { + return { + gcTime: 0, + async queryFn({ queryKey }) { + const { connector } = options + const { connectorUid: _, scopeKey: _s, ...parameters } = queryKey[1] + return getConnectorClient(config, { + ...parameters, + connector, + }) as unknown as Promise> + }, + queryKey: getConnectorClientQueryKey(options), + } as const satisfies QueryOptions< + GetConnectorClientQueryFnData, + GetConnectorClientErrorType, + GetConnectorClientData, + GetConnectorClientQueryKey + > +} + +export type GetConnectorClientQueryFnData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetConnectorClientReturnType + +export type GetConnectorClientData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetConnectorClientQueryFnData + +export function getConnectorClientQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetConnectorClientOptions = {}) { + const { connector, ...parameters } = options + return [ + 'connectorClient', + { ...filterQueryOptions(parameters), connectorUid: connector?.uid }, + ] as const +} + +export type GetConnectorClientQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/getEnsAddress.test.ts b/packages/core/src/query/getEnsAddress.test.ts new file mode 100644 index 0000000000..0e88461e61 --- /dev/null +++ b/packages/core/src/query/getEnsAddress.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsAddressQueryOptions } from './getEnsAddress.js' + +test('default', () => { + expect(getEnsAddressQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensAddress", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getEnsAddressQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensAddress", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getEnsAddress.ts b/packages/core/src/query/getEnsAddress.ts new file mode 100644 index 0000000000..7f18c9dd63 --- /dev/null +++ b/packages/core/src/query/getEnsAddress.ts @@ -0,0 +1,49 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetEnsAddressErrorType, + type GetEnsAddressParameters, + type GetEnsAddressReturnType, + getEnsAddress, +} from '../actions/getEnsAddress.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetEnsAddressOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getEnsAddressQueryOptions( + config: config, + options: GetEnsAddressOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getEnsAddress(config, { ...parameters, name }) + }, + queryKey: getEnsAddressQueryKey(options), + } as const satisfies QueryOptions< + GetEnsAddressQueryFnData, + GetEnsAddressErrorType, + GetEnsAddressData, + GetEnsAddressQueryKey + > +} + +export type GetEnsAddressQueryFnData = GetEnsAddressReturnType + +export type GetEnsAddressData = GetEnsAddressQueryFnData + +export function getEnsAddressQueryKey( + options: GetEnsAddressOptions = {}, +) { + return ['ensAddress', filterQueryOptions(options)] as const +} + +export type GetEnsAddressQueryKey = ReturnType< + typeof getEnsAddressQueryKey +> diff --git a/packages/core/src/query/getEnsAvatar.test.ts b/packages/core/src/query/getEnsAvatar.test.ts new file mode 100644 index 0000000000..8b62526426 --- /dev/null +++ b/packages/core/src/query/getEnsAvatar.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsAvatarQueryOptions } from './getEnsAvatar.js' + +test('default', () => { + expect(getEnsAvatarQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensAvatar", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getEnsAvatarQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensAvatar", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getEnsAvatar.ts b/packages/core/src/query/getEnsAvatar.ts new file mode 100644 index 0000000000..f399736e08 --- /dev/null +++ b/packages/core/src/query/getEnsAvatar.ts @@ -0,0 +1,49 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetEnsAvatarErrorType, + type GetEnsAvatarParameters, + type GetEnsAvatarReturnType, + getEnsAvatar, +} from '../actions/getEnsAvatar.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetEnsAvatarOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getEnsAvatarQueryOptions( + config: config, + options: GetEnsAvatarOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getEnsAvatar(config, { ...parameters, name }) + }, + queryKey: getEnsAvatarQueryKey(options), + } as const satisfies QueryOptions< + GetEnsAvatarQueryFnData, + GetEnsAvatarErrorType, + GetEnsAvatarData, + GetEnsAvatarQueryKey + > +} + +export type GetEnsAvatarQueryFnData = GetEnsAvatarReturnType + +export type GetEnsAvatarData = GetEnsAvatarQueryFnData + +export function getEnsAvatarQueryKey( + options: GetEnsAvatarOptions = {}, +) { + return ['ensAvatar', filterQueryOptions(options)] as const +} + +export type GetEnsAvatarQueryKey = ReturnType< + typeof getEnsAvatarQueryKey +> diff --git a/packages/core/src/query/getEnsName.test.ts b/packages/core/src/query/getEnsName.test.ts new file mode 100644 index 0000000000..006c76a12d --- /dev/null +++ b/packages/core/src/query/getEnsName.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsNameQueryOptions } from './getEnsName.js' + +test('default', () => { + expect(getEnsNameQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensName", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getEnsNameQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensName", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getEnsName.ts b/packages/core/src/query/getEnsName.ts new file mode 100644 index 0000000000..cff1534e07 --- /dev/null +++ b/packages/core/src/query/getEnsName.ts @@ -0,0 +1,49 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetEnsNameErrorType, + type GetEnsNameParameters, + type GetEnsNameReturnType, + getEnsName, +} from '../actions/getEnsName.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetEnsNameOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getEnsNameQueryOptions( + config: config, + options: GetEnsNameOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { address, scopeKey: _, ...parameters } = queryKey[1] + if (!address) throw new Error('address is required') + return getEnsName(config, { ...parameters, address }) + }, + queryKey: getEnsNameQueryKey(options), + } as const satisfies QueryOptions< + GetEnsNameQueryFnData, + GetEnsNameErrorType, + GetEnsNameData, + GetEnsNameQueryKey + > +} + +export type GetEnsNameQueryFnData = GetEnsNameReturnType + +export type GetEnsNameData = GetEnsNameQueryFnData + +export function getEnsNameQueryKey( + options: GetEnsNameOptions = {}, +) { + return ['ensName', filterQueryOptions(options)] as const +} + +export type GetEnsNameQueryKey = ReturnType< + typeof getEnsNameQueryKey +> diff --git a/packages/core/src/query/getEnsResolver.test.ts b/packages/core/src/query/getEnsResolver.test.ts new file mode 100644 index 0000000000..36baf9ba90 --- /dev/null +++ b/packages/core/src/query/getEnsResolver.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsResolverQueryOptions } from './getEnsResolver.js' + +test('default', () => { + expect(getEnsResolverQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensResolver", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getEnsResolverQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensResolver", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getEnsResolver.ts b/packages/core/src/query/getEnsResolver.ts new file mode 100644 index 0000000000..124499325d --- /dev/null +++ b/packages/core/src/query/getEnsResolver.ts @@ -0,0 +1,49 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetEnsResolverErrorType, + type GetEnsResolverParameters, + type GetEnsResolverReturnType, + getEnsResolver, +} from '../actions/getEnsResolver.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetEnsResolverOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getEnsResolverQueryOptions( + config: config, + options: GetEnsResolverOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { name, scopeKey: _, ...parameters } = queryKey[1] + if (!name) throw new Error('name is required') + return getEnsResolver(config, { ...parameters, name }) + }, + queryKey: getEnsResolverQueryKey(options), + } as const satisfies QueryOptions< + GetEnsResolverQueryFnData, + GetEnsResolverErrorType, + GetEnsResolverData, + GetEnsResolverQueryKey + > +} + +export type GetEnsResolverQueryFnData = GetEnsResolverReturnType + +export type GetEnsResolverData = GetEnsResolverQueryFnData + +export function getEnsResolverQueryKey( + options: GetEnsResolverOptions = {}, +) { + return ['ensResolver', filterQueryOptions(options)] as const +} + +export type GetEnsResolverQueryKey = ReturnType< + typeof getEnsResolverQueryKey +> diff --git a/packages/core/src/query/getEnsText.test.ts b/packages/core/src/query/getEnsText.test.ts new file mode 100644 index 0000000000..1370410633 --- /dev/null +++ b/packages/core/src/query/getEnsText.test.ts @@ -0,0 +1,46 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getEnsTextQueryOptions } from './getEnsText.js' + +test('default', () => { + expect( + getEnsTextQueryOptions(config, { + name: 'wevm.eth', + key: 'com.twitter', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensText", + { + "key": "com.twitter", + "name": "wevm.eth", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getEnsTextQueryOptions(config, { + chainId: chain.mainnet.id, + name: 'wevm.eth', + key: 'com.twitter', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "ensText", + { + "chainId": 1, + "key": "com.twitter", + "name": "wevm.eth", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getEnsText.ts b/packages/core/src/query/getEnsText.ts new file mode 100644 index 0000000000..2f1c464dea --- /dev/null +++ b/packages/core/src/query/getEnsText.ts @@ -0,0 +1,49 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetEnsTextErrorType, + type GetEnsTextParameters, + type GetEnsTextReturnType, + getEnsText, +} from '../actions/getEnsText.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetEnsTextOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getEnsTextQueryOptions( + config: config, + options: GetEnsTextOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { key, name, scopeKey: _, ...parameters } = queryKey[1] + if (!key || !name) throw new Error('key and name are required') + return getEnsText(config, { ...parameters, key, name }) + }, + queryKey: getEnsTextQueryKey(options), + } as const satisfies QueryOptions< + GetEnsTextQueryFnData, + GetEnsTextErrorType, + GetEnsTextData, + GetEnsTextQueryKey + > +} + +export type GetEnsTextQueryFnData = GetEnsTextReturnType + +export type GetEnsTextData = GetEnsTextQueryFnData + +export function getEnsTextQueryKey( + options: GetEnsTextOptions = {}, +) { + return ['ensText', filterQueryOptions(options)] as const +} + +export type GetEnsTextQueryKey = ReturnType< + typeof getEnsTextQueryKey +> diff --git a/packages/core/src/query/getFeeHistory.test.ts b/packages/core/src/query/getFeeHistory.test.ts new file mode 100644 index 0000000000..aa40793235 --- /dev/null +++ b/packages/core/src/query/getFeeHistory.test.ts @@ -0,0 +1,128 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getFeeHistoryQueryOptions } from './getFeeHistory.js' + +test('default', async () => { + expect( + getFeeHistoryQueryOptions(config, { + blockCount: 4, + rewardPercentiles: [25, 75], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + } + `) +}) + +test('parameters: chainId', async () => { + expect( + getFeeHistoryQueryOptions(config, { + blockCount: 4, + rewardPercentiles: [25, 75], + chainId: chain.mainnet2.id, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "chainId": 456, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + } + `) +}) + +test('parameters: blockNumber', async () => { + expect( + getFeeHistoryQueryOptions(config, { + blockCount: 4, + rewardPercentiles: [25, 75], + blockNumber: 18677379n, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "blockNumber": 18677379n, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + } + `) +}) + +test('parameters: blockTag', async () => { + expect( + getFeeHistoryQueryOptions(config, { + blockCount: 4, + rewardPercentiles: [25, 75], + blockTag: 'safe', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "blockTag": "safe", + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + } + `) +}) + +test('behavior: blockCount is required', async () => { + const options = getFeeHistoryQueryOptions(config, {}) + expect( + options.queryFn({ + queryKey: options.queryKey, + signal: new AbortSignal(), + meta: undefined, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot( + '[Error: blockCount is required]', + ) +}) + +test('behavior: rewardPercentiles is required', async () => { + const options = getFeeHistoryQueryOptions(config, { blockCount: 4 }) + expect( + options.queryFn({ + queryKey: options.queryKey, + signal: new AbortSignal(), + meta: undefined, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot( + '[Error: rewardPercentiles is required]', + ) +}) diff --git a/packages/core/src/query/getFeeHistory.ts b/packages/core/src/query/getFeeHistory.ts new file mode 100644 index 0000000000..6eeef0d681 --- /dev/null +++ b/packages/core/src/query/getFeeHistory.ts @@ -0,0 +1,69 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetFeeHistoryErrorType, + type GetFeeHistoryParameters, + type GetFeeHistoryReturnType, + getFeeHistory, +} from '../actions/getFeeHistory.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, PartialBy } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetFeeHistoryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute< + PartialBy< + GetFeeHistoryParameters, + 'blockCount' | 'rewardPercentiles' + > & + ScopeKeyParameter +> + +export function getFeeHistoryQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: GetFeeHistoryOptions = {}) { + return { + async queryFn({ queryKey }) { + const { + blockCount, + rewardPercentiles, + scopeKey: _, + ...parameters + } = queryKey[1] + if (!blockCount) throw new Error('blockCount is required') + if (!rewardPercentiles) throw new Error('rewardPercentiles is required') + const feeHistory = await getFeeHistory(config, { + ...(parameters as GetFeeHistoryParameters), + blockCount, + rewardPercentiles, + }) + return feeHistory ?? null + }, + queryKey: getFeeHistoryQueryKey(options), + } as const satisfies QueryOptions< + GetFeeHistoryQueryFnData, + GetFeeHistoryErrorType, + GetFeeHistoryData, + GetFeeHistoryQueryKey + > +} + +export type GetFeeHistoryQueryFnData = GetFeeHistoryReturnType + +export type GetFeeHistoryData = GetFeeHistoryQueryFnData + +export function getFeeHistoryQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetFeeHistoryOptions = {}) { + return ['feeHistory', filterQueryOptions(options)] as const +} + +export type GetFeeHistoryQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/getGasPrice.test.ts b/packages/core/src/query/getGasPrice.test.ts new file mode 100644 index 0000000000..2b90303669 --- /dev/null +++ b/packages/core/src/query/getGasPrice.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getGasPriceQueryOptions } from './getGasPrice.js' + +test('default', () => { + expect(getGasPriceQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "gasPrice", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getGasPriceQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "gasPrice", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getGasPrice.ts b/packages/core/src/query/getGasPrice.ts new file mode 100644 index 0000000000..153d4a102d --- /dev/null +++ b/packages/core/src/query/getGasPrice.ts @@ -0,0 +1,54 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetGasPriceErrorType, + type GetGasPriceParameters, + type GetGasPriceReturnType, + getGasPrice, +} from '../actions/getGasPrice.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetGasPriceOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getGasPriceQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: GetGasPriceOptions = {}) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, ...parameters } = queryKey[1] + const gasPrice = await getGasPrice(config, parameters) + return gasPrice ?? null + }, + queryKey: getGasPriceQueryKey(options), + } as const satisfies QueryOptions< + GetGasPriceQueryFnData, + GetGasPriceErrorType, + GetGasPriceData, + GetGasPriceQueryKey + > +} + +export type GetGasPriceQueryFnData = GetGasPriceReturnType + +export type GetGasPriceData = GetGasPriceQueryFnData + +export function getGasPriceQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetGasPriceOptions = {}) { + return ['gasPrice', filterQueryOptions(options)] as const +} + +export type GetGasPriceQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/getProof.test.ts b/packages/core/src/query/getProof.test.ts new file mode 100644 index 0000000000..30c02256c7 --- /dev/null +++ b/packages/core/src/query/getProof.test.ts @@ -0,0 +1,106 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getProofQueryOptions } from './getProof.js' + +test('default', () => { + expect( + getProofQueryOptions(config, { + address: '0x4200000000000000000000000000000000000016', + storageKeys: [ + '0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99', + ], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getProof", + { + "address": "0x4200000000000000000000000000000000000016", + "storageKeys": [ + "0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99", + ], + }, + ], + } + `) +}) + +test('parameters: blockNumber', () => { + expect( + getProofQueryOptions(config, { + address: '0x4200000000000000000000000000000000000016', + blockNumber: 1234567890n, + storageKeys: [ + '0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99', + ], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getProof", + { + "address": "0x4200000000000000000000000000000000000016", + "blockNumber": 1234567890n, + "storageKeys": [ + "0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99", + ], + }, + ], + } + `) +}) + +test('parameters: blockTag', () => { + expect( + getProofQueryOptions(config, { + address: '0x4200000000000000000000000000000000000016', + blockTag: 'safe', + storageKeys: [ + '0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99', + ], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getProof", + { + "address": "0x4200000000000000000000000000000000000016", + "blockTag": "safe", + "storageKeys": [ + "0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99", + ], + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getProofQueryOptions(config, { + address: '0x4200000000000000000000000000000000000016', + chainId: chain.mainnet2.id, + storageKeys: [ + '0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99', + ], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getProof", + { + "address": "0x4200000000000000000000000000000000000016", + "chainId": 456, + "storageKeys": [ + "0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99", + ], + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getProof.ts b/packages/core/src/query/getProof.ts new file mode 100644 index 0000000000..8da0b42b17 --- /dev/null +++ b/packages/core/src/query/getProof.ts @@ -0,0 +1,50 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetProofErrorType, + type GetProofParameters, + type GetProofReturnType, + getProof, +} from '../actions/getProof.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetProofOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getProofQueryOptions( + config: config, + options: GetProofOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { address, scopeKey: _, storageKeys, ...parameters } = queryKey[1] + if (!address || !storageKeys) + throw new Error('address and storageKeys are required') + return getProof(config, { ...parameters, address, storageKeys }) + }, + queryKey: getProofQueryKey(options), + } as const satisfies QueryOptions< + GetProofQueryFnData, + GetProofErrorType, + GetProofData, + GetProofQueryKey + > +} + +export type GetProofQueryFnData = GetProofReturnType + +export type GetProofData = GetProofQueryFnData + +export function getProofQueryKey( + options: GetProofOptions, +) { + return ['getProof', filterQueryOptions(options)] as const +} + +export type GetProofQueryKey = ReturnType< + typeof getProofQueryKey +> diff --git a/packages/core/src/query/getStorageAt.test.ts b/packages/core/src/query/getStorageAt.test.ts new file mode 100644 index 0000000000..87193c1e6f --- /dev/null +++ b/packages/core/src/query/getStorageAt.test.ts @@ -0,0 +1,90 @@ +import { address, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getStorageAtQueryOptions } from './getStorageAt.js' + +test('default', () => { + expect( + getStorageAtQueryOptions(config, { + address: address.wagmiMintExample, + slot: '0x0', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "slot": "0x0", + }, + ], + } + `) +}) + +test('parameters: blockNumber', () => { + expect( + getStorageAtQueryOptions(config, { + address: address.wagmiMintExample, + blockNumber: 16280770n, + slot: '0x0', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "blockNumber": 16280770n, + "slot": "0x0", + }, + ], + } + `) +}) + +test('parameters: blockTag', () => { + expect( + getStorageAtQueryOptions(config, { + address: address.wagmiMintExample, + blockTag: 'safe', + slot: '0x0', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "blockTag": "safe", + "slot": "0x0", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getStorageAtQueryOptions(config, { + address: address.wagmiMintExample, + chainId: chain.mainnet2.id, + slot: '0x0', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 456, + "slot": "0x0", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getStorageAt.ts b/packages/core/src/query/getStorageAt.ts new file mode 100644 index 0000000000..c2ed90c927 --- /dev/null +++ b/packages/core/src/query/getStorageAt.ts @@ -0,0 +1,49 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetStorageAtErrorType, + type GetStorageAtParameters, + type GetStorageAtReturnType, + getStorageAt, +} from '../actions/getStorageAt.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetStorageAtOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getStorageAtQueryOptions( + config: config, + options: GetStorageAtOptions = {}, +) { + return { + queryFn({ queryKey }) { + const { address, slot, scopeKey: _, ...parameters } = queryKey[1] + if (!address || !slot) throw new Error('address and slot are required') + return getStorageAt(config, { ...parameters, address, slot }) + }, + queryKey: getStorageAtQueryKey(options), + } as const satisfies QueryOptions< + GetStorageAtQueryFnData, + GetStorageAtErrorType, + GetStorageAtData, + GetStorageAtQueryKey + > +} + +export type GetStorageAtQueryFnData = GetStorageAtReturnType + +export type GetStorageAtData = GetStorageAtQueryFnData + +export function getStorageAtQueryKey( + options: GetStorageAtOptions, +) { + return ['getStorageAt', filterQueryOptions(options)] as const +} + +export type GetStorageAtQueryKey = ReturnType< + typeof getStorageAtQueryKey +> diff --git a/packages/core/src/query/getToken.test.ts b/packages/core/src/query/getToken.test.ts new file mode 100644 index 0000000000..87ec1731c9 --- /dev/null +++ b/packages/core/src/query/getToken.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getTokenQueryOptions } from './getToken.js' + +test('default', () => { + expect(getTokenQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "token", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getTokenQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "token", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getToken.ts b/packages/core/src/query/getToken.ts new file mode 100644 index 0000000000..8e4a2b866a --- /dev/null +++ b/packages/core/src/query/getToken.ts @@ -0,0 +1,49 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetTokenErrorType, + type GetTokenParameters, + type GetTokenReturnType, + getToken, +} from '../actions/getToken.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetTokenOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getTokenQueryOptions( + config: config, + options: GetTokenOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { address, scopeKey: _, ...parameters } = queryKey[1] + if (!address) throw new Error('address is required') + return getToken(config, { ...parameters, address }) + }, + queryKey: getTokenQueryKey(options), + } as const satisfies QueryOptions< + GetTokenQueryFnData, + GetTokenErrorType, + GetTokenData, + GetTokenQueryKey + > +} + +export type GetTokenQueryFnData = GetTokenReturnType + +export type GetTokenData = GetTokenQueryFnData + +export function getTokenQueryKey( + options: GetTokenOptions = {}, +) { + return ['token', filterQueryOptions(options)] as const +} + +export type GetTokenQueryKey = ReturnType< + typeof getTokenQueryKey +> diff --git a/packages/core/src/query/getTransaction.test.ts b/packages/core/src/query/getTransaction.test.ts new file mode 100644 index 0000000000..e1db72b325 --- /dev/null +++ b/packages/core/src/query/getTransaction.test.ts @@ -0,0 +1,32 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getTransactionQueryOptions } from './getTransaction.js' + +test('default', () => { + expect(getTransactionQueryOptions(config)).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "transaction", + {}, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getTransactionQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "transaction", + { + "chainId": 1, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getTransaction.ts b/packages/core/src/query/getTransaction.ts new file mode 100644 index 0000000000..8564854774 --- /dev/null +++ b/packages/core/src/query/getTransaction.ts @@ -0,0 +1,69 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetTransactionErrorType, + type GetTransactionParameters, + type GetTransactionReturnType, + getTransaction, +} from '../actions/getTransaction.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetTransactionOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getTransactionQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: GetTransactionOptions = {}) { + return { + async queryFn({ queryKey }) { + const { blockHash, blockNumber, blockTag, hash, index } = queryKey[1] + if (!blockHash && !blockNumber && !blockTag && !hash) + throw new Error('blockHash, blockNumber, blockTag, or hash is required') + if (!hash && !index) + throw new Error( + 'index is required for blockHash, blockNumber, or blockTag', + ) + const { scopeKey: _, ...rest } = queryKey[1] + return getTransaction( + config, + rest as GetTransactionParameters, + ) as unknown as Promise> + }, + queryKey: getTransactionQueryKey(options), + } as const satisfies QueryOptions< + GetTransactionQueryFnData, + GetTransactionErrorType, + GetTransactionData, + GetTransactionQueryKey + > +} + +export type GetTransactionQueryFnData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetTransactionReturnType + +export type GetTransactionData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetTransactionQueryFnData + +export function getTransactionQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetTransactionOptions = {}) { + return ['transaction', filterQueryOptions(options)] as const +} + +export type GetTransactionQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/getTransactionConfirmations.test.ts b/packages/core/src/query/getTransactionConfirmations.test.ts new file mode 100644 index 0000000000..ac4d6d4f4a --- /dev/null +++ b/packages/core/src/query/getTransactionConfirmations.test.ts @@ -0,0 +1,42 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getTransactionConfirmationsQueryOptions } from './getTransactionConfirmations.js' + +test('default', () => { + expect( + getTransactionConfirmationsQueryOptions(config, { + hash: '0xa559259bd2d0e8372421e222ff3545f705b5da60005bd787a23c2e68d6d7fefd', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "transactionConfirmations", + { + "hash": "0xa559259bd2d0e8372421e222ff3545f705b5da60005bd787a23c2e68d6d7fefd", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getTransactionConfirmationsQueryOptions(config, { + chainId: chain.mainnet.id, + hash: '0xa559259bd2d0e8372421e222ff3545f705b5da60005bd787a23c2e68d6d7fefd', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "transactionConfirmations", + { + "chainId": 1, + "hash": "0xa559259bd2d0e8372421e222ff3545f705b5da60005bd787a23c2e68d6d7fefd", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getTransactionConfirmations.ts b/packages/core/src/query/getTransactionConfirmations.ts new file mode 100644 index 0000000000..5c34decf37 --- /dev/null +++ b/packages/core/src/query/getTransactionConfirmations.ts @@ -0,0 +1,78 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetTransactionConfirmationsErrorType, + type GetTransactionConfirmationsParameters, + type GetTransactionConfirmationsReturnType, + getTransactionConfirmations, +} from '../actions/getTransactionConfirmations.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { UnionExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetTransactionConfirmationsOptions< + config extends Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], +> = UnionExactPartial> & + ScopeKeyParameter + +export function getTransactionConfirmationsQueryOptions< + config extends Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], +>( + config: config, + options: GetTransactionConfirmationsOptions = {} as any, +) { + return { + async queryFn({ queryKey }) { + const { + hash, + transactionReceipt, + scopeKey: _, + ...parameters + } = queryKey[1] + if (!hash && !transactionReceipt) + throw new Error('hash or transactionReceipt is required') + + const confirmations = await getTransactionConfirmations(config, { + hash, + transactionReceipt, + ...(parameters as any), + }) + return confirmations ?? null + }, + queryKey: getTransactionConfirmationsQueryKey(options), + } as const satisfies QueryOptions< + GetTransactionConfirmationsQueryFnData, + GetTransactionConfirmationsErrorType, + GetTransactionConfirmationsData, + GetTransactionConfirmationsQueryKey + > +} + +export type GetTransactionConfirmationsQueryFnData = + GetTransactionConfirmationsReturnType + +export type GetTransactionConfirmationsData = + GetTransactionConfirmationsQueryFnData + +export function getTransactionConfirmationsQueryKey< + config extends Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], +>(options: GetTransactionConfirmationsOptions = {} as any) { + return ['transactionConfirmations', filterQueryOptions(options)] as const +} + +export type GetTransactionConfirmationsQueryKey< + config extends Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/getTransactionCount.test.ts b/packages/core/src/query/getTransactionCount.test.ts new file mode 100644 index 0000000000..666953629b --- /dev/null +++ b/packages/core/src/query/getTransactionCount.test.ts @@ -0,0 +1,82 @@ +import { accounts, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getTransactionCountQueryOptions } from './getTransactionCount.js' + +const address = accounts[0] + +test('default', () => { + expect( + getTransactionCountQueryOptions(config, { address }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "transactionCount", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getTransactionCountQueryOptions(config, { + address, + chainId: chain.mainnet.id, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "transactionCount", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + }, + ], + } + `) +}) + +test('parameters: blockNumber', () => { + expect( + getTransactionCountQueryOptions(config, { + address, + blockNumber: 13677382n, + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "transactionCount", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockNumber": 13677382n, + }, + ], + } + `) +}) + +test('parameters: blockTag', () => { + expect( + getTransactionCountQueryOptions(config, { + address, + blockTag: 'earliest', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "transactionCount", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockTag": "earliest", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getTransactionCount.ts b/packages/core/src/query/getTransactionCount.ts new file mode 100644 index 0000000000..31fd2c1d8f --- /dev/null +++ b/packages/core/src/query/getTransactionCount.ts @@ -0,0 +1,55 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetTransactionCountErrorType, + type GetTransactionCountParameters, + type GetTransactionCountReturnType, + getTransactionCount, +} from '../actions/getTransactionCount.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, PartialBy } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetTransactionCountOptions = Compute< + PartialBy, 'address'> & + ScopeKeyParameter +> + +export function getTransactionCountQueryOptions( + config: config, + options: GetTransactionCountOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { address, scopeKey: _, ...parameters } = queryKey[1] + if (!address) throw new Error('address is required') + const transactionCount = await getTransactionCount(config, { + ...(parameters as GetTransactionCountParameters), + address, + }) + return transactionCount ?? null + }, + queryKey: getTransactionCountQueryKey(options), + } as const satisfies QueryOptions< + GetTransactionCountQueryFnData, + GetTransactionCountErrorType, + GetTransactionCountData, + GetTransactionCountQueryKey + > +} + +export type GetTransactionCountQueryFnData = + Compute + +export type GetTransactionCountData = GetTransactionCountQueryFnData + +export function getTransactionCountQueryKey( + options: GetTransactionCountOptions = {}, +) { + return ['transactionCount', filterQueryOptions(options)] as const +} + +export type GetTransactionCountQueryKey = ReturnType< + typeof getTransactionCountQueryKey +> diff --git a/packages/core/src/query/getTransactionReceipt.test.ts b/packages/core/src/query/getTransactionReceipt.test.ts new file mode 100644 index 0000000000..7c719b8d87 --- /dev/null +++ b/packages/core/src/query/getTransactionReceipt.test.ts @@ -0,0 +1,42 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getTransactionReceiptQueryOptions } from './getTransactionReceipt.js' + +test('default', () => { + expect( + getTransactionReceiptQueryOptions(config, { + hash: '0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getTransactionReceipt", + { + "hash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getTransactionReceiptQueryOptions(config, { + chainId: chain.mainnet2.id, + hash: '0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "getTransactionReceipt", + { + "chainId": 456, + "hash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getTransactionReceipt.ts b/packages/core/src/query/getTransactionReceipt.ts new file mode 100644 index 0000000000..0d7d559d49 --- /dev/null +++ b/packages/core/src/query/getTransactionReceipt.ts @@ -0,0 +1,60 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetTransactionReceiptErrorType, + type GetTransactionReceiptParameters, + getTransactionReceipt, +} from '../actions/getTransactionReceipt.js' +import type { GetTransactionReceiptReturnType } from '../actions/getTransactionReceipt.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetTransactionReceiptOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute< + ExactPartial> & + ScopeKeyParameter +> + +export function getTransactionReceiptQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: GetTransactionReceiptOptions = {}) { + return { + queryFn({ queryKey }) { + const { hash, scopeKey: _, ...parameters } = queryKey[1] + if (!hash) throw new Error('hash is required') + return getTransactionReceipt(config, { ...parameters, hash }) + }, + queryKey: getTransactionReceiptQueryKey(options), + } as const satisfies QueryOptions< + GetTransactionReceiptQueryFnData, + GetTransactionReceiptErrorType, + GetTransactionReceiptData, + GetTransactionReceiptQueryKey + > +} +export type GetTransactionReceiptQueryFnData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetTransactionReceiptReturnType + +export type GetTransactionReceiptData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetTransactionReceiptQueryFnData + +export function getTransactionReceiptQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetTransactionReceiptOptions) { + return ['getTransactionReceipt', filterQueryOptions(options)] as const +} + +export type GetTransactionReceiptQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/getWalletClient.test.ts b/packages/core/src/query/getWalletClient.test.ts new file mode 100644 index 0000000000..899b480882 --- /dev/null +++ b/packages/core/src/query/getWalletClient.test.ts @@ -0,0 +1,37 @@ +import { chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { getWalletClientQueryOptions } from './getWalletClient.js' + +test('default', () => { + expect(getWalletClientQueryOptions(config)).toMatchInlineSnapshot(` + { + "gcTime": 0, + "queryFn": [Function], + "queryKey": [ + "walletClient", + { + "connectorUid": undefined, + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + getWalletClientQueryOptions(config, { chainId: chain.mainnet.id }), + ).toMatchInlineSnapshot(` + { + "gcTime": 0, + "queryFn": [Function], + "queryKey": [ + "walletClient", + { + "chainId": 1, + "connectorUid": undefined, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/getWalletClient.ts b/packages/core/src/query/getWalletClient.ts new file mode 100644 index 0000000000..3c64b39a07 --- /dev/null +++ b/packages/core/src/query/getWalletClient.ts @@ -0,0 +1,65 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type GetWalletClientErrorType, + type GetWalletClientParameters, + type GetWalletClientReturnType, + getWalletClient, +} from '../actions/getWalletClient.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type GetWalletClientOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function getWalletClientQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>(config: config, options: GetWalletClientOptions = {}) { + return { + gcTime: 0, + async queryFn({ queryKey }) { + const { connector } = options + const { connectorUid: _, scopeKey: _s, ...parameters } = queryKey[1] + return getWalletClient(config, { ...parameters, connector }) + }, + queryKey: getWalletClientQueryKey(options), + } as const satisfies QueryOptions< + GetWalletClientQueryFnData, + GetWalletClientErrorType, + GetWalletClientData, + GetWalletClientQueryKey + > +} + +export type GetWalletClientQueryFnData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetWalletClientReturnType + +export type GetWalletClientData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = GetWalletClientQueryFnData + +export function getWalletClientQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: GetWalletClientOptions = {}) { + const { connector, ...parameters } = options + return [ + 'walletClient', + { ...filterQueryOptions(parameters), connectorUid: connector?.uid }, + ] as const +} + +export type GetWalletClientQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/infiniteReadContracts.test-d.ts b/packages/core/src/query/infiniteReadContracts.test-d.ts new file mode 100644 index 0000000000..c76dd3bccc --- /dev/null +++ b/packages/core/src/query/infiniteReadContracts.test-d.ts @@ -0,0 +1,201 @@ +import { abi, config } from '@wagmi/test' +import type { MulticallResponse } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { infiniteReadContractsQueryOptions } from './infiniteReadContracts.js' + +test('default', async () => { + const options = infiniteReadContractsQueryOptions(config, { + cacheKey: 'foo', + contracts(pageParam) { + expectTypeOf(pageParam).toEqualTypeOf(options.initialPageParam) + return [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ] + }, + query: { + initialPageParam: 0, + getNextPageParam(lastPage, allPages, lastPageParam, allPageParams) { + expectTypeOf(lastPage).toEqualTypeOf< + [MulticallResponse, MulticallResponse] + >() + expectTypeOf(allPages).toEqualTypeOf< + [MulticallResponse, MulticallResponse][] + >() + expectTypeOf(lastPageParam).toEqualTypeOf(options.initialPageParam) + expectTypeOf(allPageParams).toEqualTypeOf([options.initialPageParam]) + return lastPageParam + 1 + }, + }, + }) + const result = await options.queryFn({} as any) + expectTypeOf(result).toEqualTypeOf< + [MulticallResponse, MulticallResponse] + >() +}) + +test('allowFailure: false', async () => { + const options = infiniteReadContractsQueryOptions(config, { + allowFailure: false, + cacheKey: 'foo', + contracts(pageParam) { + expectTypeOf(pageParam).toEqualTypeOf(options.initialPageParam) + return [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ] + }, + query: { + initialPageParam: 0, + getNextPageParam(lastPage, allPages, lastPageParam, allPageParams) { + expectTypeOf(lastPage).toEqualTypeOf<[bigint, string]>() + expectTypeOf(allPages).toEqualTypeOf<[bigint, string][]>() + expectTypeOf(lastPageParam).toEqualTypeOf(options.initialPageParam) + expectTypeOf(allPageParams).toEqualTypeOf([options.initialPageParam]) + return lastPageParam + 1 + }, + }, + }) + const result = await options.queryFn({} as any) + expectTypeOf(result).toEqualTypeOf<[bigint, string]>() +}) + +test('initialPageParam', async () => { + const options = infiniteReadContractsQueryOptions(config, { + allowFailure: false, + cacheKey: 'foo', + contracts(pageParam) { + expectTypeOf(pageParam).toEqualTypeOf(options.initialPageParam) + return [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ] + }, + query: { + initialPageParam: 'bar', + getNextPageParam(lastPage, allPages, lastPageParam, allPageParams) { + expectTypeOf(lastPage).toEqualTypeOf<[bigint, string]>() + expectTypeOf(allPages).toEqualTypeOf<[bigint, string][]>() + expectTypeOf(lastPageParam).toEqualTypeOf(options.initialPageParam) + expectTypeOf(allPageParams).toEqualTypeOf([options.initialPageParam]) + return lastPageParam + 1 + }, + }, + }) + const result = await options.queryFn({} as any) + expectTypeOf(result).toEqualTypeOf<[bigint, string]>() +}) + +test('behavior: `contracts` after `getNextPageParam`', async () => { + const options = infiniteReadContractsQueryOptions(config, { + allowFailure: false, + cacheKey: 'foo', + query: { + initialPageParam: 0, + getNextPageParam(lastPage, allPages, lastPageParam, allPageParams) { + expectTypeOf(lastPage).toEqualTypeOf() + expectTypeOf(allPages).toEqualTypeOf() + expectTypeOf(lastPageParam).toEqualTypeOf(options.initialPageParam) + expectTypeOf(allPageParams).toEqualTypeOf([options.initialPageParam]) + return lastPageParam + 1 + }, + }, + contracts(pageParam) { + expectTypeOf(pageParam).toEqualTypeOf(options.initialPageParam) + return [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ] + }, + }) + const result = await options.queryFn({} as any) + expectTypeOf(result).toEqualTypeOf() +}) + +test('overloads', async () => { + const options = infiniteReadContractsQueryOptions(config, { + allowFailure: false, + cacheKey: 'foo', + contracts(pageParam) { + expectTypeOf(pageParam).toEqualTypeOf() + return [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + }, + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x'], + }, + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x', '0x'], + }, + ] + }, + query: { + initialPageParam: 0, + getNextPageParam(_, allPages) { + return allPages.length + 1 + }, + }, + }) + + const result = await options.queryFn({} as any) + expectTypeOf(result).toEqualTypeOf< + [ + number, + string, + { + foo: `0x${string}` + bar: `0x${string}` + }, + ] + >() +}) diff --git a/packages/core/src/query/infiniteReadContracts.test.ts b/packages/core/src/query/infiniteReadContracts.test.ts new file mode 100644 index 0000000000..509e984838 --- /dev/null +++ b/packages/core/src/query/infiniteReadContracts.test.ts @@ -0,0 +1,60 @@ +import { abi, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { infiniteReadContractsQueryOptions } from './infiniteReadContracts.js' + +test('default', () => { + expect( + infiniteReadContractsQueryOptions(config, { + allowFailure: true, + batchSize: 1024, + blockNumber: 123n, + blockTag: 'latest', + cacheKey: 'foo', + chainId: 1, + multicallAddress: '0x', + scopeKey: 'bar', + contracts(_pageParam) { + return [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ] + }, + query: { + initialPageParam: 0, + getNextPageParam(_lastPage, _allPages, lastPageParam, _allPageParams) { + return lastPageParam + 1 + }, + }, + }), + ).toMatchInlineSnapshot(` + { + "getNextPageParam": [Function], + "initialPageParam": 0, + "queryFn": [Function], + "queryKey": [ + "infiniteReadContracts", + { + "allowFailure": true, + "batchSize": 1024, + "blockNumber": 123n, + "blockTag": "latest", + "cacheKey": "foo", + "chainId": 1, + "multicallAddress": "0x", + "scopeKey": "bar", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/infiniteReadContracts.ts b/packages/core/src/query/infiniteReadContracts.ts new file mode 100644 index 0000000000..28a8d3f52a --- /dev/null +++ b/packages/core/src/query/infiniteReadContracts.ts @@ -0,0 +1,127 @@ +import type { ContractFunctionParameters } from 'viem' +import { + type ReadContractsErrorType, + type ReadContractsParameters, + type ReadContractsReturnType, + readContracts, +} from '../actions/readContracts.js' +import type { Config } from '../createConfig.js' +import type { + ChainIdParameter, + ScopeKeyParameter, +} from '../types/properties.js' +import type { StrictOmit } from '../types/utils.js' +import type { InfiniteQueryOptions } from './types.js' +import { filterQueryOptions } from './utils.js' + +export type InfiniteReadContractsOptions< + contracts extends readonly unknown[], + allowFailure extends boolean, + pageParam, + config extends Config, +> = { + cacheKey: string + contracts( + pageParam: pageParam, + ): ReadContractsParameters['contracts'] +} & StrictOmit< + ReadContractsParameters, + 'contracts' +> & + ScopeKeyParameter + +export function infiniteReadContractsQueryOptions< + config extends Config, + const contracts extends readonly ContractFunctionParameters[], + allowFailure extends boolean = true, + pageParam = unknown, +>( + config: config, + options: InfiniteReadContractsOptions< + contracts, + allowFailure, + pageParam, + config + > & + ChainIdParameter & + RequiredPageParamsParameters, +) { + return { + ...options.query, + async queryFn({ pageParam, queryKey }) { + const { contracts } = options + const { cacheKey: _, scopeKey: _s, ...parameters } = queryKey[1] + return (await readContracts(config, { + ...parameters, + contracts: contracts(pageParam as any), + })) as ReadContractsReturnType + }, + queryKey: infiniteReadContractsQueryKey(options), + } as const satisfies InfiniteQueryOptions< + InfiniteReadContractsQueryFnData, + ReadContractsErrorType, + InfiniteReadContractsData, + InfiniteReadContractsData, + InfiniteReadContractsQueryKey, + pageParam + > +} + +type RequiredPageParamsParameters< + contracts extends readonly unknown[], + allowFailure extends boolean, + pageParam, +> = { + query: { + initialPageParam: pageParam + getNextPageParam( + lastPage: InfiniteReadContractsQueryFnData, + allPages: InfiniteReadContractsQueryFnData[], + lastPageParam: pageParam, + allPageParams: pageParam[], + ): pageParam | undefined | null + } +} + +export type InfiniteReadContractsQueryFnData< + contracts extends readonly unknown[], + allowFailure extends boolean, +> = ReadContractsReturnType + +export type InfiniteReadContractsData< + contracts extends readonly unknown[], + allowFailure extends boolean, +> = InfiniteReadContractsQueryFnData + +export function infiniteReadContractsQueryKey< + config extends Config, + const contracts extends readonly unknown[], + allowFailure extends boolean, + pageParam, +>( + options: InfiniteReadContractsOptions< + contracts, + allowFailure, + pageParam, + config + > & + ChainIdParameter & + RequiredPageParamsParameters, +) { + const { contracts: _, query: _q, ...parameters } = options + return ['infiniteReadContracts', filterQueryOptions(parameters)] as const +} + +export type InfiniteReadContractsQueryKey< + contracts extends readonly unknown[], + allowFailure extends boolean, + pageParam, + config extends Config, +> = ReturnType< + typeof infiniteReadContractsQueryKey< + config, + contracts, + allowFailure, + pageParam + > +> diff --git a/packages/core/src/query/prepareTransactionRequest.test.ts b/packages/core/src/query/prepareTransactionRequest.test.ts new file mode 100644 index 0000000000..d33c156be4 --- /dev/null +++ b/packages/core/src/query/prepareTransactionRequest.test.ts @@ -0,0 +1,241 @@ +import { accounts, chain, config } from '@wagmi/test' +import { parseEther, parseGwei } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { expect, test } from 'vitest' + +import { prepareTransactionRequestQueryOptions } from './prepareTransactionRequest.js' + +const targetAccount = accounts[0] + +test('default', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: account', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + account: privateKeyToAccount( + '0x0123456789012345678901234567890123456789012345678901234567890123', + ), + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "account": { + "address": "0x14791697260E4c9A71f18484C9f997B308e59325", + "nonceManager": undefined, + "publicKey": "0x046655feed4d214c261e0a6b554395596f1f1476a77d999560e5a8df9b8a1a3515217e88dd05e938efdd71b2cce322bf01da96cd42087b236e8f5043157a9c068e", + "sign": [Function], + "signAuthorization": [Function], + "signMessage": [Function], + "signTransaction": [Function], + "signTypedData": [Function], + "source": "privateKey", + "type": "local", + }, + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: data', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + data: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "data": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + chainId: chain.mainnet2.id, + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "chainId": 456, + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: nonce', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + nonce: 5, + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "nonce": 5, + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: gasPrice', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + gasPrice: parseGwei('10'), + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "gasPrice": 10000000000n, + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: maxFeePerGas', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + maxFeePerGas: parseGwei('100'), + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "maxFeePerGas": 100000000000n, + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: maxPriorityFeePerGas', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + maxPriorityFeePerGas: parseGwei('5'), + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "maxPriorityFeePerGas": 5000000000n, + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: type', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + type: 'eip1559', + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "eip1559", + "value": 1000000000000000000n, + }, + ], + } + `) +}) + +test('parameters: parameters', () => { + expect( + prepareTransactionRequestQueryOptions(config, { + parameters: ['gas'], + to: targetAccount, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "prepareTransactionRequest", + { + "parameters": [ + "gas", + ], + "to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "value": 1000000000000000000n, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/prepareTransactionRequest.ts b/packages/core/src/query/prepareTransactionRequest.ts new file mode 100644 index 0000000000..5ed4b419fa --- /dev/null +++ b/packages/core/src/query/prepareTransactionRequest.ts @@ -0,0 +1,101 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import type { PrepareTransactionRequestRequest as viem_PrepareTransactionRequestRequest } from 'viem' + +import { + type PrepareTransactionRequestErrorType, + type PrepareTransactionRequestParameters, + type PrepareTransactionRequestReturnType, + prepareTransactionRequest, +} from '../actions/prepareTransactionRequest.js' +import type { Config } from '../createConfig.js' +import type { SelectChains } from '../types/chain.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { UnionExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type PrepareTransactionRequestOptions< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, +> = UnionExactPartial< + PrepareTransactionRequestParameters +> & + ScopeKeyParameter + +export function prepareTransactionRequestQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, +>( + config: config, + options: PrepareTransactionRequestOptions< + config, + chainId, + request + > = {} as any, +) { + return { + queryFn({ queryKey }) { + const { scopeKey: _, to, ...parameters } = queryKey[1] + if (!to) throw new Error('to is required') + return prepareTransactionRequest(config, { + to, + ...(parameters as any), + }) as unknown as Promise< + PrepareTransactionRequestQueryFnData + > + }, + queryKey: prepareTransactionRequestQueryKey(options), + } as const satisfies QueryOptions< + PrepareTransactionRequestQueryFnData, + PrepareTransactionRequestErrorType, + PrepareTransactionRequestData, + PrepareTransactionRequestQueryKey + > +} +export type PrepareTransactionRequestQueryFnData< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, +> = PrepareTransactionRequestReturnType + +export type PrepareTransactionRequestData< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, +> = PrepareTransactionRequestQueryFnData + +export function prepareTransactionRequestQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, +>(options: PrepareTransactionRequestOptions) { + return ['prepareTransactionRequest', filterQueryOptions(options)] as const +} + +export type PrepareTransactionRequestQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, +> = ReturnType< + typeof prepareTransactionRequestQueryKey +> diff --git a/packages/core/src/query/readContract.test-d.ts b/packages/core/src/query/readContract.test-d.ts new file mode 100644 index 0000000000..286b8819c7 --- /dev/null +++ b/packages/core/src/query/readContract.test-d.ts @@ -0,0 +1,15 @@ +import { abi, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { readContractQueryOptions } from './readContract.js' + +test('default', async () => { + const options = readContractQueryOptions(config, { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }) + const result = await options.queryFn({} as any) + expectTypeOf(result).toEqualTypeOf() +}) diff --git a/packages/core/src/query/readContract.test.ts b/packages/core/src/query/readContract.test.ts new file mode 100644 index 0000000000..6c17b849ab --- /dev/null +++ b/packages/core/src/query/readContract.test.ts @@ -0,0 +1,29 @@ +import { abi, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { readContractQueryOptions } from './readContract.js' + +test('default', () => { + expect( + readContractQueryOptions(config, { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "readContract", + { + "address": "0x", + "args": [ + "0x", + ], + "functionName": "balanceOf", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/readContract.ts b/packages/core/src/query/readContract.ts new file mode 100644 index 0000000000..52c586665f --- /dev/null +++ b/packages/core/src/query/readContract.ts @@ -0,0 +1,93 @@ +import type { QueryOptions } from '@tanstack/query-core' +import type { Abi, ContractFunctionArgs, ContractFunctionName } from 'viem' + +import { + type ReadContractErrorType, + type ReadContractParameters, + type ReadContractReturnType, + readContract, +} from '../actions/readContract.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { UnionExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type ReadContractOptions< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, + config extends Config, +> = UnionExactPartial> & + ScopeKeyParameter + +export function readContractQueryOptions< + config extends Config, + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, +>( + config: config, + options: ReadContractOptions = {} as any, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const abi = options.abi as Abi + if (!abi) throw new Error('abi is required') + + const { functionName, scopeKey: _, ...parameters } = queryKey[1] + const addressOrCodeParams = (() => { + const params = queryKey[1] as unknown as ReadContractParameters + if (params.address) return { address: params.address } + if (params.code) return { code: params.code } + throw new Error('address or code is required') + })() + + if (!functionName) throw new Error('functionName is required') + + return readContract(config, { + abi, + functionName, + args: parameters.args as readonly unknown[], + ...addressOrCodeParams, + ...parameters, + }) as Promise> + }, + queryKey: readContractQueryKey(options as any) as any, + } as const satisfies QueryOptions< + ReadContractQueryFnData, + ReadContractErrorType, + ReadContractData, + ReadContractQueryKey + > +} + +export type ReadContractQueryFnData< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, +> = ReadContractReturnType + +export type ReadContractData< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, +> = ReadContractQueryFnData + +export function readContractQueryKey< + config extends Config, + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, +>(options: ReadContractOptions = {} as any) { + const { abi: _, ...rest } = options + return ['readContract', filterQueryOptions(rest)] as const +} + +export type ReadContractQueryKey< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, + config extends Config, +> = ReturnType> diff --git a/packages/core/src/query/readContracts.test-d.ts b/packages/core/src/query/readContracts.test-d.ts new file mode 100644 index 0000000000..b6236e191b --- /dev/null +++ b/packages/core/src/query/readContracts.test-d.ts @@ -0,0 +1,58 @@ +import { abi, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { readContractsQueryOptions } from './readContracts.js' + +test('default', async () => { + const options = readContractsQueryOptions(config, { + contracts: [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ], + }) + const result = await options.queryFn({} as any) + expectTypeOf(result).toEqualTypeOf< + [ + ( + | { error: Error; result?: undefined; status: 'failure' } + | { error?: undefined; result: bigint; status: 'success' } + ), + ( + | { error: Error; result?: undefined; status: 'failure' } + | { error?: undefined; result: string; status: 'success' } + ), + ] + >() +}) + +test('allowFailure: false', async () => { + const options = readContractsQueryOptions(config, { + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ], + }) + const result = await options.queryFn({} as any) + expectTypeOf(result).toEqualTypeOf<[bigint, string]>() +}) diff --git a/packages/core/src/query/readContracts.test.ts b/packages/core/src/query/readContracts.test.ts new file mode 100644 index 0000000000..755b80c202 --- /dev/null +++ b/packages/core/src/query/readContracts.test.ts @@ -0,0 +1,38 @@ +import { abi, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { readContractsQueryOptions } from './readContracts.js' + +test('default', () => { + expect( + readContractsQueryOptions(config, { + contracts: [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + ], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "readContracts", + { + "contracts": [ + { + "address": "0x", + "args": [ + "0x", + ], + "chainId": undefined, + "functionName": "balanceOf", + }, + ], + }, + ], + } + `) +}) diff --git a/packages/core/src/query/readContracts.ts b/packages/core/src/query/readContracts.ts new file mode 100644 index 0000000000..96bb2163cd --- /dev/null +++ b/packages/core/src/query/readContracts.ts @@ -0,0 +1,98 @@ +import type { QueryOptions } from '@tanstack/query-core' +import type { + ContractFunctionParameters, + MulticallParameters as viem_MulticallParameters, +} from 'viem' + +import { + type ReadContractsErrorType, + type ReadContractsReturnType, + readContracts, +} from '../actions/readContracts.js' +import type { Config } from '../createConfig.js' +import type { ChainIdParameter } from '../types/properties.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type ReadContractsOptions< + contracts extends readonly unknown[], + allowFailure extends boolean, + config extends Config, +> = ExactPartial< + viem_MulticallParameters< + contracts, + allowFailure, + { optional: true; properties: ChainIdParameter } + > +> & + ScopeKeyParameter + +export function readContractsQueryOptions< + config extends Config, + const contracts extends readonly unknown[], + allowFailure extends boolean = true, +>( + config: config, + options: ReadContractsOptions & + ChainIdParameter = {}, +) { + return { + async queryFn({ queryKey }) { + const contracts: ContractFunctionParameters[] = [] + const length = queryKey[1].contracts.length + for (let i = 0; i < length; i++) { + const contract = queryKey[1].contracts[i]! + const abi = (options.contracts?.[i] as ContractFunctionParameters).abi + contracts.push({ ...contract, abi }) + } + const { scopeKey: _, ...parameters } = queryKey[1] + return readContracts(config, { + ...parameters, + contracts, + }) as Promise> + }, + queryKey: readContractsQueryKey(options), + } as const satisfies QueryOptions< + ReadContractsQueryFnData, + ReadContractsErrorType, + ReadContractsData, + ReadContractsQueryKey + > +} + +export type ReadContractsQueryFnData< + contracts extends readonly unknown[], + allowFailure extends boolean, +> = ReadContractsReturnType + +export type ReadContractsData< + contracts extends readonly unknown[], + allowFailure extends boolean, +> = ReadContractsQueryFnData + +export function readContractsQueryKey< + config extends Config, + const contracts extends readonly unknown[], + allowFailure extends boolean, +>( + options: ReadContractsOptions & + ChainIdParameter = {}, +) { + const contracts = [] + for (const contract of (options.contracts ?? + []) as (ContractFunctionParameters & { chainId: number })[]) { + const { abi: _, ...rest } = contract + contracts.push({ ...rest, chainId: rest.chainId ?? options.chainId }) + } + return [ + 'readContracts', + filterQueryOptions({ ...options, contracts }), + ] as const +} + +export type ReadContractsQueryKey< + contracts extends readonly unknown[], + allowFailure extends boolean, + config extends Config, +> = ReturnType> diff --git a/packages/core/src/query/reconnect.test.ts b/packages/core/src/query/reconnect.test.ts new file mode 100644 index 0000000000..fbcb56c3fc --- /dev/null +++ b/packages/core/src/query/reconnect.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { reconnectMutationOptions } from './reconnect.js' + +test('default', () => { + expect(reconnectMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "reconnect", + ], + } + `) +}) diff --git a/packages/core/src/query/reconnect.ts b/packages/core/src/query/reconnect.ts new file mode 100644 index 0000000000..13126cdd34 --- /dev/null +++ b/packages/core/src/query/reconnect.ts @@ -0,0 +1,42 @@ +import type { MutationOptions } from '@tanstack/query-core' + +import { + type ReconnectErrorType, + type ReconnectParameters, + type ReconnectReturnType, + reconnect, +} from '../actions/reconnect.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' +import type { Mutate, MutateAsync } from './types.js' + +export function reconnectMutationOptions(config: Config) { + return { + mutationFn(variables) { + return reconnect(config, variables) + }, + mutationKey: ['reconnect'], + } as const satisfies MutationOptions< + ReconnectData, + ReconnectErrorType, + ReconnectVariables + > +} + +export type ReconnectData = Compute + +export type ReconnectVariables = ReconnectParameters | undefined + +export type ReconnectMutate = Mutate< + ReconnectData, + ReconnectErrorType, + ReconnectVariables, + context +> + +export type ReconnectMutateAsync = MutateAsync< + ReconnectData, + ReconnectErrorType, + ReconnectVariables, + context +> diff --git a/packages/core/src/query/sendCalls.test.ts b/packages/core/src/query/sendCalls.test.ts new file mode 100644 index 0000000000..9892aa43e3 --- /dev/null +++ b/packages/core/src/query/sendCalls.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { sendCallsMutationOptions } from './sendCalls.js' + +test('default', () => { + expect(sendCallsMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "sendCalls", + ], + } + `) +}) diff --git a/packages/core/src/query/sendCalls.ts b/packages/core/src/query/sendCalls.ts new file mode 100644 index 0000000000..5d07b89b8f --- /dev/null +++ b/packages/core/src/query/sendCalls.ts @@ -0,0 +1,67 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import { + type SendCallsErrorType, + type SendCallsParameters, + type SendCallsReturnType, + sendCalls, +} from '../actions/sendCalls.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function sendCallsMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return sendCalls(config, variables) + }, + mutationKey: ['sendCalls'], + } as const satisfies MutationOptions< + SendCallsData, + SendCallsErrorType, + SendCallsVariables + > +} + +export type SendCallsData = Compute + +export type SendCallsVariables< + config extends Config, + chainId extends config['chains'][number]['id'], + calls extends readonly unknown[] = readonly unknown[], +> = SendCallsParameters + +export type SendCallsMutate = < + const calls extends readonly unknown[], + chainId extends config['chains'][number]['id'], +>( + variables: SendCallsVariables, + options?: + | Compute< + MutateOptions< + SendCallsData, + SendCallsErrorType, + Compute>, + context + > + > + | undefined, +) => void + +export type SendCallsMutateAsync = < + const calls extends readonly unknown[], + chainId extends config['chains'][number]['id'], +>( + variables: SendCallsVariables, + options?: + | Compute< + MutateOptions< + SendCallsData, + SendCallsErrorType, + Compute>, + context + > + > + | undefined, +) => Promise diff --git a/packages/core/src/query/sendTransaction.test.ts b/packages/core/src/query/sendTransaction.test.ts new file mode 100644 index 0000000000..4cda333e71 --- /dev/null +++ b/packages/core/src/query/sendTransaction.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { sendTransactionMutationOptions } from './sendTransaction.js' + +test('default', () => { + expect(sendTransactionMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "sendTransaction", + ], + } + `) +}) diff --git a/packages/core/src/query/sendTransaction.ts b/packages/core/src/query/sendTransaction.ts new file mode 100644 index 0000000000..e6df10fef7 --- /dev/null +++ b/packages/core/src/query/sendTransaction.ts @@ -0,0 +1,65 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import { + type SendTransactionErrorType, + type SendTransactionParameters, + type SendTransactionReturnType, + sendTransaction, +} from '../actions/sendTransaction.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function sendTransactionMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return sendTransaction(config, variables) + }, + mutationKey: ['sendTransaction'], + } as const satisfies MutationOptions< + SendTransactionData, + SendTransactionErrorType, + SendTransactionVariables + > +} + +export type SendTransactionData = Compute + +export type SendTransactionVariables< + config extends Config, + chainId extends config['chains'][number]['id'], +> = SendTransactionParameters + +export type SendTransactionMutate = < + chainId extends config['chains'][number]['id'], +>( + variables: SendTransactionVariables, + options?: + | Compute< + MutateOptions< + SendTransactionData, + SendTransactionErrorType, + Compute>, + context + > + > + | undefined, +) => void + +export type SendTransactionMutateAsync< + config extends Config, + context = unknown, +> = ( + variables: SendTransactionVariables, + options?: + | Compute< + MutateOptions< + SendTransactionData, + SendTransactionErrorType, + Compute>, + context + > + > + | undefined, +) => Promise diff --git a/packages/core/src/query/showCallsStatus.test.ts b/packages/core/src/query/showCallsStatus.test.ts new file mode 100644 index 0000000000..ad26ab6171 --- /dev/null +++ b/packages/core/src/query/showCallsStatus.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { showCallsStatusMutationOptions } from './showCallsStatus.js' + +test('default', () => { + expect(showCallsStatusMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "showCallsStatus", + ], + } + `) +}) diff --git a/packages/core/src/query/showCallsStatus.ts b/packages/core/src/query/showCallsStatus.ts new file mode 100644 index 0000000000..86a703cbc8 --- /dev/null +++ b/packages/core/src/query/showCallsStatus.ts @@ -0,0 +1,57 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import { + type ShowCallsStatusErrorType, + type ShowCallsStatusParameters, + type ShowCallsStatusReturnType, + showCallsStatus, +} from '../actions/showCallsStatus.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function showCallsStatusMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return showCallsStatus(config, variables) + }, + mutationKey: ['showCallsStatus'], + } as const satisfies MutationOptions< + ShowCallsStatusData, + ShowCallsStatusErrorType, + ShowCallsStatusVariables + > +} + +export type ShowCallsStatusData = Compute + +export type ShowCallsStatusVariables = ShowCallsStatusParameters + +export type ShowCallsStatusMutate = ( + variables: ShowCallsStatusVariables, + options?: + | Compute< + MutateOptions< + ShowCallsStatusData, + ShowCallsStatusErrorType, + Compute, + context + > + > + | undefined, +) => void + +export type ShowCallsStatusMutateAsync = ( + variables: ShowCallsStatusVariables, + options?: + | Compute< + MutateOptions< + ShowCallsStatusData, + ShowCallsStatusErrorType, + Compute, + context + > + > + | undefined, +) => Promise diff --git a/packages/core/src/query/signMessage.test.ts b/packages/core/src/query/signMessage.test.ts new file mode 100644 index 0000000000..b8ad84d0af --- /dev/null +++ b/packages/core/src/query/signMessage.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { signMessageMutationOptions } from './signMessage.js' + +test('default', () => { + expect(signMessageMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "signMessage", + ], + } + `) +}) diff --git a/packages/core/src/query/signMessage.ts b/packages/core/src/query/signMessage.ts new file mode 100644 index 0000000000..d2a4d8156a --- /dev/null +++ b/packages/core/src/query/signMessage.ts @@ -0,0 +1,42 @@ +import type { MutationOptions } from '@tanstack/query-core' + +import { + type SignMessageErrorType, + type SignMessageParameters, + type SignMessageReturnType, + signMessage, +} from '../actions/signMessage.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' +import type { Mutate, MutateAsync } from './types.js' + +export function signMessageMutationOptions(config: Config) { + return { + mutationFn(variables) { + return signMessage(config, variables) + }, + mutationKey: ['signMessage'], + } as const satisfies MutationOptions< + SignMessageData, + SignMessageErrorType, + SignMessageVariables + > +} + +export type SignMessageData = SignMessageReturnType + +export type SignMessageVariables = Compute + +export type SignMessageMutate = Mutate< + SignMessageData, + SignMessageErrorType, + SignMessageVariables, + context +> + +export type SignMessageMutateAsync = MutateAsync< + SignMessageData, + SignMessageErrorType, + SignMessageVariables, + context +> diff --git a/packages/core/src/query/signTypedData.test.ts b/packages/core/src/query/signTypedData.test.ts new file mode 100644 index 0000000000..02cbb66480 --- /dev/null +++ b/packages/core/src/query/signTypedData.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { signTypedDataMutationOptions } from './signTypedData.js' + +test('default', () => { + expect(signTypedDataMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "signTypedData", + ], + } + `) +}) diff --git a/packages/core/src/query/signTypedData.ts b/packages/core/src/query/signTypedData.ts new file mode 100644 index 0000000000..fe8eeb0268 --- /dev/null +++ b/packages/core/src/query/signTypedData.ts @@ -0,0 +1,75 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import type { TypedData } from 'viem' +import { + type SignTypedDataErrorType, + type SignTypedDataParameters, + type SignTypedDataReturnType, + signTypedData, +} from '../actions/signTypedData.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function signTypedDataMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return signTypedData(config, variables) + }, + mutationKey: ['signTypedData'], + } as const satisfies MutationOptions< + SignTypedDataData, + SignTypedDataErrorType, + SignTypedDataVariables + > +} + +export type SignTypedDataData = Compute + +export type SignTypedDataVariables< + typedData extends TypedData | Record = TypedData, + primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData, + /// + primaryTypes = typedData extends TypedData ? keyof typedData : string, +> = SignTypedDataParameters + +export type SignTypedDataMutate = < + const typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', +>( + variables: SignTypedDataVariables, + options?: + | MutateOptions< + SignTypedDataData, + SignTypedDataErrorType, + SignTypedDataVariables< + typedData, + primaryType, + // use `primaryType` to make sure it's not union of all possible primary types + primaryType + >, + context + > + | undefined, +) => void + +export type SignTypedDataMutateAsync = < + const typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', +>( + variables: SignTypedDataVariables, + options?: + | MutateOptions< + SignTypedDataData, + SignTypedDataErrorType, + SignTypedDataVariables< + typedData, + primaryType, + // use `primaryType` to make sure it's not union of all possible primary types + primaryType + >, + context + > + | undefined, +) => Promise diff --git a/packages/core/src/query/simulateContract.test-d.ts b/packages/core/src/query/simulateContract.test-d.ts new file mode 100644 index 0000000000..11023e7297 --- /dev/null +++ b/packages/core/src/query/simulateContract.test-d.ts @@ -0,0 +1,81 @@ +import { abi } from '@wagmi/test' +import { http, type Address } from 'viem' +import { celo, mainnet } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { + type SimulateContractOptions, + simulateContractQueryOptions, +} from './simulateContract.js' + +test('chain formatters', () => { + const config = createConfig({ + chains: [mainnet, celo], + transports: { [celo.id]: http(), [mainnet.id]: http() }, + }) + + type Result = SimulateContractOptions< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + (typeof config)['chains'][number]['id'] + > + expectTypeOf().toMatchTypeOf<{ + chainId?: typeof celo.id | typeof mainnet.id | undefined + feeCurrency?: `0x${string}` | undefined + }>() + simulateContractQueryOptions(config, { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + feeCurrency: '0x', + }) + + type Result2 = SimulateContractOptions< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof celo.id + > + expectTypeOf().toMatchTypeOf<{ + functionName?: 'approve' | 'transfer' | 'transferFrom' | undefined + args?: readonly [Address, Address, bigint] | undefined + feeCurrency?: `0x${string}` | undefined + }>() + simulateContractQueryOptions(config, { + chainId: celo.id, + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + feeCurrency: '0x', + }) + + type Result3 = SimulateContractOptions< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof mainnet.id + > + expectTypeOf().toMatchTypeOf<{ + functionName?: 'approve' | 'transfer' | 'transferFrom' | undefined + args?: readonly [Address, Address, bigint] | undefined + }>() + expectTypeOf().not.toMatchTypeOf<{ + feeCurrency?: `0x${string}` | undefined + }>() + simulateContractQueryOptions(config, { + chainId: mainnet.id, + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/core/src/query/simulateContract.test.ts b/packages/core/src/query/simulateContract.test.ts new file mode 100644 index 0000000000..354e4b0f02 --- /dev/null +++ b/packages/core/src/query/simulateContract.test.ts @@ -0,0 +1,31 @@ +import { abi, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { simulateContractQueryOptions } from './simulateContract.js' + +test('default', () => { + expect( + simulateContractQueryOptions(config, { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "simulateContract", + { + "address": "0x", + "args": [ + "0x", + "0x", + 123n, + ], + "functionName": "transferFrom", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/simulateContract.ts b/packages/core/src/query/simulateContract.ts new file mode 100644 index 0000000000..c3970999d4 --- /dev/null +++ b/packages/core/src/query/simulateContract.ts @@ -0,0 +1,132 @@ +import type { QueryOptions } from '@tanstack/query-core' +import type { Abi, ContractFunctionArgs, ContractFunctionName } from 'viem' + +import { + type SimulateContractErrorType, + type SimulateContractParameters, + type SimulateContractReturnType, + simulateContract, +} from '../actions/simulateContract.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { UnionExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type SimulateContractOptions< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, +> = UnionExactPartial< + SimulateContractParameters +> & + ScopeKeyParameter + +export function simulateContractQueryOptions< + config extends Config, + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + chainId extends config['chains'][number]['id'] | undefined, +>( + config: config, + options: SimulateContractOptions< + abi, + functionName, + args, + config, + chainId + > = {} as any, +) { + return { + async queryFn({ queryKey }) { + const { abi, connector } = options + if (!abi) throw new Error('abi is required') + const { scopeKey: _, ...parameters } = queryKey[1] + const { address, functionName } = parameters + if (!address) throw new Error('address is required') + if (!functionName) throw new Error('functionName is required') + return simulateContract(config, { + abi, + connector, + ...(parameters as any), + }) + }, + queryKey: simulateContractQueryKey(options), + } as const satisfies QueryOptions< + SimulateContractQueryFnData, + SimulateContractErrorType, + SimulateContractData, + SimulateContractQueryKey + > +} + +export type SimulateContractQueryFnData< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, +> = SimulateContractReturnType + +export type SimulateContractData< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, +> = SimulateContractQueryFnData + +export function simulateContractQueryKey< + config extends Config, + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + chainId extends config['chains'][number]['id'] | undefined, +>( + options: SimulateContractOptions< + abi, + functionName, + args, + config, + chainId + > = {} as any, +) { + const { abi: _, connector: _c, ...rest } = options + return ['simulateContract', filterQueryOptions(rest)] as const +} + +export type SimulateContractQueryKey< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + config extends Config, + chainId extends config['chains'][number]['id'] | undefined, +> = ReturnType< + typeof simulateContractQueryKey +> diff --git a/packages/core/src/query/switchAccount.test.ts b/packages/core/src/query/switchAccount.test.ts new file mode 100644 index 0000000000..25a7066c61 --- /dev/null +++ b/packages/core/src/query/switchAccount.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { switchAccountMutationOptions } from './switchAccount.js' + +test('default', () => { + expect(switchAccountMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "switchAccount", + ], + } + `) +}) diff --git a/packages/core/src/query/switchAccount.ts b/packages/core/src/query/switchAccount.ts new file mode 100644 index 0000000000..29a10b2ce8 --- /dev/null +++ b/packages/core/src/query/switchAccount.ts @@ -0,0 +1,52 @@ +import type { MutationOptions } from '@tanstack/query-core' + +import { + type SwitchAccountErrorType, + type SwitchAccountParameters, + type SwitchAccountReturnType, + switchAccount, +} from '../actions/switchAccount.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' +import type { Mutate, MutateAsync } from './types.js' + +export function switchAccountMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return switchAccount(config, variables) + }, + mutationKey: ['switchAccount'], + } as const satisfies MutationOptions< + SwitchAccountData, + SwitchAccountErrorType, + SwitchAccountVariables + > +} + +export type SwitchAccountData = Compute< + SwitchAccountReturnType +> + +export type SwitchAccountVariables = Compute + +export type SwitchAccountMutate< + config extends Config, + context = unknown, +> = Mutate< + SwitchAccountData, + SwitchAccountErrorType, + SwitchAccountVariables, + context +> + +export type SwitchAccountMutateAsync< + config extends Config, + context = unknown, +> = MutateAsync< + SwitchAccountData, + SwitchAccountErrorType, + SwitchAccountVariables, + context +> diff --git a/packages/core/src/query/switchChain.test.ts b/packages/core/src/query/switchChain.test.ts new file mode 100644 index 0000000000..09a4277c20 --- /dev/null +++ b/packages/core/src/query/switchChain.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { switchChainMutationOptions } from './switchChain.js' + +test('default', () => { + expect(switchChainMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "switchChain", + ], + } + `) +}) diff --git a/packages/core/src/query/switchChain.ts b/packages/core/src/query/switchChain.ts new file mode 100644 index 0000000000..1924ca8429 --- /dev/null +++ b/packages/core/src/query/switchChain.ts @@ -0,0 +1,67 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' + +import { + type SwitchChainErrorType, + type SwitchChainParameters, + type SwitchChainReturnType, + switchChain, +} from '../actions/switchChain.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function switchChainMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return switchChain(config, variables) + }, + mutationKey: ['switchChain'], + } as const satisfies MutationOptions< + SwitchChainData, + SwitchChainErrorType, + SwitchChainVariables + > +} + +export type SwitchChainData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute> + +export type SwitchChainVariables< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute> + +export type SwitchChainMutate = < + chainId extends config['chains'][number]['id'], +>( + variables: SwitchChainVariables, + options?: + | Compute< + MutateOptions< + SwitchChainData, + SwitchChainErrorType, + Compute>, + context + > + > + | undefined, +) => void + +export type SwitchChainMutateAsync = < + chainId extends config['chains'][number]['id'], +>( + variables: SwitchChainVariables, + options?: + | Compute< + MutateOptions< + SwitchChainData, + SwitchChainErrorType, + Compute>, + context + > + > + | undefined, +) => Promise> diff --git a/packages/core/src/query/types.ts b/packages/core/src/query/types.ts new file mode 100644 index 0000000000..6a279d52d5 --- /dev/null +++ b/packages/core/src/query/types.ts @@ -0,0 +1,84 @@ +import type { + DefaultError, + InfiniteQueryObserverOptions, + MutateOptions, + QueryFunction, + QueryKey, +} from '@tanstack/query-core' + +import type { Compute, StrictOmit } from '../types/utils.js' + +export type InfiniteQueryOptions< + queryFnData = unknown, + error = DefaultError, + data = queryFnData, + queryData = queryFnData, + queryKey extends QueryKey = QueryKey, + pageParam = unknown, + /// + options extends InfiniteQueryObserverOptions< + queryFnData, + error, + data, + queryData, + queryKey, + pageParam + > = InfiniteQueryObserverOptions< + queryFnData, + error, + data, + queryData, + queryKey, + pageParam + >, +> = Compute< + // `queryFn` doesn't pass through `pageParam` correctly + StrictOmit & { + queryFn?( + context: QueryFunctionContext, + ): options['queryFn'] extends (...args: any) => any + ? ReturnType> + : unknown + } +> + +// `QueryFunctionContext` not exported resulting in TS2742 error so grabbing from `QueryFunction` +type QueryFunctionContext< + TQueryKey extends QueryKey = QueryKey, + TPageParam = never, +> = Parameters>[0] + +export type Mutate< + data = unknown, + error = unknown, + variables = void, + context = unknown, +> = ( + ...args: Parameters, context>> +) => void + +export type MutateAsync< + data = unknown, + error = unknown, + variables = void, + context = unknown, +> = MutateFn, context> + +type MutateFn< + data = unknown, + error = unknown, + variables = void, + context = unknown, +> = undefined extends variables + ? ( + variables?: variables, + options?: + | Compute> + | undefined, + ) => Promise + : ( + variables: variables, + options?: + | Compute> + | undefined, + ) => Promise diff --git a/packages/core/src/query/utils.test.ts b/packages/core/src/query/utils.test.ts new file mode 100644 index 0000000000..920b2e5f37 --- /dev/null +++ b/packages/core/src/query/utils.test.ts @@ -0,0 +1,20 @@ +import { expect, test } from 'vitest' + +import { structuralSharing } from './utils.js' + +test('structuralSharing', () => { + expect( + structuralSharing({ foo: 'bar' }, { foo: 'bar' }), + ).toMatchInlineSnapshot(` + { + "foo": "bar", + } + `) + expect( + structuralSharing({ foo: 'bar' }, { foo: 'baz' }), + ).toMatchInlineSnapshot(` + { + "foo": "baz", + } + `) +}) diff --git a/packages/core/src/query/utils.ts b/packages/core/src/query/utils.ts new file mode 100644 index 0000000000..ab4ea3b989 --- /dev/null +++ b/packages/core/src/query/utils.ts @@ -0,0 +1,73 @@ +import { type QueryKey, replaceEqualDeep } from '@tanstack/query-core' + +export function structuralSharing( + oldData: data | undefined, + newData: data, +): data { + return replaceEqualDeep(oldData, newData) +} + +export function hashFn(queryKey: QueryKey): string { + return JSON.stringify(queryKey, (_, value) => { + if (isPlainObject(value)) + return Object.keys(value) + .sort() + .reduce((result, key) => { + result[key] = value[key] + return result + }, {} as any) + if (typeof value === 'bigint') return value.toString() + return value + }) +} + +// biome-ignore lint/complexity/noBannedTypes: +function isPlainObject(value: any): value is Object { + if (!hasObjectPrototype(value)) { + return false + } + + // If has modified constructor + const ctor = value.constructor + if (typeof ctor === 'undefined') return true + + // If has modified prototype + const prot = ctor.prototype + if (!hasObjectPrototype(prot)) return false + + // If constructor does not have an Object-specific method + // biome-ignore lint/suspicious/noPrototypeBuiltins: + if (!prot.hasOwnProperty('isPrototypeOf')) return false + + // Most likely a plain Object + return true +} + +function hasObjectPrototype(o: any): boolean { + return Object.prototype.toString.call(o) === '[object Object]' +} + +export function filterQueryOptions>( + options: type, +): type { + // destructuring is super fast + // biome-ignore format: no formatting + const { + // import('@tanstack/query-core').QueryOptions + _defaulted, behavior, gcTime, initialData, initialDataUpdatedAt, maxPages, meta, networkMode, queryFn, queryHash, queryKey, queryKeyHashFn, retry, retryDelay, structuralSharing, + + // import('@tanstack/query-core').InfiniteQueryObserverOptions + getPreviousPageParam, getNextPageParam, initialPageParam, + + // import('@tanstack/react-query').UseQueryOptions + _optimisticResults, enabled, notifyOnChangeProps, placeholderData, refetchInterval, refetchIntervalInBackground, refetchOnMount, refetchOnReconnect, refetchOnWindowFocus, retryOnMount, select, staleTime, suspense, throwOnError, + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // wagmi + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + config, connector, query, + ...rest + } = options + + return rest as type +} diff --git a/packages/core/src/query/verifyMessage.test.ts b/packages/core/src/query/verifyMessage.test.ts new file mode 100644 index 0000000000..ae43f6b07f --- /dev/null +++ b/packages/core/src/query/verifyMessage.test.ts @@ -0,0 +1,104 @@ +import { accounts, chain, config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { verifyMessageQueryOptions } from './verifyMessage.js' + +const address = accounts[0] + +test('default', () => { + expect( + verifyMessageQueryOptions(config, { + address, + message: 'This is a test message for viem!', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "message": "This is a test message for viem!", + "signature": "0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c", + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + verifyMessageQueryOptions(config, { + chainId: chain.mainnet2.id, + address, + message: 'This is a test message for viem!', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 456, + "message": "This is a test message for viem!", + "signature": "0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c", + }, + ], + } + `) +}) + +test('parameters: blockNumber', () => { + expect( + verifyMessageQueryOptions(config, { + blockNumber: 1234567890n, + address, + message: 'This is a test message for viem!', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockNumber": 1234567890n, + "message": "This is a test message for viem!", + "signature": "0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c", + }, + ], + } + `) +}) + +test('parameters: blockTag', () => { + expect( + verifyMessageQueryOptions(config, { + blockTag: 'safe', + address, + message: 'This is a test message for viem!', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockTag": "safe", + "message": "This is a test message for viem!", + "signature": "0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c", + }, + ], + } + `) +}) diff --git a/packages/core/src/query/verifyMessage.ts b/packages/core/src/query/verifyMessage.ts new file mode 100644 index 0000000000..d827e64b2b --- /dev/null +++ b/packages/core/src/query/verifyMessage.ts @@ -0,0 +1,56 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type VerifyMessageErrorType, + type VerifyMessageParameters, + type VerifyMessageReturnType, + verifyMessage, +} from '../actions/verifyMessage.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type VerifyMessageOptions = Compute< + ExactPartial> & ScopeKeyParameter +> + +export function verifyMessageQueryOptions( + config: config, + options: VerifyMessageOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { address, message, signature } = queryKey[1] + if (!address || !message || !signature) + throw new Error('address, message, and signature are required') + + const { scopeKey: _, ...parameters } = queryKey[1] + + const verified = await verifyMessage( + config, + parameters as VerifyMessageParameters, + ) + return verified ?? null + }, + queryKey: verifyMessageQueryKey(options), + } as const satisfies QueryOptions< + VerifyMessageQueryFnData, + VerifyMessageErrorType, + VerifyMessageData, + VerifyMessageQueryKey + > +} +export type VerifyMessageQueryFnData = VerifyMessageReturnType + +export type VerifyMessageData = VerifyMessageQueryFnData + +export function verifyMessageQueryKey( + options: VerifyMessageOptions, +) { + return ['verifyMessage', filterQueryOptions(options)] as const +} + +export type VerifyMessageQueryKey = ReturnType< + typeof verifyMessageQueryKey +> diff --git a/packages/core/src/query/verifyTypedData.test.ts b/packages/core/src/query/verifyTypedData.test.ts new file mode 100644 index 0000000000..f4145099ce --- /dev/null +++ b/packages/core/src/query/verifyTypedData.test.ts @@ -0,0 +1,284 @@ +import { accounts, chain, config, typedData } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { verifyTypedDataQueryOptions } from './verifyTypedData.js' + +const address = accounts[0] + +test('default', () => { + expect( + verifyTypedDataQueryOptions(config, { + address, + ...typedData.basic, + primaryType: 'Mail', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "verifyTypedData", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "domain": { + "chainId": 1, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": "0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c", + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + } + `) +}) + +test('parameters: chainId', () => { + expect( + verifyTypedDataQueryOptions(config, { + ...typedData.basic, + domain: { + ...typedData.basic.domain, + chainId: chain.mainnet2.id, + }, + chainId: chain.mainnet2.id, + address, + primaryType: 'Mail', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "verifyTypedData", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 456, + "domain": { + "chainId": 456, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": "0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c", + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + } + `) +}) + +test('parameters: blockNumber', () => { + expect( + verifyTypedDataQueryOptions(config, { + blockNumber: 1234567890n, + address, + ...typedData.basic, + primaryType: 'Mail', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "verifyTypedData", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockNumber": 1234567890n, + "domain": { + "chainId": 1, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": "0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c", + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + } + `) +}) + +test('parameters: blockTag', () => { + expect( + verifyTypedDataQueryOptions(config, { + blockTag: 'pending', + address, + ...typedData.basic, + primaryType: 'Mail', + signature: + '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "verifyTypedData", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockTag": "pending", + "domain": { + "chainId": 1, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": "0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c", + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + } + `) +}) diff --git a/packages/core/src/query/verifyTypedData.ts b/packages/core/src/query/verifyTypedData.ts new file mode 100644 index 0000000000..d7d3970b7e --- /dev/null +++ b/packages/core/src/query/verifyTypedData.ts @@ -0,0 +1,82 @@ +import type { QueryOptions } from '@tanstack/query-core' +import type { TypedData } from 'viem' + +import { + type VerifyTypedDataErrorType, + type VerifyTypedDataParameters, + type VerifyTypedDataReturnType, + verifyTypedData, +} from '../actions/verifyTypedData.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type VerifyTypedDataOptions< + typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', + config extends Config, +> = ExactPartial> & + ScopeKeyParameter + +export function verifyTypedDataQueryOptions< + config extends Config, + const typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', +>( + config: config, + options: VerifyTypedDataOptions = {} as any, +) { + return { + async queryFn({ queryKey }) { + const { + address, + message, + primaryType, + signature, + types, + scopeKey: _, + ...parameters + } = queryKey[1] + if (!address) throw new Error('address is required') + if (!message) throw new Error('message is required') + if (!primaryType) throw new Error('primaryType is required') + if (!signature) throw new Error('signature is required') + if (!types) throw new Error('types is required') + + const verified = await verifyTypedData(config, { + ...parameters, + address, + message, + primaryType, + signature, + types, + } as VerifyTypedDataParameters) + return verified ?? null + }, + queryKey: verifyTypedDataQueryKey(options), + } as const satisfies QueryOptions< + VerifyTypedDataQueryFnData, + VerifyTypedDataErrorType, + VerifyTypedDataData, + VerifyTypedDataQueryKey + > +} + +export type VerifyTypedDataQueryFnData = VerifyTypedDataReturnType + +export type VerifyTypedDataData = VerifyTypedDataQueryFnData + +export function verifyTypedDataQueryKey< + config extends Config, + const typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', +>(options: VerifyTypedDataOptions) { + return ['verifyTypedData', filterQueryOptions(options)] as const +} + +export type VerifyTypedDataQueryKey< + typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', + config extends Config, +> = ReturnType> diff --git a/packages/core/src/query/waitForCallsStatus.test.ts b/packages/core/src/query/waitForCallsStatus.test.ts new file mode 100644 index 0000000000..215ec72759 --- /dev/null +++ b/packages/core/src/query/waitForCallsStatus.test.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { waitForCallsStatusQueryOptions } from './waitForCallsStatus.js' + +test('default', () => { + expect( + waitForCallsStatusQueryOptions(config, { + id: '0x0000000000000000000000000000000000000000', + }), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "callsStatus", + { + "id": "0x0000000000000000000000000000000000000000", + }, + ], + "retry": [Function], + } + `) +}) diff --git a/packages/core/src/query/waitForCallsStatus.ts b/packages/core/src/query/waitForCallsStatus.ts new file mode 100644 index 0000000000..197c279e93 --- /dev/null +++ b/packages/core/src/query/waitForCallsStatus.ts @@ -0,0 +1,53 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type WaitForCallsStatusErrorType, + type WaitForCallsStatusParameters, + type WaitForCallsStatusReturnType, + waitForCallsStatus, +} from '../actions/waitForCallsStatus.js' +import type { Config } from '../createConfig.js' +import { ConnectorNotConnectedError } from '../errors/config.js' +import { filterQueryOptions } from '../query/utils.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, PartialBy } from '../types/utils.js' + +export type WaitForCallsStatusOptions = Compute< + PartialBy & ScopeKeyParameter +> + +export function waitForCallsStatusQueryOptions( + config: config, + options: WaitForCallsStatusOptions, +) { + return { + async queryFn({ queryKey }) { + const { scopeKey: _, id, ...parameters } = queryKey[1] + if (!id) throw new Error('id is required') + const status = await waitForCallsStatus(config, { ...parameters, id }) + return status + }, + queryKey: waitForCallsStatusQueryKey(options), + retry(failureCount, error) { + if (error instanceof ConnectorNotConnectedError) return false + return failureCount < 3 + }, + } as const satisfies QueryOptions< + WaitForCallsStatusQueryFnData, + WaitForCallsStatusErrorType, + WaitForCallsStatusData, + WaitForCallsStatusQueryKey + > +} + +export type WaitForCallsStatusQueryFnData = WaitForCallsStatusReturnType + +export type WaitForCallsStatusData = WaitForCallsStatusQueryFnData + +export function waitForCallsStatusQueryKey(options: WaitForCallsStatusOptions) { + return ['callsStatus', filterQueryOptions(options)] as const +} + +export type WaitForCallsStatusQueryKey = ReturnType< + typeof waitForCallsStatusQueryKey +> diff --git a/packages/core/src/query/waitForTransactionReceipt.test.ts b/packages/core/src/query/waitForTransactionReceipt.test.ts new file mode 100644 index 0000000000..1877e1bfeb --- /dev/null +++ b/packages/core/src/query/waitForTransactionReceipt.test.ts @@ -0,0 +1,18 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { waitForTransactionReceiptQueryOptions } from './waitForTransactionReceipt.js' + +test('default', () => { + expect( + waitForTransactionReceiptQueryOptions(config, {}), + ).toMatchInlineSnapshot(` + { + "queryFn": [Function], + "queryKey": [ + "waitForTransactionReceipt", + {}, + ], + } + `) +}) diff --git a/packages/core/src/query/waitForTransactionReceipt.ts b/packages/core/src/query/waitForTransactionReceipt.ts new file mode 100644 index 0000000000..3507dabeb8 --- /dev/null +++ b/packages/core/src/query/waitForTransactionReceipt.ts @@ -0,0 +1,71 @@ +import type { QueryOptions } from '@tanstack/query-core' + +import { + type WaitForTransactionReceiptErrorType, + type WaitForTransactionReceiptParameters, + type WaitForTransactionReceiptReturnType, + waitForTransactionReceipt, +} from '../actions/waitForTransactionReceipt.js' +import type { Config } from '../createConfig.js' +import type { ScopeKeyParameter } from '../types/properties.js' +import type { Compute, ExactPartial } from '../types/utils.js' +import { filterQueryOptions } from './utils.js' + +export type WaitForTransactionReceiptOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +> = Compute< + ExactPartial> & + ScopeKeyParameter +> + +export function waitForTransactionReceiptQueryOptions< + config extends Config, + chainId extends config['chains'][number]['id'], +>( + config: config, + options: WaitForTransactionReceiptOptions = {}, +) { + return { + async queryFn({ queryKey }) { + const { hash, ...parameters } = queryKey[1] + if (!hash) throw new Error('hash is required') + return waitForTransactionReceipt(config, { + ...parameters, + onReplaced: options.onReplaced, + hash, + }) as unknown as Promise< + WaitForTransactionReceiptReturnType + > + }, + queryKey: waitForTransactionReceiptQueryKey(options), + } as const satisfies QueryOptions< + WaitForTransactionReceiptQueryFnData, + WaitForTransactionReceiptErrorType, + WaitForTransactionReceiptData, + WaitForTransactionReceiptQueryKey + > +} + +export type WaitForTransactionReceiptQueryFnData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = WaitForTransactionReceiptReturnType + +export type WaitForTransactionReceiptData< + config extends Config, + chainId extends config['chains'][number]['id'], +> = WaitForTransactionReceiptQueryFnData + +export function waitForTransactionReceiptQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +>(options: WaitForTransactionReceiptOptions = {}) { + const { onReplaced: _, ...rest } = options + return ['waitForTransactionReceipt', filterQueryOptions(rest)] as const +} + +export type WaitForTransactionReceiptQueryKey< + config extends Config, + chainId extends config['chains'][number]['id'], +> = ReturnType> diff --git a/packages/core/src/query/watchAsset.test.ts b/packages/core/src/query/watchAsset.test.ts new file mode 100644 index 0000000000..b580beadba --- /dev/null +++ b/packages/core/src/query/watchAsset.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { watchAssetMutationOptions } from './watchAsset.js' + +test('default', () => { + expect(watchAssetMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "watchAsset", + ], + } + `) +}) diff --git a/packages/core/src/query/watchAsset.ts b/packages/core/src/query/watchAsset.ts new file mode 100644 index 0000000000..52ab82be60 --- /dev/null +++ b/packages/core/src/query/watchAsset.ts @@ -0,0 +1,42 @@ +import type { MutationOptions } from '@tanstack/query-core' + +import { + type WatchAssetErrorType, + type WatchAssetParameters, + type WatchAssetReturnType, + watchAsset, +} from '../actions/watchAsset.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' +import type { Mutate, MutateAsync } from './types.js' + +export function watchAssetMutationOptions(config: Config) { + return { + mutationFn(variables) { + return watchAsset(config, variables) + }, + mutationKey: ['watchAsset'], + } as const satisfies MutationOptions< + WatchAssetData, + WatchAssetErrorType, + WatchAssetVariables + > +} + +export type WatchAssetData = WatchAssetReturnType + +export type WatchAssetVariables = Compute + +export type WatchAssetMutate = Mutate< + WatchAssetData, + WatchAssetErrorType, + WatchAssetVariables, + context +> + +export type WatchAssetMutateAsync = MutateAsync< + WatchAssetData, + WatchAssetErrorType, + WatchAssetVariables, + context +> diff --git a/packages/core/src/query/writeContract.test-d.ts b/packages/core/src/query/writeContract.test-d.ts new file mode 100644 index 0000000000..3c80a83132 --- /dev/null +++ b/packages/core/src/query/writeContract.test-d.ts @@ -0,0 +1,145 @@ +import { http } from 'viem' +import { writeContract } from 'viem/actions' +import { base } from 'viem/chains' +import { test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import type { WriteContractMutate } from './writeContract.js' + +// https://github.com/wevm/wagmi/issues/3981 +test('gh#3981', () => { + const config = createConfig({ + chains: [base], + transports: { + [base.id]: http(), + }, + }) + + const abi = [ + { + type: 'function', + name: 'example1', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + name: 'example2', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + ] as const + + const write: WriteContractMutate = () => {} + write({ + abi, + address: '0x...', + functionName: 'example1', + args: ['0x...'], + value: 123n, + }) + write({ + abi: [ + { + type: 'function', + name: 'example1', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + name: 'example2', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + ] as const, + address: '0x...', + functionName: 'example1', + args: ['0x...'], + value: 123n, + }) + + write({ + abi, + address: '0x...', + functionName: 'example2', + args: ['0x...'], + // @ts-expect-error + value: 123n, + }) + write({ + abi: [ + { + type: 'function', + name: 'example1', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + name: 'example2', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + ], + address: '0x...', + functionName: 'example1', + args: ['0x...'], + value: 123n, + }) + + const client = config.getClient({ chainId: base.id }) + writeContract(client, { + abi, + address: '0x...', + account: '0x...', + functionName: 'example1', + args: ['0x...'], + value: 123n, + }) + writeContract(client, { + abi: [ + { + type: 'function', + name: 'example1', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + name: 'example2', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + ] as const, + address: '0x...', + account: '0x...', + functionName: 'example1', + args: ['0x...'], + value: 123n, + }) +}) diff --git a/packages/core/src/query/writeContract.test.ts b/packages/core/src/query/writeContract.test.ts new file mode 100644 index 0000000000..04cde53c76 --- /dev/null +++ b/packages/core/src/query/writeContract.test.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expect, test } from 'vitest' + +import { writeContractMutationOptions } from './writeContract.js' + +test('default', () => { + expect(writeContractMutationOptions(config)).toMatchInlineSnapshot(` + { + "mutationFn": [Function], + "mutationKey": [ + "writeContract", + ], + } + `) +}) diff --git a/packages/core/src/query/writeContract.ts b/packages/core/src/query/writeContract.ts new file mode 100644 index 0000000000..0e368fe40b --- /dev/null +++ b/packages/core/src/query/writeContract.ts @@ -0,0 +1,116 @@ +import type { MutateOptions, MutationOptions } from '@tanstack/query-core' +import type { Abi, ContractFunctionArgs, ContractFunctionName } from 'viem' + +import { + type WriteContractErrorType, + type WriteContractParameters, + type WriteContractReturnType, + writeContract, +} from '../actions/writeContract.js' +import type { Config } from '../createConfig.js' +import type { Compute } from '../types/utils.js' + +export function writeContractMutationOptions( + config: config, +) { + return { + mutationFn(variables) { + return writeContract(config, variables) + }, + mutationKey: ['writeContract'], + } as const satisfies MutationOptions< + WriteContractData, + WriteContractErrorType, + WriteContractVariables< + Abi, + string, + readonly unknown[], + config, + config['chains'][number]['id'] + > + > +} + +export type WriteContractData = Compute + +export type WriteContractVariables< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + config extends Config, + chainId extends config['chains'][number]['id'], + /// + allFunctionNames = ContractFunctionName, +> = WriteContractParameters< + abi, + functionName, + args, + config, + chainId, + allFunctionNames +> + +export type WriteContractMutate = < + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + chainId extends config['chains'][number]['id'], +>( + variables: WriteContractVariables, + options?: + | MutateOptions< + WriteContractData, + WriteContractErrorType, + WriteContractVariables< + abi, + functionName, + args, + config, + chainId, + // use `functionName` to make sure it's not union of all possible function names + functionName + >, + context + > + | undefined, +) => void + +export type WriteContractMutateAsync< + config extends Config, + context = unknown, +> = < + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + chainId extends config['chains'][number]['id'], +>( + variables: WriteContractVariables, + options?: + | MutateOptions< + WriteContractData, + WriteContractErrorType, + WriteContractVariables< + abi, + functionName, + args, + config, + chainId, + // use `functionName` to make sure it's not union of all possible function names + functionName + >, + context + > + | undefined, +) => Promise diff --git a/packages/core/src/transports/connector.test.ts b/packages/core/src/transports/connector.test.ts new file mode 100644 index 0000000000..4b71c2b46b --- /dev/null +++ b/packages/core/src/transports/connector.test.ts @@ -0,0 +1,97 @@ +import { config } from '@wagmi/test' +import { optimism } from 'viem/chains' +import { expect, test } from 'vitest' +import { createStore } from 'zustand' + +import { injected } from '../connectors/injected.js' +import { mock } from '../connectors/mock.js' +import { unstable_connector } from './connector.js' + +const connector = config.connectors[0]! + +test('setup', () => { + expect(unstable_connector(injected)({})).toMatchInlineSnapshot(` + { + "config": { + "key": "connector", + "methods": undefined, + "name": "Connector", + "request": [Function], + "retryCount": 3, + "retryDelay": 150, + "timeout": undefined, + "type": "connector", + }, + "request": [Function], + "value": undefined, + } + `) +}) + +test('behavior: connector type not found', () => { + const transport = unstable_connector({ type: 'foo' })({}) + expect(() => + transport.request({ method: 'eth_chainId' }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ProviderDisconnectedError: The Provider is disconnected from all chains. + + Details: Could not find connector of type "foo" in \`connectors\` passed to \`createConfig\`. + Version: viem@2.29.2] + `) +}) + +test('behavior: provider is disconnected', () => { + const transport = unstable_connector(mock)({ + connectors: createStore(() => [ + { + ...connector, + async getProvider() { + return undefined + }, + }, + ]), + }) + + expect(() => + transport.request({ method: 'eth_chainId' }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ProviderDisconnectedError: The Provider is disconnected from all chains. + + Details: Provider is disconnected. + Version: viem@2.29.2] + `) +}) + +test('behavior: chainId mismatch', () => { + const transport = unstable_connector(mock)({ + chain: optimism, + connectors: createStore(() => [ + { + ...connector, + async getProvider(options = {}) { + if (options.chainId === optimism.id) return connector.getProvider() + return undefined + }, + }, + ]), + }) + + expect(() => + transport.request({ method: 'eth_chainId' }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [ChainDisconnectedError: The Provider is not connected to the requested chain. + + Details: The current chain of the connector (id: 1) does not match the target chain for the request (id: 10 – OP Mainnet). + Version: viem@2.29.2] + `) +}) + +test('behavior: request', () => { + const transport = unstable_connector(mock)({ + connectors: createStore(() => [connector]), + }) + + expect( + transport.request({ method: 'eth_chainId' }), + ).resolves.toThrowErrorMatchingInlineSnapshot(`"0x1"`) +}) diff --git a/packages/core/src/transports/connector.ts b/packages/core/src/transports/connector.ts new file mode 100644 index 0000000000..0f03d43188 --- /dev/null +++ b/packages/core/src/transports/connector.ts @@ -0,0 +1,87 @@ +import { + ChainDisconnectedError, + type EIP1193Parameters, + type EIP1193Provider, + type EIP1193RequestFn, + ProviderDisconnectedError, + type TransportConfig, + type WalletRpcSchema, + createTransport, + hexToNumber, + withRetry, + withTimeout, +} from 'viem' + +import type { Connector, Transport } from '../createConfig.js' + +export type ConnectorTransportConfig = { + /** The key of the transport. */ + key?: TransportConfig['key'] | undefined + /** The name of the transport. */ + name?: TransportConfig['name'] | undefined + /** The max number of times to retry. */ + retryCount?: TransportConfig['retryCount'] | undefined + /** The base delay (in ms) between retries. */ + retryDelay?: TransportConfig['retryDelay'] | undefined +} + +export type ConnectorTransport = Transport + +export function unstable_connector( + connector: Pick, + config: ConnectorTransportConfig = {}, +): Transport<'connector'> { + const { type } = connector + const { key = 'connector', name = 'Connector', retryDelay } = config + + return (parameters) => { + const { chain, connectors } = parameters + const retryCount = config.retryCount ?? parameters.retryCount + + const request: EIP1193RequestFn = async ({ method, params }) => { + const connector = connectors?.getState().find((c) => c.type === type) + if (!connector) + throw new ProviderDisconnectedError( + new Error( + `Could not find connector of type "${type}" in \`connectors\` passed to \`createConfig\`.`, + ), + ) + + const provider = (await connector.getProvider({ + chainId: chain?.id, + })) as EIP1193Provider | undefined + if (!provider) + throw new ProviderDisconnectedError( + new Error('Provider is disconnected.'), + ) + + // We are applying a retry & timeout strategy here as some injected wallets (e.g. MetaMask) fail to + // immediately resolve a JSON-RPC request on page load. + const chainId = hexToNumber( + await withRetry(() => + withTimeout(() => provider.request({ method: 'eth_chainId' }), { + timeout: 100, + }), + ), + ) + if (chain && chainId !== chain.id) + throw new ChainDisconnectedError( + new Error( + `The current chain of the connector (id: ${chainId}) does not match the target chain for the request (id: ${chain.id} – ${chain.name}).`, + ), + ) + + const body = { method, params } as EIP1193Parameters + return provider.request(body) + } + + return createTransport({ + key, + name, + request, + retryCount, + retryDelay, + type: 'connector', + }) + } +} diff --git a/packages/core/src/transports/fallback.test.ts b/packages/core/src/transports/fallback.test.ts new file mode 100644 index 0000000000..dcae23cb79 --- /dev/null +++ b/packages/core/src/transports/fallback.test.ts @@ -0,0 +1,63 @@ +import { http } from 'viem' +import { expect, test } from 'vitest' +import { unstable_connector } from './connector.js' +import { fallback } from './fallback.js' + +test('setup', () => { + expect( + fallback([ + unstable_connector({ type: 'injected' }), + http('https://example.com'), + ])({}), + ).toMatchInlineSnapshot(` + { + "config": { + "key": "fallback", + "methods": undefined, + "name": "Fallback", + "request": [Function], + "retryCount": 3, + "retryDelay": 150, + "timeout": undefined, + "type": "fallback", + }, + "request": [Function], + "value": { + "onResponse": [Function], + "transports": [ + { + "config": { + "key": "connector", + "methods": undefined, + "name": "Connector", + "request": [Function], + "retryCount": 0, + "retryDelay": 150, + "timeout": undefined, + "type": "connector", + }, + "request": [Function], + "value": undefined, + }, + { + "config": { + "key": "http", + "methods": undefined, + "name": "HTTP JSON-RPC", + "request": [Function], + "retryCount": 0, + "retryDelay": 150, + "timeout": 10000, + "type": "http", + }, + "request": [Function], + "value": { + "fetchOptions": undefined, + "url": "https://example.com", + }, + }, + ], + }, + } + `) +}) diff --git a/packages/core/src/transports/fallback.ts b/packages/core/src/transports/fallback.ts new file mode 100644 index 0000000000..67069f5de1 --- /dev/null +++ b/packages/core/src/transports/fallback.ts @@ -0,0 +1,10 @@ +import { fallback as viem_fallback } from 'viem' + +import type { Transport } from '../createConfig.js' + +export function fallback( + transports: Transport[], + config?: Parameters[1], +) { + return viem_fallback(transports, config) +} diff --git a/packages/core/src/types/chain.test-d.ts b/packages/core/src/types/chain.test-d.ts new file mode 100644 index 0000000000..65632709e1 --- /dev/null +++ b/packages/core/src/types/chain.test-d.ts @@ -0,0 +1,33 @@ +import type { Chain, mainnet, optimism, sepolia } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import type { Config } from '../createConfig.js' +import type { SelectChains } from './chain.js' +import type { Merge } from './utils.js' + +test('not narrowable', () => { + type Result = SelectChains + expectTypeOf().toEqualTypeOf() +}) + +test('chainId', () => { + type Result = SelectChains< + Config, + 1 + > + expectTypeOf().toEqualTypeOf() +}) + +test('at least one chain has formatters', () => { + type Result = SelectChains> + expectTypeOf().toEqualTypeOf< + readonly [typeof mainnet, typeof optimism] + >() +}) + +test('no formatters', () => { + type Result = SelectChains> + expectTypeOf().toEqualTypeOf< + readonly [Merge] + >() +}) diff --git a/packages/core/src/types/chain.ts b/packages/core/src/types/chain.ts new file mode 100644 index 0000000000..5f81783171 --- /dev/null +++ b/packages/core/src/types/chain.ts @@ -0,0 +1,26 @@ +import type { Chain, ChainFormatters } from 'viem' + +import type { Config } from '../createConfig.js' +import type { IsNarrowable, Merge } from './utils.js' + +/** Filters {@link Config} chains by {@link chainId} or simplifies if no `ChainFormatters` are present. */ +export type SelectChains< + config extends Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, +> = Config extends config + ? readonly [Chain] // chains not inferrable, return default + : IsNarrowable extends true + ? readonly [Extract] // select specific chain + : HasFormatter extends true + ? config['chains'] // return all chains since one has formatter + : // return default chain with ID set to union (allows for more simple type since the only thing that is different is the chain ID for each chain) + readonly [Merge] + +type HasFormatter = chains extends readonly [ + infer head extends Chain, + ...infer tail extends readonly Chain[], +] + ? IsNarrowable extends true + ? true + : HasFormatter + : false diff --git a/packages/core/src/types/properties.ts b/packages/core/src/types/properties.ts new file mode 100644 index 0000000000..760cc46bd2 --- /dev/null +++ b/packages/core/src/types/properties.ts @@ -0,0 +1,23 @@ +import type { Config, Connector } from '../createConfig.js' + +export type ChainIdParameter< + config extends Config, + chainId extends + | config['chains'][number]['id'] + | undefined = config['chains'][number]['id'], +> = { + chainId?: + | (chainId extends config['chains'][number]['id'] ? chainId : undefined) + | config['chains'][number]['id'] + | undefined +} + +export type ConnectorParameter = { + connector?: Connector | undefined +} + +export type ScopeKeyParameter = { scopeKey?: string | undefined } + +export type SyncConnectedChainParameter = { + syncConnectedChain?: boolean | undefined +} diff --git a/packages/core/src/types/register.ts b/packages/core/src/types/register.ts new file mode 100644 index 0000000000..db420adfdc --- /dev/null +++ b/packages/core/src/types/register.ts @@ -0,0 +1,9 @@ +import type { Config } from '../createConfig.js' + +// biome-ignore lint/suspicious/noEmptyInterface: +export interface Register {} +export type ResolvedRegister = { + config: Register extends { config: infer config extends Config } + ? config + : Config +} diff --git a/packages/core/src/types/unit.ts b/packages/core/src/types/unit.ts new file mode 100644 index 0000000000..a64f794a87 --- /dev/null +++ b/packages/core/src/types/unit.ts @@ -0,0 +1 @@ +export type Unit = 'ether' | 'gwei' | 'wei' | number diff --git a/packages/core/src/types/utils.test-d.ts b/packages/core/src/types/utils.test-d.ts new file mode 100644 index 0000000000..523f9f2e36 --- /dev/null +++ b/packages/core/src/types/utils.test-d.ts @@ -0,0 +1,40 @@ +import { assertType, expectTypeOf, test } from 'vitest' + +import type { + Compute, + ExactPartial, + IsNever, + Mutable, + OneOf, + PartialBy, +} from './utils.js' + +test('ExactPartial', () => { + expectTypeOf>().toEqualTypeOf<{ + foo?: boolean | undefined + bar?: boolean | undefined + }>() +}) + +test('IsNever', () => { + expectTypeOf>().toEqualTypeOf() +}) + +test('Mutable', () => { + expectTypeOf< + Mutable<{ foo: boolean; readonly bar: boolean }> + >().toEqualTypeOf<{ foo: boolean; bar: boolean }>() +}) + +test('OneOf', () => { + assertType>({ foo: false }) + assertType>({ bar: false }) +}) + +test('PartialBy', () => { + type Result = Compute> + expectTypeOf().toEqualTypeOf<{ + foo?: string | undefined + bar: number + }>() +}) diff --git a/packages/core/src/types/utils.ts b/packages/core/src/types/utils.ts new file mode 100644 index 0000000000..aecd58a142 --- /dev/null +++ b/packages/core/src/types/utils.ts @@ -0,0 +1,101 @@ +/** Combines members of an intersection into a readable type. */ +// https://twitter.com/mattpocockuk/status/1622730173446557697?s=20&t=NdpAcmEFXY01xkqU3KO0Mg +export type Compute = { [key in keyof type]: type[key] } & unknown + +/** + * Makes all properties of an object optional. + * + * Compatible with [`exactOptionalPropertyTypes`](https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes). + */ +export type ExactPartial = { + [key in keyof type]?: type[key] | undefined +} + +/** Checks if {@link type} can be narrowed further than {@link type2} */ +export type IsNarrowable = IsUnknown extends true + ? false + : undefined extends type + ? false + : IsNever< + (type extends type2 ? true : false) & + (type2 extends type ? false : true) + > extends true + ? false + : true + +/** + * @internal + * Checks if {@link type} is `never` + */ +export type IsNever = [type] extends [never] ? true : false + +/** + * @internal + * Checks if {@link type} is `unknown` + */ +export type IsUnknown = unknown extends type ? true : false + +/** Merges two object types into new type */ +export type Merge = Compute< + LooseOmit & + obj2 +> + +/** Removes `readonly` from all properties of an object. */ +export type Mutable = { + -readonly [key in keyof type]: type[key] +} + +/** Strict version of built-in Omit type */ +export type StrictOmit = Pick< + type, + Exclude +> + +/** Makes objects destructurable. */ +export type OneOf< + union extends object, + /// + keys extends KeyofUnion = KeyofUnion, +> = union extends infer Item + ? Compute]?: undefined }> + : never +type KeyofUnion = type extends type ? keyof type : never + +/** Makes {@link key} optional in {@link type} while preserving type inference. */ +// s/o trpc (https://github.com/trpc/trpc/blob/main/packages/server/src/types.ts#L6) +export type PartialBy = ExactPartial< + Pick +> & + StrictOmit + +/* Removes `undefined` from object property */ +export type RemoveUndefined = { + [key in keyof type]: NonNullable +} + +/////////////////////////////////////////////////////////////////////////// +// Loose types + +/** Loose version of {@link StrictOmit} */ +export type LooseOmit = Pick< + type, + Exclude +> + +/////////////////////////////////////////////////////////////////////////// +// Union types + +export type UnionCompute = type extends object ? Compute : type + +export type UnionLooseOmit = type extends any + ? LooseOmit + : never + +export type UnionStrictOmit = type extends any + ? StrictOmit + : never + +export type UnionExactPartial = type extends object + ? ExactPartial + : type diff --git a/packages/core/src/utils/cookie.test.ts b/packages/core/src/utils/cookie.test.ts new file mode 100644 index 0000000000..f21f5ccee1 --- /dev/null +++ b/packages/core/src/utils/cookie.test.ts @@ -0,0 +1,69 @@ +import { http } from 'viem' +import { mainnet } from 'viem/chains' +import { expect, test } from 'vitest' + +import { createConfig } from '../createConfig.js' +import { createStorage } from '../createStorage.js' +import { cookieStorage, cookieToInitialState, parseCookie } from './cookie.js' + +test('cookieStorage', () => { + expect(cookieStorage.getItem('recentConnectorId')).toMatchInlineSnapshot( + 'null', + ) + cookieStorage.setItem('recentConnectorId', 'foo') + expect(cookieStorage.getItem('recentConnectorId')).toMatchInlineSnapshot( + `"foo"`, + ) + cookieStorage.removeItem('recentConnectorId') + expect(cookieStorage.getItem('recentConnectorId')).toMatchInlineSnapshot( + 'null', + ) +}) + +test('cookieToInitialState', () => { + const config = createConfig({ + chains: [mainnet], + transports: { [mainnet.id]: http() }, + storage: createStorage({ storage: cookieStorage }), + }) + + expect( + cookieToInitialState( + config, + 'wagmi.store={"state":{"connections":{"__type":"Map","value":[]},"chainId":1,"current":null},"version":2}; ', + ), + ).toMatchInlineSnapshot(` + { + "chainId": 1, + "connections": Map {}, + "current": null, + } + `) + + expect(cookieToInitialState(config)).toMatchInlineSnapshot('undefined') + expect(cookieToInitialState(config), 'foo').toMatchInlineSnapshot('undefined') +}) + +test('parseCookie', () => { + expect( + parseCookie( + 'wagmi.store={"state":{"connections":{"__type":"Map","value":[]},"chainId":1,"current":null},"version":2}; ', + 'wagmi.store', + ), + ).toMatchInlineSnapshot( + `"{"state":{"connections":{"__type":"Map","value":[]},"chainId":1,"current":null},"version":2}"`, + ) + + expect( + parseCookie( + 'foo="bar"; wagmi.store={"state":{"connections":{"__type":"Map","value":[]},"chainId":1,"current":null},"version":2}; ', + 'wagmi.store', + ), + ).toMatchInlineSnapshot( + `"{"state":{"connections":{"__type":"Map","value":[]},"chainId":1,"current":null},"version":2}"`, + ) + + expect(parseCookie('foo="bar"; ', 'wagmi.store')).toMatchInlineSnapshot( + 'undefined', + ) +}) diff --git a/packages/core/src/utils/cookie.ts b/packages/core/src/utils/cookie.ts new file mode 100644 index 0000000000..ccd07bc7b7 --- /dev/null +++ b/packages/core/src/utils/cookie.ts @@ -0,0 +1,33 @@ +import type { Config, State } from '../createConfig.js' +import type { BaseStorage } from '../createStorage.js' +import { deserialize } from './deserialize.js' + +export const cookieStorage = { + getItem(key) { + if (typeof window === 'undefined') return null + const value = parseCookie(document.cookie, key) + return value ?? null + }, + setItem(key, value) { + if (typeof window === 'undefined') return + document.cookie = `${key}=${value};path=/;samesite=Lax` + }, + removeItem(key) { + if (typeof window === 'undefined') return + document.cookie = `${key}=;max-age=-1;path=/` + }, +} satisfies BaseStorage + +export function cookieToInitialState(config: Config, cookie?: string | null) { + if (!cookie) return undefined + const key = `${config.storage?.key}.store` + const parsed = parseCookie(cookie, key) + if (!parsed) return undefined + return deserialize<{ state: State }>(parsed).state +} + +export function parseCookie(cookie: string, key: string) { + const keyValue = cookie.split('; ').find((x) => x.startsWith(`${key}=`)) + if (!keyValue) return undefined + return keyValue.substring(key.length + 1) +} diff --git a/packages/core/src/utils/deepEqual.test.ts b/packages/core/src/utils/deepEqual.test.ts new file mode 100644 index 0000000000..6105fe496f --- /dev/null +++ b/packages/core/src/utils/deepEqual.test.ts @@ -0,0 +1,40 @@ +import { expect, test } from 'vitest' + +import { deepEqual } from './deepEqual.js' + +test('compares primitive values', () => { + expect(deepEqual(true, true)).toBe(true) + expect(deepEqual(true, false)).toBe(false) + + expect(deepEqual(1, 1)).toBe(true) + expect(deepEqual(1, 2)).toBe(false) + + expect(deepEqual('zustand', 'zustand')).toBe(true) + expect(deepEqual('zustand', 'redux')).toBe(false) +}) + +test('compares objects', () => { + expect(deepEqual({ foo: 'bar', asd: 123 }, { foo: 'bar', asd: 123 })).toBe( + true, + ) + + expect( + deepEqual({ foo: 'bar', asd: 123 }, { foo: 'bar', foobar: true }), + ).toBe(false) + + expect( + deepEqual({ foo: 'bar', asd: 123 }, { foo: 'bar', asd: 123, foobar: true }), + ).toBe(false) +}) + +test('compares arrays', () => { + expect(deepEqual([1, 2, 3], [1, 2, 3])).toBe(true) + + expect(deepEqual([1, 2, 3], [2, 3, 4])).toBe(false) + + expect( + deepEqual([{ foo: 'bar' }, { asd: 123 }], [{ foo: 'bar' }, { asd: 123 }]), + ).toBe(true) + + expect(deepEqual([{ foo: 'bar' }], [{ foo: 'bar', asd: 123 }])).toBe(false) +}) diff --git a/packages/core/src/utils/deepEqual.ts b/packages/core/src/utils/deepEqual.ts new file mode 100644 index 0000000000..89b47a6bdd --- /dev/null +++ b/packages/core/src/utils/deepEqual.ts @@ -0,0 +1,43 @@ +/** Forked from https://github.com/epoberezkin/fast-deep-equal */ + +export function deepEqual(a: any, b: any) { + if (a === b) return true + + if (a && b && typeof a === 'object' && typeof b === 'object') { + if (a.constructor !== b.constructor) return false + + let length: number + let i: number + + if (Array.isArray(a) && Array.isArray(b)) { + length = a.length + if (length !== b.length) return false + for (i = length; i-- !== 0; ) if (!deepEqual(a[i], b[i])) return false + return true + } + + if (a.valueOf !== Object.prototype.valueOf) + return a.valueOf() === b.valueOf() + if (a.toString !== Object.prototype.toString) + return a.toString() === b.toString() + + const keys = Object.keys(a) + length = keys.length + if (length !== Object.keys(b).length) return false + + for (i = length; i-- !== 0; ) + if (!Object.prototype.hasOwnProperty.call(b, keys[i]!)) return false + + for (i = length; i-- !== 0; ) { + const key = keys[i] + + if (key && !deepEqual(a[key], b[key])) return false + } + + return true + } + + // true if both NaN, false otherwise + // biome-ignore lint/suspicious/noSelfCompare: + return a !== a && b !== b +} diff --git a/packages/core/src/utils/deserialize.test.ts b/packages/core/src/utils/deserialize.test.ts new file mode 100644 index 0000000000..11f73ff79b --- /dev/null +++ b/packages/core/src/utils/deserialize.test.ts @@ -0,0 +1,114 @@ +import { expect, test } from 'vitest' + +import { deserialize } from './deserialize.js' +import { serialize } from './serialize.js' + +test('deserializes', () => { + const deserializedCache = deserialize( + serialize({ + some: 'complex', + object: { + that: 'has', + many: [ + { many: 'many', manymany: 'many' }, + { many: 'many' }, + { many: 'many' }, + { + many: { + properties: { + ones: { + that: { + have: { + functions: () => null, + }, + }, + }, + }, + }, + }, + ], + }, + and: { + ones: { + that: { + have: { + bigints: 123456789012345678901234567890n, + }, + }, + }, + }, + also: { + ones: { + that: { + have: { + proxies: new Proxy({ lol: 'nice' }, {}), + }, + }, + }, + }, + }), + ) + expect(deserializedCache).toMatchInlineSnapshot(` + { + "also": { + "ones": { + "that": { + "have": { + "proxies": { + "lol": "nice", + }, + }, + }, + }, + }, + "and": { + "ones": { + "that": { + "have": { + "bigints": 123456789012345678901234567890n, + }, + }, + }, + }, + "object": { + "many": [ + { + "many": "many", + "manymany": "many", + }, + { + "many": "many", + }, + { + "many": "many", + }, + { + "many": { + "properties": { + "ones": { + "that": { + "have": {}, + }, + }, + }, + }, + }, + ], + "that": "has", + }, + "some": "complex", + } + `) +}) + +test('Map', () => { + const map = new Map().set('foo', { bar: 'baz' }) + const deserializedCache = deserialize(serialize({ map })) + expect(deserializedCache).toEqual({ map }) +}) + +test('bigint', () => { + const bigint = 123n + const deserializedCache = deserialize(serialize({ bigint })) + expect(deserializedCache).toEqual({ bigint }) +}) diff --git a/packages/core/src/utils/deserialize.ts b/packages/core/src/utils/deserialize.ts new file mode 100644 index 0000000000..36fe30dab8 --- /dev/null +++ b/packages/core/src/utils/deserialize.ts @@ -0,0 +1,10 @@ +type Reviver = (key: string, value: any) => any + +export function deserialize(value: string, reviver?: Reviver): type { + return JSON.parse(value, (key, value_) => { + let value = value_ + if (value?.__type === 'bigint') value = BigInt(value.value) + if (value?.__type === 'Map') value = new Map(value.value) + return reviver?.(key, value) ?? value + }) +} diff --git a/packages/core/src/utils/extractRpcUrls.test.ts b/packages/core/src/utils/extractRpcUrls.test.ts new file mode 100644 index 0000000000..6d2c21e26e --- /dev/null +++ b/packages/core/src/utils/extractRpcUrls.test.ts @@ -0,0 +1,92 @@ +import { http } from 'viem' +import { mainnet, optimism, sepolia } from 'viem/chains' +import { expect, test } from 'vitest' + +import { injected } from '../connectors/injected.js' +import { unstable_connector } from '../transports/connector.js' +import { fallback } from '../transports/fallback.js' +import { extractRpcUrls } from './extractRpcUrls.js' + +test('default', () => { + expect( + extractRpcUrls({ + chain: mainnet, + transports: { + [mainnet.id]: fallback([ + http('https://wagmi.com'), + http('https://lol.com'), + ]), + [sepolia.id]: http('https://sepoliarocks.com'), + [optimism.id]: http(), + }, + }), + ).toMatchInlineSnapshot(` + [ + "https://wagmi.com", + "https://lol.com", + ] + `) + + expect( + extractRpcUrls({ + chain: sepolia, + transports: { + [mainnet.id]: fallback([ + http('https://wagmi.com'), + http('https://lol.com'), + ]), + [sepolia.id]: http('https://sepoliarocks.com'), + [optimism.id]: http(), + }, + }), + ).toMatchInlineSnapshot(` + [ + "https://sepoliarocks.com", + ] + `) + + expect( + extractRpcUrls({ + chain: optimism, + transports: { + [mainnet.id]: fallback([ + http('https://wagmi.com'), + http('https://lol.com'), + ]), + [sepolia.id]: http('https://sepoliarocks.com'), + [optimism.id]: http(), + }, + }), + ).toMatchInlineSnapshot(` + [ + "https://mainnet.optimism.io", + ] + `) + + expect( + extractRpcUrls({ + chain: mainnet, + }), + ).toMatchInlineSnapshot(` + [ + "https://eth.merkle.io", + ] + `) + + expect( + extractRpcUrls({ + chain: mainnet, + transports: { + [mainnet.id]: fallback([ + unstable_connector(injected), + http('https://lol.com'), + ]), + }, + }), + ).toMatchInlineSnapshot(` + [ + "https://eth.merkle.io", + "https://lol.com", + ] + `) +}) diff --git a/packages/core/src/utils/extractRpcUrls.ts b/packages/core/src/utils/extractRpcUrls.ts new file mode 100644 index 0000000000..a617eac13c --- /dev/null +++ b/packages/core/src/utils/extractRpcUrls.ts @@ -0,0 +1,19 @@ +import type { Chain, Transport } from 'viem' + +type ExtractRpcUrlsParameters = { + transports?: Record | undefined + chain: Chain +} + +export function extractRpcUrls(parameters: ExtractRpcUrlsParameters) { + const { chain } = parameters + const fallbackUrl = chain.rpcUrls.default.http[0] + + if (!parameters.transports) return [fallbackUrl] + + const transport = parameters.transports?.[chain.id]?.({ chain }) + const transports = (transport?.value?.transports as NonNullable< + typeof transport + >[]) || [transport] + return transports.map(({ value }) => value?.url || fallbackUrl) +} diff --git a/packages/core/src/utils/getAction.test.ts b/packages/core/src/utils/getAction.test.ts new file mode 100644 index 0000000000..7173852947 --- /dev/null +++ b/packages/core/src/utils/getAction.test.ts @@ -0,0 +1,49 @@ +import { abi, address, config } from '@wagmi/test' +import * as viem_actions from 'viem/actions' +import { expect, test, vi } from 'vitest' + +import { getAction } from './getAction.js' + +test('uses tree-shakable action', async () => { + const client = config.getClient({ chainId: 1 }) + + const name = 'getBlockNumber' + const actions = { ...viem_actions } + const spy = vi.spyOn(actions, name) + const action = getAction(client, actions[name], name) + + await action({}) + expect(spy).toBeCalledWith(client, {}) +}) + +test('uses client action', async () => { + const client = config + .getClient({ chainId: 1 }) + .extend(() => ({ getBlockNumber: async () => 69n })) + + const name = 'getBlockNumber' + const spy = vi.spyOn(client, name) + const action = getAction(client, client[name], name) + + await action({}) + expect(spy).toBeCalledWith({}) +}) + +test('internal call', async () => { + const client = config.getClient({ chainId: 1 }).extend(() => ({ + async call() { + return { + data: '0x0000000000000000000000000000000000000000000000000000000000000045', + } + }, + })) + + await expect( + viem_actions.readContract(client, { + address: address.wagmiMintExample, + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }), + ).resolves.toEqual(69n) +}) diff --git a/packages/core/src/utils/getAction.ts b/packages/core/src/utils/getAction.ts new file mode 100644 index 0000000000..514ceb5d50 --- /dev/null +++ b/packages/core/src/utils/getAction.ts @@ -0,0 +1,44 @@ +import type { + Account, + Chain, + Client, + PublicActions, + RpcSchema, + Transport, + WalletActions, +} from 'viem' + +/** + * Retrieves and returns an action from the client (if exists), and falls + * back to the tree-shakable action. + * + * Useful for extracting overridden actions from a client (ie. if a consumer + * wants to override the `sendTransaction` implementation). + */ +export function getAction< + transport extends Transport, + chain extends Chain | undefined, + account extends Account | undefined, + rpcSchema extends RpcSchema | undefined, + extended extends { [key: string]: unknown }, + client extends Client, + parameters, + returnType, +>( + client: client, + actionFn: (_: any, parameters: parameters) => returnType, + // Some minifiers drop `Function.prototype.name`, or replace it with short letters, + // meaning that `actionFn.name` will not always work. For that case, the consumer + // needs to pass the name explicitly. + name: keyof PublicActions | keyof WalletActions, +): (parameters: parameters) => returnType { + const action_implicit = client[actionFn.name] + if (typeof action_implicit === 'function') + return action_implicit as (params: parameters) => returnType + + const action_explicit = client[name] + if (typeof action_explicit === 'function') + return action_explicit as (params: parameters) => returnType + + return (params) => actionFn(client, params) +} diff --git a/packages/core/src/utils/getUnit.test.ts b/packages/core/src/utils/getUnit.test.ts new file mode 100644 index 0000000000..1054303def --- /dev/null +++ b/packages/core/src/utils/getUnit.test.ts @@ -0,0 +1,9 @@ +import { expect, test } from 'vitest' + +import { getUnit } from './getUnit.js' + +test('default', () => { + expect(getUnit(1)).toMatchInlineSnapshot('1') + expect(getUnit('wei')).toMatchInlineSnapshot('0') + expect(getUnit('ether')).toMatchInlineSnapshot('18') +}) diff --git a/packages/core/src/utils/getUnit.ts b/packages/core/src/utils/getUnit.ts new file mode 100644 index 0000000000..084f45b622 --- /dev/null +++ b/packages/core/src/utils/getUnit.ts @@ -0,0 +1,9 @@ +import { weiUnits } from 'viem' + +import type { Unit } from '../types/unit.js' + +export function getUnit(unit: Unit) { + if (typeof unit === 'number') return unit + if (unit === 'wei') return 0 + return Math.abs(weiUnits[unit]) +} diff --git a/packages/core/src/utils/getVersion.test.ts b/packages/core/src/utils/getVersion.test.ts new file mode 100644 index 0000000000..29fa256178 --- /dev/null +++ b/packages/core/src/utils/getVersion.test.ts @@ -0,0 +1,7 @@ +import { expect, test } from 'vitest' + +import { getVersion } from './getVersion.js' + +test('default', () => { + expect(getVersion()).toMatchInlineSnapshot(`"@wagmi/core@x.y.z"`) +}) diff --git a/packages/core/src/utils/getVersion.ts b/packages/core/src/utils/getVersion.ts new file mode 100644 index 0000000000..3506365267 --- /dev/null +++ b/packages/core/src/utils/getVersion.ts @@ -0,0 +1,3 @@ +import { version } from '../version.js' + +export const getVersion = () => `@wagmi/core@${version}` diff --git a/packages/core/src/utils/normalizeChainId.test.ts b/packages/core/src/utils/normalizeChainId.test.ts new file mode 100644 index 0000000000..de9d1882e3 --- /dev/null +++ b/packages/core/src/utils/normalizeChainId.test.ts @@ -0,0 +1,24 @@ +import { expect, test } from 'vitest' + +import { normalizeChainId } from './normalizeChainId.js' + +test.each([ + { chainId: 1, expected: 1 }, + { chainId: '1', expected: 1 }, + { chainId: '0x1', expected: 1 }, + { chainId: '0x4', expected: 4 }, + { chainId: 42, expected: 42 }, + { chainId: '42', expected: 42 }, + { chainId: '0x2a', expected: 42 }, + { chainId: ' 0x2a', expected: 42 }, + { chainId: BigInt(1), expected: 1 }, + { chainId: BigInt(10), expected: 10 }, +])('normalizeChainId($chainId)', ({ chainId, expected }) => { + expect(normalizeChainId(chainId)).toEqual(expected) +}) + +test('unknown type', () => { + expect(() => normalizeChainId({})).toThrow( + 'Cannot normalize chainId "[object Object]" of type "object"', + ) +}) diff --git a/packages/core/src/utils/normalizeChainId.ts b/packages/core/src/utils/normalizeChainId.ts new file mode 100644 index 0000000000..a1017c0953 --- /dev/null +++ b/packages/core/src/utils/normalizeChainId.ts @@ -0,0 +1,13 @@ +/** @deprecated use `Number` instead */ +export function normalizeChainId(chainId: bigint | number | string | unknown) { + if (typeof chainId === 'string') + return Number.parseInt( + chainId, + chainId.trim().substring(0, 2) === '0x' ? 16 : 10, + ) + if (typeof chainId === 'bigint') return Number(chainId) + if (typeof chainId === 'number') return chainId + throw new Error( + `Cannot normalize chainId "${chainId}" of type "${typeof chainId}"`, + ) +} diff --git a/packages/core/src/utils/serialize.test.ts b/packages/core/src/utils/serialize.test.ts new file mode 100644 index 0000000000..e182101130 --- /dev/null +++ b/packages/core/src/utils/serialize.test.ts @@ -0,0 +1,241 @@ +import { describe, expect, it } from 'vitest' + +import { serialize } from './serialize.js' + +class Foo { + value: string + + constructor(value: string) { + this.value = value + } +} + +const simpleObject = { + boolean: true, + fn() { + return 'foo' + }, + nan: Number.NaN, + nil: null, + number: 123, + string: 'foo', + undef: undefined, + [Symbol('key')]: 'value', +} + +const bigintObject = Object.assign({}, simpleObject, { + bigint: 123n, + nested: { + bigint: { + value: 69n, + }, + }, +}) + +const bufferString = 'this is a test buffer' +const complexObject = Object.assign({}, simpleObject, { + array: ['foo', { bar: 'baz' }], + buffer: Buffer.alloc(bufferString.length, bufferString), + error: new Error('boom'), + foo: new Foo('value'), + map: new Map().set('foo', { bar: 'baz' }), + object: { foo: { bar: 'baz' } }, + promise: Promise.resolve('foo'), + regexp: /foo/, + set: new Set().add('foo').add({ bar: 'baz' }), + weakmap: new WeakMap([ + [{}, 'foo'], + [{}, 'bar'], + ]), + weakset: new WeakSet([{}, {}]), +}) + +const circularObject = Object.assign( + {}, + complexObject, + { + map: { __type: 'Map', value: [['foo', { bar: 'baz' }]] }, + }, + { + deeply: { + nested: { + reference: {}, + }, + }, + }, +) + +circularObject.deeply.nested.reference = circularObject + +describe('stringify', () => { + describe('handling of object types', () => { + it('should handle simple objects', () => { + const result = serialize(simpleObject) + + expect(result).toMatchInlineSnapshot( + `"{"boolean":true,"nan":null,"nil":null,"number":123,"string":"foo"}"`, + ) + }) + + it('should handle objects with bigints', () => { + const result = serialize(bigintObject) + + expect(result).toMatchInlineSnapshot( + `"{"boolean":true,"nan":null,"nil":null,"number":123,"string":"foo","bigint":{"__type":"bigint","value":"123"},"nested":{"bigint":{"value":{"__type":"bigint","value":"69"}}}}"`, + ) + }) + + it('should handle simple objects with a custom replacer', () => { + const replacer = (_key: string, value: any) => + value && typeof value === 'object' ? value : `primitive-${value}` + + const result = serialize(simpleObject, replacer) + + expect(result).toMatchInlineSnapshot( + `"{"boolean":"primitive-true","fn":"primitive-fn() {\\n return \\"foo\\";\\n }","nan":"primitive-NaN","nil":"primitive-null","number":"primitive-123","string":"primitive-foo","undef":"primitive-undefined"}"`, + ) + }) + + it('should handle simple objects with indentation', () => { + const result = serialize(simpleObject, null, 2) + + expect(result).toMatchInlineSnapshot(` + "{ + "boolean": true, + "nan": null, + "nil": null, + "number": 123, + "string": "foo" + }" + `) + }) + + it('should handle bigint objects with indentation', () => { + const result = serialize(bigintObject, null, 2) + + expect(result).toMatchInlineSnapshot(` + "{ + "boolean": true, + "nan": null, + "nil": null, + "number": 123, + "string": "foo", + "bigint": { + "__type": "bigint", + "value": "123" + }, + "nested": { + "bigint": { + "value": { + "__type": "bigint", + "value": "69" + } + } + } + }" + `) + }) + + it('should handle complex objects', () => { + const result = serialize(complexObject) + + expect(result).toMatchInlineSnapshot( + `"{"boolean":true,"nan":null,"nil":null,"number":123,"string":"foo","array":["foo",{"bar":"baz"}],"buffer":{"type":"Buffer","data":[116,104,105,115,32,105,115,32,97,32,116,101,115,116,32,98,117,102,102,101,114]},"error":{},"foo":{"value":"value"},"map":{"__type":"Map","value":[["foo",{"bar":"baz"}]]},"object":{"foo":{"bar":"baz"}},"promise":{},"regexp":{},"set":{},"weakmap":{},"weakset":{}}"`, + ) + }) + + it('should handle complex objects with a custom replacer', () => { + const replacer = (_key: string, value: any) => + value && typeof value === 'object' ? value : `primitive-${value}` + + const result = serialize(complexObject, replacer) + + expect(result).toMatchInlineSnapshot( + `"{"boolean":"primitive-true","fn":"primitive-fn() {\\n return \\"foo\\";\\n }","nan":"primitive-NaN","nil":"primitive-null","number":"primitive-123","string":"primitive-foo","undef":"primitive-undefined","array":["primitive-foo",{"bar":"primitive-baz"}],"buffer":{"type":"primitive-Buffer","data":["primitive-116","primitive-104","primitive-105","primitive-115","primitive-32","primitive-105","primitive-115","primitive-32","primitive-97","primitive-32","primitive-116","primitive-101","primitive-115","primitive-116","primitive-32","primitive-98","primitive-117","primitive-102","primitive-102","primitive-101","primitive-114"]},"error":{},"foo":{"value":"primitive-value"},"map":{"__type":"primitive-Map","value":[["primitive-foo",{"bar":"primitive-baz"}]]},"object":{"foo":{"bar":"primitive-baz"}},"promise":{},"regexp":{},"set":{},"weakmap":{},"weakset":{}}"`, + ) + }) + + it('should handle circular objects', () => { + const result = serialize(circularObject) + + expect(result).toEqual( + JSON.stringify( + circularObject, + (() => { + const cache: any[] = [] + + return (_key, value) => { + if (value && typeof value === 'object' && ~cache.indexOf(value)) { + return '[ref=.]' + } + + cache.push(value) + + return value + } + })(), + ), + ) + }) + + it('should handle circular objects with a custom circular replacer', () => { + const result = serialize( + circularObject, + null, + null, + (_key: string, _value: string, referenceKey: string) => referenceKey, + ) + const circularReplacer = (() => { + const cache: any[] = [] + + return (_key: string, value: any) => { + if (value && typeof value === 'object' && ~cache.indexOf(value)) { + return '.' + } + + cache.push(value) + + return value + } + })() + + expect(result).toEqual(JSON.stringify(circularObject, circularReplacer)) + }) + }) + + describe('key references', () => { + it('should point to the top level object when it is referenced', () => { + const object = { + foo: 'bar', + deeply: { + recursive: { + object: {}, + }, + }, + } + + object.deeply.recursive.object = object + + expect(serialize(object)).toEqual( + `{"foo":"bar","deeply":{"recursive":{"object":"[ref=.]"}}}`, + ) + }) + + it('should point to the nested object when it is referenced', () => { + const object = { + foo: 'bar', + deeply: { + recursive: { + object: {}, + }, + }, + } + + object.deeply.recursive.object = object.deeply.recursive + + expect(serialize(object)).toEqual( + `{"foo":"bar","deeply":{"recursive":{"object":"[ref=.deeply.recursive]"}}}`, + ) + }) + }) +}) diff --git a/packages/core/src/utils/serialize.ts b/packages/core/src/utils/serialize.ts new file mode 100644 index 0000000000..3f538c7b87 --- /dev/null +++ b/packages/core/src/utils/serialize.ts @@ -0,0 +1,116 @@ +/** + * Get the reference key for the circular value + * + * @param keys the keys to build the reference key from + * @param cutoff the maximum number of keys to include + * @returns the reference key + */ +function getReferenceKey(keys: string[], cutoff: number) { + return keys.slice(0, cutoff).join('.') || '.' +} + +/** + * Faster `Array.prototype.indexOf` implementation build for slicing / splicing + * + * @param array the array to match the value in + * @param value the value to match + * @returns the matching index, or -1 + */ +function getCutoff(array: any[], value: any) { + const { length } = array + + for (let index = 0; index < length; ++index) { + if (array[index] === value) { + return index + 1 + } + } + + return 0 +} + +type StandardReplacer = (key: string, value: any) => any +type CircularReplacer = (key: string, value: any, referenceKey: string) => any + +/** + * Create a replacer method that handles circular values + * + * @param [replacer] a custom replacer to use for non-circular values + * @param [circularReplacer] a custom replacer to use for circular methods + * @returns the value to stringify + */ +function createReplacer( + replacer?: StandardReplacer | null | undefined, + circularReplacer?: CircularReplacer | null | undefined, +): StandardReplacer { + const hasReplacer = typeof replacer === 'function' + const hasCircularReplacer = typeof circularReplacer === 'function' + + const cache: any[] = [] + const keys: string[] = [] + + return function replace(this: any, key: string, value: any) { + if (typeof value === 'object') { + if (cache.length) { + const thisCutoff = getCutoff(cache, this) + + if (thisCutoff === 0) { + cache[cache.length] = this + } else { + cache.splice(thisCutoff) + keys.splice(thisCutoff) + } + + keys[keys.length] = key + + const valueCutoff = getCutoff(cache, value) + + if (valueCutoff !== 0) { + return hasCircularReplacer + ? circularReplacer.call( + this, + key, + value, + getReferenceKey(keys, valueCutoff), + ) + : `[ref=${getReferenceKey(keys, valueCutoff)}]` + } + } else { + cache[0] = value + keys[0] = key + } + } + + return hasReplacer ? replacer.call(this, key, value) : value + } +} + +/** + * Stringifier that handles circular values + * + * Forked from https://github.com/planttheidea/fast-stringify + * + * @param value to stringify + * @param [replacer] a custom replacer function for handling standard values + * @param [indent] the number of spaces to indent the output by + * @param [circularReplacer] a custom replacer function for handling circular values + * @returns the stringified output + */ +export function serialize( + value: any, + replacer?: StandardReplacer | null | undefined, + indent?: number | null | undefined, + circularReplacer?: CircularReplacer | null | undefined, +) { + return JSON.stringify( + value, + createReplacer((key, value_) => { + let value = value_ + if (typeof value === 'bigint') + value = { __type: 'bigint', value: value_.toString() } + if (value instanceof Map) + value = { __type: 'Map', value: Array.from(value_.entries()) } + return replacer?.(key, value) ?? value + }, circularReplacer), + indent ?? undefined, + ) +} diff --git a/packages/core/src/utils/uid.ts b/packages/core/src/utils/uid.ts new file mode 100644 index 0000000000..4adc3e4e2b --- /dev/null +++ b/packages/core/src/utils/uid.ts @@ -0,0 +1,49 @@ +const size = 256 +let index = size +let buffer: string + +function getRandomBytes(byteLength: number): Uint8Array { + if ( + typeof globalThis !== 'undefined' && + globalThis.crypto && + typeof globalThis.crypto.getRandomValues === 'function' + ) { + const array = new Uint8Array(byteLength) + globalThis.crypto.getRandomValues(array) + return array + } + + // Fallback for Node.js environments that expose `require('crypto')`. + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const nodeCrypto = require('crypto') as { + randomBytes: (size: number) => { readonly [n: number]: number; length: number } + } + const buf = nodeCrypto.randomBytes(byteLength) + const array = new Uint8Array(byteLength) + for (let i = 0; i < byteLength; i++) array[i] = buf[i] + return array + } catch { + // ignore and fall through to non-cryptographic fallback + } + + // Last-resort, non-cryptographic fallback (used only if no crypto APIs are available). + const array = new Uint8Array(byteLength) + for (let i = 0; i < byteLength; i++) { + array[i] = (Math.random() * 256) | 0 + } + return array +} + +export function uid(length = 11) { + if (!buffer || index + length > size * 2) { + buffer = '' + index = 0 + const randomBytes = getRandomBytes(size) + for (let i = 0; i < size; i++) { + const byte = randomBytes[i] + buffer += byte.toString(16).padStart(2, '0') + } + } + return buffer.substring(index, index++ + length) +} diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index bb4c7f26f5..e291f4b23c 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1 +1 @@ -export const VERSION = '2.0.0' +export const version = '2.17.2' diff --git a/packages/core/test/setup.ts b/packages/core/test/setup.ts new file mode 100644 index 0000000000..f59a0488fe --- /dev/null +++ b/packages/core/test/setup.ts @@ -0,0 +1,5 @@ +import { vi } from 'vitest' + +vi.mock('../src/version.ts', () => { + return { version: 'x.y.z' } +}) diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json new file mode 100644 index 0000000000..fbed2b1036 --- /dev/null +++ b/packages/core/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "src/**/*.test-d.ts"], + "compilerOptions": { + "sourceMap": true + } +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000000..bacbc9228c --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.build.json", + "include": ["src/**/*.ts", "test/**/*.ts"], + "exclude": [] +} diff --git a/packages/create-wagmi/CHANGELOG.md b/packages/create-wagmi/CHANGELOG.md new file mode 100644 index 0000000000..7ab58fe7e1 --- /dev/null +++ b/packages/create-wagmi/CHANGELOG.md @@ -0,0 +1,278 @@ +# create-wagmi + +## 2.0.14 + +### Patch Changes + +- [#4450](https://github.com/wevm/wagmi/pull/4450) [`7b9a6bb35881b657a00bdd7ccd7edea32660f5bf`](https://github.com/wevm/wagmi/commit/7b9a6bb35881b657a00bdd7ccd7edea32660f5bf) Thanks [@tmm](https://github.com/tmm)! - Removed internal usage of `fs-extra`. + +## 2.0.13 + +### Patch Changes + +- [#4060](https://github.com/wevm/wagmi/pull/4060) [`95965c1f19d480b97f2b297a077a9e607dee32ad`](https://github.com/wevm/wagmi/commit/95965c1f19d480b97f2b297a077a9e607dee32ad) Thanks [@dalechyn](https://github.com/dalechyn)! - Bumped Tanstack Query dependencies to fix typing issues between exported Wagmi query options and TanStack Query suspense query methods (due to [`direction` property in `QueryFunctionContext` being deprecated](https://github.com/TanStack/query/pull/7410)). + +## 2.0.12 + +### Patch Changes + +- [`c00303d0c5909680b3124f92c0a2d2f31ea30405`](https://github.com/wevm/wagmi/commit/c00303d0c5909680b3124f92c0a2d2f31ea30405) Thanks [@tmm](https://github.com/tmm)! - Bumped Next.js version + +## 2.0.11 + +### Patch Changes + +- [#3871](https://github.com/wevm/wagmi/pull/3871) [`0e6bd286`](https://github.com/wevm/wagmi/commit/0e6bd286ca2572d2bfbe206db460528b7c2ebc63) Thanks [@jxom](https://github.com/jxom)! - Added `.npmrc` with `legacy-peer-deps = true` to templates. + +## 2.0.10 + +### Patch Changes + +- [`3f8203bd`](https://github.com/wevm/wagmi/commit/3f8203bd77fcf6b6756640b5971d09741ae3853d) Thanks [@tmm](https://github.com/tmm)! - Set Wagmi-related package versions to latest. + +## 2.0.9 + +### Patch Changes + +- [#3518](https://github.com/wevm/wagmi/pull/3518) [`338e857d`](https://github.com/wevm/wagmi/commit/338e857d8cb2fe85e13d9207bef14cada1c1962d) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +## 2.0.8 + +### Patch Changes + +- [#3510](https://github.com/wevm/wagmi/pull/3510) [`660ff80d`](https://github.com/wevm/wagmi/commit/660ff80d5b046967a446eba43ee54b8359a37d0d) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where connectors returning multiple addresses didn't checksum correctly. + +## 2.0.7 + +### Patch Changes + +- [#3496](https://github.com/wevm/wagmi/pull/3496) [`ba7f8a75`](https://github.com/wevm/wagmi/commit/ba7f8a758efb07664c6e401b5e7e325e7c62341b) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +## 2.0.6 + +### Patch Changes + +- [#3427](https://github.com/wevm/wagmi/pull/3427) [`370f1b4a`](https://github.com/wevm/wagmi/commit/370f1b4a3f154d181acf381c31c2e7862e22c0e4) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Bumped dependencies. + +## 2.0.5 + +### Patch Changes + +- [#3459](https://github.com/wevm/wagmi/pull/3459) [`d950b666`](https://github.com/wevm/wagmi/commit/d950b666b56700ca039ce16cdfdf34564991e7f5) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Bumped dependencies + +## 2.0.4 + +### Patch Changes + +- [#3443](https://github.com/wevm/wagmi/pull/3443) [`007024a6`](https://github.com/wevm/wagmi/commit/007024a684ddbecf924cdc06dd6a8854fc3d5eeb) Thanks [@jmrossy](https://github.com/jmrossy)! - Bumped dependencies. + +- [#3447](https://github.com/wevm/wagmi/pull/3447) [`a02a26ad`](https://github.com/wevm/wagmi/commit/a02a26ad030d3afb78f744377d61b5c60b65d97a) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +## 2.0.3 + +### Patch Changes + +- [`ec0d8b41`](https://github.com/wevm/wagmi/commit/ec0d8b4112181fefb11025e436a94a6114761d37) Thanks [@tmm](https://github.com/tmm)! - Added note to `metaMask` connector. + +## 2.0.2 + +### Patch Changes + +- [#3384](https://github.com/wevm/wagmi/pull/3384) [`ee868c33`](https://github.com/wevm/wagmi/commit/ee868c3385dae511230b6ddcb5627c1293cc1844) Thanks [@tmm](https://github.com/tmm)! - Fixed connectors not bubbling error when connecting with `chainId` and subsequent user rejection. + +## 2.0.1 + +### Major Changes + +- [#3333](https://github.com/wevm/wagmi/pull/3333) [`b3a0baaa`](https://github.com/wevm/wagmi/commit/b3a0baaaee7decf750d376aab2502cd33ca4825a) Thanks [@tmm](https://github.com/tmm)! - Added support for Wagmi 2.0. + +## 1.0.5 + +### Patch Changes + +- 6ba996f: Fixed optional WalletConnect Cloud Project ID prompt + +## 1.0.4 + +### Patch Changes + +- eb8dd9d: Updated wagmi & viem. Added ConnectKit template back in. + +## 1.0.3 + +### Patch Changes + +- 00d9080: Added RainbowKit & Web3Modal wagmi v1 templates + +## 1.0.2 + +### Patch Changes + +- 3c65f77: Fixed Next.js default template title + +## 1.0.0 + +### Major Changes + +- affc13e: Updated to wagmi v1. + +## 0.1.19 + +### Patch Changes + +- 1282f0e: Updated templates to use WalletConnect v2 by default + +## 0.1.18 + +### Patch Changes + +- 12dcfe0: Updated wagmi to 0.12 in ConnectKit templates. + +## 0.1.17 + +### Patch Changes + +- 6eba01a: Updated wagmi to 0.12. +- 3a0ab8c: Added .env to .gitignore in templates. + +## 0.1.16 + +### Patch Changes + +- 7ad50f1: Upgraded `@wagmi/cli` + +## 0.1.15 + +### Patch Changes + +- 6e30599: Added `test` config to `foundry.toml` on the Foundry templates. + +## 0.1.14 + +### Patch Changes + +- 19f3675: Bumped `wagmi` to `~0.11.0`. + +## 0.1.13 + +### Patch Changes + +- c1ab75c: Bump @wagmi/cli + +## 0.1.12 + +### Patch Changes + +- af6e551: Added templates for `@wagmi/cli`: + + - `@wagmi/cli (Mint NFT Example)` + - `@wagmi/cli + ERC20` + - `@wagmi/cli + Etherscan (Mint NFT Example)` + - `@wagmi/cli + Foundry (Counter.sol Example)` + +## 0.1.11 + +### Patch Changes + +- cc638dd: Fixed an issue where Vite projects used `process.env` instead of `import.meta.env`. +- 75d1c2d: Updated `wagmi` to `~0.10.0`. + +## 0.1.10 + +### Patch Changes + +- 9cd7140: Amend gitignore files for Vite templates + +## 0.1.9 + +### Patch Changes + +- 904a2e1: Fixed import env variables in Vite (React) templates + +## 0.1.8 + +### Patch Changes + +- 65cc841: Update RainbowKit & ConnectKit templates to `wagmi@~0.9.0` + +## 0.1.7 + +### Patch Changes + +- a59d9c5: Upgrade `default` & `web3modal` templates to `wagmi@0.9` + +## 0.1.6 + +### Patch Changes + +- b544457: Updated `connectkit` to `1.1.0` in the ConnectKit templates + +## 0.1.5 + +### Patch Changes + +- 6bd6c74: Updated repo link in package.json + +## 0.1.4 + +### Patch Changes + +- c39666b: Added ability to select providers +- 37708ed: **Added ability to select frameworks.** + + Each directory in `templates/` now mirrors a "framework", where its child directories mirror a "template" for that framework. + + Example: + + ``` + templates/ + next/ + connectkit/ + default/ + rainbowkit/ + web3modal + vite-react/ + connectkit/ + default/ + rainbowkit/ + web3modal/ + ``` + +- 399d2b9: Moved template configuration to the template level + added hooks + +## 0.1.3 + +### Patch Changes + +- 353332a: Added Web3Modal template +- dd95b14: **next-with-connectkit**: Update `connectkit` to `^1.0.0` + +## 0.1.2 + +### Patch Changes + +- 34f666b: Fixed issue where package manager install process would not log error + +## 0.1.1 + +### Patch Changes + +- 900cbdc: Updated `@rainbow-me/rainbowkit` dependency in Next + RainbowKit template + +## 0.1.0 + +### Minor Changes + +- 23993d2: ## 🎉 Initial release 🎉 + + Get up and running quickly with wagmi by using the `create-wagmi` CLI. This tool interactively scaffolds a new wagmi project for you so you can start building instantly without the hassle of setting up `git`, installing packages, worrying about TypeScript configuration, etc. + + To get started, `create-wagmi` can be instantiated with one of your favorite package managers: + + ```bash + npm init wagmi + # or + pnpm create wagmi + # or + yarn create wagmi + ``` diff --git a/packages/create-wagmi/README.md b/packages/create-wagmi/README.md new file mode 100644 index 0000000000..f923127ca2 --- /dev/null +++ b/packages/create-wagmi/README.md @@ -0,0 +1,17 @@ +# create-wagmi + +Get up and running quickly with [Wagmi](https://wagmi.sh) by using the `create-wagmi` CLI. + +## Installation + +```bash +npm create wagmi +# or +pnpm create wagmi +# or +yarn create wagmi +``` + +## Documentation + +For documentation and guides, visit [wagmi.sh](https://wagmi.sh/cli/create-wagmi). diff --git a/packages/create-wagmi/package.json b/packages/create-wagmi/package.json new file mode 100644 index 0000000000..7b6798c628 --- /dev/null +++ b/packages/create-wagmi/package.json @@ -0,0 +1,49 @@ +{ + "name": "create-wagmi", + "description": "Create Wagmi apps with one command", + "version": "2.0.14", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/wevm/wagmi.git", + "directory": "packages/create-wagmi" + }, + "scripts": { + "build": "pnpm run clean && pnpm run build:esm+types", + "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", + "check:types": "tsc --noEmit", + "clean": "rm -rf dist tsconfig.tsbuildinfo", + "dev": "bun src/cli.ts", + "test:build": "publint --strict" + }, + "files": [ + "dist/**", + "!dist/**/*.tsbuildinfo", + "src/**/*.ts", + "!src/**/*.test.ts", + "!src/**/*.test-d.ts", + "templates/**" + ], + "bin": { + "wagmi": "./dist/esm/cli.js" + }, + "sideEffects": false, + "type": "module", + "exports": { + "./package.json": "./package.json" + }, + "dependencies": { + "cac": "^6.7.14", + "cross-spawn": "^7.0.5", + "picocolors": "^1.0.0", + "prompts": "^2.4.2" + }, + "devDependencies": { + "@types/cross-spawn": "^6.0.6", + "@types/node": "^20.12.10", + "@types/prompts": "^2.4.9" + }, + "contributors": ["awkweb.eth ", "jxom.eth "], + "funding": "https://github.com/sponsors/wevm", + "keywords": ["wagmi", "eth", "ethereum", "dapps", "wallet", "web3", "cli"] +} diff --git a/packages/create-wagmi/src/cli.test.ts b/packages/create-wagmi/src/cli.test.ts new file mode 100644 index 0000000000..4d6915a6f3 --- /dev/null +++ b/packages/create-wagmi/src/cli.test.ts @@ -0,0 +1,151 @@ +import { + type ExecSyncOptionsWithStringEncoding, + execSync, +} from 'node:child_process' +import { mkdirSync, readdirSync, writeFileSync } from 'node:fs' +import { rm } from 'node:fs/promises' +import { join } from 'node:path' +import pc from 'picocolors' +import { afterEach, beforeAll, expect, test } from 'vitest' + +import { version } from './version.js' + +const cliPath = join(__dirname, '../dist/esm/cli.js') + +const projectName = 'test-app' +const genPath = join(__dirname, projectName) + +function run( + args: string[], + options: ExecSyncOptionsWithStringEncoding = { encoding: 'utf8' }, +): string { + return execSync(`node ${cliPath} ${args.join(' ')}`, options) +} + +function createNonEmptyDir() { + mkdirSync(genPath, { recursive: true }) + const file = join(genPath, 'foo') + writeFileSync(file, 'bar') +} + +beforeAll(async () => { + execSync('pnpm --filter create-wagmi build') + await rm(genPath, { recursive: true, force: true }) +}) +afterEach(async () => { + await rm(genPath, { recursive: true, force: true }) +}) + +test('prompts for the project name if none supplied', () => { + const stdout = run([]) + expect(stdout).toContain('Project name:') +}) + +test('prompts for the framework if none supplied when target dir is current directory', () => { + mkdirSync(genPath) + const stdout = run(['.'], { cwd: genPath, encoding: 'utf8' }) + expect(stdout).toContain('Select a framework:') +}) + +test('prompts for the framework if none supplied', () => { + const stdout = run([projectName]) + expect(stdout).toContain('Select a framework:') +}) + +test('prompts for the framework on not supplying a value for --template', () => { + const stdout = run([projectName, '--template']) + expect(stdout).toContain('Select a framework:') +}) + +test('prompts for the framework on supplying an invalid template', () => { + const stdout = run([projectName, '--template', 'unknown']) + expect(stdout).toContain( + `"unknown" isn't a valid template. Please choose from below:`, + ) +}) + +test('asks to overwrite non-empty target directory', () => { + createNonEmptyDir() + const stdout = run([projectName], { cwd: __dirname, encoding: 'utf8' }) + expect(stdout).toContain(`Target directory "${projectName}" is not empty.`) +}) + +test('asks to overwrite non-empty current directory', () => { + createNonEmptyDir() + const stdout = run(['.'], { cwd: genPath, encoding: 'utf8' }) + expect(stdout).toContain('Current directory is not empty.') +}) + +const templateFiles = readdirSync( + join(cliPath, '../../../templates/vite-react'), +) + .map((filePath) => { + if (filePath === '_gitignore') return '.gitignore' + if (filePath === '_env.local') return '.env.local' + if (filePath === '_npmrc') return '.npmrc' + return filePath + }) + .sort() + +test('successfully scaffolds a project based on vite-react starter template', () => { + mkdirSync(genPath, { recursive: true }) + const stdout = run([projectName, '--template', 'vite-react'], { + cwd: __dirname, + encoding: 'utf8', + }) + const generatedFiles = readdirSync(genPath).sort() + + expect(stdout).toContain(`Scaffolding project in ${genPath}`) + expect(templateFiles).toEqual(generatedFiles) +}) + +test('works with the -t alias', () => { + mkdirSync(genPath, { recursive: true }) + const stdout = run([projectName, '-t', 'vite-react'], { + cwd: __dirname, + encoding: 'utf8', + }) + const generatedFiles = readdirSync(genPath).sort() + + expect(stdout).toContain(`Scaffolding project in ${genPath}`) + expect(templateFiles).toEqual(generatedFiles) +}) + +test('uses different package manager', () => { + mkdirSync(genPath, { recursive: true }) + const stdout = run([projectName, '--bun', '-t', 'vite-react'], { + cwd: __dirname, + encoding: 'utf8', + }) + + expect(stdout).toContain('bun install') +}) + +test('shows help', () => { + const stdout = run(['--help']) + expect( + stdout + .replace(version, 'x.y.z') + .replace(pc.green(''), ''), + ).toMatchInlineSnapshot(` + "create-wagmi/x.y.z + + Usage: + $ create-wagmi [options] + + Options: + -t, --template [name] Template to bootstrap with. Available: vite-react, next, vite-vue, nuxt, vite-vanilla + --bun Use bun as your package manager + --npm Use npm as your package manager + --pnpm Use pnpm as your package manager + --yarn Use yarn as your package manager + -h, --help Display this message + -v, --version Display version number + " + `) +}) + +test('shows version', () => { + const stdout = run(['--version']) + expect(stdout).toContain(`create-wagmi/${version} `) +}) diff --git a/packages/create-wagmi/src/cli.ts b/packages/create-wagmi/src/cli.ts new file mode 100644 index 0000000000..ecb6a25a4c --- /dev/null +++ b/packages/create-wagmi/src/cli.ts @@ -0,0 +1,284 @@ +#!/usr/bin/env node +import * as fs from 'node:fs' +import * as path from 'node:path' +import { fileURLToPath } from 'node:url' +import { cac } from 'cac' +import spawn from 'cross-spawn' +import pc from 'picocolors' +import prompts from 'prompts' + +import { type Framework, frameworks } from './frameworks.js' +import { + copy, + emptyDir, + formatTargetDir, + isEmpty, + isValidPackageName, + pkgFromUserAgent, + toValidPackageName, +} from './utils.js' +import { version } from './version.js' + +const templates = frameworks + .map((f) => f.variants?.map((v) => v.name) || [f.name]) + .reduce((a, b) => a.concat(b), []) + +const cli = cac('create-wagmi') + +cli + .usage(`${pc.green('')} [options]`) + .option( + '-t, --template [name]', + `Template to bootstrap with. Available: ${templates + .sort((a, b) => (a && !b ? -1 : 1)) + .join(', ')}`, + ) + .option('--bun', 'Use bun as your package manager') + .option('--npm', 'Use npm as your package manager') + .option('--pnpm', 'Use pnpm as your package manager') + .option('--yarn', 'Use yarn as your package manager') + +cli.help() +cli.version(version) + +const cwd = process.cwd() + +const renameFiles: Record = { + '_env.local': '.env.local', + // https://github.com/npm/npm/issues/1862 + _gitignore: '.gitignore', + _npmrc: '.npmrc', +} + +const defaultTargetDir = 'wagmi-project' + +async function init() { + const { args, options } = cli.parse(process.argv) + if (options.help) return + if (options.version) return + + const argTargetDir = formatTargetDir(args[0]) + const argTemplate = options.template || options.t + + let targetDir = argTargetDir || defaultTargetDir + function getProjectName() { + return targetDir === '.' ? path.basename(path.resolve()) : targetDir + } + + let result: prompts.Answers< + 'framework' | 'overwrite' | 'packageName' | 'projectName' | 'variant' + > + try { + result = await prompts( + [ + { + type: argTargetDir ? null : 'text', + name: 'projectName', + message: pc.reset('Project name:'), + initial: defaultTargetDir, + onState(state) { + targetDir = formatTargetDir(state.value) || defaultTargetDir + }, + }, + { + type() { + return !fs.existsSync(targetDir) || isEmpty(targetDir) + ? null + : 'confirm' + }, + name: 'overwrite', + message() { + return `${ + targetDir === '.' + ? 'Current directory' + : `Target directory "${targetDir}"` + } is not empty. Remove existing files and continue?` + }, + }, + { + type(_, { overwrite }: { overwrite?: boolean }) { + if (overwrite === false) + throw new Error(`${pc.red('✖')} Operation cancelled`) + return null + }, + name: 'overwriteChecker', + }, + { + type() { + return isValidPackageName(getProjectName()) ? null : 'text' + }, + name: 'packageName', + message: pc.reset('Package name:'), + initial() { + return toValidPackageName(getProjectName()) + }, + validate(dir) { + return isValidPackageName(dir) || 'Invalid package.json name' + }, + }, + { + type: + argTemplate && templates.includes(argTemplate) ? null : 'select', + name: 'framework', + message: + typeof argTemplate === 'string' && !templates.includes(argTemplate) + ? pc.reset( + `"${argTemplate}" isn't a valid template. Please choose from below: `, + ) + : pc.reset('Select a framework:'), + initial: 0, + choices: frameworks.map((framework) => { + const frameworkColor = framework.color + return { + title: frameworkColor(framework.display || framework.name), + value: framework, + } + }), + }, + { + type(framework: Framework) { + return framework?.variants?.length > 1 ? 'select' : null + }, + name: 'variant', + message: pc.reset('Select a variant:'), + choices(framework: Framework) { + return framework.variants.map((variant) => { + const variantColor = variant.color + return { + title: variantColor(variant.display || variant.name), + value: variant.name, + } + }) + }, + }, + ], + { + onCancel() { + throw new Error(`${pc.red('✖')} Operation cancelled`) + }, + }, + ) + } catch (error) { + // biome-ignore lint/suspicious/noConsoleLog: + console.log((error as Error).message) + return + } + + // user choice associated with prompts + const { + framework, + overwrite, + packageName, + variant = framework?.variants[0]?.name, + } = result + + const root = path.join(cwd, targetDir) + + if (overwrite) emptyDir(root) + else if (!fs.existsSync(root)) fs.mkdirSync(root, { recursive: true }) + + // determine template + const template: string = variant || framework?.name || argTemplate + + const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent) + type PkgManager = 'bun' | 'npm' | 'pnpm' | 'yarn' + let pkgManager: PkgManager + if (options.bun) pkgManager = 'bun' + else if (options.npm) pkgManager = 'npm' + else if (options.pnpm) pkgManager = 'pnpm' + else if (options.yarn) pkgManager = 'yarn' + else pkgManager = pkgInfo ? (pkgInfo.name as PkgManager) : 'npm' + const isYarn1 = pkgManager === 'yarn' && pkgInfo?.version?.startsWith('1.') + + const { customCommand } = + frameworks.flatMap((f) => f.variants).find((v) => v.name === template) ?? {} + + if (customCommand) { + const fullCustomCommand = customCommand + .replace(/^npm create /, () => { + // `bun create` uses it's own set of templates, + // the closest alternative is using `bun x` directly on the package + if (pkgManager === 'bun') return 'bun x create-' + return `${pkgManager} create ` + }) + // Only Yarn 1.x doesn't support `@version` in the `create` command + .replace('@latest', () => (isYarn1 ? '' : '@latest')) + .replace(/^npm exec/, () => { + // Prefer `pnpm dlx`, `yarn dlx`, or `bun x` + if (pkgManager === 'pnpm') return 'pnpm dlx' + if (pkgManager === 'yarn' && !isYarn1) return 'yarn dlx' + if (pkgManager === 'bun') return 'bun x' + // Use `npm exec` in all other cases, + // including Yarn 1.x and other custom npm clients. + return 'npm exec' + }) + + const [command, ...args] = fullCustomCommand.split(' ') + // we replace TARGET_DIR here because targetDir may include a space + const replacedArgs = args.map((arg) => arg.replace('TARGET_DIR', targetDir)) + const { status } = spawn.sync(command!, replacedArgs, { + stdio: 'inherit', + }) + process.exit(status ?? 0) + } + + // biome-ignore lint/suspicious/noConsoleLog: + console.log(`\nScaffolding project in ${root}...`) + + const templateDir = path.resolve( + fileURLToPath(import.meta.url), + '../../../templates', + template, + ) + + function write(file: string, content?: string) { + const targetPath = path.join(root, renameFiles[file] ?? file) + if (content) fs.writeFileSync(targetPath, content) + else copy(path.join(templateDir, file), targetPath) + } + + const files = fs.readdirSync(templateDir) + for (const file of files.filter((f) => f !== 'package.json')) { + write(file) + } + + const pkg = JSON.parse( + fs.readFileSync(path.join(templateDir, 'package.json'), 'utf-8'), + ) + + pkg.name = packageName || getProjectName() + + write('package.json', `${JSON.stringify(pkg, null, 2)}\n`) + + const cdProjectName = path.relative(cwd, root) + // biome-ignore lint/suspicious/noConsoleLog: + console.log('\nDone. Now run:\n') + if (root !== cwd) + // biome-ignore lint/suspicious/noConsoleLog: + console.log( + ` cd ${ + cdProjectName.includes(' ') ? `"${cdProjectName}"` : cdProjectName + }`, + ) + + switch (pkgManager) { + case 'yarn': + // biome-ignore lint/suspicious/noConsoleLog: + console.log(' yarn') + // biome-ignore lint/suspicious/noConsoleLog: + console.log(' yarn dev') + break + default: + // biome-ignore lint/suspicious/noConsoleLog: + console.log(` ${pkgManager} install`) + // biome-ignore lint/suspicious/noConsoleLog: + console.log(` ${pkgManager} run dev`) + break + } + // biome-ignore lint/suspicious/noConsoleLog: + console.log() +} + +init().catch((e) => { + console.error(e) +}) diff --git a/packages/create-wagmi/src/frameworks.ts b/packages/create-wagmi/src/frameworks.ts new file mode 100644 index 0000000000..1b414bafc8 --- /dev/null +++ b/packages/create-wagmi/src/frameworks.ts @@ -0,0 +1,66 @@ +import pc from 'picocolors' + +type ColorFunc = (str: string | number) => string + +type FrameworkVariant = { + name: string + display: string + color: ColorFunc + customCommand?: string +} + +export type Framework = { + name: string + display: string + color: ColorFunc + variants: readonly FrameworkVariant[] +} + +export const frameworks: readonly Framework[] = [ + { + name: 'react', + display: 'React', + color: pc.cyan, + variants: [ + { + name: 'vite-react', + display: 'Vite', + color: pc.blue, + }, + { + name: 'next', + display: 'Next', + color: pc.yellow, + }, + ], + }, + { + name: 'vue', + display: 'Vue', + color: pc.green, + variants: [ + { + name: 'vite-vue', + display: 'Vite', + color: pc.blue, + }, + { + name: 'nuxt', + display: 'Nuxt', + color: pc.yellow, + }, + ], + }, + { + name: 'vanilla', + display: 'Vanilla', + color: pc.magenta, + variants: [ + { + name: 'vite-vanilla', + display: 'Vite', + color: pc.blue, + }, + ], + }, +] diff --git a/packages/create-wagmi/src/index.test-d.ts b/packages/create-wagmi/src/index.test-d.ts new file mode 100644 index 0000000000..b056d56358 --- /dev/null +++ b/packages/create-wagmi/src/index.test-d.ts @@ -0,0 +1,4 @@ +import { expectTypeOf } from 'vitest' + +// noop test because vitest typecheck fails unless each workspace project has type test +expectTypeOf(1).toEqualTypeOf() diff --git a/packages/create-wagmi/src/utils.ts b/packages/create-wagmi/src/utils.ts new file mode 100644 index 0000000000..d04f60ed93 --- /dev/null +++ b/packages/create-wagmi/src/utils.ts @@ -0,0 +1,79 @@ +import * as fs from 'node:fs' +import * as path from 'node:path' + +export function formatTargetDir(targetDir: string | undefined) { + return targetDir?.trim().replace(/\/+$/g, '') +} + +export function copy(src: string, dest: string) { + const stat = fs.statSync(src) + if (stat.isDirectory()) copyDir(src, dest) + else fs.copyFileSync(src, dest) +} + +function copyDir(srcDir: string, destDir: string) { + fs.mkdirSync(destDir, { recursive: true }) + for (const file of fs.readdirSync(srcDir)) { + const srcFile = path.resolve(srcDir, file) + const destFile = path.resolve(destDir, file) + copy(srcFile, destFile) + } +} + +export function isValidPackageName(projectName: string) { + return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test( + projectName, + ) +} + +export function toValidPackageName(projectName: string) { + return projectName + .trim() + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/^[._]/, '') + .replace(/[^a-z\d\-~]+/g, '-') +} + +export function isEmpty(path: string) { + const files = fs.readdirSync(path) + return files.length === 0 || (files.length === 1 && files[0] === '.git') +} + +export function emptyDir(dir: string) { + if (!fs.existsSync(dir)) return + for (const file of fs.readdirSync(dir)) { + if (file === '.git') continue + fs.rmSync(path.resolve(dir, file), { recursive: true, force: true }) + } +} + +export function pkgFromUserAgent(userAgent: string | undefined) { + if (!userAgent) return undefined + const pkgSpec = userAgent.split(' ')[0]! + const pkgSpecArr = pkgSpec.split('/') + return { + name: pkgSpecArr[0], + version: pkgSpecArr[1], + } +} + +// function setupReactSwc(root: string, isTs: boolean) { +// editFile(path.resolve(root, 'package.json'), (content) => { +// return content.replace( +// /"@vitejs\/plugin-react": ".+?"/, +// `"@vitejs/plugin-react-swc": "^3.3.2"`, +// ) +// }) +// editFile( +// path.resolve(root, `vite.config.${isTs ? 'ts' : 'js'}`), +// (content) => { +// return content.replace('@vitejs/plugin-react', '@vitejs/plugin-react-swc') +// }, +// ) +// } + +// function editFile(file: string, callback: (content: string) => string) { +// const content = fs.readFileSync(file, 'utf-8') +// fs.writeFileSync(file, callback(content), 'utf-8') +// } diff --git a/packages/create-wagmi/src/version.ts b/packages/create-wagmi/src/version.ts new file mode 100644 index 0000000000..5556800c0d --- /dev/null +++ b/packages/create-wagmi/src/version.ts @@ -0,0 +1 @@ +export const version = '2.0.14' diff --git a/packages/create-wagmi/templates/next/README.md b/packages/create-wagmi/templates/next/README.md new file mode 100644 index 0000000000..bd3aa7b7f0 --- /dev/null +++ b/packages/create-wagmi/templates/next/README.md @@ -0,0 +1 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-wagmi`](https://github.com/wevm/wagmi/tree/main/packages/create-wagmi). diff --git a/packages/create-wagmi/templates/next/_env.local b/packages/create-wagmi/templates/next/_env.local new file mode 100644 index 0000000000..9a11dba16a --- /dev/null +++ b/packages/create-wagmi/templates/next/_env.local @@ -0,0 +1,2 @@ +NEXT_PUBLIC_WC_PROJECT_ID= +NEXT_TELEMETRY_DISABLED=1 \ No newline at end of file diff --git a/packages/create-wagmi/templates/next/_gitignore b/packages/create-wagmi/templates/next/_gitignore new file mode 100644 index 0000000000..8f322f0d8f --- /dev/null +++ b/packages/create-wagmi/templates/next/_gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/create-wagmi/templates/next/_npmrc b/packages/create-wagmi/templates/next/_npmrc new file mode 100644 index 0000000000..ca1e9d98b5 --- /dev/null +++ b/packages/create-wagmi/templates/next/_npmrc @@ -0,0 +1 @@ +legacy-peer-deps = true \ No newline at end of file diff --git a/packages/create-wagmi/templates/next/next-env.d.ts b/packages/create-wagmi/templates/next/next-env.d.ts new file mode 100644 index 0000000000..4f11a03dc6 --- /dev/null +++ b/packages/create-wagmi/templates/next/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/create-wagmi/templates/next/next.config.js b/packages/create-wagmi/templates/next/next.config.js new file mode 100644 index 0000000000..767719fc4f --- /dev/null +++ b/packages/create-wagmi/templates/next/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/packages/create-wagmi/templates/next/package.json b/packages/create-wagmi/templates/next/package.json new file mode 100644 index 0000000000..55ac1dac6d --- /dev/null +++ b/packages/create-wagmi/templates/next/package.json @@ -0,0 +1,32 @@ +{ + "name": "wagmi-next-starter", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@tanstack/react-query": "5.45.1", + "next": "15.4.10", + "react": "^19.1.0", + "react-dom": "^18.3.1", + "viem": "latest", + "wagmi": "latest" + }, + "devDependencies": { + "@types/node": "^20.12.10", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@wagmi/cli": "latest", + "bufferutil": "^4.0.8", + "encoding": "^0.1.13", + "lokijs": "^1.5.12", + "pino-pretty": "^10.3.1", + "supports-color": "^9.4.0", + "typescript": "^5.4.5", + "utf-8-validate": "^5.0.2" + } +} diff --git a/packages/create-wagmi/templates/next/src/app/globals.css b/packages/create-wagmi/templates/next/src/app/globals.css new file mode 100644 index 0000000000..0733a7ee6b --- /dev/null +++ b/packages/create-wagmi/templates/next/src/app/globals.css @@ -0,0 +1,21 @@ +:root { + background-color: #181818; + color: rgba(255, 255, 255, 0.87); + color-scheme: light dark; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-synthesis: none; + font-weight: 400; + line-height: 1.5; + text-rendering: optimizeLegibility; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +@media (prefers-color-scheme: light) { + :root { + background-color: #f8f8f8; + color: #181818; + } +} diff --git a/packages/create-wagmi/templates/next/src/app/layout.tsx b/packages/create-wagmi/templates/next/src/app/layout.tsx new file mode 100644 index 0000000000..da5b293bf0 --- /dev/null +++ b/packages/create-wagmi/templates/next/src/app/layout.tsx @@ -0,0 +1,30 @@ +import './globals.css' +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import { headers } from 'next/headers' +import { type ReactNode } from 'react' +import { cookieToInitialState } from 'wagmi' + +import { getConfig } from '../wagmi' +import { Providers } from './providers' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'Create Wagmi', + description: 'Generated by create-wagmi', +} + +export default async function RootLayout(props: { children: ReactNode }) { + const initialState = cookieToInitialState( + getConfig(), + (await headers()).get('cookie'), + ) + return ( + + + {props.children} + + + ) +} diff --git a/packages/create-wagmi/templates/next/src/app/page.tsx b/packages/create-wagmi/templates/next/src/app/page.tsx new file mode 100644 index 0000000000..f5dcbdf812 --- /dev/null +++ b/packages/create-wagmi/templates/next/src/app/page.tsx @@ -0,0 +1,48 @@ +'use client' + +import { useAccount, useConnect, useDisconnect } from 'wagmi' + +function App() { + const account = useAccount() + const { connectors, connect, status, error } = useConnect() + const { disconnect } = useDisconnect() + + return ( + <> +
+

Account

+ +
+ status: {account.status} +
+ addresses: {JSON.stringify(account.addresses)} +
+ chainId: {account.chainId} +
+ + {account.status === 'connected' && ( + + )} +
+ +
+

Connect

+ {connectors.map((connector) => ( + + ))} +
{status}
+
{error?.message}
+
+ + ) +} + +export default App diff --git a/packages/create-wagmi/templates/next/src/app/providers.tsx b/packages/create-wagmi/templates/next/src/app/providers.tsx new file mode 100644 index 0000000000..b4086cf531 --- /dev/null +++ b/packages/create-wagmi/templates/next/src/app/providers.tsx @@ -0,0 +1,23 @@ +'use client' + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { type ReactNode, useState } from 'react' +import { type State, WagmiProvider } from 'wagmi' + +import { getConfig } from '@/wagmi' + +export function Providers(props: { + children: ReactNode + initialState?: State +}) { + const [config] = useState(() => getConfig()) + const [queryClient] = useState(() => new QueryClient()) + + return ( + + + {props.children} + + + ) +} diff --git a/packages/create-wagmi/templates/next/src/wagmi.ts b/packages/create-wagmi/templates/next/src/wagmi.ts new file mode 100644 index 0000000000..b34f4c0079 --- /dev/null +++ b/packages/create-wagmi/templates/next/src/wagmi.ts @@ -0,0 +1,28 @@ +import { http, cookieStorage, createConfig, createStorage } from 'wagmi' +import { mainnet, sepolia } from 'wagmi/chains' +import { coinbaseWallet, injected, walletConnect } from 'wagmi/connectors' + +export function getConfig() { + return createConfig({ + chains: [mainnet, sepolia], + connectors: [ + injected(), + coinbaseWallet(), + walletConnect({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID }), + ], + storage: createStorage({ + storage: cookieStorage, + }), + ssr: true, + transports: { + [mainnet.id]: http(), + [sepolia.id]: http(), + }, + }) +} + +declare module 'wagmi' { + interface Register { + config: ReturnType + } +} diff --git a/packages/create-wagmi/templates/next/tsconfig.json b/packages/create-wagmi/templates/next/tsconfig.json new file mode 100644 index 0000000000..e59724b283 --- /dev/null +++ b/packages/create-wagmi/templates/next/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/create-wagmi/templates/nuxt/_env.local b/packages/create-wagmi/templates/nuxt/_env.local new file mode 100644 index 0000000000..437e9e3e7b --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/_env.local @@ -0,0 +1,3 @@ +NUXT_PUBLIC_WC_PROJECT_ID= +NUXT_TELEMETRY_DISABLED=1 + diff --git a/packages/create-wagmi/templates/nuxt/_gitignore b/packages/create-wagmi/templates/nuxt/_gitignore new file mode 100644 index 0000000000..4a7f73a2ed --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/_gitignore @@ -0,0 +1,24 @@ +# Nuxt dev/build outputs +.output +.data +.nuxt +.nitro +.cache +dist + +# Node dependencies +node_modules + +# Logs +logs +*.log + +# Misc +.DS_Store +.fleet +.idea + +# Local env files +.env +.env.* +!.env.example diff --git a/packages/create-wagmi/templates/nuxt/_npmrc b/packages/create-wagmi/templates/nuxt/_npmrc new file mode 100644 index 0000000000..057cc841f2 --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/_npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps = true + diff --git a/packages/create-wagmi/templates/nuxt/app.vue b/packages/create-wagmi/templates/nuxt/app.vue new file mode 100644 index 0000000000..98b46bf528 --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/app.vue @@ -0,0 +1,28 @@ + + + diff --git a/packages/create-wagmi/templates/nuxt/components/Account.vue b/packages/create-wagmi/templates/nuxt/components/Account.vue new file mode 100644 index 0000000000..33f1491dac --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/components/Account.vue @@ -0,0 +1,22 @@ + + + diff --git a/packages/create-wagmi/templates/nuxt/components/Connect.vue b/packages/create-wagmi/templates/nuxt/components/Connect.vue new file mode 100644 index 0000000000..93320448c0 --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/components/Connect.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/create-wagmi/templates/nuxt/nuxt.config.ts b/packages/create-wagmi/templates/nuxt/nuxt.config.ts new file mode 100644 index 0000000000..adfe7fd2d8 --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/nuxt.config.ts @@ -0,0 +1,7 @@ +import { defineNuxtConfig } from 'nuxt/config' + +// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + devtools: { enabled: true }, + modules: ['@wagmi/vue/nuxt'], +}) diff --git a/packages/create-wagmi/templates/nuxt/package.json b/packages/create-wagmi/templates/nuxt/package.json new file mode 100644 index 0000000000..65ff657528 --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/package.json @@ -0,0 +1,21 @@ +{ + "name": "nuxt-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "nuxt build", + "dev": "nuxt dev", + "generate": "nuxt generate", + "preview": "nuxt preview", + "postinstall": "nuxt prepare" + }, + "dependencies": { + "@tanstack/vue-query": ">=5.45.0", + "@wagmi/vue": "latest", + "nuxt": "^3.11.2", + "viem": "latest", + "vue": ">=3.4.21", + "vue-router": "^4.3.2" + } +} diff --git a/packages/create-wagmi/templates/nuxt/plugins/wagmi.ts b/packages/create-wagmi/templates/nuxt/plugins/wagmi.ts new file mode 100644 index 0000000000..b6abe5bcd2 --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/plugins/wagmi.ts @@ -0,0 +1,10 @@ +import { VueQueryPlugin } from '@tanstack/vue-query' +import { WagmiPlugin } from '@wagmi/vue' +import { defineNuxtPlugin } from 'nuxt/app' + +import { config } from '../wagmi' + +// TODO: Move to @wagmi/vue/nuxt nitro plugin +export default defineNuxtPlugin((nuxtApp) => { + nuxtApp.vueApp.use(WagmiPlugin, { config }).use(VueQueryPlugin, {}) +}) diff --git a/packages/create-wagmi/templates/nuxt/server/tsconfig.json b/packages/create-wagmi/templates/nuxt/server/tsconfig.json new file mode 100644 index 0000000000..b9ed69c19e --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/server/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../.nuxt/tsconfig.server.json" +} diff --git a/packages/create-wagmi/templates/nuxt/tsconfig.json b/packages/create-wagmi/templates/nuxt/tsconfig.json new file mode 100644 index 0000000000..a746f2a70c --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/tsconfig.json @@ -0,0 +1,4 @@ +{ + // https://nuxt.com/docs/guide/concepts/typescript + "extends": "./.nuxt/tsconfig.json" +} diff --git a/packages/create-wagmi/templates/nuxt/wagmi.ts b/packages/create-wagmi/templates/nuxt/wagmi.ts new file mode 100644 index 0000000000..83e8569ea8 --- /dev/null +++ b/packages/create-wagmi/templates/nuxt/wagmi.ts @@ -0,0 +1,29 @@ +import { http, cookieStorage, createConfig, createStorage } from '@wagmi/vue' +import { mainnet, optimism, sepolia } from '@wagmi/vue/chains' +import { injected, metaMask, walletConnect } from '@wagmi/vue/connectors' + +export const config = createConfig({ + chains: [mainnet, sepolia, optimism], + connectors: [ + injected(), + walletConnect({ + projectId: process.env.NUXT_PUBLIC_WC_PROJECT_ID!, + }), + metaMask(), + ], + storage: createStorage({ + storage: cookieStorage, + }), + ssr: true, + transports: { + [mainnet.id]: http(), + [sepolia.id]: http(), + [optimism.id]: http(), + }, +}) + +declare module '@wagmi/vue' { + interface Register { + config: typeof config + } +} diff --git a/packages/create-wagmi/templates/vite-react/README.md b/packages/create-wagmi/templates/vite-react/README.md new file mode 100644 index 0000000000..15f6f79541 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/README.md @@ -0,0 +1 @@ +This is a [Vite](https://vitejs.dev) project bootstrapped with [`create-wagmi`](https://github.com/wevm/wagmi/tree/main/packages/create-wagmi). diff --git a/packages/create-wagmi/templates/vite-react/_env.local b/packages/create-wagmi/templates/vite-react/_env.local new file mode 100644 index 0000000000..936f763762 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/_env.local @@ -0,0 +1 @@ +VITE_WC_PROJECT_ID= \ No newline at end of file diff --git a/packages/create-wagmi/templates/vite-react/_gitignore b/packages/create-wagmi/templates/vite-react/_gitignore new file mode 100644 index 0000000000..a547bf36d8 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/_gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/create-wagmi/templates/vite-react/_npmrc b/packages/create-wagmi/templates/vite-react/_npmrc new file mode 100644 index 0000000000..ca1e9d98b5 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/_npmrc @@ -0,0 +1 @@ +legacy-peer-deps = true \ No newline at end of file diff --git a/packages/create-wagmi/templates/vite-react/biome.json b/packages/create-wagmi/templates/vite-react/biome.json new file mode 100644 index 0000000000..08eb8a26ab --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/biome.json @@ -0,0 +1,13 @@ +{ + "formatter": { + "enabled": true, + "indentStyle": "space", + "lineWidth": 120 + }, + "linter": { + "enabled": true + }, + "organizeImports": { + "enabled": true + } +} diff --git a/packages/create-wagmi/templates/vite-react/index.html b/packages/create-wagmi/templates/vite-react/index.html new file mode 100644 index 0000000000..f519ce85a7 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/index.html @@ -0,0 +1,12 @@ + + + + + + Create Wagmi + + +
+ + + diff --git a/packages/create-wagmi/templates/vite-react/package.json b/packages/create-wagmi/templates/vite-react/package.json new file mode 100644 index 0000000000..0eeaab9a10 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/package.json @@ -0,0 +1,29 @@ +{ + "name": "wagmi-vite-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "biome check .", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/react-query": "5.45.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "viem": "latest", + "wagmi": "latest" + }, + "devDependencies": { + "@biomejs/biome": "^1.8.0", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.2.1", + "@wagmi/cli": "latest", + "buffer": "^6.0.3", + "typescript": "^5.8.3", + "vite": "^5.2.11" + } +} diff --git a/packages/create-wagmi/templates/vite-react/src/App.tsx b/packages/create-wagmi/templates/vite-react/src/App.tsx new file mode 100644 index 0000000000..faa8ce1c79 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/src/App.tsx @@ -0,0 +1,46 @@ +import { useAccount, useConnect, useDisconnect } from 'wagmi' + +function App() { + const account = useAccount() + const { connectors, connect, status, error } = useConnect() + const { disconnect } = useDisconnect() + + return ( + <> +
+

Account

+ +
+ status: {account.status} +
+ addresses: {JSON.stringify(account.addresses)} +
+ chainId: {account.chainId} +
+ + {account.status === 'connected' && ( + + )} +
+ +
+

Connect

+ {connectors.map((connector) => ( + + ))} +
{status}
+
{error?.message}
+
+ + ) +} + +export default App diff --git a/packages/create-wagmi/templates/vite-react/src/index.css b/packages/create-wagmi/templates/vite-react/src/index.css new file mode 100644 index 0000000000..0733a7ee6b --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/src/index.css @@ -0,0 +1,21 @@ +:root { + background-color: #181818; + color: rgba(255, 255, 255, 0.87); + color-scheme: light dark; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-synthesis: none; + font-weight: 400; + line-height: 1.5; + text-rendering: optimizeLegibility; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +@media (prefers-color-scheme: light) { + :root { + background-color: #f8f8f8; + color: #181818; + } +} diff --git a/packages/create-wagmi/templates/vite-react/src/main.tsx b/packages/create-wagmi/templates/vite-react/src/main.tsx new file mode 100644 index 0000000000..d999e1a932 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/src/main.tsx @@ -0,0 +1,24 @@ +import { Buffer } from 'buffer' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import React from 'react' +import ReactDOM from 'react-dom/client' +import { WagmiProvider } from 'wagmi' + +import App from './App.tsx' +import { config } from './wagmi.ts' + +import './index.css' + +globalThis.Buffer = Buffer + +const queryClient = new QueryClient() + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + + + , +) diff --git a/packages/create-wagmi/templates/vite-react/src/vite-env.d.ts b/packages/create-wagmi/templates/vite-react/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/create-wagmi/templates/vite-react/src/wagmi.ts b/packages/create-wagmi/templates/vite-react/src/wagmi.ts new file mode 100644 index 0000000000..43cf231934 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/src/wagmi.ts @@ -0,0 +1,22 @@ +import { http, createConfig } from 'wagmi' +import { mainnet, sepolia } from 'wagmi/chains' +import { coinbaseWallet, injected, walletConnect } from 'wagmi/connectors' + +export const config = createConfig({ + chains: [mainnet, sepolia], + connectors: [ + injected(), + coinbaseWallet(), + walletConnect({ projectId: import.meta.env.VITE_WC_PROJECT_ID }), + ], + transports: { + [mainnet.id]: http(), + [sepolia.id]: http(), + }, +}) + +declare module 'wagmi' { + interface Register { + config: typeof config + } +} diff --git a/packages/create-wagmi/templates/vite-react/tsconfig.json b/packages/create-wagmi/templates/vite-react/tsconfig.json new file mode 100644 index 0000000000..a7fc6fbf23 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/packages/create-wagmi/templates/vite-react/tsconfig.node.json b/packages/create-wagmi/templates/vite-react/tsconfig.node.json new file mode 100644 index 0000000000..42872c59f5 --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/create-wagmi/templates/vite-react/vite.config.ts b/packages/create-wagmi/templates/vite-react/vite.config.ts new file mode 100644 index 0000000000..36f7f4e1bc --- /dev/null +++ b/packages/create-wagmi/templates/vite-react/vite.config.ts @@ -0,0 +1,7 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/packages/create-wagmi/templates/vite-vanilla/_env.local b/packages/create-wagmi/templates/vite-vanilla/_env.local new file mode 100644 index 0000000000..936f763762 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/_env.local @@ -0,0 +1 @@ +VITE_WC_PROJECT_ID= \ No newline at end of file diff --git a/packages/create-wagmi/templates/vite-vanilla/_gitignore b/packages/create-wagmi/templates/vite-vanilla/_gitignore new file mode 100644 index 0000000000..a547bf36d8 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/_gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/create-wagmi/templates/vite-vanilla/_npmrc b/packages/create-wagmi/templates/vite-vanilla/_npmrc new file mode 100644 index 0000000000..ca1e9d98b5 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/_npmrc @@ -0,0 +1 @@ +legacy-peer-deps = true \ No newline at end of file diff --git a/packages/create-wagmi/templates/vite-vanilla/index.html b/packages/create-wagmi/templates/vite-vanilla/index.html new file mode 100644 index 0000000000..e2028cc129 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/index.html @@ -0,0 +1,12 @@ + + + + + + Create Wagmi + + +
+ + + diff --git a/packages/create-wagmi/templates/vite-vanilla/package.json b/packages/create-wagmi/templates/vite-vanilla/package.json new file mode 100644 index 0000000000..e48954313c --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/package.json @@ -0,0 +1,24 @@ +{ + "name": "wagmi-core-vanilla-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@wagmi/connectors": "latest", + "@wagmi/core": "latest", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "viem": "latest" + }, + "devDependencies": { + "@wagmi/cli": "latest", + "buffer": "^6.0.3", + "typescript": "^5.8.3", + "vite": "^5.2.11" + } +} diff --git a/packages/create-wagmi/templates/vite-vanilla/src/main.ts b/packages/create-wagmi/templates/vite-vanilla/src/main.ts new file mode 100644 index 0000000000..f2b80f3459 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/src/main.ts @@ -0,0 +1,89 @@ +import { Buffer } from 'buffer' +import { connect, disconnect, reconnect, watchAccount } from '@wagmi/core' + +import './style.css' +import { config } from './wagmi' + +globalThis.Buffer = Buffer + +document.querySelector('#app')!.innerHTML = ` +
+
+

Account

+ +
+ status: +
+ addresses: +
+ chainId: +
+
+ +
+

Connect

+ ${config.connectors + .map( + (connector) => + ``, + ) + .join('')} +
+
+` + +setupApp(document.querySelector('#app')!) + +function setupApp(element: HTMLDivElement) { + const connectElement = element.querySelector('#connect') + const buttons = element.querySelectorAll('.connect') + for (const button of buttons) { + const connector = config.connectors.find( + (connector) => connector.uid === button.id, + )! + button.addEventListener('click', async () => { + try { + const errorElement = element.querySelector('#error') + if (errorElement) errorElement.remove() + await connect(config, { connector }) + } catch (error) { + const errorElement = document.createElement('div') + errorElement.id = 'error' + errorElement.innerText = (error as Error).message + connectElement?.appendChild(errorElement) + } + }) + } + + watchAccount(config, { + onChange(account) { + const accountElement = element.querySelector('#account')! + accountElement.innerHTML = ` +

Account

+
+ status: ${account.status} +
+ addresses: ${ + account.addresses ? JSON.stringify(account.addresses) : '' + } +
+ chainId: ${account.chainId ?? ''} +
+ ${ + account.status === 'connected' + ? `` + : '' + } + ` + + const disconnectButton = + element.querySelector('#disconnect') + if (disconnectButton) + disconnectButton.addEventListener('click', () => disconnect(config)) + }, + }) + + reconnect(config) + .then(() => {}) + .catch(() => {}) +} diff --git a/packages/create-wagmi/templates/vite-vanilla/src/style.css b/packages/create-wagmi/templates/vite-vanilla/src/style.css new file mode 100644 index 0000000000..0733a7ee6b --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/src/style.css @@ -0,0 +1,21 @@ +:root { + background-color: #181818; + color: rgba(255, 255, 255, 0.87); + color-scheme: light dark; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-synthesis: none; + font-weight: 400; + line-height: 1.5; + text-rendering: optimizeLegibility; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +@media (prefers-color-scheme: light) { + :root { + background-color: #f8f8f8; + color: #181818; + } +} diff --git a/packages/create-wagmi/templates/vite-vanilla/src/vite-env.d.ts b/packages/create-wagmi/templates/vite-vanilla/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/create-wagmi/templates/vite-vanilla/src/wagmi.ts b/packages/create-wagmi/templates/vite-vanilla/src/wagmi.ts new file mode 100644 index 0000000000..6baeff5293 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/src/wagmi.ts @@ -0,0 +1,16 @@ +import { coinbaseWallet, injected, walletConnect } from '@wagmi/connectors' +import { http, createConfig } from '@wagmi/core' +import { mainnet, sepolia } from '@wagmi/core/chains' + +export const config = createConfig({ + chains: [mainnet, sepolia], + connectors: [ + injected(), + coinbaseWallet(), + walletConnect({ projectId: import.meta.env.VITE_WC_PROJECT_ID }), + ], + transports: { + [mainnet.id]: http(), + [sepolia.id]: http(), + }, +}) diff --git a/packages/create-wagmi/templates/vite-vanilla/tsconfig.json b/packages/create-wagmi/templates/vite-vanilla/tsconfig.json new file mode 100644 index 0000000000..75abdef265 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vanilla/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/packages/create-wagmi/templates/vite-vue/README.md b/packages/create-wagmi/templates/vite-vue/README.md new file mode 100644 index 0000000000..15f6f79541 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/README.md @@ -0,0 +1 @@ +This is a [Vite](https://vitejs.dev) project bootstrapped with [`create-wagmi`](https://github.com/wevm/wagmi/tree/main/packages/create-wagmi). diff --git a/packages/create-wagmi/templates/vite-vue/_env.local b/packages/create-wagmi/templates/vite-vue/_env.local new file mode 100644 index 0000000000..936f763762 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/_env.local @@ -0,0 +1 @@ +VITE_WC_PROJECT_ID= \ No newline at end of file diff --git a/packages/create-wagmi/templates/vite-vue/_gitignore b/packages/create-wagmi/templates/vite-vue/_gitignore new file mode 100644 index 0000000000..a547bf36d8 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/_gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/create-wagmi/templates/vite-vue/_npmrc b/packages/create-wagmi/templates/vite-vue/_npmrc new file mode 100644 index 0000000000..ca1e9d98b5 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/_npmrc @@ -0,0 +1 @@ +legacy-peer-deps = true \ No newline at end of file diff --git a/packages/create-wagmi/templates/vite-vue/biome.json b/packages/create-wagmi/templates/vite-vue/biome.json new file mode 100644 index 0000000000..08eb8a26ab --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/biome.json @@ -0,0 +1,13 @@ +{ + "formatter": { + "enabled": true, + "indentStyle": "space", + "lineWidth": 120 + }, + "linter": { + "enabled": true + }, + "organizeImports": { + "enabled": true + } +} diff --git a/packages/create-wagmi/templates/vite-vue/index.html b/packages/create-wagmi/templates/vite-vue/index.html new file mode 100644 index 0000000000..e2028cc129 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/index.html @@ -0,0 +1,12 @@ + + + + + + Create Wagmi + + +
+ + + diff --git a/packages/create-wagmi/templates/vite-vue/package.json b/packages/create-wagmi/templates/vite-vue/package.json new file mode 100644 index 0000000000..8d306e620c --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/package.json @@ -0,0 +1,24 @@ +{ + "name": "wagmi-vue-vite-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build", + "lint": "biome check .", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/vue-query": ">=5.45.0", + "@wagmi/vue": "latest", + "viem": "latest", + "vue": ">=3.4.21" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "buffer": "^6.0.3", + "vite": "^5.2.11", + "vue-tsc": "^2.0.6" + } +} diff --git a/packages/create-wagmi/templates/vite-vue/src/App.vue b/packages/create-wagmi/templates/vite-vue/src/App.vue new file mode 100644 index 0000000000..421aeb9a7b --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/src/App.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/create-wagmi/templates/vite-vue/src/components/Account.vue b/packages/create-wagmi/templates/vite-vue/src/components/Account.vue new file mode 100644 index 0000000000..33f1491dac --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/src/components/Account.vue @@ -0,0 +1,22 @@ + + + diff --git a/packages/create-wagmi/templates/vite-vue/src/components/Connect.vue b/packages/create-wagmi/templates/vite-vue/src/components/Connect.vue new file mode 100644 index 0000000000..93320448c0 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/src/components/Connect.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/create-wagmi/templates/vite-vue/src/main.ts b/packages/create-wagmi/templates/vite-vue/src/main.ts new file mode 100644 index 0000000000..820eed3722 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/src/main.ts @@ -0,0 +1,17 @@ +import { Buffer } from 'buffer' +import { VueQueryPlugin } from '@tanstack/vue-query' +import { WagmiPlugin } from '@wagmi/vue' +import { createApp } from 'vue' + +// `@coinbase-wallet/sdk` uses `Buffer` +globalThis.Buffer = Buffer + +import App from './App.vue' +import './style.css' +import { config } from './wagmi' + +const app = createApp(App) + +app.use(WagmiPlugin, { config }).use(VueQueryPlugin, {}) + +app.mount('#app') diff --git a/packages/create-wagmi/templates/vite-vue/src/style.css b/packages/create-wagmi/templates/vite-vue/src/style.css new file mode 100644 index 0000000000..0733a7ee6b --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/src/style.css @@ -0,0 +1,21 @@ +:root { + background-color: #181818; + color: rgba(255, 255, 255, 0.87); + color-scheme: light dark; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-synthesis: none; + font-weight: 400; + line-height: 1.5; + text-rendering: optimizeLegibility; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +@media (prefers-color-scheme: light) { + :root { + background-color: #f8f8f8; + color: #181818; + } +} diff --git a/packages/create-wagmi/templates/vite-vue/src/vite-env.d.ts b/packages/create-wagmi/templates/vite-vue/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/create-wagmi/templates/vite-vue/src/wagmi.ts b/packages/create-wagmi/templates/vite-vue/src/wagmi.ts new file mode 100644 index 0000000000..f0282e9490 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/src/wagmi.ts @@ -0,0 +1,25 @@ +import { http, createConfig, createStorage } from '@wagmi/vue' +import { mainnet, optimism, sepolia } from '@wagmi/vue/chains' +import { coinbaseWallet, walletConnect } from '@wagmi/vue/connectors' + +export const config = createConfig({ + chains: [mainnet, sepolia, optimism], + connectors: [ + walletConnect({ + projectId: import.meta.env.VITE_WC_PROJECT_ID, + }), + coinbaseWallet({ appName: 'Vite Vue Playground', darkMode: true }), + ], + storage: createStorage({ storage: localStorage, key: 'vite-vue' }), + transports: { + [mainnet.id]: http(), + [sepolia.id]: http(), + [optimism.id]: http(), + }, +}) + +declare module '@wagmi/vue' { + interface Register { + config: typeof config + } +} diff --git a/packages/create-wagmi/templates/vite-vue/tsconfig.json b/packages/create-wagmi/templates/vite-vue/tsconfig.json new file mode 100644 index 0000000000..9e03e60496 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/packages/create-wagmi/templates/vite-vue/tsconfig.node.json b/packages/create-wagmi/templates/vite-vue/tsconfig.node.json new file mode 100644 index 0000000000..97ede7ee6f --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/create-wagmi/templates/vite-vue/vite.config.ts b/packages/create-wagmi/templates/vite-vue/vite.config.ts new file mode 100644 index 0000000000..c3fa62d865 --- /dev/null +++ b/packages/create-wagmi/templates/vite-vue/vite.config.ts @@ -0,0 +1,7 @@ +import vue from '@vitejs/plugin-vue' +import { defineConfig } from 'vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], +}) diff --git a/packages/create-wagmi/tsconfig.build.json b/packages/create-wagmi/tsconfig.build.json new file mode 100644 index 0000000000..45ae2069ef --- /dev/null +++ b/packages/create-wagmi/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"], + "compilerOptions": { + "sourceMap": true + } +} diff --git a/packages/create-wagmi/tsconfig.json b/packages/create-wagmi/tsconfig.json new file mode 100644 index 0000000000..bd33919ac3 --- /dev/null +++ b/packages/create-wagmi/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.build.json", + "include": ["src/**/*.ts"], + "exclude": [] +} diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md new file mode 100644 index 0000000000..7dfc14098a --- /dev/null +++ b/packages/react/CHANGELOG.md @@ -0,0 +1,5037 @@ +# wagmi + +## 2.15.4 + +### Patch Changes + +- Updated dependencies [[`42b1fed58e9ac09da0f8ebf3e9271f98a707aaac`](https://github.com/wevm/wagmi/commit/42b1fed58e9ac09da0f8ebf3e9271f98a707aaac)]: + - @wagmi/connectors@5.8.3 + +## 2.15.3 + +### Patch Changes + +- Updated dependencies [[`29297a48af72b537173d948ccd2fe37d39914c66`](https://github.com/wevm/wagmi/commit/29297a48af72b537173d948ccd2fe37d39914c66), [`07370106d5fb6b8fe300992d93abf25b3d0eaf57`](https://github.com/wevm/wagmi/commit/07370106d5fb6b8fe300992d93abf25b3d0eaf57)]: + - @wagmi/core@2.17.2 + - @wagmi/connectors@5.8.2 + +## 2.15.2 + +### Patch Changes + +- [#4649](https://github.com/wevm/wagmi/pull/4649) [`01f64e64fa4f85cdd30023903f972f4f9023681f`](https://github.com/wevm/wagmi/commit/01f64e64fa4f85cdd30023903f972f4f9023681f) Thanks [@jxom](https://github.com/jxom)! - Added `chainId` parameter to `getCapabilities`/`useCapabilities`. + +- Updated dependencies [[`01f64e64fa4f85cdd30023903f972f4f9023681f`](https://github.com/wevm/wagmi/commit/01f64e64fa4f85cdd30023903f972f4f9023681f)]: + - @wagmi/core@2.17.1 + - @wagmi/connectors@5.8.1 + +## 2.15.1 + +### Patch Changes + +- Updated dependencies [[`cc5517ff6880bb630f1b201930acc20dd1a0b451`](https://github.com/wevm/wagmi/commit/cc5517ff6880bb630f1b201930acc20dd1a0b451)]: + - @wagmi/connectors@5.8.0 + +## 2.15.0 + +### Minor Changes + +- [#4638](https://github.com/wevm/wagmi/pull/4638) [`799ee4d4b23c2ecd64e3f3668e67634e81939719`](https://github.com/wevm/wagmi/commit/799ee4d4b23c2ecd64e3f3668e67634e81939719) Thanks [@jxom](https://github.com/jxom)! - Stabilized EIP-5792 Actions & Hooks. + +### Patch Changes + +- Updated dependencies [[`88427b2bcd13ec375ef519e9ad1ccffef9f02a7b`](https://github.com/wevm/wagmi/commit/88427b2bcd13ec375ef519e9ad1ccffef9f02a7b), [`799ee4d4b23c2ecd64e3f3668e67634e81939719`](https://github.com/wevm/wagmi/commit/799ee4d4b23c2ecd64e3f3668e67634e81939719), [`3f8b2edc4f237cccff1009bcef03d51ca27a7324`](https://github.com/wevm/wagmi/commit/3f8b2edc4f237cccff1009bcef03d51ca27a7324)]: + - @wagmi/connectors@6.0.0 + - @wagmi/core@2.17.0 + +## 2.14.16 + +### Patch Changes + +- Updated dependencies [[`b59c024b23c69f5459b17390531207cfdf126ce4`](https://github.com/wevm/wagmi/commit/b59c024b23c69f5459b17390531207cfdf126ce4)]: + - @wagmi/connectors@5.7.12 + +## 2.14.15 + +### Patch Changes + +- [`a4bd0623eed28e3761a27295831a60ad835f0ee0`](https://github.com/wevm/wagmi/commit/a4bd0623eed28e3761a27295831a60ad835f0ee0) Thanks [@jxom](https://github.com/jxom)! - **Experimental (EIP-5792):** Updated `id` parameter to be optional on `useWaitForCallsStatus`. + +- Updated dependencies [[`a4bd0623eed28e3761a27295831a60ad835f0ee0`](https://github.com/wevm/wagmi/commit/a4bd0623eed28e3761a27295831a60ad835f0ee0)]: + - @wagmi/core@2.16.7 + - @wagmi/connectors@5.7.11 + +## 2.14.14 + +### Patch Changes + +- [#4586](https://github.com/wevm/wagmi/pull/4586) [`edf47477b2f6385a1c3ae01d36a8498c47f30a0b`](https://github.com/wevm/wagmi/commit/edf47477b2f6385a1c3ae01d36a8498c47f30a0b) Thanks [@jxom](https://github.com/jxom)! - **Experimental (EIP-5792):** Added `waitForCallsStatus` + `useWaitForCallsStatus`. + +- Updated dependencies [[`edf47477b2f6385a1c3ae01d36a8498c47f30a0b`](https://github.com/wevm/wagmi/commit/edf47477b2f6385a1c3ae01d36a8498c47f30a0b), [`e944812ebc234a72c1417b77cff341166f5e0fef`](https://github.com/wevm/wagmi/commit/e944812ebc234a72c1417b77cff341166f5e0fef)]: + - @wagmi/core@2.16.6 + - @wagmi/connectors@5.7.10 + +## 2.14.13 + +### Patch Changes + +- Updated dependencies [[`5b7101fddb61df56e34b2e02b46bc409e496eaf9`](https://github.com/wevm/wagmi/commit/5b7101fddb61df56e34b2e02b46bc409e496eaf9)]: + - @wagmi/connectors@5.7.9 + +## 2.14.12 + +### Patch Changes + +- [`d0c9a86921a4e939373cc6e763284e53f2a2e93c`](https://github.com/wevm/wagmi/commit/d0c9a86921a4e939373cc6e763284e53f2a2e93c) Thanks [@jxom](https://github.com/jxom)! - **Experimental (ERC-5792)**: Added support for `account: null` in `sendCalls` to cater for sending calls without a connected account (account will be filled by the wallet). + +- Updated dependencies [[`d0c9a86921a4e939373cc6e763284e53f2a2e93c`](https://github.com/wevm/wagmi/commit/d0c9a86921a4e939373cc6e763284e53f2a2e93c)]: + - @wagmi/core@2.16.5 + - @wagmi/connectors@5.7.8 + +## 2.14.11 + +### Patch Changes + +- [`507f864d91238bfd423d0e36d3619eb9f6e52eec`](https://github.com/wevm/wagmi/commit/507f864d91238bfd423d0e36d3619eb9f6e52eec) Thanks [@jxom](https://github.com/jxom)! - Updated `@coinbase/wallet-sdk`. + +- Updated dependencies [[`507f864d91238bfd423d0e36d3619eb9f6e52eec`](https://github.com/wevm/wagmi/commit/507f864d91238bfd423d0e36d3619eb9f6e52eec)]: + - @wagmi/connectors@5.7.7 + - @wagmi/core@2.16.4 + +## 2.14.10 + +### Patch Changes + +- Updated dependencies [[`639952c97f0fe3927106f42d3c9f7f366cdf7f7a`](https://github.com/wevm/wagmi/commit/639952c97f0fe3927106f42d3c9f7f366cdf7f7a), [`5aa2c095f7bfb6dfcf91c6945c3e1f9c9dd05766`](https://github.com/wevm/wagmi/commit/5aa2c095f7bfb6dfcf91c6945c3e1f9c9dd05766)]: + - @wagmi/connectors@5.7.6 + +## 2.14.9 + +### Patch Changes + +- Updated dependencies [[`a257e8d4f97431a4af872cda1817b4ae17c7bbed`](https://github.com/wevm/wagmi/commit/a257e8d4f97431a4af872cda1817b4ae17c7bbed)]: + - @wagmi/connectors@5.7.5 + +## 2.14.8 + +### Patch Changes + +- Updated dependencies [[`c8a257e0f6d2ece013b873895c35769a8a804fdc`](https://github.com/wevm/wagmi/commit/c8a257e0f6d2ece013b873895c35769a8a804fdc)]: + - @wagmi/connectors@5.7.4 + +## 2.14.7 + +### Patch Changes + +- [#4497](https://github.com/wevm/wagmi/pull/4497) [`00c144b21bac3f0797b683d8a4a81f7399c6e042`](https://github.com/wevm/wagmi/commit/00c144b21bac3f0797b683d8a4a81f7399c6e042) Thanks [@tmm](https://github.com/tmm)! - Bumped `use-sync-external-store` version for React 19. + +## 2.14.6 + +### Patch Changes + +- [#4480](https://github.com/wevm/wagmi/pull/4480) [`384a1d91597622eb59e1c05dc13ce25017c5b6d8`](https://github.com/wevm/wagmi/commit/384a1d91597622eb59e1c05dc13ce25017c5b6d8) Thanks [@RodeRickIsWatching](https://github.com/RodeRickIsWatching)! - Fixed invocation of default storage. + +- Updated dependencies [[`384a1d91597622eb59e1c05dc13ce25017c5b6d8`](https://github.com/wevm/wagmi/commit/384a1d91597622eb59e1c05dc13ce25017c5b6d8)]: + - @wagmi/core@2.16.3 + - @wagmi/connectors@5.7.3 + +## 2.14.5 + +### Patch Changes + +- [`012907032b532a438fce48f407470250cbc8f0c6`](https://github.com/wevm/wagmi/commit/012907032b532a438fce48f407470250cbc8f0c6) Thanks [@jxom](https://github.com/jxom)! - Fixed assignment in `getDefaultStorage`. + +- Updated dependencies [[`012907032b532a438fce48f407470250cbc8f0c6`](https://github.com/wevm/wagmi/commit/012907032b532a438fce48f407470250cbc8f0c6)]: + - @wagmi/core@2.16.2 + - @wagmi/connectors@5.7.2 + +## 2.14.4 + +### Patch Changes + +- Updated dependencies [[`9c8c35a3b829f2c58edcd3a29e2dcd99974d7470`](https://github.com/wevm/wagmi/commit/9c8c35a3b829f2c58edcd3a29e2dcd99974d7470), [`3892ebd21c06beef4b28ece4e70d2a38807bce6f`](https://github.com/wevm/wagmi/commit/3892ebd21c06beef4b28ece4e70d2a38807bce6f)]: + - @wagmi/connectors@5.7.1 + - @wagmi/core@2.16.1 + +## 2.14.3 + +### Patch Changes + +- Updated dependencies [[`e3f63a02c1f7d80481804584f262bc98dab0400d`](https://github.com/wevm/wagmi/commit/e3f63a02c1f7d80481804584f262bc98dab0400d)]: + - @wagmi/connectors@5.7.0 + +## 2.14.2 + +### Patch Changes + +- Updated dependencies [[`adf2253b10c6d4fc583e4bc9f01a8ef5ca267c85`](https://github.com/wevm/wagmi/commit/adf2253b10c6d4fc583e4bc9f01a8ef5ca267c85)]: + - @wagmi/connectors@5.6.2 + +## 2.14.1 + +### Patch Changes + +- Updated dependencies [[`987404f590c1d29ebb3cb68928f5e54aa032793d`](https://github.com/wevm/wagmi/commit/987404f590c1d29ebb3cb68928f5e54aa032793d)]: + - @wagmi/connectors@5.6.1 + +## 2.14.0 + +### Minor Changes + +- [#4453](https://github.com/wevm/wagmi/pull/4453) [`070e48480194c8d7f45bda1d7dd1346e6f5d7227`](https://github.com/wevm/wagmi/commit/070e48480194c8d7f45bda1d7dd1346e6f5d7227) Thanks [@tmm](https://github.com/tmm)! - Added support to `useConnect` for custom `connector.connect` parameters. + +### Patch Changes + +- Updated dependencies [[`afea6b67822a7a2b96901ec851441d27ee0f7a52`](https://github.com/wevm/wagmi/commit/afea6b67822a7a2b96901ec851441d27ee0f7a52), [`070e48480194c8d7f45bda1d7dd1346e6f5d7227`](https://github.com/wevm/wagmi/commit/070e48480194c8d7f45bda1d7dd1346e6f5d7227), [`8b0726c1106fce88b782e676498eabf0718b2619`](https://github.com/wevm/wagmi/commit/8b0726c1106fce88b782e676498eabf0718b2619)]: + - @wagmi/connectors@5.6.0 + - @wagmi/core@2.16.0 + +## 2.13.5 + +### Patch Changes + +- [#4447](https://github.com/wevm/wagmi/pull/4447) [`244f7777d9d227b3134d4cb9b9dda64f50cbddd3`](https://github.com/wevm/wagmi/commit/244f7777d9d227b3134d4cb9b9dda64f50cbddd3) Thanks [@Aerilym](https://github.com/Aerilym)! - Fixed config parameter passing in useSimulateContract and useEstimateGas + +## 2.13.4 + +### Patch Changes + +- [`2f79a3da4872d6158569017b1927a07a1ff5e7ba`](https://github.com/wevm/wagmi/commit/2f79a3da4872d6158569017b1927a07a1ff5e7ba) Thanks [@tmm](https://github.com/tmm)! - Exported `injected` and `mock`. + +## 2.13.3 + +### Patch Changes + +- [#4433](https://github.com/wevm/wagmi/pull/4433) [`06e186cd679b27fe195309110e766fcf46d4efbc`](https://github.com/wevm/wagmi/commit/06e186cd679b27fe195309110e766fcf46d4efbc) Thanks [@Aerilym](https://github.com/Aerilym)! - Bumped Metamask SDK version to `0.31.1`. + +- Updated dependencies [[`06e186cd679b27fe195309110e766fcf46d4efbc`](https://github.com/wevm/wagmi/commit/06e186cd679b27fe195309110e766fcf46d4efbc)]: + - @wagmi/connectors@5.5.3 + - @wagmi/core@2.15.2 + +## 2.13.2 + +### Patch Changes + +- Updated dependencies [[`e563ef69130a511fd6f3f72ed4cd4fbe1390541f`](https://github.com/wevm/wagmi/commit/e563ef69130a511fd6f3f72ed4cd4fbe1390541f)]: + - @wagmi/connectors@5.5.2 + +## 2.13.1 + +### Patch Changes + +- [`b8bbb409f4934538e3dd6cac5aaf7346292d0693`](https://github.com/wevm/wagmi/commit/b8bbb409f4934538e3dd6cac5aaf7346292d0693) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where `null` gas would accidentally pass through. + +- Updated dependencies [[`b8bbb409f4934538e3dd6cac5aaf7346292d0693`](https://github.com/wevm/wagmi/commit/b8bbb409f4934538e3dd6cac5aaf7346292d0693)]: + - @wagmi/core@2.15.1 + - @wagmi/connectors@5.5.1 + +## 2.13.0 + +### Minor Changes + +- [#4417](https://github.com/wevm/wagmi/pull/4417) [`42e65ea4fea99c639817088bba915e0933d17141`](https://github.com/wevm/wagmi/commit/42e65ea4fea99c639817088bba915e0933d17141) Thanks [@jxom](https://github.com/jxom)! - Removed simulation in `writeContract` & `sendTransaction`. + +### Patch Changes + +- Updated dependencies [[`42e65ea4fea99c639817088bba915e0933d17141`](https://github.com/wevm/wagmi/commit/42e65ea4fea99c639817088bba915e0933d17141)]: + - @wagmi/connectors@6.0.0 + - @wagmi/core@2.15.0 + +## 2.12.33 + +### Patch Changes + +- Updated dependencies [[`7ca62b44cd997d48f92c2b81343726a5908aa00b`](https://github.com/wevm/wagmi/commit/7ca62b44cd997d48f92c2b81343726a5908aa00b)]: + - @wagmi/connectors@5.4.0 + +## 2.12.32 + +### Patch Changes + +- Updated dependencies [[`a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3`](https://github.com/wevm/wagmi/commit/a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3), [`a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3`](https://github.com/wevm/wagmi/commit/a13aa8d7c38eb3cc8171a02d6302e6d12cf6bcb3)]: + - @wagmi/core@2.14.6 + - @wagmi/connectors@5.3.10 + +## 2.12.31 + +### Patch Changes + +- Updated dependencies [[`b12a04eeec985c48d2feac94b011d41fb29ca23e`](https://github.com/wevm/wagmi/commit/b12a04eeec985c48d2feac94b011d41fb29ca23e)]: + - @wagmi/connectors@5.3.9 + +## 2.12.30 + +### Patch Changes + +- Updated dependencies [[`6b9bbacdc7bffd44fc2165362a5e65fd434e7646`](https://github.com/wevm/wagmi/commit/6b9bbacdc7bffd44fc2165362a5e65fd434e7646), [`dac62dc99a0679fa632a0fae49873d6053d06b35`](https://github.com/wevm/wagmi/commit/dac62dc99a0679fa632a0fae49873d6053d06b35)]: + - @wagmi/core@2.14.5 + - @wagmi/connectors@5.3.8 + +## 2.12.29 + +### Patch Changes + +- Updated dependencies [[`e08681c81fbdf475213e2d0f4c5517d0abf4e743`](https://github.com/wevm/wagmi/commit/e08681c81fbdf475213e2d0f4c5517d0abf4e743)]: + - @wagmi/core@2.14.4 + - @wagmi/connectors@5.3.7 + +## 2.12.28 + +### Patch Changes + +- Updated dependencies [[`7558ff3133c11bc4c49473d08ee9a47eaa12df5b`](https://github.com/wevm/wagmi/commit/7558ff3133c11bc4c49473d08ee9a47eaa12df5b)]: + - @wagmi/connectors@5.3.6 + +## 2.12.27 + +### Patch Changes + +- Updated dependencies [[`cb7dd2ebb871d0be8f1a11a8cd8ce592cd74b7c7`](https://github.com/wevm/wagmi/commit/cb7dd2ebb871d0be8f1a11a8cd8ce592cd74b7c7), [`7fe78f2d09778fc01fd0cffe85ba198e64999275`](https://github.com/wevm/wagmi/commit/7fe78f2d09778fc01fd0cffe85ba198e64999275)]: + - @wagmi/core@2.14.3 + - @wagmi/connectors@5.3.5 + +## 2.12.26 + +### Patch Changes + +- Updated dependencies [[`b6861a4c378dab78d8751ae0ac2aa425f3c24b8f`](https://github.com/wevm/wagmi/commit/b6861a4c378dab78d8751ae0ac2aa425f3c24b8f), [`d0d0963bb5904a15cf0355862d62dd141ce0c31c`](https://github.com/wevm/wagmi/commit/d0d0963bb5904a15cf0355862d62dd141ce0c31c), [`ecac0ba36243d94c9199d0bd21937104c835d9a0`](https://github.com/wevm/wagmi/commit/ecac0ba36243d94c9199d0bd21937104c835d9a0)]: + - @wagmi/connectors@5.3.4 + - @wagmi/core@2.14.2 + +## 2.12.25 + +### Patch Changes + +- Updated dependencies [[`83c6d16b7d6dddfa6bda036e04f00ec313c6248c`](https://github.com/wevm/wagmi/commit/83c6d16b7d6dddfa6bda036e04f00ec313c6248c)]: + - @wagmi/connectors@5.3.3 + +## 2.12.24 + +### Patch Changes + +- [`a4c5389c1a299bd7acf9df4a0d607e2ced709280`](https://github.com/wevm/wagmi/commit/a4c5389c1a299bd7acf9df4a0d607e2ced709280) Thanks [@jxom](https://github.com/jxom)! - Exported `Transport` type (for inference). + +## 2.12.23 + +### Patch Changes + +- Updated dependencies [[`8970cc51398e1ac713435533096215c6d31ffdf9`](https://github.com/wevm/wagmi/commit/8970cc51398e1ac713435533096215c6d31ffdf9)]: + - @wagmi/connectors@5.3.2 + +## 2.12.22 + +### Patch Changes + +- Updated dependencies [[`052e72e1f8c1c14fcbdce04a9f8fa7ec28d83702`](https://github.com/wevm/wagmi/commit/052e72e1f8c1c14fcbdce04a9f8fa7ec28d83702), [`b250fc21ee577b2a75c5a34ff684f62fb4ad771a`](https://github.com/wevm/wagmi/commit/b250fc21ee577b2a75c5a34ff684f62fb4ad771a)]: + - @wagmi/core@2.14.1 + - @wagmi/connectors@5.3.1 + +## 2.12.21 + +### Patch Changes + +- Updated dependencies [[`f43e074f473820b208a6295d7c97f847332f1a1d`](https://github.com/wevm/wagmi/commit/f43e074f473820b208a6295d7c97f847332f1a1d)]: + - @wagmi/connectors@6.0.0 + - @wagmi/core@2.14.0 + +## 2.12.20 + +### Patch Changes + +- Updated dependencies [[`c05caabc20c3ced9682cfc7ba1f3f7dcfece0703`](https://github.com/wevm/wagmi/commit/c05caabc20c3ced9682cfc7ba1f3f7dcfece0703), [`5ae49af590ff168426c9c283d54c34ae5148fcd9`](https://github.com/wevm/wagmi/commit/5ae49af590ff168426c9c283d54c34ae5148fcd9), [`f3182b22e6e454d9bd74f1b940ef34431fd9555d`](https://github.com/wevm/wagmi/commit/f3182b22e6e454d9bd74f1b940ef34431fd9555d)]: + - @wagmi/core@2.13.9 + - @wagmi/connectors@5.2.2 + +## 2.12.19 + +### Patch Changes + +- Updated dependencies [[`91a40f2db08e3a91db421b8732a5511a1e6c88fd`](https://github.com/wevm/wagmi/commit/91a40f2db08e3a91db421b8732a5511a1e6c88fd)]: + - @wagmi/connectors@5.2.1 + +## 2.12.18 + +### Patch Changes + +- Updated dependencies [[`34a0c3b7eea778aee7c27f7ace5e4b2be4e8a0a4`](https://github.com/wevm/wagmi/commit/34a0c3b7eea778aee7c27f7ace5e4b2be4e8a0a4)]: + - @wagmi/connectors@5.2.0 + +## 2.12.17 + +### Patch Changes + +- Updated dependencies [[`3b2123664b7ac66848390739e855c3b9702ab60c`](https://github.com/wevm/wagmi/commit/3b2123664b7ac66848390739e855c3b9702ab60c)]: + - @wagmi/connectors@5.1.15 + +## 2.12.16 + +### Patch Changes + +- Updated dependencies [[`56f2482508f2ba71bd6b0295c70c6abca7101e57`](https://github.com/wevm/wagmi/commit/56f2482508f2ba71bd6b0295c70c6abca7101e57)]: + - @wagmi/connectors@5.1.14 + - @wagmi/core@2.13.8 + +## 2.12.15 + +### Patch Changes + +- [#4229](https://github.com/wevm/wagmi/pull/4229) [`c6b8efd26254c8e692b2b67286ed538fc183b992`](https://github.com/wevm/wagmi/commit/c6b8efd26254c8e692b2b67286ed538fc183b992) Thanks [@weilliao05621](https://github.com/weilliao05621)! - Stabilized `useAccount` return type object reference. + +## 2.12.14 + +### Patch Changes + +- Updated dependencies [[`be75c2d4ef636d7362420ab0a106bfdf63f5d1e6`](https://github.com/wevm/wagmi/commit/be75c2d4ef636d7362420ab0a106bfdf63f5d1e6)]: + - @wagmi/core@2.13.7 + - @wagmi/connectors@5.1.13 + +## 2.12.13 + +### Patch Changes + +- Updated dependencies [[`edcbf5d6fbe92f639bead800502edda9e0aa39f1`](https://github.com/wevm/wagmi/commit/edcbf5d6fbe92f639bead800502edda9e0aa39f1)]: + - @wagmi/core@2.13.6 + - @wagmi/connectors@5.1.12 + +## 2.12.12 + +### Patch Changes + +- Updated dependencies [[`82404c960e04c83e0bae6e1e12459ef9debf9554`](https://github.com/wevm/wagmi/commit/82404c960e04c83e0bae6e1e12459ef9debf9554), [`d07ad7f63a018256908a673d078aaf79e47ac703`](https://github.com/wevm/wagmi/commit/d07ad7f63a018256908a673d078aaf79e47ac703)]: + - @wagmi/connectors@5.1.11 + +## 2.12.11 + +### Patch Changes + +- [#4262](https://github.com/wevm/wagmi/pull/4262) [`8531f83db3a1fbb8202c3e426b7f85679f587a52`](https://github.com/wevm/wagmi/commit/8531f83db3a1fbb8202c3e426b7f85679f587a52) Thanks [@nezouse](https://github.com/nezouse)! - Added experimental actions entrypoint. + +## 2.12.10 + +### Patch Changes + +- [#4260](https://github.com/wevm/wagmi/pull/4260) [`969a208a110b760a13fd7263360320f52440a9b6`](https://github.com/wevm/wagmi/commit/969a208a110b760a13fd7263360320f52440a9b6) Thanks [@tmm](https://github.com/tmm)! - Fixed `useReadContract` deployless reads support. + +- [#4259](https://github.com/wevm/wagmi/pull/4259) [`f47ce8f6d263e49fdff90b8edb3190142d2657bb`](https://github.com/wevm/wagmi/commit/f47ce8f6d263e49fdff90b8edb3190142d2657bb) Thanks [@tmm](https://github.com/tmm)! - Disabled `useConnectorClient` and `useWalletClient` during reconnection if connector is not fully restored. + +- Updated dependencies [[`81de006e66121a18c61945c1f9b8426c83a5713c`](https://github.com/wevm/wagmi/commit/81de006e66121a18c61945c1f9b8426c83a5713c), [`f47ce8f6d263e49fdff90b8edb3190142d2657bb`](https://github.com/wevm/wagmi/commit/f47ce8f6d263e49fdff90b8edb3190142d2657bb)]: + - @wagmi/connectors@5.1.10 + - @wagmi/core@2.13.5 + +## 2.12.9 + +### Patch Changes + +- Updated dependencies [[`21bd0e473d374cbbd7a01bececa6022d529026ba`](https://github.com/wevm/wagmi/commit/21bd0e473d374cbbd7a01bececa6022d529026ba), [`5c89c6853e616437a3be2b019db895451fecfb3c`](https://github.com/wevm/wagmi/commit/5c89c6853e616437a3be2b019db895451fecfb3c)]: + - @wagmi/connectors@5.1.9 + +## 2.12.8 + +### Patch Changes + +- Updated dependencies [[`b580ad4edff1721e0b9d138cf5ae2ec74d2374c7`](https://github.com/wevm/wagmi/commit/b580ad4edff1721e0b9d138cf5ae2ec74d2374c7)]: + - @wagmi/connectors@5.1.8 + +## 2.12.7 + +### Patch Changes + +- Updated dependencies [[`91fd81a068789c5020e891f539bcad8f54a7a52f`](https://github.com/wevm/wagmi/commit/91fd81a068789c5020e891f539bcad8f54a7a52f)]: + - @wagmi/connectors@5.1.7 + +## 2.12.6 + +### Patch Changes + +- Updated dependencies [[`3168616298cbb6135d0ffda771cba4126e83eba8`](https://github.com/wevm/wagmi/commit/3168616298cbb6135d0ffda771cba4126e83eba8), [`d7608ef9a79459465dc8c06a2ab740465c881907`](https://github.com/wevm/wagmi/commit/d7608ef9a79459465dc8c06a2ab740465c881907)]: + - @wagmi/connectors@5.1.6 + +## 2.12.5 + +### Patch Changes + +- Updated dependencies [[`b4c8971788c70b09479946ecfa998cff2f1b3953`](https://github.com/wevm/wagmi/commit/b4c8971788c70b09479946ecfa998cff2f1b3953)]: + - @wagmi/core@2.13.4 + - @wagmi/connectors@5.1.5 + +## 2.12.4 + +### Patch Changes + +- Updated dependencies [[`871dbdbfe59ac8ad01d1ec6150ea7b091b7b7de4`](https://github.com/wevm/wagmi/commit/871dbdbfe59ac8ad01d1ec6150ea7b091b7b7de4)]: + - @wagmi/core@2.13.3 + - @wagmi/connectors@5.1.4 + +## 2.12.3 + +### Patch Changes + +- Updated dependencies [[`1b9b523fa9b9dfe839aecdf4b40caa9547d7e594`](https://github.com/wevm/wagmi/commit/1b9b523fa9b9dfe839aecdf4b40caa9547d7e594)]: + - @wagmi/core@2.13.2 + - @wagmi/connectors@5.1.3 + +## 2.12.2 + +### Patch Changes + +- Updated dependencies [[`abb490dac4f0f02f46cb0878e7ca9a0db6aada56`](https://github.com/wevm/wagmi/commit/abb490dac4f0f02f46cb0878e7ca9a0db6aada56), [`28e0e5c9a4f856583f9d36a807502bd51a0c6ec2`](https://github.com/wevm/wagmi/commit/28e0e5c9a4f856583f9d36a807502bd51a0c6ec2)]: + - @wagmi/connectors@5.1.2 + +## 2.12.1 + +### Patch Changes + +- Updated dependencies [[`07c1227f306d0efb9421d4bb77a774f92f5fcf45`](https://github.com/wevm/wagmi/commit/07c1227f306d0efb9421d4bb77a774f92f5fcf45)]: + - @wagmi/core@2.13.1 + - @wagmi/connectors@5.1.1 + +## 2.12.0 + +### Minor Changes + +- [#4162](https://github.com/wevm/wagmi/pull/4162) [`a73a7737b756886b388f120ae423e72cca53e8a0`](https://github.com/wevm/wagmi/commit/a73a7737b756886b388f120ae423e72cca53e8a0) Thanks [@jxom](https://github.com/jxom)! - Added functionality for consumer-defined RPC URLs (`config.transports`) to be propagated to the WalletConnect & MetaMask Connectors. + +### Patch Changes + +- Updated dependencies [[`a73a7737b756886b388f120ae423e72cca53e8a0`](https://github.com/wevm/wagmi/commit/a73a7737b756886b388f120ae423e72cca53e8a0)]: + - @wagmi/connectors@6.0.0 + - @wagmi/core@2.13.0 + +## 2.11.3 + +### Patch Changes + +- [#4124](https://github.com/wevm/wagmi/pull/4124) [`26616462db2e0140025f22c505c4541cfecb9308`](https://github.com/wevm/wagmi/commit/26616462db2e0140025f22c505c4541cfecb9308) Thanks [@t0rbik](https://github.com/t0rbik)! - Updated `useConnectorClient` to be enabled when status is `'reconnecting'` or `'connected'` (previously was also enabled when status was `'connecting'`). + +## 2.11.2 + +### Patch Changes + +- Updated dependencies [[`5bc8c8877810b2eec24a829df87dce40a51e6f20`](https://github.com/wevm/wagmi/commit/5bc8c8877810b2eec24a829df87dce40a51e6f20), [`8d81df5cc884d0a210dedd3c1ea0e2e9e52b83c5`](https://github.com/wevm/wagmi/commit/8d81df5cc884d0a210dedd3c1ea0e2e9e52b83c5)]: + - @wagmi/core@2.12.2 + - @wagmi/connectors@5.0.26 + +## 2.11.1 + +### Patch Changes + +- [#4146](https://github.com/wevm/wagmi/pull/4146) [`cc996e08e930c9e88cf753a1e874652059e81a3b`](https://github.com/wevm/wagmi/commit/cc996e08e930c9e88cf753a1e874652059e81a3b) Thanks [@jxom](https://github.com/jxom)! - Updated `@safe-global/safe-apps-sdk` + `@safe-global/safe-apps-provider` dependencies. + +- Updated dependencies [[`cc996e08e930c9e88cf753a1e874652059e81a3b`](https://github.com/wevm/wagmi/commit/cc996e08e930c9e88cf753a1e874652059e81a3b)]: + - @wagmi/connectors@5.0.25 + - @wagmi/core@2.12.1 + +## 2.11.0 + +### Minor Changes + +- [#4128](https://github.com/wevm/wagmi/pull/4128) [`5581a810ef70308e99c6f8b630cd4bca59f64afc`](https://github.com/wevm/wagmi/commit/5581a810ef70308e99c6f8b630cd4bca59f64afc) Thanks [@dalechyn](https://github.com/dalechyn)! - Added `useWatchAsset` hook. + +### Patch Changes + +- Updated dependencies [[`5581a810ef70308e99c6f8b630cd4bca59f64afc`](https://github.com/wevm/wagmi/commit/5581a810ef70308e99c6f8b630cd4bca59f64afc)]: + - @wagmi/core@2.12.0 + - @wagmi/connectors@6.0.0 + +## 2.10.11 + +### Patch Changes + +- [`d3814ab4b88f9f0e052b53bc3d458df87b43f01d`](https://github.com/wevm/wagmi/commit/d3814ab4b88f9f0e052b53bc3d458df87b43f01d) Thanks [@jxom](https://github.com/jxom)! - Updated `mipd` dependency. + +- Updated dependencies [[`b08013eaa9ce97c02f8a7128ea400e3da7ef74bb`](https://github.com/wevm/wagmi/commit/b08013eaa9ce97c02f8a7128ea400e3da7ef74bb), [`d3814ab4b88f9f0e052b53bc3d458df87b43f01d`](https://github.com/wevm/wagmi/commit/d3814ab4b88f9f0e052b53bc3d458df87b43f01d)]: + - @wagmi/core@2.11.8 + - @wagmi/connectors@5.0.23 + +## 2.10.10 + +### Patch Changes + +- Updated dependencies [[`0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e`](https://github.com/wevm/wagmi/commit/0bb8b562ae04ecfeb2d6b2f1b980ebae31dc127e)]: + - @wagmi/connectors@5.0.22 + - @wagmi/core@2.11.7 + +## 2.10.9 + +### Patch Changes + +- [#4060](https://github.com/wevm/wagmi/pull/4060) [`95965c1f19d480b97f2b297a077a9e607dee32ad`](https://github.com/wevm/wagmi/commit/95965c1f19d480b97f2b297a077a9e607dee32ad) Thanks [@dalechyn](https://github.com/dalechyn)! - Bumped Tanstack Query dependencies to fix typing issues between exported Wagmi query options and TanStack Query suspense query methods (due to [`direction` property in `QueryFunctionContext` being deprecated](https://github.com/TanStack/query/pull/7410)). + +- Updated dependencies [[`ff0760b5900114bcfdf420a9fba3cc278ac95afe`](https://github.com/wevm/wagmi/commit/ff0760b5900114bcfdf420a9fba3cc278ac95afe), [`95965c1f19d480b97f2b297a077a9e607dee32ad`](https://github.com/wevm/wagmi/commit/95965c1f19d480b97f2b297a077a9e607dee32ad)]: + - @wagmi/connectors@5.0.21 + - @wagmi/core@2.11.6 + +## 2.10.8 + +### Patch Changes + +- Updated dependencies [[`43fa971d34cac57fa5a2898ad4d839b95d7af37c`](https://github.com/wevm/wagmi/commit/43fa971d34cac57fa5a2898ad4d839b95d7af37c)]: + - @wagmi/connectors@5.0.20 + +## 2.10.7 + +### Patch Changes + +- Updated dependencies [[`b7ad208030d9f2e3f89912ff76b16cdbd848feda`](https://github.com/wevm/wagmi/commit/b7ad208030d9f2e3f89912ff76b16cdbd848feda)]: + - @wagmi/connectors@5.0.19 + +## 2.10.6 + +### Patch Changes + +- Updated dependencies [[`44d24620c9e3957f3245d14d6a042736371df70b`](https://github.com/wevm/wagmi/commit/44d24620c9e3957f3245d14d6a042736371df70b)]: + - @wagmi/connectors@5.0.18 + +## 2.10.5 + +### Patch Changes + +- Updated dependencies [[`04f2b846b113f3d300d82c9fa75212f1805817c5`](https://github.com/wevm/wagmi/commit/04f2b846b113f3d300d82c9fa75212f1805817c5)]: + - @wagmi/core@2.11.5 + - @wagmi/connectors@5.0.17 + +## 2.10.4 + +### Patch Changes + +- Updated dependencies [[`9e8345cd56186b997b5e56deaa2cfc69b30d15f6`](https://github.com/wevm/wagmi/commit/9e8345cd56186b997b5e56deaa2cfc69b30d15f6), [`02c38c28d1aa0ad7a61c33775de603ed974c5c1b`](https://github.com/wevm/wagmi/commit/02c38c28d1aa0ad7a61c33775de603ed974c5c1b)]: + - @wagmi/core@2.11.4 + - @wagmi/connectors@5.0.16 + +## 2.10.3 + +### Patch Changes + +- Updated dependencies [[`8974e6269bb5d7bfaa90db0246bc7d13e8bff798`](https://github.com/wevm/wagmi/commit/8974e6269bb5d7bfaa90db0246bc7d13e8bff798)]: + - @wagmi/core@2.11.3 + - @wagmi/connectors@5.0.15 + +## 2.10.2 + +### Patch Changes + +- Updated dependencies [[`b4d9ef79deb554ee20fed6666a474be5e7cdd522`](https://github.com/wevm/wagmi/commit/b4d9ef79deb554ee20fed6666a474be5e7cdd522)]: + - @wagmi/core@2.11.2 + - @wagmi/connectors@5.0.14 + +## 2.10.1 + +### Patch Changes + +- Updated dependencies [[`9c862d8d63e3d692a22cef2a90782b74a9103f17`](https://github.com/wevm/wagmi/commit/9c862d8d63e3d692a22cef2a90782b74a9103f17)]: + - @wagmi/connectors@5.0.13 + - @wagmi/core@2.11.1 + +## 2.10.0 + +### Minor Changes + +- [#3816](https://github.com/wevm/wagmi/pull/3816) [`06bb598a7f04c7b167f5b7ff6d46bd15886a6a14`](https://github.com/wevm/wagmi/commit/06bb598a7f04c7b167f5b7ff6d46bd15886a6a14) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `useDeployContract` hook. + +### Patch Changes + +- Updated dependencies [[`06bb598a7f04c7b167f5b7ff6d46bd15886a6a14`](https://github.com/wevm/wagmi/commit/06bb598a7f04c7b167f5b7ff6d46bd15886a6a14), [`24a45b269bd0214a29d6f82a84ac66ef8c3f3822`](https://github.com/wevm/wagmi/commit/24a45b269bd0214a29d6f82a84ac66ef8c3f3822)]: + - @wagmi/core@2.11.0 + - @wagmi/connectors@6.0.0 + +## 2.9.12 + +### Patch Changes + +- Updated dependencies [[`f2a7cefab96691ebed8b8e45ffde071c47b58dbe`](https://github.com/wevm/wagmi/commit/f2a7cefab96691ebed8b8e45ffde071c47b58dbe), [`f0ea0b2a7fe193dadfeb49a4c8031ee451c638b5`](https://github.com/wevm/wagmi/commit/f0ea0b2a7fe193dadfeb49a4c8031ee451c638b5), [`e3b124ce414b8fd1b2214e2c5a28dc72158a13d1`](https://github.com/wevm/wagmi/commit/e3b124ce414b8fd1b2214e2c5a28dc72158a13d1)]: + - @wagmi/core@2.10.6 + - @wagmi/connectors@5.0.11 + +## 2.9.11 + +### Patch Changes + +- Updated dependencies [[`560952acd4bfe33db6c7c07b35c613cef278677c`](https://github.com/wevm/wagmi/commit/560952acd4bfe33db6c7c07b35c613cef278677c)]: + - @wagmi/connectors@5.0.10 + +## 2.9.10 + +### Patch Changes + +- Updated dependencies [[`32cdd7b7dc5aff916c040628519562c3a99d418d`](https://github.com/wevm/wagmi/commit/32cdd7b7dc5aff916c040628519562c3a99d418d)]: + - @wagmi/connectors@5.0.9 + +## 2.9.9 + +### Patch Changes + +- Updated dependencies [[`c1952d1ff7f0a491dc88595a49159451b07b5621`](https://github.com/wevm/wagmi/commit/c1952d1ff7f0a491dc88595a49159451b07b5621)]: + - @wagmi/connectors@5.0.8 + +## 2.9.8 + +### Patch Changes + +- Updated dependencies [[`030c7c2cb380dfd67a2182f62e2aa7a6e1601898`](https://github.com/wevm/wagmi/commit/030c7c2cb380dfd67a2182f62e2aa7a6e1601898)]: + - @wagmi/core@2.10.5 + - @wagmi/connectors@5.0.7 + +## 2.9.7 + +### Patch Changes + +- Updated dependencies [[`51fde8a0433b4fff357c1a8d7e08b41b4c86c968`](https://github.com/wevm/wagmi/commit/51fde8a0433b4fff357c1a8d7e08b41b4c86c968)]: + - @wagmi/core@2.10.4 + - @wagmi/connectors@5.0.6 + +## 2.9.6 + +### Patch Changes + +- Updated dependencies [[`70dd28669dd8d2ce08217cd02e29a8fbba7a08d4`](https://github.com/wevm/wagmi/commit/70dd28669dd8d2ce08217cd02e29a8fbba7a08d4)]: + - @wagmi/connectors@5.0.5 + +## 2.9.5 + +### Patch Changes + +- Updated dependencies [[`be9e1b8a9818b92eb0654a20d9471e9e39329e7e`](https://github.com/wevm/wagmi/commit/be9e1b8a9818b92eb0654a20d9471e9e39329e7e)]: + - @wagmi/connectors@5.0.4 + +## 2.9.4 + +### Patch Changes + +- Updated dependencies [[`2804a8a583b1874271154898b4bae38756ef581c`](https://github.com/wevm/wagmi/commit/2804a8a583b1874271154898b4bae38756ef581c), [`2804a8a583b1874271154898b4bae38756ef581c`](https://github.com/wevm/wagmi/commit/2804a8a583b1874271154898b4bae38756ef581c)]: + - @wagmi/connectors@5.0.3 + - @wagmi/core@2.10.3 + +## 2.9.3 + +### Patch Changes + +- [`ec2f63f106fd468f28b43d3b88ab3e89aaf5e81a`](https://github.com/wevm/wagmi/commit/ec2f63f106fd468f28b43d3b88ab3e89aaf5e81a) Thanks [@tmm](https://github.com/tmm)! - Fixed `useSwitchChain` `chains` typing. + +## 2.9.2 + +### Patch Changes + +- [#3940](https://github.com/wevm/wagmi/pull/3940) [`a5071f581dfdfb961718873643a2fc629101c72a`](https://github.com/wevm/wagmi/commit/a5071f581dfdfb961718873643a2fc629101c72a) Thanks [@jxom](https://github.com/jxom)! - Fixed usage of `metaMask` connector in Vite environments. + +- Updated dependencies [[`a5071f581dfdfb961718873643a2fc629101c72a`](https://github.com/wevm/wagmi/commit/a5071f581dfdfb961718873643a2fc629101c72a)]: + - @wagmi/connectors@5.0.2 + - @wagmi/core@2.10.2 + +## 2.9.1 + +### Patch Changes + +- Bumped versions. + +- Updated dependencies []: + - @wagmi/connectors@5.0.1 + - @wagmi/core@2.10.1 + +## 2.9.0 + +### Minor Changes + +- [#3928](https://github.com/wevm/wagmi/pull/3928) [`3117e71825f9c58a0d718f3d1686f1a191fa9cb1`](https://github.com/wevm/wagmi/commit/3117e71825f9c58a0d718f3d1686f1a191fa9cb1) Thanks [@tmm](https://github.com/tmm)! - Updated the default Coinbase SDK in `coinbaseWallet` Connector to v4.x. + +### Patch Changes + +- Updated dependencies [[`3117e71825f9c58a0d718f3d1686f1a191fa9cb1`](https://github.com/wevm/wagmi/commit/3117e71825f9c58a0d718f3d1686f1a191fa9cb1), [`3117e71825f9c58a0d718f3d1686f1a191fa9cb1`](https://github.com/wevm/wagmi/commit/3117e71825f9c58a0d718f3d1686f1a191fa9cb1)]: + - @wagmi/connectors@5.0.0 + - @wagmi/core@2.10.0 + +## 2.8.8 + +### Patch Changes + +- [#3906](https://github.com/wevm/wagmi/pull/3906) [`32fcb4a31dde6b0206961d8ffe9c651f8a459c67`](https://github.com/wevm/wagmi/commit/32fcb4a31dde6b0206961d8ffe9c651f8a459c67) Thanks [@tmm](https://github.com/tmm)! - Added support for Vue. + +- Updated dependencies [[`32fcb4a31dde6b0206961d8ffe9c651f8a459c67`](https://github.com/wevm/wagmi/commit/32fcb4a31dde6b0206961d8ffe9c651f8a459c67)]: + - @wagmi/connectors@4.3.10 + - @wagmi/core@2.9.8 + +## 2.8.7 + +### Patch Changes + +- [#3924](https://github.com/wevm/wagmi/pull/3924) [`1f58734f88458e0f6adb05c99f0c90f36ab286b8`](https://github.com/wevm/wagmi/commit/1f58734f88458e0f6adb05c99f0c90f36ab286b8) Thanks [@jxom](https://github.com/jxom)! - Refactored `isChainsStale` logic in `walletConnect` connector. + +- Updated dependencies [[`1f58734f88458e0f6adb05c99f0c90f36ab286b8`](https://github.com/wevm/wagmi/commit/1f58734f88458e0f6adb05c99f0c90f36ab286b8)]: + - @wagmi/connectors@4.3.9 + - @wagmi/core@2.9.7 + +## 2.8.6 + +### Patch Changes + +- [#3917](https://github.com/wevm/wagmi/pull/3917) [`05948fdad5bb4a56b08916d45b3dec2cb1e5f55b`](https://github.com/wevm/wagmi/commit/05948fdad5bb4a56b08916d45b3dec2cb1e5f55b) Thanks [@jxom](https://github.com/jxom)! - Updated `@metamask/sdk`. + +- Updated dependencies [[`05948fdad5bb4a56b08916d45b3dec2cb1e5f55b`](https://github.com/wevm/wagmi/commit/05948fdad5bb4a56b08916d45b3dec2cb1e5f55b)]: + - @wagmi/connectors@4.3.8 + - @wagmi/core@2.9.6 + +## 2.8.5 + +### Patch Changes + +- [`4fecbbb66d0aacd03b8c62a6455d11a33cde8f85`](https://github.com/wevm/wagmi/commit/4fecbbb66d0aacd03b8c62a6455d11a33cde8f85) Thanks [@jxom](https://github.com/jxom)! - Fixed address comparison in `getConnectorClient`. + +- Updated dependencies [[`4fecbbb66d0aacd03b8c62a6455d11a33cde8f85`](https://github.com/wevm/wagmi/commit/4fecbbb66d0aacd03b8c62a6455d11a33cde8f85)]: + - @wagmi/core@2.9.5 + - @wagmi/connectors@4.3.7 + +## 2.8.4 + +### Patch Changes + +- Updated dependencies [[`e6139a97c4b8804d734b1547b5e3921ce01fbe24`](https://github.com/wevm/wagmi/commit/e6139a97c4b8804d734b1547b5e3921ce01fbe24)]: + - @wagmi/core@2.9.4 + - @wagmi/connectors@4.3.6 + +## 2.8.3 + +### Patch Changes + +- [#3904](https://github.com/wevm/wagmi/pull/3904) [`addca28ebc20f1a4367c35fe9ef786decff9c87e`](https://github.com/wevm/wagmi/commit/addca28ebc20f1a4367c35fe9ef786decff9c87e) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/ethereum-provider`. + +- Updated dependencies [[`addca28ebc20f1a4367c35fe9ef786decff9c87e`](https://github.com/wevm/wagmi/commit/addca28ebc20f1a4367c35fe9ef786decff9c87e)]: + - @wagmi/connectors@4.3.5 + - @wagmi/core@2.9.3 + +## 2.8.2 + +### Patch Changes + +- [#3902](https://github.com/wevm/wagmi/pull/3902) [`204b7b624612405500ec098fb9e35facd3f74ca4`](https://github.com/wevm/wagmi/commit/204b7b624612405500ec098fb9e35facd3f74ca4) Thanks [@jxom](https://github.com/jxom)! - Made third-party SDK imports type-only. + +- Updated dependencies [[`204b7b624612405500ec098fb9e35facd3f74ca4`](https://github.com/wevm/wagmi/commit/204b7b624612405500ec098fb9e35facd3f74ca4)]: + - @wagmi/connectors@4.3.4 + - @wagmi/core@2.9.2 + +## 2.8.1 + +### Patch Changes + +- [`cda6a5d5`](https://github.com/wevm/wagmi/commit/cda6a5d56328330fbde050b4ef40b01c58d2519a) Thanks [@jxom](https://github.com/jxom)! - Updated packages. + +- Updated dependencies [[`cda6a5d5`](https://github.com/wevm/wagmi/commit/cda6a5d56328330fbde050b4ef40b01c58d2519a)]: + - @wagmi/core@2.9.1 + - @wagmi/connectors@4.3.3 + +## 2.8.0 + +### Minor Changes + +- [#3878](https://github.com/wevm/wagmi/pull/3878) [`017828fc`](https://github.com/wevm/wagmi/commit/017828fc027c7a84b54ea9d627e9389f4d60d6c2) Thanks [@jxom](https://github.com/jxom)! - Added experimental EIP-5792 Actions & Hooks. + +### Patch Changes + +- Updated dependencies [[`017828fc`](https://github.com/wevm/wagmi/commit/017828fc027c7a84b54ea9d627e9389f4d60d6c2)]: + - @wagmi/core@2.9.0 + - @wagmi/connectors@4.3.2 + +## 2.7.1 + +### Patch Changes + +- [#3869](https://github.com/wevm/wagmi/pull/3869) [`d4a78eb0`](https://github.com/wevm/wagmi/commit/d4a78eb07119d2e5617e52481ac7d6c6d1583ddc) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where `prepareTransactionRequest` would internally call unsupported wallet RPC methods. + +- Updated dependencies [[`d4a78eb0`](https://github.com/wevm/wagmi/commit/d4a78eb07119d2e5617e52481ac7d6c6d1583ddc)]: + - @wagmi/core@2.8.1 + - @wagmi/connectors@4.3.1 + +## 2.7.0 + +### Minor Changes + +- [#3868](https://github.com/wevm/wagmi/pull/3868) [`c2af20b8`](https://github.com/wevm/wagmi/commit/c2af20b88cf16970d087faaec10b463357a5836e) Thanks [@jxom](https://github.com/jxom)! - Added `supportsSimulation` property to connectors that indicates if the connector's wallet supports contract simulation. + +### Patch Changes + +- [#3858](https://github.com/wevm/wagmi/pull/3858) [`0d141f17`](https://github.com/wevm/wagmi/commit/0d141f171d6ec44bcbfc9c876565b5e2fb8af6de) Thanks [@yulafezmesi](https://github.com/yulafezmesi)! - Fixed accessing reverted reason property inside `waitForTransactionReceipt`. + +- Updated dependencies [[`0d141f17`](https://github.com/wevm/wagmi/commit/0d141f171d6ec44bcbfc9c876565b5e2fb8af6de), [`c2af20b8`](https://github.com/wevm/wagmi/commit/c2af20b88cf16970d087faaec10b463357a5836e)]: + - @wagmi/core@2.8.0 + - @wagmi/connectors@5.0.0 + +## 2.6.0 + +### Minor Changes + +- [#3857](https://github.com/wevm/wagmi/pull/3857) [`d4274c03`](https://github.com/wevm/wagmi/commit/d4274c03a6af5f2d26d31432016ebc14950a330e) Thanks [@tmm](https://github.com/tmm)! - Added `addEthereumChainParameter` to `switchChain`-related methods. + +### Patch Changes + +- Updated dependencies [[`d4274c03`](https://github.com/wevm/wagmi/commit/d4274c03a6af5f2d26d31432016ebc14950a330e), [`4781a405`](https://github.com/wevm/wagmi/commit/4781a4056d4ffc2c74f96a75429e9b2cd2417ad8), [`400c960b`](https://github.com/wevm/wagmi/commit/400c960b30d701c134850c695ae903a382c29b5b)]: + - @wagmi/connectors@5.0.0 + - @wagmi/core@2.7.0 + +## 2.5.22 + +### Patch Changes + +- [`e3c832a1`](https://github.com/wevm/wagmi/commit/e3c832a12c301f9b0ee129d877b3101d220ba8b2) Thanks [@jxom](https://github.com/jxom)! - Fixed undefined `navigator` issue in MetaMask connector. + +- Updated dependencies [[`e3c832a1`](https://github.com/wevm/wagmi/commit/e3c832a12c301f9b0ee129d877b3101d220ba8b2)]: + - @wagmi/connectors@4.1.28 + - @wagmi/core@2.6.19 + +## 2.5.21 + +### Patch Changes + +- [#3848](https://github.com/wevm/wagmi/pull/3848) [`dd40a41c`](https://github.com/wevm/wagmi/commit/dd40a41c526ab60a288aff2250ed8dba92a27b16) Thanks [@jxom](https://github.com/jxom)! - Updated MetaMask SDK. + +- Updated dependencies [[`dd40a41c`](https://github.com/wevm/wagmi/commit/dd40a41c526ab60a288aff2250ed8dba92a27b16)]: + - @wagmi/connectors@4.1.27 + - @wagmi/core@2.6.18 + +## 2.5.20 + +### Patch Changes + +- [#3822](https://github.com/wevm/wagmi/pull/3822) [`a97bfbae`](https://github.com/wevm/wagmi/commit/a97bfbaeb615cfef04665e5e7348d85d17f960f0) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where Wagmi would not correctly rehydrate the active chain when a persisted store was being used. + +- Updated dependencies [[`a97bfbae`](https://github.com/wevm/wagmi/commit/a97bfbaeb615cfef04665e5e7348d85d17f960f0)]: + - @wagmi/core@2.6.17 + - @wagmi/connectors@4.1.26 + +## 2.5.19 + +### Patch Changes + +- [#3793](https://github.com/wevm/wagmi/pull/3793) [`f85b83ae`](https://github.com/wevm/wagmi/commit/f85b83ae95dd0bb73ffbdb49afa174e7c68298e1) Thanks [@tmm](https://github.com/tmm)! - Wired up `config` inside hooks so you can pass it explicitly and not use the `WagmiProvider`. + +- Updated dependencies [[`42ad380d`](https://github.com/wevm/wagmi/commit/42ad380d9a5d8bc0f61d73612142dea9d098de5e)]: + - @wagmi/connectors@4.1.25 + - @wagmi/core@2.6.16 + +## 2.5.18 + +### Patch Changes + +- Updated dependencies [[`b907d5ac`](https://github.com/wevm/wagmi/commit/b907d5ac3a746bcbccc06d1fe78c5bd8f9a7d685)]: + - @wagmi/core@2.6.15 + - @wagmi/connectors@4.1.24 + +## 2.5.17 + +### Patch Changes + +- [#3779](https://github.com/wevm/wagmi/pull/3779) [`3da20bb8`](https://github.com/wevm/wagmi/commit/3da20bb80e7c3efeef8227ced66ad615370fc242) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `eth_requestAccounts` would be called upon reconnect instead of `eth_accounts`. + +- [`a3d1858f`](https://github.com/wevm/wagmi/commit/a3d1858fce448d2b70e36ee692ef1589b74e9d3f) Thanks [@jxom](https://github.com/jxom)! - Fixed hydration conditional in `createConfig`. + +- Updated dependencies [[`b3b54ef1`](https://github.com/wevm/wagmi/commit/b3b54ef179c5fa0d1694d38d4b808549a0550409), [`3da20bb8`](https://github.com/wevm/wagmi/commit/3da20bb80e7c3efeef8227ced66ad615370fc242), [`a3d1858f`](https://github.com/wevm/wagmi/commit/a3d1858fce448d2b70e36ee692ef1589b74e9d3f)]: + - @wagmi/core@2.6.14 + - @wagmi/connectors@4.1.23 + +## 2.5.16 + +### Patch Changes + +- [`b80236dc`](https://github.com/wevm/wagmi/commit/b80236dc623095fe8f1e1d10957d7776fb6ab48b) Thanks [@jxom](https://github.com/jxom)! - Removed unneeded `uniqueBy` check on connectors state. + +- Updated dependencies [[`b80236dc`](https://github.com/wevm/wagmi/commit/b80236dc623095fe8f1e1d10957d7776fb6ab48b)]: + - @wagmi/core@2.6.13 + - @wagmi/connectors@4.1.22 + +## 2.5.15 + +### Patch Changes + +- [#3740](https://github.com/wevm/wagmi/pull/3740) [`3373c644`](https://github.com/wevm/wagmi/commit/3373c6444c38ef16532d18cfc351b3fe2bf2d351) Thanks [@BrickheadJohnny](https://github.com/BrickheadJohnny)! - Removed unnecessary re-renders from `useConnectorClient` and `useWalletClient`. + +- Updated dependencies [[`a59069e9`](https://github.com/wevm/wagmi/commit/a59069e9fab45dd606bb89a7f829fe94c51a5494), [`0acd3132`](https://github.com/wevm/wagmi/commit/0acd31320f534993af566be5490c2978b6184f66)]: + - @wagmi/core@2.6.12 + - @wagmi/connectors@4.1.21 + +## 2.5.14 + +### Patch Changes + +- [`e1ca4e63`](https://github.com/wevm/wagmi/commit/e1ca4e637ae6cec7f5902b0a2c0e0efc3b751a1d) Thanks [@tmm](https://github.com/tmm)! - Deprecated `normalizeChainId`. Use `Number` instead. + +- Updated dependencies [[`e1ca4e63`](https://github.com/wevm/wagmi/commit/e1ca4e637ae6cec7f5902b0a2c0e0efc3b751a1d)]: + - @wagmi/connectors@4.1.20 + - @wagmi/core@2.6.11 + +## 2.5.13 + +### Patch Changes + +- [`dbdca8fd`](https://github.com/wevm/wagmi/commit/dbdca8fd14b90c166222a66a373c1b33c06ce019) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where duplicate connectors could be instantiated if injected after page mount. + +- Updated dependencies [[`dbdca8fd`](https://github.com/wevm/wagmi/commit/dbdca8fd14b90c166222a66a373c1b33c06ce019)]: + - @wagmi/core@2.6.10 + - @wagmi/connectors@4.1.19 + +## 2.5.12 + +### Patch Changes + +- [#3612](https://github.com/wevm/wagmi/pull/3612) [`97237bb0`](https://github.com/wevm/wagmi/commit/97237bb05c30860b9b12c094e82a38ce59d9bedf) Thanks [@m1heng](https://github.com/m1heng)! - Added missing `functionName` parameter to `useWriteContract` codegen helper. + +## 2.5.11 + +### Patch Changes + +- [#3714](https://github.com/wevm/wagmi/pull/3714) [`f1628d65`](https://github.com/wevm/wagmi/commit/f1628d65f06e9ef18e6c4e2eb4f6e3ab2e700924) Thanks [@dalechyn](https://github.com/dalechyn)! - Replaced `Omit` with `UnionOmit` for `UseMutationReturnType`. + +- [#3715](https://github.com/wevm/wagmi/pull/3715) [`d56edf4f`](https://github.com/wevm/wagmi/commit/d56edf4f27c52acc7a0f57114454b0d3e22cacd6) Thanks [@jxom](https://github.com/jxom)! - Fixed SSR hydration issues. + +- Updated dependencies [[`d56edf4f`](https://github.com/wevm/wagmi/commit/d56edf4f27c52acc7a0f57114454b0d3e22cacd6)]: + - @wagmi/core@2.6.9 + - @wagmi/connectors@4.1.18 + +## 2.5.10 + +### Patch Changes + +- [#3643](https://github.com/wevm/wagmi/pull/3643) [`e46bcd47`](https://github.com/wevm/wagmi/commit/e46bcd4738a18da15b53f6612b614379c1985374) Thanks [@TateB](https://github.com/TateB)! - Fixed race condition arising from `reconnect`. + +- Updated dependencies [[`e46bcd47`](https://github.com/wevm/wagmi/commit/e46bcd4738a18da15b53f6612b614379c1985374)]: + - @wagmi/core@2.6.8 + - @wagmi/connectors@4.1.17 + +## 2.5.9 + +### Patch Changes + +- [`f5648dd2`](https://github.com/wevm/wagmi/commit/f5648dd28b3576b628f57732b89287f55acbb1c1) Thanks [@jxom](https://github.com/jxom)! - Updated `prepareTransactionRequest` types for `viem@2.8.0`. + +- [`1c1fee6a`](https://github.com/wevm/wagmi/commit/1c1fee6ab8f01f7734ac6ce05093fa8e388beb3e) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/ethereum-provider`. + +- Updated dependencies [[`b479b5e8`](https://github.com/wevm/wagmi/commit/b479b5e8a5866cba792862f22e6352c4fb566137), [`f5648dd2`](https://github.com/wevm/wagmi/commit/f5648dd28b3576b628f57732b89287f55acbb1c1), [`1c1fee6a`](https://github.com/wevm/wagmi/commit/1c1fee6ab8f01f7734ac6ce05093fa8e388beb3e), [`88a2d744`](https://github.com/wevm/wagmi/commit/88a2d744a1315908c9e54156026df3ad2435ad44)]: + - @wagmi/core@2.6.7 + - @wagmi/connectors@4.1.16 + +## 2.5.8 + +### Patch Changes + +- Updated dependencies [[`a91c0b64`](https://github.com/wevm/wagmi/commit/a91c0b64ba8b3e6537a560e69724eb601f26af27)]: + - @wagmi/core@2.6.6 + - @wagmi/connectors@4.1.15 + +## 2.5.7 + +### Patch Changes + +- [#3580](https://github.com/wevm/wagmi/pull/3580) [`c677dcd2`](https://github.com/wevm/wagmi/commit/c677dcd245dccdf69289a3d66dded237b09570a2) Thanks [@tmm](https://github.com/tmm)! - Made `useSwitchChain().chains` reactive. + +- Updated dependencies [[`ca5decdb`](https://github.com/wevm/wagmi/commit/ca5decdb712f81e3f5dab933a94b967bca5b6af4), [`c677dcd2`](https://github.com/wevm/wagmi/commit/c677dcd245dccdf69289a3d66dded237b09570a2)]: + - @wagmi/connectors@4.1.14 + - @wagmi/core@2.6.5 + +## 2.5.6 + +### Patch Changes + +- Updated dependencies [[`7c6618e6`](https://github.com/wevm/wagmi/commit/7c6618e6a0eb1ff39cf8f66b34d3ddc14be538fe), [`fa25b448`](https://github.com/wevm/wagmi/commit/fa25b4482504b4d9729a5687ea6d6dc959265bc0), [`895f28e8`](https://github.com/wevm/wagmi/commit/895f28e873af7c8eda5ca85734ff67c8979fd950)]: + - @wagmi/core@2.6.4 + - @wagmi/connectors@4.1.13 + +## 2.5.5 + +### Patch Changes + +- Updated dependencies [[`9c3b85dd`](https://github.com/wevm/wagmi/commit/9c3b85dd0a9a4a593e1d7e029345275735330e32), [`2a72214a`](https://github.com/wevm/wagmi/commit/2a72214a2901d6b6ddd39f80238aa0bd4db670a7)]: + - @wagmi/core@2.6.3 + - @wagmi/connectors@4.1.12 + +## 2.5.4 + +### Patch Changes + +- [`3f8203bd`](https://github.com/wevm/wagmi/commit/3f8203bd77fcf6b6756640b5971d09741ae3853d) Thanks [@tmm](https://github.com/tmm)! - Fixed `useBlock` parameters passthrough to Viem. + +## 2.5.3 + +### Patch Changes + +- [#3518](https://github.com/wevm/wagmi/pull/3518) [`338e857d`](https://github.com/wevm/wagmi/commit/338e857d8cb2fe85e13d9207bef14cada1c1962d) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +- Updated dependencies [[`414eb048`](https://github.com/wevm/wagmi/commit/414eb048af492caac70c0e874dfc87c30702804a), [`338e857d`](https://github.com/wevm/wagmi/commit/338e857d8cb2fe85e13d9207bef14cada1c1962d), [`338e857d`](https://github.com/wevm/wagmi/commit/338e857d8cb2fe85e13d9207bef14cada1c1962d)]: + - @wagmi/core@2.6.2 + - @wagmi/connectors@4.1.11 + +## 2.5.2 + +### Patch Changes + +- [#3433](https://github.com/wevm/wagmi/pull/3433) [`101a7dd1`](https://github.com/wevm/wagmi/commit/101a7dd131b0cae2dc25579ecab9044290efd37b) Thanks [@tmm](https://github.com/tmm)! - Fixed `useClient` and `usePublicClient` throwing when used with unconfigured `chainId`. + +- [#3510](https://github.com/wevm/wagmi/pull/3510) [`660ff80d`](https://github.com/wevm/wagmi/commit/660ff80d5b046967a446eba43ee54b8359a37d0d) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where connectors returning multiple addresses didn't checksum correctly. + +- Updated dependencies [[`660ff80d`](https://github.com/wevm/wagmi/commit/660ff80d5b046967a446eba43ee54b8359a37d0d), [`101a7dd1`](https://github.com/wevm/wagmi/commit/101a7dd131b0cae2dc25579ecab9044290efd37b)]: + - @wagmi/connectors@4.1.10 + - @wagmi/core@2.6.1 + +## 2.5.1 + +### Patch Changes + +- [#3496](https://github.com/wevm/wagmi/pull/3496) [`ba7f8a75`](https://github.com/wevm/wagmi/commit/ba7f8a758efb07664c6e401b5e7e325e7c62341b) Thanks [@tmm](https://github.com/tmm)! - Bumped dependencies. + +- Updated dependencies [[`ba7f8a75`](https://github.com/wevm/wagmi/commit/ba7f8a758efb07664c6e401b5e7e325e7c62341b), [`ba7f8a75`](https://github.com/wevm/wagmi/commit/ba7f8a758efb07664c6e401b5e7e325e7c62341b)]: + - @wagmi/connectors@4.1.9 + - @wagmi/core@2.6.0 + +## 2.5.0 + +### Minor Changes + +- [#3461](https://github.com/wevm/wagmi/pull/3461) [`ca98041d`](https://github.com/wevm/wagmi/commit/ca98041d1b39893d90246929485f4db0d1c6f9f7) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `useTransactionConfirmations` hook. + +### Patch Changes + +- Updated dependencies [[`ca98041d`](https://github.com/wevm/wagmi/commit/ca98041d1b39893d90246929485f4db0d1c6f9f7)]: + - @wagmi/core@2.5.0 + - @wagmi/connectors@4.1.8 + +## 2.4.0 + +### Minor Changes + +- [#3427](https://github.com/wevm/wagmi/pull/3427) [`370f1b4a`](https://github.com/wevm/wagmi/commit/370f1b4a3f154d181acf381c31c2e7862e22c0e4) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `usePrepareTransactionRequest` hook. + +### Patch Changes + +- Updated dependencies [[`370f1b4a`](https://github.com/wevm/wagmi/commit/370f1b4a3f154d181acf381c31c2e7862e22c0e4), [`370f1b4a`](https://github.com/wevm/wagmi/commit/370f1b4a3f154d181acf381c31c2e7862e22c0e4)]: + - @wagmi/connectors@4.1.7 + - @wagmi/core@2.4.0 + +## 2.3.1 + +### Patch Changes + +- [#3476](https://github.com/wevm/wagmi/pull/3476) [`3be5bb7b`](https://github.com/wevm/wagmi/commit/3be5bb7b0b38646e12e6da5c762ef74dff66bcc2) Thanks [@jxom](https://github.com/jxom)! - Modified persist strategy to only store "critical" properties that are needed before hydration. + +- Updated dependencies [[`3be5bb7b`](https://github.com/wevm/wagmi/commit/3be5bb7b0b38646e12e6da5c762ef74dff66bcc2)]: + - @wagmi/core@2.3.1 + - @wagmi/connectors@4.1.6 + +## 2.3.0 + +### Minor Changes + +- [#3459](https://github.com/wevm/wagmi/pull/3459) [`d950b666`](https://github.com/wevm/wagmi/commit/d950b666b56700ca039ce16cdfdf34564991e7f5) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `useEnsText` action. + +### Patch Changes + +- [#3467](https://github.com/wevm/wagmi/pull/3467) [`90ef39bb`](https://github.com/wevm/wagmi/commit/90ef39bb0f4ecb3c914d317875348e35ba0f4524) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where connectors that share the same provider instance could reconnect when they have never been connected before. + +- [`1cfb6e5a`](https://github.com/wevm/wagmi/commit/1cfb6e5a875e707abcee00dd5739e87da05e8c90) Thanks [@jxom](https://github.com/jxom)! - Bumped listener limit on WalletConnect connector. + +- Updated dependencies [[`d950b666`](https://github.com/wevm/wagmi/commit/d950b666b56700ca039ce16cdfdf34564991e7f5), [`d950b666`](https://github.com/wevm/wagmi/commit/d950b666b56700ca039ce16cdfdf34564991e7f5), [`90ef39bb`](https://github.com/wevm/wagmi/commit/90ef39bb0f4ecb3c914d317875348e35ba0f4524), [`1cfb6e5a`](https://github.com/wevm/wagmi/commit/1cfb6e5a875e707abcee00dd5739e87da05e8c90)]: + - @wagmi/core@2.3.0 + - @wagmi/connectors@5.0.0 + +## 2.2.1 + +### Patch Changes + +- [#3443](https://github.com/wevm/wagmi/pull/3443) [`007024a6`](https://github.com/wevm/wagmi/commit/007024a684ddbecf924cdc06dd6a8854fc3d5eeb) Thanks [@jmrossy](https://github.com/jmrossy)! - Bumped dependencies. + +- [#3447](https://github.com/wevm/wagmi/pull/3447) [`a02a26ad`](https://github.com/wevm/wagmi/commit/a02a26ad030d3afb78f744377d61b5c60b65d97a) Thanks [@tmm](https://github.com/tmm)! - Fixed account typing. + +- Updated dependencies [[`007024a6`](https://github.com/wevm/wagmi/commit/007024a684ddbecf924cdc06dd6a8854fc3d5eeb), [`a02a26ad`](https://github.com/wevm/wagmi/commit/a02a26ad030d3afb78f744377d61b5c60b65d97a), [`a02a26ad`](https://github.com/wevm/wagmi/commit/a02a26ad030d3afb78f744377d61b5c60b65d97a), [`007024a6`](https://github.com/wevm/wagmi/commit/007024a684ddbecf924cdc06dd6a8854fc3d5eeb)]: + - @wagmi/connectors@4.1.4 + - @wagmi/core@2.2.1 + +## 2.2.0 + +### Minor Changes + +- [#3434](https://github.com/wevm/wagmi/pull/3434) [`00bf10a4`](https://github.com/wevm/wagmi/commit/00bf10a428b0d1c5dac35ebf25b19571e033ac26) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `useBytecode` and `useStorageAt` hooks. + +- [#3408](https://github.com/wevm/wagmi/pull/3408) [`fb6c4148`](https://github.com/wevm/wagmi/commit/fb6c4148d9e9e2fccfbe74c8f343b444dc68dec5) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `useProof` hook. + +- [#3416](https://github.com/wevm/wagmi/pull/3416) [`64c073f6`](https://github.com/wevm/wagmi/commit/64c073f6c2720961e2d6aff986670b73dbfab9c3) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `useTransactionReceipt` hook. + +### Patch Changes + +- Updated dependencies [[`00bf10a4`](https://github.com/wevm/wagmi/commit/00bf10a428b0d1c5dac35ebf25b19571e033ac26), [`64c073f6`](https://github.com/wevm/wagmi/commit/64c073f6c2720961e2d6aff986670b73dbfab9c3), [`fb6c4148`](https://github.com/wevm/wagmi/commit/fb6c4148d9e9e2fccfbe74c8f343b444dc68dec5)]: + - @wagmi/core@2.2.0 + - @wagmi/connectors@5.0.0 + +## 2.1.2 + +### Patch Changes + +- [#3407](https://github.com/wevm/wagmi/pull/3407) [`e00b8205`](https://github.com/wevm/wagmi/commit/e00b82058685751637edfa9a6b2d196a12549fe7) Thanks [@jxom](https://github.com/jxom)! - Added a prelude gas estimate check to `sendTransaction`/`useSendTransaction`. + +- Updated dependencies [[`e00b8205`](https://github.com/wevm/wagmi/commit/e00b82058685751637edfa9a6b2d196a12549fe7)]: + - @wagmi/core@2.1.2 + - @wagmi/connectors@4.1.2 + +## 2.1.1 + +### Patch Changes + +- [#3402](https://github.com/wevm/wagmi/pull/3402) [`64b82282`](https://github.com/wevm/wagmi/commit/64b82282c1e57e77c25aa0814673780e4d11edd4) Thanks [@Songkeys](https://github.com/Songkeys)! - Fixed SSR cookie support for cookies that have special characters, e.g. `=`. + +- [`ec0d8b41`](https://github.com/wevm/wagmi/commit/ec0d8b4112181fefb11025e436a94a6114761d37) Thanks [@tmm](https://github.com/tmm)! - Added note to `metaMask` connector. + +- Updated dependencies [[`64b82282`](https://github.com/wevm/wagmi/commit/64b82282c1e57e77c25aa0814673780e4d11edd4), [`ec0d8b41`](https://github.com/wevm/wagmi/commit/ec0d8b4112181fefb11025e436a94a6114761d37)]: + - @wagmi/core@2.1.1 + - @wagmi/connectors@4.1.1 + +## 2.1.0 + +### Minor Changes + +- [#3387](https://github.com/wevm/wagmi/pull/3387) [`c9cd302e`](https://github.com/wevm/wagmi/commit/c9cd302e1c65c980deaee2e12567c2a8ec08b399) Thanks [@marthendalnunes](https://github.com/marthendalnunes)! - Added `useCall` hook. + +### Patch Changes + +- Updated dependencies [[`c9cd302e`](https://github.com/wevm/wagmi/commit/c9cd302e1c65c980deaee2e12567c2a8ec08b399)]: + - @wagmi/core@2.1.0 + - @wagmi/connectors@5.0.0 + +## 2.0.3 + +### Patch Changes + +- [#3384](https://github.com/wevm/wagmi/pull/3384) [`ee868c33`](https://github.com/wevm/wagmi/commit/ee868c3385dae511230b6ddcb5627c1293cc1844) Thanks [@tmm](https://github.com/tmm)! - Fixed connectors not bubbling error when connecting with `chainId` and subsequent user rejection. + +- Updated dependencies [[`ee868c33`](https://github.com/wevm/wagmi/commit/ee868c3385dae511230b6ddcb5627c1293cc1844)]: + - @wagmi/connectors@4.0.2 + - @wagmi/core@2.0.2 + +## 2.0.2 + +### Patch Changes + +- [#3379](https://github.com/wevm/wagmi/pull/3379) [`30a186e5`](https://github.com/wevm/wagmi/commit/30a186e53d1135657d04f72f40d1c27186025370) Thanks [@tmm](https://github.com/tmm)! - Fixed `useConnect` error getting unset. + +## 2.0.1 + +### Major Changes + +- [#3333](https://github.com/wevm/wagmi/pull/3333) [`b3a0baaa`](https://github.com/wevm/wagmi/commit/b3a0baaaee7decf750d376aab2502cd33ca4825a) Thanks [@tmm](https://github.com/tmm)! - Wagmi 2.0 featuring: + + - Full TanStack Query support + queryKeys + - Connect multiple connectors + - Switch chains while disconnected + - EIP-6963 enabled + - Strongly typed chainId and chain properties + - Smaller bundle size + - Miscellaneous improvements and bug fixes + + [Breaking Changes & Migration Guide](https://wagmi.sh/react/guides/migrate-from-v1-to-v2) + +### Patch Changes + +- Updated dependencies [[`b3a0baaa`](https://github.com/wevm/wagmi/commit/b3a0baaaee7decf750d376aab2502cd33ca4825a), [`b3a0baaa`](https://github.com/wevm/wagmi/commit/b3a0baaaee7decf750d376aab2502cd33ca4825a)]: + - @wagmi/connectors@4.0.0 + - @wagmi/core@2.0.0 + +## 1.4.13 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.4.13 + +## 1.4.12 + +### Patch Changes + +- [`53ca1f7e`](https://github.com/wevm/wagmi/commit/53ca1f7eb411d912e11fcce7e03bd61ed067959c) Thanks [@tmm](https://github.com/tmm)! - Removed LedgerConnector due to security vulnerability + +- Updated dependencies [[`53ca1f7e`](https://github.com/wevm/wagmi/commit/53ca1f7eb411d912e11fcce7e03bd61ed067959c)]: + - @wagmi/core@1.4.12 + +## 1.4.11 + +### Patch Changes + +- [#3299](https://github.com/wevm/wagmi/pull/3299) [`b02020b3`](https://github.com/wevm/wagmi/commit/b02020b3724e0228198f35817611bb063295906e) Thanks [@dasanra](https://github.com/dasanra)! - Fixed issue with [Safe SDK](https://github.com/wevm/viem/issues/579) by bumping `@safe-global/safe-apps-provider@0.18.1` + +- Updated dependencies [[`b02020b3`](https://github.com/wevm/wagmi/commit/b02020b3724e0228198f35817611bb063295906e)]: + - @wagmi/core@1.4.11 + +## 1.4.10 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.4.10 + +## 1.4.9 + +### Patch Changes + +- [#3276](https://github.com/wevm/wagmi/pull/3276) [`83223a06`](https://github.com/wevm/wagmi/commit/83223a0659e2f675d897a1d3374c7af752c16abf) Thanks [@glitch-txs](https://github.com/glitch-txs)! - Removed required namespaces from WalletConnect connector + +- Updated dependencies [[`83223a06`](https://github.com/wevm/wagmi/commit/83223a0659e2f675d897a1d3374c7af752c16abf)]: + - @wagmi/core@1.4.9 + +## 1.4.8 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.4.8 + +## 1.4.7 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.4.7 + +## 1.4.6 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.4.6 + +## 1.4.5 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.4.5 + +## 1.4.4 + +### Patch Changes + +- [#3125](https://github.com/wagmi-dev/wagmi/pull/3125) [`725e73fe`](https://github.com/wagmi-dev/wagmi/commit/725e73feb9143dbaa6d540bb76d2009cef29da0b) Thanks [@lukasrosario](https://github.com/lukasrosario)! - Fixed an issue where `dataSuffix` was not being passed down into viem's `simulateContract`, causing the data to be omitted from requests. + +- Updated dependencies [[`725e73fe`](https://github.com/wagmi-dev/wagmi/commit/725e73feb9143dbaa6d540bb76d2009cef29da0b)]: + - @wagmi/core@1.4.4 + +## 1.4.3 + +### Patch Changes + +- [#3076](https://github.com/wagmi-dev/wagmi/pull/3076) [`4c36831b`](https://github.com/wagmi-dev/wagmi/commit/4c36831b7aa44d03b5c0decf64dcd20faae28a67) Thanks [@jxom](https://github.com/jxom)! - Pass `chain` to viem `sendTransaction`/`writeContract`. + +- [#3006](https://github.com/wagmi-dev/wagmi/pull/3006) [`f2ddce23`](https://github.com/wagmi-dev/wagmi/commit/f2ddce23324aff0a91e066100918dac552dc3b4a) Thanks [@jxom](https://github.com/jxom)! - Changed `normalize` to a dynamic import. + +- Updated dependencies [[`4c36831b`](https://github.com/wagmi-dev/wagmi/commit/4c36831b7aa44d03b5c0decf64dcd20faae28a67), [`f2ddce23`](https://github.com/wagmi-dev/wagmi/commit/f2ddce23324aff0a91e066100918dac552dc3b4a)]: + - @wagmi/core@1.4.3 + +## 1.4.2 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.4.2 + +## 1.4.1 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.4.1 + +## 1.4.0 + +### Minor Changes + +- [#2956](https://github.com/wagmi-dev/wagmi/pull/2956) [`2abeb285`](https://github.com/wagmi-dev/wagmi/commit/2abeb285674af3e539cc2550b1f5027b1eb0c895) Thanks [@tmm](https://github.com/tmm)! - Replaced `@wagmi/chains` with `viem/chains`. + +### Patch Changes + +- Updated dependencies [[`2abeb285`](https://github.com/wagmi-dev/wagmi/commit/2abeb285674af3e539cc2550b1f5027b1eb0c895)]: + - @wagmi/core@1.4.0 + +## 1.3.11 + +### Patch Changes + +- [`557e6400`](https://github.com/wagmi-dev/wagmi/commit/557e6400b9cef3b2c5131739143956c37d7c934a) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`557e6400`](https://github.com/wagmi-dev/wagmi/commit/557e6400b9cef3b2c5131739143956c37d7c934a)]: + - @wagmi/core@1.3.10 + +## 1.3.10 + +### Patch Changes + +- [`247c5d11`](https://github.com/wagmi-dev/wagmi/commit/247c5d113e83acf3a6894264c00d4b125d455107) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`247c5d11`](https://github.com/wagmi-dev/wagmi/commit/247c5d113e83acf3a6894264c00d4b125d455107)]: + - @wagmi/core@1.3.9 + +## 1.3.9 + +### Patch Changes + +- [#2741](https://github.com/wagmi-dev/wagmi/pull/2741) [`5b1453d9`](https://github.com/wagmi-dev/wagmi/commit/5b1453d95973ed51f1c235a919fffb707eab9b70) Thanks [@jxom](https://github.com/jxom)! - Updated references + +- Updated dependencies [[`5b1453d9`](https://github.com/wagmi-dev/wagmi/commit/5b1453d95973ed51f1c235a919fffb707eab9b70)]: + - @wagmi/core@1.3.8 + +## 1.3.8 + +### Patch Changes + +- [#2700](https://github.com/wagmi-dev/wagmi/pull/2700) [`30118e97`](https://github.com/wagmi-dev/wagmi/commit/30118e979b1b00302e035f31f58c15d1aed911d5) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`30118e97`](https://github.com/wagmi-dev/wagmi/commit/30118e979b1b00302e035f31f58c15d1aed911d5)]: + - @wagmi/core@1.3.7 + +## 1.3.7 + +### Patch Changes + +- [`7ad2fdb8`](https://github.com/wagmi-dev/wagmi/commit/7ad2fdb81c7734d0c8107670800c68390e3bad99) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`7ad2fdb8`](https://github.com/wagmi-dev/wagmi/commit/7ad2fdb81c7734d0c8107670800c68390e3bad99)]: + - @wagmi/core@1.3.6 + +## 1.3.6 + +### Patch Changes + +- [`aab63fc1`](https://github.com/wagmi-dev/wagmi/commit/aab63fc1f8949004573978ecd8574fada3360758) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`aab63fc1`](https://github.com/wagmi-dev/wagmi/commit/aab63fc1f8949004573978ecd8574fada3360758)]: + - @wagmi/core@1.3.5 + +## 1.3.5 + +### Patch Changes + +- [#2669](https://github.com/wagmi-dev/wagmi/pull/2669) [`db75c459`](https://github.com/wagmi-dev/wagmi/commit/db75c4593b9c46970dc9d3c96d7adafc76878fc3) Thanks [@llllvvuu](https://github.com/llllvvuu)! - Modified `useAccount` and `useNetwork` to be reactive of wagmi Config (`config`). + +## 1.3.4 + +### Patch Changes + +- [`b056f809`](https://github.com/wagmi-dev/wagmi/commit/b056f8095674d4addc6ecd09adf6001fe52e2b15) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `onConnect` was not being called when multiple instances of `useAccount` existed. + +- [`22246d98`](https://github.com/wagmi-dev/wagmi/commit/22246d9884277d28ccad6ca2d9529b96b67d47fc) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`22246d98`](https://github.com/wagmi-dev/wagmi/commit/22246d9884277d28ccad6ca2d9529b96b67d47fc)]: + - @wagmi/core@1.3.4 + +## 1.3.3 + +### Patch Changes + +- [`1946aa43`](https://github.com/wagmi-dev/wagmi/commit/1946aa43a65b684ef41b7b4c43c67bf29c13e854) Thanks [@jxom](https://github.com/jxom)! - Updated references + +- Updated dependencies [[`1946aa43`](https://github.com/wagmi-dev/wagmi/commit/1946aa43a65b684ef41b7b4c43c67bf29c13e854)]: + - @wagmi/core@1.3.3 + +## 1.3.2 + +### Patch Changes + +- [`e86d0940`](https://github.com/wagmi-dev/wagmi/commit/e86d09409bb20b64d24e1263abcf0291314f03c7) Thanks [@jxom](https://github.com/jxom)! - Updated references + +- Updated dependencies [[`e86d0940`](https://github.com/wagmi-dev/wagmi/commit/e86d09409bb20b64d24e1263abcf0291314f03c7)]: + - @wagmi/core@1.3.2 + +## 1.3.1 + +### Patch Changes + +- [`964042fa`](https://github.com/wagmi-dev/wagmi/commit/964042fa94d682977923c595820c58283fb9244a) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`964042fa`](https://github.com/wagmi-dev/wagmi/commit/964042fa94d682977923c595820c58283fb9244a)]: + - @wagmi/core@1.3.1 + +## 1.3.0 + +### Minor Changes + +- [#2619](https://github.com/wagmi-dev/wagmi/pull/2619) [`0d79748c`](https://github.com/wagmi-dev/wagmi/commit/0d79748cec2b6ac2410ad2c9816cc662f2b70962) Thanks [@jxom](https://github.com/jxom)! - Updated references: + - Updated `@safe-global/safe-apps-sdk` to `^8.0.0` (the one with `viem` support) + +### Patch Changes + +- Updated dependencies [[`0d79748c`](https://github.com/wagmi-dev/wagmi/commit/0d79748cec2b6ac2410ad2c9816cc662f2b70962)]: + - @wagmi/core@1.3.0 + +## 1.2.2 + +### Patch Changes + +- [#2611](https://github.com/wagmi-dev/wagmi/pull/2611) [`6d1ed7a1`](https://github.com/wagmi-dev/wagmi/commit/6d1ed7a156729b4df5d66fef3ae9a8b5762a2d34) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`6d1ed7a1`](https://github.com/wagmi-dev/wagmi/commit/6d1ed7a156729b4df5d66fef3ae9a8b5762a2d34)]: + - @wagmi/core@1.2.2 + +## 1.2.1 + +### Patch Changes + +- [#2589](https://github.com/wagmi-dev/wagmi/pull/2589) [`9680c347`](https://github.com/wagmi-dev/wagmi/commit/9680c347476500d28ceca20d23eeaed7931cb6e0) Thanks [@jxom](https://github.com/jxom)! - Fixed `writeContract` parameters to be compatible with `prepareWriteContract`. + +- [#2587](https://github.com/wagmi-dev/wagmi/pull/2587) [`cfff9994`](https://github.com/wagmi-dev/wagmi/commit/cfff999459384ac644ff7e62f53a7b787cf37507) Thanks [@jxom](https://github.com/jxom)! - Updated references + +- Updated dependencies [[`9680c347`](https://github.com/wagmi-dev/wagmi/commit/9680c347476500d28ceca20d23eeaed7931cb6e0), [`cfff9994`](https://github.com/wagmi-dev/wagmi/commit/cfff999459384ac644ff7e62f53a7b787cf37507)]: + - @wagmi/core@1.2.1 + +## 1.2.0 + +### Minor Changes + +- [#2536](https://github.com/wagmi-dev/wagmi/pull/2536) [`85e9760a`](https://github.com/wagmi-dev/wagmi/commit/85e9760a140cb169ac6236d9466b96e2105dd193) Thanks [@tmm](https://github.com/tmm)! - Changed `Address` type import from ABIType to viem. + +### Patch Changes + +- [#2539](https://github.com/wagmi-dev/wagmi/pull/2539) [`96319c64`](https://github.com/wagmi-dev/wagmi/commit/96319c640b9d07b375821c08a5c213355d8c290b) Thanks [@jxom](https://github.com/jxom)! - Updated references + +- Updated dependencies [[`85e9760a`](https://github.com/wagmi-dev/wagmi/commit/85e9760a140cb169ac6236d9466b96e2105dd193), [`96319c64`](https://github.com/wagmi-dev/wagmi/commit/96319c640b9d07b375821c08a5c213355d8c290b)]: + - @wagmi/core@1.2.0 + +## 1.1.1 + +### Patch Changes + +- [`02b98a9f`](https://github.com/wagmi-dev/wagmi/commit/02b98a9f9b2c503a47af4a8967e0202b5db21787) Thanks [@jxom](https://github.com/jxom)! - Updated `viem` peer dependency. + +- [`02b98a9f`](https://github.com/wagmi-dev/wagmi/commit/02b98a9f9b2c503a47af4a8967e0202b5db21787) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`02b98a9f`](https://github.com/wagmi-dev/wagmi/commit/02b98a9f9b2c503a47af4a8967e0202b5db21787), [`02b98a9f`](https://github.com/wagmi-dev/wagmi/commit/02b98a9f9b2c503a47af4a8967e0202b5db21787)]: + - @wagmi/core@1.1.1 + +## 1.1.0 + +### Minor Changes + +- [#2482](https://github.com/wagmi-dev/wagmi/pull/2482) [`8764b54a`](https://github.com/wagmi-dev/wagmi/commit/8764b54aab68020063946112e8fe52aff650c99c) Thanks [@tmm](https://github.com/tmm)! - Bumped minimum TypeScript version to v5.0.4. + +### Patch Changes + +- [#2471](https://github.com/wagmi-dev/wagmi/pull/2471) [`74099cef`](https://github.com/wagmi-dev/wagmi/commit/74099cefd922317641529f7881a4c8a740d62cbe) Thanks [@iuriiiurevich](https://github.com/iuriiiurevich)! - Added `keepPreviousData` prop to `useContractRead`. + +- [#2484](https://github.com/wagmi-dev/wagmi/pull/2484) [`3adf1f4f`](https://github.com/wagmi-dev/wagmi/commit/3adf1f4feab863cb7b5d52c81ad46f7e4eb56f09) Thanks [@jxom](https://github.com/jxom)! - Updated `abitype` to 0.8.7 + +- [#2484](https://github.com/wagmi-dev/wagmi/pull/2484) [`3adf1f4f`](https://github.com/wagmi-dev/wagmi/commit/3adf1f4feab863cb7b5d52c81ad46f7e4eb56f09) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- [`01d4a6ed`](https://github.com/wagmi-dev/wagmi/commit/01d4a6ed53110712692599095d94f04dfb5b6e38) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `useInvalidateOnBlock`'s `onBlock` was being called on every render. + +- Updated dependencies [[`8764b54a`](https://github.com/wagmi-dev/wagmi/commit/8764b54aab68020063946112e8fe52aff650c99c), [`3adf1f4f`](https://github.com/wagmi-dev/wagmi/commit/3adf1f4feab863cb7b5d52c81ad46f7e4eb56f09), [`3adf1f4f`](https://github.com/wagmi-dev/wagmi/commit/3adf1f4feab863cb7b5d52c81ad46f7e4eb56f09)]: + - @wagmi/core@1.1.0 + +## 1.0.9 + +### Patch Changes + +- [#2446](https://github.com/wagmi-dev/wagmi/pull/2446) [`899d8c06`](https://github.com/wagmi-dev/wagmi/commit/899d8c0698e6cc958ca8ad9ec586883edf20516e) Thanks [@iuriiiurevich](https://github.com/iuriiiurevich)! - Added `cancelRefetch: false` to `useInvalidateOnBlock`. + +## 1.0.8 + +### Patch Changes + +- [#2441](https://github.com/wagmi-dev/wagmi/pull/2441) [`326edee4`](https://github.com/wagmi-dev/wagmi/commit/326edee4bc85db84a7a4e3768e33785849ab8d8e) Thanks [@tmm](https://github.com/tmm)! - Fixed internal type issue + +- Updated dependencies [[`326edee4`](https://github.com/wagmi-dev/wagmi/commit/326edee4bc85db84a7a4e3768e33785849ab8d8e)]: + - @wagmi/core@1.0.8 + +## 1.0.7 + +### Patch Changes + +- [#2433](https://github.com/wagmi-dev/wagmi/pull/2433) [`54fcff5f`](https://github.com/wagmi-dev/wagmi/commit/54fcff5f02f6933bbbe045ee0c83c5a78b6bba49) Thanks [@jxom](https://github.com/jxom)! - Added ability to pass an `account` to `useContractWrite`/`usePrepareContractWrite`. + +- Updated dependencies [[`54fcff5f`](https://github.com/wagmi-dev/wagmi/commit/54fcff5f02f6933bbbe045ee0c83c5a78b6bba49)]: + - @wagmi/core@1.0.7 + +## 1.0.6 + +### Patch Changes + +- [`ca2e1e96`](https://github.com/wagmi-dev/wagmi/commit/ca2e1e96149b87a7dc42c9db07e1f1ad2bb02c4a) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- [#2401](https://github.com/wagmi-dev/wagmi/pull/2401) [`0f9dc875`](https://github.com/wagmi-dev/wagmi/commit/0f9dc875e90cfdd7a2028e04b7204caf9ea313b2) Thanks [@jxom](https://github.com/jxom)! - Exposed `account` on `readContract`/`useContractRead`. + +- Updated dependencies [[`ca2e1e96`](https://github.com/wagmi-dev/wagmi/commit/ca2e1e96149b87a7dc42c9db07e1f1ad2bb02c4a), [`0f9dc875`](https://github.com/wagmi-dev/wagmi/commit/0f9dc875e90cfdd7a2028e04b7204caf9ea313b2)]: + - @wagmi/core@1.0.6 + +## 1.0.5 + +### Patch Changes + +- [`90e2b3b3`](https://github.com/wagmi-dev/wagmi/commit/90e2b3b39efe0585fe28645ac2264109be17362a) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`90e2b3b3`](https://github.com/wagmi-dev/wagmi/commit/90e2b3b39efe0585fe28645ac2264109be17362a)]: + - @wagmi/core@1.0.5 + +## 1.0.4 + +### Patch Changes + +- [#2344](https://github.com/wagmi-dev/wagmi/pull/2344) [`8a725458`](https://github.com/wagmi-dev/wagmi/commit/8a72545853ae1024acd9efd18c06142e8c6c5750) Thanks [@jxom](https://github.com/jxom)! - Added gas estimation back into `prepareSendTransaction`. + +- Updated dependencies [[`8a725458`](https://github.com/wagmi-dev/wagmi/commit/8a72545853ae1024acd9efd18c06142e8c6c5750)]: + - @wagmi/core@1.0.4 + +## 1.0.3 + +### Patch Changes + +- [#2338](https://github.com/wagmi-dev/wagmi/pull/2338) [`92bfdc2c`](https://github.com/wagmi-dev/wagmi/commit/92bfdc2c744539558ba93c95f140b46ad331cee4) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where synchronous switch chain behavior (WalletConnect v2) would encounter chain id race conditions in `watchWalletClient`. + +- Updated dependencies [[`92bfdc2c`](https://github.com/wagmi-dev/wagmi/commit/92bfdc2c744539558ba93c95f140b46ad331cee4)]: + - @wagmi/core@1.0.3 + +## 1.0.2 + +### Patch Changes + +- Updated dependencies [[`09a4fd38`](https://github.com/wevm/wagmi/commit/09a4fd38f44eb176797925fd85314be17b610cd4)]: + - @wagmi/core@1.0.2 + +## 1.0.1 + +### Patch Changes + +- [`ea651cd7`](https://github.com/wevm/wagmi/commit/ea651cd7fc75b7866272605467db11fd6e1d81af) Thanks [@jxom](https://github.com/jxom)! - Downgraded abitype. + +- Updated dependencies [[`ea651cd7`](https://github.com/wevm/wagmi/commit/ea651cd7fc75b7866272605467db11fd6e1d81af)]: + - @wagmi/core@1.0.1 + +## 1.0.0 + +### Major Changes + +- [#2235](https://github.com/wevm/wagmi/pull/2235) [`5be0655c`](https://github.com/wevm/wagmi/commit/5be0655c8e48b25d38009022461fbf611af54349) Thanks [@jxom](https://github.com/jxom)! - Released v1. Read [Migration Guide](https://next.wagmi.sh/react/migration-guide#1xx-breaking-changes). + +### Patch Changes + +- Updated dependencies [[`5be0655c`](https://github.com/wevm/wagmi/commit/5be0655c8e48b25d38009022461fbf611af54349)]: + - @wagmi/core@1.0.0 + +## 1.0.0-next.9 + +### Patch Changes + +- Fixed `useContractEvent` effect dependencies. + +## 1.0.0-next.8 + +### Patch Changes + +- Added "use client" banner + +## 1.0.0-next.7 + +### Major Changes + +- [#2235](https://github.com/wevm/wagmi/pull/2235) [`708b2ce2`](https://github.com/wevm/wagmi/commit/708b2ce26efa8d3d910806a97cea5171dabc65de) Thanks [@jxom](https://github.com/jxom)! - Added `config.setPublicClient` & `config.setWebSocketPublicClient` + +- Updated references. + +### Patch Changes + +- Updated dependencies [[`708b2ce2`](https://github.com/wevm/wagmi/commit/708b2ce26efa8d3d910806a97cea5171dabc65de)]: + - @wagmi/core@1.0.0-next.7 + +## 1.0.0-next.6 + +### Major Changes + +- Added `config.setConnectors` + +### Patch Changes + +- Updated dependencies [[`708b2ce2`](https://github.com/wevm/wagmi/commit/708b2ce26efa8d3d910806a97cea5171dabc65de)]: + - @wagmi/core@1.0.0-next.6 + +## 1.0.0-next.5 + +### Major Changes + +- [#2235](https://github.com/wevm/wagmi/pull/2235) [`708b2ce2`](https://github.com/wevm/wagmi/commit/708b2ce26efa8d3d910806a97cea5171dabc65de) Thanks [@jxom](https://github.com/jxom)! - Added `config.setPublicClient` & `config.setWebSocketPublicClient` + +### Patch Changes + +- Updated dependencies [[`708b2ce2`](https://github.com/wevm/wagmi/commit/708b2ce26efa8d3d910806a97cea5171dabc65de)]: + - @wagmi/core@1.0.0-next.5 + +## 1.0.0-next.4 + +### Major Changes + +- Updated viem. + Removed `goerli` export from main entrypoint. + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.0.0-next.4 + +## 1.0.0-next.3 + +### Major Changes + +- Updated references. + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.0.0-next.3 + +## 1.0.0-next.2 + +### Major Changes + +- **Breaking:** Renamed `createClient` to `createConfig` +- **Breaking:** Renamed `useClient` to `useConfig` +- **Breaking:** Removed `request` as an argument to `usePrepareSendTransaction` & `useSendTransaction`. Arguments now belong on the root level of the Hook. + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.0.0-next.2 + +## 1.0.0-next.1 + +### Major Changes + +- updated viem + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@1.0.0-next.1 + +## 1.0.0-next.0 + +### Major Changes + +- [`a7dda00c`](https://github.com/wevm/wagmi/commit/a7dda00c5b546f8b2c42b527e4d9ac1b9e9ab1fb) Thanks [@jxom](https://github.com/jxom)! - Released v1. + +### Patch Changes + +- Updated dependencies [[`a7dda00c`](https://github.com/wevm/wagmi/commit/a7dda00c5b546f8b2c42b527e4d9ac1b9e9ab1fb)]: + - @wagmi/core@1.0.0-next.0 + +## 0.12.13 + +### Patch Changes + +- [#2270](https://github.com/wevm/wagmi/pull/2270) [`6d1fa9df`](https://github.com/wevm/wagmi/commit/6d1fa9df790287729c3b33d4f01fd23c2f8153f1) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`6d1fa9df`](https://github.com/wevm/wagmi/commit/6d1fa9df790287729c3b33d4f01fd23c2f8153f1)]: + - @wagmi/core@0.10.11 + +## 0.12.12 + +### Patch Changes + +- [#2208](https://github.com/wevm/wagmi/pull/2208) [`cfc696d8`](https://github.com/wevm/wagmi/commit/cfc696d83c6f768a2e1a29c5197efeed7f1d40a1) Thanks [@bangtoven](https://github.com/bangtoven)! - Bumped references to apply coinbase wallet sdk updates + +- Updated dependencies [[`cfc696d8`](https://github.com/wevm/wagmi/commit/cfc696d83c6f768a2e1a29c5197efeed7f1d40a1)]: + - @wagmi/core@0.10.10 + +## 0.12.11 + +### Patch Changes + +- [#2203](https://github.com/wevm/wagmi/pull/2203) [`a4ca4b05`](https://github.com/wevm/wagmi/commit/a4ca4b05c5bd20c20c5d0741bfb18f2c798b9529) Thanks [@tmm](https://github.com/tmm)! - Downgraded abitype. + +## 0.12.10 + +### Patch Changes + +- [#2143](https://github.com/wevm/wagmi/pull/2143) [`26dc5326`](https://github.com/wevm/wagmi/commit/26dc53260fde1d3278018c0b20a6d48a093d9427) Thanks [@tmm](https://github.com/tmm)! - Exported Sepolia Chain. + +- [#2146](https://github.com/wevm/wagmi/pull/2146) [`21b6842e`](https://github.com/wevm/wagmi/commit/21b6842e8c296a0bbe71ebe0780d898abc4cf4a8) Thanks [@tmm](https://github.com/tmm)! - Bumped references + +- Updated dependencies [[`26dc5326`](https://github.com/wevm/wagmi/commit/26dc53260fde1d3278018c0b20a6d48a093d9427), [`21b6842e`](https://github.com/wevm/wagmi/commit/21b6842e8c296a0bbe71ebe0780d898abc4cf4a8)]: + - @wagmi/core@0.10.9 + +## 0.12.9 + +### Patch Changes + +- [#2120](https://github.com/wevm/wagmi/pull/2120) [`664c2b16`](https://github.com/wevm/wagmi/commit/664c2b1690bdce1ad7a619ac8f673c168dec6529) Thanks [@jxom](https://github.com/jxom)! - Bumped React Query & ABIType dependencies + +## 0.12.8 + +### Patch Changes + +- [#2099](https://github.com/wevm/wagmi/pull/2099) [`f1fee5b3`](https://github.com/wevm/wagmi/commit/f1fee5b30a1bd13b5e66118bf9cdc44b0dc003a1) Thanks [@jxom](https://github.com/jxom)! - Added chains: + + - `nexi` + - `polygonZkEvm` + - `xdc` + - `xdcTestnet` + +- [#2085](https://github.com/wevm/wagmi/pull/2085) [`7d64e3f5`](https://github.com/wevm/wagmi/commit/7d64e3f538a6149777bfa84ea9435769b2a7db58) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where multicall would not throw if the target chain was not configured on the wagmi client. + +- Updated dependencies [[`f1fee5b3`](https://github.com/wevm/wagmi/commit/f1fee5b30a1bd13b5e66118bf9cdc44b0dc003a1), [`7d64e3f5`](https://github.com/wevm/wagmi/commit/7d64e3f538a6149777bfa84ea9435769b2a7db58)]: + - @wagmi/core@0.10.8 + +## 0.12.7 + +### Patch Changes + +- [#2082](https://github.com/wevm/wagmi/pull/2082) [`2ccc8a25`](https://github.com/wevm/wagmi/commit/2ccc8a255e93f0a2bb7b22101656b3905ec59abd) Thanks [@jxom](https://github.com/jxom)! - Updated references. + +- Updated dependencies [[`2ccc8a25`](https://github.com/wevm/wagmi/commit/2ccc8a255e93f0a2bb7b22101656b3905ec59abd)]: + - @wagmi/core@0.10.7 + +## 0.12.6 + +### Patch Changes + +- [#2056](https://github.com/wevm/wagmi/pull/2056) [`944f6513`](https://github.com/wevm/wagmi/commit/944f6513adf09a6f0b3bd34f591d3bbd1f1ffd2e) Thanks [@tmm](https://github.com/tmm)! - Bumped references. + +- Updated dependencies [[`944f6513`](https://github.com/wevm/wagmi/commit/944f6513adf09a6f0b3bd34f591d3bbd1f1ffd2e)]: + - @wagmi/core@0.10.6 + +## 0.12.5 + +### Patch Changes + +- [#2053](https://github.com/wevm/wagmi/pull/2053) [`665df1bf`](https://github.com/wevm/wagmi/commit/665df1bf2afccb533102069def395e19fb7194dd) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where you add a new chain to MetaMask, but the switch after is rejected. + +- Updated dependencies [[`665df1bf`](https://github.com/wevm/wagmi/commit/665df1bf2afccb533102069def395e19fb7194dd)]: + - @wagmi/core@0.10.5 + +## 0.12.4 + +### Patch Changes + +- [#2046](https://github.com/wevm/wagmi/pull/2046) [`90d8e9b8`](https://github.com/wevm/wagmi/commit/90d8e9b87962b72c54311649537e91a953660f9b) Thanks [@tmm](https://github.com/tmm)! - Exported internal type. + +- Updated dependencies [[`90d8e9b8`](https://github.com/wevm/wagmi/commit/90d8e9b87962b72c54311649537e91a953660f9b)]: + - @wagmi/core@0.10.4 + +## 0.12.3 + +### Patch Changes + +- [#2039](https://github.com/wevm/wagmi/pull/2039) [`bac893ab`](https://github.com/wevm/wagmi/commit/bac893ab26012d4d8741c4f80e8b8813aee26f0c) Thanks [@tmm](https://github.com/tmm)! - Updated references. + +- [#2043](https://github.com/wevm/wagmi/pull/2043) [`49a58320`](https://github.com/wevm/wagmi/commit/49a58320ab5f1f13bc4de25abcc028c8335e98f0) Thanks [@tmm](https://github.com/tmm)! - Removed `InjectedConnector` `shimChainChangedDisconnect` shim (no longer necessary). + +- [#2042](https://github.com/wevm/wagmi/pull/2042) [`e7ac7afc`](https://github.com/wevm/wagmi/commit/e7ac7afccb005e8d208c78d55b1fec979b8522a6) Thanks [@tmm](https://github.com/tmm)! - Fixed exposed types that weren't passed down. + +- Updated dependencies [[`bac893ab`](https://github.com/wevm/wagmi/commit/bac893ab26012d4d8741c4f80e8b8813aee26f0c), [`49a58320`](https://github.com/wevm/wagmi/commit/49a58320ab5f1f13bc4de25abcc028c8335e98f0)]: + - @wagmi/core@0.10.3 + +## 0.12.2 + +### Patch Changes + +- [#2016](https://github.com/wevm/wagmi/pull/2016) [`06bf61de`](https://github.com/wevm/wagmi/commit/06bf61dee6d2920777bd9392491e6b7aedebe7ab) Thanks [@jxom](https://github.com/jxom)! - Added chains: + + - `boba` + - `chronos` + - `crossbell` + - `dfk` + - `dogechain` + - `flare` + - `flareTestnet` + - `klaytn` + - `scrollTestnet` + - `shardeumSphinx` + - `skaleCalypso` + - `skaleCalypsoTestnet` + - `skaleChaosTestnet` + - `skaleCryptoBlades` + - `skaleCryptoColosseum` + - `skaleEuropa` + - `skaleEuropaTestnet` + - `skaleExorde` + - `skaleHumanProtocol` + - `skaleNebula` + - `skaleNebulaTestnet` + - `skaleRazor` + - `skaleTitan` + - `skaleTitanTestnet` + - `songbird` + - `songbirdTestnet` + - `titan` + - `titanTestnet` + - `wanchain` + - `wanchainTestnet` + +- [#2016](https://github.com/wevm/wagmi/pull/2016) [`06bf61de`](https://github.com/wevm/wagmi/commit/06bf61dee6d2920777bd9392491e6b7aedebe7ab) Thanks [@jxom](https://github.com/jxom)! - Updated references/ submodule. + +- Updated dependencies [[`06bf61de`](https://github.com/wevm/wagmi/commit/06bf61dee6d2920777bd9392491e6b7aedebe7ab), [`06bf61de`](https://github.com/wevm/wagmi/commit/06bf61dee6d2920777bd9392491e6b7aedebe7ab)]: + - @wagmi/core@0.10.2 + +## 0.12.0 + +### Minor Changes + +- [#1902](https://github.com/wevm/wagmi/pull/1902) [`0994e896`](https://github.com/wevm/wagmi/commit/0994e8966349b8811db0a5886db3831dafc99245) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** Removed the `version` config option for `WalletConnectConnector`. + + `WalletConnectConnector` now uses WalletConnect v2 by default. WalletConnect v1 is now `WalletConnectLegacyConnector`. + + ### WalletConnect v2 + + ```diff + import { WalletConnectConnector } from 'wagmi/connectors/walletConnect' + + const connector = new WalletConnectConnector({ + options: { + - version: '2', + projectId: 'abc', + }, + }) + ``` + + ### WalletConnect v1 + + ```diff + -import { WalletConnectConnector } from 'wagmi/connectors/walletConnect' + +import { WalletConnectConnector } from 'wagmi/connectors/walletConnectLegacy' + + -const connector = new WalletConnectConnector({ + +const connector = new WalletConnectLegacyConnector({ + options: { + qrcode: true, + }, + }) + ``` + +### Patch Changes + +- Updated dependencies [[`0994e896`](https://github.com/wevm/wagmi/commit/0994e8966349b8811db0a5886db3831dafc99245)]: + - @wagmi/core@0.10.0 + +## 0.11.7 + +### Patch Changes + +- [#1907](https://github.com/wevm/wagmi/pull/1907) [`cc4e74ee`](https://github.com/wevm/wagmi/commit/cc4e74ee19665eccb3767052dab6ab956ff4e676) Thanks [@jxom](https://github.com/jxom)! - Added the following chains to the `wagmi/chains` entrypoint: + + - `baseGoerli` + - `harmonyOne` + - `polygonZkEvmTestnet` + +- Updated dependencies [[`cc4e74ee`](https://github.com/wevm/wagmi/commit/cc4e74ee19665eccb3767052dab6ab956ff4e676)]: + - @wagmi/core@0.9.7 + +## 0.11.6 + +### Patch Changes + +- [#1882](https://github.com/wevm/wagmi/pull/1882) [`282cc1b0`](https://github.com/wevm/wagmi/commit/282cc1b02003684d582cea411b11792a59c26fd0) Thanks [@tmm](https://github.com/tmm)! - Updated references. + +- Updated dependencies [[`282cc1b0`](https://github.com/wevm/wagmi/commit/282cc1b02003684d582cea411b11792a59c26fd0)]: + - @wagmi/core@0.9.6 + +## 0.11.5 + +### Patch Changes + +- [#1812](https://github.com/wevm/wagmi/pull/1812) [`c7fd7fbd`](https://github.com/wevm/wagmi/commit/c7fd7fbde6f6c69a3a9a4f89d948c4dfb1d22679) Thanks [@jxom](https://github.com/jxom)! - Added the following chains to the `wagmi/chains` entrypoint: + + - `filecoinCalibration` + - `moonbaseAlpha` + - `moonbeam` + - `moonriver` + +- Updated dependencies [[`c7fd7fbd`](https://github.com/wevm/wagmi/commit/c7fd7fbde6f6c69a3a9a4f89d948c4dfb1d22679)]: + - @wagmi/core@0.9.5 + +## 0.11.4 + +### Patch Changes + +- [#1679](https://github.com/wevm/wagmi/pull/1679) [`3cef111b`](https://github.com/wevm/wagmi/commit/3cef111b1e30120233d8754b33587cdf94aedd8f) Thanks [@aj-may](https://github.com/aj-may)! - Fixed `useAccount` `onConnect` and `onDisconnect` callbacks for React Strict Mode. + +- [#1786](https://github.com/wevm/wagmi/pull/1786) [`b173a431`](https://github.com/wevm/wagmi/commit/b173a43165c7925a4e56ce1e0327a31917e7edc5) Thanks [@tmm](https://github.com/tmm)! - Locked ethers peer dependency version to >=5.5.1 <6 + +- [#1787](https://github.com/wevm/wagmi/pull/1787) [`f023fd8f`](https://github.com/wevm/wagmi/commit/f023fd8f66befb78b9a4df5ca971ceaa64e37ab4) Thanks [@tmm](https://github.com/tmm)! - Added `SafeConnector` + +- Updated dependencies [[`b173a431`](https://github.com/wevm/wagmi/commit/b173a43165c7925a4e56ce1e0327a31917e7edc5), [`f023fd8f`](https://github.com/wevm/wagmi/commit/f023fd8f66befb78b9a4df5ca971ceaa64e37ab4)]: + - @wagmi/core@0.9.4 + +## 0.11.3 + +### Patch Changes + +- [#1773](https://github.com/wevm/wagmi/pull/1773) [`9aaf1955`](https://github.com/wevm/wagmi/commit/9aaf195514d3b5f4d085c797fc5021d42a9efb6c) Thanks [@jxom](https://github.com/jxom)! - Updated `@walletconnect/universal-provider` on `WalletConnectConnector` v2. + Added more signable methods to `WalletConnectConnector` v2. + +- [#1773](https://github.com/wevm/wagmi/pull/1773) [`9aaf1955`](https://github.com/wevm/wagmi/commit/9aaf195514d3b5f4d085c797fc5021d42a9efb6c) Thanks [@jxom](https://github.com/jxom)! - Added Telos to the `wagmi/chains` entrypoint. Thanks @donnyquixotic! + +- Updated dependencies [[`9aaf1955`](https://github.com/wevm/wagmi/commit/9aaf195514d3b5f4d085c797fc5021d42a9efb6c), [`9aaf1955`](https://github.com/wevm/wagmi/commit/9aaf195514d3b5f4d085c797fc5021d42a9efb6c)]: + - @wagmi/core@0.9.3 + +## 0.11.2 + +### Patch Changes + +- [#1756](https://github.com/wevm/wagmi/pull/1756) [`31d06b8c`](https://github.com/wevm/wagmi/commit/31d06b8ce1e7af5e9d1a7ba57f1743b2dff7a53d) Thanks [@jxom](https://github.com/jxom)! - Added OKC Chain. Thanks @clark-cui! + +- [#1756](https://github.com/wevm/wagmi/pull/1756) [`31d06b8c`](https://github.com/wevm/wagmi/commit/31d06b8ce1e7af5e9d1a7ba57f1743b2dff7a53d) Thanks [@jxom](https://github.com/jxom)! - Fixed race condition between `switchNetwork` and mutation Actions that use `chainId` (e.g. `sendTransaction`). Thanks @DanInTheD4rk! + +- Updated dependencies [[`31d06b8c`](https://github.com/wevm/wagmi/commit/31d06b8ce1e7af5e9d1a7ba57f1743b2dff7a53d), [`31d06b8c`](https://github.com/wevm/wagmi/commit/31d06b8ce1e7af5e9d1a7ba57f1743b2dff7a53d)]: + - @wagmi/core@0.9.2 + +## 0.11.1 + +### Patch Changes + +- [#1752](https://github.com/wevm/wagmi/pull/1752) [`144a0e76`](https://github.com/wevm/wagmi/commit/144a0e76ef4bb9ba0650b5ffb9c63f95329819a4) Thanks [@jxom](https://github.com/jxom)! - Improved `WalletConnectConnector` (v2) initialization & updated dependencies. + +- [#1752](https://github.com/wevm/wagmi/pull/1752) [`144a0e76`](https://github.com/wevm/wagmi/commit/144a0e76ef4bb9ba0650b5ffb9c63f95329819a4) Thanks [@jxom](https://github.com/jxom)! - Added the following chains to the `wagmi/chains` entrypoint: + + - Aurora – thanks @salil-naik + - Bronos – thanks @chedetinaveen + - Canto – thanks @tster + - Celo – thanks @aaronmgdr + +- Updated dependencies [[`144a0e76`](https://github.com/wevm/wagmi/commit/144a0e76ef4bb9ba0650b5ffb9c63f95329819a4), [`144a0e76`](https://github.com/wevm/wagmi/commit/144a0e76ef4bb9ba0650b5ffb9c63f95329819a4)]: + - @wagmi/core@0.9.1 + +## 0.11.0 + +### Minor Changes + +- [#1732](https://github.com/wevm/wagmi/pull/1732) [`01e21897`](https://github.com/wevm/wagmi/commit/01e2189747a5c22dc758c6d719b4145adc2a643c) Thanks [@tmm](https://github.com/tmm)! - Bumped minimum TypeScript version to typescript@>=4.9.4. TypeScript 5.0 is coming soon and has some great features we are excited to bring into wagmi. To prepare for this, update your TypeScript version to 4.9.4 or higher. There are likely no [breaking changes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html#correctness-fixes-and-breaking-changes) if you are coming from typescript@4.7.x || typescript@4.8.x. + +### Patch Changes + +- Updated dependencies [[`01e21897`](https://github.com/wevm/wagmi/commit/01e2189747a5c22dc758c6d719b4145adc2a643c)]: + - @wagmi/core@0.9.0 + +## 0.10.15 + +### Patch Changes + +- [#1718](https://github.com/wevm/wagmi/pull/1718) [`e62b5ef8`](https://github.com/wevm/wagmi/commit/e62b5ef8aaa8063abb5264790768899ea35bbd31) Thanks [@tmm](https://github.com/tmm)! - Updated references + +- Updated dependencies [[`e62b5ef8`](https://github.com/wevm/wagmi/commit/e62b5ef8aaa8063abb5264790768899ea35bbd31)]: + - @wagmi/core@0.8.19 + +## 0.10.14 + +### Patch Changes + +- [#1708](https://github.com/wevm/wagmi/pull/1708) [`07fc3801`](https://github.com/wevm/wagmi/commit/07fc3801fa13c2cb5f7cf9b86ba8320b05a6a135) Thanks [@jxom](https://github.com/jxom)! - Updated `references/` submodule. + +- Updated dependencies [[`07fc3801`](https://github.com/wevm/wagmi/commit/07fc3801fa13c2cb5f7cf9b86ba8320b05a6a135)]: + - @wagmi/core@0.8.18 + +## 0.10.13 + +### Patch Changes + +- [#1705](https://github.com/wevm/wagmi/pull/1705) [`9ff797dc`](https://github.com/wevm/wagmi/commit/9ff797dcb979dc86b798a432b74c98598165430d) Thanks [@jxom](https://github.com/jxom)! - Added the following chains to the `wagmi/chains` entrypoint: + + - `crossbell` (thanks @Songkeys) + - `filecoin` & `filecoinHyperspace` (thanks @neil0x46dc) + - `gnosisChiado` (thanks @theNvN) + - `metis` & `metisGoerli` (thanks @CookedCookee) + +- Updated dependencies [[`9ff797dc`](https://github.com/wevm/wagmi/commit/9ff797dcb979dc86b798a432b74c98598165430d)]: + - @wagmi/core@0.8.17 + +## 0.10.12 + +### Patch Changes + +- [#1699](https://github.com/wevm/wagmi/pull/1699) [`2f1e7950`](https://github.com/wevm/wagmi/commit/2f1e7950e55550d9b50ef5ccb97cb609f4af39b1) Thanks [@tmm](https://github.com/tmm)! - Added public RPC URL property to Chain + +- Updated dependencies [[`2f1e7950`](https://github.com/wevm/wagmi/commit/2f1e7950e55550d9b50ef5ccb97cb609f4af39b1)]: + - @wagmi/core@0.8.16 + +## 0.10.11 + +### Patch Changes + +- [#1685](https://github.com/wevm/wagmi/pull/1685) [`917f5bc1`](https://github.com/wevm/wagmi/commit/917f5bc1fad578e35a8c6ee787e339bfdc156bab) Thanks [@jxom](https://github.com/jxom)! - Replaced qrcodemodal with web3modal for the WalletConnect v2 Connector. + +- Updated dependencies [[`917f5bc1`](https://github.com/wevm/wagmi/commit/917f5bc1fad578e35a8c6ee787e339bfdc156bab)]: + - @wagmi/core@0.8.15 + +## 0.10.10 + +### Patch Changes + +- [#1648](https://github.com/wevm/wagmi/pull/1648) [`a2db9170`](https://github.com/wevm/wagmi/commit/a2db91709720161cd70eeb5e84dd78433264f0a3) Thanks [@tmm](https://github.com/tmm)! - Exported internal type. + +## 0.10.9 + +### Patch Changes + +- [#1646](https://github.com/wevm/wagmi/pull/1646) [`fcdbe353`](https://github.com/wevm/wagmi/commit/fcdbe3531e6d05cda4a4a511bae1ad4c9e426d88) Thanks [@jxom](https://github.com/jxom)! - Upgraded `zustand` to v4.3.1. + +- Updated dependencies [[`fcdbe353`](https://github.com/wevm/wagmi/commit/fcdbe3531e6d05cda4a4a511bae1ad4c9e426d88)]: + - @wagmi/core@0.8.14 + +## 0.10.8 + +### Patch Changes + +- [#1639](https://github.com/wevm/wagmi/pull/1639) [`c6869f06`](https://github.com/wevm/wagmi/commit/c6869f0604fffb197752a08256f31db77f52e746) Thanks [@jxom](https://github.com/jxom)! - Added `isRainbow` flag to `InjectedConnector`. + +- Updated dependencies [[`c6869f06`](https://github.com/wevm/wagmi/commit/c6869f0604fffb197752a08256f31db77f52e746)]: + - @wagmi/core@0.8.13 + +## 0.10.7 + +### Patch Changes + +- [#1636](https://github.com/wevm/wagmi/pull/1636) [`025f6771`](https://github.com/wevm/wagmi/commit/025f6771b32ff7eed22f527be81c5141ddaf9c3d) Thanks [@DanielSinclair](https://github.com/DanielSinclair)! - Added `isRainbow` flag to injected `window.ethereum` types. + +- Updated dependencies [[`025f6771`](https://github.com/wevm/wagmi/commit/025f6771b32ff7eed22f527be81c5141ddaf9c3d)]: + - @wagmi/core@0.8.12 + +## 0.10.6 + +### Patch Changes + +- [#1623](https://github.com/wevm/wagmi/pull/1623) [`c97a4bc5`](https://github.com/wevm/wagmi/commit/c97a4bc5df422dc9a9d3d8bac0261ec6933ce15b) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `useSigner` would not update on account change. + +## 0.10.5 + +### Patch Changes + +- [#1621](https://github.com/wevm/wagmi/pull/1621) [`5812b590`](https://github.com/wevm/wagmi/commit/5812b5909277bf2862cb57a31d52465b47291410) Thanks [@tmm](https://github.com/tmm)! - Bumped @wagmi/connectors + +- Updated dependencies [[`5812b590`](https://github.com/wevm/wagmi/commit/5812b5909277bf2862cb57a31d52465b47291410)]: + - @wagmi/core@0.8.11 + +## 0.10.4 + +### Patch Changes + +- [#1607](https://github.com/wevm/wagmi/pull/1607) [`49a41357`](https://github.com/wevm/wagmi/commit/49a41357f9ca39479bdf759f5998bc169a91ac87) Thanks [@tmm](https://github.com/tmm)! - Exported hook types. + +## 0.10.3 + +### Patch Changes + +- [#1598](https://github.com/wevm/wagmi/pull/1598) [`fc10ebe6`](https://github.com/wevm/wagmi/commit/fc10ebe659dd5f3b7a8e00581f094652280a779b) Thanks [@jxom](https://github.com/jxom)! - Fixed CJS dependency version range + +- Updated dependencies [[`fc10ebe6`](https://github.com/wevm/wagmi/commit/fc10ebe659dd5f3b7a8e00581f094652280a779b)]: + - @wagmi/core@0.8.10 + +## 0.10.2 + +### Patch Changes + +- [#1593](https://github.com/wevm/wagmi/pull/1593) [`216d555c`](https://github.com/wevm/wagmi/commit/216d555c62bd95c3c7c8f8e20f7269f6c8504610) Thanks [@jxom](https://github.com/jxom)! - Added CJS escape hatch bundle under the "cjs" tag. + +- Updated dependencies [[`216d555c`](https://github.com/wevm/wagmi/commit/216d555c62bd95c3c7c8f8e20f7269f6c8504610)]: + - @wagmi/core@0.8.9 + +## 0.10.1 + +### Patch Changes + +- [#1573](https://github.com/wevm/wagmi/pull/1573) [`ef380d9c`](https://github.com/wevm/wagmi/commit/ef380d9c6d51ae0495b9c35925d2843c75d97fd4) Thanks [@tmm](https://github.com/tmm)! - Updated internal types. + +- Updated dependencies [[`ef380d9c`](https://github.com/wevm/wagmi/commit/ef380d9c6d51ae0495b9c35925d2843c75d97fd4)]: + - @wagmi/core@0.8.8 + +## 0.10.0 + +### Minor Changes + +- [#1470](https://github.com/wevm/wagmi/pull/1470) [`3a1a6c9f`](https://github.com/wevm/wagmi/commit/3a1a6c9fe5db5c360adfd116f9a03a1238b5720c) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `useSigner` hook now always returns `undefined` when no signer is present. Previously, it returned `null`. + + When no signer is present, the hook will be in an `"idle"` status. + +### Patch Changes + +- [#1470](https://github.com/wevm/wagmi/pull/1470) [`3a1a6c9f`](https://github.com/wevm/wagmi/commit/3a1a6c9fe5db5c360adfd116f9a03a1238b5720c) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `useSigner` would broadcast to other `useSigner`s unnecessarily. + +- [#1470](https://github.com/wevm/wagmi/pull/1470) [`3a1a6c9f`](https://github.com/wevm/wagmi/commit/3a1a6c9fe5db5c360adfd116f9a03a1238b5720c) Thanks [@jxom](https://github.com/jxom)! - The `WalletConnectConnector` now supports WalletConnect v2. + + It can be enabled by setting `version` to `'2'` and supplying a [WalletConnect Cloud `projectId`](https://cloud.walletconnect.com/sign-in). + +- [#1570](https://github.com/wevm/wagmi/pull/1570) [`216f585b`](https://github.com/wevm/wagmi/commit/216f585be8a9e3a56e3243f49ccd54d655b5a6dd) Thanks [@jxom](https://github.com/jxom)! - Added `useWatchPendingTransactions` + +- Updated dependencies [[`216f585b`](https://github.com/wevm/wagmi/commit/216f585be8a9e3a56e3243f49ccd54d655b5a6dd), [`3a1a6c9f`](https://github.com/wevm/wagmi/commit/3a1a6c9fe5db5c360adfd116f9a03a1238b5720c)]: + - @wagmi/core@0.8.7 + +## 0.9.6 + +### Patch Changes + +- [#1539](https://github.com/wevm/wagmi/pull/1539) [`732da004`](https://github.com/wevm/wagmi/commit/732da0042c7e28091b2e36a484ea8239971306f5) Thanks [@0xFlicker](https://github.com/0xFlicker)! - All Providers (ie. Alchemy, Infura, Public) now use the ENS Registry address on the wagmi `Chain` object (`chain.contracts.ensRegistry`). + +- [#1574](https://github.com/wevm/wagmi/pull/1574) [`ecde3d10`](https://github.com/wevm/wagmi/commit/ecde3d1029ccdf90e2853ba0e9ae4f5f4ebb9c4c) Thanks [@jxom](https://github.com/jxom)! - Added the following chains: + + - `iotex` + - `iotexTestnet` + - `zkSync` + - `zkSyncTestnet` + +- Updated dependencies [[`732da004`](https://github.com/wevm/wagmi/commit/732da0042c7e28091b2e36a484ea8239971306f5), [`ecde3d10`](https://github.com/wevm/wagmi/commit/ecde3d1029ccdf90e2853ba0e9ae4f5f4ebb9c4c)]: + - @wagmi/core@0.8.6 + +## 0.9.5 + +### Patch Changes + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Added the following chains: + + - `evmos` + - `evmosTestnet` + - `gnosis` + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Updated Goerli symbol to `"ETH"`. + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Updated Arbitrum Goerli RPC and Block Explorer. + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where connecting to MetaMask may return with a stale address. + +- [#1542](https://github.com/wevm/wagmi/pull/1542) [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549) Thanks [@jxom](https://github.com/jxom)! - Removed ENS registry for Sepolia. + +- Updated dependencies [[`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549), [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549), [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549), [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549), [`731b3b73`](https://github.com/wevm/wagmi/commit/731b3b733c6a093d1693d49de601705b7c730549)]: + - @wagmi/core@0.8.5 + +## 0.9.4 + +### Patch Changes + +- [#1508](https://github.com/wevm/wagmi/pull/1508) [`0b50b62f`](https://github.com/wevm/wagmi/commit/0b50b62f7389619e429509a3e337e451e823b059) Thanks [@jxom](https://github.com/jxom)! - Updated `@wagmi/chains` to `0.1.3`. + +- [#1507](https://github.com/wevm/wagmi/pull/1507) [`7a083bcf`](https://github.com/wevm/wagmi/commit/7a083bcf31d671817a4da2f40fb2160a1ba9d7b7) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `useBlockNumber` would return data when `watch` is enabled and `enabled` is falsy. + +- [#1504](https://github.com/wevm/wagmi/pull/1504) [`11b8b794`](https://github.com/wevm/wagmi/commit/11b8b794fbfd4a2b40f39962e2758e9fbf48cb54) Thanks [@tmm](https://github.com/tmm)! - Converted ethers custom "ACTION_REJECTED" error to standard RPC Error. + +- Updated dependencies [[`0b50b62f`](https://github.com/wevm/wagmi/commit/0b50b62f7389619e429509a3e337e451e823b059), [`11b8b794`](https://github.com/wevm/wagmi/commit/11b8b794fbfd4a2b40f39962e2758e9fbf48cb54)]: + - @wagmi/core@0.8.4 + +## 0.9.3 + +### Patch Changes + +- [#1431](https://github.com/wevm/wagmi/pull/1431) [`af28f8f9`](https://github.com/wevm/wagmi/commit/af28f8f9cfc227e7c391927fdb934183edb5c2dc) Thanks [@jxom](https://github.com/jxom)! - Re-export connectors from `@wagmi/connectors` + +- [#1431](https://github.com/wevm/wagmi/pull/1431) [`af28f8f9`](https://github.com/wevm/wagmi/commit/af28f8f9cfc227e7c391927fdb934183edb5c2dc) Thanks [@jxom](https://github.com/jxom)! - Added `LedgerConnector` connector + +- Updated dependencies [[`af28f8f9`](https://github.com/wevm/wagmi/commit/af28f8f9cfc227e7c391927fdb934183edb5c2dc), [`af28f8f9`](https://github.com/wevm/wagmi/commit/af28f8f9cfc227e7c391927fdb934183edb5c2dc)]: + - @wagmi/core@0.8.3 + +## 0.9.2 + +### Patch Changes + +- [#1442](https://github.com/wevm/wagmi/pull/1442) [`cde15289`](https://github.com/wevm/wagmi/commit/cde152899c758dea10787412b0aef669ed7202b2) Thanks [@0xproflupin](https://github.com/0xproflupin)! - Added Phantom wallet support to `InjectedConnector` + +- [#1448](https://github.com/wevm/wagmi/pull/1448) [`c6075f3a`](https://github.com/wevm/wagmi/commit/c6075f3a16885d850ad2656272351f9517c9f67b) Thanks [@tmm](https://github.com/tmm)! - Updated [ABIType](https://github.com/wevm/abitype) version. + +- [#1444](https://github.com/wevm/wagmi/pull/1444) [`310a8bc4`](https://github.com/wevm/wagmi/commit/310a8bc428ce4e7f68377f581b45dcdd64381cce) Thanks [@jxom](https://github.com/jxom)! - Assert that a `connector` exists before invoking the callback in `watchSigner`. + +- [#1434](https://github.com/wevm/wagmi/pull/1434) [`100e2a3b`](https://github.com/wevm/wagmi/commit/100e2a3b22f4602716554487b1d98738e053be76) Thanks [@tmm](https://github.com/tmm)! - Updated `MockConnector` `chainId` behavior to default to first chain from `chains` if not provided in `options`. + +- Updated dependencies [[`cde15289`](https://github.com/wevm/wagmi/commit/cde152899c758dea10787412b0aef669ed7202b2), [`c6075f3a`](https://github.com/wevm/wagmi/commit/c6075f3a16885d850ad2656272351f9517c9f67b), [`310a8bc4`](https://github.com/wevm/wagmi/commit/310a8bc428ce4e7f68377f581b45dcdd64381cce), [`100e2a3b`](https://github.com/wevm/wagmi/commit/100e2a3b22f4602716554487b1d98738e053be76)]: + - @wagmi/core@0.8.2 + +## 0.9.1 + +### Patch Changes + +- [#1437](https://github.com/wevm/wagmi/pull/1437) [`c34a3dc6`](https://github.com/wevm/wagmi/commit/c34a3dc6396e6473d9f0505fad88ec910f8f5275) Thanks [@jxom](https://github.com/jxom)! - Omitted `"EIP712Domain"` type from `signTypedData` `types` arg since ethers throws an [internal error](https://github.com/ethers-io/ethers.js/blob/c80fcddf50a9023486e9f9acb1848aba4c19f7b6/packages/hash/src.ts/typed-data.ts#L466) if you include it. + +- [#1445](https://github.com/wevm/wagmi/pull/1445) [`51dd53cb`](https://github.com/wevm/wagmi/commit/51dd53cba3fe0f79fa1393270b738194577ddf54) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where the wagmi client wouldn't rehydrate the store in local storage when `autoConnect` is truthy. + +- Updated dependencies [[`c34a3dc6`](https://github.com/wevm/wagmi/commit/c34a3dc6396e6473d9f0505fad88ec910f8f5275), [`51dd53cb`](https://github.com/wevm/wagmi/commit/51dd53cba3fe0f79fa1393270b738194577ddf54)]: + - @wagmi/core@0.8.1 + +## 0.9.0 + +### Minor Changes + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: With the introduction of the [`wagmi/chains` entrypoint](/react/chains#wagmichains), `wagmi` no longer exports the following: + + - `chain` + - `allChains` + - `defaultChains` + - `defaultL2Chains` + - `chainId` + - `etherscanBlockExplorers` + - `alchemyRpcUrls`, `infuraRpcUrls`, `publicRpcUrls` + + Read below for migration steps. + + #### Removed `chain` + + The `chain` export has been removed. `wagmi` now only exports the `mainnet` & `goerli` chains. If you need to use an alternative chain (`polygon`, `optimism`, etc), you will need to import it from the [`wagmi/chains` entrypoint](/react/chains#wagmichains). + + ```diff + import { + - chain + configureChains + } from 'wagmi' + + import { mainnet, polygon, optimism } from 'wagmi/chains' + + const { ... } = configureChains( + - [chain.mainnet, chain.polygon, chain.optimism], + + [mainnet, polygon, optimism], + { + ... + } + ) + ``` + + #### Removed `allChains` + + The `allChains` export has been removed. If you need a list of all chains, you can utilize [`wagmi/chains` entrypoint](/react/chains#wagmichains). + + ```diff + - import { allChains } from 'wagmi' + + import * as allChains from 'wagmi/chains' + + const { ... } = configureChains(allChains, ...) + ``` + + #### Removed `defaultChains` & `defaultL2Chains` + + The `defaultChains` & `defaultL2Chains` exports have been removed. If you still need the `defaultChains` or `defaultL2Chains` exports, you can build them yourself: + + ```diff + - import { defaultChains } from 'wagmi' + + import { mainnet, goerli } from 'wagmi/chains' + + + const defaultChains = [mainnet, goerli] + ``` + + > The `defaultChains` export was previously populated with `mainnet` & `goerli`. + + ```diff + - import { defaultL2Chains } from 'wagmi' + + import { + + arbitrum, + + arbitrumGoerli, + + polygon, + + polygonMumbai, + + optimism, + + optimismGoerli + + } from 'wagmi/chains' + + + const defaultL2Chains = [ + + arbitrum, + + arbitrumGoerli, + + polygon, + + polygonMumbai, + + optimism + + optimismGoerli + + ] + ``` + + > The `defaultL2Chains` export was previously populated with `arbitrum` & `optimism`. + + #### Removed `chainId` + + The `chainId` export has been removed. You can extract a chain ID from the chain itself. + + ```diff + - import { chainId } from 'wagmi' + + import { mainnet, polygon, optimism } from 'wagmi/chains' + + -const mainnetChainId = chainId.mainnet + -const polygonChainId = chainId.polygon + -const optimismChainId = chainId.optimism + +const mainnetChainId = mainnet.chainId + +const polygonChainId = polygon.chainId + +const optimismChainId = optimism.chainId + ``` + + #### Removed `etherscanBlockExplorers` + + The `etherscanBlockExplorers` export has been removed. You can extract a block explorer from the chain itself. + + ```diff + - import { etherscanBlockExplorers } from 'wagmi' + + import { mainnet, polygon, optimism } from 'wagmi/chains' + + -const mainnetEtherscanBlockExplorer = etherscanBlockExplorers.mainnet + -const polygonEtherscanBlockExplorer = etherscanBlockExplorers.polygon + -const optimismEtherscanBlockExplorer = etherscanBlockExplorers.optimism + +const mainnetEtherscanBlockExplorer = mainnet.blockExplorer + +const polygonEtherscanBlockExplorer = polygon.blockExplorer + +const optimismEtherscanBlockExplorer = optimism.blockExplorer + ``` + + #### Removed `alchemyRpcUrls`, `infuraRpcUrls` & `publicRpcUrls` + + The `alchemyRpcUrls`, `infuraRpcUrls` & `publicRpcUrls` exports have been removed. You can extract a RPC URL from the chain itself. + + ```diff + - import { alchemyRpcUrls, infuraRpcUrls, publicRpcUrls } from 'wagmi' + + import { mainnet } from 'wagmi/chains' + + -const mainnetAlchemyRpcUrl = alchemyRpcUrls.mainnet + -const mainnetInfuraRpcUrl = infuraRpcUrls.mainnet + -const mainnetOptimismRpcUrl = publicRpcUrls.mainnet + +const mainnetAlchemyRpcUrl = mainnet.rpcUrls.alchemy + +const mainnetInfuraRpcUrl = mainnet.rpcUrls.infura + +const mainnetOptimismRpcUrl = mainnet.rpcUrls.optimism + ``` + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: the shape of the `Chain` type has been modified. + + #### RPC URLs + + The `rpcUrls` shape has changed to include an array of URLs, and also the transport method (`http` or `webSocket`): + + ```diff + type Chain = { + ... + rpcUrls: { + - [key: string]: string + + [key: string]: { + + http: string[] + + webSocket: string[] + + } + } + ... + } + ``` + + Note that you will also need to ensure that usage is migrated: + + ```diff + - const rpcUrl = mainnet.rpcUrls.alchemy + + const rpcUrl = mainnet.rpcUrls.alchemy.http[0] + ``` + + #### Contracts + + The `multicall` and `ens` attributes have been moved into the `contracts` object: + + ```diff + type Contract = { + address: Address + blockCreated?: number + } + + type Chain = { + ... + - multicall: Contract + - ens: Contract + + contracts: { + + multicall3: Contract + + ensRegistry: Contract + + } + ... + } + ``` + + Note that you will also need to ensure that usage is migrated: + + ```diff + - const multicallContract = mainnet.multicall + + const multicallContract = mainnet.contracts.multicall3 + ``` + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: Removed the `wait` config option on `useWaitForTransaction`. Use the transaction `hash` instead. + + ```diff + const { data } = useWaitForTransaction({ + - wait: transaction.wait + + hash: transaction.hash + }) + ``` + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - Updated errors to use `cause` instead of `internal` + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - `useEnsResolver`'s result is no longer persisted by the query client since it cannot serialize its prototype methods. + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: Changed `useWaitForTransaction` behavior to return an error if the transaction reverted. + +### Patch Changes + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - `useWaitForTransaction` now throws an error for cancelled or replaced transactions. + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - `useWaitForTransaction` now respects repriced (sped up) transactions. + +- [#1344](https://github.com/wevm/wagmi/pull/1344) [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652) Thanks [@jxom](https://github.com/jxom)! - Updated `@coinbase/wallet-sdk` to `^3.6.0`. + +- Updated dependencies [[`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652), [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652), [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652), [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652), [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652), [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652), [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652), [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652), [`57a19374`](https://github.com/wevm/wagmi/commit/57a1937464a4ccf72719fc86c38d1734f6306652)]: + - @wagmi/core@0.8.0 + +## 0.8.10 + +### Patch Changes + +- [#1411](https://github.com/wevm/wagmi/pull/1411) [`659be184`](https://github.com/wevm/wagmi/commit/659be1840c613ce9f7aca9ac96694c4f60da4a66) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where block invalidation was not properly disabled when setting `enabled: false`. + +- [#1409](https://github.com/wevm/wagmi/pull/1409) [`b557b3ee`](https://github.com/wevm/wagmi/commit/b557b3ee4fc58217e61d860fc3d1109d2abc813e) Thanks [@jxom](https://github.com/jxom)! - Ensure that `useSyncExternalStoreWithTracked` rerenders when no values are being tracked. + +- Updated dependencies [[`659be184`](https://github.com/wevm/wagmi/commit/659be1840c613ce9f7aca9ac96694c4f60da4a66)]: + - @wagmi/core@0.7.9 + +## 0.8.9 + +### Patch Changes + +- [#1406](https://github.com/wevm/wagmi/pull/1406) [`4f18c450`](https://github.com/wevm/wagmi/commit/4f18c450a4d7952bfcfa6c533348ffbe55893d3c) Thanks [@tmm](https://github.com/tmm)! - Function for selecting the [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) Ethereum Provider to target. Defaults to `() => typeof window !== 'undefined' ? window.ethereum : undefined`. + + ```ts + import { InjectedConnector } from "wagmi/connectors/injected"; + + const connector = new InjectedConnector({ + options: { + name: "My Injected Wallet", + getProvider: () => + typeof window !== "undefined" ? window.myInjectedWallet : undefined, + }, + }); + ``` + +- Updated dependencies [[`4f18c450`](https://github.com/wevm/wagmi/commit/4f18c450a4d7952bfcfa6c533348ffbe55893d3c)]: + - @wagmi/core@0.7.8 + +## 0.8.8 + +### Patch Changes + +- [#1386](https://github.com/wevm/wagmi/pull/1386) [`206a2adb`](https://github.com/wevm/wagmi/commit/206a2adbb4ee5149a364543b34612050ccf78c21) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `persister` would still use `window.localStorage` instead of the wagmi `storage`. + +- [#1376](https://github.com/wevm/wagmi/pull/1376) [`a70a9528`](https://github.com/wevm/wagmi/commit/a70a9528f93f4d7fea28b7652751dfef2dcacf9b) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where `switchChain` on `WalletConnectConnector` would not resolve. + +- [#1392](https://github.com/wevm/wagmi/pull/1392) [`88afc849`](https://github.com/wevm/wagmi/commit/88afc84978afe9689ab7364633e4422ecd7699ea) Thanks [@tmm](https://github.com/tmm)! - Added check for active connector when connecting + +- Updated dependencies [[`206a2adb`](https://github.com/wevm/wagmi/commit/206a2adbb4ee5149a364543b34612050ccf78c21), [`a70a9528`](https://github.com/wevm/wagmi/commit/a70a9528f93f4d7fea28b7652751dfef2dcacf9b), [`206a2adb`](https://github.com/wevm/wagmi/commit/206a2adbb4ee5149a364543b34612050ccf78c21), [`88afc849`](https://github.com/wevm/wagmi/commit/88afc84978afe9689ab7364633e4422ecd7699ea)]: + - @wagmi/core@0.7.7 + +## 0.8.7 + +### Patch Changes + +- [#1384](https://github.com/wevm/wagmi/pull/1384) [`027e88d6`](https://github.com/wevm/wagmi/commit/027e88d6e5f8d028d46ee78aec8500701e0173d9) Thanks [@tmm](https://github.com/tmm)! - Fixed issue reconnecting after disconnect with `MetaMaskConnector` in MetaMask mobile browser. + +- [#1377](https://github.com/wevm/wagmi/pull/1377) [`089c4f3b`](https://github.com/wevm/wagmi/commit/089c4f3b3b8ce5cf7807f144410e2f64b72e0580) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where transforming `useContractRead`, `useContractReads` or `useContractInfiniteReads`'s return data via `select` wasn't inferring the type. + +- Updated dependencies [[`027e88d6`](https://github.com/wevm/wagmi/commit/027e88d6e5f8d028d46ee78aec8500701e0173d9)]: + - @wagmi/core@0.7.6 + +## 0.8.6 + +### Patch Changes + +- [`1169914a`](https://github.com/wevm/wagmi/commit/1169914a0f0ad2810ca1c536b1f1bc6c20f2c1be) Thanks [@jxom](https://github.com/jxom)! - Use `get_accounts` for `getSigner` in InjectedConnector + +- Updated dependencies [[`1169914a`](https://github.com/wevm/wagmi/commit/1169914a0f0ad2810ca1c536b1f1bc6c20f2c1be)]: + - @wagmi/core@0.7.5 + +## 0.8.5 + +### Patch Changes + +- [#1282](https://github.com/wevm/wagmi/pull/1282) [`6d286c9e`](https://github.com/wevm/wagmi/commit/6d286c9ed6f64a9872352904d4d171a6bc1c7a96) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `useContractRead` would perform an unnecessary rerender if another hook had `watch` enabled. + +## 0.8.4 + +### Patch Changes + +- [#1309](https://github.com/wevm/wagmi/pull/1309) [`1f4a4261`](https://github.com/wevm/wagmi/commit/1f4a4261247b1d3a90e3123157bc851a35d49b9c) Thanks [@tmm](https://github.com/tmm)! - Fixed internal type + +- Updated dependencies [[`1f4a4261`](https://github.com/wevm/wagmi/commit/1f4a4261247b1d3a90e3123157bc851a35d49b9c)]: + - @wagmi/core@0.7.4 + +## 0.8.3 + +### Patch Changes + +- [#1294](https://github.com/wevm/wagmi/pull/1294) [`b2f88949`](https://github.com/wevm/wagmi/commit/b2f88949f32aabaf13f318472648cd51a8b7f2e7) Thanks [@tmm](https://github.com/tmm)! - Set `abi` return type value for `usePrepareContractWrite` as more permissive when not inferrable as `Abi`. + +- Updated dependencies [[`b2f88949`](https://github.com/wevm/wagmi/commit/b2f88949f32aabaf13f318472648cd51a8b7f2e7)]: + - @wagmi/core@0.7.3 + +## 0.8.2 + +### Patch Changes + +- [`e9f806b6`](https://github.com/wevm/wagmi/commit/e9f806b652ba62effb3ddac464815e447fc287f6) Thanks [@tmm](https://github.com/tmm)! - Bumped abitype and zustand versions. + +- [#1290](https://github.com/wevm/wagmi/pull/1290) [`88450052`](https://github.com/wevm/wagmi/commit/88450052b9f070fe53e18d84f72918c410b961f0) Thanks [@tmm](https://github.com/tmm)! - Fixed `useAccount`'s' `onConnect` callback `isReconnected` flag. + +- Updated dependencies [[`e9f806b6`](https://github.com/wevm/wagmi/commit/e9f806b652ba62effb3ddac464815e447fc287f6)]: + - @wagmi/core@0.7.2 + +## 0.8.1 + +### Patch Changes + +- [#1272](https://github.com/wevm/wagmi/pull/1272) [`1f7fc41`](https://github.com/wevm/wagmi/commit/1f7fc419f7960bbdc51dfa85c2f33b89f1ecc1bf) Thanks [@tmm](https://github.com/tmm)! - Fixed ethers import path + +- Updated dependencies [[`1f7fc41`](https://github.com/wevm/wagmi/commit/1f7fc419f7960bbdc51dfa85c2f33b89f1ecc1bf)]: + - @wagmi/core@0.7.1 + +## 0.8.0 + +### Minor Changes + +- [#1202](https://github.com/wevm/wagmi/pull/1202) [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Removed the following deprecated chains: + + - `ropsten` + - `rinkeby` + - `kovan` + - `optimismKovan` + - `arbitrumRinkeby` + + If you feel you still need to include one of these testnets in your application, you will have to define it manually: + + ```diff + -import { rinkeby } from 'wagmi' + +import { Chain } from 'wagmi' + + +export const rinkeby: Chain = { + + id: 4, + + name: 'Rinkeby', + + network: 'rinkeby', + + nativeCurrency: { name: 'Rinkeby Ether', symbol: 'ETH', decimals: 18 }, + + rpcUrls: { + + alchemy: 'https://eth-rinkeby.alchemyapi.io/v2', + + default: 'https://rpc.ankr.com/eth_rinkeby', + + infura: 'https://rinkeby.infura.io/v3', + + public: 'https://rpc.ankr.com/eth_rinkeby', + + }, + + blockExplorers: { + + etherscan: 'https://rinkeby.etherscan.io', + + default: 'https://rinkeby.etherscan.io', + + }, + + ens: { + + address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + + }, + + multicall: { + + address: '0xca11bde05977b3631167028862be2a173976ca11', + + blockCreated: 10299530, + + }, + + testnet: true, + } + ``` + + You can reference these removed chains [here](https://github.com/wevm/wagmi/blob/389765f7d9af063ab0df07389a2bbfbc10a41060/packages/core/src/constants/chains.ts). + +- [#1202](https://github.com/wevm/wagmi/pull/1202) [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Made `apiKey` required on `infuraProvider` and `alchemyProvider`. + + ```diff + import { configureChains } from 'wagmi' + + const config = configureChains(defaultChains, [ + - alchemyProvider(), + + alchemyProvider({ apiKey: process.env.ALCHEMY_API_KEY }) + ]) + ``` + + You can find your Alchemy API key from the [Alchemy Dashboard](https://dashboard.alchemyapi.io/), or your Infura API key from the [Infura Dashboard](https://infura.io/login). + +- [#1202](https://github.com/wevm/wagmi/pull/1202) [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `addressOrName` renamed to `address` for `useBalance` and `useEnsAvatar`. + + ```diff + const result = useBalance({ + - addressOrName: '0x…', + + address: '0x…', + }) + ``` + + If you were using an ENS name instead of an address, you can resolve the name to an address before passing it to the action. + + ```diff + + const { data: address } = useEnsAddress({ name: 'example.eth' }) + const result = useBalance({ + - addressOrName: 'example.eth', + + address, + }) + ``` + +- [#1202](https://github.com/wevm/wagmi/pull/1202) [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2) Thanks [@tmm](https://github.com/tmm)! - Removed CommonJS support + +### Patch Changes + +- Updated dependencies [[`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2), [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2), [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2), [`9bf56af`](https://github.com/wevm/wagmi/commit/9bf56af3c30bdb80abb1e785c002e00986fadfb2)]: + - @wagmi/core@0.7.0 + +## 0.7.15 + +### Patch Changes + +- [#1262](https://github.com/wevm/wagmi/pull/1262) [`45e2ca4`](https://github.com/wevm/wagmi/commit/45e2ca4d1f33a7b1165c387d420b8d47f4f66935) Thanks [@tmm](https://github.com/tmm)! - Added default for `structuralSharing` for `useContractRead`, `useContractReads`, and `useContractInfiniteReads`. + +## 0.7.14 + +### Patch Changes + +- [#1260](https://github.com/wevm/wagmi/pull/1260) [`0e12f03`](https://github.com/wevm/wagmi/commit/0e12f0380442bccca9ed18991e783819778032fe) Thanks [@ilmpc](https://github.com/ilmpc)! - Deprecated `isDataEqual` option from and added `structuralSharing` option to `useContractRead`, `useContractReads`, and `useContractInfiniteReads`. + +## 0.7.13 + +### Patch Changes + +- [#1250](https://github.com/wevm/wagmi/pull/1250) [`ce2e0f4`](https://github.com/wevm/wagmi/commit/ce2e0f4a46b8fd1c509ead552012ef4c072a525b) Thanks [@tmm](https://github.com/tmm)! - Added support for Trust Wallet browser extension. + +- Updated dependencies [[`ce2e0f4`](https://github.com/wevm/wagmi/commit/ce2e0f4a46b8fd1c509ead552012ef4c072a525b)]: + - @wagmi/core@0.6.12 + +## 0.7.12 + +### Patch Changes + +- [#1234](https://github.com/wevm/wagmi/pull/1234) [`3ff9303`](https://github.com/wevm/wagmi/commit/3ff930349250f62137cca4ca3b382522882abf8a) Thanks [@tmm](https://github.com/tmm)! - Fixed issue with adding chain to wallet without block explorer URL. + +- Updated dependencies [[`3ff9303`](https://github.com/wevm/wagmi/commit/3ff930349250f62137cca4ca3b382522882abf8a)]: + - @wagmi/core@0.6.11 + +## 0.7.11 + +### Patch Changes + +- [#1232](https://github.com/wevm/wagmi/pull/1232) [`c0ca509`](https://github.com/wevm/wagmi/commit/c0ca509506dcf6d98b058df549dc761c9a5f3d1c) Thanks [@tmm](https://github.com/tmm)! - Added validation to check that chain is configured for connector when accessing `Signer`. + +- Updated dependencies [[`c0ca509`](https://github.com/wevm/wagmi/commit/c0ca509506dcf6d98b058df549dc761c9a5f3d1c)]: + - @wagmi/core@0.6.10 + +## 0.7.10 + +### Patch Changes + +- [#1206](https://github.com/wevm/wagmi/pull/1206) [`15ff089`](https://github.com/wevm/wagmi/commit/15ff0896216abecf5967294ae5aeb26ea7fb480b) Thanks [@jxom](https://github.com/jxom)! - Added `scopeKey` as a configuration option to the Hooks which scope its cache to a given context. Hooks that have identical scope will share the same cache. + +- [#1207](https://github.com/wevm/wagmi/pull/1207) [`c73d463`](https://github.com/wevm/wagmi/commit/c73d463d65c9dbfcfe709187e47323a769589741) Thanks [@lvshaoping007](https://github.com/lvshaoping007)! - Added Kucoin wallet support to `InjectedConnector` + +- Updated dependencies [[`c73d463`](https://github.com/wevm/wagmi/commit/c73d463d65c9dbfcfe709187e47323a769589741)]: + - @wagmi/core@0.6.9 + +## 0.7.9 + +### Patch Changes + +- [#1201](https://github.com/wevm/wagmi/pull/1201) [`9a07efa`](https://github.com/wevm/wagmi/commit/9a07efaa397d3ba03f2edbe527c359f21e22139a) Thanks [@jxom](https://github.com/jxom)! - Fixed issue where non-checksum addresses did not resolve with an ENS name + +- [#1132](https://github.com/wevm/wagmi/pull/1132) [`d41c0d6`](https://github.com/wevm/wagmi/commit/d41c0d650f8c0e54145758685b7604b8909d7ae0) Thanks [@toniocodo](https://github.com/toniocodo)! - Added ERC-4626 ABI + +- Updated dependencies [[`d41c0d6`](https://github.com/wevm/wagmi/commit/d41c0d650f8c0e54145758685b7604b8909d7ae0), [`9a07efa`](https://github.com/wevm/wagmi/commit/9a07efaa397d3ba03f2edbe527c359f21e22139a)]: + - @wagmi/core@0.6.8 + +## 0.7.8 + +### Patch Changes + +- [#1174](https://github.com/wevm/wagmi/pull/1174) [`196a458`](https://github.com/wevm/wagmi/commit/196a458f64141e8a9f39c1b1e1af5937f692cb39) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `client.chains` (active connector chains) would be populated when there is no active connector (disconnected user). + +- [#1176](https://github.com/wevm/wagmi/pull/1176) [`389765f`](https://github.com/wevm/wagmi/commit/389765f7d9af063ab0df07389a2bbfbc10a41060) Thanks [@jxom](https://github.com/jxom)! - Migrate away from Alchemy RPC URLs in the public RPC URL list + +- Updated dependencies [[`196a458`](https://github.com/wevm/wagmi/commit/196a458f64141e8a9f39c1b1e1af5937f692cb39), [`389765f`](https://github.com/wevm/wagmi/commit/389765f7d9af063ab0df07389a2bbfbc10a41060)]: + - @wagmi/core@0.6.7 + +## 0.7.7 + +### Patch Changes + +- [#1166](https://github.com/wevm/wagmi/pull/1166) [`6fbe910`](https://github.com/wevm/wagmi/commit/6fbe91080b54e33e8543e9638ff5089e749ada3f) Thanks [@jxom](https://github.com/jxom)! - Export the React entrypoint `Client` type instead of `@wagmi/core`'s `Client`. + +- [`81ce9e6`](https://github.com/wevm/wagmi/commit/81ce9e64d85f7d01370324c1a529988a0919894f) Thanks [@jxom](https://github.com/jxom)! - Add `isPortal` to injected MetaMask flags. + +- [`c2c0109`](https://github.com/wevm/wagmi/commit/c2c01096ef4cd0ffadbb49062969c208604c6194) Thanks [@jxom](https://github.com/jxom)! - Add etherscan block explorer to Optimism Goerli + +- Updated dependencies [[`81ce9e6`](https://github.com/wevm/wagmi/commit/81ce9e64d85f7d01370324c1a529988a0919894f), [`c2c0109`](https://github.com/wevm/wagmi/commit/c2c01096ef4cd0ffadbb49062969c208604c6194)]: + - @wagmi/core@0.6.6 + +## 0.7.6 + +### Patch Changes + +- [#1162](https://github.com/wevm/wagmi/pull/1162) [`30335b3`](https://github.com/wevm/wagmi/commit/30335b3199fb425e398e9c492b50c68d5e2ade7e) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where non-indexed event parameter types were set to `null`. + +- [#1162](https://github.com/wevm/wagmi/pull/1162) [`30335b3`](https://github.com/wevm/wagmi/commit/30335b3199fb425e398e9c492b50c68d5e2ade7e) Thanks [@tmm](https://github.com/tmm)! - Fixed issue where `useContractReads` and `useContractInfiniteReads` types were slowing down TypeScript compiler. + +- Updated dependencies [[`30335b3`](https://github.com/wevm/wagmi/commit/30335b3199fb425e398e9c492b50c68d5e2ade7e), [`30335b3`](https://github.com/wevm/wagmi/commit/30335b3199fb425e398e9c492b50c68d5e2ade7e)]: + - @wagmi/core@0.6.5 + +## 0.7.5 + +### Patch Changes + +- [#1103](https://github.com/wevm/wagmi/pull/1103) [`651eda0`](https://github.com/wevm/wagmi/commit/651eda06384bd0955268427f898e9337b2dc5a31) Thanks [@tmm](https://github.com/tmm)! - Bumped `abitype` dependency. + +- Updated dependencies [[`651eda0`](https://github.com/wevm/wagmi/commit/651eda06384bd0955268427f898e9337b2dc5a31)]: + - @wagmi/core@0.6.4 + +## 0.7.4 + +### Patch Changes + +- [#1099](https://github.com/wevm/wagmi/pull/1099) [`748e617`](https://github.com/wevm/wagmi/commit/748e61719ad706acae057be903321ebe0c2e817e) Thanks [@jxom](https://github.com/jxom)! - Added `isFetchedAfterMount` to the return value of hooks. + + The `isFetchedAfterMount` will be truthy if the hook has fetched after the component has been mounted. This value can be utilized to not show the result if it has previously been cached. + +- [#1091](https://github.com/wevm/wagmi/pull/1091) [`a3aaf59`](https://github.com/wevm/wagmi/commit/a3aaf590e8e993017baa9a1ac50ecd63dd287caf) Thanks [@tmm](https://github.com/tmm)! - Fixed `useAccount` `onConnect`/`onDisconnect` from not firing when the account was already connected/disconnected. + +## 0.7.3 + +### Patch Changes + +- [#1086](https://github.com/wevm/wagmi/pull/1086) [`4e28d2a`](https://github.com/wevm/wagmi/commit/4e28d2ad4c2e6b3479b728563040b9529463cbcf) Thanks [@tmm](https://github.com/tmm)! - Exposed module types. + +- Updated dependencies [[`4e28d2a`](https://github.com/wevm/wagmi/commit/4e28d2ad4c2e6b3479b728563040b9529463cbcf)]: + - @wagmi/core@0.6.3 + +## 0.7.2 + +### Patch Changes + +- [#1080](https://github.com/wevm/wagmi/pull/1080) [`3be5e8b`](https://github.com/wevm/wagmi/commit/3be5e8b01e58ed40cc9dab7ef9533c0197cb74d0) Thanks [@tmm](https://github.com/tmm)! - Added `abitype` to `dependencies` so types ship correctly. + +- Updated dependencies [[`3be5e8b`](https://github.com/wevm/wagmi/commit/3be5e8b01e58ed40cc9dab7ef9533c0197cb74d0)]: + - @wagmi/core@0.6.2 + +## 0.7.1 + +### Patch Changes + +- [#1074](https://github.com/wevm/wagmi/pull/1074) [`8db807f`](https://github.com/wevm/wagmi/commit/8db807f16149aa278c2a7db9ee5245431db12173) Thanks [@IljaDaderko](https://github.com/IljaDaderko)! - Exported `EventListener` type + +- Updated dependencies [[`8db807f`](https://github.com/wevm/wagmi/commit/8db807f16149aa278c2a7db9ee5245431db12173)]: + - @wagmi/core@0.6.1 + +## 0.7.0 + +### Minor Changes + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: `usePrepareContractWrite` now throws when a `chainId` is specified and the end-user is on a different chain id (the wrong network). + + If you wish to defer this check until the click handler is pressed, you can place `chainId` in `useContractWrite` instead: + + ```diff + import { usePrepareContractWrite, useContractWrite } from 'wagmi' + import { optimism } from 'wagmi/chains' + + // ... + + const { config } = usePrepareContractWrite({ + addressOrName: '0xaf0326d92b97df1221759476b072abfd8084f9be', + contractInterface: ['function mint()'], + functionName: 'mint', + }) + const { write } = useContractWrite({ + ...config, + + chainId: optimism.id + }) + + ``` + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `usePrepareSendTransaction` hook will now only run when the end-user is connected to their wallet. + + This is to reach parity with `usePrepareContractWrite`. + + If the end-user is not connected, then the `usePrepareSendTransaction` hook will remain idle. + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: `usePrepareSendTransaction` now throws when a `chainId` is specified and the end-user is on a different chain id (the wrong network). + + If you wish to defer this check until the click handler is pressed, you can place `chainId` in `useContractWrite` instead: + + ```diff + import { usePrepareSendTransaction, useContractWrite } from 'wagmi' + import { optimism } from 'wagmi/chains' + + // ... + + const { config } = usePrepareSendTransaction({ + request: { + to: 'moxey.eth', + value: parseEther('1'), + }, + }) + const { sendTransaction } = useSendTransaction({ + ...config, + + chainId: optimism.id + }) + + ``` + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `useContractEvent` no longer accepts a `signerOrProvider` configuration option. + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `addressOrName` and `contractInterface` renamed to `address` and `abi` respectively for contract hooks: `useContract`, `useContractEvent`, `useContractRead`, `useContractReads`, `useContractInfiniteReads`, `useContractWrite`, `usePrepareContractWrite`. + + ```diff + import { useContractRead } from 'wagmi' + + const result = useContractRead({ + - addressOrName: '0x…', + + address: '0x…', + - contractInterface: […] as const, + + abi: […] as const, + functionName: 'balanceOf', + args: ['0x…'], + }) + ``` + + If you were using an ENS name instead of an address, you can resolve the name to an address before passing it to the action. + + ```diff + - import { useContractRead } from 'wagmi' + + import { useContractRead, useEnsAddress } from 'wagmi' + + + const { data: address} = useEnsAddress({ name: 'example.eth'}) + const result = useContractRead({ + - addressOrName: 'example.eth', + + address, + abi: […], + functionName: 'balanceOf', + args: ['0x…'], + }) + ``` + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Updated TypeScript generics for contract and typed data hooks. + + Adding a [const assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) to `abi` allows TypeScript to infer `functionName`, `args`, `overrides`, and return types for functions, and `eventName` and `listener` types for events. + + ```diff + import { useContractRead } from 'wagmi' + + const result = useContractRead({ + address: '0x…', + - abi: […], + + abi: […] as const, + functionName: 'balanceOf', // will autocomplete and catch typos + args: ['0x…'], // inferred based on `abi`, `functionName`, `args` + }) + result.data // inferred based on `functionName` + ``` + + This works for the following actions: `useContractRead`, `useContractWrite`, `usePrepareContractWrite`, `useContractReads`, `useContractInfiniteReads`, and `useContractEvent`. + + Adding a [const assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions) to `useSignTypedData`'s config option, `types`, allows TypeScript to infer `value`. + + ```diff + import { useSignTypedData } from 'wagmi' + + const result = useSignTypedData({ + domain: { + name: 'Ether Mail', + version: '1', + chainId: 1, + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', + }, + types: { + Person: [ + { name: 'name', type: 'string' }, + { name: 'wallet', type: 'address' }, + ], + Mail: [ + { name: 'from', type: 'Person' }, + { name: 'to', type: 'Person' }, + { name: 'contents', type: 'string' }, + ], + - }, + + } as const, + value: { // `value` is inferred based on `types` + from: { + name: 'Cow', + wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + }, + to: { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', + }, + contents: 'Hello, Bob!', + }, + }) + ``` + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Updated TypeScript version to `typescript@>=4.7.4`. + + `@wagmi/core` can now infer types based on [ABI](https://docs.soliditylang.org/en/v0.8.15/abi-spec.html#json) and [EIP-712](https://eips.ethereum.org/EIPS/eip-712) Typed Data definitions, giving you full end-to-end type-safety from your contracts to your frontend and incredible developer experience (e.g. autocomplete contract function names and catch misspellings, type contract function arguments, etc.). + + For this to work, you must upgrade to `typescript@>=4.7.4`. Why is TypeScript v4.7.4 or greater necessary? TypeScript 4.7.4 introduced the ability to [extend constraints on inferred type variables](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#extends-constraints-on-infer-type-variables), which is used extensively to help narrow types for ABIs. Good news! When upgrading TypeScript from 4.6 to 4.7 there are likely no [breaking changes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#breaking-changes) for your set up. + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Updated `paginatedIndexesConfig` `fn` parameter return type. `fn` now returns an array instead of a single object. + + ```diff + import { BigNumber } from 'ethers' + import { paginatedIndexesConfig, useContractInfiniteReads } from 'wagmi' + + useContractInfiniteReads({ + cacheKey: 'contracts', + ...paginatedIndexesConfig( + - (index) => ({ + + (index) => [{ + ...mlootContractConfig, + functionName: 'tokenURI', + args: [BigNumber.from(index)] as const, + - }), + + }], + { start: 0, perPage: 10, direction: 'increment' }, + ), + }) + ``` + +- [#941](https://github.com/wevm/wagmi/pull/941) [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `args` config option must now be an array for the following hooks: `useContractRead`, `useContractWrite`, `usePrepareContractWrite`, `useContractReads`, and `useContractInfiniteReads`. + + ```diff + import { useContractRead } from 'wagmi' + + const { data } = useContractRead({ + address: '0x…', + abi: […], + functionName: 'balanceOf', + - args: '0x…', + + args: ['0x…'], + }) + ``` + +### Patch Changes + +- [#940](https://github.com/wevm/wagmi/pull/940) [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663) Thanks [@jxom](https://github.com/jxom)! - The `useSigner` hook now accepts an optional `chainId` to use for signer initialization as an argument. + + ```tsx + import { useSigner } from "wagmi"; + import { optimism } from "wagmi/core"; + + // ... + + useSigner({ chainId: optimism.id }); + ``` + +- [#1061](https://github.com/wevm/wagmi/pull/1061) [`a4ffe8b`](https://github.com/wevm/wagmi/commit/a4ffe8b25516d5504685ae94579da4cd8c409329) Thanks [@alecananian](https://github.com/alecananian)! - Added Arbitrum Goerli Arbiscan block explorer + +- [#1050](https://github.com/wevm/wagmi/pull/1050) [`73d4d47`](https://github.com/wevm/wagmi/commit/73d4d47bc679f4f9a1cf46010fe2bf858c9d0b5c) Thanks [@jxom](https://github.com/jxom)! - update dependencies + + - `@coinbase/wallet-sdk@3.5.3` + - `@tanstack/query-sync-storage-persister@4.10.1` + - `@tanstack/react-query@4.10.1` + - `@tanstack/react-query-persist-client@4.10.1` + - `@walletconnect/ethereum-provider@1.8.0` + +- [#1048](https://github.com/wevm/wagmi/pull/1048) [`ed13074`](https://github.com/wevm/wagmi/commit/ed130747c0f28c1d9980a1328883e4000a60455e) Thanks [@Max-3-7](https://github.com/Max-3-7)! - Added support for Avalanche core wallet + +- [#1046](https://github.com/wevm/wagmi/pull/1046) [`ab9ecaa`](https://github.com/wevm/wagmi/commit/ab9ecaa74dfa4324279e167dd7e348319ef7d35d) Thanks [@jxom](https://github.com/jxom)! - make ethers block format validator compatible with Celo + +- Updated dependencies [[`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663), [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663), [`a4ffe8b`](https://github.com/wevm/wagmi/commit/a4ffe8b25516d5504685ae94579da4cd8c409329), [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663), [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c), [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663), [`ed13074`](https://github.com/wevm/wagmi/commit/ed130747c0f28c1d9980a1328883e4000a60455e), [`b6cb8f4`](https://github.com/wevm/wagmi/commit/b6cb8f4cd15eb13073bc7e9ecb4bfa2c261c0663), [`ab9ecaa`](https://github.com/wevm/wagmi/commit/ab9ecaa74dfa4324279e167dd7e348319ef7d35d), [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c), [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c), [`73d4d47`](https://github.com/wevm/wagmi/commit/73d4d47bc679f4f9a1cf46010fe2bf858c9d0b5c), [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c), [`0c96009`](https://github.com/wevm/wagmi/commit/0c96009398647a515a57f72ef25c32724f7c978c)]: + - @wagmi/core@0.6.0 + +## 0.6.8 + +### Patch Changes + +- [`0b77286b`](https://github.com/wevm/wagmi/commit/0b77286b89cb8603426cf5081872416c291a6531) Thanks [@jxom](https://github.com/jxom)! - Isolate wagmi's React Query `queryClient` instance. + +* [`8cb07462`](https://github.com/wevm/wagmi/commit/8cb07462acc3c5637398d11d2451f8b8e330d553) Thanks [@jxom](https://github.com/jxom)! - Added `chainId` as an argument to `watchBlockNumber`. + +- [`53c1a474`](https://github.com/wevm/wagmi/commit/53c1a4747d03b685e8cfbf55361fc2a56777fb06) Thanks [@tmm](https://github.com/tmm)! - Added missing `decimals` option to `Connector` `watchAsset` + +* [`4d74dd4f`](https://github.com/wevm/wagmi/commit/4d74dd4ff827ba5c43c3546a218f38cee45ea76a) Thanks [@jxom](https://github.com/jxom)! - Support ERC20 contracts that represent strings as bytes32 + +* Updated dependencies [[`8cb07462`](https://github.com/wevm/wagmi/commit/8cb07462acc3c5637398d11d2451f8b8e330d553), [`53c1a474`](https://github.com/wevm/wagmi/commit/53c1a4747d03b685e8cfbf55361fc2a56777fb06), [`4d74dd4f`](https://github.com/wevm/wagmi/commit/4d74dd4ff827ba5c43c3546a218f38cee45ea76a)]: + - @wagmi/core@0.5.8 + +## 0.6.7 + +### Patch Changes + +- [`aa51bc4d`](https://github.com/wevm/wagmi/commit/aa51bc4dc5683bf0178597d2fdb8f2e9d82e7970) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue in `CoinbaseWalletConnector` where the browser extension would unintendedly reset the network when the browser is refreshed. + +* [#955](https://github.com/wevm/wagmi/pull/955) [`e326cd80`](https://github.com/wevm/wagmi/commit/e326cd80fe65267db623eb6c80ccdd75572914cf) Thanks [@0xFlicker](https://github.com/0xFlicker)! - Added Infura RPC URL for Sepolia + +- [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `useProvider` & `getProvider` were not returning referentially equal providers. + +* [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where the `watch` option was not respecting the neighboring `chainId` option in `useBlockNumber`. + +- [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where block listeners (via `watch`) were firing excessively on L2 chains. + +- Updated dependencies [[`aa51bc4d`](https://github.com/wevm/wagmi/commit/aa51bc4dc5683bf0178597d2fdb8f2e9d82e7970), [`e326cd80`](https://github.com/wevm/wagmi/commit/e326cd80fe65267db623eb6c80ccdd75572914cf), [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69), [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69), [`cec14089`](https://github.com/wevm/wagmi/commit/cec14089500c86687226ab272b4c3fcb85ae3d69)]: + - @wagmi/core@0.5.7 + +## 0.6.6 + +### Patch Changes + +- [#936](https://github.com/wevm/wagmi/pull/936) [`3329d1f`](https://github.com/wevm/wagmi/commit/3329d1f5880431566e14ac1640f48d0975aec4c2) Thanks [@jxom](https://github.com/jxom)! - Added the ability to provide a custom logger to override how logs are broadcasted to the consumer in wagmi. + + A custom logger can be provided to the wagmi client via `logger`. + + ### API + + ```tsx + logger?: { + warn: typeof console.warn | null + } + ``` + + ### Examples + + **Passing in a custom logger** + + You can pass in a function to define your own custom logger. + + ```diff + + import { logWarn } from './logger'; + + const client = createClient({ + ... + + logger: { + + warn: message => logWarn(message) + + } + ... + }) + ``` + + **Disabling a logger** + + You can disable a logger by passing `null` as the value. + + ```diff + const client = createClient({ + ... + + logger: { + + warn: null + + } + ... + }) + ``` + +* [#889](https://github.com/wevm/wagmi/pull/889) [`27788ed`](https://github.com/wevm/wagmi/commit/27788ed989b5dc26849c7945fb91a92e56766018) Thanks [@jxom](https://github.com/jxom)! - Make multicall & readContracts more error robust + +* Updated dependencies [[`3329d1f`](https://github.com/wevm/wagmi/commit/3329d1f5880431566e14ac1640f48d0975aec4c2), [`27788ed`](https://github.com/wevm/wagmi/commit/27788ed989b5dc26849c7945fb91a92e56766018)]: + - @wagmi/core@0.5.6 + +## 0.6.5 + +### Patch Changes + +- [#912](https://github.com/wevm/wagmi/pull/912) [`e529e12`](https://github.com/wevm/wagmi/commit/e529e125c713ed3ef24a59c6bf226fe4deee7ac9) Thanks [@zouhangwithsweet](https://github.com/zouhangwithsweet)! - Added BitKeep to injected flags + +- [#912](https://github.com/wevm/wagmi/pull/910) Thanks [@mytangying](https://github.com/zouhangwithsweet)! - Added MathWallet to injected flags + +- [#904](https://github.com/wevm/wagmi/pull/904) [`c231058`](https://github.com/wevm/wagmi/commit/c23105850f335f8798031e14c7098b7dee8c2975) Thanks [@jxom](https://github.com/jxom)! - Removed `contractInterface` & `signer` from persisted query keys. + +- Updated dependencies [[`e529e12`](https://github.com/wevm/wagmi/commit/e529e125c713ed3ef24a59c6bf226fe4deee7ac9), [`c231058`](https://github.com/wevm/wagmi/commit/c23105850f335f8798031e14c7098b7dee8c2975)]: + - @wagmi/core@0.5.5 + +## 0.6.4 + +### Patch Changes + +- [#852](https://github.com/wevm/wagmi/pull/852) [`c3192d0`](https://github.com/wevm/wagmi/commit/c3192d0663aa332ae9edfd9dd49b333454013ab7) Thanks [@skeithc](https://github.com/skeithc)! - Added support for the Sepolia testnet + +- Updated dependencies [[`c3192d0`](https://github.com/wevm/wagmi/commit/c3192d0663aa332ae9edfd9dd49b333454013ab7)]: + - @wagmi/core@0.5.4 + +## 0.6.3 + +### Patch Changes + +- [#835](https://github.com/wevm/wagmi/pull/835) [`1b85e54`](https://github.com/wevm/wagmi/commit/1b85e54ae654e2564cf5bc2dae6411fe0a25875c) Thanks [@jxom](https://github.com/jxom)! - Update `@coinbase/wallet-sdk` to `3.4.1` + +* [#843](https://github.com/wevm/wagmi/pull/843) [`e77dee6`](https://github.com/wevm/wagmi/commit/e77dee6a606b8aac4279569c54cec8902476fee9) Thanks [@tmm](https://github.com/tmm)! - Fix `MockConnector` entrypoint path + +- [#834](https://github.com/wevm/wagmi/pull/834) [`9655879`](https://github.com/wevm/wagmi/commit/96558793b0319df47aefafa6b7b9c959068d491b) Thanks [@jxom](https://github.com/jxom)! - Update zustand to `4.0.0` + +* [#833](https://github.com/wevm/wagmi/pull/833) [`3ae6d0f`](https://github.com/wevm/wagmi/commit/3ae6d0f5e2d65432024272b43afe68a8f63bb7ea) Thanks [@jxom](https://github.com/jxom)! - Updated `react-query@4.0.0-beta.23` to `@tanstack/react-query@^4.0.10` + +* Updated dependencies [[`1b85e54`](https://github.com/wevm/wagmi/commit/1b85e54ae654e2564cf5bc2dae6411fe0a25875c), [`9655879`](https://github.com/wevm/wagmi/commit/96558793b0319df47aefafa6b7b9c959068d491b)]: + - @wagmi/core@0.5.3 + +## 0.6.2 + +### Patch Changes + +- [#823](https://github.com/wevm/wagmi/pull/823) [`10b8b78`](https://github.com/wevm/wagmi/commit/10b8b78605b7246b2c55b8d69f96663906e5cd20) Thanks [@tmm](https://github.com/tmm)! - Add Optimism Goerli to `chain` lookup. + +- Updated dependencies [[`10b8b78`](https://github.com/wevm/wagmi/commit/10b8b78605b7246b2c55b8d69f96663906e5cd20)]: + - @wagmi/core@0.5.2 + +## 0.6.1 + +### Patch Changes + +- [#767](https://github.com/wevm/wagmi/pull/767) [`e9392f3`](https://github.com/wevm/wagmi/commit/e9392f396e48e928bd9d2522e3ad671c589f08cb) Thanks [@klyap](https://github.com/klyap)! - Add Optimism Goerli chain ahead of [Kovan deprecation](https://dev.optimism.io/kovan-to-goerli). + +* [#817](https://github.com/wevm/wagmi/pull/817) [`7e5cac7`](https://github.com/wevm/wagmi/commit/7e5cac75815dcd8aa563462342a4853fc5207735) Thanks [@alecananian](https://github.com/alecananian)! - Added custom name mapping for 1inch Wallet injected provider + +- [#806](https://github.com/wevm/wagmi/pull/806) [`0b34e56`](https://github.com/wevm/wagmi/commit/0b34e56db97e6dcdb71088e0149b2d55ebc604a5) Thanks [@vmichalik](https://github.com/vmichalik)! - Fix canonical testnet native asset symbols by changing them to ETH + +* [#778](https://github.com/wevm/wagmi/pull/778) [`0892908`](https://github.com/wevm/wagmi/commit/08929084eeeba1a3a55aa098fa9d92a243685ad5) Thanks [@0xcadams](https://github.com/0xcadams)! - Add Arbitrum Goerli chain. + +* Updated dependencies [[`e9392f3`](https://github.com/wevm/wagmi/commit/e9392f396e48e928bd9d2522e3ad671c589f08cb), [`7e5cac7`](https://github.com/wevm/wagmi/commit/7e5cac75815dcd8aa563462342a4853fc5207735), [`0b34e56`](https://github.com/wevm/wagmi/commit/0b34e56db97e6dcdb71088e0149b2d55ebc604a5), [`0892908`](https://github.com/wevm/wagmi/commit/08929084eeeba1a3a55aa098fa9d92a243685ad5)]: + - @wagmi/core@0.5.1 + +## 0.6.0 + +### Minor Changes + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** The `useSendTransaction` hook's `data` now returns an object only consisting of `hash` & `wait`, and not the full [`TransactionResponse`](https://docs.ethers.io/v5/api/providers/types/#providers-TransactionResponse). + + If you require the full `TransactionResponse`, you can use `useTransaction`: + + ```diff + import { useSendTransaction, useTransaction } from 'wagmi' + + const { + data: { + hash, + wait, + - ...transaction + } + } = useSendTransaction(...) + + +const { data: transaction } = useTransaction({ hash }) + ``` + + > Why? The old implementation of `useSendTransaction` created a long-running async task, causing [UX pitfalls](https://wagmi.sh/docs/prepare-hooks#ux-pitfalls-without-prepare-hooks) when invoked in a click handler. + +* [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The configuration passed to the `useSendTransaction` hook now needs to be either: + + - prepared with the `usePrepareSendTransaction` hook **(new)**, or + - recklessly unprepared **(previous functionality)** + + > Why? [Read here](https://wagmi.sh/docs/prepare-hooks) + + ### Prepared usage + + ```diff + import { usePrepareSendTransaction, useSendTransaction } from 'wagmi' + + +const { config } = usePrepareSendTransaction({ + + request: { + + to: 'moxey.eth', + + value: parseEther('1'), + + } + +}) + + const { data } = useSendTransaction({ + - request: { + - to: 'moxey.eth', + - value: parseEther('1') + - } + + ...config + }) + ``` + + ### Recklessly unprepared usage + + If you are not ready to upgrade to `usePrepareSendTransaction`, it is possible to use `useSendTransaction` without preparing the configuration first by passing `mode: 'recklesslyUnprepared'`. + + ```diff + import { useSendTransaction } from 'wagmi' + + const { data } = useSendTransaction({ + + mode: 'recklesslyUnprepared', + request: { + to: 'moxey.eth', + value: parseEther('1'), + } + }) + ``` + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: If a `chainId` is passed to `useContractWrite` or `useSendTransaction`, it will no longer attempt to switch chain before sending the transaction. Instead, it will throw an error if the user is on the wrong chain. + + > Why? Eagerly prompting to switch chain in these actions created a long-running async task that that makes [iOS App Links](https://wagmi.sh/docs/prepare-hooks#ios-app-link-constraints) vulnerable. + +* [#760](https://github.com/wevm/wagmi/pull/760) [`d8af6bf`](https://github.com/wevm/wagmi/commit/d8af6bf50885aec110ae4d64716642453aa27896) Thanks [@tmm](https://github.com/tmm)! - **Breaking:** `alchemyProvider` and `infuraProvider` now use a generic `apiKey` configuration option instead of `alchemyId` and `infuraId`. + + ```diff + import { alchemyProvider } from '@wagmi/core/providers/alchemy' + import { infuraProvider } from '@wagmi/core/providers/infura' + + alchemyProvider({ + - alchemyId: 'yourAlchemyApiKey', + + apiKey: 'yourAlchemyApiKey', + }) + + infuraProvider({ + - infuraId: 'yourInfuraApiKey', + + apiKey: 'yourInfuraApiKey', + }) + ``` + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - Added the `usePrepareContractWrite` hook that eagerly fetches the parameters required for sending a contract write transaction such as the gas estimate. + + It returns config to be passed through to `useContractWrite`. + + ```ts + const { config } = usePrepareContractWrite({ + addressOrName: "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + contractInterface: wagmigotchiABI, + functionName: "feed", + }); + const { write } = useContractWrite(config); + ``` + +* [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** When `useSendTransaction` is in "prepare mode" (used with `usePrepareSendTransaction`), `sendTransaction`/`sendTransactionAsync` will be `undefined` until the configuration has been prepared. Ensure that your usage reflects this. + + ```tsx + const { config } = usePrepareSendTransaction({ ... }) + const { sendTransaction } = useSendTransaction(config) + + + ``` + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - Added the `usePrepareSendTransaction` hook that eagerly fetches the parameters required for sending a transaction such as the gas estimate and resolving an ENS address (if required). + + It returns config to be passed through to `useSendTransaction`. + + ```ts + import { usePrepareSendTransaction, useSendTransaction } from "@wagmi/core"; + + const { config } = usePrepareSendTransaction({ + request: { + to: "moxey.eth", + value: parseEther("1"), + }, + }); + const { sendTransaction } = useSendTransaction(config); + ``` + +* [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** The `sendTransaction`/`sendTransactionAsync` configuration object has now been altered to only accept "reckless" configuration. If one or more of these values are set, it can lead to [UX pitfalls](https://wagmi.sh/docs/prepare-hooks#ux-pitfalls-without-prepare-hooks). + + ```diff + + ``` + +- [#727](https://github.com/wevm/wagmi/pull/727) [`ac3b9b8`](https://github.com/wevm/wagmi/commit/ac3b9b87f80cb45b65d003f09d916d7d1427a62e) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: Moved the `pollingInterval` config option from the chain provider config to `configureChains` config. + + ```diff + const { chains, provider } = configureChains( + [chain.mainnet, chain.polygon], + [ + - alchemyProvider({ apiKey, pollingInterval: 5000 }), + - publicProvider({ pollingInterval: 5000 }) + + alchemyProvider({ apiKey }), + + publicProvider() + ], + + { pollingInterval: 5000 } + ) + ``` + +* [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** When `useContractWrite` is in "prepare mode" (used with `usePrepareContractWrite`), `write`/`writeAsync` will be `undefined` until the configuration has been prepared. Ensure that your usage reflects this. + + ```tsx + const { config } = usePrepareContractWrite({ ... }) + const { write } = useContractWrite(config) + + + ``` + +- [#658](https://github.com/wevm/wagmi/pull/658) [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The configuration passed to the `useContractWrite` hook now needs to be either: + + - prepared with the `usePrepareContractWrite` hook **(new)**, or + - recklessly unprepared **(previous functionality)** + + > Why? [Read here](https://wagmi.sh/docs/prepare-hooks) + + ### Prepared usage + + ```diff + import { usePrepareContractWrite, useContractWrite } from 'wagmi' + + +const { config } = usePrepareContractWrite({ + + addressOrName: '0x...', + + contractInterface: wagmiAbi, + + functionName: 'mint', + + args: [tokenId] + +}) + + const { data } = useContractWrite({ + - addressOrName: '0x...', + - contractInterface: wagmiAbi, + - functionName: 'mint', + - args: [tokenId], + + ...config + }) + ``` + + ### Recklessly unprepared usage + + If you are not ready to upgrade to `usePrepareContractWrite`, it is possible to use `useContractWrite` without preparing the configuration first by passing `mode: 'recklesslyUnprepared'`. + + ```diff + import { useContractWrite } from 'wagmi' + + const { data } = useContractWrite({ + + mode: 'recklesslyUnprepared', + addressOrName: '0x...', + contractInterface: wagmiAbi, + functionName: 'mint', + args: [tokenId], + }) + ``` + +### Patch Changes + +- [#733](https://github.com/wevm/wagmi/pull/733) [`6232487`](https://github.com/wevm/wagmi/commit/623248703bc728d539e28bf8a89b8ab22f0a5703) Thanks [@tmm](https://github.com/tmm)! - Add mock connector entrypoint + +* [#762](https://github.com/wevm/wagmi/pull/762) [`ccaeed5`](https://github.com/wevm/wagmi/commit/ccaeed53d731f51879e0cdd5648797a32f7d7a31) Thanks [@jxom](https://github.com/jxom)! - Fix `useContractRead` return value unexpectedly returning null for falsy values + +- [#734](https://github.com/wevm/wagmi/pull/734) [`7c2fa04`](https://github.com/wevm/wagmi/commit/7c2fa04e9b695840d6fa088e1f8d069f3c916551) Thanks [@jxom](https://github.com/jxom)! - Fix issue where `useProvider` & `useWebSocketProvider` would not update when `chainId` config changes + +* [#739](https://github.com/wevm/wagmi/pull/739) [`c2295a5`](https://github.com/wevm/wagmi/commit/c2295a56cc86d02cc6602e2b4557b8ab9a091a3f) Thanks [@tmm](https://github.com/tmm)! - Fix balance formatting for tokens that do not have 18 decimals. + +- [#759](https://github.com/wevm/wagmi/pull/759) [`959953d`](https://github.com/wevm/wagmi/commit/959953d1f5b3e8189bac56de245c62333470d18e) Thanks [@tmm](https://github.com/tmm)! - Added `useTransaction` hook: + + ```ts + import { useTransaction } from "wagmi"; + + const result = useTransaction({ + hash: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060", + }); + ``` + +- Updated dependencies [[`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432), [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432), [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432), [`d8af6bf`](https://github.com/wevm/wagmi/commit/d8af6bf50885aec110ae4d64716642453aa27896), [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432), [`c2295a5`](https://github.com/wevm/wagmi/commit/c2295a56cc86d02cc6602e2b4557b8ab9a091a3f), [`ac3b9b8`](https://github.com/wevm/wagmi/commit/ac3b9b87f80cb45b65d003f09d916d7d1427a62e), [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432), [`d70c115`](https://github.com/wevm/wagmi/commit/d70c115131f299fb61f87867b6ac4218e0bcf432), [`959953d`](https://github.com/wevm/wagmi/commit/959953d1f5b3e8189bac56de245c62333470d18e)]: + - @wagmi/core@0.5.0 + +## 0.5.11 + +### Patch Changes + +- [`4c7d123`](https://github.com/wevm/wagmi/commit/4c7d123c8e74f93e770096857eb8c40ce0a47681) Thanks [@jxom](https://github.com/jxom)! - Fix issue where `useProvider` & `useWebSocketProvider` would not update when `chainId` config changes + +## 0.5.10 + +### Patch Changes + +- [#713](https://github.com/tmm/wagmi/pull/713) [`08b0113`](https://github.com/tmm/wagmi/commit/08b0113bef9f32dceab2ecd823b1ee00f9bdc45d) Thanks [@jxom](https://github.com/jxom)! - Fix an issue where the `useContractRead` query function could return `undefined` instead of a serializable `null`. + +* [#725](https://github.com/tmm/wagmi/pull/725) [`b976920`](https://github.com/tmm/wagmi/commit/b97692051d778d7e112872663832c97963a8029a) Thanks [@tmm](https://github.com/tmm)! - Lock `react-query` version + +- [#721](https://github.com/tmm/wagmi/pull/721) [`abea25f`](https://github.com/tmm/wagmi/commit/abea25fd15d81d1ecaec9d3fbd687042ab29b1e6) Thanks [@tmm](https://github.com/tmm)! - Add `name` to `useToken` `data` value. + +- Updated dependencies [[`abea25f`](https://github.com/tmm/wagmi/commit/abea25fd15d81d1ecaec9d3fbd687042ab29b1e6), [`abea25f`](https://github.com/tmm/wagmi/commit/abea25fd15d81d1ecaec9d3fbd687042ab29b1e6)]: + - @wagmi/core@0.4.9 + +## 0.5.9 + +### Patch Changes + +- [#677](https://github.com/tmm/wagmi/pull/677) [`35e4219`](https://github.com/tmm/wagmi/commit/35e42199af9dd346549c1718e144728f55b8d7dd) Thanks [@jxom](https://github.com/jxom)! - Move `parseContractResult` to `@wagmi/core` + +* [#677](https://github.com/tmm/wagmi/pull/677) [`35e4219`](https://github.com/tmm/wagmi/commit/35e42199af9dd346549c1718e144728f55b8d7dd) Thanks [@jxom](https://github.com/jxom)! - Parse tuples correctly in `parseContractResult` + +* Updated dependencies [[`35e4219`](https://github.com/tmm/wagmi/commit/35e42199af9dd346549c1718e144728f55b8d7dd)]: + - @wagmi/core@0.4.7 + +## 0.5.8 + +### Patch Changes + +- [#670](https://github.com/tmm/wagmi/pull/670) [`29a0d21`](https://github.com/tmm/wagmi/commit/29a0d21ee83995559f63542778dfa805f15e7441) Thanks [@tmm](https://github.com/tmm)! - Fix broken release not containing `deepEqual` from `@wagmi/core`. + +- Updated dependencies [[`29a0d21`](https://github.com/tmm/wagmi/commit/29a0d21ee83995559f63542778dfa805f15e7441)]: + - @wagmi/core@0.4.6 + +## 0.5.7 + +### Patch Changes + +- [#659](https://github.com/tmm/wagmi/pull/659) [`be76586`](https://github.com/tmm/wagmi/commit/be76586431238dc5a0970a6f10a3dff9faa8ca2d) Thanks [@jxom](https://github.com/jxom)! - Added an `isDataEqual` config option to `useContractRead`, `useContractReads` & `useContractInfiniteReads` to define whether or not that data has changed. Defaults to `deepEqual`. + +* [#659](https://github.com/tmm/wagmi/pull/659) [`be76586`](https://github.com/tmm/wagmi/commit/be76586431238dc5a0970a6f10a3dff9faa8ca2d) Thanks [@jxom](https://github.com/jxom)! - Added `onBlock` config to `useBlockNumber` + +- [#659](https://github.com/tmm/wagmi/pull/659) [`be76586`](https://github.com/tmm/wagmi/commit/be76586431238dc5a0970a6f10a3dff9faa8ca2d) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue where `useContractRead` & `useContractReads` would return unstable data. + +## 0.5.6 + +### Patch Changes + +- [#654](https://github.com/tmm/wagmi/pull/654) [`e66530b`](https://github.com/tmm/wagmi/commit/e66530bf4881b3533c528f8c5a5f41be0eab0a64) Thanks [@jxom](https://github.com/jxom)! - omit `contractInterface` from `useContractRead` & `useContractReads` query key hash + +* [#654](https://github.com/tmm/wagmi/pull/654) [`e66530b`](https://github.com/tmm/wagmi/commit/e66530bf4881b3533c528f8c5a5f41be0eab0a64) Thanks [@jxom](https://github.com/jxom)! - fix `multicall` returning nullish data for all calls unexpectedly + +* Updated dependencies [[`e66530b`](https://github.com/tmm/wagmi/commit/e66530bf4881b3533c528f8c5a5f41be0eab0a64)]: + - @wagmi/core@0.4.5 + +## 0.5.5 + +### Patch Changes + +- [#629](https://github.com/tmm/wagmi/pull/629) [`199db71`](https://github.com/tmm/wagmi/commit/199db7165eed43d36cb882d373f95e7c49212f23) Thanks [@jxom](https://github.com/jxom)! - Add `wagmi/actions` entrypoint that exports imperative `@wagmi/core` actions + +* [#616](https://github.com/tmm/wagmi/pull/616) [`7a7a17a`](https://github.com/tmm/wagmi/commit/7a7a17a46d4c9e6465cc46a111b5fe8a56109f1b) Thanks [@tmm](https://github.com/tmm)! - Adds `UNSTABLE_shimOnConnectSelectAccount` flag. With this flag and "disconnected" with `shimDisconnect` enabled, the user is prompted to select a different MetaMask account (than the currently connected account) when trying to connect (e.g. `useConnect`/`connect` action). + +* Updated dependencies [[`7a7a17a`](https://github.com/tmm/wagmi/commit/7a7a17a46d4c9e6465cc46a111b5fe8a56109f1b)]: + - @wagmi/core@0.4.4 + +## 0.5.4 + +### Patch Changes + +- [#631](https://github.com/tmm/wagmi/pull/631) [`a780e32`](https://github.com/tmm/wagmi/commit/a780e32e91a0072c795fa0b5a6111302768e2a01) Thanks [@tmm](https://github.com/tmm)! - Fix WalletConnect stale session + +- Updated dependencies [[`a780e32`](https://github.com/tmm/wagmi/commit/a780e32e91a0072c795fa0b5a6111302768e2a01)]: + - @wagmi/core@0.4.3 + +## 0.5.3 + +### Patch Changes + +- [#627](https://github.com/tmm/wagmi/pull/627) [`5985530`](https://github.com/tmm/wagmi/commit/59855301d138313e83a607b3f05053e9f46a78a8) Thanks [@jxom](https://github.com/jxom)! - equalityFn in `useSyncExternalStoreWithTracked` should return truthy when there are no tracked keys. + +## 0.5.2 + +### Patch Changes + +- [#624](https://github.com/tmm/wagmi/pull/624) [`416fa7e`](https://github.com/tmm/wagmi/commit/416fa7ee1f8019ab86e33fb93783ffddecc02c49) Thanks [@jxom](https://github.com/jxom)! - Fix broken `WebSocketProvider` type defs + +- Updated dependencies [[`416fa7e`](https://github.com/tmm/wagmi/commit/416fa7ee1f8019ab86e33fb93783ffddecc02c49)]: + - @wagmi/core@0.4.2 + +## 0.5.1 + +### Patch Changes + +- [#622](https://github.com/tmm/wagmi/pull/622) [`d171581`](https://github.com/tmm/wagmi/commit/d171581464891dd870d97b6232205da0cb152d9b) Thanks [@tmm](https://github.com/tmm)! - Use `domain.chainId` to validate and switch chain before signing in `useSignTypedData`. + +* [#618](https://github.com/tmm/wagmi/pull/618) [`a5138e8`](https://github.com/tmm/wagmi/commit/a5138e82a00e4d9469ad78c97b2d34200d7f1fbe) Thanks [@tmm](https://github.com/tmm)! - Fix adding chains when using MetaMask mobile app, add `publicRpcUrls` constant, and default to public endpoint when adding chain. + +* Updated dependencies [[`d171581`](https://github.com/tmm/wagmi/commit/d171581464891dd870d97b6232205da0cb152d9b), [`a5138e8`](https://github.com/tmm/wagmi/commit/a5138e82a00e4d9469ad78c97b2d34200d7f1fbe)]: + - @wagmi/core@0.4.1 + +## 0.5.0 + +### Minor Changes + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `useContractWrite` hook parameters have been consolidated into a singular config parameter. + + Before: + + ```tsx + useContractWrite( + { + addressOrName: mlootContractAddress, + contractInterface: mlootABI, + }, + "claim", + ); + ``` + + After: + + ```tsx + useContractWrite({ + addressOrName: mlootContractAddress, + contractInterface: mlootABI, + functionName: "claim", + }); + ``` + +* [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `useContractEvent` hook parameters have been consolidated into a singular config parameter. + + Before: + + ```tsx + useContractEvent( + { + addressOrName: uniContractAddress, + contractInterface: erc20ABI, + }, + 'Transfer', + listener, + ), + ``` + + After: + + ```tsx + useContractEvent({ + addressOrName: uniContractAddress, + contractInterface: erc20ABI, + eventName: "Transfer", + listener, + }); + ``` + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `client` prop is now required on `WagmiConfig`. + + ````diff + ```tsx + import { + createClient, + + configureChains, + + defaultChains + } from 'wagmi' + +import { publicProvider } from 'wagmi/providers/public' + + +const { provider, webSocketProvider } = configureChains(defaultChains, [ + + publicProvider(), + +]) + + +const client = createClient({ + + provider, + + webSocketProvider, + +}) + + function App() { + return ( + + + + ) + } + ```` + +* [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `provider` config option is now required on `createClient`. It is recommended to pass the [`provider` given from `configureChains`](https://wagmi.sh/docs/providers/configuring-chains). + + ```diff + import { + createClient, + + defaultChains, + + configureChains + } from 'wagmi' + +import { publicProvider } from 'wagmi/providers/publicProvider' + + +const { provider } = configureChains(defaultChains, [ + + publicProvider + +]) + + const client = createClient({ + + provider + }) + ``` + + If you previously used an ethers.js Provider, you now need to provide your `chains` on the Provider instance: + + ```diff + import { + createClient, + + defaultChains + } from 'wagmi' + import ethers from 'ethers' + + const client = createClient({ + - provider: getDefaultProvider() + + provider: Object.assign(getDefaultProvider(), { chains: defaultChains }) + }) + ``` + +- [`4f8f3c0`](https://github.com/tmm/wagmi/commit/4f8f3c0d65383bd8bbdfc3f1033adfdb11d80ebb) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Use ethereum-lists chains symbols + +* [#582](https://github.com/tmm/wagmi/pull/582) [`b03830a`](https://github.com/tmm/wagmi/commit/b03830a54465215c2526f9509543fe2c978bfe70) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The following changes were made to the `useAccount` return value: + + ### The `data` value is now `address` & `connector` + + ```diff + { + - data?: { + - address: string + - connector: Connector + - } + + address?: string + + connector?: Connector + } + ``` + + ### Global connection status values have been added + + The following global connection status values have been added: + + ```diff + { + + isConnecting: boolean + + isReconnecting: boolean + + isConnected: boolean + + isDisconnected: boolean + + status: 'connecting' | 'reconnecting' | 'connected' | 'disconnected' + } + ``` + + The `useAccount` hook is now aware of any connection event in your application, so now you can use these connection status values to determine if your user is connected, disconnected or connecting to a wallet on a global scope. + + ### `error`, states & `refetch` values have been removed + + Since the `useAccount` hook never dealt with asynchronous data, all of these values were + redundant & unused. + + ```diff + { + - error?: Error + - isIdle: boolean + - isLoading: boolean + - isFetching: boolean + - isSuccess: boolean + - isError: boolean + - isFetched: boolean + - isRefetching: boolean + - refetch: (options: { + - throwOnError: boolean + - cancelRefetch: boolean + - }) => Promise<{ + - address: string + - connector: Connector + - }> + - status: 'idle' | 'error' | 'loading' | 'success' + } + ``` + + ### Summary of changes + + Below is the whole diff of changes to the `useAccount` return value. + + ```diff + { + - data?: { + - address: string + - connector: Connector + - } + + address?: string + + connector?: Connector + - error?: Error + - isIdle: boolean + - isLoading: boolean + - isFetching: boolean + - isSuccess: boolean + - isError: boolean + - isFetched: boolean + - isRefetching: boolean + + isConnecting: boolean + + isReconnecting: boolean + + isConnected: boolean + + isDisconnected: boolean + - refetch: (options: { + - throwOnError: boolean + - cancelRefetch: boolean + - }) => Promise<{ + - address: string + - connector: Connector + - }> + - status: 'idle' | 'error' | 'loading' | 'success' + + status: 'connecting' | 'reconnecting' | 'connected' | 'disconnected' + } + ``` + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking:** Removed the `chainId` parameter from `connectors` function on `createClient`. + + ```diff + const client = createClient({ + - connectors({ chainId }) { + + connectors() { + ... + } + }) + ``` + + If you previously derived RPC URLs from the `chainId` on `connectors`, you can now remove that logic as `wagmi` now handles RPC URLs internally when used with `configureChains`. + + ```diff + import { + chain, + + configureChains, + createClient + } from 'wagmi'; + + +import { publicProvider } from 'wagmi/providers/public' + + import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet' + import { InjectedConnector } from 'wagmi/connectors/injected' + import { MetaMaskConnector } from 'wagmi/connectors/metaMask' + import { WalletConnectConnector } from 'wagmi/connectors/walletConnect' + + +const { chains } = configureChains( + + [chain.mainnet], + + [publicProvider()] + +); + + const client = createClient({ + - connectors({ chainId }) { + - const chain = chains.find((x) => x.id === chainId) ?? defaultChain + - const rpcUrl = chain.rpcUrls.alchemy + - ? `${chain.rpcUrls.alchemy}/${alchemyId}` + - : chain.rpcUrls.default + - return [ + + connectors: [ + new MetaMaskConnector({ chains }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: 'wagmi', + - chainId: chain.id, + - jsonRpcUrl: rpcUrl, + }, + }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + - rpc: { [chain.id]: rpcUrl }, + }, + }), + new InjectedConnector({ + chains, + options: { name: 'Injected' }, + }), + ] + - }, + }) + ``` + +* [#596](https://github.com/tmm/wagmi/pull/596) [`a770af7`](https://github.com/tmm/wagmi/commit/a770af7d2cb214b6620d5341115f1e938e1e77ff) Thanks [@tmm](https://github.com/tmm)! - **Breaking**: `TypedDataDomain` and `TypedDataField` types were removed and incorporated into `SignTypedDataArgs`. + +- [#582](https://github.com/tmm/wagmi/pull/582) [`b03830a`](https://github.com/tmm/wagmi/commit/b03830a54465215c2526f9509543fe2c978bfe70) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The following changes were made to the `useAccount` configuration: + + ## `onConnect` has been added + + The `onConnect` callback is invoked when the account connects. + + It provides the connected address & connector, as well as a `isReconnected` flag for if the user reconnected via `autoConnect`. + + ```tsx + const account = useAccount({ + onConnect({ address, connector, isReconnected }) { + console.log("Connected"); + }, + }); + ``` + + ## `onDisconnect` has been added + + The `onDisconnect` callback is invoked when the account disconnected. + + ```tsx + const account = useAccount({ + onDisconnect() { + console.log("Disconnected"); + }, + }); + ``` + + ## `suspense` has been removed + + The `useAccount` hook is a synchronous hook – so `suspense` never worked. + + ```diff + const account = useAccount({ + - suspense: true, + }) + ``` + + ## `onError` has been removed + + The `useAccount` hook never had any error definitions – so `onError` was never invoked. + + ```diff + const account = useAccount({ + - onError(error) { + - console.log('Error', error) + - }, + }) + ``` + + ## `onSettled` has been removed + + The `useAccount` hook is a synchronous hook. `onSettled` was always invoked immediately. + + ```diff + const account = useAccount({ + - onSettled(data) { + - console.log('Settled', data) + - }, + }) + ``` + + If you used `onSettled`, you can move the code beneath the `useAccount` hook: + + ```diff + const account = useAccount({ + - onSettled(data) { + - console.log('Address:', data.address) + - }, + }) + + console.log('Address:', account.address) + ``` + + ## `onSuccess` has been removed + + The `useAccount` hook is a synchronous hook. `onSuccess` was always invoked immediately. + + ```diff + const account = useAccount({ + - onSuccess(data) { + - console.log('Success', data) + - }, + }) + ``` + + If you used `onSuccess`, you can move the code beneath the `useAccount` hook: + + ```diff + const account = useAccount({ + - onSuccess(data) { + - console.log('Address:', data.address) + - }, + }) + + console.log('Address:', account.address) + ``` + +* [#582](https://github.com/tmm/wagmi/pull/582) [`b03830a`](https://github.com/tmm/wagmi/commit/b03830a54465215c2526f9509543fe2c978bfe70) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The following changes were made to the `useConnect` return value: + + ### Connection status flags have been moved + + The `isConnected`, `isConnecting`, `isReconnecting` & `isDisconnected` flags have been moved to the `useAccount` hook. + + ```diff + -import { useConnect } from 'wagmi' + +import { useAccount } from 'wagmi' + + function App() { + const { + isConnected, + isConnecting, + isConnecting, + isDisconnected + - } = useConnect() + + } = useAccount() + } + ``` + + ### New `connect` mutation status flags have been added + + The `isLoading`, `isSuccess` and `isError` flags have been added to `useConnect`. + + These flags represent the **local** async state of `useConnect`. + + ### `activeConnector` has been removed + + The `activeConnector` value has been removed. You can find the active connector on `useAccount`. + + ```diff + -import { useConnect } from 'wagmi' + +import { useAccount } from 'wagmi' + + function App() { + - const { activeConnector } = useConnect() + + const { connector } = useAccount() + } + ``` + +- [#582](https://github.com/tmm/wagmi/pull/582) [`b03830a`](https://github.com/tmm/wagmi/commit/b03830a54465215c2526f9509543fe2c978bfe70) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The following changes were made to the `useConnect` configuration: + + ### `onBeforeConnect` has been renamed + + The `onBeforeConnect` callback has been renamed to `onMutate` + + ### `onConnect` has been renamed + + The `onConnect` callback has been renamed to `onSuccess` + +* [#582](https://github.com/tmm/wagmi/pull/582) [`b03830a`](https://github.com/tmm/wagmi/commit/b03830a54465215c2526f9509543fe2c978bfe70) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `connector` parameter to `connect` & `connectAsync` now has to be in the config object parameter shape. + + ```diff + import { useConnect } from 'wagmi' + + function App() { + const { connect, connectors } = useConnect() + + return ( + + ) + } + ``` + +- [#582](https://github.com/tmm/wagmi/pull/582) [`b03830a`](https://github.com/tmm/wagmi/commit/b03830a54465215c2526f9509543fe2c978bfe70) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The "switch network" functionality has been moved out of `useNetwork` into a new `useSwitchNetwork` hook. + + The `useNetwork` hook now accepts no configuration and only returns `chain` (renamed from `activeChain`) and `chains`. + + ```diff + import { + useNetwork + + useSwitchNetwork + } from 'wagmi' + + const { + - activeChain + + chain, + chains, + - data, + - error, + - isError, + - isIdle, + - isLoading, + - isSuccess, + - pendingChainId, + - switchNetwork, + - switchNetworkAsync, + - status, + - reset, + -} = useNetwork({ + - chainId: 69, + - onError(error) {}, + - onMutate(args) {}, + - onSettled(data, error) {}, + - onSuccess(data) {} + -}) + +} = useNetwork() + + +const { + + data, + + error, + + isError, + + isIdle, + + isLoading, + + isSuccess, + + pendingChainId, + + switchNetwork, + + switchNetworkAsync, + + status, + + reset, + +} = useSwitchNetwork({ + + chainId: 69, + + onError(error) {}, + + onMutate(args) {}, + + onSettled(data, error) {}, + + onSuccess(data) {} + +}) + ``` + +* [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - **Breaking**: The `useContractRead` hook parameters have been consolidated into a singular config parameter. + + Before: + + ```tsx + useContractRead( + { + addressOrName: wagmigotchiContractAddress, + contractInterface: wagmigotchiABI, + }, + "love", + { args: "0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c" }, + ); + ``` + + After: + + ```tsx + useContractRead({ + addressOrName: wagmigotchiContractAddress, + contractInterface: wagmigotchiABI, + functionName: "love", + args: "0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c", + }); + ``` + +### Patch Changes + +- [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - Added a `useContractInfiniteReads` hook that provides the ability to call multiple ethers Contract read-only methods with "infinite scrolling" ("fetch more") support. Useful for rendering a dynamic list of contract data. + + [Learn more](https://wagmi.sh/docs/hooks/useContractInfiniteReads) + +* [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4) Thanks [@jxom](https://github.com/jxom)! - Added a `useContractReads` hook that provides the ability to batch up multiple ethers Contract read-only methods. + + [Learn more](https://wagmi.sh/docs/hooks/useContractReads) + +- [#598](https://github.com/tmm/wagmi/pull/598) [`fef26bf`](https://github.com/tmm/wagmi/commit/fef26bf8aef76fc9621e3cd54d4e0ca8f69abb38) Thanks [@markdalgleish](https://github.com/markdalgleish)! - Update `@coinbase/wallet-sdk` to fix errors when connecting with older versions of the Coinbase Wallet extension and mobile app. + +* [#611](https://github.com/tmm/wagmi/pull/611) [`3089c34`](https://github.com/tmm/wagmi/commit/3089c34196d4034acabac031e0a2f7ee63ae30cc) Thanks [@tmm](https://github.com/tmm)! - Added `chainId` config parameter for `useContractWrite` and `useSendTransaction`. + + If `chainId` is provided, the connector will validate that `chainId` is the active chain before sending a transaction (and switch to `chainId` if necessary). + +* Updated dependencies [[`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4), [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4), [`4f8f3c0`](https://github.com/tmm/wagmi/commit/4f8f3c0d65383bd8bbdfc3f1033adfdb11d80ebb), [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4), [`3089c34`](https://github.com/tmm/wagmi/commit/3089c34196d4034acabac031e0a2f7ee63ae30cc), [`a770af7`](https://github.com/tmm/wagmi/commit/a770af7d2cb214b6620d5341115f1e938e1e77ff), [`4f8f3c0`](https://github.com/tmm/wagmi/commit/4f8f3c0d65383bd8bbdfc3f1033adfdb11d80ebb), [`fef26bf`](https://github.com/tmm/wagmi/commit/fef26bf8aef76fc9621e3cd54d4e0ca8f69abb38), [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4), [`3089c34`](https://github.com/tmm/wagmi/commit/3089c34196d4034acabac031e0a2f7ee63ae30cc), [`b03830a`](https://github.com/tmm/wagmi/commit/b03830a54465215c2526f9509543fe2c978bfe70), [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4), [`fc94210`](https://github.com/tmm/wagmi/commit/fc94210b67daa91aa164625dfe189d5b6c2f92d4)]: + - @wagmi/core@0.4.0 + +## 0.4.12 + +### Patch Changes + +- [#570](https://github.com/tmm/wagmi/pull/570) [`0e3fe15`](https://github.com/tmm/wagmi/commit/0e3fe15445377f35d6f4142b49bf1c96bfeb62cd) Thanks [@tmm](https://github.com/tmm)! - adds chain for [Foundry](https://github.com/foundry-rs) + +- Updated dependencies [[`0e3fe15`](https://github.com/tmm/wagmi/commit/0e3fe15445377f35d6f4142b49bf1c96bfeb62cd)]: + - @wagmi/core@0.3.8 + +## 0.4.11 + +### Patch Changes + +- [#566](https://github.com/tmm/wagmi/pull/566) [`8713c00`](https://github.com/tmm/wagmi/commit/8713c00f70fcac3afef4ba183e3c87c6d3cbbf65) Thanks [@jxom](https://github.com/jxom)! - Fixed `parseContractResult` breaking `useContractRead` for more complex contract types + +## 0.4.10 + +### Patch Changes + +- [`20a1ab7`](https://github.com/tmm/wagmi/commit/20a1ab7bd02a24c4f1ea02be1bc3ecfbe4abc584) Thanks [@jxom](https://github.com/jxom)! - Updated to `react-query@4.0.0-beta.23` + +* [`20a1ab7`](https://github.com/tmm/wagmi/commit/20a1ab7bd02a24c4f1ea02be1bc3ecfbe4abc584) Thanks [@jxom](https://github.com/jxom)! - Fixed an issue in `useContractRead` where contract structs wouldn't be parsed back to an ethers `Result` correctly. + +## 0.4.9 + +### Patch Changes + +- [#555](https://github.com/tmm/wagmi/pull/555) [`8bf014d`](https://github.com/tmm/wagmi/commit/8bf014d8167e9f9feb1fd91488aab42dd51c92af) Thanks [@tmm](https://github.com/tmm)! - wire up `useEnsName` `chainId` + +## 0.4.8 + +### Patch Changes + +- [#550](https://github.com/tmm/wagmi/pull/550) [`2a5313e`](https://github.com/tmm/wagmi/commit/2a5313e8cbc9ba6335e8e4b85e43862c9b711bd3) Thanks [@tmm](https://github.com/tmm)! - fix `CoinbaseWalletConnector` possible type error + +* [#548](https://github.com/tmm/wagmi/pull/548) [`0c48719`](https://github.com/tmm/wagmi/commit/0c487199f2421f042abc1f1d139468ccbbc5646a) Thanks [@dohaki](https://github.com/dohaki)! - add ensAddress to Chain type + +- [#549](https://github.com/tmm/wagmi/pull/549) [`89b3a74`](https://github.com/tmm/wagmi/commit/89b3a74ead4234daacd0dcf8506659887ebf0553) Thanks [@tmm](https://github.com/tmm)! - Turns on [`noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess=) and [`strictNullChecks`](https://www.typescriptlang.org/tsconfig#strictNullChecks=) for better runtime safety. + +- Updated dependencies [[`2a5313e`](https://github.com/tmm/wagmi/commit/2a5313e8cbc9ba6335e8e4b85e43862c9b711bd3), [`0c48719`](https://github.com/tmm/wagmi/commit/0c487199f2421f042abc1f1d139468ccbbc5646a), [`89b3a74`](https://github.com/tmm/wagmi/commit/89b3a74ead4234daacd0dcf8506659887ebf0553)]: + - @wagmi/core@0.3.7 + +## 0.4.7 + +### Patch Changes + +- [#526](https://github.com/tmm/wagmi/pull/526) [`e95c5f9`](https://github.com/tmm/wagmi/commit/e95c5f91859e57d079b962a72d06b93dce004d2f) Thanks [@jxom](https://github.com/jxom)! - Added `shimChainChangedDisconnect` option to `InjectedConnector`. Defaults to `true` for `MetaMaskConnector`. + +* [#526](https://github.com/tmm/wagmi/pull/526) [`e95c5f9`](https://github.com/tmm/wagmi/commit/e95c5f91859e57d079b962a72d06b93dce004d2f) Thanks [@jxom](https://github.com/jxom)! - Added `chainId` config option to `useConnect()` & `connect()`. Consumers can now pick what chain they want their user to be connected to. + + Examples: + + ```tsx + import { useConnect, chain } from "wagmi"; + import { InjectedConnector } from "wagmi/connectors/injected"; + + function App() { + const connect = useConnect({ + chainId: chain.polygon.id, + }); + } + ``` + + ```tsx + import { useConnect, chain } from "wagmi"; + import { InjectedConnector } from "wagmi/connectors/injected"; + + function App() { + const connect = useConnect(); + + return ( + + ); + } + ``` + +* Updated dependencies [[`e95c5f9`](https://github.com/tmm/wagmi/commit/e95c5f91859e57d079b962a72d06b93dce004d2f), [`e95c5f9`](https://github.com/tmm/wagmi/commit/e95c5f91859e57d079b962a72d06b93dce004d2f), [`e95c5f9`](https://github.com/tmm/wagmi/commit/e95c5f91859e57d079b962a72d06b93dce004d2f)]: + - @wagmi/core@0.3.6 + +## 0.4.6 + +### Patch Changes + +- [#543](https://github.com/tmm/wagmi/pull/543) [`4d489fd`](https://github.com/tmm/wagmi/commit/4d489fd630dd8c00440bdaf4d646de662c41ff52) Thanks [@tmm](https://github.com/tmm)! - fix fee data formatting for null values + +- Updated dependencies [[`4d489fd`](https://github.com/tmm/wagmi/commit/4d489fd630dd8c00440bdaf4d646de662c41ff52)]: + - @wagmi/core@0.3.5 + +## 0.4.5 + +### Patch Changes + +- [`01cc47b`](https://github.com/tmm/wagmi/commit/01cc47b2385c78d82bc799c2dedacb2a42457e2f) Thanks [@jxom](https://github.com/jxom)! - Update `react-query` to `4.0.0-beta.19` + +## 0.4.4 + +### Patch Changes + +- [`c4deb66`](https://github.com/tmm/wagmi/commit/c4deb6655a52e4cc4e5b3fd82202db11d6106848) Thanks [@jxom](https://github.com/jxom)! - infer `options.chainId` config from `chains` on WalletConnectConnector + +- Updated dependencies [[`c4deb66`](https://github.com/tmm/wagmi/commit/c4deb6655a52e4cc4e5b3fd82202db11d6106848)]: + - @wagmi/core@0.3.4 + +## 0.4.3 + +### Patch Changes + +- [#486](https://github.com/tmm/wagmi/pull/486) [`dbfe3dd`](https://github.com/tmm/wagmi/commit/dbfe3dd320d178d6854a8096101200c1508786bb) Thanks [@tmm](https://github.com/tmm)! - add chains entrypoint + +- Updated dependencies [[`dbfe3dd`](https://github.com/tmm/wagmi/commit/dbfe3dd320d178d6854a8096101200c1508786bb)]: + - @wagmi/core@0.3.3 + +## 0.4.2 + +### Patch Changes + +- [`b1a2e58`](https://github.com/tmm/wagmi/commit/b1a2e5830e325be448bf865aeccda60217fc8d75) Thanks [@jxom](https://github.com/jxom)! - Made the `defaultChains` type generic in `configureChains`. + +## 0.4.1 + +### Patch Changes + +- [#484](https://github.com/tmm/wagmi/pull/484) [`1b9a503`](https://github.com/tmm/wagmi/commit/1b9a5033d51c6655b4f6570c490da6e0e9a29da9) Thanks [@tmm](https://github.com/tmm)! - export React Context + +- Updated dependencies [[`1b9a503`](https://github.com/tmm/wagmi/commit/1b9a5033d51c6655b4f6570c490da6e0e9a29da9)]: + - @wagmi/core@0.3.1 + +## 0.4.0 + +### Minor Changes + +- [#468](https://github.com/tmm/wagmi/pull/468) [`44a884b`](https://github.com/tmm/wagmi/commit/44a884b84171c418f57701e80ef8de972948ef0b) Thanks [@tmm](https://github.com/tmm)! - **Breaking:** Duplicate exports with different names and the same functionality were removed to simplify the public API. In addition, confusing exports were renamed to be more descriptive. + + - `createWagmiClient` alias was removed. Use `createClient` instead. + - `useWagmiClient` alias was removed. Use `useClient` instead. + - `WagmiClient` alias was removed. Use `Client` instead. + - `createWagmiStorage` alias was removed. Use `createStorage` instead. + - `Provider` was renamed and `WagmiProvider` alias was removed. Use `WagmiConfig` instead. + +* [#408](https://github.com/tmm/wagmi/pull/408) [`bfcc3a5`](https://github.com/tmm/wagmi/commit/bfcc3a51bbb1551753e3ccde6af134e9fd4fec9a) Thanks [@jxom](https://github.com/jxom)! - Add `configureChains` API. + + The `configureChains` function allows you to configure your chains with a selected provider (Alchemy, Infura, JSON RPC, Public RPC URLs). This means you don't have to worry about deriving your own RPC URLs for each chain, or instantiating a Ethereum Provider. + + `configureChains` accepts 3 parameters: an array of chains, and an array of providers, and a config object. + + [Learn more about configuring chains & providers.](https://wagmi.sh/docs/providers/configuring-chains) + + ### Before + + ```tsx + import { providers } from "ethers"; + import { chain, createClient, defaultChains } from "wagmi"; + import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet"; + import { InjectedConnector } from "wagmi/connectors/injected"; + import { MetaMaskConnector } from "wagmi/connectors/metaMask"; + import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; + + const alchemyId = process.env.ALCHEMY_ID; + + const chains = defaultChains; + const defaultChain = chain.mainnet; + + const client = createClient({ + autoConnect: true, + connectors({ chainId }) { + const chain = chains.find((x) => x.id === chainId) ?? defaultChain; + const rpcUrl = chain.rpcUrls.alchemy + ? `${chain.rpcUrls.alchemy}/${alchemyId}` + : chain.rpcUrls.default; + return [ + new MetaMaskConnector({ chains }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: "wagmi", + chainId: chain.id, + jsonRpcUrl: rpcUrl, + }, + }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + rpc: { [chain.id]: rpcUrl }, + }, + }), + new InjectedConnector({ + chains, + options: { + name: "Injected", + shimDisconnect: true, + }, + }), + ]; + }, + provider: ({ chainId }) => + new providers.AlchemyProvider(chainId, alchemyId), + }); + ``` + + ### After + + ```tsx + import { chain, createClient, defaultChains } from "wagmi"; + + import { alchemyProvider } from "wagmi/providers/alchemy"; + import { publicProvider } from "wagmi/providers/public"; + + import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet"; + import { InjectedConnector } from "wagmi/connectors/injected"; + import { MetaMaskConnector } from "wagmi/connectors/metaMask"; + import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; + + const alchemyId = process.env.ALCHEMY_ID; + + const { chains, provider, webSocketProvider } = configureChains( + defaultChains, + [alchemyProvider({ alchemyId }), publicProvider()], + ); + + const client = createClient({ + autoConnect: true, + connectors: [ + new MetaMaskConnector({ chains }), + new CoinbaseWalletConnector({ + chains, + options: { + appName: "wagmi", + }, + }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + }, + }), + new InjectedConnector({ + chains, + options: { + name: "Injected", + shimDisconnect: true, + }, + }), + ], + provider, + webSocketProvider, + }); + ``` + +### Patch Changes + +- [#404](https://github.com/tmm/wagmi/pull/404) [`f81c156`](https://github.com/tmm/wagmi/commit/f81c15665e2e71534f84ada3fa705f2d78627472) Thanks [@holic](https://github.com/holic)! - Add `ProviderRpcError` and `RpcError` error classes. + + Certain wagmi-standardized errors may wrap `ProviderRpcError` or `RpcError`. For these cases, you can access the original provider rpc or rpc error value using the `internal` property. + +* [#459](https://github.com/tmm/wagmi/pull/459) [`72dcf7c`](https://github.com/tmm/wagmi/commit/72dcf7c09e814261b2e43a8fa364c57675c472de) Thanks [@tmm](https://github.com/tmm)! - update dependencies + +- [#447](https://github.com/tmm/wagmi/pull/447) [`b9ebf78`](https://github.com/tmm/wagmi/commit/b9ebf782e0900725bcb76483686b30f09d357ebd) Thanks [@tmm](https://github.com/tmm)! - Fix case where connector disconnected while app was closed and stale data was returned when autoconnecting. For example, [MetaMask was locked](https://github.com/tmm/wagmi/issues/444) when page was closed. + +- Updated dependencies [[`f81c156`](https://github.com/tmm/wagmi/commit/f81c15665e2e71534f84ada3fa705f2d78627472), [`bfcc3a5`](https://github.com/tmm/wagmi/commit/bfcc3a51bbb1551753e3ccde6af134e9fd4fec9a), [`44a884b`](https://github.com/tmm/wagmi/commit/44a884b84171c418f57701e80ef8de972948ef0b), [`72dcf7c`](https://github.com/tmm/wagmi/commit/72dcf7c09e814261b2e43a8fa364c57675c472de), [`a54f3e2`](https://github.com/tmm/wagmi/commit/a54f3e23ea385ed8aa4ad188128d7089ba20f83e), [`b9ebf78`](https://github.com/tmm/wagmi/commit/b9ebf782e0900725bcb76483686b30f09d357ebd), [`bfcc3a5`](https://github.com/tmm/wagmi/commit/bfcc3a51bbb1551753e3ccde6af134e9fd4fec9a)]: + - @wagmi/core@0.3.0 + +## 0.3.5 + +### Patch Changes + +- [`4e03666`](https://github.com/tmm/wagmi/commit/4e03666428d42fc9186c617001b5eb356229677e) Thanks [@tmm](https://github.com/tmm)! - bump dependencies #429 + add imToken support for WC switch chains #432 + fix MetaMask and Brave Wallet collision #436 +- Updated dependencies [[`4e03666`](https://github.com/tmm/wagmi/commit/4e03666428d42fc9186c617001b5eb356229677e)]: + - @wagmi/core@0.2.5 + +## 0.3.4 + +### Patch Changes + +- [#421](https://github.com/tmm/wagmi/pull/421) [`a232b3f`](https://github.com/tmm/wagmi/commit/a232b3ff5cc41e882c4d2a34c599a8cb670edd2b) Thanks [@tmm](https://github.com/tmm)! - fix erc721 abi + +- Updated dependencies [[`a232b3f`](https://github.com/tmm/wagmi/commit/a232b3ff5cc41e882c4d2a34c599a8cb670edd2b)]: + - @wagmi/core@0.2.4 + +## 0.3.3 + +### Patch Changes + +- [#412](https://github.com/tmm/wagmi/pull/412) [`80bef4f`](https://github.com/tmm/wagmi/commit/80bef4ff3f714b0b8f896f1b4b658acc7266299b) Thanks [@markdalgleish](https://github.com/markdalgleish)! - Import providers from `ethers` peer dependency rather than `@ethersproject/providers` to avoid multiple conflicting versions being installed + +- Updated dependencies [[`80bef4f`](https://github.com/tmm/wagmi/commit/80bef4ff3f714b0b8f896f1b4b658acc7266299b)]: + - @wagmi/core@0.2.3 + +## 0.3.2 + +### Patch Changes + +- [`018c2a1`](https://github.com/tmm/wagmi/commit/018c2a11b22ee513571cc7f83fd63f7eb169ee70) Thanks [@tmm](https://github.com/tmm)! - - warn and fallback to default client #380 + + - remove signerOrProvider option from read contract #390 + + - MetaMaskConnector #391 + +- Updated dependencies [[`018c2a1`](https://github.com/tmm/wagmi/commit/018c2a11b22ee513571cc7f83fd63f7eb169ee70)]: + - @wagmi/core@0.2.2 + +## 0.3.1 + +### Patch Changes + +- [`afc4607`](https://github.com/tmm/wagmi/commit/afc46071e91601ab8a2b465524da796cd60b6ad4) Thanks [@tmm](https://github.com/tmm)! - - Fix time scaling e9593df + - Use fully-specified path for use-sync-external-store import 7b235c1 + - Update serialize 236fc17 +- Updated dependencies [[`afc4607`](https://github.com/tmm/wagmi/commit/afc46071e91601ab8a2b465524da796cd60b6ad4)]: + - @wagmi/core@0.2.1 + +## 0.3.0 + +### Minor Changes + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - don't persist account data when `autoConnect` is falsy + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - - fix(@wagmi/core): persist connector chains to local storage + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - - Favour `message` event over `connecting` event to conform to EIP-1193 + - Export `useWaitForTransaction` + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - force address to be required in `useAccount` + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - Initial 0.3.0 release + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - Add `cacheOnBlock` config for `useContractRead` + Update `react-query` to v4 + Fix `watchBlockNumber` listener leak + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - - fix `useContractWrite` mutation fn arguments + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - Update react-query to 4.0.0-beta.5 + +### Patch Changes + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - add chainId to actions and hooks + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - showtime + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - improve type support for ethers providers + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - update zustand + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - update babel target + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - update block explorers and rpc urls structure + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - keep previous data when watching + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - republish + +- [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - fix stale connectors when switching chains + +* [#311](https://github.com/tmm/wagmi/pull/311) [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d) Thanks [@tmm](https://github.com/tmm)! - last beta + +* Updated dependencies [[`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d), [`24ce011`](https://github.com/tmm/wagmi/commit/24ce0113022b890e9582c6cc24035926e0d2b32d)]: + - @wagmi/core@0.2.0 + +## 0.3.0-next.21 + +### Patch Changes + +- showtime + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.18 + +## 0.3.0-next.20 + +### Patch Changes + +- update block explorers and rpc urls structure + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.17 + +## 0.3.0-next.19 + +### Minor Changes + +- Update react-query to 4.0.0-beta.5 + +## 0.3.0-next.18 + +### Patch Changes + +- last beta + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.16 + +## 0.3.0-next.17 + +### Patch Changes + +- update zustand + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.15 + +## 0.3.0-next.16 + +### Minor Changes + +- Add `cacheOnBlock` config for `useContractRead` +- Update `react-query` to v4 +- Fix `watchBlockNumber` listener leak + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.14 + +## 0.3.0-next.15 + +### Patch Changes + +- keep previous data when watching + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.13 + +## 0.3.0-next.14 + +### Patch Changes + +- add chainId to actions and hooks + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.12 + +## 0.3.0-next.13 + +### Patch Changes + +- fix stale connectors when switching chains + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.11 + +## 0.3.0-next.12 + +### Patch Changes + +- republish + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.10 + +## 0.3.0-next.11 + +### Patch Changes + +- improve type support for ethers providers + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.9 + +## 0.3.0-next.10 + +### Patch Changes + +- update babel target + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.8 + +## 0.3.0-next.9 + +### Minor Changes + +- - Favour `message` event over `connecting` event to conform to EIP-1193 + - Export `useWaitForTransaction` + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.7 + +## 0.3.0-next.8 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.6 + +## 0.3.0-next.7 + +### Minor Changes + +- don't persist account data when `autoConnect` is falsy + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.5 + +## 0.3.0-next.6 + +### Minor Changes + +- fix `useContractWrite` mutation fn arguments + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.4 + +## 0.3.0-next.5 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.3 + +## 0.3.0-next.4 + +### Minor Changes + +- force address to be required in `useAccount` + +## 0.3.0-next.3 + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.2 + +## 0.3.0-next.2 + +### Minor Changes + +- Initial 0.3.0 release + +### Patch Changes + +- Updated dependencies []: + - @wagmi/core@0.2.0-next.1 + +## 0.2.28 + +### Patch Changes + +- [`747d895`](https://github.com/tmm/wagmi/commit/747d895a54b562958afde34b1d34e81ab5039e2c) Thanks [@tmm](https://github.com/tmm)! - add warning to WalletLinkConnector + +- Updated dependencies [[`747d895`](https://github.com/tmm/wagmi/commit/747d895a54b562958afde34b1d34e81ab5039e2c)]: + - wagmi-core@0.1.22 + +## 0.2.27 + +### Patch Changes + +- [`c858c51`](https://github.com/tmm/wagmi/commit/c858c51b44d9039f1d0db5bcf016639f47d1931f) Thanks [@tmm](https://github.com/tmm)! - update coinbase connector + +- Updated dependencies [[`c858c51`](https://github.com/tmm/wagmi/commit/c858c51b44d9039f1d0db5bcf016639f47d1931f)]: + - wagmi-core@0.1.21 + +## 0.2.26 + +### Patch Changes + +- Updated dependencies [[`36e6989`](https://github.com/tmm/wagmi/commit/36e69894f4c27aaad7fb6d678476c8bb870244bb)]: + - wagmi-core@0.1.20 + +## 0.2.25 + +### Patch Changes + +- [`d467df6`](https://github.com/tmm/wagmi/commit/d467df6374210dbc4b016788b4beb4fded54cb4d) Thanks [@tmm](https://github.com/tmm)! - fix global type leaking + +- Updated dependencies [[`d467df6`](https://github.com/tmm/wagmi/commit/d467df6374210dbc4b016788b4beb4fded54cb4d)]: + - wagmi-core@0.1.19 + +## 0.2.24 + +### Patch Changes + +- [#294](https://github.com/tmm/wagmi/pull/294) [`1d253f3`](https://github.com/tmm/wagmi/commit/1d253f3a59b61d24c88d25c99decd84a6c734e5d) Thanks [@tmm](https://github.com/tmm)! - change babel target + +- Updated dependencies [[`1d253f3`](https://github.com/tmm/wagmi/commit/1d253f3a59b61d24c88d25c99decd84a6c734e5d)]: + - wagmi-core@0.1.18 + +## 0.2.23 + +### Patch Changes + +- [#292](https://github.com/tmm/wagmi/pull/292) [`53c9be1`](https://github.com/tmm/wagmi/commit/53c9be17ee0c2ae6b8f34f2351b8858257b3f5f2) Thanks [@tmm](https://github.com/tmm)! - fix private fields + +- Updated dependencies [[`53c9be1`](https://github.com/tmm/wagmi/commit/53c9be17ee0c2ae6b8f34f2351b8858257b3f5f2)]: + - wagmi-core@0.1.17 + +## 0.2.22 + +### Patch Changes + +- [`79a2499`](https://github.com/tmm/wagmi/commit/79a249989029f818c32c0e84c0dd2c75e8aa990a) Thanks [@tmm](https://github.com/tmm)! - update build target to es2021 + +- Updated dependencies [[`79a2499`](https://github.com/tmm/wagmi/commit/79a249989029f818c32c0e84c0dd2c75e8aa990a)]: + - wagmi-core@0.1.16 + +## 0.2.21 + +### Patch Changes + +- [`f9790b5`](https://github.com/tmm/wagmi/commit/f9790b55600df09c77bb8ca349c5a3457df1b07c) Thanks [@tmm](https://github.com/tmm)! - fix WalletConnect issue + +- Updated dependencies [[`f9790b5`](https://github.com/tmm/wagmi/commit/f9790b55600df09c77bb8ca349c5a3457df1b07c)]: + - wagmi-core@0.1.15 + +## 0.2.20 + +### Patch Changes + +- [`fed29fb`](https://github.com/tmm/wagmi/commit/fed29fb4714abe234e2dabb63782cfc4439a10cf) Thanks [@tmm](https://github.com/tmm)! - export useSignTypedData + +## 0.2.19 + +### Patch Changes + +- [`6987278`](https://github.com/tmm/wagmi/commit/69872786e0b54b89a20945cc5471c1f4675b0a68) Thanks [@tmm](https://github.com/tmm)! - add useSignedTypeData + +## 0.2.18 + +### Patch Changes + +- [#236](https://github.com/tmm/wagmi/pull/236) [`53bad61`](https://github.com/tmm/wagmi/commit/53bad615788764e31121678083c382c1bd042fe8) Thanks [@markdalgleish](https://github.com/markdalgleish)! - Updated `@walletconnect/ethereum-provider` to [v1.7.4](https://github.com/WalletConnect/walletconnect-monorepo/releases/tag/1.7.4) + +- Updated dependencies [[`53bad61`](https://github.com/tmm/wagmi/commit/53bad615788764e31121678083c382c1bd042fe8)]: + - wagmi-core@0.1.14 + +## 0.2.17 + +### Patch Changes + +- [`8e9412a`](https://github.com/tmm/wagmi/commit/8e9412af71958301ae2f9748febb936e79900aa0) Thanks [@tmm](https://github.com/tmm)! - bump walletlink + +- Updated dependencies [[`8e9412a`](https://github.com/tmm/wagmi/commit/8e9412af71958301ae2f9748febb936e79900aa0)]: + - wagmi-core@0.1.13 + +## 0.2.16 + +### Patch Changes + +- [#210](https://github.com/tmm/wagmi/pull/210) [`684468a`](https://github.com/tmm/wagmi/commit/684468aee3e42a1ce2b4b599f3f17d1819213de8) Thanks [@tmm](https://github.com/tmm)! - update chains to match chainslist.org + +- Updated dependencies [[`684468a`](https://github.com/tmm/wagmi/commit/684468aee3e42a1ce2b4b599f3f17d1819213de8)]: + - wagmi-core@0.1.12 + +## 0.2.15 + +### Patch Changes + +- [#195](https://github.com/tmm/wagmi/pull/195) [`25b6083`](https://github.com/tmm/wagmi/commit/25b6083a662a0236794d1765343467691421c14b) Thanks [@tmm](https://github.com/tmm)! - rename wagmi-private to wagmi-core + +- Updated dependencies [[`25b6083`](https://github.com/tmm/wagmi/commit/25b6083a662a0236794d1765343467691421c14b)]: + - wagmi-core@0.1.11 + +## 0.2.14 + +### Patch Changes + +- [#192](https://github.com/tmm/wagmi/pull/192) [`428cedb`](https://github.com/tmm/wagmi/commit/428cedb3dec4e3e4b9f4559c8e65932e05f94e05) Thanks [@tmm](https://github.com/tmm)! - rename core and testing packages + +- Updated dependencies [[`428cedb`](https://github.com/tmm/wagmi/commit/428cedb3dec4e3e4b9f4559c8e65932e05f94e05)]: + - wagmi-core@0.1.10 + +## 0.2.13 + +### Patch Changes + +- [#190](https://github.com/tmm/wagmi/pull/190) [`7034bb8`](https://github.com/tmm/wagmi/commit/7034bb868412b9f481b206371280e84c2d52706d) Thanks [@tmm](https://github.com/tmm)! - add shim for metamask chain changed to prevent disconnect + +- Updated dependencies [[`7034bb8`](https://github.com/tmm/wagmi/commit/7034bb868412b9f481b206371280e84c2d52706d)]: + - wagmi-private@0.1.9 + +## 0.2.12 + +### Patch Changes + +- [`566b47f`](https://github.com/tmm/wagmi/commit/566b47f53c80e1cdcc368d43c53b1772eeb5be20) Thanks [@tmm](https://github.com/tmm)! - fix contract read skip option + +## 0.2.11 + +### Patch Changes + +- [`09f0719`](https://github.com/tmm/wagmi/commit/09f071947012e3133362a7eb80c0f39356899190) Thanks [@tmm](https://github.com/tmm)! - - safe state updates h/t @bpierre + - add useEnsResolveName hook h/t @shunkakinoki + +## 0.2.10 + +### Patch Changes + +- [#159](https://github.com/tmm/wagmi/pull/159) [`981438d`](https://github.com/tmm/wagmi/commit/981438d527fb6b5f025dd9bb405fa9e7a2751597) Thanks [@markdalgleish](https://github.com/markdalgleish)! - add `WagmiProvider` alias for `Provider` + +## 0.2.9 + +### Patch Changes + +- [#137](https://github.com/tmm/wagmi/pull/137) [`dceeb43`](https://github.com/tmm/wagmi/commit/dceeb430d9021fbf98366859cb1cd0149e80c55c) Thanks [@tmm](https://github.com/tmm)! - add siwe guide + +- Updated dependencies [[`dceeb43`](https://github.com/tmm/wagmi/commit/dceeb430d9021fbf98366859cb1cd0149e80c55c)]: + - wagmi-private@0.1.8 + +## 0.2.8 + +### Patch Changes + +- [`b49cb89`](https://github.com/tmm/wagmi/commit/b49cb89ef59289ee1185eafab427d3ab55c17c25) Thanks [@tmm](https://github.com/tmm)! - refactor contract read/write hook state + +## 0.2.7 + +### Patch Changes + +- [`7132631`](https://github.com/tmm/wagmi/commit/713263159899feb257c11614716f0af4f6b06a14) Thanks [@tmm](https://github.com/tmm)! - update contract read and write state + +## 0.2.6 + +### Patch Changes + +- [#127](https://github.com/tmm/wagmi/pull/127) [`f05b031`](https://github.com/tmm/wagmi/commit/f05b0310f7f7e6447e9b6c81cedbb27dcf2f3649) Thanks [@tmm](https://github.com/tmm)! - update switch chain return type + +- Updated dependencies [[`f05b031`](https://github.com/tmm/wagmi/commit/f05b0310f7f7e6447e9b6c81cedbb27dcf2f3649)]: + - wagmi-private@0.1.7 + +## 0.2.5 + +### Patch Changes + +- [`1412eed`](https://github.com/tmm/wagmi/commit/1412eed0d1494bb4f8c6845a0e890f79e4e68e03) Thanks [@tmm](https://github.com/tmm)! - add frame to injected + +- Updated dependencies [[`1412eed`](https://github.com/tmm/wagmi/commit/1412eed0d1494bb4f8c6845a0e890f79e4e68e03)]: + - wagmi-private@0.1.6 + +## 0.2.4 + +### Patch Changes + +- [#122](https://github.com/tmm/wagmi/pull/122) [`94f599c`](https://github.com/tmm/wagmi/commit/94f599cc1de74a977956d4118d85ab0d36915471) Thanks [@tmm](https://github.com/tmm)! - add decimals to useBalance + +## 0.2.3 + +### Patch Changes + +- [`e338c3b`](https://github.com/tmm/wagmi/commit/e338c3b6cc255742be6a67593aa5da6c17e90fbd) Thanks [@tmm](https://github.com/tmm)! - checksum connector address on change events + + add shim to injected connector for simulating disconnect + +- Updated dependencies [[`e338c3b`](https://github.com/tmm/wagmi/commit/e338c3b6cc255742be6a67593aa5da6c17e90fbd)]: + - wagmi-private@0.1.5 + +## 0.2.2 + +### Patch Changes + +- [`0176c4e`](https://github.com/tmm/wagmi/commit/0176c4e83fb0c5f159c3c802a1da3d6deb2184ae) Thanks [@tmm](https://github.com/tmm)! - added switchChain to WalletConnect and WalletLink connectors + +- Updated dependencies [[`0176c4e`](https://github.com/tmm/wagmi/commit/0176c4e83fb0c5f159c3c802a1da3d6deb2184ae)]: + - wagmi-private@0.1.4 + +## 0.2.1 + +### Patch Changes + +- [`f12d9cc`](https://github.com/tmm/wagmi/commit/f12d9ccfdf87a2f75299b53a7dd6b1ad046a49d8) Thanks [@tmm](https://github.com/tmm)! - fixes overrides type + +## 0.2.0 + +### Minor Changes + +- [#98](https://github.com/tmm/wagmi/pull/98) [`b2ec758`](https://github.com/tmm/wagmi/commit/b2ec7580436f52fd35005c6dd3f4472650a14d02) Thanks [@oveddan](https://github.com/oveddan)! - Exported Context + +## 0.1.7 + +### Patch Changes + +- [`d965757`](https://github.com/tmm/wagmi/commit/d9657578bc17648716c4671b8cc35ad295bc71d2) Thanks [@tmm](https://github.com/tmm)! - use balance eth symbol + +## 0.1.6 + +### Patch Changes + +- [`071d7fb`](https://github.com/tmm/wagmi/commit/071d7fbca35ec4832700b5343661ceb2dae20598) Thanks [@tmm](https://github.com/tmm)! - add hardhat chain + +- Updated dependencies [[`071d7fb`](https://github.com/tmm/wagmi/commit/071d7fbca35ec4832700b5343661ceb2dae20598)]: + - wagmi-private@0.1.3 + +## 0.1.5 + +### Patch Changes + +- [`db4d869`](https://github.com/tmm/wagmi/commit/db4d869fd9380b26a1f3f96ab34abd14ca73d068) Thanks [@tmm](https://github.com/tmm)! - - add global connecting property for `Provider` `autoConnect` (h/t @sammdec) + - fix `useContractEvent` error (h/t @math-marcellino) + +## 0.1.4 + +### Patch Changes + +- [#73](https://github.com/tmm/wagmi/pull/73) [`0c78ccc`](https://github.com/tmm/wagmi/commit/0c78ccc4e7f311525d4ea712b79cf532899e2006) Thanks [@tmm](https://github.com/tmm)! - fix module exports + +## 0.1.3 + +### Patch Changes + +- [`78bade9`](https://github.com/tmm/wagmi/commit/78bade9d0da97ab38a7e6594c34e3841ec1c8fe6) Thanks [@tmm](https://github.com/tmm)! - add type definitions + +- Updated dependencies [[`78bade9`](https://github.com/tmm/wagmi/commit/78bade9d0da97ab38a7e6594c34e3841ec1c8fe6)]: + - wagmi-private@0.1.2 + +## 0.1.2 + +### Patch Changes + +- [#56](https://github.com/tmm/wagmi/pull/56) [`2ebfd8e`](https://github.com/tmm/wagmi/commit/2ebfd8e85b560f25cd46cff04619c84643cab297) Thanks [@tmm](https://github.com/tmm)! - add chain support status + +- Updated dependencies [[`2ebfd8e`](https://github.com/tmm/wagmi/commit/2ebfd8e85b560f25cd46cff04619c84643cab297)]: + - wagmi-private@0.1.1 + +## 0.1.1 + +### Patch Changes + +- [#54](https://github.com/tmm/wagmi/pull/54) [`25acd3d`](https://github.com/tmm/wagmi/commit/25acd3dfbb4498af5e1139ae9c892f5013404cbc) Thanks [@tmm](https://github.com/tmm)! - Stale contract object when useContract hook arguments change @zakangelle + +## 0.1.0 + +### Minor Changes + +- [#52](https://github.com/tmm/wagmi/pull/52) [`da7a3a6`](https://github.com/tmm/wagmi/commit/da7a3a615def2443f65c041999100ce35e9774cc) Thanks [@tmm](https://github.com/tmm)! - Moves connectors to their own entrypoints to reduce bundle size. + + ```ts + // old - WalletLinkConnector unused, but still in final bundle + import { InjectedConnector, WalletConnectConnector } from "wagmi"; + + // new - WalletLinkConnector not in final bundle + import { InjectedConnector } from "wagmi/connectors/injected"; + import { WalletConnectConnector } from "wagmi/connectors/walletConnect"; + ``` + +### Patch Changes + +- Updated dependencies [[`da7a3a6`](https://github.com/tmm/wagmi/commit/da7a3a615def2443f65c041999100ce35e9774cc)]: + - wagmi-private@0.1.0 + +## 0.0.17 + +### Patch Changes + +- [#25](https://github.com/tmm/wagmi/pull/25) [`9a7dab7`](https://github.com/tmm/wagmi/commit/9a7dab78b3518658bc7d85dc397990f0d28da175) Thanks [@tmm](https://github.com/tmm)! - update response types + +- Updated dependencies [[`9a7dab7`](https://github.com/tmm/wagmi/commit/9a7dab78b3518658bc7d85dc397990f0d28da175)]: + - wagmi-private@0.0.17 + +## 0.0.16 + +### Patch Changes + +- [`d1574cf`](https://github.com/tmm/wagmi/commit/d1574cf5f7a578ccd480889c2e375134145a4aba) Thanks [@tmm](https://github.com/tmm)! - add better type information for contract results + +- Updated dependencies [[`d1574cf`](https://github.com/tmm/wagmi/commit/d1574cf5f7a578ccd480889c2e375134145a4aba)]: + - wagmi-private@0.0.16 + +## 0.0.15 + +### Patch Changes + +- [`3909624`](https://github.com/tmm/wagmi/commit/39096249c1fa9516beabb11735beb67c94032879) Thanks [@tmm](https://github.com/tmm)! - make contract read and write execute overrides param optional + +- Updated dependencies [[`3909624`](https://github.com/tmm/wagmi/commit/39096249c1fa9516beabb11735beb67c94032879)]: + - wagmi-private@0.0.15 + +## 0.0.14 + +### Patch Changes + +- [`63312e2`](https://github.com/tmm/wagmi/commit/63312e2b06b8d835abc2908cba399d941ca79408) Thanks [@tmm](https://github.com/tmm)! - add once to contract event + +- Updated dependencies [[`63312e2`](https://github.com/tmm/wagmi/commit/63312e2b06b8d835abc2908cba399d941ca79408)]: + - wagmi-private@0.0.14 + +## 0.0.13 + +### Patch Changes + +- [`6f890b0`](https://github.com/tmm/wagmi/commit/6f890b0dabbdbea913ec91cb8bfc970c05ed0a93) Thanks [@tmm](https://github.com/tmm)! - update readme + +- Updated dependencies [[`6f890b0`](https://github.com/tmm/wagmi/commit/6f890b0dabbdbea913ec91cb8bfc970c05ed0a93)]: + - wagmi-private@0.0.13 + +## 0.0.12 + +### Patch Changes + +- [#19](https://github.com/tmm/wagmi/pull/19) [`7bc1c47`](https://github.com/tmm/wagmi/commit/7bc1c47875e9ef24e9c79cfafc6b23e7a838b5bc) Thanks [@tmm](https://github.com/tmm)! - remove console log from walletlink connector + +- Updated dependencies [[`7bc1c47`](https://github.com/tmm/wagmi/commit/7bc1c47875e9ef24e9c79cfafc6b23e7a838b5bc)]: + - wagmi-private@0.0.12 + +## 0.0.11 + +### Patch Changes + +- [#17](https://github.com/tmm/wagmi/pull/17) [`571648b`](https://github.com/tmm/wagmi/commit/571648b754f7f538536bafc9387bd3104657ea49) Thanks [@tmm](https://github.com/tmm)! - standardize connector provider + +- Updated dependencies [[`571648b`](https://github.com/tmm/wagmi/commit/571648b754f7f538536bafc9387bd3104657ea49)]: + - wagmi-private@0.0.11 + +## 0.0.10 + +### Patch Changes + +- [#15](https://github.com/tmm/wagmi/pull/15) [`5f7675c`](https://github.com/tmm/wagmi/commit/5f7675c3ffd848522d4117c07c1f62b17dfc6616) Thanks [@tmm](https://github.com/tmm)! - read and write contract functions + +- Updated dependencies [[`5f7675c`](https://github.com/tmm/wagmi/commit/5f7675c3ffd848522d4117c07c1f62b17dfc6616)]: + - wagmi-private@0.0.10 + +## 0.0.9 + +### Patch Changes + +- [#13](https://github.com/tmm/wagmi/pull/13) [`e5545f5`](https://github.com/tmm/wagmi/commit/e5545f5565cf0bbf5e62ec7ccab3051705b1d313) Thanks [@tmm](https://github.com/tmm)! - add testing package + +- Updated dependencies [[`e5545f5`](https://github.com/tmm/wagmi/commit/e5545f5565cf0bbf5e62ec7ccab3051705b1d313)]: + - wagmi-private@0.0.9 + +## 0.0.8 + +### Patch Changes + +- [`5332500`](https://github.com/tmm/wagmi/commit/5332500918ac240d29ffe4d2aed8566a8ac001e4) Thanks [@tmm](https://github.com/tmm)! - update signing + +- Updated dependencies [[`5332500`](https://github.com/tmm/wagmi/commit/5332500918ac240d29ffe4d2aed8566a8ac001e4)]: + - wagmi-private@0.0.8 + +## 0.0.7 + +### Patch Changes + +- [`0bff89a`](https://github.com/tmm/wagmi/commit/0bff89ab2ad28b2cb9b346d1ac870e859d9278bc) Thanks [@tmm](https://github.com/tmm)! - update injected connector + +- Updated dependencies [[`0bff89a`](https://github.com/tmm/wagmi/commit/0bff89ab2ad28b2cb9b346d1ac870e859d9278bc)]: + - wagmi-private@0.0.7 + +## 0.0.6 + +### Patch Changes + +- [`37d39d1`](https://github.com/tmm/wagmi/commit/37d39d174ddfa122462bbe2d02141cd61eb9db4a) Thanks [@tmm](https://github.com/tmm)! - add message signing + +- Updated dependencies [[`37d39d1`](https://github.com/tmm/wagmi/commit/37d39d174ddfa122462bbe2d02141cd61eb9db4a)]: + - wagmi-private@0.0.6 + +## 0.0.5 + +### Patch Changes + +- [`d7d94f0`](https://github.com/tmm/wagmi/commit/d7d94f06f7d30468e5e39d64db63124c6315cf82) Thanks [@tmm](https://github.com/tmm)! - fix injected connector name + +- Updated dependencies [[`d7d94f0`](https://github.com/tmm/wagmi/commit/d7d94f06f7d30468e5e39d64db63124c6315cf82)]: + - wagmi-private@0.0.5 + +## 0.0.4 + +### Patch Changes + +- [`29fbe29`](https://github.com/tmm/wagmi/commit/29fbe2920046b9e87a34faa04500ccf3c4f83748) Thanks [@tmm](https://github.com/tmm)! - fix external deps + +- Updated dependencies [[`29fbe29`](https://github.com/tmm/wagmi/commit/29fbe2920046b9e87a34faa04500ccf3c4f83748)]: + - wagmi-private@0.0.4 + +## 0.0.3 + +### Patch Changes + +- [#6](https://github.com/tmm/wagmi/pull/6) [`8dc3a5d`](https://github.com/tmm/wagmi/commit/8dc3a5d5f418813b09663534fe585d9bcf94dbeb) Thanks [@tmm](https://github.com/tmm)! - clean up deps + +- Updated dependencies [[`8dc3a5d`](https://github.com/tmm/wagmi/commit/8dc3a5d5f418813b09663534fe585d9bcf94dbeb)]: + - wagmi-private@0.0.3 + +## 0.0.2 + +### Patch Changes + +- [#4](https://github.com/tmm/wagmi/pull/4) [`2fbd821`](https://github.com/tmm/wagmi/commit/2fbd8216379bd03c9cc5c06b10b75637e75cb7d8) Thanks [@tmm](https://github.com/tmm)! - init changesets + +- Updated dependencies [[`2fbd821`](https://github.com/tmm/wagmi/commit/2fbd8216379bd03c9cc5c06b10b75637e75cb7d8)]: + - wagmi-private@0.0.2 diff --git a/packages/react/README.md b/packages/react/README.md new file mode 100644 index 0000000000..4c87fd5e67 --- /dev/null +++ b/packages/react/README.md @@ -0,0 +1,13 @@ +# wagmi + +React Hooks for Ethereum + +## Installation + +```bash +pnpm add wagmi viem @tanstack/react-query +``` + +## Documentation + +For documentation and guides, visit [wagmi.sh](https://wagmi.sh). diff --git a/packages/react/package.json b/packages/react/package.json new file mode 100644 index 0000000000..79311aa5ce --- /dev/null +++ b/packages/react/package.json @@ -0,0 +1,119 @@ +{ + "name": "wagmi", + "description": "React Hooks for Ethereum", + "version": "2.15.4", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/wevm/wagmi.git", + "directory": "packages/react" + }, + "scripts": { + "build": "pnpm run clean && pnpm run build:esm+types", + "build:esm+types": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationMap --declarationDir ./dist/types", + "check:types": "tsc --noEmit", + "clean": "rm -rf dist tsconfig.tsbuildinfo actions chains codegen connectors experimental query", + "test:build": "publint --strict && attw --pack --ignore-rules cjs-resolves-to-esm" + }, + "files": [ + "dist/**", + "!dist/**/*.tsbuildinfo", + "src/**/*.ts", + "!src/**/*.test.ts", + "!src/**/*.test-d.ts", + "/actions", + "/chains", + "/codegen", + "/connectors", + "/experimental", + "/query" + ], + "sideEffects": false, + "type": "module", + "main": "./dist/esm/exports/index.js", + "types": "./dist/types/exports/index.d.ts", + "typings": "./dist/types/exports/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/exports/index.d.ts", + "default": "./dist/esm/exports/index.js" + }, + "./actions": { + "types": "./dist/types/exports/actions.d.ts", + "default": "./dist/esm/exports/actions.js" + }, + "./actions/experimental": { + "types": "./dist/types/exports/actions/experimental.d.ts", + "default": "./dist/esm/exports/actions/experimental.js" + }, + "./chains": { + "types": "./dist/types/exports/chains.d.ts", + "default": "./dist/esm/exports/chains.js" + }, + "./codegen": { + "types": "./dist/types/exports/codegen.d.ts", + "default": "./dist/esm/exports/codegen.js" + }, + "./connectors": { + "types": "./dist/types/exports/connectors.d.ts", + "default": "./dist/esm/exports/connectors.js" + }, + "./experimental": { + "types": "./dist/types/exports/experimental.d.ts", + "default": "./dist/esm/exports/experimental.js" + }, + "./query": { + "types": "./dist/types/exports/query.d.ts", + "default": "./dist/esm/exports/query.js" + }, + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "actions": ["./dist/types/exports/actions.d.ts"], + "chains": ["./dist/types/exports/chains.d.ts"], + "codegen": ["./dist/types/exports/codegen.d.ts"], + "connectors": ["./dist/types/exports/connectors.d.ts"], + "experimental": ["./dist/types/exports/experimental.d.ts"], + "query": ["./dist/types/exports/query.d.ts"] + } + }, + "peerDependencies": { + "@tanstack/react-query": ">=5.0.0", + "react": ">=18", + "typescript": ">=5.0.4", + "viem": "2.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + }, + "dependencies": { + "@wagmi/connectors": "workspace:*", + "@wagmi/core": "workspace:*", + "use-sync-external-store": "1.4.0" + }, + "devDependencies": { + "@tanstack/react-query": "catalog:", + "@testing-library/dom": "catalog:", + "@testing-library/react": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "@types/use-sync-external-store": "^0.0.6", + "react": "catalog:", + "react-dom": "catalog:" + }, + "contributors": ["awkweb.eth ", "jxom.eth "], + "funding": "https://github.com/sponsors/wevm", + "keywords": [ + "wagmi", + "react", + "hooks", + "eth", + "ethereum", + "dapps", + "wallet", + "web3" + ] +} diff --git a/packages/react/src/context.test.tsx b/packages/react/src/context.test.tsx new file mode 100644 index 0000000000..7a716ac685 --- /dev/null +++ b/packages/react/src/context.test.tsx @@ -0,0 +1,101 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { render, waitFor } from '@testing-library/react' +import { http, connect, createConfig, mock } from '@wagmi/core' +import { accounts, addressRegex, config, mainnet } from '@wagmi/test' +import React from 'react' +import { expect, test } from 'vitest' + +import { WagmiProvider } from './context.js' +import { useAccount } from './hooks/useAccount.js' +import { useConnectorClient } from './hooks/useConnectorClient.js' + +test('default', () => { + function Component() { + const { address } = useAccount() + const { data } = useConnectorClient() + return ( +
+

wevm

+
useAccount: {address}
+
useConnectorClient: {data?.account?.address}
+
+ ) + } + + const queryClient = new QueryClient() + const result = render( + + + + + , + ) + expect(result.getByRole('heading').innerText).toMatchInlineSnapshot(`"wevm"`) + result.unmount() +}) + +test('fake ssr config', () => { + const config = createConfig({ + chains: [mainnet], + pollingInterval: 100, + ssr: true, + transports: { + [mainnet.id]: http(), + }, + }) + const queryClient = new QueryClient() + + const result = render( + + +

wevm

+
+
, + ) + expect(result.getAllByRole('heading')).toMatchInlineSnapshot(` + [ +

+ wevm +

, + ] + `) + result.unmount() +}) + +test('mock reconnect', async () => { + function Component() { + const { address } = useAccount() + return ( +
+

{address}

+
+ ) + } + + const connector = mock({ + accounts, + features: { reconnect: true }, + }) + const config = createConfig({ + chains: [mainnet], + connectors: [connector], + storage: null, + transports: { + [mainnet.id]: http(), + }, + }) + await connect(config, { connector }) + + const queryClient = new QueryClient() + const result = render( + + + + + , + ) + await waitFor(() => + expect(result.getByRole('heading').innerText).toMatch(addressRegex), + ) + result.unmount() +}) diff --git a/packages/react/src/context.ts b/packages/react/src/context.ts new file mode 100644 index 0000000000..ec484ccd74 --- /dev/null +++ b/packages/react/src/context.ts @@ -0,0 +1,28 @@ +'use client' + +import type { ResolvedRegister, State } from '@wagmi/core' +import { createContext, createElement } from 'react' +import { Hydrate } from './hydrate.js' + +export const WagmiContext = createContext< + ResolvedRegister['config'] | undefined +>(undefined) + +export type WagmiProviderProps = { + config: ResolvedRegister['config'] + initialState?: State | undefined + reconnectOnMount?: boolean | undefined +} + +export function WagmiProvider( + parameters: React.PropsWithChildren, +) { + const { children, config } = parameters + + const props = { value: config } + return createElement( + Hydrate, + parameters, + createElement(WagmiContext.Provider, props, children), + ) +} diff --git a/packages/react/src/errors/base.test.ts b/packages/react/src/errors/base.test.ts new file mode 100644 index 0000000000..2980541ed8 --- /dev/null +++ b/packages/react/src/errors/base.test.ts @@ -0,0 +1,155 @@ +import { expect, test } from 'vitest' + +import { BaseError } from './base.js' + +test('BaseError', () => { + expect(new BaseError('An error occurred.')).toMatchInlineSnapshot(` + [WagmiError: An error occurred. + + Version: wagmi@x.y.z] + `) + + expect( + new BaseError('An error occurred.', { details: 'details' }), + ).toMatchInlineSnapshot(` + [WagmiError: An error occurred. + + Details: details + Version: wagmi@x.y.z] + `) + + expect(new BaseError('', { details: 'details' })).toMatchInlineSnapshot(` + [WagmiError: An error occurred. + + Details: details + Version: wagmi@x.y.z] + `) +}) + +test('BaseError (w/ docsPath)', () => { + expect( + new BaseError('An error occurred.', { + details: 'details', + docsPath: '/lol', + }), + ).toMatchInlineSnapshot(` + [WagmiError: An error occurred. + + Docs: https://wagmi.sh/react/lol.html + Details: details + Version: wagmi@x.y.z] + `) + expect( + new BaseError('An error occurred.', { + cause: new BaseError('error', { docsPath: '/docs' }), + }), + ).toMatchInlineSnapshot(` + [WagmiError: An error occurred. + + Docs: https://wagmi.sh/react/docs.html + Version: wagmi@x.y.z] + `) + expect( + new BaseError('An error occurred.', { + cause: new BaseError('error'), + docsPath: '/lol', + }), + ).toMatchInlineSnapshot(` + [WagmiError: An error occurred. + + Docs: https://wagmi.sh/react/lol.html + Version: wagmi@x.y.z] + `) + expect( + new BaseError('An error occurred.', { + details: 'details', + docsPath: '/lol', + docsSlug: 'test', + }), + ).toMatchInlineSnapshot(` + [WagmiError: An error occurred. + + Docs: https://wagmi.sh/react/lol.html#test + Details: details + Version: wagmi@x.y.z] + `) +}) + +test('BaseError (w/ metaMessages)', () => { + expect( + new BaseError('An error occurred.', { + details: 'details', + metaMessages: ['Reason: idk', 'Cause: lol'], + }), + ).toMatchInlineSnapshot(` + [WagmiError: An error occurred. + + Reason: idk + Cause: lol + + Details: details + Version: wagmi@x.y.z] + `) +}) + +test('inherited BaseError', () => { + const err = new BaseError('An error occurred.', { + details: 'details', + docsPath: '/lol', + }) + expect( + new BaseError('An internal error occurred.', { + cause: err, + }), + ).toMatchInlineSnapshot(` + [WagmiError: An internal error occurred. + + Docs: https://wagmi.sh/react/lol.html + Details: details + Version: wagmi@x.y.z] + `) +}) + +test('inherited Error', () => { + const err = new Error('details') + expect( + new BaseError('An internal error occurred.', { + cause: err, + docsPath: '/lol', + }), + ).toMatchInlineSnapshot(` + [WagmiError: An internal error occurred. + + Docs: https://wagmi.sh/react/lol.html + Details: details + Version: wagmi@x.y.z] + `) +}) + +test('walk: no predicate fn (walks to leaf)', () => { + class FooError extends BaseError {} + class BarError extends BaseError {} + + const err = new BaseError('test1', { + cause: new FooError('test2', { cause: new BarError('test3') }), + }) + expect(err.walk()).toMatchInlineSnapshot(` + [WagmiError: test3 + + Version: wagmi@x.y.z] + `) +}) + +test('walk: predicate fn', () => { + class FooError extends BaseError {} + class BarError extends BaseError {} + + const err = new BaseError('test1', { + cause: new FooError('test2', { cause: new BarError('test3') }), + }) + expect(err.walk((err) => err instanceof FooError)).toMatchInlineSnapshot(` + [WagmiError: test2 + + Version: wagmi@x.y.z] + `) +}) diff --git a/packages/react/src/errors/base.ts b/packages/react/src/errors/base.ts new file mode 100644 index 0000000000..b2ee83c86e --- /dev/null +++ b/packages/react/src/errors/base.ts @@ -0,0 +1,14 @@ +import { BaseError as CoreError } from '@wagmi/core' + +import { getVersion } from '../utils/getVersion.js' + +export type BaseErrorType = BaseError & { name: 'WagmiError' } +export class BaseError extends CoreError { + override name = 'WagmiError' + override get docsBaseUrl() { + return 'https://wagmi.sh/react' + } + override get version() { + return getVersion() + } +} diff --git a/packages/react/src/errors/context.test.ts b/packages/react/src/errors/context.test.ts new file mode 100644 index 0000000000..8dc5a16c4a --- /dev/null +++ b/packages/react/src/errors/context.test.ts @@ -0,0 +1,12 @@ +import { expect, test } from 'vitest' + +import { WagmiProviderNotFoundError } from './context.js' + +test('WagmiProviderNotFoundError', () => { + expect(new WagmiProviderNotFoundError()).toMatchInlineSnapshot(` + [WagmiProviderNotFoundError: \`useConfig\` must be used within \`WagmiProvider\`. + + Docs: https://wagmi.sh/react/api/WagmiProvider.html + Version: wagmi@x.y.z] + `) +}) diff --git a/packages/react/src/errors/context.ts b/packages/react/src/errors/context.ts new file mode 100644 index 0000000000..0ea5cadaaa --- /dev/null +++ b/packages/react/src/errors/context.ts @@ -0,0 +1,13 @@ +import { BaseError } from './base.js' + +export type WagmiProviderNotFoundErrorType = WagmiProviderNotFoundError & { + name: 'WagmiProviderNotFoundError' +} +export class WagmiProviderNotFoundError extends BaseError { + override name = 'WagmiProviderNotFoundError' + constructor() { + super('`useConfig` must be used within `WagmiProvider`.', { + docsPath: '/api/WagmiProvider', + }) + } +} diff --git a/packages/react/src/experimental/hooks/useWriteContracts.test.ts b/packages/react/src/experimental/hooks/useWriteContracts.test.ts new file mode 100644 index 0000000000..ea6d4815de --- /dev/null +++ b/packages/react/src/experimental/hooks/useWriteContracts.test.ts @@ -0,0 +1,45 @@ +import { connect, disconnect } from '@wagmi/core' +import { abi, address, config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useWriteContracts } from './useWriteContracts.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useWriteContracts()) + + result.current.writeContracts({ + contracts: [ + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + { + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }, + ], + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatchInlineSnapshot( + ` + { + "id": "0x8913636bd97cf4bcc0a6343c730905a27ead0f7480ff82190072e916439eb212", + } + `, + ) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/experimental/hooks/useWriteContracts.ts b/packages/react/src/experimental/hooks/useWriteContracts.ts new file mode 100644 index 0000000000..8d0d32aefb --- /dev/null +++ b/packages/react/src/experimental/hooks/useWriteContracts.ts @@ -0,0 +1,85 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { Config, ResolvedRegister } from '@wagmi/core' +import { + type WriteContractsData, + type WriteContractsErrorType, + type WriteContractsMutate, + type WriteContractsMutateAsync, + type WriteContractsVariables, + writeContractsMutationOptions, +} from '@wagmi/core/experimental' +import type { Compute } from '@wagmi/core/internal' +import type { ContractFunctionParameters } from 'viem' + +import { useConfig } from '../../hooks/useConfig.js' +import type { ConfigParameter } from '../../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../../utils/query.js' + +export type UseWriteContractsParameters< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + WriteContractsData, + WriteContractsErrorType, + WriteContractsVariables< + contracts, + config, + config['chains'][number]['id'] + >, + context + > + | undefined + } +> + +export type UseWriteContractsReturnType< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + config extends Config = Config, + context = unknown, +> = Compute< + UseMutationReturnType< + WriteContractsData, + WriteContractsErrorType, + WriteContractsVariables, + context + > & { + writeContracts: WriteContractsMutate + writeContractsAsync: WriteContractsMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useWriteContracts */ +export function useWriteContracts< + const contracts extends + readonly unknown[] = readonly ContractFunctionParameters[], + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseWriteContractsParameters = {}, +): UseWriteContractsReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = writeContractsMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseWriteContractsReturnType + return { + ...result, + writeContracts: mutate as Return['writeContracts'], + writeContractsAsync: mutateAsync as Return['writeContractsAsync'], + } +} diff --git a/packages/react/src/exports/actions.test.ts b/packages/react/src/exports/actions.test.ts new file mode 100644 index 0000000000..eaaedba14f --- /dev/null +++ b/packages/react/src/exports/actions.test.ts @@ -0,0 +1,86 @@ +import { expect, test } from 'vitest' + +import * as actions from './actions.js' + +test('exports', () => { + expect(Object.keys(actions)).toMatchInlineSnapshot(` + [ + "call", + "connect", + "deployContract", + "disconnect", + "estimateGas", + "estimateFeesPerGas", + "estimateMaxPriorityFeePerGas", + "getAccount", + "getBalance", + "fetchBalance", + "getBlock", + "getBlockNumber", + "fetchBlockNumber", + "getBlockTransactionCount", + "getBytecode", + "getCallsStatus", + "getCapabilities", + "getChainId", + "getChains", + "getClient", + "getConnections", + "getConnectors", + "getConnectorClient", + "getEnsAddress", + "fetchEnsAddress", + "getEnsAvatar", + "fetchEnsAvatar", + "getEnsName", + "fetchEnsName", + "getEnsResolver", + "fetchEnsResolver", + "getEnsText", + "getFeeHistory", + "getGasPrice", + "getProof", + "getPublicClient", + "getStorageAt", + "getToken", + "fetchToken", + "getTransaction", + "fetchTransaction", + "getTransactionConfirmations", + "getTransactionCount", + "getTransactionReceipt", + "getWalletClient", + "multicall", + "prepareTransactionRequest", + "readContract", + "readContracts", + "reconnect", + "sendCalls", + "sendTransaction", + "showCallsStatus", + "signMessage", + "signTypedData", + "simulateContract", + "switchAccount", + "switchChain", + "switchNetwork", + "verifyMessage", + "verifyTypedData", + "waitForCallsStatus", + "watchAccount", + "watchAsset", + "watchBlocks", + "watchBlockNumber", + "watchChainId", + "watchClient", + "watchConnections", + "watchConnectors", + "watchContractEvent", + "watchPendingTransactions", + "watchPublicClient", + "waitForTransactionReceipt", + "waitForTransaction", + "writeContract", + ] + `) +}) diff --git a/packages/react/src/exports/actions.ts b/packages/react/src/exports/actions.ts new file mode 100644 index 0000000000..3ff9c743c5 --- /dev/null +++ b/packages/react/src/exports/actions.ts @@ -0,0 +1,7 @@ +//////////////////////////////////////////////////////////////////////////////// +// @wagmi/core/actions +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +// biome-ignore lint/performance/noReExportAll: entrypoint module +export * from '@wagmi/core/actions' diff --git a/packages/react/src/exports/actions/experimental.test.ts b/packages/react/src/exports/actions/experimental.test.ts new file mode 100644 index 0000000000..7c4b92df8c --- /dev/null +++ b/packages/react/src/exports/actions/experimental.test.ts @@ -0,0 +1,25 @@ +import { expect, test } from 'vitest' + +import * as experimentalActions from './experimental.js' + +test('exports', () => { + expect(Object.keys(experimentalActions)).toMatchInlineSnapshot(` + [ + "getCallsStatus", + "getCapabilities", + "sendCalls", + "showCallsStatus", + "waitForCallsStatus", + "writeContracts", + "getCallsStatusQueryOptions", + "getCallsStatusQueryKey", + "getCapabilitiesQueryOptions", + "getCapabilitiesQueryKey", + "sendCallsMutationOptions", + "showCallsStatusMutationOptions", + "waitForCallsStatusQueryKey", + "waitForCallsStatusQueryOptions", + "writeContractsMutationOptions", + ] + `) +}) diff --git a/packages/react/src/exports/actions/experimental.ts b/packages/react/src/exports/actions/experimental.ts new file mode 100644 index 0000000000..6ee0334af5 --- /dev/null +++ b/packages/react/src/exports/actions/experimental.ts @@ -0,0 +1,7 @@ +//////////////////////////////////////////////////////////////////////////////// +// @wagmi/core/experimental +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +// biome-ignore lint/performance/noReExportAll: entrypoint module +export * from '@wagmi/core/experimental' diff --git a/packages/react/src/exports/chains.ts b/packages/react/src/exports/chains.ts new file mode 100644 index 0000000000..1fca7f537f --- /dev/null +++ b/packages/react/src/exports/chains.ts @@ -0,0 +1,7 @@ +//////////////////////////////////////////////////////////////////////////////// +// viem/chains +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +// biome-ignore lint/performance/noReExportAll: entrypoint module +export * from 'viem/chains' diff --git a/packages/react/src/exports/codegen.test.ts b/packages/react/src/exports/codegen.test.ts new file mode 100644 index 0000000000..19697cc9a9 --- /dev/null +++ b/packages/react/src/exports/codegen.test.ts @@ -0,0 +1,18 @@ +import { expect, test } from 'vitest' + +import * as codegen from './codegen.js' + +test('exports', () => { + expect(Object.keys(codegen)).toMatchInlineSnapshot(` + [ + "createSimulateContract", + "createReadContract", + "createWatchContractEvent", + "createWriteContract", + "createUseSimulateContract", + "createUseReadContract", + "createUseWatchContractEvent", + "createUseWriteContract", + ] + `) +}) diff --git a/packages/react/src/exports/codegen.ts b/packages/react/src/exports/codegen.ts new file mode 100644 index 0000000000..c642f63bcb --- /dev/null +++ b/packages/react/src/exports/codegen.ts @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////// +// @wagmi/core/codegen +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +// biome-ignore lint/performance/noReExportAll: entrypoint module +export * from '@wagmi/core/codegen' + +//////////////////////////////////////////////////////////////////////////////// +// Hooks +//////////////////////////////////////////////////////////////////////////////// + +export { + type CreateUseSimulateContractParameters, + type CreateUseSimulateContractReturnType, + createUseSimulateContract, +} from '../hooks/codegen/createUseSimulateContract.js' + +export { + type CreateUseReadContractParameters, + type CreateUseReadContractReturnType, + createUseReadContract, +} from '../hooks/codegen/createUseReadContract.js' + +export { + type CreateUseWatchContractEventParameters, + type CreateUseWatchContractEventReturnType, + createUseWatchContractEvent, +} from '../hooks/codegen/createUseWatchContractEvent.js' + +export { + type CreateUseWriteContractParameters, + type CreateUseWriteContractReturnType, + createUseWriteContract, +} from '../hooks/codegen/createUseWriteContract.js' diff --git a/packages/react/src/exports/connectors.test.ts b/packages/react/src/exports/connectors.test.ts new file mode 100644 index 0000000000..068db8227c --- /dev/null +++ b/packages/react/src/exports/connectors.test.ts @@ -0,0 +1,17 @@ +import { expect, test } from 'vitest' + +import * as connectors from './connectors.js' + +test('exports', () => { + expect(Object.keys(connectors)).toMatchInlineSnapshot(` + [ + "injected", + "mock", + "coinbaseWallet", + "metaMask", + "safe", + "walletConnect", + "version", + ] + `) +}) diff --git a/packages/react/src/exports/connectors.ts b/packages/react/src/exports/connectors.ts new file mode 100644 index 0000000000..e10367e318 --- /dev/null +++ b/packages/react/src/exports/connectors.ts @@ -0,0 +1,7 @@ +//////////////////////////////////////////////////////////////////////////////// +// @wagmi/connectors +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +// biome-ignore lint/performance/noReExportAll: entrypoint module +export * from '@wagmi/connectors' diff --git a/packages/react/src/exports/experimental.ts b/packages/react/src/exports/experimental.ts new file mode 100644 index 0000000000..996eb56a73 --- /dev/null +++ b/packages/react/src/exports/experimental.ts @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +// Hooks +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + /** @deprecated This is no longer experimental – use `import type { UseCallsStatusParameters } from 'wagmi'` instead. */ + type UseCallsStatusParameters, + /** @deprecated This is no longer experimental – use `import type { UseCallsStatusReturnType } from 'wagmi'` instead. */ + type UseCallsStatusReturnType, + /** @deprecated This is no longer experimental – use `import { useCallsStatus } from 'wagmi'` instead. */ + useCallsStatus, +} from '../hooks/useCallsStatus.js' + +export { + /** @deprecated This is no longer experimental – use `import type { UseCapabilitiesParameters } from 'wagmi'` instead. */ + type UseCapabilitiesParameters, + /** @deprecated This is no longer experimental – use `import type { UseCapabilitiesReturnType } from 'wagmi'` instead. */ + type UseCapabilitiesReturnType, + /** @deprecated This is no longer experimental – use `import { useCapabilities } from 'wagmi'` instead. */ + useCapabilities, +} from '../hooks/useCapabilities.js' + +export { + /** @deprecated This is no longer experimental – use `import type { UseSendCallsParameters } from 'wagmi'` instead. */ + type UseSendCallsParameters, + /** @deprecated This is no longer experimental – use `import type { UseSendCallsReturnType } from 'wagmi'` instead. */ + type UseSendCallsReturnType, + /** @deprecated This is no longer experimental – use `import { useSendCalls } from 'wagmi'` instead. */ + useSendCalls, +} from '../hooks/useSendCalls.js' + +export { + /** @deprecated This is no longer experimental – use `import type { UseShowCallsStatusParameters } from 'wagmi'` instead. */ + type UseShowCallsStatusParameters, + /** @deprecated This is no longer experimental – use `import type { UseShowCallsStatusReturnType } from 'wagmi'` instead. */ + type UseShowCallsStatusReturnType, + /** @deprecated This is no longer experimental – use `import { useShowCallsStatus } from 'wagmi'` instead. */ + useShowCallsStatus, +} from '../hooks/useShowCallsStatus.js' + +export { + /** @deprecated This is no longer experimental – use `import type { UseWaitForCallsStatusParameters } from 'wagmi'` instead. */ + type UseWaitForCallsStatusParameters, + /** @deprecated This is no longer experimental – use `import type { UseWaitForCallsStatusReturnType } from 'wagmi'` instead. */ + type UseWaitForCallsStatusReturnType, + /** @deprecated This is no longer experimental – use `import { useWaitForCallsStatus } from 'wagmi'` instead. */ + useWaitForCallsStatus, +} from '../hooks/useWaitForCallsStatus.js' + +export { + /** @deprecated Use `UseSendCallsParameters` instead. */ + type UseWriteContractsParameters, + /** @deprecated Use `UseSendCallsReturnType` instead. */ + type UseWriteContractsReturnType, + /** @deprecated Use `useSendCalls` instead. */ + useWriteContracts, +} from '../experimental/hooks/useWriteContracts.js' diff --git a/packages/react/src/exports/index.test.ts b/packages/react/src/exports/index.test.ts new file mode 100644 index 0000000000..d8d6f7c6d5 --- /dev/null +++ b/packages/react/src/exports/index.test.ts @@ -0,0 +1,111 @@ +import { expect, test } from 'vitest' + +import * as react from './index.js' + +test('exports', () => { + expect(Object.keys(react)).toMatchInlineSnapshot(` + [ + "WagmiContext", + "WagmiProvider", + "Context", + "WagmiConfig", + "BaseError", + "WagmiProviderNotFoundError", + "useAccount", + "useAccountEffect", + "useBalance", + "useBlock", + "useBlockNumber", + "useBlockTransactionCount", + "useBytecode", + "useCallsStatus", + "useCapabilities", + "useCall", + "useChainId", + "useChains", + "useClient", + "useConfig", + "useConnect", + "useConnections", + "useConnectors", + "useConnectorClient", + "useDeployContract", + "useDisconnect", + "useEnsAddress", + "useEnsAvatar", + "useEnsName", + "useEnsResolver", + "useEnsText", + "useEstimateFeesPerGas", + "useFeeData", + "useEstimateGas", + "useEstimateMaxPriorityFeePerGas", + "useFeeHistory", + "useGasPrice", + "useInfiniteReadContracts", + "useContractInfiniteReads", + "usePrepareTransactionRequest", + "useProof", + "usePublicClient", + "useReadContract", + "useContractRead", + "useReadContracts", + "useContractReads", + "useReconnect", + "useSendCalls", + "useSendTransaction", + "useShowCallsStatus", + "useSignMessage", + "useSignTypedData", + "useSimulateContract", + "useStorageAt", + "useSwitchAccount", + "useSwitchChain", + "useToken", + "useTransaction", + "useTransactionConfirmations", + "useTransactionCount", + "useTransactionReceipt", + "useVerifyMessage", + "useVerifyTypedData", + "useWalletClient", + "useWaitForCallsStatus", + "useWaitForTransactionReceipt", + "useWatchAsset", + "useWatchBlocks", + "useWatchBlockNumber", + "useWatchContractEvent", + "useWatchPendingTransactions", + "useWriteContract", + "useContractWrite", + "Hydrate", + "createConfig", + "createConnector", + "injected", + "mock", + "ChainNotConfiguredError", + "ConnectorAlreadyConnectedError", + "ConnectorNotFoundError", + "ConnectorAccountNotFoundError", + "ConnectorChainMismatchError", + "ConnectorUnavailableReconnectingError", + "ProviderNotFoundError", + "SwitchChainNotSupportedError", + "createStorage", + "noopStorage", + "custom", + "fallback", + "http", + "webSocket", + "unstable_connector", + "cookieStorage", + "cookieToInitialState", + "deepEqual", + "deserialize", + "normalizeChainId", + "parseCookie", + "serialize", + "version", + ] + `) +}) diff --git a/packages/react/src/exports/index.ts b/packages/react/src/exports/index.ts new file mode 100644 index 0000000000..a4b8502982 --- /dev/null +++ b/packages/react/src/exports/index.ts @@ -0,0 +1,487 @@ +//////////////////////////////////////////////////////////////////////////////// +// Context +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type WagmiProviderProps, + WagmiContext, + WagmiProvider, + /** @deprecated Use `WagmiContext` instead */ + WagmiContext as Context, + /** @deprecated Use `WagmiProvider` instead */ + WagmiProvider as WagmiConfig, +} from '../context.js' + +//////////////////////////////////////////////////////////////////////////////// +// Errors +//////////////////////////////////////////////////////////////////////////////// + +export { type BaseErrorType, BaseError } from '../errors/base.js' + +export { + type WagmiProviderNotFoundErrorType, + WagmiProviderNotFoundError, +} from '../errors/context.js' + +//////////////////////////////////////////////////////////////////////////////// +// Hooks +//////////////////////////////////////////////////////////////////////////////// + +export { + type UseAccountParameters, + type UseAccountReturnType, + useAccount, +} from '../hooks/useAccount.js' + +export { + type UseAccountEffectParameters, + useAccountEffect, +} from '../hooks/useAccountEffect.js' + +export { + type UseBalanceParameters, + type UseBalanceReturnType, + useBalance, +} from '../hooks/useBalance.js' + +export { + type UseBlockParameters, + type UseBlockReturnType, + useBlock, +} from '../hooks/useBlock.js' + +export { + type UseBlockNumberParameters, + type UseBlockNumberReturnType, + useBlockNumber, +} from '../hooks/useBlockNumber.js' + +export { + type UseBlockTransactionCountParameters, + type UseBlockTransactionCountReturnType, + useBlockTransactionCount, +} from '../hooks/useBlockTransactionCount.js' + +export { + type UseBytecodeParameters, + type UseBytecodeReturnType, + useBytecode, +} from '../hooks/useBytecode.js' + +export { + type UseCallsStatusParameters, + type UseCallsStatusReturnType, + useCallsStatus, +} from '../hooks/useCallsStatus.js' + +export { + type UseCapabilitiesParameters, + type UseCapabilitiesReturnType, + useCapabilities, +} from '../hooks/useCapabilities.js' + +export { + type UseCallParameters, + type UseCallReturnType, + useCall, +} from '../hooks/useCall.js' + +export { + type UseChainIdParameters, + type UseChainIdReturnType, + useChainId, +} from '../hooks/useChainId.js' + +export { + type UseChainsParameters, + type UseChainsReturnType, + useChains, +} from '../hooks/useChains.js' + +export { + type UseClientParameters, + type UseClientReturnType, + useClient, +} from '../hooks/useClient.js' + +export { + type UseConfigParameters, + type UseConfigReturnType, + useConfig, +} from '../hooks/useConfig.js' + +export { + type UseConnectParameters, + type UseConnectReturnType, + useConnect, +} from '../hooks/useConnect.js' + +export { + type UseConnectionsParameters, + type UseConnectionsReturnType, + useConnections, +} from '../hooks/useConnections.js' + +export { + type UseConnectorsParameters, + type UseConnectorsReturnType, + useConnectors, +} from '../hooks/useConnectors.js' + +export { + type UseConnectorClientParameters, + type UseConnectorClientReturnType, + useConnectorClient, +} from '../hooks/useConnectorClient.js' + +export { + type UseDeployContractParameters, + type UseDeployContractReturnType, + useDeployContract, +} from '../hooks/useDeployContract.js' + +export { + type UseDisconnectParameters, + type UseDisconnectReturnType, + useDisconnect, +} from '../hooks/useDisconnect.js' + +export { + type UseEnsAddressParameters, + type UseEnsAddressReturnType, + useEnsAddress, +} from '../hooks/useEnsAddress.js' + +export { + type UseEnsAvatarParameters, + type UseEnsAvatarReturnType, + useEnsAvatar, +} from '../hooks/useEnsAvatar.js' + +export { + type UseEnsNameParameters, + type UseEnsNameReturnType, + useEnsName, +} from '../hooks/useEnsName.js' + +export { + type UseEnsResolverParameters, + type UseEnsResolverReturnType, + useEnsResolver, +} from '../hooks/useEnsResolver.js' + +export { + type UseEnsTextParameters, + type UseEnsTextReturnType, + useEnsText, +} from '../hooks/useEnsText.js' + +export { + type UseEstimateFeesPerGasParameters, + type UseEstimateFeesPerGasReturnType, + useEstimateFeesPerGas, + /** @deprecated Use `useEstimateFeesPerGas` instead */ + useEstimateFeesPerGas as useFeeData, +} from '../hooks/useEstimateFeesPerGas.js' + +export { + type UseEstimateGasParameters, + type UseEstimateGasReturnType, + useEstimateGas, +} from '../hooks/useEstimateGas.js' + +export { + type UseEstimateMaxPriorityFeePerGasParameters, + type UseEstimateMaxPriorityFeePerGasReturnType, + useEstimateMaxPriorityFeePerGas, +} from '../hooks/useEstimateMaxPriorityFeePerGas.js' + +export { + type UseFeeHistoryParameters, + type UseFeeHistoryReturnType, + useFeeHistory, +} from '../hooks/useFeeHistory.js' + +export { + type UseGasPriceParameters, + type UseGasPriceReturnType, + useGasPrice, +} from '../hooks/useGasPrice.js' + +export { + type UseInfiniteContractReadsParameters, + type UseInfiniteContractReadsReturnType, + useInfiniteReadContracts, + /** @deprecated Use `useInfiniteReadContracts` instead */ + useInfiniteReadContracts as useContractInfiniteReads, +} from '../hooks/useInfiniteReadContracts.js' + +export { + type UsePrepareTransactionRequestParameters, + type UsePrepareTransactionRequestReturnType, + usePrepareTransactionRequest, +} from '../hooks/usePrepareTransactionRequest.js' + +export { + type UseProofParameters, + type UseProofReturnType, + useProof, +} from '../hooks/useProof.js' + +export { + type UsePublicClientParameters, + type UsePublicClientReturnType, + usePublicClient, +} from '../hooks/usePublicClient.js' + +export { + type UseReadContractParameters, + type UseReadContractReturnType, + useReadContract, + /** @deprecated Use `useWriteContract` instead */ + useReadContract as useContractRead, +} from '../hooks/useReadContract.js' + +export { + type UseReadContractsParameters, + type UseReadContractsReturnType, + useReadContracts, + /** @deprecated Use `useWriteContract` instead */ + useReadContracts as useContractReads, +} from '../hooks/useReadContracts.js' + +export { + type UseReconnectParameters, + type UseReconnectReturnType, + useReconnect, +} from '../hooks/useReconnect.js' + +export { + type UseSendCallsParameters, + type UseSendCallsReturnType, + useSendCalls, +} from '../hooks/useSendCalls.js' + +export { + type UseSendTransactionParameters, + type UseSendTransactionReturnType, + useSendTransaction, +} from '../hooks/useSendTransaction.js' + +export { + type UseShowCallsStatusParameters, + type UseShowCallsStatusReturnType, + useShowCallsStatus, +} from '../hooks/useShowCallsStatus.js' + +export { + type UseSignMessageParameters, + type UseSignMessageReturnType, + useSignMessage, +} from '../hooks/useSignMessage.js' + +export { + type UseSignTypedDataParameters, + type UseSignTypedDataReturnType, + useSignTypedData, +} from '../hooks/useSignTypedData.js' + +export { + type UseSimulateContractParameters, + type UseSimulateContractReturnType, + useSimulateContract, +} from '../hooks/useSimulateContract.js' + +export { + type UseStorageAtParameters, + type UseStorageAtReturnType, + useStorageAt, +} from '../hooks/useStorageAt.js' + +export { + type UseSwitchAccountParameters, + type UseSwitchAccountReturnType, + useSwitchAccount, +} from '../hooks/useSwitchAccount.js' + +export { + type UseSwitchChainParameters, + type UseSwitchChainReturnType, + useSwitchChain, +} from '../hooks/useSwitchChain.js' + +export { + type UseTokenParameters, + type UseTokenReturnType, + /** @deprecated Use `useReadContracts` instead */ + useToken, +} from '../hooks/useToken.js' + +export { + type UseTransactionParameters, + type UseTransactionReturnType, + useTransaction, +} from '../hooks/useTransaction.js' + +export { + type UseTransactionConfirmationsParameters, + type UseTransactionConfirmationsReturnType, + useTransactionConfirmations, +} from '../hooks/useTransactionConfirmations.js' + +export { + type UseTransactionCountParameters, + type UseTransactionCountReturnType, + useTransactionCount, +} from '../hooks/useTransactionCount.js' + +export { + type UseTransactionReceiptParameters, + type UseTransactionReceiptReturnType, + useTransactionReceipt, +} from '../hooks/useTransactionReceipt.js' + +export { + type UseVerifyMessageParameters, + type UseVerifyMessageReturnType, + useVerifyMessage, +} from '../hooks/useVerifyMessage.js' + +export { + type UseVerifyTypedDataParameters, + type UseVerifyTypedDataReturnType, + useVerifyTypedData, +} from '../hooks/useVerifyTypedData.js' + +export { + type UseWalletClientParameters, + type UseWalletClientReturnType, + useWalletClient, +} from '../hooks/useWalletClient.js' + +export { + type UseWaitForCallsStatusParameters, + type UseWaitForCallsStatusReturnType, + useWaitForCallsStatus, +} from '../hooks/useWaitForCallsStatus.js' + +export { + type UseWaitForTransactionReceiptParameters, + type UseWaitForTransactionReceiptReturnType, + useWaitForTransactionReceipt, +} from '../hooks/useWaitForTransactionReceipt.js' + +export { + type UseWatchAssetParameters, + type UseWatchAssetReturnType, + useWatchAsset, +} from '../hooks/useWatchAsset.js' + +export { + type UseWatchBlocksParameters, + type UseWatchBlocksReturnType, + useWatchBlocks, +} from '../hooks/useWatchBlocks.js' + +export { + type UseWatchBlockNumberParameters, + type UseWatchBlockNumberReturnType, + useWatchBlockNumber, +} from '../hooks/useWatchBlockNumber.js' + +export { + type UseWatchContractEventParameters, + type UseWatchContractEventReturnType, + useWatchContractEvent, +} from '../hooks/useWatchContractEvent.js' + +export { + type UseWatchPendingTransactionsParameters, + type UseWatchPendingTransactionsReturnType, + useWatchPendingTransactions, +} from '../hooks/useWatchPendingTransactions.js' + +export { + type UseWriteContractParameters, + type UseWriteContractReturnType, + useWriteContract, + /** @deprecated Use `useWriteContract` instead */ + useWriteContract as useContractWrite, +} from '../hooks/useWriteContract.js' + +//////////////////////////////////////////////////////////////////////////////// +// Hydrate +//////////////////////////////////////////////////////////////////////////////// + +export { + type HydrateProps, + Hydrate, +} from '../hydrate.js' + +//////////////////////////////////////////////////////////////////////////////// +// @wagmi/core +//////////////////////////////////////////////////////////////////////////////// + +export { + // Config + type Connection, + type Connector, + type Config, + type CreateConfigParameters, + type PartializedState, + type State, + createConfig, + // Connector + type ConnectorEventMap, + type CreateConnectorFn, + createConnector, + injected, + mock, + // Errors + type ChainNotConfiguredErrorType, + ChainNotConfiguredError, + type ConnectorAlreadyConnectedErrorType, + ConnectorAlreadyConnectedError, + type ConnectorNotFoundErrorType, + ConnectorNotFoundError, + type ConnectorAccountNotFoundErrorType, + ConnectorAccountNotFoundError, + type ConnectorChainMismatchErrorType, + ConnectorChainMismatchError, + type ConnectorUnavailableReconnectingErrorType, + ConnectorUnavailableReconnectingError, + type ProviderNotFoundErrorType, + ProviderNotFoundError, + type SwitchChainNotSupportedErrorType, + SwitchChainNotSupportedError, + // Storage + type CreateStorageParameters, + type Storage, + createStorage, + noopStorage, + // Transports + custom, + fallback, + http, + webSocket, + unstable_connector, + type Transport, + // Types + type Register, + type ResolvedRegister, + // Utilities + cookieStorage, + cookieToInitialState, + deepEqual, + deserialize, + normalizeChainId, + parseCookie, + serialize, +} from '@wagmi/core' + +//////////////////////////////////////////////////////////////////////////////// +// Version +//////////////////////////////////////////////////////////////////////////////// + +export { version } from '../version.js' diff --git a/packages/react/src/exports/query.test.ts b/packages/react/src/exports/query.test.ts new file mode 100644 index 0000000000..002b6abaab --- /dev/null +++ b/packages/react/src/exports/query.test.ts @@ -0,0 +1,100 @@ +import { expect, test } from 'vitest' + +import * as query from './query.js' + +test('exports', () => { + expect(Object.keys(query)).toMatchInlineSnapshot(` + [ + "callQueryKey", + "callQueryOptions", + "connectMutationOptions", + "deployContractMutationOptions", + "disconnectMutationOptions", + "estimateFeesPerGasQueryKey", + "estimateFeesPerGasQueryOptions", + "estimateGasQueryKey", + "estimateGasQueryOptions", + "estimateMaxPriorityFeePerGasQueryKey", + "estimateMaxPriorityFeePerGasQueryOptions", + "getBalanceQueryKey", + "getBalanceQueryOptions", + "getBlockQueryKey", + "getBlockQueryOptions", + "getBlockNumberQueryKey", + "getBlockNumberQueryOptions", + "getBlockTransactionCountQueryKey", + "getBlockTransactionCountQueryOptions", + "getBytecodeQueryKey", + "getBytecodeQueryOptions", + "getCallsStatusQueryKey", + "getCallsStatusQueryOptions", + "getCapabilitiesQueryKey", + "getCapabilitiesQueryOptions", + "getConnectorClientQueryKey", + "getConnectorClientQueryOptions", + "getEnsAddressQueryKey", + "getEnsAddressQueryOptions", + "getEnsAvatarQueryKey", + "getEnsAvatarQueryOptions", + "getEnsNameQueryKey", + "getEnsNameQueryOptions", + "getEnsResolverQueryKey", + "getEnsResolverQueryOptions", + "getEnsTextQueryKey", + "getEnsTextQueryOptions", + "getFeeHistoryQueryKey", + "getFeeHistoryQueryOptions", + "getGasPriceQueryKey", + "getGasPriceQueryOptions", + "getProofQueryKey", + "getProofQueryOptions", + "getStorageAtQueryKey", + "getStorageAtQueryOptions", + "getTokenQueryKey", + "getTokenQueryOptions", + "getTransactionQueryKey", + "getTransactionQueryOptions", + "getTransactionConfirmationsQueryKey", + "getTransactionConfirmationsQueryOptions", + "getTransactionCountQueryKey", + "getTransactionCountQueryOptions", + "getTransactionReceiptQueryKey", + "getTransactionReceiptQueryOptions", + "getWalletClientQueryKey", + "getWalletClientQueryOptions", + "infiniteReadContractsQueryKey", + "infiniteReadContractsQueryOptions", + "prepareTransactionRequestQueryKey", + "prepareTransactionRequestQueryOptions", + "readContractQueryKey", + "readContractQueryOptions", + "readContractsQueryKey", + "readContractsQueryOptions", + "reconnectMutationOptions", + "sendCallsMutationOptions", + "showCallsStatusMutationOptions", + "sendTransactionMutationOptions", + "signMessageMutationOptions", + "signTypedDataMutationOptions", + "switchAccountMutationOptions", + "simulateContractQueryKey", + "simulateContractQueryOptions", + "switchChainMutationOptions", + "verifyMessageQueryKey", + "verifyMessageQueryOptions", + "verifyTypedDataQueryKey", + "verifyTypedDataQueryOptions", + "waitForCallsStatusQueryKey", + "waitForCallsStatusQueryOptions", + "waitForTransactionReceiptQueryKey", + "waitForTransactionReceiptQueryOptions", + "watchAssetMutationOptions", + "writeContractMutationOptions", + "hashFn", + "structuralSharing", + "useInfiniteQuery", + "useMutation", + "useQuery", + ] + `) +}) diff --git a/packages/react/src/exports/query.ts b/packages/react/src/exports/query.ts new file mode 100644 index 0000000000..ff91fee2d9 --- /dev/null +++ b/packages/react/src/exports/query.ts @@ -0,0 +1,19 @@ +//////////////////////////////////////////////////////////////////////////////// +// @wagmi/core/query +//////////////////////////////////////////////////////////////////////////////// + +// biome-ignore lint/performance/noBarrelFile: entrypoint module +// biome-ignore lint/performance/noReExportAll: entrypoint module +export * from '@wagmi/core/query' + +export { + type UseInfiniteQueryParameters, + type UseInfiniteQueryReturnType, + type UseMutationParameters, + type UseMutationReturnType, + type UseQueryParameters, + type UseQueryReturnType, + useInfiniteQuery, + useMutation, + useQuery, +} from '../utils/query.js' diff --git a/packages/react/src/hooks/codegen/createUseReadContract.test-d.ts b/packages/react/src/hooks/codegen/createUseReadContract.test-d.ts new file mode 100644 index 0000000000..f0331efed2 --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseReadContract.test-d.ts @@ -0,0 +1,152 @@ +import { abi, mainnet, optimism } from '@wagmi/test' +import { assertType, expectTypeOf, test } from 'vitest' + +import { createUseReadContract } from './createUseReadContract.js' + +test('default', () => { + const useReadErc20 = createUseReadContract({ + abi: abi.erc20, + }) + + const result = useReadErc20({ + functionName: 'balanceOf', + args: ['0x'], + chainId: 123, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('select data', () => { + const useReadErc20 = createUseReadContract({ + abi: abi.erc20, + }) + + const result = useReadErc20({ + address: '0x', + functionName: 'balanceOf', + args: ['0x'], + query: { + select(data) { + expectTypeOf(data).toEqualTypeOf() + return data?.toString() + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('multichain address', () => { + const useReadErc20 = createUseReadContract({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + const result = useReadErc20({ + functionName: 'balanceOf', + args: ['0x'], + chainId: mainnet.id, + // ^? + }) + assertType(result.data) + + useReadErc20({ + functionName: 'balanceOf', + args: ['0x'], + // @ts-expect-error chain id must match address keys + chainId: 420, + }) + + useReadErc20({ + functionName: 'balanceOf', + args: ['0x'], + // @ts-expect-error address not allowed + address: '0x', + }) +}) + +test('overloads', () => { + const useReadViewOverloads = createUseReadContract({ + abi: abi.viewOverloads, + }) + + const result1 = useReadViewOverloads({ + functionName: 'foo', + }) + assertType(result1.data) + + const result2 = useReadViewOverloads({ + functionName: 'foo', + args: [], + }) + assertType(result2.data) + + const result3 = useReadViewOverloads({ + functionName: 'foo', + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3.data) + + const result4 = useReadViewOverloads({ + functionName: 'foo', + args: ['0x', '0x'], + }) + assertType< + | { + foo: `0x${string}` + bar: `0x${string}` + } + | undefined + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + >(result4.data) +}) + +test('functionName', () => { + const useReadErc20BalanceOf = createUseReadContract({ + abi: abi.erc20, + address: '0x', + functionName: 'balanceOf', + }) + + const result = useReadErc20BalanceOf({ + args: ['0x'], + chainId: 1, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('functionName with overloads', () => { + const useReadViewOverloads = createUseReadContract({ + abi: abi.viewOverloads, + functionName: 'foo', + }) + + const result1 = useReadViewOverloads() + assertType(result1.data) + + const result2 = useReadViewOverloads({ + args: [], + }) + assertType(result2.data) + + const result3 = useReadViewOverloads({ + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3.data) + + const result4 = useReadViewOverloads({ + args: ['0x', '0x'], + }) + assertType< + | { + foo: `0x${string}` + bar: `0x${string}` + } + | undefined + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + >(result4.data) +}) diff --git a/packages/react/src/hooks/codegen/createUseReadContract.test.ts b/packages/react/src/hooks/codegen/createUseReadContract.test.ts new file mode 100644 index 0000000000..4419c030c5 --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseReadContract.test.ts @@ -0,0 +1,177 @@ +import { abi, address, chain } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { createUseReadContract } from './createUseReadContract.js' + +test('default', async () => { + const useReadWagmiMintExample = createUseReadContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + }) + + const { result } = renderHook(() => + useReadWagmiMintExample({ + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 4n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "readContract", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 1, + "functionName": "balanceOf", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('multichain', async () => { + const useReadWagmiMintExample = createUseReadContract({ + address: { + [chain.mainnet.id]: address.wagmiMintExample, + [chain.mainnet2.id]: address.wagmiMintExample, + }, + abi: abi.wagmiMintExample, + }) + + const { result } = renderHook(() => + useReadWagmiMintExample({ + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + chainId: chain.mainnet2.id, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 4n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "readContract", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 456, + "functionName": "balanceOf", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('functionName', async () => { + const useReadWagmiMintExampleBalanceOf = createUseReadContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + }) + + const { result } = renderHook(() => + useReadWagmiMintExampleBalanceOf({ + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 4n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "readContract", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 1, + "functionName": "balanceOf", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/codegen/createUseReadContract.ts b/packages/react/src/hooks/codegen/createUseReadContract.ts new file mode 100644 index 0000000000..1c5f509e58 --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseReadContract.ts @@ -0,0 +1,127 @@ +import type { + Config, + ReadContractErrorType, + ReadContractParameters, + ResolvedRegister, +} from '@wagmi/core' +import type { + ScopeKeyParameter, + UnionCompute, + UnionExactPartial, + UnionStrictOmit, +} from '@wagmi/core/internal' +import type { + ReadContractData, + ReadContractQueryFnData, + ReadContractQueryKey, +} from '@wagmi/core/query' +import type { + Abi, + Address, + ContractFunctionArgs, + ContractFunctionName, +} from 'viem' + +import type { ConfigParameter, QueryParameter } from '../../types/properties.js' +import { useAccount } from '../useAccount.js' +import { useChainId } from '../useChainId.js' +import { useConfig } from '../useConfig.js' +import { + type UseReadContractReturnType, + useReadContract, +} from '../useReadContract.js' + +type stateMutability = 'pure' | 'view' + +export type CreateUseReadContractParameters< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +> = { + abi: abi | Abi | readonly unknown[] + address?: address | Address | Record | undefined + functionName?: + | functionName + | ContractFunctionName + | undefined +} + +export type CreateUseReadContractReturnType< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined, + functionName extends ContractFunctionName | undefined, + /// + omittedProperties extends 'abi' | 'address' | 'chainId' | 'functionName' = + | 'abi' + | (address extends undefined ? never : 'address') + | (address extends Record ? 'chainId' : never) + | (functionName extends undefined ? never : 'functionName'), +> = < + name extends functionName extends ContractFunctionName + ? functionName + : ContractFunctionName, + args extends ContractFunctionArgs, + config extends Config = ResolvedRegister['config'], + selectData = ReadContractData, +>( + parameters?: UnionCompute< + UnionExactPartial< + UnionStrictOmit< + ReadContractParameters, + omittedProperties + > + > & + ScopeKeyParameter & + ConfigParameter & + QueryParameter< + ReadContractQueryFnData, + ReadContractErrorType, + selectData, + ReadContractQueryKey + > + > & + (address extends Record + ? { chainId?: keyof address | undefined } + : unknown), +) => UseReadContractReturnType + +export function createUseReadContract< + const abi extends Abi | readonly unknown[], + const address extends + | Address + | Record + | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +>( + props: CreateUseReadContractParameters, +): CreateUseReadContractReturnType { + if (props.address !== undefined && typeof props.address === 'object') + return (parameters) => { + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const account = useAccount({ config }) + const chainId = + (parameters as { chainId?: number })?.chainId ?? + account.chainId ?? + configChainId + return useReadContract({ + ...(parameters as any), + ...(props.functionName ? { functionName: props.functionName } : {}), + address: props.address?.[chainId], + abi: props.abi, + }) + } + + return (parameters) => { + return useReadContract({ + ...(parameters as any), + ...(props.address ? { address: props.address } : {}), + ...(props.functionName ? { functionName: props.functionName } : {}), + abi: props.abi, + }) + } +} diff --git a/packages/react/src/hooks/codegen/createUseSimulateContract.test-d.ts b/packages/react/src/hooks/codegen/createUseSimulateContract.test-d.ts new file mode 100644 index 0000000000..5388e69109 --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseSimulateContract.test-d.ts @@ -0,0 +1,199 @@ +import { abi, mainnet, optimism } from '@wagmi/test' +import type { Address } from 'viem' +import { assertType, expectTypeOf, test } from 'vitest' + +import { createUseSimulateContract } from './createUseSimulateContract.js' + +test('default', () => { + const useSimulateErc20 = createUseSimulateContract({ + abi: abi.erc20, + }) + + const result = useSimulateErc20({ + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 123, + }) + result.data?.request.chainId + expectTypeOf(result.data).toMatchTypeOf< + | { + result: boolean + request: { + chainId: 123 + abi: readonly [ + { + readonly name: 'transferFrom' + readonly type: 'function' + readonly stateMutability: 'nonpayable' + readonly inputs: readonly [ + { readonly type: 'address'; readonly name: 'sender' }, + { readonly type: 'address'; readonly name: 'recipient' }, + { readonly type: 'uint256'; readonly name: 'amount' }, + ] + readonly outputs: readonly [{ type: 'bool' }] + }, + ] + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + } + } + | undefined + >() +}) + +test('select data', () => { + const useSimulateErc20 = createUseSimulateContract({ + abi: abi.erc20, + }) + + const result = useSimulateErc20({ + address: '0x', + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + query: { + select(data) { + expectTypeOf(data.result).toEqualTypeOf() + return data?.toString() + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('multichain address', () => { + const useSimulateErc20 = createUseSimulateContract({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + const result = useSimulateErc20({ + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: optimism.id, + }) + expectTypeOf(result.data?.result).toEqualTypeOf() + + useSimulateErc20({ + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error chain id must match address keys + chainId: 420, + }) + + useSimulateErc20({ + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error address not allowed + address: '0x', + }) +}) + +test('overloads', () => { + const useSimulateWriteOverloads = createUseSimulateContract({ + abi: abi.writeOverloads, + }) + + const result1 = useSimulateWriteOverloads({ + functionName: 'foo', + }) + assertType(result1.data?.result) + + const result2 = useSimulateWriteOverloads({ + functionName: 'foo', + args: [], + }) + assertType(result2.data?.result) + + const result3 = useSimulateWriteOverloads({ + functionName: 'foo', + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3.data?.result) + + const result4 = useSimulateWriteOverloads({ + functionName: 'foo', + args: ['0x', '0x'], + }) + assertType< + | { + foo: `0x${string}` + bar: `0x${string}` + } + | undefined + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + >(result4.data?.result) +}) + +test('functionName', () => { + const useSimulateErc20 = createUseSimulateContract({ + abi: abi.erc20, + functionName: 'transferFrom', + }) + + const result = useSimulateErc20({ + args: ['0x', '0x', 123n], + chainId: 123, + }) + result.data?.request.chainId + expectTypeOf(result.data).toMatchTypeOf< + | { + result: boolean + request: { + chainId: 123 + abi: readonly [ + { + readonly name: 'transferFrom' + readonly type: 'function' + readonly stateMutability: 'nonpayable' + readonly inputs: readonly [ + { readonly type: 'address'; readonly name: 'sender' }, + { readonly type: 'address'; readonly name: 'recipient' }, + { readonly type: 'uint256'; readonly name: 'amount' }, + ] + readonly outputs: readonly [{ type: 'bool' }] + }, + ] + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + } + } + | undefined + >() +}) + +test('functionName with overloads', () => { + const useSimulateWriteOverloads = createUseSimulateContract({ + abi: abi.writeOverloads, + functionName: 'foo', + }) + + const result1 = useSimulateWriteOverloads({}) + assertType(result1.data?.result) + + const result2 = useSimulateWriteOverloads({ + args: [], + }) + assertType(result2.data?.result) + + const result3 = useSimulateWriteOverloads({ + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3.data?.result) + + const result4 = useSimulateWriteOverloads({ + args: ['0x', '0x'], + }) + assertType< + | { + foo: `0x${string}` + bar: `0x${string}` + } + | undefined + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + >(result4.data?.result) +}) diff --git a/packages/react/src/hooks/codegen/createUseSimulateContract.test.ts b/packages/react/src/hooks/codegen/createUseSimulateContract.test.ts new file mode 100644 index 0000000000..c6e70b90ca --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseSimulateContract.test.ts @@ -0,0 +1,258 @@ +import { connect, disconnect } from '@wagmi/core' +import { abi, address, chain, config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { createUseSimulateContract } from './createUseSimulateContract.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const useSimulateWagmiMintExample = createUseSimulateContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + }) + + const { result } = renderHook(() => + useSimulateWagmiMintExample({ + functionName: 'mint', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "chainId": 1, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": 1, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "simulateContract", + { + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 1, + "functionName": "mint", + }, + ], + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) + +test('multichain', async () => { + await connect(config, { connector }) + + const useReadWagmiMintExample = createUseSimulateContract({ + address: { + [chain.mainnet.id]: address.wagmiMintExample, + [chain.mainnet2.id]: address.wagmiMintExample, + }, + abi: abi.wagmiMintExample, + }) + + const { result } = renderHook(() => + useReadWagmiMintExample({ + functionName: 'mint', + chainId: chain.mainnet2.id, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "chainId": 456, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": 456, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "simulateContract", + { + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 456, + "functionName": "mint", + }, + ], + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) + +test('functionName', async () => { + await connect(config, { connector }) + + const useSimulateWagmiMintExample = createUseSimulateContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'mint', + }) + + const { result } = renderHook(() => useSimulateWagmiMintExample({})) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "chainId": 1, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": 1, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "simulateContract", + { + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 1, + "functionName": "mint", + }, + ], + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/codegen/createUseSimulateContract.ts b/packages/react/src/hooks/codegen/createUseSimulateContract.ts new file mode 100644 index 0000000000..51758b995a --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseSimulateContract.ts @@ -0,0 +1,120 @@ +import type { + Config, + ResolvedRegister, + SimulateContractErrorType, + SimulateContractParameters, +} from '@wagmi/core' +import type { ScopeKeyParameter, UnionExactPartial } from '@wagmi/core/internal' +import type { + SimulateContractData, + SimulateContractQueryFnData, + SimulateContractQueryKey, +} from '@wagmi/core/query' +import type { + Abi, + Address, + ContractFunctionArgs, + ContractFunctionName, +} from 'viem' + +import type { ConfigParameter, QueryParameter } from '../../types/properties.js' +import { useAccount } from '../useAccount.js' +import { useChainId } from '../useChainId.js' +import { useConfig } from '../useConfig.js' +import { + type UseSimulateContractReturnType, + useSimulateContract, +} from '../useSimulateContract.js' + +type stateMutability = 'nonpayable' | 'payable' + +export type CreateUseSimulateContractParameters< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +> = { + abi: abi | Abi | readonly unknown[] + address?: address | Address | Record | undefined + functionName?: + | functionName + | ContractFunctionName + | undefined +} + +export type CreateUseSimulateContractReturnType< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined, + functionName extends ContractFunctionName | undefined, +> = < + name extends functionName extends ContractFunctionName + ? functionName + : ContractFunctionName, + args extends ContractFunctionArgs, + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = SimulateContractData, +>( + parameters?: { + abi?: undefined + address?: address extends undefined ? Address : undefined + functionName?: functionName extends undefined ? name : undefined + chainId?: address extends Record + ? + | keyof address + | (chainId extends keyof address ? chainId : never) + | undefined + : chainId | number | undefined + } & UnionExactPartial< + // TODO: Take `abi` and `address` from above and omit from below (currently breaks inference) + SimulateContractParameters + > & + ScopeKeyParameter & + ConfigParameter & + QueryParameter< + SimulateContractQueryFnData, + SimulateContractErrorType, + selectData, + SimulateContractQueryKey + >, +) => UseSimulateContractReturnType + +export function createUseSimulateContract< + const abi extends Abi | readonly unknown[], + const address extends + | Address + | Record + | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +>( + props: CreateUseSimulateContractParameters, +): CreateUseSimulateContractReturnType { + if (props.address !== undefined && typeof props.address === 'object') + return (parameters) => { + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const account = useAccount({ config }) + const chainId = + (parameters as { chainId?: number })?.chainId ?? + account.chainId ?? + configChainId + return useSimulateContract({ + ...(parameters as any), + ...(props.functionName ? { functionName: props.functionName } : {}), + address: props.address?.[chainId], + abi: props.abi, + }) + } + + return (parameters) => { + return useSimulateContract({ + ...(parameters as any), + ...(props.address ? { address: props.address } : {}), + ...(props.functionName ? { functionName: props.functionName } : {}), + abi: props.abi, + }) + } +} diff --git a/packages/react/src/hooks/codegen/createUseWatchContractEvent.test-d.ts b/packages/react/src/hooks/codegen/createUseWatchContractEvent.test-d.ts new file mode 100644 index 0000000000..b3a69775b8 --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseWatchContractEvent.test-d.ts @@ -0,0 +1,123 @@ +import { http, createConfig, webSocket } from '@wagmi/core' +import { abi, mainnet, optimism } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { createUseWatchContractEvent } from './createUseWatchContractEvent.js' + +test('default', () => { + const useWatchErc20Event = createUseWatchContractEvent({ + abi: abi.erc20, + }) + + useWatchErc20Event({ + eventName: 'Transfer', + chainId: 123, + onLogs(logs) { + expectTypeOf(logs[0]!.eventName).toEqualTypeOf<'Transfer'>() + expectTypeOf(logs[0]!.args).toEqualTypeOf<{ + from?: `0x${string}` | undefined + to?: `0x${string}` | undefined + value?: bigint | undefined + }>() + }, + }) +}) + +test('multichain address', () => { + const useWatchErc20Event = createUseWatchContractEvent({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + useWatchErc20Event({ + eventName: 'Transfer', + chainId: mainnet.id, + // ^? + }) + + useWatchErc20Event({ + eventName: 'Transfer', + // @ts-expect-error chain id must match address keys + chainId: 420, + }) + + useWatchErc20Event({ + eventName: 'Transfer', + // @ts-expect-error chain id must match address keys + address: '0x', + }) +}) + +test('differing transports', () => { + const useWatchErc20Event = createUseWatchContractEvent({ + abi: abi.erc20, + }) + + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + useWatchErc20Event({ + config, + poll: false, + address: '0x', + onLogs() {}, + }) + + useWatchErc20Event({ + config, + chainId: mainnet.id, + poll: true, + address: '0x', + onLogs() {}, + }) + useWatchErc20Event({ + config, + chainId: mainnet.id, + // @ts-expect-error poll required since http transport + poll: false, + address: '0x', + onLogs() {}, + }) + + useWatchErc20Event({ + config, + chainId: optimism.id, + poll: true, + address: '0x', + onLogs() {}, + }) + useWatchErc20Event({ + config, + chainId: optimism.id, + poll: false, + address: '0x', + onLogs() {}, + }) +}) + +test('eventName', () => { + const useWatchErc20Event = createUseWatchContractEvent({ + abi: abi.erc20, + eventName: 'Transfer', + }) + + useWatchErc20Event({ + chainId: 123, + onLogs(logs) { + expectTypeOf(logs[0]!.eventName).toEqualTypeOf<'Transfer'>() + expectTypeOf(logs[0]!.args).toEqualTypeOf<{ + from?: `0x${string}` | undefined + to?: `0x${string}` | undefined + value?: bigint | undefined + }>() + }, + }) +}) diff --git a/packages/react/src/hooks/codegen/createUseWatchContractEvent.test.ts b/packages/react/src/hooks/codegen/createUseWatchContractEvent.test.ts new file mode 100644 index 0000000000..61464fe3d2 --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseWatchContractEvent.test.ts @@ -0,0 +1,44 @@ +import { abi, address, chain } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import type { WatchEventOnLogsParameter } from 'viem' +import { test } from 'vitest' + +import { createUseWatchContractEvent } from './createUseWatchContractEvent.js' + +test('default', async () => { + const useWatchErc20Event = createUseWatchContractEvent({ + address: address.usdc, + abi: abi.wagmiMintExample, + }) + + let logs: WatchEventOnLogsParameter = [] + renderHook(() => + useWatchErc20Event({ + eventName: 'Transfer', + onLogs(next) { + logs = logs.concat(next) + }, + }), + ) +}) + +test('multichain', async () => { + const useWatchErc20Event = createUseWatchContractEvent({ + address: { + [chain.mainnet.id]: address.usdc, + [chain.mainnet2.id]: address.usdc, + }, + abi: abi.wagmiMintExample, + }) + + let logs: WatchEventOnLogsParameter = [] + renderHook(() => + useWatchErc20Event({ + eventName: 'Transfer', + chainId: chain.mainnet2.id, + onLogs(next) { + logs = logs.concat(next) + }, + }), + ) +}) diff --git a/packages/react/src/hooks/codegen/createUseWatchContractEvent.ts b/packages/react/src/hooks/codegen/createUseWatchContractEvent.ts new file mode 100644 index 0000000000..e453b9442b --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseWatchContractEvent.ts @@ -0,0 +1,101 @@ +import type { + Config, + ResolvedRegister, + WatchContractEventParameters, +} from '@wagmi/core' +import type { + UnionCompute, + UnionExactPartial, + UnionStrictOmit, +} from '@wagmi/core/internal' +import type { Abi, Address, ContractEventName } from 'viem' + +import type { + ConfigParameter, + EnabledParameter, +} from '../../types/properties.js' +import { useAccount } from '../useAccount.js' +import { useChainId } from '../useChainId.js' +import { useConfig } from '../useConfig.js' +import { useWatchContractEvent } from '../useWatchContractEvent.js' + +export type CreateUseWatchContractEventParameters< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined = undefined, + eventName extends ContractEventName | undefined = undefined, +> = { + abi: abi | Abi | readonly unknown[] + address?: address | Address | Record | undefined + eventName?: eventName | ContractEventName | undefined +} + +export type CreateUseWatchContractEventReturnType< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined, + eventName extends ContractEventName | undefined, + /// + omittedProperties extends 'abi' | 'address' | 'chainId' | 'eventName' = + | 'abi' + | (address extends undefined ? never : 'address') + | (address extends Record ? 'chainId' : never) + | (eventName extends undefined ? never : 'eventName'), +> = < + name extends eventName extends ContractEventName + ? eventName + : ContractEventName, + strict extends boolean | undefined = undefined, + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + parameters?: UnionCompute< + UnionExactPartial< + UnionStrictOmit< + WatchContractEventParameters, + omittedProperties + > + > & + ConfigParameter & + EnabledParameter + > & + (address extends Record + ? { chainId?: keyof address | undefined } + : unknown), +) => void + +export function createUseWatchContractEvent< + const abi extends Abi | readonly unknown[], + const address extends + | Address + | Record + | undefined = undefined, + eventName extends ContractEventName | undefined = undefined, +>( + props: CreateUseWatchContractEventParameters, +): CreateUseWatchContractEventReturnType { + if (props.address !== undefined && typeof props.address === 'object') + return (parameters) => { + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const account = useAccount({ config }) + const chainId = + (parameters as { chainId?: number })?.chainId ?? + account.chainId ?? + configChainId + return useWatchContractEvent({ + ...(parameters as any), + ...(props.eventName ? { eventName: props.eventName } : {}), + address: props.address?.[chainId], + abi: props.abi, + }) + } + + return (parameters) => { + return useWatchContractEvent({ + ...(parameters as any), + ...(props.address ? { address: props.address } : {}), + ...(props.eventName ? { eventName: props.eventName } : {}), + abi: props.abi, + }) + } +} diff --git a/packages/react/src/hooks/codegen/createUseWriteContract.test-d.ts b/packages/react/src/hooks/codegen/createUseWriteContract.test-d.ts new file mode 100644 index 0000000000..14906fda41 --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseWriteContract.test-d.ts @@ -0,0 +1,153 @@ +import { abi } from '@wagmi/test' +import type { Address, Hash } from 'viem' +import { mainnet, optimism } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { useSimulateContract } from '../useSimulateContract.js' +import { createUseWriteContract } from './createUseWriteContract.js' + +const contextValue = { foo: 'bar' } as const + +test('default', () => { + const useWriteErc20 = createUseWriteContract({ + abi: abi.erc20, + }) + + const { writeContract } = useWriteErc20() + writeContract({ + address: '0x', + functionName: 'transfer', + args: ['0x', 123n], + }) +}) + +test('context', () => { + const useWriteErc20 = createUseWriteContract({ + abi: abi.erc20, + }) + + const { writeContract } = useWriteErc20({ + mutation: { + onMutate() { + return contextValue + }, + onSuccess(data, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(variables.functionName).toEqualTypeOf() + expectTypeOf(variables.args).toEqualTypeOf< + readonly unknown[] | undefined + >() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + writeContract( + { + address: '0x', + functionName: 'transfer', + args: ['0x', 123n], + }, + { + onSuccess(data, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(variables.functionName).toEqualTypeOf<'transfer'>() + expectTypeOf(variables.args).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) + +test('multichain address', () => { + const useWriteErc20 = createUseWriteContract({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + const { writeContract } = useWriteErc20() + writeContract({ + functionName: 'transfer', + args: ['0x', 123n], + chainId: mainnet.id, + // ^? + }) + + writeContract({ + functionName: 'transfer', + args: ['0x', 123n], + // @ts-expect-error chain id must match address keys + chainId: 420, + }) + + writeContract({ + // @ts-expect-error address not allowed + address: '0x', + functionName: 'transfer', + args: ['0x', 123n], + }) +}) + +test('overloads', () => { + const useWriteOverloads = createUseWriteContract({ + abi: abi.writeOverloads, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + const { writeContract } = useWriteOverloads() + writeContract({ + functionName: 'foo', + args: [], + }) + + writeContract({ + functionName: 'foo', + args: ['0x'], + }) + + writeContract({ + functionName: 'foo', + args: ['0x', '0x'], + }) +}) + +test('useSimulateContract', () => { + const useWriteErc20 = createUseWriteContract({ + abi: abi.erc20, + address: { + [mainnet.id]: '0x', + [optimism.id]: '0x', + }, + }) + + const { data } = useSimulateContract({ + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + }) + const { writeContract } = useWriteErc20() + + const request = data?.request + if (request) writeContract(request) +}) + +test('functionName', () => { + const useWriteErc20 = createUseWriteContract({ + abi: abi.erc20, + functionName: 'transfer', + }) + + const { writeContract } = useWriteErc20() + writeContract({ + address: '0x', + args: ['0x', 123n], + }) +}) diff --git a/packages/react/src/hooks/codegen/createUseWriteContract.test.ts b/packages/react/src/hooks/codegen/createUseWriteContract.test.ts new file mode 100644 index 0000000000..e89dc62141 --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseWriteContract.test.ts @@ -0,0 +1,13 @@ +import { abi } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { test } from 'vitest' + +import { createUseWriteContract } from './createUseWriteContract.js' + +test('default', () => { + const useWriteErc20 = createUseWriteContract({ + abi: abi.erc20, + }) + + renderHook(() => useWriteErc20()) +}) diff --git a/packages/react/src/hooks/codegen/createUseWriteContract.ts b/packages/react/src/hooks/codegen/createUseWriteContract.ts new file mode 100644 index 0000000000..9e58fd973b --- /dev/null +++ b/packages/react/src/hooks/codegen/createUseWriteContract.ts @@ -0,0 +1,297 @@ +import type { MutateOptions } from '@tanstack/react-query' +import type { + Config, + ResolvedRegister, + WriteContractErrorType, +} from '@wagmi/core' +import type { + ChainIdParameter, + Compute, + ConnectorParameter, + SelectChains, + UnionCompute, + UnionStrictOmit, +} from '@wagmi/core/internal' +import type { + WriteContractData, + WriteContractVariables, +} from '@wagmi/core/query' +import { useCallback } from 'react' +import type { + Abi, + Account, + Address, + Chain, + ContractFunctionArgs, + ContractFunctionName, +} from 'viem' +import type { WriteContractParameters as viem_WriteContractParameters } from 'viem/actions' + +import { useAccount } from '../useAccount.js' +import { useChainId } from '../useChainId.js' +import { useConfig } from '../useConfig.js' +import { + type UseWriteContractParameters, + useWriteContract, + type UseWriteContractReturnType as wagmi_UseWriteContractReturnType, +} from '../useWriteContract.js' + +type stateMutability = 'nonpayable' | 'payable' + +export type CreateUseWriteContractParameters< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +> = { + abi: abi | Abi | readonly unknown[] + address?: address | Address | Record | undefined + functionName?: + | functionName + | ContractFunctionName + | undefined +} + +export type CreateUseWriteContractReturnType< + abi extends Abi | readonly unknown[], + address extends Address | Record | undefined, + functionName extends ContractFunctionName | undefined, +> = ( + parameters?: UseWriteContractParameters, +) => Compute< + Omit< + wagmi_UseWriteContractReturnType, + 'writeContract' | 'writeContractAsync' + > & { + writeContract: < + const abi2 extends abi, + name extends functionName extends ContractFunctionName< + abi, + stateMutability + > + ? functionName + : ContractFunctionName, + args extends ContractFunctionArgs, + chainId extends config['chains'][number]['id'], + >( + variables: Variables< + abi2, + functionName, + name, + args, + config, + chainId, + address + >, + options?: + | MutateOptions< + WriteContractData, + WriteContractErrorType, + WriteContractVariables< + abi2, + name, + args, + config, + chainId, + // use `functionName` to make sure it's not union of all possible function names + name + >, + context + > + | undefined, + ) => void + writeContractAsync: < + const abi2 extends abi, + name extends functionName extends ContractFunctionName< + abi, + stateMutability + > + ? functionName + : ContractFunctionName, + args extends ContractFunctionArgs, + chainId extends config['chains'][number]['id'], + >( + variables: Variables< + abi2, + functionName, + name, + args, + config, + chainId, + address + >, + options?: + | MutateOptions< + WriteContractData, + WriteContractErrorType, + WriteContractVariables< + abi2, + name, + args, + config, + chainId, + // use `functionName` to make sure it's not union of all possible function names + name + >, + context + > + | undefined, + ) => Promise + } +> + +export function createUseWriteContract< + const abi extends Abi | readonly unknown[], + const address extends + | Address + | Record + | undefined = undefined, + functionName extends + | ContractFunctionName + | undefined = undefined, +>( + props: CreateUseWriteContractParameters, +): CreateUseWriteContractReturnType { + if (props.address !== undefined && typeof props.address === 'object') + return (parameters) => { + const config = useConfig(parameters) + const result = useWriteContract(parameters) + const configChainId = useChainId({ config }) + const account = useAccount({ config }) + type Args = Parameters + return { + ...(result as any), + writeContract: useCallback( + (...args: Args) => { + let chainId: number | undefined + if (args[0].chainId) chainId = args[0].chainId + else if (args[0].account && args[0].account === account.address) + chainId = account.chainId + else if (args[0].account === undefined) chainId = account.chainId + else chainId = configChainId + + const variables = { + ...(args[0] as any), + address: chainId ? props.address?.[chainId] : undefined, + ...(props.functionName + ? { functionName: props.functionName } + : {}), + abi: props.abi, + } + result.writeContract(variables, args[1] as any) + }, + [ + account.address, + account.chainId, + props, + configChainId, + result.writeContract, + ], + ), + writeContractAsync: useCallback( + (...args: Args) => { + let chainId: number | undefined + if (args[0].chainId) chainId = args[0].chainId + else if (args[0].account && args[0].account === account.address) + chainId = account.chainId + else if (args[0].account === undefined) chainId = account.chainId + else chainId = configChainId + + const variables = { + ...(args[0] as any), + address: chainId ? props.address?.[chainId] : undefined, + ...(props.functionName + ? { functionName: props.functionName } + : {}), + abi: props.abi, + } + return result.writeContractAsync(variables, args[1] as any) + }, + [ + account.address, + account.chainId, + props, + configChainId, + result.writeContractAsync, + ], + ), + } + } + + return (parameters) => { + const result = useWriteContract(parameters) + type Args = Parameters + return { + ...(result as any), + writeContract: useCallback( + (...args: Args) => { + const variables = { + ...(args[0] as any), + ...(props.address ? { address: props.address } : {}), + ...(props.functionName ? { functionName: props.functionName } : {}), + abi: props.abi, + } + result.writeContract(variables, args[1] as any) + }, + [props, result.writeContract], + ), + writeContractAsync: useCallback( + (...args: Args) => { + const variables = { + ...(args[0] as any), + ...(props.address ? { address: props.address } : {}), + ...(props.functionName ? { functionName: props.functionName } : {}), + abi: props.abi, + } + return result.writeContractAsync(variables, args[1] as any) + }, + [props, result.writeContractAsync], + ), + } + } +} + +type Variables< + abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName | undefined, + name extends ContractFunctionName, + args extends ContractFunctionArgs, + config extends Config, + chainId extends config['chains'][number]['id'], + address extends Address | Record | undefined, + /// + allFunctionNames = ContractFunctionName, + chains extends readonly Chain[] = SelectChains, + omittedProperties extends 'abi' | 'address' | 'functionName' = + | 'abi' + | (address extends undefined ? never : 'address') + | (functionName extends undefined ? never : 'functionName'), +> = UnionCompute< + { + [key in keyof chains]: UnionStrictOmit< + viem_WriteContractParameters< + abi, + name, + args, + chains[key], + Account, + chains[key], + allFunctionNames + >, + omittedProperties | 'chain' + > + }[number] & + (address extends Record + ? { + chainId?: + | keyof address + | (chainId extends keyof address ? chainId : never) + | undefined + } + : Compute>) & + ConnectorParameter & { + /** @deprecated */ + __mode?: 'prepared' + } +> diff --git a/packages/react/src/hooks/useAccount.test-d.ts b/packages/react/src/hooks/useAccount.test-d.ts new file mode 100644 index 0000000000..a22d816d6a --- /dev/null +++ b/packages/react/src/hooks/useAccount.test-d.ts @@ -0,0 +1,68 @@ +import type { Connector } from '@wagmi/core' +import type { Address, Chain } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { useAccount } from './useAccount.js' + +test('states', () => { + const result = useAccount() + + switch (result.status) { + case 'reconnecting': { + expectTypeOf(result).toMatchTypeOf<{ + address: Address | undefined + chain: Chain | undefined + chainId: number | undefined + connector: Connector | undefined + isConnected: boolean + isConnecting: false + isDisconnected: false + isReconnecting: true + status: 'reconnecting' + }>() + break + } + case 'connecting': { + expectTypeOf(result).toMatchTypeOf<{ + address: Address | undefined + chain: Chain | undefined + chainId: number | undefined + connector: Connector | undefined + isConnected: false + isReconnecting: false + isConnecting: true + isDisconnected: false + status: 'connecting' + }>() + break + } + case 'connected': { + expectTypeOf(result).toMatchTypeOf<{ + address: Address + chain: Chain | undefined + chainId: number + connector: Connector + isConnected: true + isConnecting: false + isDisconnected: false + isReconnecting: false + status: 'connected' + }>() + break + } + case 'disconnected': { + expectTypeOf(result).toMatchTypeOf<{ + address: undefined + chain: undefined + chainId: undefined + connector: undefined + isConnected: false + isReconnecting: false + isConnecting: false + isDisconnected: true + status: 'disconnected' + }>() + break + } + } +}) diff --git a/packages/react/src/hooks/useAccount.test.ts b/packages/react/src/hooks/useAccount.test.ts new file mode 100644 index 0000000000..3c4af3c3f9 --- /dev/null +++ b/packages/react/src/hooks/useAccount.test.ts @@ -0,0 +1,29 @@ +import { connect, disconnect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { Fragment, createElement } from 'react' +import { expect, test } from 'vitest' + +import { useAccount } from './useAccount.js' + +test('default', async () => { + const { result, rerender } = renderHook(() => useAccount()) + + expect(result.current.address).not.toBeDefined() + expect(result.current.status).toEqual('disconnected') + + await connect(config, { connector: config.connectors[0]! }) + rerender() + + expect(result.current.address).toBeDefined() + expect(result.current.status).toEqual('connected') + + await disconnect(config) +}) + +test('parameters: config', () => { + const { result } = renderHook(() => useAccount({ config }), { + wrapper: ({ children }) => createElement(Fragment, { children }), + }) + expect(result.current).toBeDefined() +}) diff --git a/packages/react/src/hooks/useAccount.ts b/packages/react/src/hooks/useAccount.ts new file mode 100644 index 0000000000..c7c1779469 --- /dev/null +++ b/packages/react/src/hooks/useAccount.ts @@ -0,0 +1,31 @@ +'use client' + +import { + type Config, + type GetAccountReturnType, + type ResolvedRegister, + getAccount, + watchAccount, +} from '@wagmi/core' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' +import { useSyncExternalStoreWithTracked } from './useSyncExternalStoreWithTracked.js' + +export type UseAccountParameters = + ConfigParameter + +export type UseAccountReturnType = + GetAccountReturnType + +/** https://wagmi.sh/react/api/hooks/useAccount */ +export function useAccount( + parameters: UseAccountParameters = {}, +): UseAccountReturnType { + const config = useConfig(parameters) + + return useSyncExternalStoreWithTracked( + (onChange) => watchAccount(config, { onChange }), + () => getAccount(config), + ) +} diff --git a/packages/react/src/hooks/useAccountEffect.test.ts b/packages/react/src/hooks/useAccountEffect.test.ts new file mode 100644 index 0000000000..b252ee0f98 --- /dev/null +++ b/packages/react/src/hooks/useAccountEffect.test.ts @@ -0,0 +1,77 @@ +import { mock } from '@wagmi/connectors' +import { http, connect, createConfig, disconnect } from '@wagmi/core' +import { accounts, chain, config } from '@wagmi/test' +import { createWrapper, renderHook, waitFor } from '@wagmi/test/react' +import { Fragment, createElement } from 'react' +import { expect, test, vi } from 'vitest' + +import { WagmiProvider } from '../context.js' +import { useAccountEffect } from './useAccountEffect.js' +import { useConnect } from './useConnect.js' +import { useDisconnect } from './useDisconnect.js' + +test('parameters: config', () => { + const { result } = renderHook(() => useAccountEffect({ config }), { + wrapper: ({ children }) => createElement(Fragment, { children }), + }) + expect(result.current).toBeUndefined() +}) + +test('behavior: connect and disconnect called once', async () => { + const onConnect = vi.fn() + const onDisconnect = vi.fn() + + const { result } = renderHook(() => ({ + useAccountEffect: useAccountEffect({ onConnect, onDisconnect }), + useConnect: useConnect(), + useDisconnect: useDisconnect(), + })) + + result.current.useConnect.connect({ + connector: result.current.useConnect.connectors[0]!, + }) + await waitFor(() => expect(result.current.useConnect.isSuccess).toBeTruthy()) + + result.current.useConnect.connect({ + connector: result.current.useConnect.connectors[0]!, + }) + await waitFor(() => expect(result.current.useConnect.isSuccess).toBeTruthy()) + + result.current.useDisconnect.disconnect() + await waitFor(() => + expect(result.current.useDisconnect.isSuccess).toBeTruthy(), + ) + result.current.useDisconnect.disconnect() + await waitFor(() => + expect(result.current.useDisconnect.isSuccess).toBeTruthy(), + ) + + expect(onConnect).toBeCalledTimes(1) + expect(onDisconnect).toBeCalledTimes(1) +}) + +test('behavior: connect called on reconnect', async () => { + const config = createConfig({ + chains: [chain.mainnet], + connectors: [ + mock({ + accounts, + features: { reconnect: true }, + }), + ], + transports: { [chain.mainnet.id]: http() }, + }) + + await connect(config, { connector: config.connectors[0]! }) + const onConnect = vi.fn((data) => { + expect(data.isReconnected).toBeTruthy() + }) + + renderHook(() => useAccountEffect({ onConnect }), { + wrapper: createWrapper(WagmiProvider, { config, reconnectOnMount: true }), + }) + + await waitFor(() => expect(onConnect).toBeCalledTimes(1)) + + await disconnect(config) +}) diff --git a/packages/react/src/hooks/useAccountEffect.ts b/packages/react/src/hooks/useAccountEffect.ts new file mode 100644 index 0000000000..5c77585e35 --- /dev/null +++ b/packages/react/src/hooks/useAccountEffect.ts @@ -0,0 +1,62 @@ +'use client' + +import { type GetAccountReturnType, watchAccount } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { useEffect } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UseAccountEffectParameters = Compute< + { + onConnect?( + data: Compute< + Pick< + Extract, + 'address' | 'addresses' | 'chain' | 'chainId' | 'connector' + > & { + isReconnected: boolean + } + >, + ): void + onDisconnect?(): void + } & ConfigParameter +> + +/** https://wagmi.sh/react/api/hooks/useAccountEffect */ +export function useAccountEffect(parameters: UseAccountEffectParameters = {}) { + const { onConnect, onDisconnect } = parameters + + const config = useConfig(parameters) + + useEffect(() => { + return watchAccount(config, { + onChange(data, prevData) { + if ( + (prevData.status === 'reconnecting' || + (prevData.status === 'connecting' && + prevData.address === undefined)) && + data.status === 'connected' + ) { + const { address, addresses, chain, chainId, connector } = data + const isReconnected = + prevData.status === 'reconnecting' || + // if `previousAccount.status` is `undefined`, the connector connected immediately. + prevData.status === undefined + onConnect?.({ + address, + addresses, + chain, + chainId, + connector, + isReconnected, + }) + } else if ( + prevData.status === 'connected' && + data.status === 'disconnected' + ) + onDisconnect?.() + }, + }) + }, [config, onConnect, onDisconnect]) +} diff --git a/packages/react/src/hooks/useBalance.test-d.ts b/packages/react/src/hooks/useBalance.test-d.ts new file mode 100644 index 0000000000..74630d50c7 --- /dev/null +++ b/packages/react/src/hooks/useBalance.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useBalance } from './useBalance.js' + +test('select data', () => { + const result = useBalance({ + query: { + select(data) { + return data?.value + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useBalance.test.ts b/packages/react/src/hooks/useBalance.test.ts new file mode 100644 index 0000000000..ba7f857808 --- /dev/null +++ b/packages/react/src/hooks/useBalance.test.ts @@ -0,0 +1,312 @@ +import { accounts, chain, testClient, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { type Address, parseEther } from 'viem' +import { beforeEach, expect, test } from 'vitest' + +import { useBalance } from './useBalance.js' + +const address = accounts[0] + +beforeEach(async () => { + await testClient.mainnet.setBalance({ address, value: parseEther('10000') }) + await testClient.mainnet.mine({ blocks: 1 }) + await testClient.mainnet2.setBalance({ address, value: parseEther('69') }) + await testClient.mainnet2.mine({ blocks: 1 }) +}) + +test('default', async () => { + const { result } = renderHook(() => useBalance({ address })) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toMatchObject( + expect.objectContaining({ + decimals: expect.any(Number), + formatted: expect.any(String), + symbol: expect.any(String), + value: expect.any(BigInt), + }), + ) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "balance", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useBalance({ address, chainId: chain.mainnet2.id }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "decimals": 18, + "formatted": "69", + "symbol": "WAG", + "value": 69000000000000000000n, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "balance", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 456, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: token', async () => { + const { result } = renderHook(() => + useBalance({ + address: '0x4557B18E779944BFE9d78A672452331C186a9f48', + token: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "decimals": 18, + "formatted": "0.559062564299199392", + "symbol": "DAI", + "value": 559062564299199392n, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "balance", + { + "address": "0x4557B18E779944BFE9d78A672452331C186a9f48", + "chainId": 1, + "token": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: unit', async () => { + const { result } = renderHook(() => + useBalance({ address, chainId: chain.mainnet2.id, unit: 'wei' }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "decimals": 18, + "formatted": "69000000000000000000", + "symbol": "WAG", + "value": 69000000000000000000n, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "balance", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 456, + "unit": "wei", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: address: undefined -> defined', async () => { + let address: Address | undefined = undefined + + const { result, rerender } = renderHook(() => useBalance({ address })) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "balance", + { + "address": undefined, + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + address = accounts[0] + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "decimals": 18, + "formatted": "10000", + "symbol": "ETH", + "value": 10000000000000000000000n, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "balance", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useBalance()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useBalance.ts b/packages/react/src/hooks/useBalance.ts new file mode 100644 index 0000000000..93568089f7 --- /dev/null +++ b/packages/react/src/hooks/useBalance.ts @@ -0,0 +1,54 @@ +'use client' + +import type { Config, GetBalanceErrorType, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetBalanceData, + type GetBalanceOptions, + type GetBalanceQueryKey, + getBalanceQueryOptions, +} from '@wagmi/core/query' +import type { GetBalanceQueryFnData } from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseBalanceParameters< + config extends Config = Config, + selectData = GetBalanceData, +> = Compute< + GetBalanceOptions & + ConfigParameter & + QueryParameter< + GetBalanceQueryFnData, + GetBalanceErrorType, + selectData, + GetBalanceQueryKey + > +> + +export type UseBalanceReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useBalance */ +export function useBalance< + config extends Config = ResolvedRegister['config'], + selectData = GetBalanceData, +>( + parameters: UseBalanceParameters = {}, +): UseBalanceReturnType { + const { address, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getBalanceQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useBlock.test-d.ts b/packages/react/src/hooks/useBlock.test-d.ts new file mode 100644 index 0000000000..04aae75076 --- /dev/null +++ b/packages/react/src/hooks/useBlock.test-d.ts @@ -0,0 +1,64 @@ +import { http, createConfig, webSocket } from '@wagmi/core' +import { mainnet, optimism } from '@wagmi/core/chains' +import { expectTypeOf, test } from 'vitest' + +import { useBlock } from './useBlock.js' + +test('select data', () => { + const result = useBlock({ + query: { + select(data) { + return data?.number + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + useBlock({ + config, + watch: { + poll: false, + }, + }) + + useBlock({ + config, + chainId: mainnet.id, + watch: { + poll: true, + }, + }) + useBlock({ + config, + chainId: mainnet.id, + watch: { + // @ts-expect-error + poll: false, + }, + }) + + useBlock({ + config, + chainId: optimism.id, + watch: { + poll: true, + }, + }) + useBlock({ + config, + chainId: optimism.id, + watch: { + poll: false, + }, + }) +}) diff --git a/packages/react/src/hooks/useBlock.test.ts b/packages/react/src/hooks/useBlock.test.ts new file mode 100644 index 0000000000..0d2095ec61 --- /dev/null +++ b/packages/react/src/hooks/useBlock.test.ts @@ -0,0 +1,67 @@ +import { testClient } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useBlock } from './useBlock.js' + +test('mounts', async () => { + const { result } = renderHook(() => useBlock()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeDefined() + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "block", + { + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: watch', async () => { + await testClient.mainnet.restart() + + const { result } = renderHook(() => useBlock({ watch: true })) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + const block = result.current.data! + expect(block).toBeDefined() + + await testClient.mainnet.mine({ blocks: 1 }) + await waitFor(() => { + expect(result.current.data?.number).toEqual(block.number + 1n) + }) + + await testClient.mainnet.mine({ blocks: 1 }) + await waitFor(() => { + expect(result.current.data?.number).toEqual(block.number + 2n) + }) +}) diff --git a/packages/react/src/hooks/useBlock.ts b/packages/react/src/hooks/useBlock.ts new file mode 100644 index 0000000000..1a16a5bd9e --- /dev/null +++ b/packages/react/src/hooks/useBlock.ts @@ -0,0 +1,131 @@ +'use client' + +import { useQueryClient } from '@tanstack/react-query' +import type { Config, GetBlockErrorType, ResolvedRegister } from '@wagmi/core' +import type { + Compute, + UnionCompute, + UnionStrictOmit, +} from '@wagmi/core/internal' +import { + type GetBlockData, + type GetBlockOptions, + type GetBlockQueryFnData, + type GetBlockQueryKey, + getBlockQueryOptions, +} from '@wagmi/core/query' +import type { BlockTag } from 'viem' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' +import { + type UseWatchBlocksParameters, + useWatchBlocks, +} from './useWatchBlocks.js' + +export type UseBlockParameters< + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetBlockData, +> = Compute< + GetBlockOptions & + ConfigParameter & + QueryParameter< + GetBlockQueryFnData, + GetBlockErrorType, + selectData, + GetBlockQueryKey + > & { + watch?: + | boolean + | UnionCompute< + UnionStrictOmit< + UseWatchBlocksParameters< + includeTransactions, + blockTag, + config, + chainId + >, + 'chainId' | 'config' | 'onBlock' | 'onError' + > + > + | undefined + } +> + +export type UseBlockReturnType< + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetBlockData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/hooks/useBlock */ +export function useBlock< + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetBlockData, +>( + parameters: UseBlockParameters< + includeTransactions, + blockTag, + config, + chainId, + selectData + > = {}, +): UseBlockReturnType< + includeTransactions, + blockTag, + config, + chainId, + selectData +> { + const { query = {}, watch } = parameters + + const config = useConfig(parameters) + const queryClient = useQueryClient() + const configChainId = useChainId({ config }) + const chainId = parameters.chainId ?? configChainId + + const options = getBlockQueryOptions(config, { + ...parameters, + chainId, + }) + const enabled = Boolean(query.enabled ?? true) + + useWatchBlocks({ + ...({ + config: parameters.config, + chainId: parameters.chainId!, + ...(typeof watch === 'object' ? watch : {}), + } as UseWatchBlocksParameters), + enabled: Boolean( + enabled && (typeof watch === 'object' ? watch.enabled : watch), + ), + onBlock(block) { + queryClient.setQueryData(options.queryKey, block) + }, + }) + + return useQuery({ + ...(query as any), + ...options, + enabled, + }) as UseBlockReturnType< + includeTransactions, + blockTag, + config, + chainId, + selectData + > +} diff --git a/packages/react/src/hooks/useBlockNumber.test-d.ts b/packages/react/src/hooks/useBlockNumber.test-d.ts new file mode 100644 index 0000000000..dfb4ca4dbe --- /dev/null +++ b/packages/react/src/hooks/useBlockNumber.test-d.ts @@ -0,0 +1,65 @@ +import { http, createConfig, webSocket } from '@wagmi/core' +import { mainnet, optimism } from '@wagmi/core/chains' +import { expectTypeOf, test } from 'vitest' + +import { useBlockNumber } from './useBlockNumber.js' + +test('select data', () => { + const result = useBlockNumber({ + query: { + select(data) { + expectTypeOf(data).toEqualTypeOf() + return data?.toString() + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + useBlockNumber({ + config, + watch: { + poll: false, + }, + }) + + useBlockNumber({ + config, + chainId: mainnet.id, + watch: { + poll: true, + }, + }) + useBlockNumber({ + config, + chainId: mainnet.id, + watch: { + // @ts-expect-error + poll: false, + }, + }) + + useBlockNumber({ + config, + chainId: optimism.id, + watch: { + poll: true, + }, + }) + useBlockNumber({ + config, + chainId: optimism.id, + watch: { + poll: false, + }, + }) +}) diff --git a/packages/react/src/hooks/useBlockNumber.test.ts b/packages/react/src/hooks/useBlockNumber.test.ts new file mode 100644 index 0000000000..93006a6663 --- /dev/null +++ b/packages/react/src/hooks/useBlockNumber.test.ts @@ -0,0 +1,68 @@ +import { testClient } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useBlockNumber } from './useBlockNumber.js' + +test('mounts', async () => { + await testClient.mainnet.resetFork() + + const { result } = renderHook(() => useBlockNumber()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 19258213n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "blockNumber", + { + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: watch', async () => { + await testClient.mainnet.restart() + + const { result } = renderHook(() => useBlockNumber({ watch: true })) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + const blockNumber = result.current.data! + expect(result.current.data).toMatchInlineSnapshot('19258213n') + + await testClient.mainnet.mine({ blocks: 1 }) + await waitFor(() => { + expect(result.current.data).toEqual(blockNumber + 1n) + }) + + await testClient.mainnet.mine({ blocks: 1 }) + await waitFor(() => { + expect(result.current.data).toEqual(blockNumber + 2n) + }) +}) diff --git a/packages/react/src/hooks/useBlockNumber.ts b/packages/react/src/hooks/useBlockNumber.ts new file mode 100644 index 0000000000..f91d8466e3 --- /dev/null +++ b/packages/react/src/hooks/useBlockNumber.ts @@ -0,0 +1,97 @@ +'use client' + +import { useQueryClient } from '@tanstack/react-query' +import type { + Config, + GetBlockNumberErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { + Compute, + UnionCompute, + UnionStrictOmit, +} from '@wagmi/core/internal' +import { + type GetBlockNumberData, + type GetBlockNumberOptions, + type GetBlockNumberQueryFnData, + type GetBlockNumberQueryKey, + getBlockNumberQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' +import { + type UseWatchBlockNumberParameters, + useWatchBlockNumber, +} from './useWatchBlockNumber.js' + +export type UseBlockNumberParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetBlockNumberData, +> = Compute< + GetBlockNumberOptions & + ConfigParameter & + QueryParameter< + GetBlockNumberQueryFnData, + GetBlockNumberErrorType, + selectData, + GetBlockNumberQueryKey + > & { + watch?: + | boolean + | UnionCompute< + UnionStrictOmit< + UseWatchBlockNumberParameters, + 'chainId' | 'config' | 'onBlockNumber' | 'onError' + > + > + | undefined + } +> + +export type UseBlockNumberReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useBlockNumber */ +export function useBlockNumber< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetBlockNumberData, +>( + parameters: UseBlockNumberParameters = {}, +): UseBlockNumberReturnType { + const { query = {}, watch } = parameters + + const config = useConfig(parameters) + const queryClient = useQueryClient() + const configChainId = useChainId({ config }) + const chainId = parameters.chainId ?? configChainId + + const options = getBlockNumberQueryOptions(config, { + ...parameters, + chainId, + }) + + useWatchBlockNumber({ + ...({ + config: parameters.config, + chainId: parameters.chainId, + ...(typeof watch === 'object' ? watch : {}), + } as UseWatchBlockNumberParameters), + enabled: Boolean( + (query.enabled ?? true) && + (typeof watch === 'object' ? watch.enabled : watch), + ), + onBlockNumber(blockNumber) { + queryClient.setQueryData(options.queryKey, blockNumber) + }, + }) + + return useQuery({ ...query, ...options }) +} diff --git a/packages/react/src/hooks/useBlockTransactionCount.test-d.ts b/packages/react/src/hooks/useBlockTransactionCount.test-d.ts new file mode 100644 index 0000000000..ff23fcfe95 --- /dev/null +++ b/packages/react/src/hooks/useBlockTransactionCount.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useBlockTransactionCount } from './useBlockTransactionCount.js' + +test('select data', () => { + const result = useBlockTransactionCount({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useBlockTransactionCount.test.ts b/packages/react/src/hooks/useBlockTransactionCount.test.ts new file mode 100644 index 0000000000..dad6c95506 --- /dev/null +++ b/packages/react/src/hooks/useBlockTransactionCount.test.ts @@ -0,0 +1,231 @@ +import { chain } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useBlockTransactionCount } from './useBlockTransactionCount.js' + +test('default', async () => { + const { result } = renderHook(() => useBlockTransactionCount({})) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "blockTransactionCount", + { + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useBlockTransactionCount({ chainId: chain.mainnet2.id }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "blockTransactionCount", + { + "chainId": 456, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockNumber', async () => { + const { result } = renderHook(() => + useBlockTransactionCount({ blockNumber: 13677382n }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "blockTransactionCount", + { + "blockNumber": 13677382n, + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockHash', async () => { + const { result } = renderHook(() => + useBlockTransactionCount({ + blockHash: + '0x6201f37a245850d1f11e4be3ac45bc51bd9d43ee4a127192cad550f351cfa575', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "blockTransactionCount", + { + "blockHash": "0x6201f37a245850d1f11e4be3ac45bc51bd9d43ee4a127192cad550f351cfa575", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockTag', async () => { + const { result } = renderHook(() => + useBlockTransactionCount({ + blockTag: 'safe', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "blockTransactionCount", + { + "blockTag": "safe", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useBlockTransactionCount.ts b/packages/react/src/hooks/useBlockTransactionCount.ts new file mode 100644 index 0000000000..edbd6e8ddf --- /dev/null +++ b/packages/react/src/hooks/useBlockTransactionCount.ts @@ -0,0 +1,67 @@ +'use client' + +import type { + Config, + GetBlockTransactionCountErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { UnionCompute } from '@wagmi/core/internal' +import { + type GetBlockTransactionCountData, + type GetBlockTransactionCountOptions, + type GetBlockTransactionCountQueryFnData, + type GetBlockTransactionCountQueryKey, + getBlockTransactionCountQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseBlockTransactionCountParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetBlockTransactionCountData, +> = UnionCompute< + GetBlockTransactionCountOptions & + ConfigParameter & + QueryParameter< + GetBlockTransactionCountQueryFnData, + GetBlockTransactionCountErrorType, + selectData, + GetBlockTransactionCountQueryKey + > +> + +export type UseBlockTransactionCountReturnType< + selectData = GetBlockTransactionCountData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useBlockTransactionCount */ +export function useBlockTransactionCount< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetBlockTransactionCountData, +>( + parameters: UseBlockTransactionCountParameters< + config, + chainId, + selectData + > = {}, +): UseBlockTransactionCountReturnType { + const { query = {} } = parameters + + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const chainId = parameters.chainId ?? configChainId + + const options = getBlockTransactionCountQueryOptions(config, { + ...parameters, + chainId, + }) + + return useQuery({ ...query, ...options }) +} diff --git a/packages/react/src/hooks/useBytecode.test-d.ts b/packages/react/src/hooks/useBytecode.test-d.ts new file mode 100644 index 0000000000..eff094e710 --- /dev/null +++ b/packages/react/src/hooks/useBytecode.test-d.ts @@ -0,0 +1,15 @@ +import { expectTypeOf, test } from 'vitest' + +import type { Hex } from 'viem' +import { useBytecode } from './useBytecode.js' + +test('select data', () => { + const result = useBytecode({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useBytecode.test.ts b/packages/react/src/hooks/useBytecode.test.ts new file mode 100644 index 0000000000..4c25fa9558 --- /dev/null +++ b/packages/react/src/hooks/useBytecode.test.ts @@ -0,0 +1,291 @@ +import { address, chain, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import type { Address } from 'viem' +import { useBytecode } from './useBytecode.js' + +test('default', async () => { + const { result } = renderHook(() => + useBytecode({ + address: address.wagmiMintExample, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) + expect(data).toMatch(/^0x.*/) +}) + +test('parameters: blockNumber', async () => { + const { result } = renderHook(() => + useBytecode({ + address: address.wagmiMintExample, + blockNumber: 15564163n, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": null, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "blockNumber": 15564163n, + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockTag', async () => { + const { result } = renderHook(() => + useBytecode({ + address: address.wagmiMintExample, + blockTag: 'earliest', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": null, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "blockTag": "earliest", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useBytecode({ + address: address.wagmiMintExample, + chainId: chain.optimism.id, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": null, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 10, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: address: undefined -> defined', async () => { + let contractAddress: Address | undefined = undefined + + const { result, rerender } = renderHook(() => + useBytecode({ + address: contractAddress, + }), + ) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "getBytecode", + { + "address": undefined, + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + contractAddress = address.wagmiMintExample + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getBytecode", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) + expect(data).toMatch(/^0x.*/) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useBytecode()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useBytecode.ts b/packages/react/src/hooks/useBytecode.ts new file mode 100644 index 0000000000..3dd01d438a --- /dev/null +++ b/packages/react/src/hooks/useBytecode.ts @@ -0,0 +1,57 @@ +'use client' + +import type { + Config, + GetBytecodeErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetBytecodeData, + type GetBytecodeOptions, + type GetBytecodeQueryKey, + getBytecodeQueryOptions, +} from '@wagmi/core/query' +import type { GetBytecodeQueryFnData } from '@wagmi/core/query' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseBytecodeParameters< + config extends Config = Config, + selectData = GetBytecodeData, +> = Compute< + GetBytecodeOptions & + ConfigParameter & + QueryParameter< + GetBytecodeQueryFnData, + GetBytecodeErrorType, + selectData, + GetBytecodeQueryKey + > +> + +export type UseBytecodeReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useBytecode */ +export function useBytecode< + config extends Config = ResolvedRegister['config'], + selectData = GetBytecodeData, +>( + parameters: UseBytecodeParameters = {}, +): UseBytecodeReturnType { + const { address, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getBytecodeQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useCall.test-d.ts b/packages/react/src/hooks/useCall.test-d.ts new file mode 100644 index 0000000000..5c47b096c7 --- /dev/null +++ b/packages/react/src/hooks/useCall.test-d.ts @@ -0,0 +1,14 @@ +import type { CallReturnType } from 'viem' +import { expectTypeOf, test } from 'vitest' +import { useCall } from './useCall.js' + +test('select data', () => { + const result = useCall({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useCall.test.ts b/packages/react/src/hooks/useCall.test.ts new file mode 100644 index 0000000000..ee8ce813c4 --- /dev/null +++ b/packages/react/src/hooks/useCall.test.ts @@ -0,0 +1,224 @@ +import { accounts, address, chain } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useCall } from './useCall.js' + +const name4bytes = '0x06fdde03' + +const account = accounts[0] + +test('default', async () => { + const { result } = renderHook(() => + useCall({ + account, + data: name4bytes, + to: address.wagmiMintExample, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000057761676d69000000000000000000000000000000000000000000000000000000", + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +// TODO: Re-enable +test.skip('parameters: blockTag', async () => { + const { result } = renderHook(() => + useCall({ + account, + data: name4bytes, + to: address.wagmiMintExample, + blockTag: 'safe', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000057761676d69000000000000000000000000000000000000000000000000000000", + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockTag": "safe", + "chainId": 1, + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +// TODO: Re-enable +test.skip('parameters: blockNumber', async () => { + const { result } = renderHook(() => + useCall({ + account, + data: name4bytes, + to: address.wagmiMintExample, + blockNumber: 16280770n, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000057761676d69000000000000000000000000000000000000000000000000000000", + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockNumber": 16280770n, + "chainId": 1, + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useCall({ + account, + data: name4bytes, + to: address.wagmiMintExample, + chainId: chain.mainnet2.id, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000057761676d69000000000000000000000000000000000000000000000000000000", + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "call", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 456, + "data": "0x06fdde03", + "to": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useCall.ts b/packages/react/src/hooks/useCall.ts new file mode 100644 index 0000000000..423e04fbff --- /dev/null +++ b/packages/react/src/hooks/useCall.ts @@ -0,0 +1,55 @@ +'use client' + +import type { CallErrorType, Config, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type CallData, + type CallOptions, + type CallQueryKey, + callQueryOptions, +} from '@wagmi/core/query' +import type { CallQueryFnData } from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseCallParameters< + config extends Config = Config, + selectData = CallData, +> = Compute< + CallOptions & + ConfigParameter & + QueryParameter< + CallQueryFnData, + CallErrorType, + selectData, + CallQueryKey + > +> + +export type UseCallReturnType = UseQueryReturnType< + selectData, + CallErrorType +> + +/** https://wagmi.sh/react/api/hooks/useCall */ +export function useCall< + config extends Config = ResolvedRegister['config'], + selectData = CallData, +>( + parameters: UseCallParameters = {}, +): UseCallReturnType { + const { query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = callQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + + return useQuery({ ...query, ...options }) +} diff --git a/packages/react/src/hooks/useCallsStatus.test.ts b/packages/react/src/hooks/useCallsStatus.test.ts new file mode 100644 index 0000000000..f3af1c24bc --- /dev/null +++ b/packages/react/src/hooks/useCallsStatus.test.ts @@ -0,0 +1,101 @@ +import { connect, disconnect } from '@wagmi/core' +import { accounts, config, testClient } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { useCallsStatus } from './useCallsStatus.js' +import { useSendCalls } from './useSendCalls.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useSendCalls()) + + result.current.sendCalls({ + calls: [ + { + data: '0xdeadbeef', + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { result: result_2 } = renderHook(() => + useCallsStatus({ id: result.current.data?.id! }), + ) + await waitFor(() => expect(result_2.current.isSuccess).toBeTruthy()) + + expect(result_2.current.data).toMatchInlineSnapshot( + ` + { + "atomic": false, + "chainId": 1, + "id": "0x5dedb5a4ff8968db37459b52b83cbdc92b01c9c709c9cff26e345ef5cf27f92e", + "receipts": [], + "status": "pending", + "statusCode": 100, + "version": "2.0.0", + } + `, + ) + + await testClient.mainnet.mine({ blocks: 1 }) + + const { result: result_3 } = renderHook(() => + useCallsStatus({ id: result.current.data?.id! }), + ) + await waitFor(() => expect(result_3.current.isSuccess).toBeTruthy()) + + expect(result_3.current.data?.status).toBe('success') + expect(result_3.current.data?.statusCode).toBe(200) + expect( + result_3.current.data?.receipts?.map((x) => ({ + ...x, + blockHash: undefined, + })), + ).toMatchInlineSnapshot( + ` + [ + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21064n, + "logs": [], + "status": "success", + "transactionHash": "0x13c53b2d4d9da424835525349cd66e553330f323d6fb19458b801ae1f7989a41", + }, + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21000n, + "logs": [], + "status": "success", + "transactionHash": "0xd8397b3e82b061c26a0c2093f1ceca0c3662a512614f7d6370349e89d0eea007", + }, + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21000n, + "logs": [], + "status": "success", + "transactionHash": "0x4d26e346593d9ea265bb164b115e89aa92df43b0b8778ac75d4ad28e2a22b101", + }, + ] + `, + ) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useCallsStatus.ts b/packages/react/src/hooks/useCallsStatus.ts new file mode 100644 index 0000000000..ab4c90c1f2 --- /dev/null +++ b/packages/react/src/hooks/useCallsStatus.ts @@ -0,0 +1,52 @@ +'use client' + +import type { + Config, + GetCallsStatusErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetCallsStatusData, + type GetCallsStatusOptions, + type GetCallsStatusQueryFnData, + type GetCallsStatusQueryKey, + getCallsStatusQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseCallsStatusParameters< + config extends Config = Config, + selectData = GetCallsStatusData, +> = Compute< + GetCallsStatusOptions & + ConfigParameter & + QueryParameter< + GetCallsStatusQueryFnData, + GetCallsStatusErrorType, + selectData, + GetCallsStatusQueryKey + > +> + +export type UseCallsStatusReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useCallsStatus */ +export function useCallsStatus< + config extends Config = ResolvedRegister['config'], + selectData = GetCallsStatusData, +>( + parameters: UseCallsStatusParameters, +): UseCallsStatusReturnType { + const { query = {} } = parameters + + const config = useConfig(parameters) + + const options = getCallsStatusQueryOptions(config, parameters) + + return useQuery({ ...query, ...options }) +} diff --git a/packages/react/src/hooks/useCapabilities.test.ts b/packages/react/src/hooks/useCapabilities.test.ts new file mode 100644 index 0000000000..65dcf171b5 --- /dev/null +++ b/packages/react/src/hooks/useCapabilities.test.ts @@ -0,0 +1,167 @@ +import { connect, disconnect } from '@wagmi/core' +import { accounts, config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useCapabilities } from './useCapabilities.js' + +const connector = config.connectors[0]! + +test('mounts', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useCapabilities()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "8453": { + "paymasterService": { + "supported": true, + }, + "sessionKeys": { + "supported": true, + }, + }, + "84532": { + "paymasterService": { + "supported": true, + }, + }, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "capabilities", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + }, + ], + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) + +test('args: account', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useCapabilities({ account: accounts[1] })) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "8453": { + "paymasterService": { + "supported": false, + }, + "sessionKeys": { + "supported": true, + }, + }, + "84532": { + "paymasterService": { + "supported": false, + }, + }, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "capabilities", + { + "account": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + }, + ], + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) + +test('behavior: not connected', async () => { + const { result } = renderHook(() => useCapabilities()) + + await waitFor(() => expect(result.current.isError).toBeTruthy()) + + const { error, failureReason: _, ...rest } = result.current + expect(error?.message.includes('Connector not connected.')).toBeTruthy() + expect(rest).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "errorUpdateCount": 2, + "errorUpdatedAt": 1675209600000, + "failureCount": 1, + "fetchStatus": "idle", + "isError": true, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": true, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": false, + "queryKey": [ + "capabilities", + { + "account": undefined, + }, + ], + "refetch": [Function], + "status": "error", + } + `) +}) diff --git a/packages/react/src/hooks/useCapabilities.ts b/packages/react/src/hooks/useCapabilities.ts new file mode 100644 index 0000000000..399a56cd38 --- /dev/null +++ b/packages/react/src/hooks/useCapabilities.ts @@ -0,0 +1,62 @@ +'use client' + +import type { + Config, + GetCapabilitiesErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetCapabilitiesData, + type GetCapabilitiesOptions, + type GetCapabilitiesQueryFnData, + type GetCapabilitiesQueryKey, + getCapabilitiesQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useAccount } from './useAccount.js' +import { useConfig } from './useConfig.js' + +export type UseCapabilitiesParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = GetCapabilitiesData, +> = Compute< + GetCapabilitiesOptions & + ConfigParameter & + QueryParameter< + GetCapabilitiesQueryFnData, + GetCapabilitiesErrorType, + selectData, + GetCapabilitiesQueryKey + > +> + +export type UseCapabilitiesReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = GetCapabilitiesData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useCapabilities */ +export function useCapabilities< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = GetCapabilitiesData, +>( + parameters: UseCapabilitiesParameters = {}, +): UseCapabilitiesReturnType { + const { account, query = {} } = parameters + + const { address } = useAccount() + const config = useConfig(parameters) + + const options = getCapabilitiesQueryOptions(config, { + ...parameters, + account: account ?? address, + }) + + return useQuery({ ...query, ...options }) +} diff --git a/packages/react/src/hooks/useChainId.test-d.ts b/packages/react/src/hooks/useChainId.test-d.ts new file mode 100644 index 0000000000..682b5fab7a --- /dev/null +++ b/packages/react/src/hooks/useChainId.test-d.ts @@ -0,0 +1,14 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useChainId } from './useChainId.js' + +test('default', () => { + const chainId = useChainId() + expectTypeOf(chainId).toEqualTypeOf() +}) + +test('parameters: config', () => { + const chainId = useChainId({ config }) + expectTypeOf(chainId).toEqualTypeOf<1 | 456 | 10>() +}) diff --git a/packages/react/src/hooks/useChainId.test.ts b/packages/react/src/hooks/useChainId.test.ts new file mode 100644 index 0000000000..e4745b46e3 --- /dev/null +++ b/packages/react/src/hooks/useChainId.test.ts @@ -0,0 +1,24 @@ +import { config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { Fragment, createElement } from 'react' +import { expect, test } from 'vitest' + +import { useChainId } from './useChainId.js' + +test('default', async () => { + const { result, rerender } = renderHook(() => useChainId()) + + expect(result.current).toMatchInlineSnapshot('1') + + config.setState((x) => ({ ...x, chainId: 456 })) + rerender() + + expect(result.current).toMatchInlineSnapshot('456') +}) + +test('parameters: config', () => { + const { result } = renderHook(() => useChainId({ config }), { + wrapper: ({ children }) => createElement(Fragment, { children }), + }) + expect(result.current).toBeDefined() +}) diff --git a/packages/react/src/hooks/useChainId.ts b/packages/react/src/hooks/useChainId.ts new file mode 100644 index 0000000000..ce7cb208dd --- /dev/null +++ b/packages/react/src/hooks/useChainId.ts @@ -0,0 +1,32 @@ +'use client' + +import { + type Config, + type GetChainIdReturnType, + type ResolvedRegister, + getChainId, + watchChainId, +} from '@wagmi/core' +import { useSyncExternalStore } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UseChainIdParameters = + ConfigParameter + +export type UseChainIdReturnType = + GetChainIdReturnType + +/** https://wagmi.sh/react/api/hooks/useChainId */ +export function useChainId( + parameters: UseChainIdParameters = {}, +): UseChainIdReturnType { + const config = useConfig(parameters) + + return useSyncExternalStore( + (onChange) => watchChainId(config, { onChange }), + () => getChainId(config), + () => getChainId(config), + ) +} diff --git a/packages/react/src/hooks/useChains.test.ts b/packages/react/src/hooks/useChains.test.ts new file mode 100644 index 0000000000..1d9d6fca4f --- /dev/null +++ b/packages/react/src/hooks/useChains.test.ts @@ -0,0 +1,31 @@ +import { config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { Fragment, createElement } from 'react' +import { expect, test } from 'vitest' + +import { useChains } from './useChains.js' + +test('default', async () => { + const { result } = renderHook(() => useChains()) + + expect(result.current.map((x) => x.id)).toMatchInlineSnapshot(` + [ + 1, + 456, + 10, + ] + `) +}) + +test('parameters: config', () => { + const { result } = renderHook(() => useChains({ config }), { + wrapper: ({ children }) => createElement(Fragment, { children }), + }) + expect(result.current.map((x) => x.id)).toMatchInlineSnapshot(` + [ + 1, + 456, + 10, + ] + `) +}) diff --git a/packages/react/src/hooks/useChains.ts b/packages/react/src/hooks/useChains.ts new file mode 100644 index 0000000000..b3e93b417a --- /dev/null +++ b/packages/react/src/hooks/useChains.ts @@ -0,0 +1,32 @@ +'use client' + +import { + type Config, + type GetChainsReturnType, + type ResolvedRegister, + getChains, +} from '@wagmi/core' +import { watchChains } from '@wagmi/core/internal' +import { useSyncExternalStore } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UseChainsParameters = + ConfigParameter + +export type UseChainsReturnType = + GetChainsReturnType + +/** https://wagmi.sh/react/api/hooks/useChains */ +export function useChains( + parameters: UseChainsParameters = {}, +): UseChainsReturnType { + const config = useConfig(parameters) + + return useSyncExternalStore( + (onChange) => watchChains(config, { onChange }), + () => getChains(config), + () => getChains(config), + ) +} diff --git a/packages/react/src/hooks/useClient.test-d.ts b/packages/react/src/hooks/useClient.test-d.ts new file mode 100644 index 0000000000..80ee4ec48d --- /dev/null +++ b/packages/react/src/hooks/useClient.test-d.ts @@ -0,0 +1,40 @@ +import { chain, config } from '@wagmi/test' +import type { Chain } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { useClient } from './useClient.js' + +test('default', () => { + const client = useClient({ config }) + expectTypeOf(client.chain).toEqualTypeOf<(typeof config)['chains'][number]>() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = useClient({ + config, + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('behavior: unconfigured chain', () => { + { + const client = useClient({ chainId: 123456 }) + if (client) { + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf() + } else { + expectTypeOf(client).toEqualTypeOf() + } + } + + const client = useClient({ + config, + // @ts-expect-error + chainId: 123456, + }) + expectTypeOf(client).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useClient.test.ts b/packages/react/src/hooks/useClient.test.ts new file mode 100644 index 0000000000..93ef8d9fa9 --- /dev/null +++ b/packages/react/src/hooks/useClient.test.ts @@ -0,0 +1,30 @@ +import { switchChain } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { Fragment, createElement } from 'react' +import { expect, test } from 'vitest' + +import { useClient } from './useClient.js' + +test('default', async () => { + const { result, rerender } = renderHook(() => useClient()) + + expect(result.current?.chain.id).toEqual(1) + + await switchChain(config, { chainId: 456 }) + rerender() + + expect(result.current?.chain.id).toEqual(456) +}) + +test('parameters: config', () => { + const { result } = renderHook(() => useClient({ config }), { + wrapper: ({ children }) => createElement(Fragment, { children }), + }) + expect(result.current).toBeDefined() +}) + +test('behavior: unconfigured chain', () => { + const { result } = renderHook(() => useClient({ chainId: 123456 })) + expect(result.current).toBeUndefined() +}) diff --git a/packages/react/src/hooks/useClient.ts b/packages/react/src/hooks/useClient.ts new file mode 100644 index 0000000000..abfee745b3 --- /dev/null +++ b/packages/react/src/hooks/useClient.ts @@ -0,0 +1,49 @@ +'use client' + +import { + type Config, + type GetClientParameters, + type GetClientReturnType, + type ResolvedRegister, + getClient, + watchClient, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector.js' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UseClientParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | number | undefined = + | config['chains'][number]['id'] + | undefined, +> = Compute & ConfigParameter> + +export type UseClientReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | number | undefined = + | config['chains'][number]['id'] + | undefined, +> = GetClientReturnType + +/** https://wagmi.sh/react/api/hooks/useClient */ +export function useClient< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] | number | undefined = + | config['chains'][number]['id'] + | undefined, +>( + parameters: UseClientParameters = {}, +): UseClientReturnType { + const config = useConfig(parameters) + + return useSyncExternalStoreWithSelector( + (onChange) => watchClient(config, { onChange }), + () => getClient(config, parameters), + () => getClient(config, parameters), + (x) => x, + (a, b) => a?.uid === b?.uid, + ) as any +} diff --git a/packages/react/src/hooks/useConfig.test-d.ts b/packages/react/src/hooks/useConfig.test-d.ts new file mode 100644 index 0000000000..f2b9d2bb31 --- /dev/null +++ b/packages/react/src/hooks/useConfig.test-d.ts @@ -0,0 +1,16 @@ +import type { Config } from '@wagmi/core' +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useConfig } from './useConfig.js' + +test('default', async () => { + const result = useConfig() + expectTypeOf(result).toEqualTypeOf() +}) + +test('parameters: config', async () => { + const result = useConfig({ config }) + expectTypeOf(result).not.toEqualTypeOf() + expectTypeOf(result).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useConfig.test.ts b/packages/react/src/hooks/useConfig.test.ts new file mode 100644 index 0000000000..e0fc9c40ce --- /dev/null +++ b/packages/react/src/hooks/useConfig.test.ts @@ -0,0 +1,23 @@ +import { createWrapper, renderHook } from '@wagmi/test/react' +import { expect, test, vi } from 'vitest' + +import { useConfig } from './useConfig.js' + +test('mounts', () => { + const { result } = renderHook(() => useConfig()) + expect(result.current).toBeDefined() +}) + +test('behavior: throws when not inside Provider', () => { + vi.spyOn(console, 'error').mockImplementation(() => {}) + + try { + renderHook(() => useConfig(), { + wrapper: createWrapper(() => null, undefined), + }) + } catch (error) { + expect(error).toMatchInlineSnapshot( + '[Error: `useConfig` must be used within `WagmiProvider`.]', + ) + } +}) diff --git a/packages/react/src/hooks/useConfig.ts b/packages/react/src/hooks/useConfig.ts new file mode 100644 index 0000000000..af9ea5b9c0 --- /dev/null +++ b/packages/react/src/hooks/useConfig.ts @@ -0,0 +1,22 @@ +'use client' + +import type { Config, ResolvedRegister } from '@wagmi/core' +import { useContext } from 'react' + +import { WagmiContext } from '../context.js' +import { WagmiProviderNotFoundError } from '../errors/context.js' +import type { ConfigParameter } from '../types/properties.js' + +export type UseConfigParameters = + ConfigParameter + +export type UseConfigReturnType = config + +/** https://wagmi.sh/react/api/hooks/useConfig */ +export function useConfig( + parameters: UseConfigParameters = {}, +): UseConfigReturnType { + const config = parameters.config ?? useContext(WagmiContext) + if (!config) throw new WagmiProviderNotFoundError() + return config as UseConfigReturnType +} diff --git a/packages/react/src/hooks/useConnect.test-d.ts b/packages/react/src/hooks/useConnect.test-d.ts new file mode 100644 index 0000000000..e678d2edd8 --- /dev/null +++ b/packages/react/src/hooks/useConnect.test-d.ts @@ -0,0 +1,124 @@ +import type { + ConnectErrorType, + Connector, + CreateConnectorFn, +} from '@wagmi/core' +import { config } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { useConnect } from './useConnect.js' + +const connector = config.connectors[0]! +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { connect, context, data, error, variables } = useConnect({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toEqualTypeOf<{ + chainId?: number | undefined + connector: Connector | CreateConnectorFn + }>() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ + chainId?: number | undefined + connector: Connector | CreateConnectorFn + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ + chainId?: number | undefined + connector: Connector | CreateConnectorFn + }>() + expectTypeOf(data).toEqualTypeOf<{ + accounts: readonly [Address, ...Address[]] + chainId: number + }>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + } + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf<{ + chainId?: number | undefined + connector: Connector | CreateConnectorFn + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + } + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf< + | { + chainId?: number | undefined + connector: CreateConnectorFn | Connector + } + | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + connect( + { + connector, + foo: 'bar', + }, + { + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ + chainId?: number | undefined + connector: typeof connector | CreateConnectorFn + foo?: string | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ + chainId?: number | undefined + connector: typeof connector | CreateConnectorFn + foo?: string | undefined + }>() + expectTypeOf(data).toEqualTypeOf<{ + accounts: readonly [Address, ...Address[]] + chainId: number + }>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + } + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf<{ + chainId?: number | undefined + connector: typeof connector | CreateConnectorFn + foo?: string | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useConnect.test.ts b/packages/react/src/hooks/useConnect.test.ts new file mode 100644 index 0000000000..d80ff30509 --- /dev/null +++ b/packages/react/src/hooks/useConnect.test.ts @@ -0,0 +1,35 @@ +import { disconnect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { afterEach, expect, test } from 'vitest' + +import { useAccount } from './useAccount.js' +import { useConnect } from './useConnect.js' + +const connector = config.connectors[0]! + +afterEach(async () => { + if (config.state.current === connector.uid) + await disconnect(config, { connector }) +}) + +test('default', async () => { + const { result } = renderHook(() => ({ + useAccount: useAccount(), + useConnect: useConnect(), + })) + + expect(result.current.useAccount.address).not.toBeDefined() + expect(result.current.useAccount.status).toEqual('disconnected') + + result.current.useConnect.connect({ + connector: result.current.useConnect.connectors[0]!, + }) + + await waitFor(() => + expect(result.current.useAccount.isConnected).toBeTruthy(), + ) + + expect(result.current.useAccount.address).toBeDefined() + expect(result.current.useAccount.status).toEqual('connected') +}) diff --git a/packages/react/src/hooks/useConnect.ts b/packages/react/src/hooks/useConnect.ts new file mode 100644 index 0000000000..acfe69234d --- /dev/null +++ b/packages/react/src/hooks/useConnect.ts @@ -0,0 +1,90 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { Config, ConnectErrorType, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type ConnectData, + type ConnectMutate, + type ConnectMutateAsync, + type ConnectVariables, + connectMutationOptions, +} from '@wagmi/core/query' +import { useEffect } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' +import { type UseConnectorsReturnType, useConnectors } from './useConnectors.js' + +export type UseConnectParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + ConnectData, + ConnectErrorType, + ConnectVariables, + context + > + | undefined + } +> + +export type UseConnectReturnType< + config extends Config = Config, + context = unknown, +> = Compute< + UseMutationReturnType< + ConnectData, + ConnectErrorType, + ConnectVariables, + context + > & { + connect: ConnectMutate + connectAsync: ConnectMutateAsync + connectors: Compute | config['connectors'] + } +> + +/** https://wagmi.sh/react/api/hooks/useConnect */ +export function useConnect< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseConnectParameters = {}, +): UseConnectReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = connectMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + // Reset mutation back to an idle state when the connector disconnects. + useEffect(() => { + return config.subscribe( + ({ status }) => status, + (status, previousStatus) => { + if (previousStatus === 'connected' && status === 'disconnected') + result.reset() + }, + ) + }, [config, result.reset]) + + type Return = UseConnectReturnType + return { + ...(result as Return), + connect: mutate as Return['connect'], + connectAsync: mutateAsync as Return['connectAsync'], + connectors: useConnectors({ config }), + } +} diff --git a/packages/react/src/hooks/useConnections.test.ts b/packages/react/src/hooks/useConnections.test.ts new file mode 100644 index 0000000000..1eb4febcdb --- /dev/null +++ b/packages/react/src/hooks/useConnections.test.ts @@ -0,0 +1,25 @@ +import { connect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { Fragment, createElement } from 'react' +import { expect, test } from 'vitest' + +import { useConnections } from './useConnections.js' + +test('default', async () => { + const { result, rerender } = renderHook(() => useConnections()) + + expect(result.current).toEqual([]) + + await connect(config, { connector: config.connectors[0]! }) + rerender() + + expect(result.current.length).toBe(1) +}) + +test('parameters: config', () => { + const { result } = renderHook(() => useConnections({ config }), { + wrapper: ({ children }) => createElement(Fragment, { children }), + }) + expect(result.current).toBeDefined() +}) diff --git a/packages/react/src/hooks/useConnections.ts b/packages/react/src/hooks/useConnections.ts new file mode 100644 index 0000000000..db53db7227 --- /dev/null +++ b/packages/react/src/hooks/useConnections.ts @@ -0,0 +1,28 @@ +'use client' + +import { + type GetConnectionsReturnType, + getConnections, + watchConnections, +} from '@wagmi/core' +import { useSyncExternalStore } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UseConnectionsParameters = ConfigParameter + +export type UseConnectionsReturnType = GetConnectionsReturnType + +/** https://wagmi.sh/react/api/hooks/useConnections */ +export function useConnections( + parameters: UseConnectionsParameters = {}, +): UseConnectionsReturnType { + const config = useConfig(parameters) + + return useSyncExternalStore( + (onChange) => watchConnections(config, { onChange }), + () => getConnections(config), + () => getConnections(config), + ) +} diff --git a/packages/react/src/hooks/useConnectorClient.test-d.ts b/packages/react/src/hooks/useConnectorClient.test-d.ts new file mode 100644 index 0000000000..b919e6a904 --- /dev/null +++ b/packages/react/src/hooks/useConnectorClient.test-d.ts @@ -0,0 +1,12 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useConnectorClient } from './useConnectorClient.js' + +test('parameters: config', async () => { + const client = useConnectorClient({ config }) + expectTypeOf(client.data?.chain?.id!).toEqualTypeOf<1 | 456 | 10>() + + const client2 = useConnectorClient({ config, chainId: 1 }) + expectTypeOf(client2.data?.chain?.id!).toEqualTypeOf<1>() +}) diff --git a/packages/react/src/hooks/useConnectorClient.test.tsx b/packages/react/src/hooks/useConnectorClient.test.tsx new file mode 100644 index 0000000000..f8737c53ec --- /dev/null +++ b/packages/react/src/hooks/useConnectorClient.test.tsx @@ -0,0 +1,239 @@ +import { connect, disconnect } from '@wagmi/core' +import { config, wait } from '@wagmi/test' +import { render, renderHook, waitFor } from '@wagmi/test/react' +import * as React from 'react' +import { expect, test } from 'vitest' + +import { useAccount } from './useAccount.js' +import { useConnect } from './useConnect.js' +import { useConnectorClient } from './useConnectorClient.js' +import { useDisconnect } from './useDisconnect.js' +import { useSwitchChain } from './useSwitchChain.js' + +const connector = config.connectors[0]! + +test('default', async () => { + const { result } = renderHook(() => useConnectorClient()) + + await waitFor(() => expect(result.current.isPending).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "connectorClient", + { + "chainId": 1, + "connectorUid": undefined, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) +}) + +test('behavior: connected on mount', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useConnectorClient()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, queryKey: _, ...rest } = result.current + expect(data).toMatchObject( + expect.objectContaining({ + account: expect.any(Object), + chain: expect.any(Object), + }), + ) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": true, + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) + +test('behavior: connect and disconnect', async () => { + const { result } = renderHook(() => ({ + useConnect: useConnect(), + useConnectorClient: useConnectorClient(), + useDisconnect: useDisconnect(), + })) + + expect(result.current.useConnectorClient.data).not.toBeDefined() + + result.current.useConnect.connect({ + connector: result.current.useConnect.connectors[0]!, + }) + + await waitFor(() => + expect(result.current.useConnectorClient.data).toBeDefined(), + ) + + result.current.useDisconnect.disconnect() + + await waitFor(() => + expect(result.current.useConnectorClient.data).not.toBeDefined(), + ) +}) + +test('behavior: switch chains', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => ({ + useConnectorClient: useConnectorClient(), + useSwitchChain: useSwitchChain(), + })) + + expect(result.current.useConnectorClient.data).not.toBeDefined() + + await waitFor(() => + expect(result.current.useConnectorClient.data).toBeDefined(), + ) + + result.current.useSwitchChain.switchChain({ chainId: 456 }) + await waitFor(() => { + expect(result.current.useSwitchChain.isSuccess).toBeTruthy() + result.current.useSwitchChain.reset() + }) + expect(result.current.useConnectorClient.data?.chain.id).toEqual(456) + + result.current.useSwitchChain.switchChain({ chainId: 1 }) + await waitFor(() => + expect(result.current.useSwitchChain.isSuccess).toBeTruthy(), + ) + expect(result.current.useConnectorClient.data?.chain.id).toEqual(1) + + await disconnect(config, { connector }) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useConnectorClient()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) + +test('behavior: disabled when connecting', async () => { + const { result } = renderHook(() => useConnectorClient()) + + config.setState((x) => ({ ...x, status: 'connecting' })) + + await wait(100) + expect(result.current.isLoading).not.toBeTruthy() +}) + +test('behavior: re-render does not invalidate query', async () => { + const { getByTestId } = render() + + getByTestId('connect').click() + await waitFor(() => { + expect(getByTestId('address').innerText).toContain( + '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + ) + expect(getByTestId('client').innerText).toBeTruthy() + + expect(getByTestId('child-client').innerText).toBeTruthy() + expect(getByTestId('render-count').innerText).toEqual('1') + }) + + const initialClient = getByTestId('child-client').innerText + + getByTestId('rerender').click() + await waitFor(() => { + expect(getByTestId('render-count').innerText).toEqual('2') + }) + await wait(200) + + expect(getByTestId('child-client').innerText).toEqual(initialClient) +}) + +function Parent() { + const [renderCount, setRenderCount] = React.useState(1) + + const { connectors, connect } = useConnect() + const { address } = useAccount() + const { data } = useConnectorClient() + + return ( + <> +
{address}
+
{data?.uid}
+ + + + + + ) +} + +function Child(props: { + renderCount: number +}) { + const { renderCount } = props + const { data } = useConnectorClient() + return ( +
+ {data?.uid} + {renderCount} +
+ ) +} diff --git a/packages/react/src/hooks/useConnectorClient.ts b/packages/react/src/hooks/useConnectorClient.ts new file mode 100644 index 0000000000..16bbf33ae0 --- /dev/null +++ b/packages/react/src/hooks/useConnectorClient.ts @@ -0,0 +1,113 @@ +'use client' + +import { useQueryClient } from '@tanstack/react-query' +import type { + Config, + GetConnectorClientErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute, Omit } from '@wagmi/core/internal' +import { + type GetConnectorClientData, + type GetConnectorClientOptions, + type GetConnectorClientQueryFnData, + type GetConnectorClientQueryKey, + getConnectorClientQueryOptions, +} from '@wagmi/core/query' +import { useEffect, useRef } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import { + type UseQueryParameters, + type UseQueryReturnType, + useQuery, +} from '../utils/query.js' +import { useAccount } from './useAccount.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseConnectorClientParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetConnectorClientData, +> = Compute< + GetConnectorClientOptions & + ConfigParameter & { + query?: + | Compute< + Omit< + UseQueryParameters< + GetConnectorClientQueryFnData, + GetConnectorClientErrorType, + selectData, + GetConnectorClientQueryKey + >, + 'gcTime' | 'staleTime' + > + > + | undefined + } +> + +export type UseConnectorClientReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetConnectorClientData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useConnectorClient */ +export function useConnectorClient< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetConnectorClientData, +>( + parameters: UseConnectorClientParameters = {}, +): UseConnectorClientReturnType { + const { query = {}, ...rest } = parameters + + const config = useConfig(rest) + const queryClient = useQueryClient() + const { address, connector, status } = useAccount({ config }) + const chainId = useChainId({ config }) + const activeConnector = parameters.connector ?? connector + + const { queryKey, ...options } = getConnectorClientQueryOptions< + config, + chainId + >(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + connector: activeConnector, + }) + const enabled = Boolean( + (status === 'connected' || + (status === 'reconnecting' && activeConnector?.getProvider)) && + (query.enabled ?? true), + ) + + const addressRef = useRef(address) + // biome-ignore lint/correctness/useExhaustiveDependencies: `queryKey` not required + useEffect(() => { + const previousAddress = addressRef.current + if (!address && previousAddress) { + // remove when account is disconnected + queryClient.removeQueries({ queryKey }) + addressRef.current = undefined + } else if (address !== previousAddress) { + // invalidate when address changes + queryClient.invalidateQueries({ queryKey }) + addressRef.current = address + } + }, [address, queryClient]) + + return useQuery({ + ...query, + ...options, + queryKey, + enabled, + staleTime: Number.POSITIVE_INFINITY, + }) +} diff --git a/packages/react/src/hooks/useConnectors.test.ts b/packages/react/src/hooks/useConnectors.test.ts new file mode 100644 index 0000000000..f287cceb32 --- /dev/null +++ b/packages/react/src/hooks/useConnectors.test.ts @@ -0,0 +1,30 @@ +import { mock } from '@wagmi/connectors' +import { accounts, config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { Fragment, createElement } from 'react' +import { expect, test } from 'vitest' + +import { useConnectors } from './useConnectors.js' + +test('default', async () => { + const { result, rerender } = renderHook(() => useConnectors()) + + const count = config.connectors.length + expect(result.current.length).toBe(count) + expect(result.current).toEqual(config.connectors) + + config._internal.connectors.setState(() => [ + ...config.connectors, + config._internal.connectors.setup(mock({ accounts })), + ]) + rerender() + + expect(result.current.length).toBe(count + 1) +}) + +test('parameters: config', () => { + const { result } = renderHook(() => useConnectors({ config }), { + wrapper: ({ children }) => createElement(Fragment, { children }), + }) + expect(result.current).toBeDefined() +}) diff --git a/packages/react/src/hooks/useConnectors.ts b/packages/react/src/hooks/useConnectors.ts new file mode 100644 index 0000000000..40a681689d --- /dev/null +++ b/packages/react/src/hooks/useConnectors.ts @@ -0,0 +1,34 @@ +'use client' + +import { + type Config, + type GetConnectorsReturnType, + type ResolvedRegister, + getConnectors, + watchConnectors, +} from '@wagmi/core' +import { useSyncExternalStore } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UseConnectorsParameters = + ConfigParameter + +export type UseConnectorsReturnType = + GetConnectorsReturnType + +/** https://wagmi.sh/react/api/hooks/useConnectors */ +export function useConnectors< + config extends Config = ResolvedRegister['config'], +>( + parameters: UseConnectorsParameters = {}, +): UseConnectorsReturnType { + const config = useConfig(parameters) + + return useSyncExternalStore( + (onChange) => watchConnectors(config, { onChange }), + () => getConnectors(config), + () => getConnectors(config), + ) +} diff --git a/packages/react/src/hooks/useDeployContract.test-d.ts b/packages/react/src/hooks/useDeployContract.test-d.ts new file mode 100644 index 0000000000..93e53c472f --- /dev/null +++ b/packages/react/src/hooks/useDeployContract.test-d.ts @@ -0,0 +1,105 @@ +import type { DeployContractErrorType } from '@wagmi/core' +import { abi, bytecode } from '@wagmi/test' +import type { Abi, Hash } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { useDeployContract } from './useDeployContract.js' + +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, deployContract, variables } = useDeployContract( + { + mutation: { + onMutate(variables) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: Abi + args?: readonly unknown[] | undefined + }>() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: Abi + args?: readonly unknown[] | undefined + }>() + }, + onSuccess(data, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: Abi + args?: readonly unknown[] | undefined + }>() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: Abi + args?: readonly unknown[] | undefined + }>() + }, + }, + }, + ) + + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf< + { chainId?: number | undefined } | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + deployContract( + { + abi: abi.bayc, + bytecode: bytecode.bayc, + args: ['Bored Ape Wagmi Club', 'BAYC', 69420n, 0n], + chainId: 1, + }, + { + onError(error, variables, context) { + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: typeof abi.bayc + args: readonly [string, string, bigint, bigint] + }>() + }, + onSuccess(data, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: typeof abi.bayc + args: readonly [string, string, bigint, bigint] + }>() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: typeof abi.bayc + args: readonly [string, string, bigint, bigint] + }>() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useDeployContract.test.ts b/packages/react/src/hooks/useDeployContract.test.ts new file mode 100644 index 0000000000..561129bdbc --- /dev/null +++ b/packages/react/src/hooks/useDeployContract.test.ts @@ -0,0 +1,24 @@ +import { connect, disconnect } from '@wagmi/core' +import { abi, bytecode, config, transactionHashRegex } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useDeployContract } from './useDeployContract.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + const { result } = renderHook(() => useDeployContract()) + + result.current.deployContract({ + abi: abi.bayc, + bytecode: bytecode.bayc, + args: ['Bored Ape Wagmi Club', 'BAYC', 69420n, 0n], + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatch(transactionHashRegex) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useDeployContract.ts b/packages/react/src/hooks/useDeployContract.ts new file mode 100644 index 0000000000..d37f3206d4 --- /dev/null +++ b/packages/react/src/hooks/useDeployContract.ts @@ -0,0 +1,78 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { + Config, + DeployContractErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type DeployContractData, + type DeployContractMutate, + type DeployContractMutateAsync, + type DeployContractVariables, + deployContractMutationOptions, +} from '@wagmi/core/query' +import type { Abi } from 'viem' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseDeployContractParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + DeployContractData, + DeployContractErrorType, + DeployContractVariables, + context + > + | undefined + } +> + +export type UseDeployContractReturnType< + config extends Config = Config, + context = unknown, +> = UseMutationReturnType< + DeployContractData, + DeployContractErrorType, + DeployContractVariables, + context +> & { + deployContract: DeployContractMutate + deployContractAsync: DeployContractMutateAsync +} + +/** https://wagmi.sh/react/api/hooks/useDeployContract */ +export function useDeployContract< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseDeployContractParameters = {}, +): UseDeployContractReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = deployContractMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseDeployContractReturnType + return { + ...result, + deployContract: mutate as Return['deployContract'], + deployContractAsync: mutateAsync as Return['deployContractAsync'], + } +} diff --git a/packages/react/src/hooks/useDisconnect.test-d.ts b/packages/react/src/hooks/useDisconnect.test-d.ts new file mode 100644 index 0000000000..7bf3689450 --- /dev/null +++ b/packages/react/src/hooks/useDisconnect.test-d.ts @@ -0,0 +1,87 @@ +import type { Connector, DisconnectErrorType } from '@wagmi/core' +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useDisconnect } from './useDisconnect.js' + +const connector = config.connectors[0]! +const contextValue = { foo: 'bar' } as const + +test('parameter', () => { + expectTypeOf(useDisconnect().disconnect) + .parameter(0) + .toEqualTypeOf<{ connector?: Connector | undefined } | undefined>() + expectTypeOf(useDisconnect().disconnect) + .parameter(0) + .toEqualTypeOf<{ connector?: Connector | undefined } | undefined>() +}) + +test('context', () => { + const { context, data, disconnect, error, variables } = useDisconnect({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toEqualTypeOf< + { connector?: Connector | undefined } | undefined + >() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf< + { connector?: Connector | undefined } | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf< + { connector?: Connector | undefined } | undefined + >() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf< + { connector?: Connector | undefined } | undefined + >() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf< + { connector?: Connector | undefined } | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + disconnect( + { connector }, + { + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf< + { connector?: Connector | undefined } | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf< + { connector?: Connector | undefined } | undefined + >() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf< + { connector?: Connector | undefined } | undefined + >() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useDisconnect.test.ts b/packages/react/src/hooks/useDisconnect.test.ts new file mode 100644 index 0000000000..26c7787d98 --- /dev/null +++ b/packages/react/src/hooks/useDisconnect.test.ts @@ -0,0 +1,32 @@ +import { connect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { beforeEach, expect, test } from 'vitest' + +import { useAccount } from './useAccount.js' +import { useDisconnect } from './useDisconnect.js' + +const connector = config.connectors[0]! + +beforeEach(async () => { + await connect(config, { connector }) +}) + +test('default', async () => { + const { result } = renderHook(() => ({ + useAccount: useAccount(), + useDisconnect: useDisconnect(), + })) + + expect(result.current.useAccount.address).toBeDefined() + expect(result.current.useAccount.status).toEqual('connected') + + result.current.useDisconnect.disconnect() + + await waitFor(() => + expect(result.current.useAccount.isDisconnected).toBeTruthy(), + ) + + expect(result.current.useAccount.address).not.toBeDefined() + expect(result.current.useAccount.status).toEqual('disconnected') +}) diff --git a/packages/react/src/hooks/useDisconnect.ts b/packages/react/src/hooks/useDisconnect.ts new file mode 100644 index 0000000000..b5bff5bfe3 --- /dev/null +++ b/packages/react/src/hooks/useDisconnect.ts @@ -0,0 +1,70 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { Connector, DisconnectErrorType } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type DisconnectData, + type DisconnectMutate, + type DisconnectMutateAsync, + type DisconnectVariables, + disconnectMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' +import { useConnections } from './useConnections.js' + +export type UseDisconnectParameters = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + DisconnectData, + DisconnectErrorType, + DisconnectVariables, + context + > + | undefined + } +> + +export type UseDisconnectReturnType = Compute< + UseMutationReturnType< + DisconnectData, + DisconnectErrorType, + DisconnectVariables, + context + > & { + connectors: readonly Connector[] + disconnect: DisconnectMutate + disconnectAsync: DisconnectMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useDisconnect */ +export function useDisconnect( + parameters: UseDisconnectParameters = {}, +): UseDisconnectReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = disconnectMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + return { + ...result, + connectors: useConnections({ config }).map( + (connection) => connection.connector, + ), + disconnect: mutate, + disconnectAsync: mutateAsync, + } +} diff --git a/packages/react/src/hooks/useEnsAddress.test.ts b/packages/react/src/hooks/useEnsAddress.test.ts new file mode 100644 index 0000000000..ff73c45f9d --- /dev/null +++ b/packages/react/src/hooks/useEnsAddress.test.ts @@ -0,0 +1,50 @@ +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useEnsAddress } from './useEnsAddress.js' + +test('default', async () => { + const { result } = renderHook(() => + useEnsAddress({ + name: 'wevm.eth', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "0xd2135CfB216b74109775236E36d4b433F1DF507B", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "ensAddress", + { + "chainId": 1, + "name": "wevm.eth", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useEnsAddress.ts b/packages/react/src/hooks/useEnsAddress.ts new file mode 100644 index 0000000000..999e07d999 --- /dev/null +++ b/packages/react/src/hooks/useEnsAddress.ts @@ -0,0 +1,58 @@ +'use client' + +import type { + Config, + GetEnsAddressErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetEnsAddressData, + type GetEnsAddressOptions, + type GetEnsAddressQueryFnData, + type GetEnsAddressQueryKey, + getEnsAddressQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseEnsAddressParameters< + config extends Config = Config, + selectData = GetEnsAddressData, +> = Compute< + GetEnsAddressOptions & + ConfigParameter & + QueryParameter< + GetEnsAddressQueryFnData, + GetEnsAddressErrorType, + selectData, + GetEnsAddressQueryKey + > +> + +export type UseEnsAddressReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useEnsAddress */ +export function useEnsAddress< + config extends Config = ResolvedRegister['config'], + selectData = GetEnsAddressData, +>( + parameters: UseEnsAddressParameters = {}, +): UseEnsAddressReturnType { + const { name, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getEnsAddressQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsAvatar.test.ts b/packages/react/src/hooks/useEnsAvatar.test.ts new file mode 100644 index 0000000000..d1f558be13 --- /dev/null +++ b/packages/react/src/hooks/useEnsAvatar.test.ts @@ -0,0 +1,50 @@ +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useEnsAvatar } from './useEnsAvatar.js' + +test('default', async () => { + const { result } = renderHook(() => + useEnsAvatar({ + name: 'wevm.eth', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "https://euc.li/wevm.eth", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "ensAvatar", + { + "chainId": 1, + "name": "wevm.eth", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useEnsAvatar.ts b/packages/react/src/hooks/useEnsAvatar.ts new file mode 100644 index 0000000000..721653bf07 --- /dev/null +++ b/packages/react/src/hooks/useEnsAvatar.ts @@ -0,0 +1,58 @@ +'use client' + +import type { + Config, + GetEnsAvatarErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetEnsAvatarData, + type GetEnsAvatarOptions, + type GetEnsAvatarQueryFnData, + type GetEnsAvatarQueryKey, + getEnsAvatarQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseEnsAvatarParameters< + config extends Config = Config, + selectData = GetEnsAvatarData, +> = Compute< + GetEnsAvatarOptions & + ConfigParameter & + QueryParameter< + GetEnsAvatarQueryFnData, + GetEnsAvatarErrorType, + selectData, + GetEnsAvatarQueryKey + > +> + +export type UseEnsAvatarReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useEnsAvatar */ +export function useEnsAvatar< + config extends Config = ResolvedRegister['config'], + selectData = GetEnsAvatarData, +>( + parameters: UseEnsAvatarParameters = {}, +): UseEnsAvatarReturnType { + const { name, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getEnsAvatarQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsName.test.ts b/packages/react/src/hooks/useEnsName.test.ts new file mode 100644 index 0000000000..8a069b076d --- /dev/null +++ b/packages/react/src/hooks/useEnsName.test.ts @@ -0,0 +1,50 @@ +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useEnsName } from './useEnsName.js' + +test('default', async () => { + const { result } = renderHook(() => + useEnsName({ + address: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "wevm.eth", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "ensName", + { + "address": "0xd2135CfB216b74109775236E36d4b433F1DF507B", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useEnsName.ts b/packages/react/src/hooks/useEnsName.ts new file mode 100644 index 0000000000..ddf88fed9d --- /dev/null +++ b/packages/react/src/hooks/useEnsName.ts @@ -0,0 +1,54 @@ +'use client' + +import type { Config, GetEnsNameErrorType, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetEnsNameData, + type GetEnsNameOptions, + type GetEnsNameQueryFnData, + type GetEnsNameQueryKey, + getEnsNameQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseEnsNameParameters< + config extends Config = Config, + selectData = GetEnsNameData, +> = Compute< + GetEnsNameOptions & + ConfigParameter & + QueryParameter< + GetEnsNameQueryFnData, + GetEnsNameErrorType, + selectData, + GetEnsNameQueryKey + > +> + +export type UseEnsNameReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useEnsName */ +export function useEnsName< + config extends Config = ResolvedRegister['config'], + selectData = GetEnsNameData, +>( + parameters: UseEnsNameParameters = {}, +): UseEnsNameReturnType { + const { address, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getEnsNameQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsResolver.test.ts b/packages/react/src/hooks/useEnsResolver.test.ts new file mode 100644 index 0000000000..f5b89d8f63 --- /dev/null +++ b/packages/react/src/hooks/useEnsResolver.test.ts @@ -0,0 +1,50 @@ +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useEnsResolver } from './useEnsResolver.js' + +test('default', async () => { + const { result } = renderHook(() => + useEnsResolver({ + name: 'wevm.eth', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "ensResolver", + { + "chainId": 1, + "name": "wevm.eth", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useEnsResolver.ts b/packages/react/src/hooks/useEnsResolver.ts new file mode 100644 index 0000000000..e883debfa6 --- /dev/null +++ b/packages/react/src/hooks/useEnsResolver.ts @@ -0,0 +1,58 @@ +'use client' + +import type { + Config, + GetEnsResolverErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetEnsResolverData, + type GetEnsResolverOptions, + type GetEnsResolverQueryFnData, + type GetEnsResolverQueryKey, + getEnsResolverQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseEnsResolverParameters< + config extends Config = Config, + selectData = GetEnsResolverData, +> = Compute< + GetEnsResolverOptions & + ConfigParameter & + QueryParameter< + GetEnsResolverQueryFnData, + GetEnsResolverErrorType, + selectData, + GetEnsResolverQueryKey + > +> + +export type UseEnsResolverReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useEnsResolver */ +export function useEnsResolver< + config extends Config = ResolvedRegister['config'], + selectData = GetEnsResolverData, +>( + parameters: UseEnsResolverParameters = {}, +): UseEnsResolverReturnType { + const { name, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getEnsResolverQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEnsText.test.ts b/packages/react/src/hooks/useEnsText.test.ts new file mode 100644 index 0000000000..5e1c4b1d8c --- /dev/null +++ b/packages/react/src/hooks/useEnsText.test.ts @@ -0,0 +1,150 @@ +import { wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useEnsText } from './useEnsText.js' + +test('default', async () => { + const { result } = renderHook(() => + useEnsText({ + key: 'com.twitter', + name: 'wevm.eth', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "wevm_dev", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "ensText", + { + "chainId": 1, + "key": "com.twitter", + "name": "wevm.eth", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: name: undefined -> defined', async () => { + let name: string | undefined = undefined + + const { result, rerender } = renderHook(() => + useEnsText({ + key: 'com.twitter', + name, + }), + ) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "ensText", + { + "chainId": 1, + "key": "com.twitter", + "name": undefined, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + name = 'wevm.eth' + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "wevm_dev", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "ensText", + { + "chainId": 1, + "key": "com.twitter", + "name": "wevm.eth", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useEnsText()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useEnsText.ts b/packages/react/src/hooks/useEnsText.ts new file mode 100644 index 0000000000..2e65238c4f --- /dev/null +++ b/packages/react/src/hooks/useEnsText.ts @@ -0,0 +1,54 @@ +'use client' + +import type { Config, GetEnsTextErrorType, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetEnsTextData, + type GetEnsTextOptions, + type GetEnsTextQueryFnData, + type GetEnsTextQueryKey, + getEnsTextQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseEnsTextParameters< + config extends Config = Config, + selectData = GetEnsTextData, +> = Compute< + GetEnsTextOptions & + ConfigParameter & + QueryParameter< + GetEnsTextQueryFnData, + GetEnsTextErrorType, + selectData, + GetEnsTextQueryKey + > +> + +export type UseEnsTextReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useEnsText */ +export function useEnsText< + config extends Config = ResolvedRegister['config'], + selectData = GetEnsTextData, +>( + parameters: UseEnsTextParameters = {}, +): UseEnsTextReturnType { + const { key, name, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getEnsTextQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(key && name && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEstimateFeesPerGas.test-d.ts b/packages/react/src/hooks/useEstimateFeesPerGas.test-d.ts new file mode 100644 index 0000000000..679def0fd6 --- /dev/null +++ b/packages/react/src/hooks/useEstimateFeesPerGas.test-d.ts @@ -0,0 +1,49 @@ +import { expectTypeOf, test } from 'vitest' +import { useEstimateFeesPerGas } from './useEstimateFeesPerGas.js' + +test('types', () => { + const result = useEstimateFeesPerGas() + expectTypeOf(result.data).toMatchTypeOf< + | { + gasPrice?: undefined + maxFeePerGas: bigint + maxPriorityFeePerGas: bigint + formatted: { + gasPrice?: undefined + maxFeePerGas: string + maxPriorityFeePerGas: string + } + } + | undefined + >() + + const result2 = useEstimateFeesPerGas({ type: 'legacy' }) + expectTypeOf(result2.data).toMatchTypeOf< + | { + gasPrice: bigint + maxFeePerGas?: undefined + maxPriorityFeePerGas?: undefined + formatted: { + gasPrice: string + maxFeePerGas?: undefined + maxPriorityFeePerGas?: undefined + } + } + | undefined + >() + + const result3 = useEstimateFeesPerGas({ type: 'eip1559' }) + expectTypeOf(result3.data).toMatchTypeOf< + | { + gasPrice?: undefined + maxFeePerGas: bigint + maxPriorityFeePerGas: bigint + formatted: { + gasPrice?: undefined + maxFeePerGas: string + maxPriorityFeePerGas: string + } + } + | undefined + >() +}) diff --git a/packages/react/src/hooks/useEstimateFeesPerGas.test.ts b/packages/react/src/hooks/useEstimateFeesPerGas.test.ts new file mode 100644 index 0000000000..5d1859eda9 --- /dev/null +++ b/packages/react/src/hooks/useEstimateFeesPerGas.test.ts @@ -0,0 +1,19 @@ +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useEstimateFeesPerGas } from './useEstimateFeesPerGas.js' + +test('default', async () => { + const { result } = renderHook(() => useEstimateFeesPerGas()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(Object.keys(result.current.data!)).toMatchInlineSnapshot(` + [ + "formatted", + "gasPrice", + "maxFeePerGas", + "maxPriorityFeePerGas", + ] + `) +}) diff --git a/packages/react/src/hooks/useEstimateFeesPerGas.ts b/packages/react/src/hooks/useEstimateFeesPerGas.ts new file mode 100644 index 0000000000..59dc2aa910 --- /dev/null +++ b/packages/react/src/hooks/useEstimateFeesPerGas.ts @@ -0,0 +1,62 @@ +'use client' + +import type { + Config, + EstimateFeesPerGasErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type EstimateFeesPerGasData, + type EstimateFeesPerGasOptions, + type EstimateFeesPerGasQueryFnData, + type EstimateFeesPerGasQueryKey, + estimateFeesPerGasQueryOptions, +} from '@wagmi/core/query' +import type { FeeValuesType } from 'viem' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseEstimateFeesPerGasParameters< + type extends FeeValuesType = FeeValuesType, + config extends Config = Config, + selectData = EstimateFeesPerGasData, +> = Compute< + EstimateFeesPerGasOptions & + ConfigParameter & + QueryParameter< + EstimateFeesPerGasQueryFnData, + EstimateFeesPerGasErrorType, + selectData, + EstimateFeesPerGasQueryKey + > +> + +export type UseEstimateFeesPerGasReturnType< + type extends FeeValuesType = FeeValuesType, + selectData = EstimateFeesPerGasData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useEstimateFeesPerGas */ +export function useEstimateFeesPerGas< + config extends Config = ResolvedRegister['config'], + type extends FeeValuesType = 'eip1559', + selectData = EstimateFeesPerGasData, +>( + parameters: UseEstimateFeesPerGasParameters = {}, +): UseEstimateFeesPerGasReturnType { + const { query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = estimateFeesPerGasQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + + return useQuery({ ...query, ...options }) +} diff --git a/packages/react/src/hooks/useEstimateGas.test-d.ts b/packages/react/src/hooks/useEstimateGas.test-d.ts new file mode 100644 index 0000000000..d50618aad2 --- /dev/null +++ b/packages/react/src/hooks/useEstimateGas.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useEstimateGas } from './useEstimateGas.js' + +test('select data', () => { + const result = useEstimateGas({ + query: { + select(data) { + return data.toString() + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useEstimateGas.test.ts b/packages/react/src/hooks/useEstimateGas.test.ts new file mode 100644 index 0000000000..0c3ab08324 --- /dev/null +++ b/packages/react/src/hooks/useEstimateGas.test.ts @@ -0,0 +1,139 @@ +import { accounts } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { type Address, parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { useEstimateGas } from './useEstimateGas.js' + +test('default', async () => { + const { result } = renderHook(() => + useEstimateGas({ + account: accounts[0], + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 21000n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "estimateGas", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "to": "0xd2135CfB216b74109775236E36d4b433F1DF507B", + "value": 10000000000000000n, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: address: undefined -> defined', async () => { + let account: Address | undefined = undefined + + const { result, rerender } = renderHook(() => useEstimateGas({ account })) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "estimateGas", + { + "account": undefined, + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + account = accounts[0] + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 53001n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "estimateGas", + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useEstimateGas.ts b/packages/react/src/hooks/useEstimateGas.ts new file mode 100644 index 0000000000..87c0e19e44 --- /dev/null +++ b/packages/react/src/hooks/useEstimateGas.ts @@ -0,0 +1,70 @@ +'use client' + +import type { + Config, + EstimateGasErrorType, + ResolvedRegister, +} from '@wagmi/core' +import { + type EstimateGasData, + type EstimateGasOptions, + type EstimateGasQueryFnData, + type EstimateGasQueryKey, + estimateGasQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' +import { useConnectorClient } from './useConnectorClient.js' + +export type UseEstimateGasParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = EstimateGasData, +> = EstimateGasOptions & + ConfigParameter & + QueryParameter< + EstimateGasQueryFnData, + EstimateGasErrorType, + selectData, + EstimateGasQueryKey + > + +export type UseEstimateGasReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useEstimateGas */ +export function useEstimateGas< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = EstimateGasData, +>( + parameters?: UseEstimateGasParameters, +): UseEstimateGasReturnType + +export function useEstimateGas( + parameters: UseEstimateGasParameters = {}, +): UseEstimateGasReturnType { + const { connector, query = {} } = parameters + + const config = useConfig(parameters) + const { data: connectorClient } = useConnectorClient({ + config, + connector, + query: { enabled: parameters.account === undefined }, + }) + const account = parameters.account ?? connectorClient?.account + const chainId = useChainId({ config }) + + const options = estimateGasQueryOptions(config, { + ...parameters, + account, + chainId: parameters.chainId ?? chainId, + connector, + }) + const enabled = Boolean((account || connector) && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.test-d.ts b/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.test-d.ts new file mode 100644 index 0000000000..c4b1be1c8b --- /dev/null +++ b/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.test-d.ts @@ -0,0 +1,13 @@ +import { expectTypeOf, test } from 'vitest' +import { useEstimateMaxPriorityFeePerGas } from './useEstimateMaxPriorityFeePerGas.js' + +test('select data', () => { + const result = useEstimateMaxPriorityFeePerGas({ + query: { + select(data) { + return data.toString() + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.test.ts b/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.test.ts new file mode 100644 index 0000000000..21c09188e3 --- /dev/null +++ b/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.test.ts @@ -0,0 +1,96 @@ +import { chain, testClient } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useEstimateMaxPriorityFeePerGas } from './useEstimateMaxPriorityFeePerGas.js' + +test('default', async () => { + await testClient.mainnet.restart() + + const { result } = renderHook(() => useEstimateMaxPriorityFeePerGas()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('bigint') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "estimateMaxPriorityFeePerGas", + { + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + await testClient.mainnet2.restart() + await testClient.mainnet2.mine({ blocks: 1 }) + + const { result } = renderHook(() => + useEstimateMaxPriorityFeePerGas({ chainId: chain.mainnet2.id }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('bigint') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "estimateMaxPriorityFeePerGas", + { + "chainId": 456, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.ts b/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.ts new file mode 100644 index 0000000000..0f884dba75 --- /dev/null +++ b/packages/react/src/hooks/useEstimateMaxPriorityFeePerGas.ts @@ -0,0 +1,61 @@ +'use client' + +import type { + Config, + EstimateMaxPriorityFeePerGasErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type EstimateMaxPriorityFeePerGasData, + type EstimateMaxPriorityFeePerGasOptions, + type EstimateMaxPriorityFeePerGasQueryFnData, + type EstimateMaxPriorityFeePerGasQueryKey, + estimateMaxPriorityFeePerGasQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseEstimateMaxPriorityFeePerGasParameters< + config extends Config = Config, + selectData = EstimateMaxPriorityFeePerGasData, +> = Compute< + EstimateMaxPriorityFeePerGasOptions & + ConfigParameter & + QueryParameter< + EstimateMaxPriorityFeePerGasQueryFnData, + EstimateMaxPriorityFeePerGasErrorType, + selectData, + EstimateMaxPriorityFeePerGasQueryKey + > +> + +export type UseEstimateMaxPriorityFeePerGasReturnType< + selectData = EstimateMaxPriorityFeePerGasData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useEstimateMaxPriorityFeePerGas */ +export function useEstimateMaxPriorityFeePerGas< + config extends Config = ResolvedRegister['config'], + selectData = EstimateMaxPriorityFeePerGasData, +>( + parameters: UseEstimateMaxPriorityFeePerGasParameters< + config, + selectData + > = {}, +): UseEstimateMaxPriorityFeePerGasReturnType { + const { query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = estimateMaxPriorityFeePerGasQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + + return useQuery({ ...query, ...options }) +} diff --git a/packages/react/src/hooks/useFeeHistory.test-d.ts b/packages/react/src/hooks/useFeeHistory.test-d.ts new file mode 100644 index 0000000000..855fa893ae --- /dev/null +++ b/packages/react/src/hooks/useFeeHistory.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useFeeHistory } from './useFeeHistory.js' + +test('select data', () => { + const result = useFeeHistory({ + query: { + select(data) { + return data.gasUsedRatio + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useFeeHistory.test.ts b/packages/react/src/hooks/useFeeHistory.test.ts new file mode 100644 index 0000000000..53b6a8c55b --- /dev/null +++ b/packages/react/src/hooks/useFeeHistory.test.ts @@ -0,0 +1,452 @@ +import { chain, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useFeeHistory } from './useFeeHistory.js' + +test('default', async () => { + const { result } = renderHook(() => + useFeeHistory({ + blockCount: 4, + rewardPercentiles: [25, 75], + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "chainId": 1, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useFeeHistory({ + blockCount: 4, + rewardPercentiles: [25, 75], + chainId: chain.mainnet2.id, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "chainId": 456, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockNumber', async () => { + const { result } = renderHook(() => + useFeeHistory({ + blockCount: 4, + rewardPercentiles: [25, 75], + blockNumber: 18677379n, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "blockNumber": 18677379n, + "chainId": 1, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockTag', async () => { + const { result } = renderHook(() => + useFeeHistory({ + blockCount: 4, + rewardPercentiles: [25, 75], + blockTag: 'safe', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "blockTag": "safe", + "chainId": 1, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: blockCount: undefined -> defined', async () => { + let blockCount: number | undefined = undefined + + const { result, rerender } = renderHook(() => + useFeeHistory({ + blockCount, + rewardPercentiles: [25, 75], + }), + ) + + { + const { data, ...rest } = result.current + expect(data).toBeTypeOf('undefined') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "feeHistory", + { + "blockCount": undefined, + "chainId": 1, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + } + + blockCount = 4 + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "chainId": 1, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: rewardPercentiles: undefined -> defined', async () => { + let rewardPercentiles: number[] | undefined = undefined + + const { result, rerender } = renderHook(() => + useFeeHistory({ + blockCount: 4, + rewardPercentiles, + }), + ) + + { + const { data, ...rest } = result.current + expect(data).toBeTypeOf('undefined') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "chainId": 1, + "rewardPercentiles": undefined, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + } + + rewardPercentiles = [25, 75] + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toMatchObject({ + baseFeePerGas: expect.arrayContaining([expect.any(BigInt)]), + gasUsedRatio: expect.arrayContaining([expect.any(Number)]), + oldestBlock: expect.any(BigInt), + reward: expect.any(Array), + }) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "feeHistory", + { + "blockCount": 4, + "chainId": 1, + "rewardPercentiles": [ + 25, + 75, + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useFeeHistory()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useFeeHistory.ts b/packages/react/src/hooks/useFeeHistory.ts new file mode 100644 index 0000000000..0754757b31 --- /dev/null +++ b/packages/react/src/hooks/useFeeHistory.ts @@ -0,0 +1,64 @@ +'use client' + +import type { + Config, + GetFeeHistoryErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetFeeHistoryData, + type GetFeeHistoryOptions, + type GetFeeHistoryQueryFnData, + type GetFeeHistoryQueryKey, + getFeeHistoryQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseFeeHistoryParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetFeeHistoryData, +> = Compute< + GetFeeHistoryOptions & + ConfigParameter & + QueryParameter< + GetFeeHistoryQueryFnData, + GetFeeHistoryErrorType, + selectData, + GetFeeHistoryQueryKey + > +> + +export type UseFeeHistoryReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useFeeHistory */ +export function useFeeHistory< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetFeeHistoryData, +>( + parameters: UseFeeHistoryParameters = {}, +): UseFeeHistoryReturnType { + const { blockCount, rewardPercentiles, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getFeeHistoryQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean( + blockCount && rewardPercentiles && (query.enabled ?? true), + ) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useGasPrice.test-d.ts b/packages/react/src/hooks/useGasPrice.test-d.ts new file mode 100644 index 0000000000..7108993a20 --- /dev/null +++ b/packages/react/src/hooks/useGasPrice.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useGasPrice } from './useGasPrice.js' + +test('select data', () => { + const result = useGasPrice({ + query: { + select(data) { + return data?.toString() + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useGasPrice.test.ts b/packages/react/src/hooks/useGasPrice.test.ts new file mode 100644 index 0000000000..5dd97cc96c --- /dev/null +++ b/packages/react/src/hooks/useGasPrice.test.ts @@ -0,0 +1,103 @@ +import { chain, testClient } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useGasPrice } from './useGasPrice.js' + +test('default', async () => { + await testClient.mainnet.restart() + + await testClient.mainnet.setNextBlockBaseFeePerGas({ + baseFeePerGas: 2_000_000_000n, + }) + await testClient.mainnet.mine({ blocks: 1 }) + + const { result } = renderHook(() => useGasPrice()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 2750000000n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "gasPrice", + { + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + await testClient.mainnet2.restart() + + await testClient.mainnet2.setNextBlockBaseFeePerGas({ + baseFeePerGas: 1_000_000_000n, + }) + await testClient.mainnet2.mine({ blocks: 1 }) + + const { result } = renderHook(() => + useGasPrice({ chainId: chain.mainnet2.id }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 1875000000n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "gasPrice", + { + "chainId": 456, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useGasPrice.ts b/packages/react/src/hooks/useGasPrice.ts new file mode 100644 index 0000000000..dc823dcf64 --- /dev/null +++ b/packages/react/src/hooks/useGasPrice.ts @@ -0,0 +1,62 @@ +'use client' + +import type { + Config, + GetGasPriceErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetGasPriceData, + type GetGasPriceOptions, + type GetGasPriceQueryFnData, + type GetGasPriceQueryKey, + getGasPriceQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseGasPriceParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetGasPriceData, +> = Compute< + GetGasPriceOptions & + ConfigParameter & + QueryParameter< + GetGasPriceQueryFnData, + GetGasPriceErrorType, + selectData, + GetGasPriceQueryKey + > +> + +export type UseGasPriceReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useGasPrice */ +export function useGasPrice< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetGasPriceData, +>( + parameters: UseGasPriceParameters = {}, +): UseGasPriceReturnType { + const { query = {} } = parameters + + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const chainId = parameters.chainId ?? configChainId + + const options = getGasPriceQueryOptions(config, { + ...parameters, + chainId, + }) + + return useQuery({ ...query, ...options }) +} diff --git a/packages/react/src/hooks/useInfiniteReadContracts.test-d.ts b/packages/react/src/hooks/useInfiniteReadContracts.test-d.ts new file mode 100644 index 0000000000..60b3a2cddd --- /dev/null +++ b/packages/react/src/hooks/useInfiniteReadContracts.test-d.ts @@ -0,0 +1,44 @@ +import { abi } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useInfiniteReadContracts } from './useInfiniteReadContracts.js' + +test('select data', () => { + const result = useInfiniteReadContracts({ + allowFailure: false, + cacheKey: 'foo', + contracts(pageParam) { + expectTypeOf(pageParam).toEqualTypeOf() + return [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ] + }, + query: { + initialPageParam: '0', + getNextPageParam(lastPage, allPages, lastPageParam, allPageParams) { + expectTypeOf(lastPage).toEqualTypeOf<[bigint, string]>() + expectTypeOf(allPages).toEqualTypeOf<[bigint, string][]>() + expectTypeOf(lastPageParam).toEqualTypeOf() + expectTypeOf(allPageParams).toEqualTypeOf() + return lastPageParam + 1 + }, + select(data) { + expectTypeOf(data.pageParams[0]!).toEqualTypeOf() + expectTypeOf(data.pages[0]!).toEqualTypeOf<[bigint, string]>() + return data.pages[0]?.[0]! + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useInfiniteReadContracts.test.ts b/packages/react/src/hooks/useInfiniteReadContracts.test.ts new file mode 100644 index 0000000000..0feb1e0e98 --- /dev/null +++ b/packages/react/src/hooks/useInfiniteReadContracts.test.ts @@ -0,0 +1,91 @@ +import { abi, address } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useInfiniteReadContracts } from './useInfiniteReadContracts.js' + +test( + 'default', + async () => { + const limit = 3 + + const { result } = renderHook(() => + useInfiniteReadContracts({ + cacheKey: 'foo', + contracts(pageParam) { + return [...new Array(limit)].map( + (_, i) => + ({ + address: address.shields, + abi: abi.shields, + functionName: 'tokenURI', + args: [BigInt(pageParam + i + 1)], + }) as const, + ) + }, + query: { + initialPageParam: 0, + getNextPageParam(_lastPage, _allPages, lastPageParam) { + return lastPageParam + limit + }, + select(data) { + const results = [] + for (const page of data.pages) { + for (const response of page) { + if (response.status === 'success') { + const decoded = atob( + response.result.replace(/(^.*base64,)/, ''), + ) + const json = JSON.parse(decoded) as { name: string } + results.push(json.name) + } else results.push('Error fetching shield') + } + } + return results + }, + }, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + expect(result.current.data).toMatchInlineSnapshot(` + [ + "Three Shields on Pink Perfect", + "Three Shields on Sky Perfect", + "Three Shields on Evergreen Perfect", + ] + `) + + await result.current.fetchNextPage() + + await waitFor(() => expect(result.current.hasNextPage).toBeTruthy()) + expect(result.current.data).toMatchInlineSnapshot(` + [ + "Three Shields on Pink Perfect", + "Three Shields on Sky Perfect", + "Three Shields on Evergreen Perfect", + "Three Shields on Hi-Vis Perfect", + "Three Shields on Gray Perfect", + "Everlasting: Tracery on Onyx and Pink Razor Bordure", + ] + `) + + await result.current.fetchNextPage() + + await waitFor(() => expect(result.current.hasNextPage).toBeTruthy()) + expect(result.current.data).toMatchInlineSnapshot(` + [ + "Three Shields on Pink Perfect", + "Three Shields on Sky Perfect", + "Three Shields on Evergreen Perfect", + "Three Shields on Hi-Vis Perfect", + "Three Shields on Gray Perfect", + "Everlasting: Tracery on Onyx and Pink Razor Bordure", + "The Book of Shields on Pink Perfect", + "Menacing: Necklace on Evergreen and Hi-Vis Chief Indented", + "Secured: Telescope and Stars on Ultraviolet and Sky Doppler", + ] + `) + }, + { timeout: 20_000 }, +) diff --git a/packages/react/src/hooks/useInfiniteReadContracts.ts b/packages/react/src/hooks/useInfiniteReadContracts.ts new file mode 100644 index 0000000000..2622f434a6 --- /dev/null +++ b/packages/react/src/hooks/useInfiniteReadContracts.ts @@ -0,0 +1,89 @@ +'use client' + +import type { + Config, + ReadContractsErrorType, + ResolvedRegister, +} from '@wagmi/core' +import { + type InfiniteReadContractsQueryFnData, + type InfiniteReadContractsQueryKey, + infiniteReadContractsQueryOptions, + structuralSharing, +} from '@wagmi/core/query' +import type { ContractFunctionParameters } from 'viem' + +import type { + InfiniteReadContractsData, + InfiniteReadContractsOptions, +} from '../exports/query.js' +import type { + ConfigParameter, + InfiniteQueryParameter, +} from '../types/properties.js' +import { + type UseInfiniteQueryParameters, + type UseInfiniteQueryReturnType, + useInfiniteQuery, +} from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseInfiniteContractReadsParameters< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + allowFailure extends boolean = true, + config extends Config = Config, + pageParam = unknown, + selectData = InfiniteReadContractsData, +> = InfiniteReadContractsOptions & + ConfigParameter & + InfiniteQueryParameter< + InfiniteReadContractsQueryFnData, + ReadContractsErrorType, + selectData, + InfiniteReadContractsData, + InfiniteReadContractsQueryKey, + pageParam + > + +export type UseInfiniteContractReadsReturnType< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + allowFailure extends boolean = true, + selectData = InfiniteReadContractsData, +> = UseInfiniteQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useInfiniteReadContracts */ +export function useInfiniteReadContracts< + const contracts extends readonly unknown[], + allowFailure extends boolean = true, + config extends Config = ResolvedRegister['config'], + pageParam = unknown, + selectData = InfiniteReadContractsData, +>( + parameters: UseInfiniteContractReadsParameters< + contracts, + allowFailure, + config, + pageParam, + selectData + >, +): UseInfiniteContractReadsReturnType { + const { contracts = [], query } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = infiniteReadContractsQueryOptions(config, { + ...parameters, + chainId, + contracts: contracts as UseInfiniteContractReadsParameters['contracts'], + query: query as UseInfiniteQueryParameters, + }) + + return useInfiniteQuery({ + ...(query as any), + ...options, + initialPageParam: options.initialPageParam, + structuralSharing: query.structuralSharing ?? structuralSharing, + }) +} diff --git a/packages/react/src/hooks/usePrepareTransactionRequest.test-d.ts b/packages/react/src/hooks/usePrepareTransactionRequest.test-d.ts new file mode 100644 index 0000000000..9c39595ab4 --- /dev/null +++ b/packages/react/src/hooks/usePrepareTransactionRequest.test-d.ts @@ -0,0 +1,27 @@ +import { config } from '@wagmi/test' +import type { PrepareTransactionRequestReturnType } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { usePrepareTransactionRequest } from './usePrepareTransactionRequest.js' + +test('select data', () => { + const result = usePrepareTransactionRequest({ + query: { + select(data) { + return data + }, + }, + }) + + expectTypeOf(result.data).toMatchTypeOf< + PrepareTransactionRequestReturnType | undefined + >() +}) + +test('parameters: config', () => { + const result = usePrepareTransactionRequest({ + config, + chainId: 456, + }) + if (result.data) expectTypeOf(result.data.chainId).toEqualTypeOf<456>() +}) diff --git a/packages/react/src/hooks/usePrepareTransactionRequest.test.ts b/packages/react/src/hooks/usePrepareTransactionRequest.test.ts new file mode 100644 index 0000000000..7b90729dbf --- /dev/null +++ b/packages/react/src/hooks/usePrepareTransactionRequest.test.ts @@ -0,0 +1,81 @@ +import { connect, disconnect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { usePrepareTransactionRequest } from './usePrepareTransactionRequest.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => + usePrepareTransactionRequest({ + to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + value: parseEther('1'), + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { + data: { + gas: _gas, + maxFeePerGas: _mfpg, + maxPriorityFeePerGas: _mpfpg, + nonce: _nonce, + ...data + } = {}, + ...rest + } = result.current + + expect(data).toMatchInlineSnapshot(` + { + "account": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "type": "eip1559", + "value": 1000000000000000000n, + } + `) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "prepareTransactionRequest", + { + "chainId": 1, + "to": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", + "value": 1000000000000000000n, + }, + ], + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/usePrepareTransactionRequest.ts b/packages/react/src/hooks/usePrepareTransactionRequest.ts new file mode 100644 index 0000000000..509d85eb08 --- /dev/null +++ b/packages/react/src/hooks/usePrepareTransactionRequest.ts @@ -0,0 +1,102 @@ +'use client' + +import type { + Config, + PrepareTransactionRequestErrorType, + ResolvedRegister, + SelectChains, +} from '@wagmi/core' +import { + type PrepareTransactionRequestData, + type PrepareTransactionRequestOptions, + type PrepareTransactionRequestQueryKey, + prepareTransactionRequestQueryOptions, +} from '@wagmi/core/query' +import type { PrepareTransactionRequestQueryFnData } from '@wagmi/core/query' +import type { PrepareTransactionRequestRequest as viem_PrepareTransactionRequestRequest } from 'viem' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UsePrepareTransactionRequestParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + > = viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, + selectData = PrepareTransactionRequestData, +> = PrepareTransactionRequestOptions & + ConfigParameter & + QueryParameter< + PrepareTransactionRequestQueryFnData, + PrepareTransactionRequestErrorType, + selectData, + PrepareTransactionRequestQueryKey + > + +export type UsePrepareTransactionRequestReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + > = viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, + selectData = PrepareTransactionRequestData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/usePrepareTransactionRequest */ +export function usePrepareTransactionRequest< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] | undefined = undefined, + request extends viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + > = viem_PrepareTransactionRequestRequest< + SelectChains[0], + SelectChains[0] + >, + selectData = PrepareTransactionRequestData, +>( + parameters: UsePrepareTransactionRequestParameters< + config, + chainId, + request, + selectData + > = {} as any, +): UsePrepareTransactionRequestReturnType< + config, + chainId, + request, + selectData +> { + const { to, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = prepareTransactionRequestQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + } as PrepareTransactionRequestOptions) + const enabled = Boolean(to && (query.enabled ?? true)) + + return useQuery({ + ...(query as any), + ...options, + enabled, + }) as UsePrepareTransactionRequestReturnType< + config, + chainId, + request, + selectData + > +} diff --git a/packages/react/src/hooks/useProof.test-d.ts b/packages/react/src/hooks/useProof.test-d.ts new file mode 100644 index 0000000000..55f8b15330 --- /dev/null +++ b/packages/react/src/hooks/useProof.test-d.ts @@ -0,0 +1,14 @@ +import type { GetProofReturnType } from 'viem' +import { expectTypeOf, test } from 'vitest' +import { useProof } from './useProof.js' + +test('select data', () => { + const result = useProof({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useProof.test.ts b/packages/react/src/hooks/useProof.test.ts new file mode 100644 index 0000000000..3c3cb51945 --- /dev/null +++ b/packages/react/src/hooks/useProof.test.ts @@ -0,0 +1,163 @@ +import { chain, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import type { Address } from 'viem' +import { useProof } from './useProof.js' + +test('default', async () => { + const { result } = renderHook(() => + useProof({ + address: '0x4200000000000000000000000000000000000016', + chainId: chain.optimism.id, + storageKeys: [ + '0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99', + ], + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect({ ...result.current, data: null }).toMatchInlineSnapshot(` + { + "data": null, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getProof", + { + "address": "0x4200000000000000000000000000000000000016", + "chainId": 10, + "storageKeys": [ + "0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99", + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: address: undefined -> defined', async () => { + let address: Address | undefined = undefined + + const { result, rerender } = renderHook(() => + useProof({ + address, + chainId: chain.optimism.id, + storageKeys: [ + '0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99', + ], + }), + ) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "getProof", + { + "address": undefined, + "chainId": 10, + "storageKeys": [ + "0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99", + ], + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + address = '0x4200000000000000000000000000000000000016' + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect({ ...result.current, data: null }).toMatchInlineSnapshot(` + { + "data": null, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getProof", + { + "address": "0x4200000000000000000000000000000000000016", + "chainId": 10, + "storageKeys": [ + "0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99", + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useProof()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useProof.ts b/packages/react/src/hooks/useProof.ts new file mode 100644 index 0000000000..f473d509aa --- /dev/null +++ b/packages/react/src/hooks/useProof.ts @@ -0,0 +1,56 @@ +'use client' + +import type { Config, GetProofErrorType, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetProofData, + type GetProofOptions, + type GetProofQueryKey, + getProofQueryOptions, +} from '@wagmi/core/query' +import type { GetProofQueryFnData } from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseProofParameters< + config extends Config = Config, + selectData = GetProofData, +> = Compute< + GetProofOptions & + ConfigParameter & + QueryParameter< + GetProofQueryFnData, + GetProofErrorType, + selectData, + GetProofQueryKey + > +> + +export type UseProofReturnType = UseQueryReturnType< + selectData, + GetProofErrorType +> + +/** https://wagmi.sh/react/api/hooks/useProof */ +export function useProof< + config extends Config = ResolvedRegister['config'], + selectData = GetProofData, +>( + parameters: UseProofParameters = {}, +): UseProofReturnType { + const { address, storageKeys, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getProofQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && storageKeys && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/usePublicClient.test-d.ts b/packages/react/src/hooks/usePublicClient.test-d.ts new file mode 100644 index 0000000000..5d259e5c4e --- /dev/null +++ b/packages/react/src/hooks/usePublicClient.test-d.ts @@ -0,0 +1,40 @@ +import { chain, config } from '@wagmi/test' +import type { Chain } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { usePublicClient } from './usePublicClient.js' + +test('default', () => { + const client = usePublicClient({ config }) + expectTypeOf(client.chain).toEqualTypeOf<(typeof config)['chains'][number]>() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = usePublicClient({ + config, + chainId: chain.mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.chain).not.toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('behavior: unconfigured chain', () => { + { + const client = usePublicClient({ chainId: 123456 }) + if (client) { + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf() + } else { + expectTypeOf(client).toEqualTypeOf() + } + } + + const client = usePublicClient({ + config, + // @ts-expect-error + chainId: 123456, + }) + expectTypeOf(client).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/usePublicClient.test.ts b/packages/react/src/hooks/usePublicClient.test.ts new file mode 100644 index 0000000000..93602746bb --- /dev/null +++ b/packages/react/src/hooks/usePublicClient.test.ts @@ -0,0 +1,30 @@ +import { switchChain } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { Fragment, createElement } from 'react' +import { expect, test } from 'vitest' + +import { usePublicClient } from './usePublicClient.js' + +test('default', async () => { + const { result, rerender } = renderHook(() => usePublicClient()) + + expect(result.current?.chain.id).toEqual(1) + + await switchChain(config, { chainId: 456 }) + rerender() + + expect(result.current?.chain.id).toEqual(456) +}) + +test('parameters: config', () => { + const { result } = renderHook(() => usePublicClient({ config }), { + wrapper: ({ children }) => createElement(Fragment, { children }), + }) + expect(result.current).toBeDefined() +}) + +test('behavior: unconfigured chain', () => { + const { result } = renderHook(() => usePublicClient({ chainId: 123456 })) + expect(result.current).toBeUndefined() +}) diff --git a/packages/react/src/hooks/usePublicClient.ts b/packages/react/src/hooks/usePublicClient.ts new file mode 100644 index 0000000000..5193f170b6 --- /dev/null +++ b/packages/react/src/hooks/usePublicClient.ts @@ -0,0 +1,51 @@ +'use client' + +import { + type Config, + type GetPublicClientParameters, + type GetPublicClientReturnType, + type ResolvedRegister, + getPublicClient, + watchPublicClient, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector.js' + +import type { ConfigParameter } from '../types/properties.js' +import { useConfig } from './useConfig.js' + +export type UsePublicClientParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | number | undefined = + | config['chains'][number]['id'] + | undefined, +> = Compute< + GetPublicClientParameters & ConfigParameter +> + +export type UsePublicClientReturnType< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | number | undefined = + | config['chains'][number]['id'] + | undefined, +> = GetPublicClientReturnType + +/** https://wagmi.sh/react/api/hooks/usePublicClient */ +export function usePublicClient< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] | number | undefined = + | config['chains'][number]['id'] + | undefined, +>( + parameters: UsePublicClientParameters = {}, +): UsePublicClientReturnType { + const config = useConfig(parameters) + + return useSyncExternalStoreWithSelector( + (onChange) => watchPublicClient(config, { onChange }), + () => getPublicClient(config, parameters), + () => getPublicClient(config, parameters), + (x) => x, + (a, b) => a?.uid === b?.uid, + ) as any +} diff --git a/packages/react/src/hooks/useReadContract.test-d.ts b/packages/react/src/hooks/useReadContract.test-d.ts new file mode 100644 index 0000000000..3b57d49a9e --- /dev/null +++ b/packages/react/src/hooks/useReadContract.test-d.ts @@ -0,0 +1,96 @@ +import { abi } from '@wagmi/test' +import type { Address } from 'viem' +import { assertType, expectTypeOf, test } from 'vitest' + +import { + type UseReadContractParameters, + type UseReadContractReturnType, + useReadContract, +} from './useReadContract.js' + +test('select data', () => { + const result = useReadContract({ + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + query: { + select(data) { + expectTypeOf(data).toEqualTypeOf() + return data?.toString() + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('UseReadContractParameters', () => { + type Result = UseReadContractParameters + expectTypeOf>().toEqualTypeOf<{ + functionName?: + | 'symbol' + | 'name' + | 'allowance' + | 'balanceOf' + | 'decimals' + | 'totalSupply' + | undefined + args?: readonly [Address] | undefined + }>() +}) + +test('UseReadContractReturnType', () => { + type Result = UseReadContractReturnType + expectTypeOf().toEqualTypeOf() +}) + +test('overloads', () => { + const result1 = useReadContract({ + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + }) + assertType(result1.data) + + const result2 = useReadContract({ + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: [], + }) + assertType(result2.data) + + const result3 = useReadContract({ + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x'], + }) + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + assertType(result3.data) + + const result4 = useReadContract({ + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x', '0x'], + }) + assertType< + | { + foo: `0x${string}` + bar: `0x${string}` + } + | undefined + // @ts-ignore – TODO: Fix https://github.com/wevm/viem/issues/1916 + >(result4.data) +}) + +test('deployless read (bytecode)', () => { + const result = useReadContract({ + code: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + args: ['0x'], + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useReadContract.test.ts b/packages/react/src/hooks/useReadContract.test.ts new file mode 100644 index 0000000000..c94ca996ce --- /dev/null +++ b/packages/react/src/hooks/useReadContract.test.ts @@ -0,0 +1,194 @@ +import { QueryClientProvider } from '@tanstack/react-query' +import { abi, address, bytecode, chain, config, wait } from '@wagmi/test' +import { queryClient, renderHook, waitFor } from '@wagmi/test/react' +import { createElement } from 'react' +import { expect, test } from 'vitest' + +import { useReadContract } from './useReadContract.js' + +test('default', async () => { + const { result } = renderHook(() => + useReadContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 4n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "readContract", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 1, + "functionName": "balanceOf", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useReadContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + chainId: chain.mainnet2.id, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 4n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "readContract", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 456, + "functionName": "balanceOf", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: config', async () => { + const { result } = renderHook( + () => + useReadContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + config, + }), + { + wrapper: ({ children }) => + createElement(QueryClientProvider, { client: queryClient }, children), + }, + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": 4n, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "readContract", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 1, + "functionName": "balanceOf", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: deployless read (bytecode)', async () => { + const { result } = renderHook(() => + useReadContract({ + abi: abi.wagmiMintExample, + functionName: 'name', + code: bytecode.wagmiMintExample, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatchInlineSnapshot(`"wagmi"`) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useReadContract()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useReadContract.ts b/packages/react/src/hooks/useReadContract.ts new file mode 100644 index 0000000000..6eb6d6d64f --- /dev/null +++ b/packages/react/src/hooks/useReadContract.ts @@ -0,0 +1,99 @@ +'use client' + +import type { + Config, + ReadContractErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { UnionCompute } from '@wagmi/core/internal' +import { + type ReadContractData, + type ReadContractOptions, + type ReadContractQueryFnData, + type ReadContractQueryKey, + readContractQueryOptions, + structuralSharing, +} from '@wagmi/core/query' +import type { Abi, ContractFunctionArgs, ContractFunctionName, Hex } from 'viem' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseReadContractParameters< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'pure' | 'view' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'pure' | 'view', + functionName + > = ContractFunctionArgs, + config extends Config = Config, + selectData = ReadContractData, +> = UnionCompute< + ReadContractOptions & + ConfigParameter & + QueryParameter< + ReadContractQueryFnData, + ReadContractErrorType, + selectData, + ReadContractQueryKey + > +> + +export type UseReadContractReturnType< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'pure' | 'view' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'pure' | 'view', + functionName + > = ContractFunctionArgs, + selectData = ReadContractData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useReadContract */ +export function useReadContract< + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, + config extends Config = ResolvedRegister['config'], + selectData = ReadContractData, +>( + parameters: UseReadContractParameters< + abi, + functionName, + args, + config, + selectData + > = {} as any, +): UseReadContractReturnType { + const { abi, address, functionName, query = {} } = parameters + // @ts-ignore + const code = parameters.code as Hex | undefined + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = readContractQueryOptions( + config, + { ...(parameters as any), chainId: parameters.chainId ?? chainId }, + ) + const enabled = Boolean( + (address || code) && abi && functionName && (query.enabled ?? true), + ) + + return useQuery({ + ...query, + ...options, + enabled, + structuralSharing: query.structuralSharing ?? structuralSharing, + }) +} diff --git a/packages/react/src/hooks/useReadContracts.test-d.ts b/packages/react/src/hooks/useReadContracts.test-d.ts new file mode 100644 index 0000000000..8aa786436a --- /dev/null +++ b/packages/react/src/hooks/useReadContracts.test-d.ts @@ -0,0 +1,93 @@ +import { abi } from '@wagmi/test' +import { assertType, expectTypeOf, test } from 'vitest' + +import { useReadContracts } from './useReadContracts.js' + +test('select data', () => { + const result = useReadContracts({ + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.erc20, + functionName: 'balanceOf', + chainId: 1, + args: ['0x'], + }, + { + address: '0x', + abi: abi.wagmiMintExample, + functionName: 'tokenURI', + args: [123n], + }, + ], + query: { + select(data) { + expectTypeOf(data).toEqualTypeOf<[bigint, string]>() + return data[0] + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('overloads', async () => { + const result1 = useReadContracts({ + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + }, + ], + }) + assertType<[number] | undefined>(result1.data) + + const result2 = useReadContracts({ + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: [], + }, + ], + }) + assertType<[number] | undefined>(result2.data) + + const result3 = useReadContracts({ + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x'], + }, + ], + }) + assertType<[string] | undefined>(result3.data) + + const result4 = useReadContracts({ + allowFailure: false, + contracts: [ + { + address: '0x', + abi: abi.viewOverloads, + functionName: 'foo', + args: ['0x', '0x'], + }, + ], + }) + assertType< + | [ + { + foo: `0x${string}` + bar: `0x${string}` + }, + ] + | undefined + >(result4.data) +}) diff --git a/packages/react/src/hooks/useReadContracts.test.ts b/packages/react/src/hooks/useReadContracts.test.ts new file mode 100644 index 0000000000..e7636b254e --- /dev/null +++ b/packages/react/src/hooks/useReadContracts.test.ts @@ -0,0 +1,262 @@ +import { abi, address, chain } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useReadContracts } from './useReadContracts.js' + +test('default', async () => { + const { result } = renderHook(() => + useReadContracts({ + contracts: [ + { + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + { + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'symbol', + }, + ], + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": [ + { + "result": 4n, + "status": "success", + }, + { + "result": "WAGMI", + "status": "success", + }, + ], + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "readContracts", + { + "chainId": 1, + "contracts": [ + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 1, + "functionName": "balanceOf", + }, + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 1, + "functionName": "symbol", + }, + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test.skip('multichain', async () => { + const { mainnet, mainnet2, optimism } = chain + const { result } = renderHook(() => + useReadContracts({ + contracts: [ + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet.id, + functionName: 'love', + args: ['0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet.id, + functionName: 'love', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet.id, + functionName: 'love', + args: ['0xd2135CfB216b74109775236E36d4b433F1DF507B'], + }, + { + abi: abi.wagmigotchi, + address: address.wagmigotchi, + chainId: mainnet2.id, + functionName: 'getAlive', + }, + { + abi: abi.mloot, + address: address.mloot, + chainId: mainnet2.id, + functionName: 'tokenOfOwnerByIndex', + args: ['0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', 0n], + }, + { + abi: abi.erc20, + address: address.optimism.usdc, + chainId: optimism.id, + functionName: 'symbol', + }, + { + abi: abi.erc20, + address: address.optimism.usdc, + chainId: optimism.id, + functionName: 'balanceOf', + args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC'], + }, + ], + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": [ + { + "result": 2n, + "status": "success", + }, + { + "result": 1n, + "status": "success", + }, + { + "result": 0n, + "status": "success", + }, + { + "result": false, + "status": "success", + }, + { + "result": 370395n, + "status": "success", + }, + { + "result": "USDC", + "status": "success", + }, + { + "result": 10959340n, + "status": "success", + }, + ], + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "readContracts", + { + "chainId": 1, + "contracts": [ + { + "address": "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + "args": [ + "0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c", + ], + "chainId": 1, + "functionName": "love", + }, + { + "address": "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 1, + "functionName": "love", + }, + { + "address": "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + "args": [ + "0xd2135CfB216b74109775236E36d4b433F1DF507B", + ], + "chainId": 1, + "functionName": "love", + }, + { + "address": "0xecb504d39723b0be0e3a9aa33d646642d1051ee1", + "chainId": 456, + "functionName": "getAlive", + }, + { + "address": "0x1dfe7ca09e99d10835bf73044a23b73fc20623df", + "args": [ + "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e", + 0n, + ], + "chainId": 456, + "functionName": "tokenOfOwnerByIndex", + }, + { + "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607", + "chainId": 10, + "functionName": "symbol", + }, + { + "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607", + "args": [ + "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC", + ], + "chainId": 10, + "functionName": "balanceOf", + }, + ], + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useReadContracts.ts b/packages/react/src/hooks/useReadContracts.ts new file mode 100644 index 0000000000..7be786c4a8 --- /dev/null +++ b/packages/react/src/hooks/useReadContracts.ts @@ -0,0 +1,91 @@ +'use client' + +import type { + Config, + ReadContractsErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type ReadContractsData, + type ReadContractsOptions, + type ReadContractsQueryFnData, + type ReadContractsQueryKey, + readContractsQueryOptions, + structuralSharing, +} from '@wagmi/core/query' +import { useMemo } from 'react' +import type { ContractFunctionParameters } from 'viem' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseReadContractsParameters< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + allowFailure extends boolean = true, + config extends Config = Config, + selectData = ReadContractsData, +> = Compute< + ReadContractsOptions & + ConfigParameter & + QueryParameter< + ReadContractsQueryFnData, + ReadContractsErrorType, + selectData, + ReadContractsQueryKey + > +> + +export type UseReadContractsReturnType< + contracts extends readonly unknown[] = readonly ContractFunctionParameters[], + allowFailure extends boolean = true, + selectData = ReadContractsData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useReadContracts */ +export function useReadContracts< + const contracts extends readonly unknown[], + allowFailure extends boolean = true, + config extends Config = ResolvedRegister['config'], + selectData = ReadContractsData, +>( + parameters: UseReadContractsParameters< + contracts, + allowFailure, + config, + selectData + > = {}, +): UseReadContractsReturnType { + const { contracts = [], query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = readContractsQueryOptions( + config, + { ...parameters, chainId }, + ) + + const enabled = useMemo(() => { + let isContractsValid = false + for (const contract of contracts) { + const { abi, address, functionName } = + contract as ContractFunctionParameters + if (!abi || !address || !functionName) { + isContractsValid = false + break + } + isContractsValid = true + } + return Boolean(isContractsValid && (query.enabled ?? true)) + }, [contracts, query.enabled]) + + return useQuery({ + ...options, + ...query, + enabled, + structuralSharing: query.structuralSharing ?? structuralSharing, + }) +} diff --git a/packages/react/src/hooks/useReconnect.test-d.ts b/packages/react/src/hooks/useReconnect.test-d.ts new file mode 100644 index 0000000000..424159e469 --- /dev/null +++ b/packages/react/src/hooks/useReconnect.test-d.ts @@ -0,0 +1,154 @@ +import type { + Connector, + CreateConnectorFn, + ReconnectErrorType, +} from '@wagmi/core' +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import type { Address } from 'viem' +import { useReconnect } from './useReconnect.js' + +const connectors = [config.connectors[0]!] +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, reconnect, variables } = useReconnect({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toEqualTypeOf< + | { + connectors?: + | readonly (CreateConnectorFn | Connector)[] + | undefined + } + | undefined + >() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf< + | { + connectors?: + | readonly (CreateConnectorFn | Connector)[] + | undefined + } + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf< + | { + connectors?: + | readonly (CreateConnectorFn | Connector)[] + | undefined + } + | undefined + >() + expectTypeOf(data).toEqualTypeOf< + { + accounts: readonly [Address, ...Address[]] + chainId: number + connector: Connector + }[] + >() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + connector: Connector + }[] + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf< + | { + connectors?: + | readonly (CreateConnectorFn | Connector)[] + | undefined + } + | undefined + >() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + connector: Connector + }[] + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf< + | { + connectors?: readonly (CreateConnectorFn | Connector)[] | undefined + } + | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + reconnect( + { connectors }, + { + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf< + | { + connectors?: + | readonly (CreateConnectorFn | Connector)[] + | undefined + } + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf< + | { + connectors?: + | readonly (CreateConnectorFn | Connector)[] + | undefined + } + | undefined + >() + expectTypeOf(data).toEqualTypeOf< + { + accounts: readonly [Address, ...Address[]] + chainId: number + connector: Connector + }[] + >() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + connector: Connector + }[] + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf< + | { + connectors?: + | readonly (CreateConnectorFn | Connector)[] + | undefined + } + | undefined + >() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useReconnect.test.ts b/packages/react/src/hooks/useReconnect.test.ts new file mode 100644 index 0000000000..be783b2440 --- /dev/null +++ b/packages/react/src/hooks/useReconnect.test.ts @@ -0,0 +1,83 @@ +import { mock } from '@wagmi/connectors' +import { connect, disconnect } from '@wagmi/core' +import { accounts, config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { afterEach, expect, test } from 'vitest' + +import { useReconnect } from './useReconnect.js' + +const connector = config._internal.connectors.setup( + mock({ + accounts, + features: { reconnect: true }, + }), +) + +afterEach(async () => { + if (config.state.current) await disconnect(config) +}) + +test('default', async () => { + const { result } = renderHook(() => useReconnect()) + + expect(result.current.connectors).toBeDefined() + + result.current.reconnect() + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toStrictEqual([]) +}) + +test('parameters: connectors (Connector)', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useReconnect()) + + expect(result.current.connectors).toBeDefined() + + result.current.reconnect({ connectors: [connector] }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatchObject( + expect.arrayContaining([ + expect.objectContaining({ + accounts: expect.any(Array), + chainId: expect.any(Number), + }), + ]), + ) +}) + +test('parameters: connectors (CreateConnectorFn)', async () => { + const connector = mock({ + accounts, + features: { reconnect: true }, + }) + await connect(config, { connector }) + + const { result } = renderHook(() => useReconnect()) + + expect(result.current.connectors).toBeDefined() + + result.current.reconnect({ connectors: [connector] }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatchObject( + expect.arrayContaining([ + expect.objectContaining({ + accounts: expect.any(Array), + chainId: expect.any(Number), + }), + ]), + ) +}) + +test("behavior: doesn't reconnect if already reconnecting", async () => { + const previousStatus = config.state.status + config.setState((x) => ({ ...x, status: 'reconnecting' })) + const { result } = renderHook(() => useReconnect()) + await expect( + result.current.reconnectAsync({ connectors: [connector] }), + ).resolves.toStrictEqual([]) + config.setState((x) => ({ ...x, status: previousStatus })) +}) diff --git a/packages/react/src/hooks/useReconnect.ts b/packages/react/src/hooks/useReconnect.ts new file mode 100644 index 0000000000..23e6d365e4 --- /dev/null +++ b/packages/react/src/hooks/useReconnect.ts @@ -0,0 +1,67 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { Connector, ReconnectErrorType } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type ReconnectData, + type ReconnectMutate, + type ReconnectMutateAsync, + type ReconnectVariables, + reconnectMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseReconnectParameters = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + ReconnectData, + ReconnectErrorType, + ReconnectVariables, + context + > + | undefined + } +> + +export type UseReconnectReturnType = Compute< + UseMutationReturnType< + ReconnectData, + ReconnectErrorType, + ReconnectVariables, + context + > & { + connectors: readonly Connector[] + reconnect: ReconnectMutate + reconnectAsync: ReconnectMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useReconnect */ +export function useReconnect( + parameters: UseReconnectParameters = {}, +): UseReconnectReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = reconnectMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + return { + ...result, + connectors: config.connectors, + reconnect: mutate, + reconnectAsync: mutateAsync, + } +} diff --git a/packages/react/src/hooks/useSendCalls.test.ts b/packages/react/src/hooks/useSendCalls.test.ts new file mode 100644 index 0000000000..a088dcf221 --- /dev/null +++ b/packages/react/src/hooks/useSendCalls.test.ts @@ -0,0 +1,44 @@ +import { connect, disconnect } from '@wagmi/core' +import { accounts, config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { useSendCalls } from './useSendCalls.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useSendCalls()) + + result.current.sendCalls({ + calls: [ + { + data: '0xdeadbeef', + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatchInlineSnapshot( + ` + { + "id": "0x5dedb5a4ff8968db37459b52b83cbdc92b01c9c709c9cff26e345ef5cf27f92e", + } + `, + ) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useSendCalls.ts b/packages/react/src/hooks/useSendCalls.ts new file mode 100644 index 0000000000..49366cb218 --- /dev/null +++ b/packages/react/src/hooks/useSendCalls.ts @@ -0,0 +1,75 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { Config, ResolvedRegister, SendCallsErrorType } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type SendCallsData, + type SendCallsMutate, + type SendCallsMutateAsync, + type SendCallsVariables, + sendCallsMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseSendCallsParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + SendCallsData, + SendCallsErrorType, + SendCallsVariables, + context + > + | undefined + } +> + +export type UseSendCallsReturnType< + config extends Config = Config, + context = unknown, +> = Compute< + UseMutationReturnType< + SendCallsData, + SendCallsErrorType, + SendCallsVariables, + context + > & { + sendCalls: SendCallsMutate + sendCallsAsync: SendCallsMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useSendCalls */ +export function useSendCalls< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseSendCallsParameters = {}, +): UseSendCallsReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = sendCallsMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseSendCallsReturnType + return { + ...result, + sendCalls: mutate as Return['sendCalls'], + sendCallsAsync: mutateAsync as Return['sendCallsAsync'], + } +} diff --git a/packages/react/src/hooks/useSendTransaction.test-d.ts b/packages/react/src/hooks/useSendTransaction.test-d.ts new file mode 100644 index 0000000000..170c1e61e3 --- /dev/null +++ b/packages/react/src/hooks/useSendTransaction.test-d.ts @@ -0,0 +1,78 @@ +import type { SendTransactionErrorType } from '@wagmi/core' +import type { Hash } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { useSendTransaction } from './useSendTransaction.js' + +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, sendTransaction, variables } = + useSendTransaction({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toMatchTypeOf< + { chainId?: number | undefined } | undefined + >() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf< + { chainId?: number | undefined } | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + sendTransaction( + { to: '0x' }, + { + onError(error, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useSendTransaction.test.ts b/packages/react/src/hooks/useSendTransaction.test.ts new file mode 100644 index 0000000000..a2e8977e2f --- /dev/null +++ b/packages/react/src/hooks/useSendTransaction.test.ts @@ -0,0 +1,25 @@ +import { connect, disconnect } from '@wagmi/core' +import { config, transactionHashRegex } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { useSendTransaction } from './useSendTransaction.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useSendTransaction()) + + result.current.sendTransaction({ + to: '0xd2135CfB216b74109775236E36d4b433F1DF507B', + value: parseEther('0.01'), + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toMatch(transactionHashRegex) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useSendTransaction.ts b/packages/react/src/hooks/useSendTransaction.ts new file mode 100644 index 0000000000..8f57b1509c --- /dev/null +++ b/packages/react/src/hooks/useSendTransaction.ts @@ -0,0 +1,79 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { + Config, + ResolvedRegister, + SendTransactionErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type SendTransactionData, + type SendTransactionMutate, + type SendTransactionMutateAsync, + type SendTransactionVariables, + sendTransactionMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseSendTransactionParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + SendTransactionData, + SendTransactionErrorType, + SendTransactionVariables, + context + > + | undefined + } +> + +export type UseSendTransactionReturnType< + config extends Config = Config, + context = unknown, +> = Compute< + UseMutationReturnType< + SendTransactionData, + SendTransactionErrorType, + SendTransactionVariables, + context + > & { + sendTransaction: SendTransactionMutate + sendTransactionAsync: SendTransactionMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useSendTransaction */ +export function useSendTransaction< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseSendTransactionParameters = {}, +): UseSendTransactionReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = sendTransactionMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseSendTransactionReturnType + return { + ...result, + sendTransaction: mutate as Return['sendTransaction'], + sendTransactionAsync: mutateAsync as Return['sendTransactionAsync'], + } +} diff --git a/packages/react/src/hooks/useShowCallsStatus.ts b/packages/react/src/hooks/useShowCallsStatus.ts new file mode 100644 index 0000000000..82a4dd3909 --- /dev/null +++ b/packages/react/src/hooks/useShowCallsStatus.ts @@ -0,0 +1,76 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { + Config, + ResolvedRegister, + ShowCallsStatusErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type ShowCallsStatusData, + type ShowCallsStatusMutate, + type ShowCallsStatusMutateAsync, + type ShowCallsStatusVariables, + showCallsStatusMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseShowCallsStatusParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + ShowCallsStatusData, + ShowCallsStatusErrorType, + ShowCallsStatusVariables, + context + > + | undefined + } +> + +export type UseShowCallsStatusReturnType = Compute< + UseMutationReturnType< + ShowCallsStatusData, + ShowCallsStatusErrorType, + ShowCallsStatusVariables, + context + > & { + showCallsStatus: ShowCallsStatusMutate + showCallsStatusAsync: ShowCallsStatusMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useShowCallsStatus */ +export function useShowCallsStatus< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseShowCallsStatusParameters = {}, +): UseShowCallsStatusReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = showCallsStatusMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseShowCallsStatusReturnType + return { + ...result, + showCallsStatus: mutate as Return['showCallsStatus'], + showCallsStatusAsync: mutateAsync as Return['showCallsStatusAsync'], + } +} diff --git a/packages/react/src/hooks/useSignMessage.test-d.ts b/packages/react/src/hooks/useSignMessage.test-d.ts new file mode 100644 index 0000000000..706c4a51fe --- /dev/null +++ b/packages/react/src/hooks/useSignMessage.test-d.ts @@ -0,0 +1,62 @@ +import type { SignMessageErrorType } from '@wagmi/core' +import type { SignMessageVariables } from '@wagmi/core/query' +import { expectTypeOf, test } from 'vitest' + +import { useSignMessage } from './useSignMessage.js' + +const message = 'hello world' +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, signMessage, variables } = useSignMessage({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toEqualTypeOf() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(data).toEqualTypeOf<`0x${string}`>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf<`0x${string}` | undefined>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf<`0x${string}` | undefined>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + signMessage( + { message }, + { + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(data).toEqualTypeOf<`0x${string}`>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf<`0x${string}` | undefined>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useSignMessage.test.ts b/packages/react/src/hooks/useSignMessage.test.ts new file mode 100644 index 0000000000..aa6dd4a184 --- /dev/null +++ b/packages/react/src/hooks/useSignMessage.test.ts @@ -0,0 +1,43 @@ +import { connect, disconnect, getAccount } from '@wagmi/core' +import { config, privateKey } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { recoverMessageAddress } from 'viem' +import { expect, test } from 'vitest' + +import { privateKeyToAccount } from 'viem/accounts' +import { useSignMessage } from './useSignMessage.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useSignMessage()) + + result.current.signMessage({ message: 'foo bar baz' }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + await expect( + recoverMessageAddress({ + message: 'foo bar baz', + signature: result.current.data!, + }), + ).resolves.toEqual(getAccount(config).address) + + await disconnect(config, { connector }) +}) + +test('behavior: local account', async () => { + const { result } = renderHook(() => useSignMessage()) + + const account = privateKeyToAccount(privateKey) + result.current.signMessage({ account, message: 'foo bar baz' }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + await expect( + recoverMessageAddress({ + message: 'foo bar baz', + signature: result.current.data!, + }), + ).resolves.toEqual(account.address) +}) diff --git a/packages/react/src/hooks/useSignMessage.ts b/packages/react/src/hooks/useSignMessage.ts new file mode 100644 index 0000000000..0bea4bb65f --- /dev/null +++ b/packages/react/src/hooks/useSignMessage.ts @@ -0,0 +1,65 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { SignMessageErrorType } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type SignMessageData, + type SignMessageMutate, + type SignMessageMutateAsync, + type SignMessageVariables, + signMessageMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseSignMessageParameters = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + SignMessageData, + SignMessageErrorType, + SignMessageVariables, + context + > + | undefined + } +> + +export type UseSignMessageReturnType = Compute< + UseMutationReturnType< + SignMessageData, + SignMessageErrorType, + SignMessageVariables, + context + > & { + signMessage: SignMessageMutate + signMessageAsync: SignMessageMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useSignMessage */ +export function useSignMessage( + parameters: UseSignMessageParameters = {}, +): UseSignMessageReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = signMessageMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + return { + ...result, + signMessage: mutate, + signMessageAsync: mutateAsync, + } +} diff --git a/packages/react/src/hooks/useSignTypedData.test-d.ts b/packages/react/src/hooks/useSignTypedData.test-d.ts new file mode 100644 index 0000000000..a51f77cfbb --- /dev/null +++ b/packages/react/src/hooks/useSignTypedData.test-d.ts @@ -0,0 +1,93 @@ +import type { + SignTypedDataErrorType, + SignTypedDataReturnType, +} from '@wagmi/core' +import type { SignTypedDataVariables } from '@wagmi/core/query' +import { typedData } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useSignTypedData } from './useSignTypedData.js' + +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, signTypedData, variables } = useSignTypedData({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toMatchTypeOf() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toMatchTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toMatchTypeOf() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf() + expectTypeOf(context).toEqualTypeOf() + + signTypedData( + { + types: typedData.basic.types, + primaryType: 'Person', + message: { + name: 'Bob', + wallet: '0x', + }, + }, + { + onError(error, variables, context) { + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + types: typeof typedData.basic.types + primaryType: 'Person' + message: { + name: string + wallet: `0x${string}` + } + }>() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + types: typeof typedData.basic.types + primaryType: 'Person' + message: { + name: string + wallet: `0x${string}` + } + }>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf<{ + types: typeof typedData.basic.types + primaryType: 'Person' + message: { + name: string + wallet: `0x${string}` + } + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useSignTypedData.test.ts b/packages/react/src/hooks/useSignTypedData.test.ts new file mode 100644 index 0000000000..3a38daa0da --- /dev/null +++ b/packages/react/src/hooks/useSignTypedData.test.ts @@ -0,0 +1,56 @@ +import { connect, disconnect, getAccount } from '@wagmi/core' +import { config, privateKey, typedData } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { recoverTypedDataAddress } from 'viem' +import { expect, test } from 'vitest' + +import { privateKeyToAccount } from 'viem/accounts' +import { useSignTypedData } from './useSignTypedData.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useSignTypedData()) + + result.current.signTypedData({ + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + await expect( + recoverTypedDataAddress({ + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + signature: result.current.data!, + }), + ).resolves.toEqual(getAccount(config).address) + + await disconnect(config, { connector }) +}) + +test('behavior: local account', async () => { + const { result } = renderHook(() => useSignTypedData()) + + const account = privateKeyToAccount(privateKey) + result.current.signTypedData({ + account, + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + await expect( + recoverTypedDataAddress({ + types: typedData.basic.types, + primaryType: 'Mail', + message: typedData.basic.message, + signature: result.current.data!, + }), + ).resolves.toEqual(account.address) +}) diff --git a/packages/react/src/hooks/useSignTypedData.ts b/packages/react/src/hooks/useSignTypedData.ts new file mode 100644 index 0000000000..080cd04405 --- /dev/null +++ b/packages/react/src/hooks/useSignTypedData.ts @@ -0,0 +1,66 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { SignTypedDataErrorType } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type SignTypedDataData, + type SignTypedDataMutate, + type SignTypedDataMutateAsync, + type SignTypedDataVariables, + signTypedDataMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseSignTypedDataParameters = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + SignTypedDataData, + SignTypedDataErrorType, + SignTypedDataVariables, + context + > + | undefined + } +> + +export type UseSignTypedDataReturnType = Compute< + UseMutationReturnType< + SignTypedDataData, + SignTypedDataErrorType, + SignTypedDataVariables, + context + > & { + signTypedData: SignTypedDataMutate + signTypedDataAsync: SignTypedDataMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useSignTypedData */ +export function useSignTypedData( + parameters: UseSignTypedDataParameters = {}, +): UseSignTypedDataReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = signTypedDataMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseSignTypedDataReturnType + return { + ...result, + signTypedData: mutate as Return['signTypedData'], + signTypedDataAsync: mutateAsync as Return['signTypedDataAsync'], + } +} diff --git a/packages/react/src/hooks/useSimulateContract.test-d.ts b/packages/react/src/hooks/useSimulateContract.test-d.ts new file mode 100644 index 0000000000..8159cdfccf --- /dev/null +++ b/packages/react/src/hooks/useSimulateContract.test-d.ts @@ -0,0 +1,104 @@ +import { abi, type config } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { + type UseSimulateContractParameters, + type UseSimulateContractReturnType, + useSimulateContract, +} from './useSimulateContract.js' + +test('default', () => { + const result = useSimulateContract({ + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + }) + + expectTypeOf(result.data).toMatchTypeOf< + | { + result: boolean + request: { + chainId?: undefined + abi: readonly [ + { + readonly name: 'transferFrom' + readonly type: 'function' + readonly stateMutability: 'nonpayable' + readonly inputs: readonly [ + { readonly type: 'address'; readonly name: 'sender' }, + { readonly type: 'address'; readonly name: 'recipient' }, + { readonly type: 'uint256'; readonly name: 'amount' }, + ] + readonly outputs: readonly [{ type: 'bool' }] + }, + ] + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + } + } + | undefined + >() +}) + +test('select data', () => { + const result = useSimulateContract({ + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + query: { + select(data) { + expectTypeOf(data.result).toEqualTypeOf() + return data.request.args + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf< + readonly [Address, Address, bigint] | undefined + >() +}) + +test('UseSimulateContractParameters', () => { + type Result = UseSimulateContractParameters + expectTypeOf().toMatchTypeOf<{ + functionName?: 'approve' | 'transfer' | 'transferFrom' | undefined + args?: readonly [Address, Address, bigint] | undefined + }>() +}) + +test('UseSimulateContractReturnType', () => { + type Result = UseSimulateContractReturnType< + typeof abi.erc20, + 'transferFrom', + ['0x', '0x', 123n], + typeof config, + 1 + > + expectTypeOf().toMatchTypeOf< + | { + result: boolean + request: { + chainId: number + abi: readonly [ + { + readonly name: 'transferFrom' + readonly type: 'function' + readonly stateMutability: 'nonpayable' + readonly inputs: readonly [ + { readonly type: 'address'; readonly name: 'sender' }, + { readonly type: 'address'; readonly name: 'recipient' }, + { readonly type: 'uint256'; readonly name: 'amount' }, + ] + readonly outputs: readonly [{ type: 'bool' }] + }, + ] + functionName: 'approve' | 'transfer' | 'transferFrom' + args: readonly [Address, Address, bigint] + } + } + | undefined + >() +}) diff --git a/packages/react/src/hooks/useSimulateContract.test.ts b/packages/react/src/hooks/useSimulateContract.test.ts new file mode 100644 index 0000000000..3c785133c9 --- /dev/null +++ b/packages/react/src/hooks/useSimulateContract.test.ts @@ -0,0 +1,95 @@ +import { connect, disconnect } from '@wagmi/core' +import { abi, address, config, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useSimulateContract } from './useSimulateContract.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => + useSimulateContract({ + address: address.wagmiMintExample, + abi: abi.wagmiMintExample, + functionName: 'mint', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "chainId": 1, + "request": { + "abi": [ + { + "inputs": [], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }, + ], + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "args": undefined, + "chainId": 1, + "dataSuffix": undefined, + "functionName": "mint", + }, + "result": undefined, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "simulateContract", + { + "account": { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "type": "json-rpc", + }, + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 1, + "functionName": "mint", + }, + ], + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useSimulateContract()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useSimulateContract.ts b/packages/react/src/hooks/useSimulateContract.ts new file mode 100644 index 0000000000..e17913fb4f --- /dev/null +++ b/packages/react/src/hooks/useSimulateContract.ts @@ -0,0 +1,117 @@ +'use client' + +import type { + Config, + ResolvedRegister, + SimulateContractErrorType, +} from '@wagmi/core' +import { + type SimulateContractData, + type SimulateContractOptions, + type SimulateContractQueryFnData, + type SimulateContractQueryKey, + simulateContractQueryOptions, +} from '@wagmi/core/query' +import type { Abi, ContractFunctionArgs, ContractFunctionName } from 'viem' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' +import { useConnectorClient } from './useConnectorClient.js' + +export type UseSimulateContractParameters< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'nonpayable' | 'payable' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + > = ContractFunctionArgs, + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = SimulateContractData, +> = SimulateContractOptions & + ConfigParameter & + QueryParameter< + SimulateContractQueryFnData, + SimulateContractErrorType, + selectData, + SimulateContractQueryKey + > + +export type UseSimulateContractReturnType< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + 'nonpayable' | 'payable' + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + > = ContractFunctionArgs, + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = SimulateContractData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useSimulateContract */ +export function useSimulateContract< + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs< + abi, + 'nonpayable' | 'payable', + functionName + >, + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = SimulateContractData, +>( + parameters: UseSimulateContractParameters< + abi, + functionName, + args, + config, + chainId, + selectData + > = {} as any, +): UseSimulateContractReturnType< + abi, + functionName, + args, + config, + chainId, + selectData +> { + const { abi, address, connector, functionName, query = {} } = parameters + + const config = useConfig(parameters) + const { data: connectorClient } = useConnectorClient({ + config, + connector, + query: { enabled: parameters.account === undefined }, + }) + const chainId = useChainId({ config }) + + const options = simulateContractQueryOptions< + config, + abi, + functionName, + args, + chainId + >(config, { + ...parameters, + account: parameters.account ?? connectorClient?.account, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean( + abi && address && functionName && (query.enabled ?? true), + ) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useStorageAt.test-d.ts b/packages/react/src/hooks/useStorageAt.test-d.ts new file mode 100644 index 0000000000..bbc37fa03d --- /dev/null +++ b/packages/react/src/hooks/useStorageAt.test-d.ts @@ -0,0 +1,15 @@ +import { expectTypeOf, test } from 'vitest' + +import type { Hex } from 'viem' +import { useStorageAt } from './useStorageAt.js' + +test('select data', () => { + const result = useStorageAt({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useStorageAt.test.ts b/packages/react/src/hooks/useStorageAt.test.ts new file mode 100644 index 0000000000..9386480c93 --- /dev/null +++ b/packages/react/src/hooks/useStorageAt.test.ts @@ -0,0 +1,299 @@ +import { address, chain, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import type { Address } from 'viem' +import { useStorageAt } from './useStorageAt.js' + +test('default', async () => { + const { result } = renderHook(() => + useStorageAt({ + address: address.wagmiMintExample, + slot: '0x0', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "0x7761676d6900000000000000000000000000000000000000000000000000000a", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 1, + "slot": "0x0", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockNumber', async () => { + const { result } = renderHook(() => + useStorageAt({ + address: address.wagmiMintExample, + blockNumber: 16280770n, + slot: '0x0', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "0x7761676d6900000000000000000000000000000000000000000000000000000a", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "blockNumber": 16280770n, + "chainId": 1, + "slot": "0x0", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockTag', async () => { + const { result } = renderHook(() => + useStorageAt({ + address: address.wagmiMintExample, + blockTag: 'safe', + slot: '0x0', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "0x7761676d6900000000000000000000000000000000000000000000000000000a", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "blockTag": "safe", + "chainId": 1, + "slot": "0x0", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useStorageAt({ + address: address.wagmiMintExample, + chainId: chain.optimism.id, + slot: '0x0', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": "0x0000000000000000000000000000000000000000000000000000000000000000", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 10, + "slot": "0x0", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: address: undefined -> defined', async () => { + let contractAddress: Address | undefined = undefined + + const { result, rerender } = renderHook(() => + useStorageAt({ + address: contractAddress, + slot: '0x0', + }), + ) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "getStorageAt", + { + "address": undefined, + "chainId": 1, + "slot": "0x0", + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + contractAddress = address.wagmiMintExample + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + expect(result.current).toMatchInlineSnapshot(` + { + "data": "0x7761676d6900000000000000000000000000000000000000000000000000000a", + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getStorageAt", + { + "address": "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + "chainId": 1, + "slot": "0x0", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useStorageAt()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useStorageAt.ts b/packages/react/src/hooks/useStorageAt.ts new file mode 100644 index 0000000000..3a58376fd4 --- /dev/null +++ b/packages/react/src/hooks/useStorageAt.ts @@ -0,0 +1,57 @@ +'use client' + +import type { + Config, + GetStorageAtErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetStorageAtData, + type GetStorageAtOptions, + type GetStorageAtQueryKey, + getStorageAtQueryOptions, +} from '@wagmi/core/query' +import type { GetStorageAtQueryFnData } from '@wagmi/core/query' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseStorageAtParameters< + config extends Config = Config, + selectData = GetStorageAtData, +> = Compute< + GetStorageAtOptions & + ConfigParameter & + QueryParameter< + GetStorageAtQueryFnData, + GetStorageAtErrorType, + selectData, + GetStorageAtQueryKey + > +> + +export type UseStorageAtReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useStorageAt */ +export function useStorageAt< + config extends Config = ResolvedRegister['config'], + selectData = GetStorageAtData, +>( + parameters: UseStorageAtParameters = {}, +): UseStorageAtReturnType { + const { address, slot, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getStorageAtQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && slot && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useSwitchAccount.test-d.ts b/packages/react/src/hooks/useSwitchAccount.test-d.ts new file mode 100644 index 0000000000..f7d97355a3 --- /dev/null +++ b/packages/react/src/hooks/useSwitchAccount.test-d.ts @@ -0,0 +1,87 @@ +import type { Connector, SwitchAccountErrorType } from '@wagmi/core' +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import type { Address } from 'viem' +import { useSwitchAccount } from './useSwitchAccount.js' + +const connector = config.connectors[0]! +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, switchAccount, variables } = useSwitchAccount({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toEqualTypeOf<{ connector: Connector }>() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ connector: Connector }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ connector: Connector }>() + expectTypeOf(data).toEqualTypeOf<{ + accounts: readonly [Address, ...Address[]] + chainId: number + }>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + } + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf<{ connector: Connector }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + } + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf<{ connector: Connector } | undefined>() + expectTypeOf(context).toEqualTypeOf() + + switchAccount( + { connector }, + { + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ connector: Connector }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ connector: Connector }>() + expectTypeOf(data).toEqualTypeOf<{ + accounts: readonly [Address, ...Address[]] + chainId: number + }>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf< + | { + accounts: readonly [Address, ...Address[]] + chainId: number + } + | undefined + >() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf<{ connector: Connector }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useSwitchAccount.test.ts b/packages/react/src/hooks/useSwitchAccount.test.ts new file mode 100644 index 0000000000..5461d08213 --- /dev/null +++ b/packages/react/src/hooks/useSwitchAccount.test.ts @@ -0,0 +1,44 @@ +import { connect, disconnect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useAccount } from './useAccount.js' +import { useSwitchAccount } from './useSwitchAccount.js' + +const connector1 = config.connectors[0]! +const connector2 = config.connectors[1]! + +test('default', async () => { + await connect(config, { connector: connector2 }) + await connect(config, { connector: connector1 }) + + const { result } = renderHook(() => ({ + useAccount: useAccount(), + useSwitchAccount: useSwitchAccount(), + })) + + const address1 = result.current.useAccount.address + expect(address1).toBeDefined() + + result.current.useSwitchAccount.switchAccount({ connector: connector2 }) + await waitFor(() => + expect(result.current.useSwitchAccount.isSuccess).toBeTruthy(), + ) + + const address2 = result.current.useAccount.address + expect(address2).toBeDefined() + expect(address1).not.toBe(address2) + + result.current.useSwitchAccount.switchAccount({ connector: connector1 }) + await waitFor(() => + expect(result.current.useSwitchAccount.isSuccess).toBeTruthy(), + ) + + const address3 = result.current.useAccount.address + expect(address3).toBeDefined() + expect(address1).toBe(address3) + + await disconnect(config, { connector: connector1 }) + await disconnect(config, { connector: connector2 }) +}) diff --git a/packages/react/src/hooks/useSwitchAccount.ts b/packages/react/src/hooks/useSwitchAccount.ts new file mode 100644 index 0000000000..e9dc305545 --- /dev/null +++ b/packages/react/src/hooks/useSwitchAccount.ts @@ -0,0 +1,84 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { + Config, + Connector, + ResolvedRegister, + SwitchAccountErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type SwitchAccountData, + type SwitchAccountMutate, + type SwitchAccountMutateAsync, + type SwitchAccountVariables, + switchAccountMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' +import { useConnections } from './useConnections.js' + +export type UseSwitchAccountParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + SwitchAccountData, + SwitchAccountErrorType, + SwitchAccountVariables, + context + > + | undefined + } +> + +export type UseSwitchAccountReturnType< + config extends Config = Config, + context = unknown, +> = Compute< + UseMutationReturnType< + SwitchAccountData, + SwitchAccountErrorType, + SwitchAccountVariables, + context + > & { + connectors: readonly Connector[] + switchAccount: SwitchAccountMutate + switchAccountAsync: SwitchAccountMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useSwitchAccount */ +export function useSwitchAccount< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseSwitchAccountParameters = {}, +): UseSwitchAccountReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = switchAccountMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + return { + ...result, + connectors: useConnections({ config }).map( + (connection) => connection.connector, + ), + switchAccount: mutate, + switchAccountAsync: mutateAsync, + } +} diff --git a/packages/react/src/hooks/useSwitchChain.test-d.ts b/packages/react/src/hooks/useSwitchChain.test-d.ts new file mode 100644 index 0000000000..07098c7724 --- /dev/null +++ b/packages/react/src/hooks/useSwitchChain.test-d.ts @@ -0,0 +1,118 @@ +import type { Connector, SwitchChainErrorType } from '@wagmi/core' +import type { Chain } from '@wagmi/core/chains' +import type { Compute, ExactPartial } from '@wagmi/core/internal' +import { chain } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import type { AddEthereumChainParameter } from 'viem' +import { useSwitchChain } from './useSwitchChain.js' + +const chainId = chain.mainnet.id +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { chains, context, data, error, switchChain, variables } = + useSwitchChain({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toEqualTypeOf<{ + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + connector?: Connector | undefined + }>() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + connector?: Connector | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + connector?: Connector | undefined + }>() + expectTypeOf(data).toEqualTypeOf>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf | undefined>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf<{ + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + connector?: Connector | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(chains).toEqualTypeOf() + expectTypeOf(data).toEqualTypeOf | undefined>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf< + | { + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + connector?: Connector | undefined + } + | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + switchChain( + { chainId }, + { + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + connector?: Connector | undefined + }>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf<{ + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + connector?: Connector | undefined + }>() + expectTypeOf(data).toEqualTypeOf>() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf | undefined>() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf<{ + addEthereumChainParameter?: + | ExactPartial> + | undefined + chainId: number + connector?: Connector | undefined + }>() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useSwitchChain.test.ts b/packages/react/src/hooks/useSwitchChain.test.ts new file mode 100644 index 0000000000..1fe0ca46b2 --- /dev/null +++ b/packages/react/src/hooks/useSwitchChain.test.ts @@ -0,0 +1,114 @@ +import { connect, disconnect } from '@wagmi/core' +import { chain, config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useAccount } from './useAccount.js' +import { useSwitchChain } from './useSwitchChain.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => ({ + useAccount: useAccount(), + useSwitchChain: useSwitchChain(), + })) + + const chainId1 = result.current.useAccount.chainId + expect(chainId1).toBeDefined() + + result.current.useSwitchChain.switchChain({ chainId: chain.mainnet2.id }) + await waitFor(() => + expect(result.current.useSwitchChain.isSuccess).toBeTruthy(), + ) + + const chainId2 = result.current.useAccount.chainId + expect(chainId2).toBeDefined() + expect(chainId1).not.toBe(chainId2) + + result.current.useSwitchChain.switchChain({ chainId: chain.mainnet.id }) + await waitFor(() => + expect(result.current.useSwitchChain.isSuccess).toBeTruthy(), + ) + + const chainId3 = result.current.useAccount.chainId + expect(chainId3).toBeDefined() + expect(chainId1).toBe(chainId3) + + await disconnect(config, { connector }) +}) + +test('behavior: chains updates', () => { + const { result, rerender } = renderHook(() => useSwitchChain()) + + const chains = result.current.chains + expect( + result.current.chains.map(({ id, name }) => ({ + id, + name, + })), + ).toMatchInlineSnapshot(` + [ + { + "id": 1, + "name": "Ethereum", + }, + { + "id": 456, + "name": "Ethereum", + }, + { + "id": 10, + "name": "OP Mainnet", + }, + ] + `) + + config._internal.chains.setState([chain.mainnet, chain.mainnet2]) + rerender() + + expect( + result.current.chains.map(({ id, name }) => ({ + id, + name, + })), + ).toMatchInlineSnapshot(` + [ + { + "id": 1, + "name": "Ethereum", + }, + { + "id": 456, + "name": "Ethereum", + }, + ] + `) + + config._internal.chains.setState(chains) + rerender() + + expect( + result.current.chains.map(({ id, name }) => ({ + id, + name, + })), + ).toMatchInlineSnapshot(` + [ + { + "id": 1, + "name": "Ethereum", + }, + { + "id": 456, + "name": "Ethereum", + }, + { + "id": 10, + "name": "OP Mainnet", + }, + ] + `) +}) diff --git a/packages/react/src/hooks/useSwitchChain.ts b/packages/react/src/hooks/useSwitchChain.ts new file mode 100644 index 0000000000..97ecf4ce9e --- /dev/null +++ b/packages/react/src/hooks/useSwitchChain.ts @@ -0,0 +1,82 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { + Config, + ResolvedRegister, + SwitchChainErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type SwitchChainData, + type SwitchChainMutate, + type SwitchChainMutateAsync, + type SwitchChainVariables, + switchChainMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useChains } from './useChains.js' +import { useConfig } from './useConfig.js' + +export type UseSwitchChainParameters< + config extends Config = Config, + context = unknown, +> = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + SwitchChainData, + SwitchChainErrorType, + SwitchChainVariables, + context + > + | undefined + } +> + +export type UseSwitchChainReturnType< + config extends Config = Config, + context = unknown, +> = Compute< + UseMutationReturnType< + SwitchChainData, + SwitchChainErrorType, + SwitchChainVariables, + context + > & { + chains: config['chains'] + switchChain: SwitchChainMutate + switchChainAsync: SwitchChainMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useSwitchChain */ +export function useSwitchChain< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseSwitchChainParameters = {}, +): UseSwitchChainReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = switchChainMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseSwitchChainReturnType + return { + ...result, + chains: useChains({ config }) as unknown as config['chains'], + switchChain: mutate as Return['switchChain'], + switchChainAsync: mutateAsync as Return['switchChainAsync'], + } +} diff --git a/packages/react/src/hooks/useSyncExternalStoreWithTracked.test.tsx b/packages/react/src/hooks/useSyncExternalStoreWithTracked.test.tsx new file mode 100644 index 0000000000..e0b1e71a94 --- /dev/null +++ b/packages/react/src/hooks/useSyncExternalStoreWithTracked.test.tsx @@ -0,0 +1,275 @@ +import { fireEvent, screen } from '@testing-library/react' +import { act, cleanup, render, renderHook } from '@wagmi/test/react' +import React from 'react' +import * as ReactDOM from 'react-dom' +import { afterEach, expect, test } from 'vitest' + +import { useSyncExternalStoreWithTracked } from './useSyncExternalStoreWithTracked.js' + +function createExternalStore(initialState: state) { + const listeners = new Set<() => void>() + let currentState = initialState + return { + set(updater: (state: state) => state) { + currentState = updater(currentState) + ReactDOM.unstable_batchedUpdates(() => { + for (const listener of listeners) { + listener() + } + }) + }, + subscribe(listener: () => void) { + listeners.add(listener) + return () => listeners.delete(listener) + }, + getState() { + return currentState + }, + } +} + +function useExternalStore( + store: ReturnType, + cb: (state: any) => void, +) { + const state = useSyncExternalStoreWithTracked( + store.subscribe, + store.getState, + store.getState, + ) + cb(state) + return state as any +} + +afterEach(() => { + cleanup() +}) + +test('rerenders only when the tracked value changes', async () => { + const externalStore = createExternalStore({ + foo: 'bar', + gm: 'wagmi', + isGonnaMakeIt: false, + }) + + const renders: any[] = [] + + renderHook(() => { + const { gm } = useExternalStore(externalStore, (state) => { + renders.push(state) + }) + + return gm + }) + + act(() => { + externalStore.set((x) => ({ ...x, foo: 'baz', isGonnaMakeIt: true })) + }) + + expect(renders).toMatchInlineSnapshot(` + [ + { + "foo": "bar", + "gm": "wagmi", + "isGonnaMakeIt": false, + }, + ] + `) + + act(() => { + externalStore.set((x) => ({ ...x, gm: 'ngmi' })) + }) + + expect(renders).toMatchInlineSnapshot(` + [ + { + "foo": "bar", + "gm": "wagmi", + "isGonnaMakeIt": false, + }, + { + "foo": "baz", + "gm": "ngmi", + "isGonnaMakeIt": true, + }, + ] + `) +}) + +test('rerenders when all values are being tracked', async () => { + const externalStore = createExternalStore({ + foo: 'bar', + gm: 'wagmi', + isGonnaMakeIt: false, + }) + + const renders: any[] = [] + + renderHook(() => { + const { foo, gm, isGonnaMakeIt } = useExternalStore( + externalStore, + (state) => { + renders.push(state) + }, + ) + + return { + foo, + gm, + isGonnaMakeIt, + } + }) + + act(() => { + externalStore.set((x) => ({ ...x, isGonnaMakeIt: true })) + }) + + expect(renders).toMatchInlineSnapshot(` + [ + { + "foo": "bar", + "gm": "wagmi", + "isGonnaMakeIt": false, + }, + { + "foo": "bar", + "gm": "wagmi", + "isGonnaMakeIt": true, + }, + ] + `) +}) + +test('rerenders when no values are being tracked', async () => { + const externalStore = createExternalStore({ + foo: 'bar', + gm: 'wagmi', + isGonnaMakeIt: false, + }) + + const renders: any[] = [] + + renderHook(() => { + useExternalStore(externalStore, (state) => { + renders.push(state) + }) + }) + + act(() => { + externalStore.set((x) => ({ ...x, isGonnaMakeIt: true })) + }) + + expect(renders).toMatchInlineSnapshot(` + [ + { + "foo": "bar", + "gm": "wagmi", + "isGonnaMakeIt": false, + }, + { + "foo": "bar", + "gm": "wagmi", + "isGonnaMakeIt": true, + }, + ] + `) +}) + +test('store object reference is stable across rerenders', async () => { + const externalStore = createExternalStore({ + foo: 'bar', + gm: 'wagmi', + isGonnaMakeIt: false, + }) + + let childRenderCount = 0 + const MemoComponent = React.memo((props: { store: any }) => { + childRenderCount++ + return
{props.store.isGonnaMakeIt}
+ }) + + const renders: any[] = [] + + function Test() { + const store = useExternalStore(externalStore, (state) => { + renders.push(state) + }) + const [, rerender] = React.useState(0) + + return ( + <> + + + + ) + } + + render() + + const forceRerenderBtn = screen.getByRole('button') + expect(childRenderCount).toBe(1) + expect(renders.length).toBe(1) + + // updating parent state, child should not rerender + fireEvent.click(forceRerenderBtn) + expect(childRenderCount).toBe(1) + expect(renders.length).toBe(2) + + // child and parent both rerender when store changes + act(() => { + externalStore.set((x) => ({ ...x, isGonnaMakeIt: true })) + }) + expect(childRenderCount).toBe(2) + expect(renders.length).toBe(3) +}) + +test('array', async () => { + const externalStore = createExternalStore(['foo']) + + const renders: any[] = [] + + renderHook(() => { + const array = useExternalStore(externalStore, (state) => { + renders.push(state) + }) + + return array + }) + + act(() => { + externalStore.set((x) => [...x, 'bar']) + }) + + expect(renders).toMatchInlineSnapshot(` + [ + [ + "foo", + ], + [ + "foo", + "bar", + ], + ] + `) + + act(() => { + externalStore.set((x) => [...x, 'baz']) + }) + + expect(renders).toMatchInlineSnapshot(` + [ + [ + "foo", + ], + [ + "foo", + "bar", + ], + [ + "foo", + "bar", + "baz", + ], + ] + `) +}) diff --git a/packages/react/src/hooks/useSyncExternalStoreWithTracked.ts b/packages/react/src/hooks/useSyncExternalStoreWithTracked.ts new file mode 100644 index 0000000000..4e372a556d --- /dev/null +++ b/packages/react/src/hooks/useSyncExternalStoreWithTracked.ts @@ -0,0 +1,67 @@ +'use client' + +import { deepEqual } from '@wagmi/core/internal' +import { useMemo, useRef } from 'react' +import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector.js' + +const isPlainObject = (obj: unknown) => + typeof obj === 'object' && !Array.isArray(obj) + +export function useSyncExternalStoreWithTracked< + snapshot extends selection, + selection = snapshot, +>( + subscribe: (onStoreChange: () => void) => () => void, + getSnapshot: () => snapshot, + getServerSnapshot: undefined | null | (() => snapshot) = getSnapshot, + isEqual: (a: selection, b: selection) => boolean = deepEqual, +) { + const trackedKeys = useRef([]) + const result = useSyncExternalStoreWithSelector( + subscribe, + getSnapshot, + getServerSnapshot, + (x) => x, + (a, b) => { + if (isPlainObject(a) && isPlainObject(b) && trackedKeys.current.length) { + for (const key of trackedKeys.current) { + const equal = isEqual( + (a as { [_a: string]: any })[key], + (b as { [_b: string]: any })[key], + ) + if (!equal) return false + } + return true + } + return isEqual(a, b) + }, + ) + + return useMemo(() => { + if (isPlainObject(result)) { + const trackedResult = { ...result } + let properties = {} + for (const [key, value] of Object.entries( + trackedResult as { [key: string]: any }, + )) { + properties = { + ...properties, + [key]: { + configurable: false, + enumerable: true, + get: () => { + if (!trackedKeys.current.includes(key)) { + trackedKeys.current.push(key) + } + return value + }, + }, + } + } + Object.defineProperties(trackedResult, properties) + return trackedResult + } + + return result + }, [result]) +} diff --git a/packages/react/src/hooks/useToken.test-d.ts b/packages/react/src/hooks/useToken.test-d.ts new file mode 100644 index 0000000000..2018951919 --- /dev/null +++ b/packages/react/src/hooks/useToken.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useToken } from './useToken.js' + +test('select data', () => { + const result = useToken({ + query: { + select(data) { + return data?.name + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useToken.test.ts b/packages/react/src/hooks/useToken.test.ts new file mode 100644 index 0000000000..6023797532 --- /dev/null +++ b/packages/react/src/hooks/useToken.test.ts @@ -0,0 +1,59 @@ +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useToken } from './useToken.js' + +test('default', async () => { + const { result } = renderHook(() => + useToken({ + address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", + "decimals": 18, + "name": "Uniswap", + "symbol": "UNI", + "totalSupply": { + "formatted": "1000000000", + "value": 1000000000000000000000000000n, + }, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "token", + { + "address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useToken.ts b/packages/react/src/hooks/useToken.ts new file mode 100644 index 0000000000..1d43912f72 --- /dev/null +++ b/packages/react/src/hooks/useToken.ts @@ -0,0 +1,60 @@ +'use client' + +import type { Config, GetTokenErrorType, ResolvedRegister } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetTokenData, + type GetTokenOptions, + type GetTokenQueryFnData, + type GetTokenQueryKey, + getTokenQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseTokenParameters< + config extends Config = Config, + selectData = GetTokenData, +> = Compute< + GetTokenOptions & + ConfigParameter & + QueryParameter< + GetTokenQueryFnData, + GetTokenErrorType, + selectData, + GetTokenQueryKey + > +> + +export type UseTokenReturnType = UseQueryReturnType< + selectData, + GetTokenErrorType +> + +/** + * @deprecated + * + * https://wagmi.sh/react/api/hooks/useToken + */ +export function useToken< + config extends Config = ResolvedRegister['config'], + selectData = GetTokenData, +>( + parameters: UseTokenParameters = {}, +): UseTokenReturnType { + const { address, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getTokenQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useTransaction.test-d.ts b/packages/react/src/hooks/useTransaction.test-d.ts new file mode 100644 index 0000000000..211efa7f6c --- /dev/null +++ b/packages/react/src/hooks/useTransaction.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useTransaction } from './useTransaction.js' + +test('select data', () => { + const result = useTransaction({ + query: { + select(data) { + return data?.nonce + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useTransaction.test.ts b/packages/react/src/hooks/useTransaction.test.ts new file mode 100644 index 0000000000..190cffb1fe --- /dev/null +++ b/packages/react/src/hooks/useTransaction.test.ts @@ -0,0 +1,72 @@ +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useTransaction } from './useTransaction.js' + +test('default', async () => { + const { result } = renderHook(() => + useTransaction({ + hash: '0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "accessList": [], + "blockHash": "0xd725a38b51e5ceec8c5f6c9ccfdb2cc423af993bb650af5eedca5e4be7156ba7", + "blockNumber": 15189204n, + "chainId": 1, + "from": "0xa0cf798816d4b9b9866b5330eea46a18382f251e", + "gas": 21000n, + "gasPrice": 9371645552n, + "hash": "0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30", + "input": "0x", + "maxFeePerGas": 13644824566n, + "maxPriorityFeePerGas": 1500000000n, + "nonce": 86, + "r": "0x40174f9a38df876c1a7ce2587848819d4082ccd6d67a88aa5cabe59bf594e14f", + "s": "0x7c0c82f62a8a5a9b0e9cf30a54a72fdae8fc54b5b79ddafef0acd30e94e83872", + "to": "0xd2135cfb216b74109775236e36d4b433f1df507b", + "transactionIndex": 144, + "type": "eip1559", + "typeHex": "0x2", + "v": 0n, + "value": 100000000000000000n, + "yParity": 0, + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "transaction", + { + "chainId": 1, + "hash": "0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) diff --git a/packages/react/src/hooks/useTransaction.ts b/packages/react/src/hooks/useTransaction.ts new file mode 100644 index 0000000000..6cc920e873 --- /dev/null +++ b/packages/react/src/hooks/useTransaction.ts @@ -0,0 +1,72 @@ +'use client' + +import type { + Config, + GetTransactionErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetTransactionData, + type GetTransactionOptions, + type GetTransactionQueryFnData, + type GetTransactionQueryKey, + getTransactionQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseTransactionParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetTransactionData, +> = Compute< + GetTransactionOptions & + ConfigParameter & + QueryParameter< + GetTransactionQueryFnData, + GetTransactionErrorType, + selectData, + GetTransactionQueryKey + > +> + +export type UseTransactionReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetTransactionData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useTransaction */ +export function useTransaction< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetTransactionData, +>( + parameters: UseTransactionParameters = {}, +): UseTransactionReturnType { + const { blockHash, blockNumber, blockTag, hash, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getTransactionQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean( + !(blockHash && blockNumber && blockTag && hash) && (query.enabled ?? true), + ) + + return useQuery({ + ...(query as any), + ...options, + enabled, + }) as UseTransactionReturnType +} diff --git a/packages/react/src/hooks/useTransactionConfirmations.test-d.ts b/packages/react/src/hooks/useTransactionConfirmations.test-d.ts new file mode 100644 index 0000000000..e2bbcb4266 --- /dev/null +++ b/packages/react/src/hooks/useTransactionConfirmations.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useTransactionConfirmations } from './useTransactionConfirmations.js' + +test('select data', () => { + const result = useTransactionConfirmations({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useTransactionConfirmations.test.ts b/packages/react/src/hooks/useTransactionConfirmations.test.ts new file mode 100644 index 0000000000..4f4a79c251 --- /dev/null +++ b/packages/react/src/hooks/useTransactionConfirmations.test.ts @@ -0,0 +1,215 @@ +import { config, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import type { Hash } from 'viem' +import { expect, test } from 'vitest' + +import { getTransactionReceipt } from '@wagmi/core' +import { useTransactionConfirmations } from './useTransactionConfirmations.js' + +test('default', async () => { + const { result } = renderHook(() => + useTransactionConfirmations({ + hash: '0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('bigint') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "transactionConfirmations", + { + "chainId": 1, + "hash": "0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: transactionReceipt', async () => { + const transactionReceipt = await getTransactionReceipt(config, { + hash: '0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30', + }) + + const { result } = renderHook(() => + useTransactionConfirmations({ + transactionReceipt, + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('bigint') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "transactionConfirmations", + { + "chainId": 1, + "transactionReceipt": { + "blockHash": "0xd725a38b51e5ceec8c5f6c9ccfdb2cc423af993bb650af5eedca5e4be7156ba7", + "blockNumber": 15189204n, + "contractAddress": null, + "cumulativeGasUsed": 12949744n, + "effectiveGasPrice": 9371645552n, + "from": "0xa0cf798816d4b9b9866b5330eea46a18382f251e", + "gasUsed": 21000n, + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": "success", + "to": "0xd2135cfb216b74109775236e36d4b433f1df507b", + "transactionHash": "0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30", + "transactionIndex": 144, + "type": "eip1559", + }, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: hash: undefined -> defined', async () => { + let hash: Hash | undefined = undefined + + const { result, rerender } = renderHook(() => + useTransactionConfirmations({ + hash, + }), + ) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "transactionConfirmations", + { + "chainId": 1, + "hash": undefined, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + hash = '0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30' + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('bigint') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "transactionConfirmations", + { + "chainId": 1, + "hash": "0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useTransactionConfirmations()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useTransactionConfirmations.ts b/packages/react/src/hooks/useTransactionConfirmations.ts new file mode 100644 index 0000000000..c8e9ddec3f --- /dev/null +++ b/packages/react/src/hooks/useTransactionConfirmations.ts @@ -0,0 +1,66 @@ +'use client' + +import type { + Config, + GetTransactionConfirmationsErrorType, + ResolvedRegister, +} from '@wagmi/core' +import { + type GetTransactionConfirmationsData, + type GetTransactionConfirmationsOptions, + type GetTransactionConfirmationsQueryFnData, + type GetTransactionConfirmationsQueryKey, + getTransactionConfirmationsQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseTransactionConfirmationsParameters< + config extends Config = Config, + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = GetTransactionConfirmationsData, +> = GetTransactionConfirmationsOptions & + ConfigParameter & + QueryParameter< + GetTransactionConfirmationsQueryFnData, + GetTransactionConfirmationsErrorType, + selectData, + GetTransactionConfirmationsQueryKey + > + +export type UseTransactionConfirmationsReturnType< + selectData = GetTransactionConfirmationsData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useTransactionConfirmations */ +export function useTransactionConfirmations< + config extends Config = ResolvedRegister['config'], + chainId extends config['chains'][number]['id'] | undefined = undefined, + selectData = GetTransactionConfirmationsData, +>( + parameters: UseTransactionConfirmationsParameters< + config, + chainId, + selectData + > = {} as any, +): UseTransactionConfirmationsReturnType { + const { hash, transactionReceipt, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getTransactionConfirmationsQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean( + !(hash && transactionReceipt) && + (hash || transactionReceipt) && + (query.enabled ?? true), + ) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useTransactionCount.test-d.ts b/packages/react/src/hooks/useTransactionCount.test-d.ts new file mode 100644 index 0000000000..069d10b9cd --- /dev/null +++ b/packages/react/src/hooks/useTransactionCount.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useTransactionCount } from './useTransactionCount.js' + +test('select data', () => { + const result = useTransactionCount({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useTransactionCount.test.ts b/packages/react/src/hooks/useTransactionCount.test.ts new file mode 100644 index 0000000000..13a241ea88 --- /dev/null +++ b/packages/react/src/hooks/useTransactionCount.test.ts @@ -0,0 +1,238 @@ +import { accounts, chain, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import type { Address } from 'viem' +import { expect, test } from 'vitest' + +import { useTransactionCount } from './useTransactionCount.js' + +const address = accounts[0] + +test('default', async () => { + const { result } = renderHook(() => useTransactionCount({ address })) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "transactionCount", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useTransactionCount({ address, chainId: chain.mainnet2.id }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "transactionCount", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 456, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockNumber', async () => { + const { result } = renderHook(() => + useTransactionCount({ address, blockNumber: 13677382n }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "transactionCount", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockNumber": 13677382n, + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: address: undefined -> defined', async () => { + let address: Address | undefined = undefined + + const { result, rerender } = renderHook(() => + useTransactionCount({ address }), + ) + + { + const { data, ...rest } = result.current + expect(data).toBeTypeOf('undefined') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "transactionCount", + { + "address": undefined, + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + } + + address = accounts[0] + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, ...rest } = result.current + expect(data).toBeTypeOf('number') + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "transactionCount", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useTransactionCount()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useTransactionCount.ts b/packages/react/src/hooks/useTransactionCount.ts new file mode 100644 index 0000000000..341536aea7 --- /dev/null +++ b/packages/react/src/hooks/useTransactionCount.ts @@ -0,0 +1,59 @@ +'use client' + +import type { + Config, + GetTransactionCountErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import type { GetTransactionCountQueryFnData } from '@wagmi/core/query' +import { + type GetTransactionCountData, + type GetTransactionCountOptions, + type GetTransactionCountQueryKey, + getTransactionCountQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseTransactionCountParameters< + config extends Config = Config, + selectData = GetTransactionCountData, +> = Compute< + GetTransactionCountOptions & + ConfigParameter & + QueryParameter< + GetTransactionCountQueryFnData, + GetTransactionCountErrorType, + selectData, + GetTransactionCountQueryKey + > +> + +export type UseTransactionCountReturnType< + selectData = GetTransactionCountData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useTransactionCount */ +export function useTransactionCount< + config extends Config = ResolvedRegister['config'], + selectData = GetTransactionCountData, +>( + parameters: UseTransactionCountParameters = {}, +): UseTransactionCountReturnType { + const { address, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getTransactionCountQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(address && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useTransactionReceipt.test-d.ts b/packages/react/src/hooks/useTransactionReceipt.test-d.ts new file mode 100644 index 0000000000..af2785934a --- /dev/null +++ b/packages/react/src/hooks/useTransactionReceipt.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useTransactionReceipt } from './useTransactionReceipt.js' + +test('select data', () => { + const result = useTransactionReceipt({ + query: { + select(data) { + return data?.blockNumber + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useTransactionReceipt.test.ts b/packages/react/src/hooks/useTransactionReceipt.test.ts new file mode 100644 index 0000000000..fd2e24d76b --- /dev/null +++ b/packages/react/src/hooks/useTransactionReceipt.test.ts @@ -0,0 +1,237 @@ +import { chain, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import type { Hash } from 'viem' +import { expect, test } from 'vitest' +import { useTransactionReceipt } from './useTransactionReceipt.js' + +test('default', async () => { + const { result } = renderHook(() => + useTransactionReceipt({ + hash: '0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "blockHash": "0xb932f77cf770d1d1c8f861153eec1e990f5d56b6ffdb4ac06aef3cca51ef37d4", + "blockNumber": 16280769n, + "contractAddress": null, + "cumulativeGasUsed": 21000n, + "effectiveGasPrice": 33427926161n, + "from": "0x043022ef9fca1066024d19d681e2ccf44ff90de3", + "gasUsed": 21000n, + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": "success", + "to": "0x318a5fb4f1604fc46375a1db9a9018b6e423b345", + "transactionHash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + "transactionIndex": 0, + "type": "legacy", + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getTransactionReceipt", + { + "chainId": 1, + "hash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useTransactionReceipt({ + chainId: chain.mainnet2.id, + hash: '0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "blockHash": "0xb932f77cf770d1d1c8f861153eec1e990f5d56b6ffdb4ac06aef3cca51ef37d4", + "blockNumber": 16280769n, + "contractAddress": null, + "cumulativeGasUsed": 21000n, + "effectiveGasPrice": 33427926161n, + "from": "0x043022ef9fca1066024d19d681e2ccf44ff90de3", + "gasUsed": 21000n, + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": "success", + "to": "0x318a5fb4f1604fc46375a1db9a9018b6e423b345", + "transactionHash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + "transactionIndex": 0, + "type": "legacy", + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getTransactionReceipt", + { + "chainId": 456, + "hash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: hash: undefined -> defined', async () => { + let hash: Hash | undefined = undefined + + const { result, rerender } = renderHook(() => + useTransactionReceipt({ + hash, + }), + ) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "getTransactionReceipt", + { + "chainId": 1, + "hash": undefined, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + hash = '0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871' + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": { + "blockHash": "0xb932f77cf770d1d1c8f861153eec1e990f5d56b6ffdb4ac06aef3cca51ef37d4", + "blockNumber": 16280769n, + "contractAddress": null, + "cumulativeGasUsed": 21000n, + "effectiveGasPrice": 33427926161n, + "from": "0x043022ef9fca1066024d19d681e2ccf44ff90de3", + "gasUsed": 21000n, + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": "success", + "to": "0x318a5fb4f1604fc46375a1db9a9018b6e423b345", + "transactionHash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + "transactionIndex": 0, + "type": "legacy", + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "getTransactionReceipt", + { + "chainId": 1, + "hash": "0xbf7d27700d053765c9638d3b9d39eb3c56bfc48377583e8be483d61f9f18a871", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useTransactionReceipt()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useTransactionReceipt.ts b/packages/react/src/hooks/useTransactionReceipt.ts new file mode 100644 index 0000000000..f29ab6204e --- /dev/null +++ b/packages/react/src/hooks/useTransactionReceipt.ts @@ -0,0 +1,69 @@ +'use client' + +import type { + Config, + GetTransactionReceiptErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type GetTransactionReceiptData, + type GetTransactionReceiptOptions, + type GetTransactionReceiptQueryKey, + getTransactionReceiptQueryOptions, +} from '@wagmi/core/query' +import type { GetTransactionReceiptQueryFnData } from '@wagmi/core/query' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseTransactionReceiptParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetTransactionReceiptData, +> = Compute< + GetTransactionReceiptOptions & + ConfigParameter & + QueryParameter< + GetTransactionReceiptQueryFnData, + GetTransactionReceiptErrorType, + selectData, + GetTransactionReceiptQueryKey + > +> + +export type UseTransactionReceiptReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetTransactionReceiptData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useTransactionReceipt */ +export function useTransactionReceipt< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetTransactionReceiptData, +>( + parameters: UseTransactionReceiptParameters = {}, +): UseTransactionReceiptReturnType { + const { hash, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = getTransactionReceiptQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(hash && (query.enabled ?? true)) + + return useQuery({ + ...(query as any), + ...options, + enabled, + }) as UseTransactionReceiptReturnType +} diff --git a/packages/react/src/hooks/useVerifyMessage.test-d.ts b/packages/react/src/hooks/useVerifyMessage.test-d.ts new file mode 100644 index 0000000000..5a9ad7e257 --- /dev/null +++ b/packages/react/src/hooks/useVerifyMessage.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useVerifyMessage } from './useVerifyMessage.js' + +test('select data', () => { + const result = useVerifyMessage({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useVerifyMessage.test.ts b/packages/react/src/hooks/useVerifyMessage.test.ts new file mode 100644 index 0000000000..244b6331f7 --- /dev/null +++ b/packages/react/src/hooks/useVerifyMessage.test.ts @@ -0,0 +1,318 @@ +import { accounts, chain, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import type { Hex } from 'viem' +import { useVerifyMessage } from './useVerifyMessage.js' + +const address = accounts[0] + +test('default', async () => { + const { result } = renderHook(() => + useVerifyMessage({ + address, + message: 'This is a test message for viem!', + signature: + '0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": true, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "message": "This is a test message for viem!", + "signature": "0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: chainId', async () => { + const { result } = renderHook(() => + useVerifyMessage({ + chainId: chain.mainnet2.id, + address, + message: 'This is a test message for viem!', + signature: + '0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": true, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 456, + "message": "This is a test message for viem!", + "signature": "0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockNumber', async () => { + const { result } = renderHook(() => + useVerifyMessage({ + blockNumber: 12345678n, + address, + message: 'This is a test message for viem!', + signature: + '0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": true, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockNumber": 12345678n, + "chainId": 1, + "message": "This is a test message for viem!", + "signature": "0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('parameters: blockTag', async () => { + const { result } = renderHook(() => + useVerifyMessage({ + blockTag: 'pending', + address, + message: 'This is a test message for viem!', + signature: + '0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": true, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "blockTag": "pending", + "chainId": 1, + "message": "This is a test message for viem!", + "signature": "0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: signature: undefined -> defined', async () => { + let signature: Hex | undefined = undefined + + const { result, rerender } = renderHook(() => + useVerifyMessage({ + address, + message: 'This is a test message for viem!', + signature, + }), + ) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "message": "This is a test message for viem!", + "signature": undefined, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + signature = + '0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b' + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": true, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyMessage", + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "chainId": 1, + "message": "This is a test message for viem!", + "signature": "0xc4c7f2820177020d66d5fd00d084cdd3f575a868c059c29a2d7f23398d04819709a14f83d98b446dda539ca5dcb87d75aa3340eb15e66d67606850622a3420f61b", + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useVerifyMessage()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useVerifyMessage.ts b/packages/react/src/hooks/useVerifyMessage.ts new file mode 100644 index 0000000000..ae8bb6df21 --- /dev/null +++ b/packages/react/src/hooks/useVerifyMessage.ts @@ -0,0 +1,59 @@ +'use client' + +import type { + Config, + ResolvedRegister, + VerifyMessageErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type VerifyMessageData, + type VerifyMessageOptions, + type VerifyMessageQueryKey, + verifyMessageQueryOptions, +} from '@wagmi/core/query' +import type { VerifyMessageQueryFnData } from '@wagmi/core/query' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseVerifyMessageParameters< + config extends Config = Config, + selectData = VerifyMessageData, +> = Compute< + VerifyMessageOptions & + ConfigParameter & + QueryParameter< + VerifyMessageQueryFnData, + VerifyMessageErrorType, + selectData, + VerifyMessageQueryKey + > +> + +export type UseVerifyMessageReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useVerifyMessage */ +export function useVerifyMessage< + config extends Config = ResolvedRegister['config'], + selectData = VerifyMessageData, +>( + parameters: UseVerifyMessageParameters = {}, +): UseVerifyMessageReturnType { + const { address, message, signature, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = verifyMessageQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean( + address && message && signature && (query.enabled ?? true), + ) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useVerifyTypedData.test-d.ts b/packages/react/src/hooks/useVerifyTypedData.test-d.ts new file mode 100644 index 0000000000..91f875e0b6 --- /dev/null +++ b/packages/react/src/hooks/useVerifyTypedData.test-d.ts @@ -0,0 +1,40 @@ +import type { typedData } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import type { Address } from 'viem' +import { + type UseVerifyTypedDataParameters, + useVerifyTypedData, +} from './useVerifyTypedData.js' + +test('select data', () => { + const result = useVerifyTypedData({ + query: { + select(data) { + return data + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) + +test('UseReadContractParameters', () => { + type Result = UseVerifyTypedDataParameters< + typeof typedData.basic.types, + 'Mail' + > + expectTypeOf>().toEqualTypeOf<{ + primaryType?: 'Mail' | 'Person' + message?: { + from: { + name: string + wallet: Address + } + to: { + name: string + wallet: Address + } + contents: string + } + }>() +}) diff --git a/packages/react/src/hooks/useVerifyTypedData.test.ts b/packages/react/src/hooks/useVerifyTypedData.test.ts new file mode 100644 index 0000000000..d57331743f --- /dev/null +++ b/packages/react/src/hooks/useVerifyTypedData.test.ts @@ -0,0 +1,481 @@ +import { typedData, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import type { Hex } from 'viem' +import { expect, test } from 'vitest' + +import { useVerifyTypedData } from './useVerifyTypedData.js' + +const smartAccountAddress = '0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145' +const notDeployedAddress = '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' + +test('valid signature', async () => { + const { result } = renderHook(() => + useVerifyTypedData({ + ...typedData.basic, + primaryType: 'Mail', + address: smartAccountAddress, + signature: + '0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": true, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyTypedData", + { + "address": "0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145", + "chainId": 1, + "domain": { + "chainId": 1, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": "0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c", + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('invalid signature', async () => { + const { result } = renderHook(() => + useVerifyTypedData({ + ...typedData.basic, + primaryType: 'Mail', + address: smartAccountAddress, + signature: '0xdead', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": false, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyTypedData", + { + "address": "0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145", + "chainId": 1, + "domain": { + "chainId": 1, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": "0xdead", + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('account not deployed', async () => { + const { result } = renderHook(() => + useVerifyTypedData({ + ...typedData.basic, + primaryType: 'Mail', + address: notDeployedAddress, + signature: + '0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": false, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyTypedData", + { + "address": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + "chainId": 1, + "domain": { + "chainId": 1, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": "0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c", + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: signature: undefined -> defined', async () => { + let signature: Hex | undefined = undefined + + const { result, rerender } = renderHook(() => + useVerifyTypedData({ + ...typedData.basic, + primaryType: 'Mail', + address: smartAccountAddress, + signature, + }), + ) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "verifyTypedData", + { + "address": "0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145", + "chainId": 1, + "domain": { + "chainId": 1, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": undefined, + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) + + signature = + '0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c' + rerender() + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": true, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "verifyTypedData", + { + "address": "0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145", + "chainId": 1, + "domain": { + "chainId": 1, + "name": "Ether Mail", + "verifyingContract": "0x0000000000000000000000000000000000000000", + "version": "1", + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + }, + "primaryType": "Mail", + "signature": "0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c", + "types": { + "Mail": [ + { + "name": "from", + "type": "Person", + }, + { + "name": "to", + "type": "Person", + }, + { + "name": "contents", + "type": "string", + }, + ], + "Person": [ + { + "name": "name", + "type": "string", + }, + { + "name": "wallet", + "type": "address", + }, + ], + }, + }, + ], + "refetch": [Function], + "status": "success", + } + `) +}) + +test('behavior: disabled when properties missing', async () => { + const { result } = renderHook(() => useVerifyTypedData()) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useVerifyTypedData.ts b/packages/react/src/hooks/useVerifyTypedData.ts new file mode 100644 index 0000000000..b02d1a8510 --- /dev/null +++ b/packages/react/src/hooks/useVerifyTypedData.ts @@ -0,0 +1,81 @@ +'use client' + +import type { + Config, + ResolvedRegister, + VerifyTypedDataErrorType, +} from '@wagmi/core' +import { + type VerifyTypedDataData, + type VerifyTypedDataOptions, + type VerifyTypedDataQueryKey, + verifyTypedDataQueryOptions, +} from '@wagmi/core/query' +import type { VerifyTypedDataQueryFnData } from '@wagmi/core/query' +import type { TypedData } from 'viem' +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseVerifyTypedDataParameters< + typedData extends TypedData | Record = TypedData, + primaryType extends keyof typedData | 'EIP712Domain' = keyof typedData, + config extends Config = Config, + selectData = VerifyTypedDataData, +> = VerifyTypedDataOptions & + ConfigParameter & + QueryParameter< + VerifyTypedDataQueryFnData, + VerifyTypedDataErrorType, + selectData, + VerifyTypedDataQueryKey + > + +export type UseVerifyTypedDataReturnType = + UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useVerifyTypedData */ +export function useVerifyTypedData< + const typedData extends TypedData | Record, + primaryType extends keyof typedData | 'EIP712Domain', + config extends Config = ResolvedRegister['config'], + selectData = VerifyTypedDataData, +>( + parameters: UseVerifyTypedDataParameters< + typedData, + primaryType, + config, + selectData + > = {} as any, +): UseVerifyTypedDataReturnType { + const { + address, + message, + primaryType, + signature, + types, + query = {}, + } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = verifyTypedDataQueryOptions( + config, + { + ...parameters, + chainId: parameters.chainId ?? chainId, + }, + ) + const enabled = Boolean( + address && + message && + primaryType && + signature && + types && + (query.enabled ?? true), + ) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useWaitForCallsStatus.test.ts b/packages/react/src/hooks/useWaitForCallsStatus.test.ts new file mode 100644 index 0000000000..96f5a2a7e4 --- /dev/null +++ b/packages/react/src/hooks/useWaitForCallsStatus.test.ts @@ -0,0 +1,101 @@ +import { connect, disconnect } from '@wagmi/core' +import { accounts, config, testClient, wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { parseEther } from 'viem' +import { expect, test } from 'vitest' + +import { useSendCalls } from './useSendCalls.js' +import { useWaitForCallsStatus } from './useWaitForCallsStatus.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const useSendCalls_render = renderHook(() => useSendCalls()) + const useWaitForCallsStatus_render = renderHook(() => + useWaitForCallsStatus({ id: useSendCalls_render.result.current.data?.id }), + ) + + useSendCalls_render.result.current.sendCalls({ + calls: [ + { + data: '0xdeadbeef', + to: accounts[1], + value: parseEther('1'), + }, + { + to: accounts[2], + value: parseEther('2'), + }, + { + to: accounts[3], + value: parseEther('3'), + }, + ], + }) + await waitFor(() => + expect(useSendCalls_render.result.current.isSuccess).toBeTruthy(), + ) + + expect(useWaitForCallsStatus_render.result.current.fetchStatus).toBe('idle') + useWaitForCallsStatus_render.rerender() + expect(useWaitForCallsStatus_render.result.current.fetchStatus).toBe( + 'fetching', + ) + + await Promise.all([ + waitFor(() => + expect( + useWaitForCallsStatus_render.result.current.isSuccess, + ).toBeTruthy(), + ), + (async () => { + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + })(), + ]) + + expect(useWaitForCallsStatus_render.result.current.data?.status).toBe( + 'success', + ) + expect( + useWaitForCallsStatus_render.result.current.data?.receipts?.map((x) => ({ + ...x, + blockHash: undefined, + })), + ).toMatchInlineSnapshot( + ` + [ + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21064n, + "logs": [], + "status": "success", + "transactionHash": "0x13c53b2d4d9da424835525349cd66e553330f323d6fb19458b801ae1f7989a41", + }, + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21000n, + "logs": [], + "status": "success", + "transactionHash": "0xd8397b3e82b061c26a0c2093f1ceca0c3662a512614f7d6370349e89d0eea007", + }, + { + "blockHash": undefined, + "blockNumber": 19258214n, + "gasUsed": 21000n, + "logs": [], + "status": "success", + "transactionHash": "0x4d26e346593d9ea265bb164b115e89aa92df43b0b8778ac75d4ad28e2a22b101", + }, + ] + `, + ) + + await testClient.mainnet.mine({ blocks: 1 }) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useWaitForCallsStatus.ts b/packages/react/src/hooks/useWaitForCallsStatus.ts new file mode 100644 index 0000000000..7c428c9405 --- /dev/null +++ b/packages/react/src/hooks/useWaitForCallsStatus.ts @@ -0,0 +1,54 @@ +'use client' + +import type { + Config, + ResolvedRegister, + WaitForCallsStatusErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type WaitForCallsStatusData, + type WaitForCallsStatusOptions, + type WaitForCallsStatusQueryFnData, + type WaitForCallsStatusQueryKey, + waitForCallsStatusQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseWaitForCallsStatusParameters< + config extends Config = Config, + selectData = WaitForCallsStatusData, +> = Compute< + WaitForCallsStatusOptions & + ConfigParameter & + QueryParameter< + WaitForCallsStatusQueryFnData, + WaitForCallsStatusErrorType, + selectData, + WaitForCallsStatusQueryKey + > +> + +export type UseWaitForCallsStatusReturnType< + selectData = WaitForCallsStatusData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useWaitForCallsStatus */ +export function useWaitForCallsStatus< + config extends Config = ResolvedRegister['config'], + selectData = WaitForCallsStatusData, +>( + parameters: UseWaitForCallsStatusParameters, +): UseWaitForCallsStatusReturnType { + const { id, query = {} } = parameters + + const config = useConfig(parameters) + + const options = waitForCallsStatusQueryOptions(config, parameters) + const enabled = Boolean(id && (query.enabled ?? true)) + + return useQuery({ ...query, ...options, enabled }) +} diff --git a/packages/react/src/hooks/useWaitForTransactionReceipt.test-d.ts b/packages/react/src/hooks/useWaitForTransactionReceipt.test-d.ts new file mode 100644 index 0000000000..2d3bd28445 --- /dev/null +++ b/packages/react/src/hooks/useWaitForTransactionReceipt.test-d.ts @@ -0,0 +1,14 @@ +import { expectTypeOf, test } from 'vitest' + +import { useWaitForTransactionReceipt } from './useWaitForTransactionReceipt.js' + +test('select data', () => { + const result = useWaitForTransactionReceipt({ + query: { + select(data) { + return data?.blockNumber + }, + }, + }) + expectTypeOf(result.data).toEqualTypeOf() +}) diff --git a/packages/react/src/hooks/useWaitForTransactionReceipt.test.ts b/packages/react/src/hooks/useWaitForTransactionReceipt.test.ts new file mode 100644 index 0000000000..484d25087f --- /dev/null +++ b/packages/react/src/hooks/useWaitForTransactionReceipt.test.ts @@ -0,0 +1,77 @@ +import { wait } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' +import { useWaitForTransactionReceipt } from './useWaitForTransactionReceipt.js' + +test('default', async () => { + const { result } = renderHook(() => + useWaitForTransactionReceipt({ + hash: '0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30', + }), + ) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result).toMatchInlineSnapshot(` + { + "current": { + "data": { + "blockHash": "0xd725a38b51e5ceec8c5f6c9ccfdb2cc423af993bb650af5eedca5e4be7156ba7", + "blockNumber": 15189204n, + "chainId": 1, + "contractAddress": null, + "cumulativeGasUsed": 12949744n, + "effectiveGasPrice": 9371645552n, + "from": "0xa0cf798816d4b9b9866b5330eea46a18382f251e", + "gasUsed": 21000n, + "logs": [], + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "status": "success", + "to": "0xd2135cfb216b74109775236e36d4b433f1df507b", + "transactionHash": "0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30", + "transactionIndex": 144, + "type": "eip1559", + }, + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": true, + "isSuccess": true, + "queryKey": [ + "waitForTransactionReceipt", + { + "chainId": 1, + "hash": "0x60668ed8c2dc110d61d945a936fcd45b8f13654e5c78481c8c825d1148c7ef30", + }, + ], + "refetch": [Function], + "status": "success", + }, + } + `) +}) + +test('disabled when hash is undefined', async () => { + const { result } = renderHook(() => + useWaitForTransactionReceipt({ hash: undefined }), + ) + + await wait(100) + await waitFor(() => expect(result.current.isPending).toBeTruthy()) +}) diff --git a/packages/react/src/hooks/useWaitForTransactionReceipt.ts b/packages/react/src/hooks/useWaitForTransactionReceipt.ts new file mode 100644 index 0000000000..c07d1639bc --- /dev/null +++ b/packages/react/src/hooks/useWaitForTransactionReceipt.ts @@ -0,0 +1,74 @@ +'use client' + +import type { + Config, + ResolvedRegister, + WaitForTransactionReceiptErrorType, +} from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type WaitForTransactionReceiptData, + type WaitForTransactionReceiptOptions, + type WaitForTransactionReceiptQueryFnData, + type WaitForTransactionReceiptQueryKey, + waitForTransactionReceiptQueryOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter, QueryParameter } from '../types/properties.js' +import { type UseQueryReturnType, useQuery } from '../utils/query.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseWaitForTransactionReceiptParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = WaitForTransactionReceiptData, +> = Compute< + WaitForTransactionReceiptOptions & + ConfigParameter & + QueryParameter< + WaitForTransactionReceiptQueryFnData, + WaitForTransactionReceiptErrorType, + selectData, + WaitForTransactionReceiptQueryKey + > +> + +export type UseWaitForTransactionReceiptReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = WaitForTransactionReceiptData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useWaitForTransactionReceipt */ +export function useWaitForTransactionReceipt< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = WaitForTransactionReceiptData, +>( + parameters: UseWaitForTransactionReceiptParameters< + config, + chainId, + selectData + > = {}, +): UseWaitForTransactionReceiptReturnType { + const { hash, query = {} } = parameters + + const config = useConfig(parameters) + const chainId = useChainId({ config }) + + const options = waitForTransactionReceiptQueryOptions(config, { + ...parameters, + chainId: parameters.chainId ?? chainId, + }) + const enabled = Boolean(hash && (query.enabled ?? true)) + + return useQuery({ + ...(query as any), + ...options, + enabled, + }) as UseWaitForTransactionReceiptReturnType +} diff --git a/packages/react/src/hooks/useWalletClient.test-d.ts b/packages/react/src/hooks/useWalletClient.test-d.ts new file mode 100644 index 0000000000..c2ea48bcb1 --- /dev/null +++ b/packages/react/src/hooks/useWalletClient.test-d.ts @@ -0,0 +1,12 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { useWalletClient } from './useWalletClient.js' + +test('parameters: config', async () => { + const client = useWalletClient({ config }) + expectTypeOf(client.data?.chain?.id!).toEqualTypeOf<1 | 456 | 10>() + + const client2 = useWalletClient({ config, chainId: 1 }) + expectTypeOf(client2.data?.chain?.id!).toEqualTypeOf<1>() +}) diff --git a/packages/react/src/hooks/useWalletClient.test.tsx b/packages/react/src/hooks/useWalletClient.test.tsx new file mode 100644 index 0000000000..40fdd5850a --- /dev/null +++ b/packages/react/src/hooks/useWalletClient.test.tsx @@ -0,0 +1,222 @@ +import { connect, disconnect } from '@wagmi/core' +import { config, wait } from '@wagmi/test' +import { render, renderHook, waitFor } from '@wagmi/test/react' +import * as React from 'react' +import { expect, test } from 'vitest' + +import { useAccount } from './useAccount.js' +import { useConnect } from './useConnect.js' +import { useDisconnect } from './useDisconnect.js' +import { useSwitchChain } from './useSwitchChain.js' +import { useWalletClient } from './useWalletClient.js' + +// Almost identical implementation to `useConnectorClient` (except for return type) +// Should update both in tandem + +const connector = config.connectors[0]! + +test('default', async () => { + const { result } = renderHook(() => useWalletClient()) + + await waitFor(() => expect(result.current.isPending).toBeTruthy()) + + expect(result.current).toMatchInlineSnapshot(` + { + "data": undefined, + "dataUpdatedAt": 0, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": false, + "isFetchedAfterMount": false, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": true, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": false, + "queryKey": [ + "walletClient", + { + "chainId": 1, + "connectorUid": undefined, + }, + ], + "refetch": [Function], + "status": "pending", + } + `) +}) + +test('behavior: connected on mount', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useWalletClient()) + + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + const { data, queryKey: _, ...rest } = result.current + expect(data).toMatchObject( + expect.objectContaining({ + account: expect.any(Object), + chain: expect.any(Object), + }), + ) + expect(rest).toMatchInlineSnapshot(` + { + "dataUpdatedAt": 1675209600000, + "error": null, + "errorUpdateCount": 0, + "errorUpdatedAt": 0, + "failureCount": 0, + "failureReason": null, + "fetchStatus": "idle", + "isError": false, + "isFetched": true, + "isFetchedAfterMount": true, + "isFetching": false, + "isInitialLoading": false, + "isLoading": false, + "isLoadingError": false, + "isPaused": false, + "isPending": false, + "isPlaceholderData": false, + "isRefetchError": false, + "isRefetching": false, + "isStale": false, + "isSuccess": true, + "refetch": [Function], + "status": "success", + } + `) + + await disconnect(config, { connector }) +}) + +test('behavior: connect and disconnect', async () => { + const { result } = renderHook(() => ({ + useConnect: useConnect(), + useWalletClient: useWalletClient(), + useDisconnect: useDisconnect(), + })) + + expect(result.current.useWalletClient.data).not.toBeDefined() + + result.current.useConnect.connect({ + connector: result.current.useConnect.connectors[0]!, + }) + + await waitFor(() => expect(result.current.useWalletClient.data).toBeDefined()) + + result.current.useDisconnect.disconnect() + + await waitFor(() => + expect(result.current.useWalletClient.data).not.toBeDefined(), + ) +}) + +test('behavior: switch chains', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => ({ + useWalletClient: useWalletClient(), + useSwitchChain: useSwitchChain(), + })) + + expect(result.current.useWalletClient.data).not.toBeDefined() + + await waitFor(() => expect(result.current.useWalletClient.data).toBeDefined()) + + result.current.useSwitchChain.switchChain({ chainId: 456 }) + await waitFor(() => { + expect(result.current.useSwitchChain.isSuccess).toBeTruthy() + result.current.useSwitchChain.reset() + }) + expect(result.current.useWalletClient.data?.chain.id).toEqual(456) + + result.current.useSwitchChain.switchChain({ chainId: 1 }) + await waitFor(() => + expect(result.current.useSwitchChain.isSuccess).toBeTruthy(), + ) + expect(result.current.useWalletClient.data?.chain.id).toEqual(1) + + await disconnect(config, { connector }) +}) + +test('behavior: re-render does not invalidate query', async () => { + const { getByTestId } = render() + + getByTestId('connect').click() + await waitFor(() => { + expect(getByTestId('address').innerText).toContain( + '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + ) + expect(getByTestId('client').innerText).toBeTruthy() + + expect(getByTestId('child-client').innerText).toBeTruthy() + expect(getByTestId('render-count').innerText).toEqual('1') + }) + + const initialClient = getByTestId('child-client').innerText + + getByTestId('rerender').click() + await waitFor(() => { + expect(getByTestId('render-count').innerText).toEqual('2') + }) + await wait(200) + + expect(getByTestId('child-client').innerText).toEqual(initialClient) +}) + +function Parent() { + const [renderCount, setRenderCount] = React.useState(1) + + const { connectors, connect } = useConnect() + const { address } = useAccount() + const { data } = useWalletClient() + + return ( + <> +
{address}
+
{data?.uid}
+ + + + + + ) +} + +function Child(props: { + renderCount: number +}) { + const { renderCount } = props + const { data } = useWalletClient() + return ( +
+ {data?.uid} + {renderCount} +
+ ) +} diff --git a/packages/react/src/hooks/useWalletClient.ts b/packages/react/src/hooks/useWalletClient.ts new file mode 100644 index 0000000000..24a3bccdda --- /dev/null +++ b/packages/react/src/hooks/useWalletClient.ts @@ -0,0 +1,116 @@ +'use client' + +// Almost identical implementation to `useConnectorClient` (except for return type) +// Should update both in tandem + +import { useQueryClient } from '@tanstack/react-query' +import type { + Config, + GetWalletClientErrorType, + ResolvedRegister, +} from '@wagmi/core' +import type { Compute, Omit } from '@wagmi/core/internal' +import { + type GetWalletClientData, + type GetWalletClientOptions, + type GetWalletClientQueryFnData, + type GetWalletClientQueryKey, + getWalletClientQueryOptions, +} from '@wagmi/core/query' +import { useEffect, useRef } from 'react' + +import type { ConfigParameter } from '../types/properties.js' +import { + type UseQueryParameters, + type UseQueryReturnType, + useQuery, +} from '../utils/query.js' +import { useAccount } from './useAccount.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseWalletClientParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetWalletClientData, +> = Compute< + GetWalletClientOptions & + ConfigParameter & { + query?: + | Compute< + Omit< + UseQueryParameters< + GetWalletClientQueryFnData, + GetWalletClientErrorType, + selectData, + GetWalletClientQueryKey + >, + 'gcTime' | 'staleTime' + > + > + | undefined + } +> + +export type UseWalletClientReturnType< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetWalletClientData, +> = UseQueryReturnType + +/** https://wagmi.sh/react/api/hooks/useWalletClient */ +export function useWalletClient< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + selectData = GetWalletClientData, +>( + parameters: UseWalletClientParameters = {}, +): UseWalletClientReturnType { + const { query = {}, ...rest } = parameters + + const config = useConfig(rest) + const queryClient = useQueryClient() + const { address, connector, status } = useAccount({ config }) + const chainId = useChainId({ config }) + const activeConnector = parameters.connector ?? connector + + const { queryKey, ...options } = getWalletClientQueryOptions( + config, + { + ...parameters, + chainId: parameters.chainId ?? chainId, + connector: parameters.connector ?? connector, + }, + ) + const enabled = Boolean( + (status === 'connected' || + (status === 'reconnecting' && activeConnector?.getProvider)) && + (query.enabled ?? true), + ) + + const addressRef = useRef(address) + // biome-ignore lint/correctness/useExhaustiveDependencies: `queryKey` not required + useEffect(() => { + const previousAddress = addressRef.current + if (!address && previousAddress) { + // remove when account is disconnected + queryClient.removeQueries({ queryKey }) + addressRef.current = undefined + } else if (address !== previousAddress) { + // invalidate when address changes + queryClient.invalidateQueries({ queryKey }) + addressRef.current = address + } + }, [address, queryClient]) + + return useQuery({ + ...query, + ...options, + queryKey, + enabled, + staleTime: Number.POSITIVE_INFINITY, + } as any) as UseWalletClientReturnType +} diff --git a/packages/react/src/hooks/useWatchAsset.test-d.ts b/packages/react/src/hooks/useWatchAsset.test-d.ts new file mode 100644 index 0000000000..0b7258c583 --- /dev/null +++ b/packages/react/src/hooks/useWatchAsset.test-d.ts @@ -0,0 +1,66 @@ +import type { WatchAssetErrorType } from '@wagmi/core' +import type { WatchAssetVariables } from '@wagmi/core/query' +import { expectTypeOf, test } from 'vitest' + +import { useWatchAsset } from './useWatchAsset.js' + +const tokenInfo = { + address: '0x0000000000000000000000000000000000000000', + symbol: 'NULL', + decimals: 18, +} +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { context, data, error, watchAsset, variables } = useWatchAsset({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toEqualTypeOf() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + watchAsset( + { type: 'ERC20', options: tokenInfo }, + { + onError(error, variables, context) { + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSuccess(data, variables, context) { + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + }, + }, + ) +}) diff --git a/packages/react/src/hooks/useWatchAsset.test.ts b/packages/react/src/hooks/useWatchAsset.test.ts new file mode 100644 index 0000000000..989b0323c8 --- /dev/null +++ b/packages/react/src/hooks/useWatchAsset.test.ts @@ -0,0 +1,27 @@ +import { connect, disconnect } from '@wagmi/core' +import { config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useWatchAsset } from './useWatchAsset.js' + +const connector = config.connectors[0]! + +const tokenInfo = { + address: '0x0000000000000000000000000000000000000000', + symbol: 'NULL', + decimals: 18, +} + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useWatchAsset()) + + result.current.watchAsset({ type: 'ERC20', options: tokenInfo }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toEqual(true) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useWatchAsset.ts b/packages/react/src/hooks/useWatchAsset.ts new file mode 100644 index 0000000000..cda4d6b0be --- /dev/null +++ b/packages/react/src/hooks/useWatchAsset.ts @@ -0,0 +1,65 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { WatchAssetErrorType } from '@wagmi/core' +import type { Compute } from '@wagmi/core/internal' +import { + type WatchAssetData, + type WatchAssetMutate, + type WatchAssetMutateAsync, + type WatchAssetVariables, + watchAssetMutationOptions, +} from '@wagmi/core/query' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseWatchAssetParameters = Compute< + ConfigParameter & { + mutation?: + | UseMutationParameters< + WatchAssetData, + WatchAssetErrorType, + WatchAssetVariables, + context + > + | undefined + } +> + +export type UseWatchAssetReturnType = Compute< + UseMutationReturnType< + WatchAssetData, + WatchAssetErrorType, + WatchAssetVariables, + context + > & { + watchAsset: WatchAssetMutate + watchAssetAsync: WatchAssetMutateAsync + } +> + +/** https://wagmi.sh/react/api/hooks/useWatchAsset */ +export function useWatchAsset( + parameters: UseWatchAssetParameters = {}, +): UseWatchAssetReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = watchAssetMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + return { + ...result, + watchAsset: mutate, + watchAssetAsync: mutateAsync, + } +} diff --git a/packages/react/src/hooks/useWatchBlockNumber.test-d.ts b/packages/react/src/hooks/useWatchBlockNumber.test-d.ts new file mode 100644 index 0000000000..0d669725bc --- /dev/null +++ b/packages/react/src/hooks/useWatchBlockNumber.test-d.ts @@ -0,0 +1,71 @@ +import { createConfig } from '@wagmi/core' +import { http, webSocket } from 'viem' +import { mainnet, optimism } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { + type UseWatchBlockNumberParameters, + useWatchBlockNumber, +} from './useWatchBlockNumber.js' + +test('default', () => { + useWatchBlockNumber({ + poll: false, + onBlockNumber() {}, + }) +}) + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + type Result = UseWatchBlockNumberParameters< + typeof config, + typeof mainnet.id | typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + useWatchBlockNumber({ + config, + poll: false, + onBlockNumber() {}, + }) + + type Result2 = UseWatchBlockNumberParameters + expectTypeOf().toEqualTypeOf() + useWatchBlockNumber({ + config, + chainId: mainnet.id, + poll: true, + onBlockNumber() {}, + }) + useWatchBlockNumber({ + config, + chainId: mainnet.id, + // @ts-expect-error + poll: false, + onBlockNumber() {}, + }) + + type Result3 = UseWatchBlockNumberParameters< + typeof config, + typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + useWatchBlockNumber({ + config, + chainId: optimism.id, + poll: true, + onBlockNumber() {}, + }) + useWatchBlockNumber({ + config, + chainId: optimism.id, + poll: false, + onBlockNumber() {}, + }) +}) diff --git a/packages/react/src/hooks/useWatchBlockNumber.test.ts b/packages/react/src/hooks/useWatchBlockNumber.test.ts new file mode 100644 index 0000000000..ecb900b357 --- /dev/null +++ b/packages/react/src/hooks/useWatchBlockNumber.test.ts @@ -0,0 +1,28 @@ +import { testClient, wait } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useWatchBlockNumber } from './useWatchBlockNumber.js' + +test('default', async () => { + const blockNumbers: bigint[] = [] + renderHook(() => + useWatchBlockNumber({ + onBlockNumber(blockNumber) { + blockNumbers.push(blockNumber) + }, + }), + ) + + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + + expect(blockNumbers.length).toBe(3) + expect( + blockNumbers.map((blockNumber) => blockNumber - blockNumbers[0]!), + ).toEqual([0n, 1n, 2n]) +}) diff --git a/packages/react/src/hooks/useWatchBlockNumber.ts b/packages/react/src/hooks/useWatchBlockNumber.ts new file mode 100644 index 0000000000..33ddac48a6 --- /dev/null +++ b/packages/react/src/hooks/useWatchBlockNumber.ts @@ -0,0 +1,65 @@ +'use client' + +import { + type Config, + type ResolvedRegister, + type WatchBlockNumberParameters, + watchBlockNumber, +} from '@wagmi/core' +import type { UnionCompute, UnionExactPartial } from '@wagmi/core/internal' +import { useEffect } from 'react' + +import type { ConfigParameter, EnabledParameter } from '../types/properties.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseWatchBlockNumberParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = UnionCompute< + UnionExactPartial> & + ConfigParameter & + EnabledParameter +> + +export type UseWatchBlockNumberReturnType = void + +/** https://wagmi.sh/react/api/hooks/useWatchBlockNumber */ +export function useWatchBlockNumber< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + parameters: UseWatchBlockNumberParameters = {} as any, +): UseWatchBlockNumberReturnType { + const { enabled = true, onBlockNumber, config: _, ...rest } = parameters + + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const chainId = parameters.chainId ?? configChainId + + // TODO(react@19): cleanup + // biome-ignore lint/correctness/useExhaustiveDependencies: `rest` changes every render so only including properties in dependency array + useEffect(() => { + if (!enabled) return + if (!onBlockNumber) return + return watchBlockNumber(config, { + ...(rest as any), + chainId, + onBlockNumber, + }) + }, [ + chainId, + config, + enabled, + onBlockNumber, + /// + rest.onError, + rest.emitMissed, + rest.emitOnBegin, + rest.poll, + rest.pollingInterval, + rest.syncConnectedChain, + ]) +} diff --git a/packages/react/src/hooks/useWatchBlocks.test-d.ts b/packages/react/src/hooks/useWatchBlocks.test-d.ts new file mode 100644 index 0000000000..2051bfb66f --- /dev/null +++ b/packages/react/src/hooks/useWatchBlocks.test-d.ts @@ -0,0 +1,73 @@ +import { createConfig } from '@wagmi/core' +import { http, webSocket } from 'viem' +import { mainnet, optimism } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { + type UseWatchBlocksParameters, + useWatchBlocks, +} from './useWatchBlocks.js' + +test('default', () => { + useWatchBlocks({ + poll: false, + onBlock() {}, + }) +}) + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + type Result = UseWatchBlocksParameters< + false, + 'latest', + typeof config, + typeof mainnet.id | typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + useWatchBlocks({ + config, + poll: false, + onBlock() {}, + }) + + type Result2 = UseWatchBlocksParameters< + false, + 'latest', + typeof config, + typeof mainnet.id + > + expectTypeOf().toEqualTypeOf() + useWatchBlocks({ + config, + chainId: mainnet.id, + poll: true, + onBlock() {}, + }) + + type Result3 = UseWatchBlocksParameters< + false, + 'latest', + typeof config, + typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + useWatchBlocks({ + config, + chainId: optimism.id, + poll: true, + onBlock() {}, + }) + useWatchBlocks({ + config, + chainId: optimism.id, + poll: false, + onBlock() {}, + }) +}) diff --git a/packages/react/src/hooks/useWatchBlocks.test.ts b/packages/react/src/hooks/useWatchBlocks.test.ts new file mode 100644 index 0000000000..039c718834 --- /dev/null +++ b/packages/react/src/hooks/useWatchBlocks.test.ts @@ -0,0 +1,31 @@ +import { testClient, wait } from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import type { Block } from 'viem' +import { expect, test } from 'vitest' + +import { useWatchBlocks } from './useWatchBlocks.js' + +test('default', async () => { + const blocks: Block[] = [] + renderHook(() => + useWatchBlocks({ + onBlock(block) { + blocks.push(block) + }, + }), + ) + + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(100) + + expect(blocks.length).toBe(3) + expect(blocks.map((block) => block.number! - blocks[0]!.number!)).toEqual([ + 0n, + 1n, + 2n, + ]) +}) diff --git a/packages/react/src/hooks/useWatchBlocks.ts b/packages/react/src/hooks/useWatchBlocks.ts new file mode 100644 index 0000000000..466929c841 --- /dev/null +++ b/packages/react/src/hooks/useWatchBlocks.ts @@ -0,0 +1,79 @@ +'use client' + +import { + type Config, + type ResolvedRegister, + type WatchBlocksParameters, + watchBlocks, +} from '@wagmi/core' +import type { UnionCompute, UnionExactPartial } from '@wagmi/core/internal' +import { useEffect } from 'react' +import type { BlockTag } from 'viem' + +import type { ConfigParameter, EnabledParameter } from '../types/properties.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseWatchBlocksParameters< + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = UnionCompute< + UnionExactPartial< + WatchBlocksParameters + > & + ConfigParameter & + EnabledParameter +> + +export type UseWatchBlocksReturnType = void + +/** https://wagmi.sh/react/hooks/useWatchBlocks */ +export function useWatchBlocks< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], + includeTransactions extends boolean = false, + blockTag extends BlockTag = 'latest', +>( + parameters: UseWatchBlocksParameters< + includeTransactions, + blockTag, + config, + chainId + > = {} as any, +): UseWatchBlocksReturnType { + const { enabled = true, onBlock, config: _, ...rest } = parameters + + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const chainId = parameters.chainId ?? configChainId + + // TODO(react@19): cleanup + // biome-ignore lint/correctness/useExhaustiveDependencies: `rest` changes every render so only including properties in dependency array + useEffect(() => { + if (!enabled) return + if (!onBlock) return + return watchBlocks(config, { + ...(rest as any), + chainId, + onBlock, + }) + }, [ + chainId, + config, + enabled, + onBlock, + /// + rest.blockTag, + rest.emitMissed, + rest.emitOnBegin, + rest.includeTransactions, + rest.onError, + rest.poll, + rest.pollingInterval, + rest.syncConnectedChain, + ]) +} diff --git a/packages/react/src/hooks/useWatchContractEvent.test-d.ts b/packages/react/src/hooks/useWatchContractEvent.test-d.ts new file mode 100644 index 0000000000..b1d74de450 --- /dev/null +++ b/packages/react/src/hooks/useWatchContractEvent.test-d.ts @@ -0,0 +1,128 @@ +import { http, createConfig, webSocket } from '@wagmi/core' +import { mainnet, optimism } from '@wagmi/core/chains' +import { abi } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' + +import { + type UseWatchContractEventParameters, + useWatchContractEvent, +} from './useWatchContractEvent.js' + +test('default', () => { + useWatchContractEvent({ + address: '0x', + abi: abi.erc20, + eventName: 'Transfer', + poll: false, + args: { + from: '0x', + to: '0x', + }, + onLogs(logs) { + expectTypeOf(logs[0]!.eventName).toEqualTypeOf<'Transfer'>() + expectTypeOf(logs[0]!.args).toEqualTypeOf<{ + from?: `0x${string}` | undefined + to?: `0x${string}` | undefined + value?: bigint | undefined + }>() + }, + }) +}) + +test('behavior: no eventName', () => { + useWatchContractEvent({ + address: '0x', + abi: abi.erc20, + args: { + // TODO: Figure out why this is not working + // @ts-ignore + from: '0x', + to: '0x', + }, + onLogs(logs) { + expectTypeOf(logs[0]!.eventName).toEqualTypeOf<'Transfer' | 'Approval'>() + expectTypeOf(logs[0]!.args).toEqualTypeOf< + | Record + | readonly unknown[] + | { + from?: `0x${string}` | undefined + to?: `0x${string}` | undefined + value?: bigint | undefined + } + | { + owner?: `0x${string}` | undefined + spender?: `0x${string}` | undefined + value?: bigint | undefined + } + >() + }, + }) +}) + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + type Result = UseWatchContractEventParameters< + typeof abi.erc20, + 'Transfer' | 'Approval', + true, + typeof config, + typeof mainnet.id | typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + useWatchContractEvent({ + config, + poll: false, + address: '0x', + abi: abi.erc20, + onLogs() {}, + }) + + type Result2 = UseWatchContractEventParameters< + typeof abi.erc20, + 'Transfer' | 'Approval', + true, + typeof config, + typeof mainnet.id + > + expectTypeOf().toEqualTypeOf() + useWatchContractEvent({ + config, + chainId: mainnet.id, + poll: true, + address: '0x', + abi: abi.erc20, + onLogs() {}, + }) + + type Result3 = UseWatchContractEventParameters< + typeof abi.erc20, + 'Transfer' | 'Approval', + true, + typeof config, + typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + useWatchContractEvent({ + config, + chainId: optimism.id, + poll: true, + address: '0x', + abi: abi.erc20, + onLogs() {}, + }) + useWatchContractEvent({ + config, + chainId: optimism.id, + poll: false, + address: '0x', + abi: abi.erc20, + onLogs() {}, + }) +}) diff --git a/packages/react/src/hooks/useWatchContractEvent.test.ts b/packages/react/src/hooks/useWatchContractEvent.test.ts new file mode 100644 index 0000000000..e6ad2aa1dc --- /dev/null +++ b/packages/react/src/hooks/useWatchContractEvent.test.ts @@ -0,0 +1,86 @@ +import { connect, disconnect, getBalance, writeContract } from '@wagmi/core' +import { + abi, + accounts, + address, + config, + testClient, + transactionHashRegex, + wait, +} from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { http, createWalletClient, parseEther } from 'viem' +import type { WatchEventOnLogsParameter } from 'viem/actions' +import { expect, test } from 'vitest' + +import { useWatchContractEvent } from './useWatchContractEvent.js' + +const connector = config.connectors[0]! + +test('default', async () => { + const data = await connect(config, { connector }) + const connectedAddress = data.accounts[0] + + // impersonate usdc holder account and transfer usdc to connected account + await testClient.mainnet.impersonateAccount({ address: address.usdcHolder }) + await testClient.mainnet.setBalance({ + address: address.usdcHolder, + value: 10000000000000000000000n, + }) + await createWalletClient({ + account: address.usdcHolder, + chain: testClient.mainnet.chain, + transport: http(), + }).writeContract({ + address: address.usdc, + abi: abi.erc20, + functionName: 'transfer', + args: [connectedAddress, parseEther('10', 'gwei')], + }) + await testClient.mainnet.mine({ blocks: 1 }) + await testClient.mainnet.stopImpersonatingAccount({ + address: address.usdcHolder, + }) + + const balance = await getBalance(config, { + address: connectedAddress, + token: address.usdc, + }) + expect(balance.value).toBeGreaterThan(0n) + + // start watching transfer events + let logs: WatchEventOnLogsParameter = [] + renderHook(() => + useWatchContractEvent({ + address: address.usdc, + abi: abi.erc20, + eventName: 'Transfer', + onLogs(next) { + logs = logs.concat(next) + }, + }), + ) + + await writeContract(config, { + address: address.usdc, + abi: abi.erc20, + functionName: 'transfer', + args: [accounts[1], parseEther('1', 'gwei')], + }) + + await writeContract(config, { + address: address.usdc, + abi: abi.erc20, + functionName: 'transfer', + args: [accounts[3], parseEther('1', 'gwei')], + }) + + await testClient.mainnet.mine({ blocks: 1 }) + await testClient.mainnet.mine({ blocks: 1 }) + await wait(1000) // wait for events to be emitted + + expect(logs.length).toBe(2) + expect(logs[0]?.transactionHash).toMatch(transactionHashRegex) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useWatchContractEvent.ts b/packages/react/src/hooks/useWatchContractEvent.ts new file mode 100644 index 0000000000..0d7710df2a --- /dev/null +++ b/packages/react/src/hooks/useWatchContractEvent.ts @@ -0,0 +1,85 @@ +'use client' + +import { + type Config, + type ResolvedRegister, + type WatchContractEventParameters, + watchContractEvent, +} from '@wagmi/core' +import type { UnionCompute, UnionExactPartial } from '@wagmi/core/internal' +import { useEffect } from 'react' +import type { Abi, ContractEventName } from 'viem' + +import type { ConfigParameter, EnabledParameter } from '../types/properties.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseWatchContractEventParameters< + abi extends Abi | readonly unknown[] = Abi, + eventName extends ContractEventName = ContractEventName, + strict extends boolean | undefined = undefined, + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = UnionCompute< + UnionExactPartial< + WatchContractEventParameters + > & + ConfigParameter & + EnabledParameter +> + +export type UseWatchContractEventReturnType = void + +/** https://wagmi.sh/react/api/hooks/useWatchContractEvent */ +export function useWatchContractEvent< + const abi extends Abi | readonly unknown[], + eventName extends ContractEventName, + strict extends boolean | undefined = undefined, + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + parameters: UseWatchContractEventParameters< + abi, + eventName, + strict, + config, + chainId + > = {} as any, +): UseWatchContractEventReturnType { + const { enabled = true, onLogs, config: _, ...rest } = parameters + + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const chainId = parameters.chainId ?? configChainId + + // TODO(react@19): cleanup + // biome-ignore lint/correctness/useExhaustiveDependencies: `rest` changes every render so only including properties in dependency array + useEffect(() => { + if (!enabled) return + if (!onLogs) return + return watchContractEvent(config, { + ...(rest as any), + chainId, + onLogs, + }) + }, [ + chainId, + config, + enabled, + onLogs, + /// + rest.abi, + rest.address, + rest.args, + rest.batch, + rest.eventName, + rest.fromBlock, + rest.onError, + rest.poll, + rest.pollingInterval, + rest.strict, + rest.syncConnectedChain, + ]) +} diff --git a/packages/react/src/hooks/useWatchPendingTransactions.test-d.ts b/packages/react/src/hooks/useWatchPendingTransactions.test-d.ts new file mode 100644 index 0000000000..56ccc49cbf --- /dev/null +++ b/packages/react/src/hooks/useWatchPendingTransactions.test-d.ts @@ -0,0 +1,67 @@ +import { createConfig } from '@wagmi/core' +import { http, webSocket } from 'viem' +import { mainnet, optimism } from 'viem/chains' +import { expectTypeOf, test } from 'vitest' + +import { + type UseWatchPendingTransactionsParameters, + useWatchPendingTransactions, +} from './useWatchPendingTransactions.js' + +test('default', () => { + useWatchPendingTransactions({ + poll: false, + onTransactions() {}, + }) +}) + +test('differing transports', () => { + const config = createConfig({ + chains: [mainnet, optimism], + transports: { + [mainnet.id]: http(), + [optimism.id]: webSocket(), + }, + }) + + type Result = UseWatchPendingTransactionsParameters< + typeof config, + typeof mainnet.id | typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + useWatchPendingTransactions({ + config, + poll: false, + onTransactions() {}, + }) + + type Result2 = UseWatchPendingTransactionsParameters< + typeof config, + typeof mainnet.id + > + expectTypeOf().toEqualTypeOf() + useWatchPendingTransactions({ + config, + chainId: mainnet.id, + poll: true, + onTransactions() {}, + }) + + type Result3 = UseWatchPendingTransactionsParameters< + typeof config, + typeof optimism.id + > + expectTypeOf().toEqualTypeOf() + useWatchPendingTransactions({ + config, + chainId: optimism.id, + poll: true, + onTransactions() {}, + }) + useWatchPendingTransactions({ + config, + chainId: optimism.id, + poll: false, + onTransactions() {}, + }) +}) diff --git a/packages/react/src/hooks/useWatchPendingTransactions.test.ts b/packages/react/src/hooks/useWatchPendingTransactions.test.ts new file mode 100644 index 0000000000..e1981f3fa1 --- /dev/null +++ b/packages/react/src/hooks/useWatchPendingTransactions.test.ts @@ -0,0 +1,49 @@ +import { connect, disconnect, sendTransaction } from '@wagmi/core' +import { + accounts, + config, + testClient, + transactionHashRegex, + wait, +} from '@wagmi/test' +import { renderHook } from '@wagmi/test/react' +import { parseEther } from 'viem' +import type { OnTransactionsParameter } from 'viem/actions' +import { expect, test } from 'vitest' + +import { useWatchPendingTransactions } from './useWatchPendingTransactions.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + let transactions: OnTransactionsParameter = [] + renderHook(() => + useWatchPendingTransactions({ + onTransactions(next) { + transactions = [...transactions, ...next] + }, + }), + ) + await wait(1000) + + await sendTransaction(config, { + to: accounts[1], + value: parseEther('1'), + }) + await wait(200) + + await sendTransaction(config, { + to: accounts[3], + value: parseEther('1'), + }) + await wait(200) + + await testClient.mainnet.mine({ blocks: 1 }) + + expect(transactions.length).toBe(2) + expect(transactions[0]).toMatch(transactionHashRegex) + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useWatchPendingTransactions.ts b/packages/react/src/hooks/useWatchPendingTransactions.ts new file mode 100644 index 0000000000..7461ffcfb4 --- /dev/null +++ b/packages/react/src/hooks/useWatchPendingTransactions.ts @@ -0,0 +1,67 @@ +'use client' + +import { + type Config, + type ResolvedRegister, + type WatchPendingTransactionsParameters, + watchPendingTransactions, +} from '@wagmi/core' +import type { UnionCompute, UnionExactPartial } from '@wagmi/core/internal' +import { useEffect } from 'react' + +import type { ConfigParameter, EnabledParameter } from '../types/properties.js' +import { useChainId } from './useChainId.js' +import { useConfig } from './useConfig.js' + +export type UseWatchPendingTransactionsParameters< + config extends Config = Config, + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +> = UnionCompute< + UnionExactPartial> & + ConfigParameter & + EnabledParameter +> + +export type UseWatchPendingTransactionsReturnType = void + +/** https://wagmi.sh/react/api/hooks/useWatchPendingTransactions */ +export function useWatchPendingTransactions< + config extends Config = ResolvedRegister['config'], + chainId extends + config['chains'][number]['id'] = config['chains'][number]['id'], +>( + parameters: UseWatchPendingTransactionsParameters< + config, + chainId + > = {} as any, +): UseWatchPendingTransactionsReturnType { + const { enabled = true, onTransactions, config: _, ...rest } = parameters + + const config = useConfig(parameters) + const configChainId = useChainId({ config }) + const chainId = parameters.chainId ?? configChainId + + // TODO(react@19): cleanup + // biome-ignore lint/correctness/useExhaustiveDependencies: `rest` changes every render so only including properties in dependency array + useEffect(() => { + if (!enabled) return + if (!onTransactions) return + return watchPendingTransactions(config, { + ...(rest as any), + chainId, + onTransactions, + }) + }, [ + chainId, + config, + enabled, + onTransactions, + /// + rest.batch, + rest.onError, + rest.poll, + rest.pollingInterval, + rest.syncConnectedChain, + ]) +} diff --git a/packages/react/src/hooks/useWriteContract.test-d.ts b/packages/react/src/hooks/useWriteContract.test-d.ts new file mode 100644 index 0000000000..344c11255b --- /dev/null +++ b/packages/react/src/hooks/useWriteContract.test-d.ts @@ -0,0 +1,185 @@ +import { http, type WriteContractErrorType, createConfig } from '@wagmi/core' +import { base } from '@wagmi/core/chains' +import { abi } from '@wagmi/test' +import type { Abi, Address, Hash } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import { useSimulateContract } from './useSimulateContract.js' +import { useWriteContract } from './useWriteContract.js' + +const contextValue = { foo: 'bar' } as const + +test('context', () => { + const { + context, + data, + error, + writeContract: write, + variables, + } = useWriteContract({ + mutation: { + onMutate(variables) { + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: Abi + functionName: string + args?: readonly unknown[] | undefined + }>() + return contextValue + }, + onError(error, variables, context) { + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: Abi + functionName: string + args?: readonly unknown[] | undefined + }>() + }, + onSuccess(data, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: Abi + functionName: string + args?: readonly unknown[] | undefined + }>() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: Abi + functionName: string + args?: readonly unknown[] | undefined + }>() + }, + }, + }) + + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(variables).toMatchTypeOf< + { chainId?: number | undefined } | undefined + >() + expectTypeOf(context).toEqualTypeOf() + + write( + { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + }, + { + onError(error, variables, context) { + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: typeof abi.erc20 + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + }>() + }, + onSuccess(data, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables.functionName).toEqualTypeOf<'transferFrom'>() + expectTypeOf(variables.args).toEqualTypeOf< + readonly [Address, Address, bigint] + >() + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: typeof abi.erc20 + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + }>() + }, + onSettled(data, error, variables, context) { + expectTypeOf(data).toEqualTypeOf() + expectTypeOf(error).toEqualTypeOf() + expectTypeOf(context).toEqualTypeOf() + + expectTypeOf(variables).toMatchTypeOf<{ + chainId?: number | undefined + abi: typeof abi.erc20 + functionName: 'transferFrom' + args: readonly [Address, Address, bigint] + }>() + }, + }, + ) +}) + +test('useSimulateContract', () => { + const { data } = useSimulateContract({ + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + chainId: 1, + }) + const { writeContract } = useWriteContract() + + const request = data?.request + if (request) writeContract(request) +}) + +// https://github.com/wevm/wagmi/issues/3981 +test('gh#3981', () => { + const config = createConfig({ + chains: [base], + transports: { + [base.id]: http(), + }, + }) + + const abi = [ + { + type: 'function', + name: 'example1', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + name: 'example2', + inputs: [ + { name: 'exampleName', type: 'address', internalType: 'address' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + ] as const + + const { writeContract } = useWriteContract({ config }) + writeContract({ + abi, + address: '0x...', + functionName: 'example1', + args: ['0x...'], + value: 123n, + }) + writeContract({ + abi, + address: '0x...', + functionName: 'example2', + args: ['0x...'], + // @ts-expect-error + value: 123n, + }) +}) diff --git a/packages/react/src/hooks/useWriteContract.test.ts b/packages/react/src/hooks/useWriteContract.test.ts new file mode 100644 index 0000000000..16ef870c2b --- /dev/null +++ b/packages/react/src/hooks/useWriteContract.test.ts @@ -0,0 +1,25 @@ +import { connect, disconnect } from '@wagmi/core' +import { abi, address, config } from '@wagmi/test' +import { renderHook, waitFor } from '@wagmi/test/react' +import { expect, test } from 'vitest' + +import { useWriteContract } from './useWriteContract.js' + +const connector = config.connectors[0]! + +test('default', async () => { + await connect(config, { connector }) + + const { result } = renderHook(() => useWriteContract()) + + result.current.writeContract({ + abi: abi.wagmiMintExample, + address: address.wagmiMintExample, + functionName: 'mint', + }) + await waitFor(() => expect(result.current.isSuccess).toBeTruthy()) + + expect(result.current.data).toBeDefined() + + await disconnect(config, { connector }) +}) diff --git a/packages/react/src/hooks/useWriteContract.ts b/packages/react/src/hooks/useWriteContract.ts new file mode 100644 index 0000000000..b07e856c93 --- /dev/null +++ b/packages/react/src/hooks/useWriteContract.ts @@ -0,0 +1,87 @@ +'use client' + +import { useMutation } from '@tanstack/react-query' +import type { + Config, + ResolvedRegister, + WriteContractErrorType, +} from '@wagmi/core' +import { + type WriteContractData, + type WriteContractMutate, + type WriteContractMutateAsync, + type WriteContractVariables, + writeContractMutationOptions, +} from '@wagmi/core/query' +import type { Abi } from 'viem' + +import type { ConfigParameter } from '../types/properties.js' +import type { + UseMutationParameters, + UseMutationReturnType, +} from '../utils/query.js' +import { useConfig } from './useConfig.js' + +export type UseWriteContractParameters< + config extends Config = Config, + context = unknown, +> = ConfigParameter & { + mutation?: + | UseMutationParameters< + WriteContractData, + WriteContractErrorType, + WriteContractVariables< + Abi, + string, + readonly unknown[], + config, + config['chains'][number]['id'] + >, + context + > + | undefined +} + +export type UseWriteContractReturnType< + config extends Config = Config, + context = unknown, +> = UseMutationReturnType< + WriteContractData, + WriteContractErrorType, + WriteContractVariables< + Abi, + string, + readonly unknown[], + config, + config['chains'][number]['id'] + >, + context +> & { + writeContract: WriteContractMutate + writeContractAsync: WriteContractMutateAsync +} + +/** https://wagmi.sh/react/api/hooks/useWriteContract */ +export function useWriteContract< + config extends Config = ResolvedRegister['config'], + context = unknown, +>( + parameters: UseWriteContractParameters = {}, +): UseWriteContractReturnType { + const { mutation } = parameters + + const config = useConfig(parameters) + + const mutationOptions = writeContractMutationOptions(config) + const { mutate, mutateAsync, ...result } = useMutation({ + ...mutation, + ...mutationOptions, + }) + + type Return = UseWriteContractReturnType + return { + ...result, + writeContract: mutate as Return['writeContract'], + writeContractAsync: mutateAsync as Return['writeContractAsync'], + } +} diff --git a/packages/react/src/hydrate.ts b/packages/react/src/hydrate.ts new file mode 100644 index 0000000000..b185929c22 --- /dev/null +++ b/packages/react/src/hydrate.ts @@ -0,0 +1,36 @@ +'use client' + +import { type ResolvedRegister, type State, hydrate } from '@wagmi/core' +import { type ReactElement, useEffect, useRef } from 'react' + +export type HydrateProps = { + config: ResolvedRegister['config'] + initialState?: State | undefined + reconnectOnMount?: boolean | undefined +} + +export function Hydrate(parameters: React.PropsWithChildren) { + const { children, config, initialState, reconnectOnMount = true } = parameters + + const { onMount } = hydrate(config, { + initialState, + reconnectOnMount, + }) + + // Hydrate for non-SSR + if (!config._internal.ssr) onMount() + + // Hydrate for SSR + const active = useRef(true) + // biome-ignore lint/correctness/useExhaustiveDependencies: `queryKey` not required + useEffect(() => { + if (!active.current) return + if (!config._internal.ssr) return + onMount() + return () => { + active.current = false + } + }, []) + + return children as ReactElement +} diff --git a/packages/react/src/types/properties.ts b/packages/react/src/types/properties.ts new file mode 100644 index 0000000000..7d903faf0a --- /dev/null +++ b/packages/react/src/types/properties.ts @@ -0,0 +1,51 @@ +import type { DefaultError, QueryKey } from '@tanstack/react-query' +import type { Config } from '@wagmi/core' +import type { Omit } from '@wagmi/core/internal' + +import type { + UseInfiniteQueryParameters, + UseQueryParameters, +} from '../utils/query.js' + +export type EnabledParameter = { + enabled?: boolean | undefined +} + +export type ConfigParameter = { + config?: Config | config | undefined +} + +export type QueryParameter< + queryFnData = unknown, + error = DefaultError, + data = queryFnData, + queryKey extends QueryKey = QueryKey, +> = { + query?: + | Omit< + UseQueryParameters, + 'queryFn' | 'queryHash' | 'queryKey' | 'queryKeyHashFn' | 'throwOnError' + > + | undefined +} + +export type InfiniteQueryParameter< + queryFnData = unknown, + error = DefaultError, + data = queryFnData, + queryData = queryFnData, + queryKey extends QueryKey = QueryKey, + pageParam = unknown, +> = { + query: Omit< + UseInfiniteQueryParameters< + queryFnData, + error, + data, + queryData, + queryKey, + pageParam + >, + 'queryFn' | 'queryHash' | 'queryKey' | 'queryKeyHashFn' | 'throwOnError' + > +} diff --git a/packages/react/src/utils/getVersion.test.ts b/packages/react/src/utils/getVersion.test.ts new file mode 100644 index 0000000000..e8368facea --- /dev/null +++ b/packages/react/src/utils/getVersion.test.ts @@ -0,0 +1,7 @@ +import { expect, test } from 'vitest' + +import { getVersion } from './getVersion.js' + +test('default', () => { + expect(getVersion()).toMatchInlineSnapshot(`"wagmi@x.y.z"`) +}) diff --git a/packages/react/src/utils/getVersion.ts b/packages/react/src/utils/getVersion.ts new file mode 100644 index 0000000000..1a8a9437b3 --- /dev/null +++ b/packages/react/src/utils/getVersion.ts @@ -0,0 +1,3 @@ +import { version } from '../version.js' + +export const getVersion = () => `wagmi@${version}` diff --git a/packages/react/src/utils/query.ts b/packages/react/src/utils/query.ts new file mode 100644 index 0000000000..3aafb16123 --- /dev/null +++ b/packages/react/src/utils/query.ts @@ -0,0 +1,145 @@ +import { + type DefaultError, + type QueryKey, + type UseInfiniteQueryOptions, + type UseInfiniteQueryResult, + type UseMutationOptions, + type UseMutationResult, + type UseQueryOptions, + type UseQueryResult, + useInfiniteQuery as tanstack_useInfiniteQuery, + useQuery as tanstack_useQuery, + useMutation, +} from '@tanstack/react-query' +import type { + Compute, + ExactPartial, + Omit, + UnionStrictOmit, +} from '@wagmi/core/internal' +import { hashFn } from '@wagmi/core/query' + +export type UseMutationParameters< + data = unknown, + error = Error, + variables = void, + context = unknown, +> = Compute< + Omit< + UseMutationOptions, context>, + 'mutationFn' | 'mutationKey' | 'throwOnError' + > +> + +export type UseMutationReturnType< + data = unknown, + error = Error, + variables = void, + context = unknown, +> = Compute< + UnionStrictOmit< + UseMutationResult, + 'mutate' | 'mutateAsync' + > +> + +export { useMutation } + +//////////////////////////////////////////////////////////////////////////////// + +export type UseQueryParameters< + queryFnData = unknown, + error = DefaultError, + data = queryFnData, + queryKey extends QueryKey = QueryKey, +> = Compute< + ExactPartial< + Omit, 'initialData'> + > & { + // Fix `initialData` type + initialData?: + | UseQueryOptions['initialData'] + | undefined + } +> + +export type UseQueryReturnType = Compute< + UseQueryResult & { + queryKey: QueryKey + } +> + +// Adding some basic customization. +// Ideally we don't have this function, but `import('@tanstack/react-query').useQuery` currently has some quirks where it is super hard to +// pass down the inferred `initialData` type because of it's discriminated overload in the on `useQuery`. +export function useQuery( + parameters: UseQueryParameters & { + queryKey: QueryKey + }, +): UseQueryReturnType { + const result = tanstack_useQuery({ + ...(parameters as any), + queryKeyHashFn: hashFn, // for bigint support + }) as UseQueryReturnType + result.queryKey = parameters.queryKey + return result +} + +//////////////////////////////////////////////////////////////////////////////// + +export type UseInfiniteQueryParameters< + queryFnData = unknown, + error = DefaultError, + data = queryFnData, + queryData = queryFnData, + queryKey extends QueryKey = QueryKey, + pageParam = unknown, +> = Compute< + Omit< + UseInfiniteQueryOptions< + queryFnData, + error, + data, + queryData, + queryKey, + pageParam + >, + 'initialData' + > & { + // Fix `initialData` type + initialData?: + | UseInfiniteQueryOptions< + queryFnData, + error, + data, + queryKey + >['initialData'] + | undefined + } +> + +export type UseInfiniteQueryReturnType< + data = unknown, + error = DefaultError, +> = UseInfiniteQueryResult & { + queryKey: QueryKey +} + +// Adding some basic customization. +export function useInfiniteQuery< + queryFnData, + error, + data, + queryKey extends QueryKey, +>( + parameters: UseInfiniteQueryParameters & { + queryKey: QueryKey + }, +): UseInfiniteQueryReturnType { + const result = tanstack_useInfiniteQuery({ + ...(parameters as any), + queryKeyHashFn: hashFn, // for bigint support + }) as UseInfiniteQueryReturnType + result.queryKey = parameters.queryKey + return result +} diff --git a/packages/react/src/version.ts b/packages/react/src/version.ts new file mode 100644 index 0000000000..0ec209c466 --- /dev/null +++ b/packages/react/src/version.ts @@ -0,0 +1 @@ +export const version = '2.15.4' diff --git a/packages/react/test/setup.ts b/packages/react/test/setup.ts new file mode 100644 index 0000000000..5c0dcc071d --- /dev/null +++ b/packages/react/test/setup.ts @@ -0,0 +1,8 @@ +import { vi } from 'vitest' + +// Make dates stable across runs +Date.now = vi.fn(() => new Date(Date.UTC(2023, 1, 1)).valueOf()) + +vi.mock('../src/version.ts', () => { + return { version: 'x.y.z' } +}) diff --git a/packages/react/tsconfig.build.json b/packages/react/tsconfig.build.json new file mode 100644 index 0000000000..fbed2b1036 --- /dev/null +++ b/packages/react/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "src/**/*.test-d.ts"], + "compilerOptions": { + "sourceMap": true + } +} diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json new file mode 100644 index 0000000000..1b247fdd49 --- /dev/null +++ b/packages/react/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts", "test/**/*.tsx"], + "exclude": [] +} diff --git a/packages/register-tests/react/package.json b/packages/register-tests/react/package.json new file mode 100644 index 0000000000..769092f959 --- /dev/null +++ b/packages/register-tests/react/package.json @@ -0,0 +1,16 @@ +{ + "name": "react-register", + "private": true, + "type": "module", + "scripts": { + "check:types": "tsc --noEmit" + }, + "dependencies": { + "@tanstack/react-query": "catalog:", + "react": "catalog:", + "wagmi": "workspace:*" + }, + "devDependencies": { + "@types/react": "catalog:" + } +} diff --git a/packages/register-tests/react/src/config.ts b/packages/register-tests/react/src/config.ts new file mode 100644 index 0000000000..5772212c1b --- /dev/null +++ b/packages/register-tests/react/src/config.ts @@ -0,0 +1,26 @@ +import { http } from 'viem' +import { createConfig, mock } from 'wagmi' +import { celo, mainnet, optimism, zkSync } from 'wagmi/chains' + +export const config = createConfig({ + chains: [celo, mainnet, optimism, zkSync], + connectors: [mock({ accounts: ['0x'] })], + transports: { + [celo.id]: http(), + [mainnet.id]: http(), + [optimism.id]: http(), + [zkSync.id]: http(), + }, +}) + +export type ChainId = + | typeof celo.id + | typeof mainnet.id + | typeof optimism.id + | typeof zkSync.id + +declare module 'wagmi' { + interface Register { + config: typeof config + } +} diff --git a/packages/register-tests/react/src/createUseSimulateContract.test-d.ts b/packages/register-tests/react/src/createUseSimulateContract.test-d.ts new file mode 100644 index 0000000000..b469ccd024 --- /dev/null +++ b/packages/register-tests/react/src/createUseSimulateContract.test-d.ts @@ -0,0 +1,39 @@ +import { abi, config as testConfig } from '@wagmi/test' +import { test } from 'vitest' +import { celo, mainnet, optimism } from 'wagmi/chains' +import { createUseSimulateContract } from 'wagmi/codegen' + +const useSimulateErc20 = createUseSimulateContract({ + abi: abi.erc20, +}) + +test('chain formatters', () => { + useSimulateErc20({ + feeCurrency: '0x', + }) + + useSimulateErc20({ + chainId: celo.id, + feeCurrency: '0x', + }) + + useSimulateErc20({ + chainId: mainnet.id, + // @ts-expect-error + feeCurrency: '0x', + }) + + useSimulateErc20({ + chainId: optimism.id, + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('parameters: config', async () => { + useSimulateErc20({ + config: testConfig, + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/react/src/createUseWriteContract.test-d.ts b/packages/register-tests/react/src/createUseWriteContract.test-d.ts new file mode 100644 index 0000000000..0b6c538130 --- /dev/null +++ b/packages/register-tests/react/src/createUseWriteContract.test-d.ts @@ -0,0 +1,66 @@ +import { abi, config } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' +import { celo, mainnet, optimism } from 'wagmi/chains' +import { createUseWriteContract } from 'wagmi/codegen' + +const useWriteErc20 = createUseWriteContract({ + abi: abi.erc20, +}) + +test('chain formatters', () => { + const { writeContract } = useWriteErc20() + const shared = { + address: '0x', + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + } as const + + writeContract({ + ...shared, + feeCurrency: '0x', + }) + + type Result = Parameters< + typeof writeContract< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof celo.id + > + >[0] + expectTypeOf().toEqualTypeOf< + `0x${string}` | undefined + >() + writeContract({ + ...shared, + chainId: celo.id, + feeCurrency: '0x', + }) + + writeContract({ + ...shared, + chainId: mainnet.id, + // @ts-expect-error + feeCurrency: '0x', + }) + + writeContract({ + ...shared, + chainId: optimism.id, + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('parameters: config', async () => { + const { writeContract } = useWriteErc20({ config }) + + writeContract({ + address: '0x', + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/react/src/useAccount.test-d.ts b/packages/register-tests/react/src/useAccount.test-d.ts new file mode 100644 index 0000000000..be563933a6 --- /dev/null +++ b/packages/register-tests/react/src/useAccount.test-d.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { useAccount } from 'wagmi' + +import type { ChainId } from './config.js' + +test('default', () => { + const result = useAccount() + if (result.chain) expectTypeOf(result.chain.id).toEqualTypeOf() +}) + +test('parameters: config', async () => { + const result = useAccount({ config }) + if (result.chain) expectTypeOf(result.chain.id).toEqualTypeOf<1 | 10 | 456>() +}) diff --git a/packages/register-tests/react/src/useBlock.test-d.ts b/packages/register-tests/react/src/useBlock.test-d.ts new file mode 100644 index 0000000000..5f583a63e6 --- /dev/null +++ b/packages/register-tests/react/src/useBlock.test-d.ts @@ -0,0 +1,27 @@ +import type { Hex } from 'viem' +import { expectTypeOf, test } from 'vitest' +import { useBlock } from 'wagmi' +import { celo } from 'wagmi/chains' + +test('chain formatters', () => { + const result = useBlock() + + if (result.data) expectTypeOf(result.data.difficulty).toEqualTypeOf() + + if (result.data?.chainId === celo.id) { + expectTypeOf(result.data.difficulty).toEqualTypeOf() + expectTypeOf(result.data.gasLimit).toEqualTypeOf() + expectTypeOf(result.data.mixHash).toEqualTypeOf() + expectTypeOf(result.data.nonce).toEqualTypeOf<`0x${string}`>() + expectTypeOf(result.data.uncles).toEqualTypeOf() + } + + const result2 = useBlock({ chainId: celo.id }) + if (result2.data) { + expectTypeOf(result2.data.difficulty).toEqualTypeOf() + expectTypeOf(result2.data.gasLimit).toEqualTypeOf() + expectTypeOf(result2.data.mixHash).toEqualTypeOf() + expectTypeOf(result2.data.nonce).toEqualTypeOf<`0x${string}`>() + expectTypeOf(result2.data.uncles).toEqualTypeOf() + } +}) diff --git a/packages/register-tests/react/src/useChainId.test-d.ts b/packages/register-tests/react/src/useChainId.test-d.ts new file mode 100644 index 0000000000..d1a8e035d6 --- /dev/null +++ b/packages/register-tests/react/src/useChainId.test-d.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { useChainId } from 'wagmi' + +import type { ChainId } from './config.js' + +test('default', async () => { + const chainId = useChainId() + expectTypeOf(chainId).toEqualTypeOf() +}) + +test('parameters: config', async () => { + const chainId = useChainId({ config }) + expectTypeOf(chainId).toEqualTypeOf<1 | 456 | 10>() +}) diff --git a/packages/register-tests/react/src/useChains.test-d.ts b/packages/register-tests/react/src/useChains.test-d.ts new file mode 100644 index 0000000000..c40fd5e1d2 --- /dev/null +++ b/packages/register-tests/react/src/useChains.test-d.ts @@ -0,0 +1,11 @@ +import { expectTypeOf, test } from 'vitest' +import { useChains } from 'wagmi' +import type { Chain, celo, optimism } from 'wagmi/chains' + +test('default', () => { + const chains = useChains() + + expectTypeOf(chains[0]).toEqualTypeOf() + expectTypeOf(chains[2]).toEqualTypeOf() + expectTypeOf(chains[5]).toEqualTypeOf() +}) diff --git a/packages/register-tests/react/src/useClient.test-d.ts b/packages/register-tests/react/src/useClient.test-d.ts new file mode 100644 index 0000000000..0ce810b31c --- /dev/null +++ b/packages/register-tests/react/src/useClient.test-d.ts @@ -0,0 +1,38 @@ +import { type chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { useClient } from 'wagmi' +import { mainnet } from 'wagmi/chains' + +import type { ChainId } from './config.js' + +test('default', () => { + const client = useClient() + expectTypeOf(client.chain.id).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: config', () => { + const client = useClient({ config }) + expectTypeOf(client.chain.id).toEqualTypeOf< + (typeof config)['chains'][number]['id'] + >() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = useClient({ + config, + chainId: mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('behavior: unconfigured chain', () => { + const client = useClient({ + config, + // @ts-expect-error + chainId: 123456, + }) + expectTypeOf(client).toEqualTypeOf() +}) diff --git a/packages/register-tests/react/src/useConfig.test-d.ts b/packages/register-tests/react/src/useConfig.test-d.ts new file mode 100644 index 0000000000..25eb5246de --- /dev/null +++ b/packages/register-tests/react/src/useConfig.test-d.ts @@ -0,0 +1,17 @@ +import { config as testConfig } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { type Config, useConfig } from 'wagmi' + +import type { config } from './config.js' + +test('default', async () => { + const result = useConfig() + expectTypeOf(result).not.toEqualTypeOf() + expectTypeOf(result).toEqualTypeOf() +}) + +test('parameters: config', async () => { + const result = useConfig({ config: testConfig }) + expectTypeOf(result).not.toEqualTypeOf() + expectTypeOf(result).toEqualTypeOf() +}) diff --git a/packages/register-tests/react/src/useConnect.test-d.ts b/packages/register-tests/react/src/useConnect.test-d.ts new file mode 100644 index 0000000000..2386dca7e9 --- /dev/null +++ b/packages/register-tests/react/src/useConnect.test-d.ts @@ -0,0 +1,13 @@ +import { expectTypeOf, test } from 'vitest' +import { useConnect } from 'wagmi' + +test('infers connect parameters', () => { + const { connect, connectors, variables } = useConnect() + const connector = connectors[0]! + + expectTypeOf(variables?.foo).toEqualTypeOf() + connect({ + connector, + foo: 'bar', + }) +}) diff --git a/packages/register-tests/react/src/usePrepareTransactionRequest.test-d.ts b/packages/register-tests/react/src/usePrepareTransactionRequest.test-d.ts new file mode 100644 index 0000000000..460408515c --- /dev/null +++ b/packages/register-tests/react/src/usePrepareTransactionRequest.test-d.ts @@ -0,0 +1,42 @@ +import { config as testConfig } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { usePrepareTransactionRequest } from 'wagmi' +import { celo, mainnet, optimism } from 'wagmi/chains' + +test('chain formatters', () => { + const { data } = usePrepareTransactionRequest({ + feeCurrency: '0x', + }) + if (data && data.chainId === celo.id) { + expectTypeOf(data.feeCurrency).toEqualTypeOf<`0x${string}` | undefined>() + } + + const { data: data2 } = usePrepareTransactionRequest({ + chainId: celo.id, + feeCurrency: '0x', + }) + if (data2) { + expectTypeOf(data2.chainId).toEqualTypeOf(celo.id) + expectTypeOf(data2.feeCurrency).toEqualTypeOf<`0x${string}` | undefined>() + } + + usePrepareTransactionRequest({ + chainId: mainnet.id, + // @ts-expect-error + feeCurrency: '0x', + }) + + usePrepareTransactionRequest({ + chainId: optimism.id, + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('parameters: config', async () => { + usePrepareTransactionRequest({ + config: testConfig, + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/react/src/usePublicClient.ts b/packages/register-tests/react/src/usePublicClient.ts new file mode 100644 index 0000000000..55c6967ab2 --- /dev/null +++ b/packages/register-tests/react/src/usePublicClient.ts @@ -0,0 +1,38 @@ +import { type chain, config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { usePublicClient } from 'wagmi' +import { mainnet } from 'wagmi/chains' + +import type { ChainId } from './config.js' + +test('default', () => { + const client = usePublicClient() + expectTypeOf(client.chain.id).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: config', () => { + const client = usePublicClient({ config }) + expectTypeOf(client.chain.id).toEqualTypeOf< + (typeof config)['chains'][number]['id'] + >() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = usePublicClient({ + config, + chainId: mainnet.id, + }) + expectTypeOf(client.chain).toEqualTypeOf() + expectTypeOf(client.transport.type).toEqualTypeOf<'http'>() +}) + +test('behavior: unconfigured chain', () => { + const client = usePublicClient({ + config, + // @ts-expect-error + chainId: 123456, + }) + expectTypeOf(client).toEqualTypeOf() +}) diff --git a/packages/register-tests/react/src/useReadContract.test-d.ts b/packages/register-tests/react/src/useReadContract.test-d.ts new file mode 100644 index 0000000000..02ca83ed64 --- /dev/null +++ b/packages/register-tests/react/src/useReadContract.test-d.ts @@ -0,0 +1,24 @@ +import type { abi } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' +import type { useReadContract } from 'wagmi' + +import type { ChainId } from './config.js' + +test('UseReadContractParameters', () => { + type Result = NonNullable< + Parameters>[0] + > + expectTypeOf().toMatchTypeOf<{ + functionName?: + | 'symbol' + | 'name' + | 'allowance' + | 'balanceOf' + | 'decimals' + | 'totalSupply' + | undefined + args?: readonly [Address] | undefined + chainId?: ChainId | undefined + }>() +}) diff --git a/packages/register-tests/react/src/useReadContracts.test-d.ts b/packages/register-tests/react/src/useReadContracts.test-d.ts new file mode 100644 index 0000000000..bb719b7c49 --- /dev/null +++ b/packages/register-tests/react/src/useReadContracts.test-d.ts @@ -0,0 +1,45 @@ +import type { abi } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' +import type { useReadContracts } from 'wagmi' + +import type { ChainId } from './config.js' + +test('UseReadContractsParameters', () => { + type Result = NonNullable< + Parameters< + typeof useReadContracts< + [ + { + abi: typeof abi.erc20 + functionName: 'balanceOf' + address: Address + args: readonly [Address] + }, + ] + > + >[0] + >['contracts'] + expectTypeOf().toMatchTypeOf< + | readonly [ + { + abi?: typeof abi.erc20 | undefined + functionName?: + | 'approve' + | 'symbol' + | 'name' + | 'allowance' + | 'balanceOf' + | 'decimals' + | 'totalSupply' + | 'transfer' + | 'transferFrom' + | undefined + address?: Address | undefined + args?: readonly [Address] | undefined + chainId?: ChainId | undefined + }, + ] + | undefined + >() +}) diff --git a/packages/register-tests/react/src/useSendTransaction.test-d.ts b/packages/register-tests/react/src/useSendTransaction.test-d.ts new file mode 100644 index 0000000000..60839f5023 --- /dev/null +++ b/packages/register-tests/react/src/useSendTransaction.test-d.ts @@ -0,0 +1,55 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { useSendTransaction } from 'wagmi' +import { celo, mainnet, optimism } from 'wagmi/chains' +import type { ChainId } from './config.js' + +test('chain formatters', () => { + const { sendTransaction } = useSendTransaction() + + sendTransaction( + { + to: '0x', + feeCurrency: '0x', + }, + { + onSuccess(_data, variables) { + expectTypeOf(variables.chainId).toEqualTypeOf() + }, + }, + ) + + type Result = Parameters>[0] + expectTypeOf().toEqualTypeOf< + `0x${string}` | undefined + >() + sendTransaction({ + chainId: celo.id, + to: '0x', + feeCurrency: '0x', + }) + + sendTransaction({ + chainId: mainnet.id, + to: '0x', + // @ts-expect-error + feeCurrency: '0x', + }) + + sendTransaction({ + chainId: optimism.id, + to: '0x', + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('parameters: config', async () => { + const { sendTransaction } = useSendTransaction({ config }) + + sendTransaction({ + to: '0x', + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/react/src/useSimulateContract.test-d.ts b/packages/register-tests/react/src/useSimulateContract.test-d.ts new file mode 100644 index 0000000000..96c75d76e8 --- /dev/null +++ b/packages/register-tests/react/src/useSimulateContract.test-d.ts @@ -0,0 +1,94 @@ +import { type abi, config as testConfig } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' +import { type UseSimulateContractParameters, useSimulateContract } from 'wagmi' +import type { SimulateContractParameters } from 'wagmi/actions' +import { celo, mainnet, optimism } from 'wagmi/chains' +import type { SimulateContractOptions } from 'wagmi/query' + +import type { ChainId, config } from './config.js' + +test('chain formatters', () => { + const { data } = useSimulateContract({ + feeCurrency: '0x', + }) + if (data && data.chainId === celo.id) { + expectTypeOf(data.request.feeCurrency).toEqualTypeOf< + `0x${string}` | undefined + >() + } + + const { data: data2 } = useSimulateContract({ + chainId: celo.id, + feeCurrency: '0x', + }) + if (data2) { + expectTypeOf(data2.request.feeCurrency).toEqualTypeOf< + `0x${string}` | undefined + >() + } + + useSimulateContract({ + chainId: mainnet.id, + // @ts-expect-error + feeCurrency: '0x', + }) + + useSimulateContract({ + chainId: optimism.id, + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('UseSimulateContractParameters', () => { + type Result = UseSimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config + > + + expectTypeOf<{ + functionName?: 'approve' | 'transfer' | 'transferFrom' | undefined + args?: readonly [Address, Address, bigint] | undefined + chainId?: ChainId | undefined + }>().toMatchTypeOf() + + type Result2 = UseSimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof celo.id + > + expectTypeOf().toEqualTypeOf() + expectTypeOf().toEqualTypeOf< + `0x${string}` | undefined + >() + + type Result3 = SimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof celo.id + > + expectTypeOf().toEqualTypeOf() + type Result4 = SimulateContractOptions< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof celo.id + > + expectTypeOf().toEqualTypeOf() +}) + +test('parameters: config', async () => { + useSimulateContract({ + config: testConfig, + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/react/src/useSwitchChain.test-d.ts b/packages/register-tests/react/src/useSwitchChain.test-d.ts new file mode 100644 index 0000000000..2d02557b85 --- /dev/null +++ b/packages/register-tests/react/src/useSwitchChain.test-d.ts @@ -0,0 +1,25 @@ +import { expectTypeOf, test } from 'vitest' +import { useSwitchChain } from 'wagmi' +import { type celo, mainnet, type optimism } from 'wagmi/chains' + +import type { ChainId, config } from './config.js' + +test('default', () => { + const { chains, switchChain } = useSwitchChain() + + expectTypeOf(chains).toEqualTypeOf<(typeof config)['chains']>() + expectTypeOf(chains[0]).toEqualTypeOf() + expectTypeOf(chains[2]).toEqualTypeOf() + + switchChain( + { chainId: 1 }, + { + onSuccess(data) { + expectTypeOf(data).toEqualTypeOf(mainnet) + }, + }, + ) + + type Result = Parameters[0] + expectTypeOf().toEqualTypeOf() +}) diff --git a/packages/register-tests/react/src/useTransaction.test-d.ts b/packages/register-tests/react/src/useTransaction.test-d.ts new file mode 100644 index 0000000000..135b99bb2d --- /dev/null +++ b/packages/register-tests/react/src/useTransaction.test-d.ts @@ -0,0 +1,23 @@ +import { config } from '@wagmi/test' +import { expectTypeOf, test } from 'vitest' +import { useTransaction } from 'wagmi' +import { celo } from 'wagmi/chains' + +test('chain formatters', () => { + const result = useTransaction() + if (result.data?.chainId === celo.id) { + expectTypeOf(result.data.feeCurrency).toEqualTypeOf<`0x${string}` | null>() + } + + const result2 = useTransaction({ chainId: celo.id }) + expectTypeOf(result2.data?.feeCurrency).toEqualTypeOf< + `0x${string}` | null | undefined + >() +}) + +test('parameters: config', async () => { + const result = useTransaction({ config }) + + if (result.data && 'feeCurrency' in result.data) + expectTypeOf(result.data.feeCurrency).toEqualTypeOf() +}) diff --git a/packages/register-tests/react/src/useTransactionConfirmations.test-d.ts b/packages/register-tests/react/src/useTransactionConfirmations.test-d.ts new file mode 100644 index 0000000000..fe2445db21 --- /dev/null +++ b/packages/register-tests/react/src/useTransactionConfirmations.test-d.ts @@ -0,0 +1,66 @@ +import { config as testConfig } from '@wagmi/test' +import { test } from 'vitest' +import { useTransactionConfirmations } from 'wagmi' +import { mainnet, zkSync } from 'wagmi/chains' + +const transactionReceipt = { + blockHash: '0x', + blockNumber: 1n, + contractAddress: '0x', + cumulativeGasUsed: 1n, + effectiveGasPrice: 1n, + from: '0x', + gasUsed: 1n, + logsBloom: '0x', + status: 'success', + to: '0x', + transactionHash: '0x', + transactionIndex: 1, + type: 'eip1559', +} as const + +test('chain formatters', async () => { + useTransactionConfirmations({ + transactionReceipt: { + ...transactionReceipt, + l1BatchNumber: 1n, + l1BatchTxIndex: 1n, + logs: [], + l2ToL1Logs: [], + }, + }) + + useTransactionConfirmations({ + chainId: zkSync.id, + transactionReceipt: { + ...transactionReceipt, + l1BatchNumber: 1n, + l1BatchTxIndex: 1n, + logs: [], + l2ToL1Logs: [], + }, + }) + + useTransactionConfirmations({ + chainId: mainnet.id, + transactionReceipt: { + ...transactionReceipt, + // @ts-expect-error + l1BatchNumber: 1n, + l1BatchTxIndex: 1n, + logs: [], + l2ToL1Logs: [], + }, + }) +}) + +test('parameters: config', async () => { + useTransactionConfirmations({ + config: testConfig, + transactionReceipt: { + ...transactionReceipt, + // @ts-expect-error + l1BatchNumber: 1n, + }, + }) +}) diff --git a/packages/register-tests/react/src/useTransactionReceipt.test-d.ts b/packages/register-tests/react/src/useTransactionReceipt.test-d.ts new file mode 100644 index 0000000000..1bbd8183da --- /dev/null +++ b/packages/register-tests/react/src/useTransactionReceipt.test-d.ts @@ -0,0 +1,31 @@ +import { config } from '@wagmi/test' +import type { ZkSyncL2ToL1Log, ZkSyncLog } from 'viem/zksync' +import { expectTypeOf, test } from 'vitest' +import { useTransactionReceipt } from 'wagmi' +import { zkSync } from 'wagmi/chains' + +test('chain formatters', () => { + const result = useTransactionReceipt() + + if (result.data?.chainId === zkSync.id) { + expectTypeOf(result.data.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result.data.l1BatchTxIndex).toEqualTypeOf() + expectTypeOf(result.data.logs).toEqualTypeOf() + expectTypeOf(result.data.l2ToL1Logs).toEqualTypeOf() + } + + const result2 = useTransactionReceipt({ chainId: zkSync.id }) + if (result2.data) { + expectTypeOf(result2.data.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result2.data.l1BatchTxIndex).toEqualTypeOf() + expectTypeOf(result2.data.logs).toEqualTypeOf() + expectTypeOf(result2.data.l2ToL1Logs).toEqualTypeOf() + } +}) + +test('parameters: config', async () => { + const result = useTransactionReceipt({ config }) + + if (result.data && 'l1BatchNumber' in result.data) + expectTypeOf(result.data.l1BatchNumber).toEqualTypeOf() +}) diff --git a/packages/register-tests/react/src/useWaitForTransactionReceipt.ts b/packages/register-tests/react/src/useWaitForTransactionReceipt.ts new file mode 100644 index 0000000000..0d2501f500 --- /dev/null +++ b/packages/register-tests/react/src/useWaitForTransactionReceipt.ts @@ -0,0 +1,31 @@ +import { config } from '@wagmi/test' +import type { ZkSyncL2ToL1Log, ZkSyncLog } from 'viem/zksync' +import { expectTypeOf, test } from 'vitest' +import { useWaitForTransactionReceipt } from 'wagmi' +import { zkSync } from 'wagmi/chains' + +test('chain formatters', () => { + const result = useWaitForTransactionReceipt() + + if (result.data?.chainId === zkSync.id) { + expectTypeOf(result.data.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result.data.l1BatchTxIndex).toEqualTypeOf() + expectTypeOf(result.data.logs).toEqualTypeOf() + expectTypeOf(result.data.l2ToL1Logs).toEqualTypeOf() + } + + const result2 = useWaitForTransactionReceipt({ chainId: zkSync.id }) + if (result2.data) { + expectTypeOf(result2.data.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result2.data.l1BatchTxIndex).toEqualTypeOf() + expectTypeOf(result2.data.logs).toEqualTypeOf() + expectTypeOf(result2.data.l2ToL1Logs).toEqualTypeOf() + } +}) + +test('parameters: config', async () => { + const result = useWaitForTransactionReceipt({ config }) + + if (result.data && 'l1BatchNumber' in result.data) + expectTypeOf(result.data.l1BatchNumber).toEqualTypeOf() +}) diff --git a/packages/register-tests/react/src/useWriteContract.test-d.ts b/packages/register-tests/react/src/useWriteContract.test-d.ts new file mode 100644 index 0000000000..c499ff8f37 --- /dev/null +++ b/packages/register-tests/react/src/useWriteContract.test-d.ts @@ -0,0 +1,65 @@ +import { abi, config } from '@wagmi/test' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' +import { useWriteContract } from 'wagmi' +import { celo, mainnet, optimism } from 'wagmi/chains' + +test('chain formatters', () => { + const { writeContract } = useWriteContract() + + const shared = { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + } as const + + writeContract({ + ...shared, + feeCurrency: '0x', + }) + + type Result = Parameters< + typeof writeContract< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof celo.id + > + >[0] + expectTypeOf().toEqualTypeOf< + `0x${string}` | undefined + >() + writeContract({ + ...shared, + chainId: celo.id, + feeCurrency: '0x', + }) + + writeContract({ + ...shared, + chainId: mainnet.id, + // @ts-expect-error + feeCurrency: '0x', + }) + + writeContract({ + ...shared, + chainId: optimism.id, + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('parameters: config', async () => { + const { writeContract } = useWriteContract({ config }) + + writeContract({ + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/react/tsconfig.json b/packages/register-tests/react/tsconfig.json new file mode 100644 index 0000000000..77a211dbbb --- /dev/null +++ b/packages/register-tests/react/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": [] +} diff --git a/packages/register-tests/vue/package.json b/packages/register-tests/vue/package.json new file mode 100644 index 0000000000..fc41697d7f --- /dev/null +++ b/packages/register-tests/vue/package.json @@ -0,0 +1,13 @@ +{ + "name": "vue-register", + "private": true, + "type": "module", + "scripts": { + "check:types": "tsc --noEmit" + }, + "dependencies": { + "@tanstack/vue-query": "catalog:", + "@wagmi/vue": "workspace:*", + "vue": "catalog:" + } +} diff --git a/packages/register-tests/vue/src/config.ts b/packages/register-tests/vue/src/config.ts new file mode 100644 index 0000000000..fd13c5a6be --- /dev/null +++ b/packages/register-tests/vue/src/config.ts @@ -0,0 +1,26 @@ +import { createConfig, mock } from '@wagmi/vue' +import { celo, mainnet, optimism, zkSync } from '@wagmi/vue/chains' +import { http } from 'viem' + +export const config = createConfig({ + chains: [celo, mainnet, optimism, zkSync], + connectors: [mock({ accounts: ['0x'] })], + transports: { + [celo.id]: http(), + [mainnet.id]: http(), + [optimism.id]: http(), + [zkSync.id]: http(), + }, +}) + +export type ChainId = + | typeof celo.id + | typeof mainnet.id + | typeof optimism.id + | typeof zkSync.id + +declare module '@wagmi/vue' { + interface Register { + config: typeof config + } +} diff --git a/packages/register-tests/vue/src/useAccount.test-d.ts b/packages/register-tests/vue/src/useAccount.test-d.ts new file mode 100644 index 0000000000..6471bf4550 --- /dev/null +++ b/packages/register-tests/vue/src/useAccount.test-d.ts @@ -0,0 +1,17 @@ +import { config } from '@wagmi/test' +import { useAccount } from '@wagmi/vue' +import { expectTypeOf, test } from 'vitest' + +import type { ChainId } from './config.js' + +test('default', () => { + const result = useAccount() + if (result.chain.value) + expectTypeOf(result.chain.value.id).toEqualTypeOf() +}) + +test('parameters: config', async () => { + const result = useAccount({ config }) + if (result.chain.value) + expectTypeOf(result.chain.value.id).toEqualTypeOf<1 | 10 | 456>() +}) diff --git a/packages/register-tests/vue/src/useChainId.test-d.ts b/packages/register-tests/vue/src/useChainId.test-d.ts new file mode 100644 index 0000000000..ffeddcb6ba --- /dev/null +++ b/packages/register-tests/vue/src/useChainId.test-d.ts @@ -0,0 +1,15 @@ +import { config } from '@wagmi/test' +import { useChainId } from '@wagmi/vue' +import { expectTypeOf, test } from 'vitest' + +import type { ChainId } from './config.js' + +test('default', async () => { + const chainId = useChainId() + expectTypeOf(chainId.value).toEqualTypeOf() +}) + +test('parameters: config', async () => { + const chainId = useChainId({ config }) + expectTypeOf(chainId.value).toEqualTypeOf<1 | 456 | 10>() +}) diff --git a/packages/register-tests/vue/src/useChains.test-d.ts b/packages/register-tests/vue/src/useChains.test-d.ts new file mode 100644 index 0000000000..d1bf0eb151 --- /dev/null +++ b/packages/register-tests/vue/src/useChains.test-d.ts @@ -0,0 +1,11 @@ +import { useChains } from '@wagmi/vue' +import type { Chain, celo, optimism } from '@wagmi/vue/chains' +import { expectTypeOf, test } from 'vitest' + +test('default', () => { + const chains = useChains() + + expectTypeOf(chains.value[0]).toEqualTypeOf() + expectTypeOf(chains.value[2]).toEqualTypeOf() + expectTypeOf(chains.value[5]).toEqualTypeOf() +}) diff --git a/packages/register-tests/vue/src/useClient.test-d.ts b/packages/register-tests/vue/src/useClient.test-d.ts new file mode 100644 index 0000000000..99cea8d3f2 --- /dev/null +++ b/packages/register-tests/vue/src/useClient.test-d.ts @@ -0,0 +1,38 @@ +import { type chain, config } from '@wagmi/test' +import { useClient } from '@wagmi/vue' +import { mainnet } from '@wagmi/vue/chains' +import { expectTypeOf, test } from 'vitest' + +import type { ChainId } from './config.js' + +test('default', () => { + const client = useClient() + expectTypeOf(client.value.chain.id).toEqualTypeOf() + expectTypeOf(client.value.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: config', () => { + const client = useClient({ config }) + expectTypeOf(client.value.chain.id).toEqualTypeOf< + (typeof config)['chains'][number]['id'] + >() + expectTypeOf(client.value.transport.type).toEqualTypeOf<'http'>() +}) + +test('parameters: chainId', () => { + const client = useClient({ + config, + chainId: mainnet.id, + }) + expectTypeOf(client.value.chain).toEqualTypeOf() + expectTypeOf(client.value.transport.type).toEqualTypeOf<'http'>() +}) + +test('behavior: unconfigured chain', () => { + const client = useClient({ + config, + // @ts-expect-error + chainId: 123456, + }) + expectTypeOf(client.value).toEqualTypeOf() +}) diff --git a/packages/register-tests/vue/src/useConfig.test-d.ts b/packages/register-tests/vue/src/useConfig.test-d.ts new file mode 100644 index 0000000000..c65f70c214 --- /dev/null +++ b/packages/register-tests/vue/src/useConfig.test-d.ts @@ -0,0 +1,17 @@ +import { config as testConfig } from '@wagmi/test' +import { type Config, useConfig } from '@wagmi/vue' +import { expectTypeOf, test } from 'vitest' + +import type { config } from './config.js' + +test('default', async () => { + const result = useConfig() + expectTypeOf(result).not.toEqualTypeOf() + expectTypeOf(result).toEqualTypeOf() +}) + +test('parameters: config', async () => { + const result = useConfig({ config: testConfig }) + expectTypeOf(result).not.toEqualTypeOf() + expectTypeOf(result).toEqualTypeOf() +}) diff --git a/packages/register-tests/vue/src/useConnect.test-d.ts b/packages/register-tests/vue/src/useConnect.test-d.ts new file mode 100644 index 0000000000..128834ff6d --- /dev/null +++ b/packages/register-tests/vue/src/useConnect.test-d.ts @@ -0,0 +1,13 @@ +import { useConnect } from '@wagmi/vue' +import { expectTypeOf, test } from 'vitest' + +test('infers connect parameters', () => { + const { connect, connectors, variables } = useConnect() + const connector = connectors[0]! + + expectTypeOf(variables.value?.foo).toEqualTypeOf() + connect({ + connector, + foo: 'bar', + }) +}) diff --git a/packages/register-tests/vue/src/useReadContract.test-d.ts b/packages/register-tests/vue/src/useReadContract.test-d.ts new file mode 100644 index 0000000000..c2a1a6386a --- /dev/null +++ b/packages/register-tests/vue/src/useReadContract.test-d.ts @@ -0,0 +1,30 @@ +import type { abi } from '@wagmi/test' +import type { useReadContract } from '@wagmi/vue' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import type { DeepUnwrapRef } from '../../../vue/src/types/ref.js' +import type { ChainId } from './config.js' + +test('UseReadContractParameters', () => { + type Result = DeepUnwrapRef< + NonNullable< + Parameters< + typeof useReadContract + >[0] + > + > + + expectTypeOf().toMatchTypeOf<{ + functionName?: + | 'symbol' + | 'name' + | 'allowance' + | 'balanceOf' + | 'decimals' + | 'totalSupply' + | undefined + args?: readonly [Address] | undefined + chainId?: ChainId | undefined + }>() +}) diff --git a/packages/register-tests/vue/src/useSendTransaction.test-d.ts b/packages/register-tests/vue/src/useSendTransaction.test-d.ts new file mode 100644 index 0000000000..c49a8ef386 --- /dev/null +++ b/packages/register-tests/vue/src/useSendTransaction.test-d.ts @@ -0,0 +1,55 @@ +import { config } from '@wagmi/test' +import { useSendTransaction } from '@wagmi/vue' +import { celo, mainnet, optimism } from '@wagmi/vue/chains' +import { expectTypeOf, test } from 'vitest' +import type { ChainId } from './config.js' + +test('chain formatters', () => { + const { sendTransaction } = useSendTransaction() + + sendTransaction( + { + to: '0x', + feeCurrency: '0x', + }, + { + onSuccess(_data, variables) { + expectTypeOf(variables.chainId).toEqualTypeOf() + }, + }, + ) + + type Result = Parameters>[0] + expectTypeOf().toEqualTypeOf< + `0x${string}` | undefined + >() + sendTransaction({ + chainId: celo.id, + to: '0x', + feeCurrency: '0x', + }) + + sendTransaction({ + chainId: mainnet.id, + to: '0x', + // @ts-expect-error + feeCurrency: '0x', + }) + + sendTransaction({ + chainId: optimism.id, + to: '0x', + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('parameters: config', async () => { + const { sendTransaction } = useSendTransaction({ config }) + + sendTransaction({ + to: '0x', + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/vue/src/useSimulateContract.test-d.ts b/packages/register-tests/vue/src/useSimulateContract.test-d.ts new file mode 100644 index 0000000000..2a2b65c4d5 --- /dev/null +++ b/packages/register-tests/vue/src/useSimulateContract.test-d.ts @@ -0,0 +1,86 @@ +import { type abi, config as testConfig } from '@wagmi/test' +import { + type UseSimulateContractParameters, + useSimulateContract, +} from '@wagmi/vue' +import type { SimulateContractParameters } from '@wagmi/vue/actions' +import { celo, mainnet, optimism } from '@wagmi/vue/chains' +import type { SimulateContractOptions } from '@wagmi/vue/query' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' + +import type { ChainId, config } from './config.js' + +test('chain formatters', () => { + const { data } = useSimulateContract({ + feeCurrency: '0x', + }) + if (data.value && data.value.chainId === celo.id) { + expectTypeOf(data.value.request.feeCurrency).toEqualTypeOf< + `0x${string}` | undefined + >() + } + + const { data: data2 } = useSimulateContract({ + chainId: celo.id, + feeCurrency: '0x', + }) + if (data2.value) { + expectTypeOf(data2.value.request.feeCurrency).toEqualTypeOf< + `0x${string}` | undefined + >() + } + + useSimulateContract({ + chainId: mainnet.id, + // @ts-expect-error + feeCurrency: '0x', + }) + + useSimulateContract({ + chainId: optimism.id, + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('UseSimulateContractParameters', () => { + type Result = UseSimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config + > + + expectTypeOf<{ + functionName?: 'approve' | 'transfer' | 'transferFrom' | undefined + args?: readonly [Address, Address, bigint] | undefined + chainId?: ChainId | undefined + }>().toMatchTypeOf() + + type Result2 = SimulateContractParameters< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof celo.id + > + expectTypeOf().toEqualTypeOf() + + type Result3 = SimulateContractOptions< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof config, + typeof celo.id + > + expectTypeOf().toEqualTypeOf() +}) + +test('parameters: config', async () => { + useSimulateContract({ + config: testConfig, + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/vue/src/useSwitchChain.test-d.ts b/packages/register-tests/vue/src/useSwitchChain.test-d.ts new file mode 100644 index 0000000000..cf0153b3b7 --- /dev/null +++ b/packages/register-tests/vue/src/useSwitchChain.test-d.ts @@ -0,0 +1,27 @@ +import { useSwitchChain } from '@wagmi/vue' +import { type celo, mainnet, type optimism } from '@wagmi/vue/chains' +import { expectTypeOf, test } from 'vitest' + +import type { ChainId, config } from './config.js' + +test('default', () => { + const switchChain = useSwitchChain() + + const chains = switchChain.chains.value + + expectTypeOf(chains).toEqualTypeOf<(typeof config)['chains']>() + expectTypeOf(chains[0]).toEqualTypeOf() + expectTypeOf(chains[2]).toEqualTypeOf() + + switchChain.switchChain( + { chainId: 1 }, + { + onSuccess(data) { + expectTypeOf(data).toEqualTypeOf(mainnet) + }, + }, + ) + + type Result = Parameters<(typeof switchChain)['switchChain']>[0] + expectTypeOf().toEqualTypeOf() +}) diff --git a/packages/register-tests/vue/src/useTransaction.test-d.ts b/packages/register-tests/vue/src/useTransaction.test-d.ts new file mode 100644 index 0000000000..7bc262c38f --- /dev/null +++ b/packages/register-tests/vue/src/useTransaction.test-d.ts @@ -0,0 +1,25 @@ +import { config } from '@wagmi/test' +import { useTransaction } from '@wagmi/vue' +import { celo } from '@wagmi/vue/chains' +import { expectTypeOf, test } from 'vitest' + +test('chain formatters', () => { + const result = useTransaction() + if (result.data?.value?.chainId === celo.id) { + expectTypeOf(result.data.value.feeCurrency).toEqualTypeOf< + `0x${string}` | null + >() + } + + const result2 = useTransaction({ chainId: celo.id }) + expectTypeOf(result2.data?.value?.feeCurrency).toEqualTypeOf< + `0x${string}` | null | undefined + >() +}) + +test('parameters: config', async () => { + const result = useTransaction({ config }) + + if (result.data && 'feeCurrency' in result.data) + expectTypeOf(result.data.feeCurrency).toEqualTypeOf() +}) diff --git a/packages/register-tests/vue/src/useTransactionReceipt.test-d.ts b/packages/register-tests/vue/src/useTransactionReceipt.test-d.ts new file mode 100644 index 0000000000..c202d82dbb --- /dev/null +++ b/packages/register-tests/vue/src/useTransactionReceipt.test-d.ts @@ -0,0 +1,41 @@ +import { config } from '@wagmi/test' +import { useTransactionReceipt } from '@wagmi/vue' +import { zkSync } from '@wagmi/vue/chains' +import type { ZkSyncL2ToL1Log, ZkSyncLog } from 'viem/zksync' +import { expectTypeOf, test } from 'vitest' + +test('chain formatters', () => { + const result = useTransactionReceipt() + + if (result.data?.value?.chainId === zkSync.id) { + expectTypeOf(result.data.value.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result.data.value.l1BatchTxIndex).toEqualTypeOf< + bigint | null + >() + expectTypeOf(result.data.value.logs).toEqualTypeOf() + expectTypeOf(result.data.value.l2ToL1Logs).toEqualTypeOf< + ZkSyncL2ToL1Log[] + >() + } + + const result2 = useTransactionReceipt({ chainId: zkSync.id }) + if (result2.data.value) { + expectTypeOf(result2.data.value.l1BatchNumber).toEqualTypeOf< + bigint | null + >() + expectTypeOf(result2.data.value.l1BatchTxIndex).toEqualTypeOf< + bigint | null + >() + expectTypeOf(result2.data.value.logs).toEqualTypeOf() + expectTypeOf(result2.data.value.l2ToL1Logs).toEqualTypeOf< + ZkSyncL2ToL1Log[] + >() + } +}) + +test('parameters: config', async () => { + const result = useTransactionReceipt({ config }) + + if (result.data && 'l1BatchNumber' in result.data) + expectTypeOf(result.data.l1BatchNumber).toEqualTypeOf() +}) diff --git a/packages/register-tests/vue/src/useWaitForTransaction.test-d.ts b/packages/register-tests/vue/src/useWaitForTransaction.test-d.ts new file mode 100644 index 0000000000..e0286617c1 --- /dev/null +++ b/packages/register-tests/vue/src/useWaitForTransaction.test-d.ts @@ -0,0 +1,41 @@ +import { config } from '@wagmi/test' +import { useWaitForTransactionReceipt } from '@wagmi/vue' +import { zkSync } from '@wagmi/vue/chains' +import type { ZkSyncL2ToL1Log, ZkSyncLog } from 'viem/zksync' +import { expectTypeOf, test } from 'vitest' + +test('chain formatters', () => { + const result = useWaitForTransactionReceipt() + + if (result.data?.value?.chainId === zkSync.id) { + expectTypeOf(result.data.value.l1BatchNumber).toEqualTypeOf() + expectTypeOf(result.data.value.l1BatchTxIndex).toEqualTypeOf< + bigint | null + >() + expectTypeOf(result.data.value.logs).toEqualTypeOf() + expectTypeOf(result.data.value.l2ToL1Logs).toEqualTypeOf< + ZkSyncL2ToL1Log[] + >() + } + + const result2 = useWaitForTransactionReceipt({ chainId: zkSync.id }) + if (result2.data.value) { + expectTypeOf(result2.data.value.l1BatchNumber).toEqualTypeOf< + bigint | null + >() + expectTypeOf(result2.data.value.l1BatchTxIndex).toEqualTypeOf< + bigint | null + >() + expectTypeOf(result2.data.value.logs).toEqualTypeOf() + expectTypeOf(result2.data.value.l2ToL1Logs).toEqualTypeOf< + ZkSyncL2ToL1Log[] + >() + } +}) + +test('parameters: config', async () => { + const result = useWaitForTransactionReceipt({ config }) + + if (result.data && 'l1BatchNumber' in result.data) + expectTypeOf(result.data.l1BatchNumber).toEqualTypeOf() +}) diff --git a/packages/register-tests/vue/src/useWriteContract.test-d.ts b/packages/register-tests/vue/src/useWriteContract.test-d.ts new file mode 100644 index 0000000000..ec4f38430c --- /dev/null +++ b/packages/register-tests/vue/src/useWriteContract.test-d.ts @@ -0,0 +1,65 @@ +import { abi, config } from '@wagmi/test' +import { useWriteContract } from '@wagmi/vue' +import { celo, mainnet, optimism } from '@wagmi/vue/chains' +import type { Address } from 'viem' +import { expectTypeOf, test } from 'vitest' + +test('chain formatters', () => { + const { writeContract } = useWriteContract() + + const shared = { + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + } as const + + writeContract({ + ...shared, + feeCurrency: '0x', + }) + + type Result = Parameters< + typeof writeContract< + typeof abi.erc20, + 'transferFrom', + [Address, Address, bigint], + typeof celo.id + > + >[0] + expectTypeOf().toEqualTypeOf< + `0x${string}` | undefined + >() + writeContract({ + ...shared, + chainId: celo.id, + feeCurrency: '0x', + }) + + writeContract({ + ...shared, + chainId: mainnet.id, + // @ts-expect-error + feeCurrency: '0x', + }) + + writeContract({ + ...shared, + chainId: optimism.id, + // @ts-expect-error + feeCurrency: '0x', + }) +}) + +test('parameters: config', async () => { + const { writeContract } = useWriteContract({ config }) + + writeContract({ + address: '0x', + abi: abi.erc20, + functionName: 'transferFrom', + args: ['0x', '0x', 123n], + // @ts-expect-error + feeCurrency: '0x', + }) +}) diff --git a/packages/register-tests/vue/tsconfig.json b/packages/register-tests/vue/tsconfig.json new file mode 100644 index 0000000000..77a211dbbb --- /dev/null +++ b/packages/register-tests/vue/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": [] +} diff --git a/packages/sequence-core-1.0.0/.changeset/README.md b/packages/sequence-core-1.0.0/.changeset/README.md new file mode 100644 index 0000000000..e5b6d8d6a6 --- /dev/null +++ b/packages/sequence-core-1.0.0/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/packages/sequence-core-1.0.0/.changeset/config.json b/packages/sequence-core-1.0.0/.changeset/config.json new file mode 100644 index 0000000000..6b372552ca --- /dev/null +++ b/packages/sequence-core-1.0.0/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/packages/sequence-core-1.0.0/.github/CODEOWNERS b/packages/sequence-core-1.0.0/.github/CODEOWNERS new file mode 100644 index 0000000000..7f34c7a889 --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/CODEOWNERS @@ -0,0 +1 @@ +* @0xsequence/disable-codeowners-notifications @0xsequence/core diff --git a/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/bug_report.md b/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..b5c68e55be --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] + +**Smartphone (please complete the following information):** + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/custom.md b/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000..96a47352ab --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,7 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' +--- diff --git a/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/feature_request.md b/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..2f28cead03 --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +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/packages/sequence-core-1.0.0/.github/actions/install-dependencies/action.yml b/packages/sequence-core-1.0.0/.github/actions/install-dependencies/action.yml new file mode 100644 index 0000000000..ca81d1a40a --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/actions/install-dependencies/action.yml @@ -0,0 +1,39 @@ +name: Setup Node and PNPM dependencies + +runs: + using: 'composite' + + steps: + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Setup PNPM + uses: pnpm/action-setup@v3 + with: + version: 10 + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: | + ${{ steps.pnpm-cache.outputs.STORE_PATH }} + node_modules + packages/*/node_modules + ~/.cache/puppeteer + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + shell: bash + run: pnpm install --frozen-lockfile + if: ${{ steps.pnpm-cache.outputs.cache-hit != 'true' }} diff --git a/packages/sequence-core-1.0.0/.github/workflows/on_pr_pnpm-format-label.yml b/packages/sequence-core-1.0.0/.github/workflows/on_pr_pnpm-format-label.yml new file mode 100644 index 0000000000..84fb27cb3e --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/workflows/on_pr_pnpm-format-label.yml @@ -0,0 +1,23 @@ +name: pnpm-format-label + +on: + pull_request: + types: [labeled] + +jobs: + proto: + if: ${{ github.event.label.name == 'pnpm format' }} + uses: ./.github/workflows/pnpm-format.yml + secrets: inherit + + rm: + if: ${{ github.event.label.name == 'pnpm format' }} + runs-on: ubuntu-latest + steps: + - name: Remove the label + run: | + LABEL=$(echo "${{ github.event.label.name }}" | sed 's/ /%20/g') + curl -X DELETE \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/$LABEL diff --git a/packages/sequence-core-1.0.0/.github/workflows/pnpm-format.yml b/packages/sequence-core-1.0.0/.github/workflows/pnpm-format.yml new file mode 100644 index 0000000000..1be36e1a6b --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/workflows/pnpm-format.yml @@ -0,0 +1,27 @@ +name: pnpm format + +on: + workflow_call: + +jobs: + run: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + fetch-depth: 20 + + - uses: ./.github/actions/install-dependencies + + - run: pnpm format + + - name: Commit back + uses: 0xsequence/actions/git-commit@v0.0.4 + env: + API_TOKEN_GITHUB: ${{ secrets.GH_TOKEN_GIT_COMMIT }} + with: + files: './' + branch: ${{ github.head_ref }} + commit_message: '[AUTOMATED] pnpm format' diff --git a/packages/sequence-core-1.0.0/.github/workflows/publish-dists.yml b/packages/sequence-core-1.0.0/.github/workflows/publish-dists.yml new file mode 100644 index 0000000000..fd4bb79054 --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/workflows/publish-dists.yml @@ -0,0 +1,88 @@ +name: Publish Dists for Packages + +on: + workflow_dispatch: + push: + branches: + - master + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: ./.github/actions/install-dependencies + + - name: Build package + run: pnpm run build + + - name: Prepare dist branch + run: | + PACKAGES=("services/guard" "services/identity-instrument" "services/relayer" "wallet/core" "wallet/primitives" "wallet/wdk" "wallet/dapp-client") + + for PACKAGE in "${PACKAGES[@]}"; do + BRANCH="dists/$PACKAGE" + PKG_DIR="packages/$PACKAGE" + + echo "📦 Publishing $PACKAGE to $BRANCH" + + mkdir -p /tmp/$PACKAGE + shopt -s dotglob + cp -r $PKG_DIR/* /tmp/$PACKAGE || true + + cd /tmp/$PACKAGE + git init + git checkout -b $BRANCH + + git config user.name "github-actions" + git config user.email "actions@github.com" + + echo "🔧 Rewriting workspace: deps in package.json..." + node -e ' + const fs = require("fs"); + const path = require("path"); + const pkgPath = path.resolve("package.json"); + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); + const repo = "github:0xsequence/sequence.js"; + + const versions = { + "@0xsequence/guard": `${repo}#dists/services/guard`, + "@0xsequence/identity-instrument": `${repo}#dists/services/identity-instrument`, + "@0xsequence/relayer": `${repo}#dists/services/relayer`, + "@0xsequence/wallet-core": `${repo}#dists/wallet/core`, + "@0xsequence/wallet-primitives": `${repo}#dists/wallet/primitives`, + "@0xsequence/wallet-wdk": `${repo}#dists/wallet/wdk`, + }; + + const rewrite = (deps = {}) => { + for (const k in deps) { + if (deps[k].startsWith("workspace:")) { + const version = versions[k]; + + if (!version) { + console.warn(`No version found for ${k}, skipping...`); + continue; + } + + deps[k] = version; + console.log(`→ ${k} → ${deps[k]}`); + } + } + }; + + rewrite(pkg.dependencies); + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)); + ' + + git add . + git commit -m "Build: publish $PACKAGE dist" + + git remote add origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git + git push -f origin HEAD:$BRANCH + + cd - + done diff --git a/packages/sequence-core-1.0.0/.github/workflows/tests.yml b/packages/sequence-core-1.0.0/.github/workflows/tests.yml new file mode 100644 index 0000000000..62fc357db0 --- /dev/null +++ b/packages/sequence-core-1.0.0/.github/workflows/tests.yml @@ -0,0 +1,63 @@ +on: [push] + +name: tests + +jobs: + install: + name: Install dependencies + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + + build: + name: Run build + runs-on: ubuntu-latest + needs: [install] + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + - run: pnpm clean + - run: pnpm build + + tests: + name: Run all tests + runs-on: ubuntu-latest + needs: [install] + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/install-dependencies + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + - name: Start Anvil in background + run: anvil --fork-url https://nodes.sequence.app/arbitrum & + - run: pnpm test + + # NOTE: if you'd like to see example of how to run + # tests per package in parallel, see 'v2' branch + # .github/workflows/tests.yml + + # coverage: + # name: Run coverage + # runs-on: ubuntu-latest + # needs: [install] + # steps: + # - uses: actions/checkout@v4 + # - uses: actions/setup-node@v4 + # with: + # node-version: 20 + # - uses: actions/cache@v4 + # id: pnpm-cache + # with: + # path: | + # node_modules + # */*/node_modules + # key: ${{ runner.os }}-install-${{ hashFiles('**/package.json', '**/pnpm.lock') }} + # - run: pnpm dev && (pnpm coverage || true) + # - uses: codecov/codecov-action@v1 + # with: + # fail_ci_if_error: true + # verbose: true + # directory: ./coverage diff --git a/packages/sequence-core-1.0.0/.gitmodules b/packages/sequence-core-1.0.0/.gitmodules new file mode 100644 index 0000000000..6131d73996 --- /dev/null +++ b/packages/sequence-core-1.0.0/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/signals-implicit-mode"] + path = lib/signals-implicit-mode + url = https://github.com/0xsequence/signals-implicit-mode diff --git a/packages/sequence-core-1.0.0/.prettierrc b/packages/sequence-core-1.0.0/.prettierrc new file mode 100644 index 0000000000..cbe842acd7 --- /dev/null +++ b/packages/sequence-core-1.0.0/.prettierrc @@ -0,0 +1,5 @@ +{ + "printWidth": 120, + "semi": false, + "singleQuote": true +} diff --git a/packages/sequence-core-1.0.0/.vscode/launch.json b/packages/sequence-core-1.0.0/.vscode/launch.json new file mode 100644 index 0000000000..dc22920a87 --- /dev/null +++ b/packages/sequence-core-1.0.0/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch primitives-cli server", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/packages/wallet/primitives-cli/dist/index.js", + "args": ["server"], + "runtimeArgs": ["--enable-source-maps"], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/packages/wallet/primitives-cli/dist/**/*.js", + "${workspaceFolder}/packages/wallet/core/dist/**/*.js", + "${workspaceFolder}/packages/wallet/primitives/dist/**/*.js", + "${workspaceFolder}/packages/wallet/wdk/dist/**/*.js" + ], + "sourceMapPathOverrides": { + "../packages/wallet/primitives-cli/src/*": "${workspaceFolder}/packages/wallet/primitives-cli/src/*", + "../packages/wallet/core/src/*": "${workspaceFolder}/packages/wallet/core/src/*", + "../packages/wallet/primitives/src/*": "${workspaceFolder}/packages/wallet/primitives/src/*", + "../packages/wallet/wdk/src/*": "${workspaceFolder}/packages/wallet/wdk/src/*" + } + } + ] +} diff --git a/packages/sequence-core-1.0.0/.vscode/settings.json b/packages/sequence-core-1.0.0/.vscode/settings.json new file mode 100644 index 0000000000..44a73ec3a9 --- /dev/null +++ b/packages/sequence-core-1.0.0/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "eslint.workingDirectories": [ + { + "mode": "auto" + } + ] +} diff --git a/packages/sequence-core-1.0.0/LICENSE b/packages/sequence-core-1.0.0/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/packages/sequence-core-1.0.0/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/sequence-core-1.0.0/README.md b/packages/sequence-core-1.0.0/README.md new file mode 100644 index 0000000000..4c663ccf86 --- /dev/null +++ b/packages/sequence-core-1.0.0/README.md @@ -0,0 +1,39 @@ +## sequence.js v3 core libraries and SDK + +**NOTE: please see [v2](https://github.com/0xsequence/sequence.js/tree/v2) branch for sequence.js 2.x.x** + +--- + +Sequence v3 core libraries and [wallet-contracts-v3](https://github.com/0xsequence/wallet-contracts-v3) SDK. + +## Packages + +- `@0xsequence/wallet-primitives`: stateless low-level utilities specifically for interacting directly with sequence wallet's smart contracts +- `@0xsequence/wallet-core`: higher level utilities for creating and using sequence wallets +- `@0xsequence/wallet-wdk`: all-in-one wallet development kit for building a sequence wallet product + +## Development + +### Getting Started + +1. Install dependencies: + `pnpm install` + +2. Build all packages: + `pnpm build` + +### Development Workflow + +- Run development mode across all packages: + `pnpm dev` + +- Run tests: + `pnpm test` + + > **Note:** Tests require [anvil](https://github.com/foundry-rs/foundry/tree/master/anvil) and [forge](https://github.com/foundry-rs/foundry) to be installed. You can run a local anvil instance using `pnpm run test:anvil`. + +- Linting and formatting is enforced via git hooks + +## License + +Apache-2.0 diff --git a/packages/sequence-core-1.0.0/corepack.tgz b/packages/sequence-core-1.0.0/corepack.tgz new file mode 100644 index 0000000000000000000000000000000000000000..afa76b295c7db9783722ef00487b4fa4ddd20901 GIT binary patch literal 4311947 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 z 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'Os-&^VHk9;Z=xnsxJ` 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