From a9c53c6437dd6b2c31b944d64e2fa944ce95578e Mon Sep 17 00:00:00 2001 From: Gancho Radkov <43912948+ganchoradkov@users.noreply.github.com> Date: Tue, 7 May 2024 19:30:00 +0300 Subject: [PATCH] feat: Sign 2.5 (#2033) Co-authored-by: Gancho Radkov Co-authored-by: Glitch Co-authored-by: Glitch <66949816+glitch-txs@users.noreply.github.com> Co-authored-by: Sven Co-authored-by: tomiir Co-authored-by: Enes --- apps/demo/package.json | 2 +- apps/laboratory/package.json | 4 +- .../src/pages/api/auth/[...nextauth].ts | 32 +- .../src/pages/library/wagmi-siwe.tsx | 1 - apps/laboratory/src/utils/SiweUtils.ts | 33 +- .../tests/shared/pages/ModalPage.ts | 3 - apps/laboratory/tests/smart-account.spec.ts | 3 +- examples/html-wagmi/package.json | 4 +- examples/next-wagmi/package.json | 2 +- examples/react-wagmi/package.json | 2 +- examples/vue-wagmi/package.json | 6 +- package-lock.json | 708 ++++++++++++++++-- packages/core/src/utils/SwapApiUtil.ts | 49 +- packages/ethers/package.json | 2 +- packages/ethers/src/client.ts | 55 +- packages/ethers5/package.json | 2 +- packages/ethers5/src/client.ts | 52 +- .../siwe/core/controller/SIWEController.ts | 1 + packages/siwe/core/helpers/index.ts | 33 + packages/siwe/core/utils/TypeUtils.ts | 47 +- packages/siwe/exports/index.ts | 6 + packages/siwe/package.json | 1 + .../views/w3m-connecting-siwe-view/index.ts | 9 + packages/siwe/src/client.ts | 28 +- .../tests/controllers/SIWEController.test.ts | 9 +- packages/solana/src/client.ts | 20 +- packages/wagmi/.eslintrc.json | 3 +- packages/wagmi/exports/index.ts | 1 + packages/wagmi/package.json | 7 +- packages/wagmi/src/client.ts | 56 +- .../src/connectors/alphaWalletConnect.ts | 440 +++++++++++ .../wagmi/src/utils/defaultWagmiCoreConfig.ts | 5 +- .../src/utils/defaultWagmiReactConfig.ts | 5 +- 33 files changed, 1459 insertions(+), 172 deletions(-) create mode 100644 packages/siwe/core/helpers/index.ts create mode 100644 packages/wagmi/src/connectors/alphaWalletConnect.ts diff --git a/apps/demo/package.json b/apps/demo/package.json index b6b4026d85..95e243ff03 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -21,7 +21,7 @@ "tailwind-merge": "2.2.1", "vaul": "0.9.0", "viem": "2.7.19", - "wagmi": "2.5.7", + "wagmi": "2.5.19", "zustand": "4.5.2" }, "devDependencies": { diff --git a/apps/laboratory/package.json b/apps/laboratory/package.json index f43bd60ccd..826af4fb58 100644 --- a/apps/laboratory/package.json +++ b/apps/laboratory/package.json @@ -40,10 +40,10 @@ "next": "14.0.4", "next-auth": "4.24.5", "react-icons": "4.12.0", - "siwe": "2.1.4", + "@walletconnect/utils": "2.12.0", "valtio": "1.11.2", "viem": "2.9.3", - "wagmi": "2.5.7" + "wagmi": "2.5.19" }, "devDependencies": { "@aws-sdk/client-cloudwatch": "3.509.0", diff --git a/apps/laboratory/src/pages/api/auth/[...nextauth].ts b/apps/laboratory/src/pages/api/auth/[...nextauth].ts index bfdb989512..c62ea6e6a2 100644 --- a/apps/laboratory/src/pages/api/auth/[...nextauth].ts +++ b/apps/laboratory/src/pages/api/auth/[...nextauth].ts @@ -1,10 +1,12 @@ -import type { SIWESession } from '@web3modal/siwe' import type { NextApiRequest, NextApiResponse } from 'next' import nextAuth from 'next-auth' import credentialsProvider from 'next-auth/providers/credentials' -import { getCsrfToken } from 'next-auth/react' -import { SiweMessage } from 'siwe' -import { ethers } from 'ethers' +import { + type SIWESession, + verifySignature, + getChainIdFromMessage, + getAddressFromMessage +} from '@web3modal/siwe' declare module 'next-auth' { interface Session extends SIWESession { @@ -12,7 +14,6 @@ declare module 'next-auth' { chainId: number } } - /* * For more information on each option (and a full list of options) go to * https://next-auth.js.org/configuration/options @@ -47,22 +48,15 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) { if (!credentials?.message) { throw new Error('SiweMessage is undefined') } - const siwe = new SiweMessage(credentials.message) - const provider = new ethers.JsonRpcProvider( - `https://rpc.walletconnect.com/v1?chainId=eip155:${siwe.chainId}&projectId=${projectId}` - ) - const nonce = await getCsrfToken({ req: { headers: req.headers } }) - const result = await siwe.verify( - { - signature: credentials?.signature || '', - nonce - }, - { provider } - ) + const { message, signature } = credentials + const address = getAddressFromMessage(message) + const chainId = getChainIdFromMessage(message) + + const isValid = await verifySignature({ address, message, signature, chainId, projectId }) - if (result.success) { + if (isValid) { return { - id: `eip155:${siwe.chainId}:${siwe.address}` + id: `${chainId}:${address}` } } diff --git a/apps/laboratory/src/pages/library/wagmi-siwe.tsx b/apps/laboratory/src/pages/library/wagmi-siwe.tsx index 1a56b9184f..3cbd3350e3 100644 --- a/apps/laboratory/src/pages/library/wagmi-siwe.tsx +++ b/apps/laboratory/src/pages/library/wagmi-siwe.tsx @@ -18,7 +18,6 @@ export const wagmiConfig = defaultWagmiConfig({ metadata: ConstantsUtil.Metadata, ssr: true }) - const modal = createWeb3Modal({ wagmiConfig, projectId: ConstantsUtil.ProjectId, diff --git a/apps/laboratory/src/utils/SiweUtils.ts b/apps/laboratory/src/utils/SiweUtils.ts index faa4b4de06..b8dcc9f541 100644 --- a/apps/laboratory/src/utils/SiweUtils.ts +++ b/apps/laboratory/src/utils/SiweUtils.ts @@ -1,20 +1,18 @@ -import { SiweMessage } from 'siwe' import { getCsrfToken, signIn, signOut, getSession } from 'next-auth/react' import type { SIWEVerifyMessageArgs, SIWECreateMessageArgs, SIWESession } from '@web3modal/siwe' -import { createSIWEConfig } from '@web3modal/siwe' +import { createSIWEConfig, formatMessage } from '@web3modal/siwe' +import { WagmiConstantsUtil } from '../utils/WagmiConstants' export const siweConfig = createSIWEConfig({ - createMessage: ({ nonce, address, chainId }: SIWECreateMessageArgs) => - new SiweMessage({ - version: '1', - domain: window.location.host, - uri: window.location.origin, - address, - chainId, - nonce, - // Human-readable ASCII assertion that the user will sign, and it must not contain `\n`. - statement: 'Sign in With Ethereum.' - }).prepareMessage(), + // We don't require any async action to populate params but other apps might + // eslint-disable-next-line @typescript-eslint/require-await + getMessageParams: async () => ({ + domain: window.location.host, + uri: window.location.origin, + chains: WagmiConstantsUtil.chains.map(chain => chain.id), + statement: 'Please sign with your account' + }), + createMessage: ({ address, ...args }: SIWECreateMessageArgs) => formatMessage(args, address), getNonce: async () => { const nonce = await getCsrfToken() if (!nonce) { @@ -33,8 +31,15 @@ export const siweConfig = createSIWEConfig({ return { address, chainId } }, - verifyMessage: async ({ message, signature }: SIWEVerifyMessageArgs) => { + verifyMessage: async ({ message, signature, cacao }: SIWEVerifyMessageArgs) => { try { + /* + * Signed Cacao (CAIP-74) will be available for further validations if the wallet supports caip-222 signing + * When personal_sign fallback is used, cacao will be undefined + */ + if (cacao) { + // Do something + } const success = await signIn('credentials', { message, redirect: false, diff --git a/apps/laboratory/tests/shared/pages/ModalPage.ts b/apps/laboratory/tests/shared/pages/ModalPage.ts index b8c7325a9d..15c79075ee 100644 --- a/apps/laboratory/tests/shared/pages/ModalPage.ts +++ b/apps/laboratory/tests/shared/pages/ModalPage.ts @@ -71,8 +71,6 @@ export class ModalPage { context: BrowserContext, mailsacApiKey: string ): Promise { - await this.load() - this.emailAddress = emailAddress const email = new Email(mailsacApiKey) @@ -113,7 +111,6 @@ export class ModalPage { } async loginWithEmail(email: string) { - await this.page.goto(this.url) // Connect Button doesn't have a proper `disabled` attribute so we need to wait for the button to change the text await this.page .getByTestId('connect-button') diff --git a/apps/laboratory/tests/smart-account.spec.ts b/apps/laboratory/tests/smart-account.spec.ts index 4d232f3797..2255df1ee8 100644 --- a/apps/laboratory/tests/smart-account.spec.ts +++ b/apps/laboratory/tests/smart-account.spec.ts @@ -80,6 +80,7 @@ testModalSmartAccount( await walletModalPage.openSettings() await walletModalPage.togglePreferredAccountType() await walletModalPage.disconnect() + await walletModalPage.page.waitForTimeout(500) await walletModalPage.emailFlow( email.getEmailAddressToUse(parallelIndex, NOT_ENABLED_DOMAIN), @@ -89,8 +90,6 @@ testModalSmartAccount( await walletModalPage.page.waitForTimeout(1500) await walletModalPage.openAccount() await walletModalPage.openSettings() - await walletModalPage.switchNetwork('Sepolia') - await walletModalValidator.expectSwitchedNetwork('Sepolia') await walletModalValidator.expectTogglePreferredTypeVisible(false) await walletModalPage.closeModal() diff --git a/examples/html-wagmi/package.json b/examples/html-wagmi/package.json index ff6ea8f2e8..96ff7e4aed 100644 --- a/examples/html-wagmi/package.json +++ b/examples/html-wagmi/package.json @@ -7,9 +7,9 @@ "build:examples": "vite build" }, "dependencies": { + "@wagmi/connectors": "4.1.25", + "@wagmi/core": "2.6.16", "@web3modal/wagmi": "4.1.12-910a844.0", - "@wagmi/connectors": "4.1.14", - "@wagmi/core": "2.6.5", "react": "18.2.0", "react-dom": "18.2.0" }, diff --git a/examples/next-wagmi/package.json b/examples/next-wagmi/package.json index 6789189a96..0211479707 100644 --- a/examples/next-wagmi/package.json +++ b/examples/next-wagmi/package.json @@ -15,7 +15,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "viem": "2.7.13", - "wagmi": "2.5.7" + "wagmi": "2.5.19" }, "devDependencies": { "@types/node": "20.11.5", diff --git a/examples/react-wagmi/package.json b/examples/react-wagmi/package.json index df43a0d728..bb737a7c31 100644 --- a/examples/react-wagmi/package.json +++ b/examples/react-wagmi/package.json @@ -13,7 +13,7 @@ "react-dom": "18.2.0", "vite": "5.0.12", "viem": "2.7.13", - "wagmi": "2.5.7" + "wagmi": "2.5.19" }, "devDependencies": { "@vitejs/plugin-react": "4.2.1", diff --git a/examples/vue-wagmi/package.json b/examples/vue-wagmi/package.json index bdf707bcf6..83fa11213e 100644 --- a/examples/vue-wagmi/package.json +++ b/examples/vue-wagmi/package.json @@ -7,9 +7,9 @@ "build:examples": "vite build" }, "dependencies": { - "@web3modal/wagmi": "4.1.12-910a844.0", - "@wagmi/connectors": "4.1.14", - "@wagmi/core": "2.6.5" + "@wagmi/connectors": "4.1.25", + "@wagmi/core": "2.6.16", + "@web3modal/wagmi": "4.1.12-910a844.0" }, "devDependencies": { "@vitejs/plugin-vue": "5.0.2" diff --git a/package-lock.json b/package-lock.json index 7f5143fffe..67ac6d49c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,7 @@ "tailwind-merge": "2.2.1", "vaul": "0.9.0", "viem": "2.7.19", - "wagmi": "2.5.7", + "wagmi": "2.5.19", "zustand": "4.5.2" }, "devDependencies": { @@ -156,6 +156,7 @@ "@sentry/react": "7.92.0", "@solana/web3.js": "1.87.6", "@tanstack/react-query": "5.24.8", + "@walletconnect/utils": "2.12.0", "@web3modal/ethers": "4.1.12-910a844.0", "@web3modal/siwe": "4.1.12-910a844.0", "@web3modal/solana": "4.1.12-910a844.0", @@ -165,10 +166,9 @@ "next": "14.0.4", "next-auth": "4.24.5", "react-icons": "4.12.0", - "siwe": "2.1.4", "valtio": "1.11.2", "viem": "2.9.3", - "wagmi": "2.5.7" + "wagmi": "2.5.19" }, "devDependencies": { "@aws-sdk/client-cloudwatch": "3.509.0", @@ -177,6 +177,67 @@ "dotenv": "16.3.1" } }, + "apps/laboratory/node_modules/@walletconnect/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.0.tgz", + "integrity": "sha512-uhB3waGmujQVJcPgJvGOpB8RalgYSBT+HpmVbfl4Qe0xJyqpRUo4bPjQa0UYkrHaW20xIw94OuP4+FMLYdeemg==", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.0.1", + "events": "^3.3.0" + } + }, + "apps/laboratory/node_modules/@walletconnect/utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.12.0.tgz", + "integrity": "sha512-GIpfHUe1Bjp1Tjda0SkJEizKOT2biuv7VPFnKsOLT1T+8QxEP9NruC+K2UUEvijS1Qr/LKH9P5004RYNgrch+w==", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "^1.0.3", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/window-getters": "^1.0.1", + "@walletconnect/window-metadata": "^1.0.1", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "^3.1.0" + } + }, + "apps/laboratory/node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "dependencies": { + "tslib": "1.14.1" + } + }, + "apps/laboratory/node_modules/@walletconnect/window-getters/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "apps/laboratory/node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "dependencies": { + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" + } + }, + "apps/laboratory/node_modules/@walletconnect/window-metadata/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "apps/laboratory/node_modules/framer-motion": { "version": "10.17.9", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.17.9.tgz", @@ -364,8 +425,8 @@ "name": "@examples/html-wagmi", "version": "4.1.12-910a844.0", "dependencies": { - "@wagmi/connectors": "4.1.14", - "@wagmi/core": "2.6.5", + "@wagmi/connectors": "4.1.25", + "@wagmi/core": "2.6.16", "@web3modal/wagmi": "4.1.12-910a844.0", "react": "18.2.0", "react-dom": "18.2.0" @@ -384,7 +445,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "viem": "2.7.13", - "wagmi": "2.5.7" + "wagmi": "2.5.19" }, "devDependencies": { "@types/node": "20.11.5", @@ -905,7 +966,7 @@ "react-dom": "18.2.0", "viem": "2.7.13", "vite": "5.0.12", - "wagmi": "2.5.7" + "wagmi": "2.5.19" }, "devDependencies": { "@vitejs/plugin-react": "4.2.1", @@ -1003,8 +1064,8 @@ "name": "@examples/vue-wagmi", "version": "4.1.12-910a844.0", "dependencies": { - "@wagmi/connectors": "4.1.14", - "@wagmi/core": "2.6.5", + "@wagmi/connectors": "4.1.25", + "@wagmi/core": "2.6.16", "@web3modal/wagmi": "4.1.12-910a844.0" }, "devDependencies": { @@ -11544,17 +11605,6 @@ "@solana/web3.js": "*" } }, - "node_modules/@spruceid/siwe-parser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-2.1.2.tgz", - "integrity": "sha512-d/r3S1LwJyMaRAKQ0awmo9whfXeE88Qt00vRj91q5uv5ATtWIQEGJ67Yr5eSZw5zp1/fZCXZYuEckt8lSkereQ==", - "dependencies": { - "@noble/hashes": "^1.1.2", - "apg-js": "^4.3.0", - "uri-js": "^4.4.1", - "valid-url": "^1.0.9" - } - }, "node_modules/@stablelib/aead": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/aead/-/aead-1.0.1.tgz", @@ -15164,22 +15214,22 @@ "integrity": "sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==" }, "node_modules/@wagmi/connectors": { - "version": "4.1.14", - "resolved": "https://registry.npmjs.org/@wagmi/connectors/-/connectors-4.1.14.tgz", - "integrity": "sha512-e8I89FsNBtzhIilU3nqmgMR9xvSgCfmkWLz9iCKBTqyitbK5EJU7WTEtjjYFm1v2J//JeAwaA2XEKtG9BLR9jQ==", + "version": "4.1.25", + "resolved": "https://registry.npmjs.org/@wagmi/connectors/-/connectors-4.1.25.tgz", + "integrity": "sha512-4Tot1Gtiv7uhiUAxZ9On37aai35l5S0sV7N2yQSNgzqXe55bAlI0cqyBAIJRvyKwOe1+hzKfoFqYQSaoCgj5Lg==", "dependencies": { "@coinbase/wallet-sdk": "3.9.1", "@metamask/sdk": "0.14.3", "@safe-global/safe-apps-provider": "0.18.1", "@safe-global/safe-apps-sdk": "8.1.0", - "@walletconnect/ethereum-provider": "2.11.1", + "@walletconnect/ethereum-provider": "2.11.2", "@walletconnect/modal": "2.6.2" }, "funding": { "url": "https://github.com/sponsors/wevm" }, "peerDependencies": { - "@wagmi/core": "2.6.5", + "@wagmi/core": "2.6.16", "typescript": ">=5.0.4", "viem": "2.x" }, @@ -15189,10 +15239,123 @@ } } }, + "node_modules/@wagmi/connectors/node_modules/@walletconnect/core": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.11.2.tgz", + "integrity": "sha512-bB4SiXX8hX3/hyBfVPC5gwZCXCl+OPj+/EDVM71iAO3TDsh78KPbrVAbDnnsbHzZVHlsMohtXX3j5XVsheN3+g==", + "dependencies": { + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-provider": "1.0.13", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.14", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.0.1", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/relay-auth": "^1.0.4", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.11.2", + "@walletconnect/utils": "2.11.2", + "events": "^3.3.0", + "isomorphic-unfetch": "3.1.0", + "lodash.isequal": "4.5.0", + "uint8arrays": "^3.1.0" + } + }, + "node_modules/@wagmi/connectors/node_modules/@walletconnect/ethereum-provider": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.11.2.tgz", + "integrity": "sha512-BUDqee0Uy2rCZVkW5Ao3q6Ado/3fePYnFdryVF+YL6bPhj+xQZ5OfKodl+uvs7Rwq++O5wTX2RqOTzpW7+v+Mg==", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "^1.0.7", + "@walletconnect/jsonrpc-provider": "^1.0.13", + "@walletconnect/jsonrpc-types": "^1.0.3", + "@walletconnect/jsonrpc-utils": "^1.0.8", + "@walletconnect/modal": "^2.6.2", + "@walletconnect/sign-client": "2.11.2", + "@walletconnect/types": "2.11.2", + "@walletconnect/universal-provider": "2.11.2", + "@walletconnect/utils": "2.11.2", + "events": "^3.3.0" + } + }, + "node_modules/@wagmi/connectors/node_modules/@walletconnect/sign-client": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.11.2.tgz", + "integrity": "sha512-MfBcuSz2GmMH+P7MrCP46mVE5qhP0ZyWA0FyIH6/WuxQ6G+MgKsGfaITqakpRPsykWOJq8tXMs3XvUPDU413OQ==", + "dependencies": { + "@walletconnect/core": "2.11.2", + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "^2.0.1", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.11.2", + "@walletconnect/utils": "2.11.2", + "events": "^3.3.0" + } + }, + "node_modules/@wagmi/connectors/node_modules/@walletconnect/types": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.11.2.tgz", + "integrity": "sha512-p632MFB+lJbip2cvtXPBQslpUdiw1sDtQ5y855bOlAGquay+6fZ4h1DcDePeKQDQM3P77ax2a9aNPZxV6y/h1Q==", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.0.1", + "events": "^3.3.0" + } + }, + "node_modules/@wagmi/connectors/node_modules/@walletconnect/utils": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.11.2.tgz", + "integrity": "sha512-LyfdmrnZY6dWqlF4eDrx5jpUwsB2bEPjoqR5Z6rXPiHJKUOdJt7az+mNOn5KTSOlRpd1DmozrBrWr+G9fFLYVw==", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "^1.0.3", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.11.2", + "@walletconnect/window-getters": "^1.0.1", + "@walletconnect/window-metadata": "^1.0.1", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "^3.1.0" + } + }, + "node_modules/@wagmi/connectors/node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@wagmi/connectors/node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "dependencies": { + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" + } + }, + "node_modules/@wagmi/connectors/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@wagmi/core": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.6.5.tgz", - "integrity": "sha512-DLyrc0o+dx05oIhBJuxnS7ekS5e6rB5mytlqPme+Km7aLdeBdcfYB4yJyYCyWoi93OLa7M5sbflTttz3o56bKw==", + "version": "2.6.16", + "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.6.16.tgz", + "integrity": "sha512-95r+2CCf4Yz4CWG7UZMALIcGSUfpr9YbZ2HOqmz6gJEBaW9Cf9xUEZj2MXOHZIP+Ri/3CZJtbBEclDot4enZWA==", "dependencies": { "eventemitter3": "5.0.1", "mipd": "0.0.5", @@ -16760,11 +16923,6 @@ "node": ">= 8" } }, - "node_modules/apg-js": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/apg-js/-/apg-js-4.3.0.tgz", - "integrity": "sha512-8U8MULS+JocCnm11bfrVS4zxtAcE3uOiCAI21SnjDrV9LNhMSGwTGGeko3QfyK1JLWwT7KebFqJMB2puzfdFMQ==" - }, "node_modules/app-root-dir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz", @@ -29114,20 +29272,6 @@ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, - "node_modules/siwe": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/siwe/-/siwe-2.1.4.tgz", - "integrity": "sha512-Dke1Qqa3mgiLm3vjqw/+SQ7dl8WV/Pfk3AlQBF94cBFydTYhztngqYrikzE3X5UTsJ6565dfVbQptszsuYZNYg==", - "dependencies": { - "@spruceid/siwe-parser": "*", - "@stablelib/random": "^1.0.1", - "uri-js": "^4.4.1", - "valid-url": "^1.0.9" - }, - "peerDependencies": { - "ethers": "^5.6.8 || ^6.0.8" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -31344,11 +31488,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "node_modules/valid-url": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", - "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==" - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -31744,12 +31883,12 @@ } }, "node_modules/wagmi": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/wagmi/-/wagmi-2.5.7.tgz", - "integrity": "sha512-xSuteMXFKvra4xDddqZbZv/gQlcg3X+To5AoZW7WoAm0iVlF8/vEGpQzCWy6KZs2z1szxPrr0YnH3Zr1Qj4E/A==", + "version": "2.5.19", + "resolved": "https://registry.npmjs.org/wagmi/-/wagmi-2.5.19.tgz", + "integrity": "sha512-fy6s3qTuXpfrrghhoNXuV92yqOqJI7m/9iLIejHxEYxiddVDTR8BVdkt0BuBQZzoXSAutDkyIlJbtFcpX5dfrQ==", "dependencies": { - "@wagmi/connectors": "4.1.14", - "@wagmi/core": "2.6.5", + "@wagmi/connectors": "4.1.25", + "@wagmi/core": "2.6.16", "use-sync-external-store": "1.2.0" }, "funding": { @@ -32503,7 +32642,7 @@ "license": "Apache-2.0", "dependencies": { "@coinbase/wallet-sdk": "3.9.1", - "@walletconnect/ethereum-provider": "2.11.1", + "@walletconnect/ethereum-provider": "2.12.0", "@web3modal/polyfills": "4.1.12-910a844.0", "@web3modal/scaffold": "4.1.12-910a844.0", "@web3modal/scaffold-react": "4.1.12-910a844.0", @@ -32634,12 +32773,141 @@ "integrity": "sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ==", "dev": true }, + "packages/ethers/node_modules/@walletconnect/core": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.12.0.tgz", + "integrity": "sha512-CORck4dRvCpIn6hl2ZtUnjrSJ0JHt9TRteGCViwPyXNSuvXz70RvaIkvPoybYZBGCRQR4WTJ4dMdqeQpuyrL/g==", + "dependencies": { + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-provider": "1.0.13", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.14", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.1.0", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/relay-auth": "^1.0.4", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/utils": "2.12.0", + "events": "^3.3.0", + "isomorphic-unfetch": "3.1.0", + "lodash.isequal": "4.5.0", + "uint8arrays": "^3.1.0" + } + }, + "packages/ethers/node_modules/@walletconnect/ethereum-provider": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.12.0.tgz", + "integrity": "sha512-sX7vQHTRxByU+3/gY6eDTvt4jxQHfiX6WwqRI08UTN/Ixz+IJSBo3UnNRxNmPaC4vG8zUpsFQ4xYSsDnhfaviw==", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "^1.0.7", + "@walletconnect/jsonrpc-provider": "^1.0.13", + "@walletconnect/jsonrpc-types": "^1.0.3", + "@walletconnect/jsonrpc-utils": "^1.0.8", + "@walletconnect/modal": "^2.6.2", + "@walletconnect/sign-client": "2.12.0", + "@walletconnect/types": "2.12.0", + "@walletconnect/universal-provider": "2.12.0", + "@walletconnect/utils": "2.12.0", + "events": "^3.3.0" + } + }, + "packages/ethers/node_modules/@walletconnect/sign-client": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.12.0.tgz", + "integrity": "sha512-JUHJVZtW9iJmn3I2byLzhMRSFiQicTPU92PLuHIF2nG98CqsvlPn8Cu8Cx5CEPFrxPQWwLA+Dv/F+wuSgQiD/w==", + "dependencies": { + "@walletconnect/core": "2.12.0", + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "^2.0.1", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/utils": "2.12.0", + "events": "^3.3.0" + } + }, + "packages/ethers/node_modules/@walletconnect/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.0.tgz", + "integrity": "sha512-uhB3waGmujQVJcPgJvGOpB8RalgYSBT+HpmVbfl4Qe0xJyqpRUo4bPjQa0UYkrHaW20xIw94OuP4+FMLYdeemg==", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.0.1", + "events": "^3.3.0" + } + }, + "packages/ethers/node_modules/@walletconnect/universal-provider": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.12.0.tgz", + "integrity": "sha512-CMo10Lh6/DyCznVRMg1nHptWCTeVqMzXBcPNNyCnr3SazE0Shsne/5v/7Kr6j+Yts2hVbLp6lkI2F9ZAFpL6ug==", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "^1.0.7", + "@walletconnect/jsonrpc-provider": "1.0.13", + "@walletconnect/jsonrpc-types": "^1.0.2", + "@walletconnect/jsonrpc-utils": "^1.0.7", + "@walletconnect/logger": "^2.0.1", + "@walletconnect/sign-client": "2.12.0", + "@walletconnect/types": "2.12.0", + "@walletconnect/utils": "2.12.0", + "events": "^3.3.0" + } + }, + "packages/ethers/node_modules/@walletconnect/utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.12.0.tgz", + "integrity": "sha512-GIpfHUe1Bjp1Tjda0SkJEizKOT2biuv7VPFnKsOLT1T+8QxEP9NruC+K2UUEvijS1Qr/LKH9P5004RYNgrch+w==", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "^1.0.3", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/window-getters": "^1.0.1", + "@walletconnect/window-metadata": "^1.0.1", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "^3.1.0" + } + }, + "packages/ethers/node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "dependencies": { + "tslib": "1.14.1" + } + }, + "packages/ethers/node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "dependencies": { + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" + } + }, "packages/ethers/node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true }, + "packages/ethers/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "packages/ethers/node_modules/vue": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.3.tgz", @@ -32667,7 +32935,7 @@ "license": "Apache-2.0", "dependencies": { "@coinbase/wallet-sdk": "3.9.1", - "@walletconnect/ethereum-provider": "2.11.1", + "@walletconnect/ethereum-provider": "2.12.0", "@web3modal/polyfills": "4.1.12-910a844.0", "@web3modal/scaffold": "4.1.12-910a844.0", "@web3modal/scaffold-react": "4.1.12-910a844.0", @@ -32799,6 +33067,130 @@ "integrity": "sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ==", "dev": true }, + "packages/ethers5/node_modules/@walletconnect/core": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.12.0.tgz", + "integrity": "sha512-CORck4dRvCpIn6hl2ZtUnjrSJ0JHt9TRteGCViwPyXNSuvXz70RvaIkvPoybYZBGCRQR4WTJ4dMdqeQpuyrL/g==", + "dependencies": { + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-provider": "1.0.13", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.14", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.1.0", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/relay-auth": "^1.0.4", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/utils": "2.12.0", + "events": "^3.3.0", + "isomorphic-unfetch": "3.1.0", + "lodash.isequal": "4.5.0", + "uint8arrays": "^3.1.0" + } + }, + "packages/ethers5/node_modules/@walletconnect/ethereum-provider": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.12.0.tgz", + "integrity": "sha512-sX7vQHTRxByU+3/gY6eDTvt4jxQHfiX6WwqRI08UTN/Ixz+IJSBo3UnNRxNmPaC4vG8zUpsFQ4xYSsDnhfaviw==", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "^1.0.7", + "@walletconnect/jsonrpc-provider": "^1.0.13", + "@walletconnect/jsonrpc-types": "^1.0.3", + "@walletconnect/jsonrpc-utils": "^1.0.8", + "@walletconnect/modal": "^2.6.2", + "@walletconnect/sign-client": "2.12.0", + "@walletconnect/types": "2.12.0", + "@walletconnect/universal-provider": "2.12.0", + "@walletconnect/utils": "2.12.0", + "events": "^3.3.0" + } + }, + "packages/ethers5/node_modules/@walletconnect/sign-client": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.12.0.tgz", + "integrity": "sha512-JUHJVZtW9iJmn3I2byLzhMRSFiQicTPU92PLuHIF2nG98CqsvlPn8Cu8Cx5CEPFrxPQWwLA+Dv/F+wuSgQiD/w==", + "dependencies": { + "@walletconnect/core": "2.12.0", + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "^2.0.1", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/utils": "2.12.0", + "events": "^3.3.0" + } + }, + "packages/ethers5/node_modules/@walletconnect/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.0.tgz", + "integrity": "sha512-uhB3waGmujQVJcPgJvGOpB8RalgYSBT+HpmVbfl4Qe0xJyqpRUo4bPjQa0UYkrHaW20xIw94OuP4+FMLYdeemg==", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.0.1", + "events": "^3.3.0" + } + }, + "packages/ethers5/node_modules/@walletconnect/universal-provider": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.12.0.tgz", + "integrity": "sha512-CMo10Lh6/DyCznVRMg1nHptWCTeVqMzXBcPNNyCnr3SazE0Shsne/5v/7Kr6j+Yts2hVbLp6lkI2F9ZAFpL6ug==", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "^1.0.7", + "@walletconnect/jsonrpc-provider": "1.0.13", + "@walletconnect/jsonrpc-types": "^1.0.2", + "@walletconnect/jsonrpc-utils": "^1.0.7", + "@walletconnect/logger": "^2.0.1", + "@walletconnect/sign-client": "2.12.0", + "@walletconnect/types": "2.12.0", + "@walletconnect/utils": "2.12.0", + "events": "^3.3.0" + } + }, + "packages/ethers5/node_modules/@walletconnect/utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.12.0.tgz", + "integrity": "sha512-GIpfHUe1Bjp1Tjda0SkJEizKOT2biuv7VPFnKsOLT1T+8QxEP9NruC+K2UUEvijS1Qr/LKH9P5004RYNgrch+w==", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "^1.0.3", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/window-getters": "^1.0.1", + "@walletconnect/window-metadata": "^1.0.1", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "^3.1.0" + } + }, + "packages/ethers5/node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "dependencies": { + "tslib": "1.14.1" + } + }, + "packages/ethers5/node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "dependencies": { + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" + } + }, "packages/ethers5/node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", @@ -32853,6 +33245,11 @@ "@ethersproject/wordlists": "5.7.0" } }, + "packages/ethers5/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "packages/ethers5/node_modules/vue": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.3.tgz", @@ -33079,12 +33476,69 @@ "version": "4.1.12-910a844.0", "license": "Apache-2.0", "dependencies": { + "@walletconnect/utils": "2.12.0", "@web3modal/core": "4.1.12-910a844.0", "@web3modal/scaffold-utils": "4.1.12-910a844.0", "lit": "3.1.0", "valtio": "1.11.2" } }, + "packages/siwe/node_modules/@walletconnect/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.0.tgz", + "integrity": "sha512-uhB3waGmujQVJcPgJvGOpB8RalgYSBT+HpmVbfl4Qe0xJyqpRUo4bPjQa0UYkrHaW20xIw94OuP4+FMLYdeemg==", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.0.1", + "events": "^3.3.0" + } + }, + "packages/siwe/node_modules/@walletconnect/utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.12.0.tgz", + "integrity": "sha512-GIpfHUe1Bjp1Tjda0SkJEizKOT2biuv7VPFnKsOLT1T+8QxEP9NruC+K2UUEvijS1Qr/LKH9P5004RYNgrch+w==", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "^1.0.3", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/window-getters": "^1.0.1", + "@walletconnect/window-metadata": "^1.0.1", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "^3.1.0" + } + }, + "packages/siwe/node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "dependencies": { + "tslib": "1.14.1" + } + }, + "packages/siwe/node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "dependencies": { + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" + } + }, + "packages/siwe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "packages/solana": { "name": "@web3modal/solana", "version": "4.1.12-910a844.0", @@ -33196,6 +33650,7 @@ "version": "4.1.12-910a844.0", "license": "Apache-2.0", "dependencies": { + "@walletconnect/ethereum-provider": "2.12.1", "@web3modal/polyfills": "4.1.12-910a844.0", "@web3modal/scaffold": "4.1.12-910a844.0", "@web3modal/scaffold-react": "4.1.12-910a844.0", @@ -33204,15 +33659,15 @@ "@web3modal/siwe": "4.1.12-910a844.0" }, "devDependencies": { - "@wagmi/connectors": "4.1.14", - "@wagmi/core": "2.6.5", + "@wagmi/connectors": "4.1.25", + "@wagmi/core": "2.6.16", "react": "18.2.0", "react-dom": "18.2.0", "viem": "2.7.13", "vue": "3.4.3" }, "peerDependencies": { - "@wagmi/connectors": ">=4.0.0", + "@wagmi/connectors": ">=4", "@wagmi/core": ">=2.0.0", "react": ">=17", "react-dom": ">=17", @@ -33330,12 +33785,141 @@ "integrity": "sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ==", "dev": true }, + "packages/wagmi/node_modules/@walletconnect/core": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.12.1.tgz", + "integrity": "sha512-CIxWNRNvmFNwn+8kPbKyBXS1JHBFJpDE8f73dXtUIElVnZhmXzEOSE5fug91EX57wTrv4/qW66H9kNB3c7Pp5g==", + "dependencies": { + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-provider": "1.0.13", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.14", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.1.0", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/relay-auth": "^1.0.4", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.1", + "@walletconnect/utils": "2.12.1", + "events": "^3.3.0", + "isomorphic-unfetch": "3.1.0", + "lodash.isequal": "4.5.0", + "uint8arrays": "^3.1.0" + } + }, + "packages/wagmi/node_modules/@walletconnect/ethereum-provider": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.12.1.tgz", + "integrity": "sha512-C57sIcKDNKx6UgnW4EVrmBGAXGddfjgC88vpkOTBrClFF8zhSfdf/fKnLLo70spr8z3u77IppD36m6DGhJ+xpw==", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "^1.0.7", + "@walletconnect/jsonrpc-provider": "^1.0.13", + "@walletconnect/jsonrpc-types": "^1.0.3", + "@walletconnect/jsonrpc-utils": "^1.0.8", + "@walletconnect/modal": "^2.6.2", + "@walletconnect/sign-client": "2.12.1", + "@walletconnect/types": "2.12.1", + "@walletconnect/universal-provider": "2.12.1", + "@walletconnect/utils": "2.12.1", + "events": "^3.3.0" + } + }, + "packages/wagmi/node_modules/@walletconnect/sign-client": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.12.1.tgz", + "integrity": "sha512-6PegtNZgqmOX2G022fyrHjyN3PW6Ov2GVFvG8f+80uqikEO3IAL3dgazlnUYtuaUNYs+Hx7sSvjNVanMiJsE1Q==", + "dependencies": { + "@walletconnect/core": "2.12.1", + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "^2.0.1", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.1", + "@walletconnect/utils": "2.12.1", + "events": "^3.3.0" + } + }, + "packages/wagmi/node_modules/@walletconnect/types": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.1.tgz", + "integrity": "sha512-mPzGj5ssgcOJKqwn8qsdCr+J9swsjTmDPAV10CghXIe3GGQKOb4noTUhOofb4LDbFaio1GBql8+Xfy+6bulobw==", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.0.1", + "events": "^3.3.0" + } + }, + "packages/wagmi/node_modules/@walletconnect/universal-provider": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.12.1.tgz", + "integrity": "sha512-ZLl5+wY3A7pss5UbIOKBcTwoFQmhW6ilDq33vX2Hu69yUnK+OEKlKcgAy4vjN2wAWakUctO4j7RhpionKBZfCw==", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "^1.0.7", + "@walletconnect/jsonrpc-provider": "1.0.13", + "@walletconnect/jsonrpc-types": "^1.0.2", + "@walletconnect/jsonrpc-utils": "^1.0.7", + "@walletconnect/logger": "^2.0.1", + "@walletconnect/sign-client": "2.12.1", + "@walletconnect/types": "2.12.1", + "@walletconnect/utils": "2.12.1", + "events": "^3.3.0" + } + }, + "packages/wagmi/node_modules/@walletconnect/utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.12.1.tgz", + "integrity": "sha512-v2Oc8mTb+3y8MW94Rnj9hxVjJU3wdnE1g8eLZXmcNf7zAvsm1iJPtHl7ZxZsjpVpo1Vg79Oo1rS9gWq9z0kKKw==", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "^1.0.3", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.1", + "@walletconnect/window-getters": "^1.0.1", + "@walletconnect/window-metadata": "^1.0.1", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "^3.1.0" + } + }, + "packages/wagmi/node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "dependencies": { + "tslib": "1.14.1" + } + }, + "packages/wagmi/node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "dependencies": { + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" + } + }, "packages/wagmi/node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true }, + "packages/wagmi/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "packages/wagmi/node_modules/viem": { "version": "2.7.13", "resolved": "https://registry.npmjs.org/viem/-/viem-2.7.13.tgz", diff --git a/packages/core/src/utils/SwapApiUtil.ts b/packages/core/src/utils/SwapApiUtil.ts index f429f11de3..4ea46124bc 100644 --- a/packages/core/src/utils/SwapApiUtil.ts +++ b/packages/core/src/utils/SwapApiUtil.ts @@ -27,20 +27,20 @@ export const SwapApiUtil = { chainId: NetworkController.state.caipNetwork?.id, projectId: OptionsController.state.projectId }) - const tokens = - response?.tokens?.map(token => { - return { - ...token, - eip2612: false, - quantity: { - decimals: '0', - numeric: '0' - }, - price: 0, - value: 0 - } as SwapTokenWithBalance - }) || [] + response?.tokens?.map( + token => + ({ + ...token, + eip2612: false, + quantity: { + decimals: '0', + numeric: '0' + }, + price: 0, + value: 0 + }) as SwapTokenWithBalance + ) || [] return tokens }, @@ -102,17 +102,18 @@ export const SwapApiUtil = { mapBalancesToSwapTokens(balances: BlockchainApiBalanceResponse['balances']) { return ( - balances?.map(token => { - return { - ...token, - address: token?.address - ? token.address - : `${token.chainId}:${ConstantsUtil.NATIVE_TOKEN_ADDRESS}`, - decimals: parseInt(token.quantity.decimals, 10), - logoUri: token.iconUrl, - eip2612: false - } as SwapTokenWithBalance - }) || [] + balances?.map( + token => + ({ + ...token, + address: token?.address + ? token.address + : `${token.chainId}:${ConstantsUtil.NATIVE_TOKEN_ADDRESS}`, + decimals: parseInt(token.quantity.decimals, 10), + logoUri: token.iconUrl, + eip2612: false + }) as SwapTokenWithBalance + ) || [] ) } } diff --git a/packages/ethers/package.json b/packages/ethers/package.json index 65b9a8c486..3d5ce7c388 100644 --- a/packages/ethers/package.json +++ b/packages/ethers/package.json @@ -44,7 +44,7 @@ }, "dependencies": { "@coinbase/wallet-sdk": "3.9.1", - "@walletconnect/ethereum-provider": "2.11.1", + "@walletconnect/ethereum-provider": "2.12.0", "@web3modal/polyfills": "4.1.12-910a844.0", "@web3modal/scaffold": "4.1.12-910a844.0", "@web3modal/scaffold-react": "4.1.12-910a844.0", diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index b3a3ca89f3..d5336d99e5 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -14,7 +14,7 @@ import type { } from '@web3modal/scaffold' import { Web3ModalScaffold } from '@web3modal/scaffold' import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils' -import EthereumProvider from '@walletconnect/ethereum-provider' +import EthereumProvider, { OPTIONAL_METHODS } from '@walletconnect/ethereum-provider' import type { Web3ModalSIWEClient } from '@web3modal/siwe' import type { Address, @@ -187,7 +187,51 @@ export class Web3Modal extends Web3ModalScaffold { onUri(uri) }) - await WalletConnectProvider.connect() + if (siweConfig?.options?.enabled) { + const { SIWEController, getDidChainId, getDidAddress } = await import('@web3modal/siwe') + const result = await WalletConnectProvider.authenticate({ + nonce: await siweConfig.getNonce(), + methods: OPTIONAL_METHODS, + ...(await siweConfig.getMessageParams()) + }) + // Auths is an array of signed CACAO objects https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-74.md + const signedCacao = result?.auths?.[0] + if (signedCacao) { + const { p, s } = signedCacao + const chainId = getDidChainId(p.iss) + const address = getDidAddress(p.iss) + if (address && chainId) { + SIWEController.setSession({ + address, + chainId: parseInt(chainId, 10) + }) + } + try { + // Kicks off verifyMessage and populates external states + const message = WalletConnectProvider.signer.client.formatAuthMessage({ + request: p, + iss: p.iss + }) + + await SIWEController.verifyMessage({ + message, + signature: s.s, + cacao: signedCacao + }) + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error verifying message', error) + // eslint-disable-next-line no-console + await WalletConnectProvider.disconnect().catch(console.error) + // eslint-disable-next-line no-console + await SIWEController.signOut().catch(console.error) + throw error + } + } + } else { + await WalletConnectProvider.connect() + } + await this.setWalletConnectProvider() }, @@ -256,7 +300,12 @@ export class Web3Modal extends Web3ModalScaffold { disconnect: async () => { const provider = EthersStoreUtil.state.provider const providerType = EthersStoreUtil.state.providerType - + localStorage.removeItem(EthersConstantsUtil.WALLET_ID) + EthersStoreUtil.reset() + if (siweConfig?.options?.signOutOnDisconnect) { + const { SIWEController } = await import('@web3modal/siwe') + await SIWEController.signOut() + } if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) { const WalletConnectProvider = provider await (WalletConnectProvider as unknown as EthereumProvider).disconnect() diff --git a/packages/ethers5/package.json b/packages/ethers5/package.json index 8e8b114228..bc0f6bad6e 100644 --- a/packages/ethers5/package.json +++ b/packages/ethers5/package.json @@ -44,7 +44,7 @@ }, "dependencies": { "@coinbase/wallet-sdk": "3.9.1", - "@walletconnect/ethereum-provider": "2.11.1", + "@walletconnect/ethereum-provider": "2.12.0", "@web3modal/polyfills": "4.1.12-910a844.0", "@web3modal/scaffold": "4.1.12-910a844.0", "@web3modal/scaffold-react": "4.1.12-910a844.0", diff --git a/packages/ethers5/src/client.ts b/packages/ethers5/src/client.ts index 96affacf74..dbc953ec01 100644 --- a/packages/ethers5/src/client.ts +++ b/packages/ethers5/src/client.ts @@ -14,7 +14,7 @@ import type { import { Web3ModalScaffold } from '@web3modal/scaffold' import type { Web3ModalSIWEClient } from '@web3modal/siwe' import { ConstantsUtil, PresetsUtil, HelpersUtil } from '@web3modal/scaffold-utils' -import EthereumProvider from '@walletconnect/ethereum-provider' +import EthereumProvider, { OPTIONAL_METHODS } from '@walletconnect/ethereum-provider' import type { Address, Metadata, @@ -166,7 +166,51 @@ export class Web3Modal extends Web3ModalScaffold { onUri(uri) }) - await WalletConnectProvider.connect() + if (siweConfig?.options?.enabled) { + const { SIWEController, getDidChainId, getDidAddress } = await import('@web3modal/siwe') + const result = await WalletConnectProvider.authenticate({ + nonce: await siweConfig.getNonce(), + methods: OPTIONAL_METHODS, + ...(await siweConfig.getMessageParams()) + }) + // Auths is an array of signed CACAO objects https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-74.md + const signedCacao = result?.auths?.[0] + if (signedCacao) { + const { p, s } = signedCacao + const chainId = getDidChainId(p.iss) + const address = getDidAddress(p.iss) + if (address && chainId) { + SIWEController.setSession({ + address, + chainId: parseInt(chainId, 10) + }) + } + + try { + // Kicks off verifyMessage and populates external states + const message = WalletConnectProvider.signer.client.formatAuthMessage({ + request: p, + iss: p.iss + }) + + await SIWEController.verifyMessage({ + message, + signature: s.s, + cacao: signedCacao + }) + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error verifying message', error) + // eslint-disable-next-line no-console + await WalletConnectProvider.disconnect().catch(console.error) + // eslint-disable-next-line no-console + await SIWEController.signOut().catch(console.error) + throw error + } + } + } else { + await WalletConnectProvider.connect() + } await this.setWalletConnectProvider() }, @@ -235,6 +279,10 @@ export class Web3Modal extends Web3ModalScaffold { const providerType = EthersStoreUtil.state.providerType localStorage.removeItem(EthersConstantsUtil.WALLET_ID) EthersStoreUtil.reset() + if (siweConfig?.options?.signOutOnDisconnect) { + const { SIWEController } = await import('@web3modal/siwe') + await SIWEController.signOut() + } if (providerType === ConstantsUtil.WALLET_CONNECT_CONNECTOR_ID) { const WalletConnectProvider = provider await (WalletConnectProvider as unknown as EthereumProvider).disconnect() diff --git a/packages/siwe/core/controller/SIWEController.ts b/packages/siwe/core/controller/SIWEController.ts index d38c8b8546..1b3ba75ed1 100644 --- a/packages/siwe/core/controller/SIWEController.ts +++ b/packages/siwe/core/controller/SIWEController.ts @@ -104,6 +104,7 @@ export const SIWEController = { const client = this._getClient() await client.signOut() this.setStatus('ready') + this.setSession(undefined) client.onSignOut?.() }, diff --git a/packages/siwe/core/helpers/index.ts b/packages/siwe/core/helpers/index.ts new file mode 100644 index 0000000000..33e23c1051 --- /dev/null +++ b/packages/siwe/core/helpers/index.ts @@ -0,0 +1,33 @@ +import { isValidEip191Signature, isValidEip1271Signature } from '@walletconnect/utils' + +const ETH_ADDRESS_PATTERN = /0x[a-fA-F0-9]{40}/u +const ETH_CHAIN_ID_IN_SIWE_PATTERN = /Chain ID: (?\d+)/u + +export function getAddressFromMessage(message: string) { + return message.match(ETH_ADDRESS_PATTERN)?.[0] || '' +} + +export function getChainIdFromMessage(message: string) { + return `eip155:${message.match(ETH_CHAIN_ID_IN_SIWE_PATTERN)?.[1] || 1}` +} + +export async function verifySignature({ + address, + message, + signature, + chainId, + projectId +}: { + address: string + message: string + signature: string + chainId: string + projectId: string +}) { + let isValid = isValidEip191Signature(address, message, signature) + if (!isValid) { + isValid = await isValidEip1271Signature(address, message, signature, chainId, projectId) + } + + return isValid +} diff --git a/packages/siwe/core/utils/TypeUtils.ts b/packages/siwe/core/utils/TypeUtils.ts index 0381f8971f..e1f35753cb 100644 --- a/packages/siwe/core/utils/TypeUtils.ts +++ b/packages/siwe/core/utils/TypeUtils.ts @@ -3,19 +3,64 @@ export interface SIWESession { chainId: number } +interface CacaoHeader { + t: 'caip122' +} + export interface SIWECreateMessageArgs { + chainId: number + domain: string nonce: string + uri: string address: string - chainId: number + version: '1' + type?: CacaoHeader['t'] + nbf?: string + exp?: string + statement?: string + requestId?: string + resources?: string[] + expiry?: number +} +export type SIWEMessageArgs = { + chains: number[] + methods?: string[] +} & Omit +// Signed Cacao (CAIP-74) +interface CacaoPayload { + domain: string + aud: string + nonce: string + iss: string + version?: string + iat?: string + nbf?: string + exp?: string + statement?: string + requestId?: string + resources?: string[] + type?: string +} + +interface Cacao { + h: CacaoHeader + p: CacaoPayload + s: { + t: 'eip191' | 'eip1271' + s: string + m?: string + } } export interface SIWEVerifyMessageArgs { message: string signature: string + cacao?: Cacao } export interface SIWEClientMethods { getNonce: (address?: string) => Promise + getMessageParams: () => Promise createMessage: (args: SIWECreateMessageArgs) => string verifyMessage: (args: SIWEVerifyMessageArgs) => Promise getSession: () => Promise diff --git a/packages/siwe/exports/index.ts b/packages/siwe/exports/index.ts index be2d7efec0..1fc165be46 100644 --- a/packages/siwe/exports/index.ts +++ b/packages/siwe/exports/index.ts @@ -6,6 +6,12 @@ import type { SIWEClientMethods } from '../core/utils/TypeUtils.js' import { Web3ModalSIWEClient } from '../src/client.js' +export { + getAddressFromMessage, + getChainIdFromMessage, + verifySignature +} from '../core/helpers/index.js' +export { formatMessage, getDidChainId, getDidAddress } from '@walletconnect/utils' export { SIWEController, type SIWEControllerClient } from '../core/controller/SIWEController.js' export type { diff --git a/packages/siwe/package.json b/packages/siwe/package.json index 0ee73de324..a6372c73ee 100644 --- a/packages/siwe/package.json +++ b/packages/siwe/package.json @@ -16,6 +16,7 @@ "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, "dependencies": { + "@walletconnect/utils": "2.12.0", "@web3modal/core": "4.1.12-910a844.0", "@web3modal/scaffold-utils": "4.1.12-910a844.0", "valtio": "1.11.2", diff --git a/packages/siwe/scaffold/views/w3m-connecting-siwe-view/index.ts b/packages/siwe/scaffold/views/w3m-connecting-siwe-view/index.ts index bb93ed95d8..a5f99db431 100644 --- a/packages/siwe/scaffold/views/w3m-connecting-siwe-view/index.ts +++ b/packages/siwe/scaffold/views/w3m-connecting-siwe-view/index.ts @@ -21,6 +21,8 @@ export class W3mConnectingSiweView extends LitElement { // -- Render -------------------------------------------- // public override render() { + this.onRender() + return html` @@ -69,6 +71,13 @@ export class W3mConnectingSiweView extends LitElement { } // -- Private ------------------------------------------- // + + private onRender() { + if (SIWEController.state.session) { + ModalController.close() + } + } + private async onSign() { this.isSigning = true EventsController.sendEvent({ diff --git a/packages/siwe/src/client.ts b/packages/siwe/src/client.ts index 572da86e42..b24e54e21a 100644 --- a/packages/siwe/src/client.ts +++ b/packages/siwe/src/client.ts @@ -2,7 +2,8 @@ import type { SIWECreateMessageArgs, SIWEVerifyMessageArgs, SIWEConfig, - SIWEClientMethods + SIWEClientMethods, + SIWESession } from '../core/utils/TypeUtils.js' import type { SIWEControllerClient } from '../core/controller/SIWEController.js' @@ -10,7 +11,9 @@ import { AccountController, NetworkController, ConnectionController, - RouterUtil + RouterUtil, + RouterController, + StorageUtil } from '@web3modal/core' import { NetworkUtil } from '@web3modal/common' @@ -55,6 +58,12 @@ export class Web3ModalSIWEClient { return nonce } + async getMessageParams() { + const params = await this.methods.getMessageParams() + + return params || {} + } + createMessage(args: SIWECreateMessageArgs) { const message = this.methods.createMessage(args) @@ -80,7 +89,7 @@ export class Web3ModalSIWEClient { return session } - async signIn() { + async signIn(): Promise { const { address } = AccountController.state const nonce = await this.methods.getNonce(address) if (!address) { @@ -90,7 +99,18 @@ export class Web3ModalSIWEClient { if (!chainId) { throw new Error('A chainId is required to create a SIWE message.') } - const message = this.methods.createMessage({ address, nonce, chainId }) + const messageParams = await this.getMessageParams() + const message = this.methods.createMessage({ + address: `eip155:${chainId}:${address}`, + chainId, + nonce, + version: '1', + ...messageParams + }) + const type = StorageUtil.getConnectedConnector() + if (type === 'EMAIL') { + RouterController.push('ApproveTransaction') + } const signature = await ConnectionController.signMessage(message) const isValid = await this.methods.verifyMessage({ message, signature }) if (!isValid) { diff --git a/packages/siwe/tests/controllers/SIWEController.test.ts b/packages/siwe/tests/controllers/SIWEController.test.ts index 3e52674c04..5b20c66505 100644 --- a/packages/siwe/tests/controllers/SIWEController.test.ts +++ b/packages/siwe/tests/controllers/SIWEController.test.ts @@ -17,7 +17,14 @@ const client = { createMessage: () => 'mock-message', verifyMessage: async () => Promise.resolve(true), getSession: async () => Promise.resolve(session), - signOut: async () => Promise.resolve(true) + signOut: async () => Promise.resolve(true), + getMessageParams: () => + Promise.resolve({ + chains: [1], + domain: 'mock-domain', + uri: 'mock-uri', + nonce: 'mock-nonce' + }) } // -- Tests -------------------------------------------------------------------- diff --git a/packages/solana/src/client.ts b/packages/solana/src/client.ts index 44d87d18db..8c815b0764 100644 --- a/packages/solana/src/client.ts +++ b/packages/solana/src/client.ts @@ -147,9 +147,7 @@ export class Web3Modal extends Web3ModalScaffold { return signature as string }, - estimateGas: async () => { - return await Promise.resolve(BigInt(0)) - }, + estimateGas: async () => await Promise.resolve(BigInt(0)), // -- Transaction methods --------------------------------------------------- /** @@ -157,21 +155,13 @@ export class Web3Modal extends Web3ModalScaffold { * These methods are supported only on `wagmi` and `ethers` since the Solana SDK does not support them in the same way. * These function definition is to have a type parity between the clients. Currently not in use. */ - sendTransaction: async () => { - return await Promise.resolve('0x') - }, + sendTransaction: async () => await Promise.resolve('0x'), - writeContract: async () => { - return await Promise.resolve('0x') - }, + writeContract: async () => await Promise.resolve('0x'), - getEnsAddress: async (value: string) => { - return await Promise.resolve(value) - }, + getEnsAddress: async (value: string) => await Promise.resolve(value), - getEnsAvatar: async (value: string) => { - return await Promise.resolve(value) - }, + getEnsAvatar: async (value: string) => await Promise.resolve(value), parseUnits: () => BigInt(0), diff --git a/packages/wagmi/.eslintrc.json b/packages/wagmi/.eslintrc.json index 0f959f167e..ce51ea095d 100644 --- a/packages/wagmi/.eslintrc.json +++ b/packages/wagmi/.eslintrc.json @@ -1,4 +1,5 @@ { "extends": ["plugin:require-extensions/recommended", "../../.eslintrc.json"], - "plugins": ["require-extensions"] + "plugins": ["require-extensions"], + "ignorePatterns": ["alphaWalletConnect.ts"] } diff --git a/packages/wagmi/exports/index.ts b/packages/wagmi/exports/index.ts index 7bed4b6ec8..bc59ff30fb 100644 --- a/packages/wagmi/exports/index.ts +++ b/packages/wagmi/exports/index.ts @@ -5,6 +5,7 @@ import { ConstantsUtil } from '@web3modal/scaffold-utils' export type { Web3Modal, Web3ModalOptions } from '../src/client.js' export { defaultWagmiConfig } from '../src/utils/defaultWagmiCoreConfig.js' export { emailConnector } from '../src/connectors/EmailConnector.js' +export { alphaWalletConnect } from '../src/connectors/alphaWalletConnect.js' export function createWeb3Modal(options: Web3ModalOptions) { return new Web3Modal({ ...options, _sdkVersion: `html-wagmi-${ConstantsUtil.VERSION}` }) diff --git a/packages/wagmi/package.json b/packages/wagmi/package.json index 57e4e37336..c11300d2f6 100644 --- a/packages/wagmi/package.json +++ b/packages/wagmi/package.json @@ -51,6 +51,7 @@ "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, "dependencies": { + "@walletconnect/ethereum-provider": "2.12.1", "@web3modal/polyfills": "4.1.12-910a844.0", "@web3modal/scaffold": "4.1.12-910a844.0", "@web3modal/scaffold-react": "4.1.12-910a844.0", @@ -59,15 +60,15 @@ "@web3modal/siwe": "4.1.12-910a844.0" }, "devDependencies": { - "@wagmi/connectors": "4.1.14", - "@wagmi/core": "2.6.5", + "@wagmi/connectors": "4.1.25", + "@wagmi/core": "2.6.16", "react": "18.2.0", "react-dom": "18.2.0", "viem": "2.7.13", "vue": "3.4.3" }, "peerDependencies": { - "@wagmi/connectors": ">=4.0.0", + "@wagmi/connectors": ">=4", "@wagmi/core": ">=2.0.0", "react": ">=17", "react-dom": ">=17", diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index 8179ab84c6..918a5522d3 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -1,4 +1,5 @@ -import { EthereumProvider } from '@walletconnect/ethereum-provider' +/* eslint-disable no-console */ +import { EthereumProvider, OPTIONAL_METHODS } from '@walletconnect/ethereum-provider' import { connect, disconnect, @@ -136,7 +137,60 @@ export class Web3Modal extends Web3ModalScaffold { }) const chainId = NetworkUtil.caipNetworkIdToNumber(this.getCaipNetwork()?.id) + // Make sure client uses ethereum provider version that supports `authenticate` + if (siweConfig?.options?.enabled && typeof provider?.authenticate === 'function') { + const { SIWEController, getDidChainId, getDidAddress } = await import('@web3modal/siwe') + const siweParams = await siweConfig.getMessageParams() + // @ts-expect-error - setting requested chains beforehand avoids wagmi auto disconnecting the session when `connect` is called because it things chains are stale + await connector.setRequestedChainsIds(siweParams.chains) + + const result = await provider.authenticate({ + nonce: await siweConfig.getNonce(), + methods: [...OPTIONAL_METHODS], + ...siweParams + }) + // Auths is an array of signed CACAO objects https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-74.md + const signedCacao = result?.auths?.[0] + if (signedCacao) { + const { p, s } = signedCacao + const cacaoChainId = getDidChainId(p.iss) || '' + const address = getDidAddress(p.iss) + if (address && cacaoChainId) { + SIWEController.setSession({ + address, + chainId: parseInt(cacaoChainId, 10) + }) + } + try { + // Kicks off verifyMessage and populates external states + const message = provider.signer.client.formatAuthMessage({ + request: p, + iss: p.iss + }) + + await SIWEController.verifyMessage({ + message, + signature: s.s, + cacao: signedCacao + }) + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error verifying message', error) + // eslint-disable-next-line no-console + await provider.disconnect().catch(console.error) + // eslint-disable-next-line no-console + await SIWEController.signOut().catch(console.error) + throw error + } + /* + * Unassign the connector from the wagmiConfig and allow connect() to reassign it in the next step + * this avoids case where wagmi throws because the connector is already connected + * what we need connect() to do is to only setup internal event listeners + */ + this.wagmiConfig.state.current = '' + } + } await connect(this.wagmiConfig, { connector, chainId }) }, diff --git a/packages/wagmi/src/connectors/alphaWalletConnect.ts b/packages/wagmi/src/connectors/alphaWalletConnect.ts new file mode 100644 index 0000000000..1206d91ea9 --- /dev/null +++ b/packages/wagmi/src/connectors/alphaWalletConnect.ts @@ -0,0 +1,440 @@ +import { + ChainNotConfiguredError, + type Connector, + ProviderNotFoundError, + createConnector +} from '@wagmi/core' +import { type Evaluate, type ExactPartial, type Omit } from '@wagmi/core/internal' +import { type EthereumProvider } from '@walletconnect/ethereum-provider' +import { + 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 = Evaluate< + { + /** + * 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> +> + +alphaWalletConnect.type = 'walletConnect' as const +export function alphaWalletConnect(parameters: WalletConnectParameters) { + const isNewChainsStale = parameters.isNewChainsStale ?? true + + type Provider = Awaited> + type Properties = { + connect(parameters?: { chainId?: number; pairingTopic?: string }): 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: alphaWalletConnect.type, + async setup() { + const provider = await this.getProvider().catch(() => null) + if (!provider) return + 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 => [chain.id, chain.rpcUrls.default.http[0]!]) + ), + showQrModal: parameters.showQrModal ?? true + }) + } + + if (!provider_) { + if (!providerPromise) providerPromise = initProvider() + provider_ = await providerPromise + provider_?.events.setMaxListeners(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({ 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 }) => { + 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 + + // 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 + if (blockExplorer) + blockExplorerUrls = [ + blockExplorer.url, + ...Object.values(blockExplorers).map(x => x.url) + ] + + await provider.request({ + method: 'wallet_addEthereumChain', + params: [ + { + chainId: numberToHex(chainId), + chainName: chain.name, + nativeCurrency: chain.nativeCurrency, + rpcUrls: [chain.rpcUrls.default?.http[0] ?? ''], + blockExplorerUrls + } + ] + }) + + const currentChainId = await this.getChainId() + if (currentChainId !== chainId) + throw new UserRejectedRequestError( + new Error('User rejected switch after adding network.') + ) + + const requestedChains = await this.getRequestedChainsIds() + this.setRequestedChainsIds([...requestedChains, chainId]) + return chain + } catch (error) { + throw new UserRejectedRequestError(error as Error) + } + } + + if (error.code === UserRejectedRequestError.code) throw new UserRejectedRequestError(error) + throw new SwitchChainError(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 + } + 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]?.chains?.map(chain => + parseInt(chain.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/wagmi/src/utils/defaultWagmiCoreConfig.ts b/packages/wagmi/src/utils/defaultWagmiCoreConfig.ts index 19e6d22a4e..1c2fe54c57 100644 --- a/packages/wagmi/src/utils/defaultWagmiCoreConfig.ts +++ b/packages/wagmi/src/utils/defaultWagmiCoreConfig.ts @@ -2,9 +2,10 @@ import '@web3modal/polyfills' import type { CreateConfigParameters, CreateConnectorFn } from '@wagmi/core' import { createConfig } from '@wagmi/core' -import { coinbaseWallet, walletConnect, injected } from '@wagmi/connectors' +import { coinbaseWallet, injected } from '@wagmi/connectors' import { emailConnector } from '../connectors/EmailConnector.js' +import { alphaWalletConnect } from '../connectors/alphaWalletConnect.js' import { getTransport } from './helpers.js' export type ConfigOptions = Partial & { @@ -43,7 +44,7 @@ export function defaultWagmiConfig({ // Enabled by default if (enableWalletConnect !== false) { - connectors.push(walletConnect({ projectId, metadata, showQrModal: false })) + connectors.push(alphaWalletConnect({ projectId, metadata, showQrModal: false })) } if (enableInjected !== false) { diff --git a/packages/wagmi/src/utils/defaultWagmiReactConfig.ts b/packages/wagmi/src/utils/defaultWagmiReactConfig.ts index 6e76ea9bc3..5f7f96c458 100644 --- a/packages/wagmi/src/utils/defaultWagmiReactConfig.ts +++ b/packages/wagmi/src/utils/defaultWagmiReactConfig.ts @@ -2,9 +2,10 @@ import '@web3modal/polyfills' import type { CreateConfigParameters, CreateConnectorFn, Config } from 'wagmi' import { createConfig } from 'wagmi' -import { coinbaseWallet, walletConnect, injected } from 'wagmi/connectors' +import { coinbaseWallet, injected } from 'wagmi/connectors' import { emailConnector } from '../connectors/EmailConnector.js' +import { alphaWalletConnect } from '../connectors/alphaWalletConnect.js' import { getTransport } from './helpers.js' export type ConfigOptions = Partial & { @@ -43,7 +44,7 @@ export function defaultWagmiConfig({ // Enabled by default if (enableWalletConnect !== false) { - connectors.push(walletConnect({ projectId, metadata, showQrModal: false })) + connectors.push(alphaWalletConnect({ projectId, metadata, showQrModal: false })) } if (enableInjected !== false) {