diff --git a/.circleci/config.yml b/.circleci/config.yml index 34af55e64..d63e1854b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -167,7 +167,7 @@ commands: jobs: build-core: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:16.15.1 working_directory: ~/web3-onboard-monorepo/packages/core steps: - node-build-steps @@ -275,7 +275,7 @@ jobs: - node-build-steps build-react: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:16.15.1 working_directory: ~/web3-onboard-monorepo/packages/react steps: - node-build-steps @@ -293,7 +293,7 @@ jobs: - node-build-steps build-web3auth: docker: - - image: cimg/node:16.18.1 + - image: cimg/node:18.0.0 working_directory: ~/web3-onboard-monorepo/packages/web3auth steps: - node-build-steps @@ -305,7 +305,7 @@ jobs: - node-build-steps build-vue: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:16.15.1 working_directory: ~/web3-onboard-monorepo/packages/vue steps: - node-build-steps @@ -341,7 +341,7 @@ jobs: - node-build-steps build-uauth: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:18.0.0 working_directory: ~/web3-onboard-monorepo/packages/uauth steps: - node-build-steps @@ -395,7 +395,7 @@ jobs: - node-build-steps build-arcana-auth: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:18.0.0 working_directory: ~/web3-onboard-monorepo/packages/arcana-auth steps: - node-build-steps @@ -407,7 +407,7 @@ jobs: - node-build-steps build-venly: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:18.18.2 working_directory: ~/web3-onboard-monorepo/packages/venly steps: - node-build-steps @@ -432,7 +432,7 @@ jobs: resource_class: large build-solid: docker: - - image: cimg/node:16.14.2 + - image: cimg/node:16.15.1 working_directory: ~/web3-onboard-monorepo/packages/solid steps: - node-build-steps @@ -448,6 +448,12 @@ jobs: working_directory: ~/web3-onboard-monorepo/packages/particle-network steps: - node-build-steps + build-wagmi: + docker: + - image: cimg/node:18.0.0 + working_directory: ~/web3-onboard-monorepo/packages/wagmi + steps: + - node-build-steps build-passport: docker: - image: cimg/node:18.0.0 @@ -458,7 +464,7 @@ jobs: # Build staging/Alpha releases build-staging-core: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:16.15.1 working_directory: ~/web3-onboard-monorepo/packages/core steps: - node-staging-build-steps @@ -566,7 +572,7 @@ jobs: - node-staging-build-steps build-staging-react: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:16.15.1 working_directory: ~/web3-onboard-monorepo/packages/react steps: - node-staging-build-steps @@ -584,7 +590,7 @@ jobs: - node-staging-build-steps build-staging-web3auth: docker: - - image: cimg/node:16.18.1 + - image: cimg/node:18.0.0 working_directory: ~/web3-onboard-monorepo/packages/web3auth steps: - node-staging-build-steps @@ -596,7 +602,7 @@ jobs: - node-staging-build-steps build-staging-vue: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:16.15.1 working_directory: ~/web3-onboard-monorepo/packages/vue steps: - node-staging-build-steps @@ -632,7 +638,7 @@ jobs: - node-staging-build-steps build-staging-uauth: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:18.0.0 working_directory: ~/web3-onboard-monorepo/packages/uauth steps: - node-staging-build-steps @@ -686,7 +692,7 @@ jobs: - node-staging-build-steps build-staging-arcana-auth: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:18.0.0 working_directory: ~/web3-onboard-monorepo/packages/arcana-auth steps: - node-staging-build-steps @@ -698,7 +704,7 @@ jobs: - node-staging-build-steps build-staging-venly: docker: - - image: cimg/node:16.13.1 + - image: cimg/node:18.18.2 working_directory: ~/web3-onboard-monorepo/packages/venly steps: - node-staging-build-steps @@ -722,7 +728,7 @@ jobs: - node-staging-build-steps build-staging-solid: docker: - - image: cimg/node:16.14.2 + - image: cimg/node:16.15.1 working_directory: ~/web3-onboard-monorepo/packages/solid steps: - node-staging-build-steps @@ -738,6 +744,12 @@ jobs: working_directory: ~/web3-onboard-monorepo/packages/particle-network steps: - node-staging-build-steps + build-staging-wagmi: + docker: + - image: cimg/node:18.0.0 + working_directory: ~/web3-onboard-monorepo/packages/wagmi + steps: + - node-staging-build-steps build-staging-passport: docker: - image: cimg/node:18.0.0 @@ -753,6 +765,7 @@ workflows: <<: *deploy_production_filters - build-staging-common: <<: *deploy_staging_filters + core: jobs: - build-core: @@ -1030,6 +1043,12 @@ workflows: <<: *deploy_production_filters - build-staging-particle: <<: *deploy_staging_filters + wagmi: + jobs: + - build-wagmi: + <<: *deploy_production_filters + - build-staging-wagmi: + <<: *deploy_staging_filters passport: jobs: - build-passport: diff --git a/docs/src/routes/docs/[...1]overview/[...1]introduction/+page.md b/docs/src/routes/docs/[...1]overview/[...1]introduction/+page.md index 4488aab3a..3048f872c 100644 --- a/docs/src/routes/docs/[...1]overview/[...1]introduction/+page.md +++ b/docs/src/routes/docs/[...1]overview/[...1]introduction/+page.md @@ -102,6 +102,7 @@ const MAINNET_RPC_URL = 'https://mainnet.infura.io/v3/' const injected = injectedModule() const onboard = Onboard({ + // This javascript object is unordered meaning props do not require a certain order wallets: [injected], chains: [ { diff --git a/docs/src/routes/docs/[...2]getting-started/[...1]installation/+page.md b/docs/src/routes/docs/[...2]getting-started/[...1]installation/+page.md index 5a76b1313..851183733 100644 --- a/docs/src/routes/docs/[...2]getting-started/[...1]installation/+page.md +++ b/docs/src/routes/docs/[...2]getting-started/[...1]installation/+page.md @@ -110,6 +110,7 @@ const appMetadata = { ```ts const onboard = Onboard({ + // This javascript object is unordered meaning props do not require a certain order wallets, chains, appMetadata diff --git a/docs/src/routes/docs/[...3]modules/[...1]core/+page.md b/docs/src/routes/docs/[...3]modules/[...1]core/+page.md index 0edaa8c3e..f0253a60f 100644 --- a/docs/src/routes/docs/[...3]modules/[...1]core/+page.md +++ b/docs/src/routes/docs/[...3]modules/[...1]core/+page.md @@ -621,6 +621,7 @@ import injectedModule from '@web3-onboard/injected-wallets' const injected = injectedModule() const onboard = Onboard({ + // This javascript object is unordered meaning props do not require a certain order // head to https://explorer.blocknative.com/account to sign up for free apiKey: 'xxx387fb-bxx1-4xxc-a0x3-9d37e426xxxx' wallets: [injected], diff --git a/docs/src/routes/docs/[...3]modules/[...3]react/+page.md b/docs/src/routes/docs/[...3]modules/[...3]react/+page.md index 9bee922bb..77265fa88 100644 --- a/docs/src/routes/docs/[...3]modules/[...3]react/+page.md +++ b/docs/src/routes/docs/[...3]modules/[...3]react/+page.md @@ -46,6 +46,7 @@ const rpcUrl = `https://mainnet.infura.io/v3/${infuraKey}` // initialize Onboard init({ + // This javascript object is unordered meaning props do not require a certain order apiKey, wallets: [injected], chains: [ @@ -724,3 +725,31 @@ export default { } } ``` + +## `useWagmiConfig` + +This hook allows you to get the WagmiConfig (Config from the Wagmi project) from @web3-onboard/core if W3O has been initialized with the [WAGMI property imported and passing into the web3-onboard/core config](../../modules/wagmi.md#usage). + +```typescript +import { sendTransaction as wagmiSendTransaction } from '@web3-onboard/wagmi' +import { parseEther } from 'viem' +import { useWagmiConfig, wallets } from '@web3-onboard/react' +import type { WagmiConfig } from '@web3-onboard/core' + +type useWagmiConfig = (): WagmiConfig + +const wagmiConfig = useWagmiConfig() +const w3OWallets = useWallets() + +const sendTransaction = async () => { + // current primary wallet - as multiple wallets can connect this value is the currently active + const [currentPrimaryWallet] = w3OWallets + const result = await wagmiSendTransaction(wagmiConfig, { + to: toAddress, + // desired connector to send txn from + account: currentPrimaryWallet.accounts[0], + value: parseEther('0.001') + }) + console.log(result) +} +``` diff --git a/docs/src/routes/docs/[...3]modules/[...8]wagmi/+page.md b/docs/src/routes/docs/[...3]modules/[...8]wagmi/+page.md new file mode 100644 index 000000000..af928652e --- /dev/null +++ b/docs/src/routes/docs/[...3]modules/[...8]wagmi/+page.md @@ -0,0 +1,97 @@ +--- +title: wagmi +--- + +# {$frontmatter.title} + +A module for connecting wallets using WAGMI which returns a WAGMI config object to be used with [@wagmi/core](https://wagmi.sh/core/getting-started) functions. + +### Install + + + + +```sh copy +yarn add @web3-onboard/wagmi +``` + + + + +```sh copy +npm install @web3-onboard/wagmi +``` + + + + +## Usage + +This example assumes you have already setup web3-onboard to connect wallets to your dapp. +For more information see [web3-onboard docs](https://onboard.blocknative.com/docs/modules/core#install). + +```ts +import Onboard from '@web3-onboard/core' +import injectedModule from '@web3-onboard/injected-wallets' +import wagmi from '@web3-onboard/wagmi' +import { parseEther } from 'viem' +import { + sendTransaction as wagmiSendTransaction, + switchChain, + disconnect, + getConnectors +} from '@web3-onboard/wagmi' +import { parseEther, isHex, fromHex } from 'viem' + +const injected = injectedModule() + +const onboard = Onboard({ + // This javascript object is unordered meaning props do not require a certain order + // ... other Onboard options + wagmi, + wallets: [injected], + chains: [ + { + id: '0x1', + token: 'ETH', + label: 'Ethereum', + rpcUrl: 'https://mainnet.infura.io/v3/17c1e1500e384acfb6a72c5d2e67742e' + } + ] + // ... other Onboard options +}) + +const sendTransaction = async () => { + // current primary wallet - as multiple wallets can connect this value is the currently active + const [currentPrimaryWallet] = onboard.state.get().wallets + const wagmiConfig = onboard.state.get().wagmiConfig + const result = await wagmiSendTransaction(wagmiConfig, { + to: toAddress, + // desired connector to send txn from + account: currentPrimaryWallet.accounts[0], + value: parseEther('0.001') + }) + console.log(result) +} + +async function switchWagmiChain(chainId) { + let chainAsNumber + if (isHex(chainId)) { + chainAsNumber = fromHex(chainId, 'number') + } else if (!isHex(chainId) && typeof chainId === 'number') { + chainAsNumber = chainId + } else { + throw new Error('Invalid chainId') + } + const wagmiConfig = onboard.state.get().wagmiConfig + await switchChain(wagmiConfig, { chainId: chainAsNumber }) +} + +async function disconnectWallet() { + const wagmiConfig = onboard.state.get().wagmiConfig + const disconnectThisWallet = getConnectors(wagmiConfig).find( + (connector) => connector.name === label + ) + disconnect(wagmiConfig, { connector: disconnectThisWallet }) +} +``` diff --git a/package.json b/package.json index 95e3c626c..d856c72ae 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "devDependencies": { "prettier": "^2.4.1", "prettier-plugin-svelte": "^2.4.0", - "typescript": "^4.5.5" + "typescript": "^4.9.4" }, "peerDependencies": { "react": "*", diff --git a/packages/arcana-auth/package.json b/packages/arcana-auth/package.json index d47cf60f3..b0035efbf 100644 --- a/packages/arcana-auth/package.json +++ b/packages/arcana-auth/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/arcana-auth", - "version": "2.0.0", + "version": "2.1.0-alpha.2", "license": "MIT", "description": "Arcana wallet is a built-in, secure Web3 wallet that users can access instantly when logging into an app integrated with the Arcana Auth SDK. It offers a customizable interface that can be branded to match the app's style. Users don't need to generate or manage cryptographic keys or remember passphrases. The wallet uses advanced distributed key generation, giving users full control over their wallets while onboarding Web3 apps using familiar Web2 authentication methods. It is user-friendly, secure, and puts users in control of their Web3 experience.", "private": false, @@ -13,10 +13,10 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@arcana/auth": "^1.0.7", - "@web3-onboard/common": "2.3.3" + "@arcana/auth": "^1.0.10", + "@web3-onboard/common": "2.4.0-alpha.2" }, "devDependencies": { - "typescript": "^5.1.6" + "typescript": "^5.4.5" } } diff --git a/packages/arcana-auth/src/index.ts b/packages/arcana-auth/src/index.ts index 57be577fd..26f661e26 100644 --- a/packages/arcana-auth/src/index.ts +++ b/packages/arcana-auth/src/index.ts @@ -1,6 +1,5 @@ import { createEIP1193Provider, WalletInit } from '@web3-onboard/common' -import icon from './icon.js' -import type { ConstructorParams } from '@arcana/auth/types' +import type { ConstructorParams } from '@arcana/auth' export default function (opts: { clientID: string @@ -8,9 +7,7 @@ export default function (opts: { }): WalletInit { return () => ({ label: 'Arcana Auth', - async getIcon() { - return icon - }, + getIcon: async () => (await import('./icon.js')).default, async getInterface() { const { AuthProvider } = await import('@arcana/auth') diff --git a/packages/arcana-auth/tsconfig.json b/packages/arcana-auth/tsconfig.json index 592d71e8f..ea2f762cc 100644 --- a/packages/arcana-auth/tsconfig.json +++ b/packages/arcana-auth/tsconfig.json @@ -5,7 +5,9 @@ "outDir": "dist", "rootDir": "src", "declarationDir": "dist", + "moduleResolution": "node", "paths": { + "@arcana/auth/types/*": ["./node_modules/@arcana/auth/types/*"], "*": ["./src/*", "./node_modules/*"] }, "typeRoots": ["node_modules/@types"], diff --git a/packages/bitget/package.json b/packages/bitget/package.json index c311db41f..ba4e1b205 100644 --- a/packages/bitget/package.json +++ b/packages/bitget/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/bitget", - "version": "2.0.1", + "version": "2.1.0-alpha.2", "description": "bitget-wallet SDK wallet module for connecting to Web3-Onboard. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardized spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.", "keywords": [ "Ethereum", @@ -58,10 +58,10 @@ "license": "MIT", "devDependencies": { "@types/node": "^17.0.21", - "typescript": "^4.5.5" + "typescript": "^5.4.5" }, "dependencies": { "@bitget-wallet/web3-sdk": "^0.0.8", - "@web3-onboard/common": "^2.3.3" + "@web3-onboard/common": "^2.4.0-alpha.2" } } diff --git a/packages/bitget/src/index.ts b/packages/bitget/src/index.ts index 209a065c4..f37eecb71 100644 --- a/packages/bitget/src/index.ts +++ b/packages/bitget/src/index.ts @@ -31,7 +31,6 @@ function bitgetWallet(): WalletInit { const { currentProvider, getIsInstall, - getDownload, installWalletMessage } = await loadBitgetWalletDependencies() @@ -39,7 +38,6 @@ function bitgetWallet(): WalletInit { if (getIsInstall()) { provider = currentProvider() } else { - window.open(getDownload(), '_blank') throw new Error(installWalletMessage) } return { provider } diff --git a/packages/bitkeep/package.json b/packages/bitkeep/package.json index 0341e99aa..dc1b8845a 100644 --- a/packages/bitkeep/package.json +++ b/packages/bitkeep/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/bitkeep", - "version": "2.0.1", + "version": "2.1.0-alpha.2", "description": "Bitkeep Wallet SDK wallet module for connecting to Web3-Onboard. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardized spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.", "keywords": [ "Ethereum", @@ -58,10 +58,10 @@ "license": "MIT", "devDependencies": { "@types/node": "^17.0.21", - "typescript": "^4.5.5" + "typescript": "^5.4.5" }, "dependencies": { "@bitget-wallet/web3-sdk": "^0.0.8", - "@web3-onboard/common": "^2.3.3" + "@web3-onboard/common": "^2.4.0-alpha.2" } } diff --git a/packages/bitkeep/src/index.ts b/packages/bitkeep/src/index.ts index 321a188ae..327438695 100644 --- a/packages/bitkeep/src/index.ts +++ b/packages/bitkeep/src/index.ts @@ -34,7 +34,6 @@ function bitKeep(): WalletInit { const { currentProvider, getIsInstall, - getDownload, installWalletMessage } = await loadBitKeepDependencies() @@ -42,7 +41,6 @@ function bitKeep(): WalletInit { if (getIsInstall()) { provider = currentProvider() } else { - window.open(getDownload(), '_blank') throw new Error(installWalletMessage) } return { provider } diff --git a/packages/blocto/package.json b/packages/blocto/package.json index 17d2fdeb0..d60768271 100644 --- a/packages/blocto/package.json +++ b/packages/blocto/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/blocto", - "version": "2.0.1", + "version": "2.1.0-alpha.2", "description": "Blocto SDK wallet module for connecting to Web3-Onboard. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardised spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.", "keywords": [ "Ethereum", @@ -57,15 +57,14 @@ }, "license": "MIT", "devDependencies": { - "@ethersproject/providers": "^5.5.0", "@types/lodash.uniqby": "^4.7.6", "@types/node": "^17.0.21", "ts-node": "^10.2.1", - "typescript": "^4.5.5", + "typescript": "^5.4.5", "window": "^4.2.7" }, "dependencies": { - "@web3-onboard/common": "^2.3.1", + "@web3-onboard/common": "^2.4.0-alpha.2", "@blocto/sdk": "^0.9.1" } } diff --git a/packages/capsule/package.json b/packages/capsule/package.json index 585292b21..462fcfb39 100644 --- a/packages/capsule/package.json +++ b/packages/capsule/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/capsule", - "version": "2.0.3", + "version": "2.1.0-alpha.2", "description": "Capsule SDK wallet module for connecting to Web3-Onboard. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardised spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.", "module": "dist/index.js", "browser": "dist/index.js", @@ -60,10 +60,10 @@ "@usecapsule/react-sdk": "^2.3.1", "@usecapsule/wagmi-v2-integration": "^1.7.1", "@wagmi/chains": "^1.8.0", - "@web3-onboard/common": "^2.3.3", + "@web3-onboard/common": "^2.4.0-alpha.2", "react-dom": "^18.2.0", - "viem": "^2.9.15", - "wagmi": "^2.5.19" + "viem": "2.9.15", + "wagmi": "2.5.19" }, "peerDependencies": { "react": ">=18.2" @@ -71,6 +71,6 @@ "devDependencies": { "@types/react": "^18.0.2", "react": "^18.2.0", - "typescript": "^5.2.2" + "typescript": "^5.4.5" } } diff --git a/packages/capsule/src/index.ts b/packages/capsule/src/index.ts index 9c1c3d58c..0954c46d3 100644 --- a/packages/capsule/src/index.ts +++ b/packages/capsule/src/index.ts @@ -12,7 +12,7 @@ async function buildChainsMap(): Promise { const chainEntries = Object.entries(chains) const chainsMap: ChainsMap = new Map() - for (const [chainName, chainObject] of chainEntries) { + for (const [, chainObject] of chainEntries) { if (chainObject && 'id' in chainObject) { chainsMap.set(chainObject.id, chainObject as Chain) } diff --git a/packages/cede-store/package.json b/packages/cede-store/package.json index 9c74d5f70..e6b4e4c85 100644 --- a/packages/cede-store/package.json +++ b/packages/cede-store/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/cede-store", - "version": "2.2.0", + "version": "2.3.0-alpha.2", "description": "cede.store SDK wallet module for connecting to Web3-Onboard. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardised spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.", "keywords": [ "Ethereum", @@ -65,11 +65,11 @@ "@types/lodash.uniqby": "^4.7.6", "@types/node": "^17.0.21", "ts-node": "^10.2.1", - "typescript": "^4.5.5", + "typescript": "^5.4.5", "window": "^4.2.7" }, "dependencies": { "@cedelabs/providers": "^1.5.0", - "@web3-onboard/common": "^2.3.3" + "@web3-onboard/common": "^2.4.0-alpha.2" } } diff --git a/packages/cede-store/src/index.ts b/packages/cede-store/src/index.ts index 9ff9e908c..edd9d2549 100644 --- a/packages/cede-store/src/index.ts +++ b/packages/cede-store/src/index.ts @@ -1,6 +1,9 @@ import { CedeProvider, detectCedeProvider } from '@cedelabs/providers' -import type { WalletInit } from '@web3-onboard/common' -import { createEIP1193Provider } from '@web3-onboard/common' +import type { ProviderAccounts, WalletInit } from '@web3-onboard/common' +import { + createDownloadMessage, + createEIP1193Provider +} from '@web3-onboard/common' type CustomWindow = typeof window & { cede: CedeProvider @@ -16,8 +19,9 @@ function cedeStoreWallet(): WalletInit { getInterface: async () => { const provider = await detectCedeProvider() if (!provider) { - window.open('https://cede.store', '_blank') - throw new Error('Please, install cede.store to use this wallet') + throw new Error( + createDownloadMessage('cede.store', 'https://cede.store') + ) } // handle disconnect @@ -38,7 +42,7 @@ function cedeStoreWallet(): WalletInit { const activeVault = accounts.find(account => account.isActive) - return [activeVault?.name || accounts[0].name] + return [activeVault?.name || accounts[0].name] as ProviderAccounts }, eth_chainId: () => Promise.resolve('0x1'), // cede.store doesn't support chains, but we have to provide a value to complete the connection wallet_switchEthereumChain: null, diff --git a/packages/coinbase/package.json b/packages/coinbase/package.json index 6e9854042..33ddcfb4f 100644 --- a/packages/coinbase/package.json +++ b/packages/coinbase/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/coinbase", - "version": "2.2.7", + "version": "2.3.0-alpha.2", "description": "Coinbase SDK wallet module for connecting to Web3-Onboard. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardised spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.", "keywords": [ "Ethereum", @@ -55,10 +55,10 @@ }, "license": "MIT", "devDependencies": { - "typescript": "^4.5.5" + "typescript": "^5.4.5" }, "dependencies": { - "@coinbase/wallet-sdk": "^3.9.3", - "@web3-onboard/common": "^2.3.3" + "@coinbase/wallet-sdk": "3.9.2", + "@web3-onboard/common": "^2.4.0-alpha.2" } } diff --git a/packages/common/package.json b/packages/common/package.json index 66212267f..f6e8a62f4 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/common", - "version": "2.3.4", + "version": "2.4.0-alpha.2", "description": "Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardised spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.", "keywords": [ "Ethereum", @@ -71,11 +71,11 @@ "svelte-check": "^2.2.6", "svelte-preprocess": "^4.9.4", "tslib": "^2.0.0", - "typescript": "^4.5.5" + "typescript": "^5.4.5", + "ethers": "5.5.4" }, "dependencies": { - "bignumber.js": "^9.1.0", - "ethers": "5.5.4", - "joi": "17.9.1" + "joi": "17.9.1", + "viem": "2.12.0" } } diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 5e46b5eb8..45735c0f1 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,7 +1,15 @@ export { ProviderRpcError } from './errors.js' export { createEIP1193Provider } from './eip-1193.js' export { InterVar } from './fonts.js' -export { weiToEth } from './utils.js' +export { + weiHexToEth, + weiToEth, + isAddress, + bigIntToHex, + ethToWeiBigInt, + createDownloadMessage, + chainIdToViemImport +} from './utils.js' export * from './types.js' export * from './validation.js' diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 2579ab5ad..273e16972 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -1,7 +1,6 @@ import type { ConnectionInfo } from 'ethers/lib/utils' -import type EventEmitter from 'eventemitter3' +import EventEmitter from 'eventemitter3' import type { TypedData as EIP712TypedData } from 'eip-712' -import type { ethers } from 'ethers' export type { TypedData as EIP712TypedData } from 'eip-712' /** @@ -120,14 +119,20 @@ export type RecommendedInjectedWallets = { /** * A method that takes `WalletHelpers` and - * returns an initialised `WalletModule` or array of `WalletModule`s. + * returns an initialized `WalletModule` or array of `WalletModule`s. */ export type WalletInit = ( helpers: WalletHelpers ) => WalletModule | WalletModule[] | null +export type DeviceNotBrowser = { + type: null + os: null + browser: null +} + export type WalletHelpers = { - device: Device + device: Device | DeviceNotBrowser } export interface APIKey { @@ -189,7 +194,6 @@ export interface WalletModule { export type GetInterfaceHelpers = { chains: Chain[] appMetadata: AppMetadata | null - BigNumber: typeof ethers.BigNumber EventEmitter: typeof EventEmitter } @@ -219,7 +223,7 @@ export interface ProviderInfo { chainId: ChainId } -export type AccountAddress = string +export type AccountAddress = Address /** * An array of addresses @@ -296,7 +300,7 @@ export interface EthSignTransactionRequest { params: [TransactionObject] } -type Address = string +export type Address = `0x${string}` type Message = string export interface EthSignMessageRequest { method: 'eth_sign' @@ -421,8 +425,8 @@ export interface Chain { providerConnectionInfo?: ConnectionInfo /* An optional public RPC used when adding a new chain config to the wallet */ publicRpcUrl?: string - /** An optional protected RPC URL - Defaults to Blocknative's private and - * protected RPC to allow users to update the chain RPC within their wallet, + /** An optional protected RPC URL - Defaults to Blocknative's private and + * protected RPC to allow users to update the chain RPC within their wallet, * specifically for private RPCs that protect user transactions */ protectedRpcUrl?: string diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index 3d11abf68..778f5d630 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -1,5 +1,146 @@ -import Bignumber from 'bignumber.js' +import { + formatEther, + fromHex, + hexToBigInt, + numberToHex, + parseEther +} from 'viem' +import type { Address, Chain } from './types.js' +import type { Chain as ViemChain } from 'viem' -export function weiToEth(wei: string): string { - return new Bignumber(wei).div(1e18).toString(10) +export const isAddress = (address: string): address is Address => { + return isAddress(address) } + +export const weiHexToEth = (wei: `0x${string}`): string => { + const weiBigInt = hexToBigInt(wei) + return formatEther(weiBigInt) +} + +export const weiToEth = (wei: string): string => { + if (!wei) return wei + const weiBigInt = fromHex(wei as `0x${string}`, 'bigint') + return formatEther(weiBigInt) +} + +export const ethToWeiBigInt = (eth: string | number): bigint => { + if (typeof eth !== 'string' && typeof eth !== 'number') { + throw new Error('eth must be a string or number value') + } + + const ethString = typeof eth === 'number' ? eth.toString() : eth + + return parseEther(ethString) +} + +export const bigIntToHex = (value: bigint): string => { + return numberToHex(value) +} + +export const createDownloadMessage = ( + walletLabel: string, + download?: string | (() => void) +): string => { + if (!download) return `Please switch to ${walletLabel} to continue` + if (typeof download === 'function') { + return `Please install or enable to ${walletLabel} to continue` + } else { + return `Please install or enable to ${walletLabel} to continue` + } +} + +export const chainIdToViemImport = async ( + w3oChain: Chain +): Promise => { + const viemChains = await import('viem/chains') + const { id, label, token, publicRpcUrl, blockExplorerUrl, rpcUrl } = w3oChain + switch (id) { + case '0x89': { + const { polygon } = viemChains + return polygon + } + case '0xa': { + const { optimism } = viemChains + return optimism + } + case '0xa4b1': { + const { arbitrum } = viemChains + return arbitrum + } + case '0x144': { + const { zkSync } = viemChains + return zkSync + } + case '0x38': { + const { bsc } = viemChains + return bsc + } + case '0x1': { + const { mainnet } = viemChains + return mainnet + } + case '0xaa36a7': { + const { sepolia } = viemChains + return sepolia + } + case '0xfa': { + const { fantom } = viemChains + return fantom + } + case '0xa86a': { + const { avalanche } = viemChains + return avalanche + } + case '0xa4ec': { + const { celo } = viemChains + return celo + } + case '0x2105': { + const { base } = viemChains + return base + } + case '0x14a33': { + const { baseGoerli } = viemChains + return baseGoerli + } + case '0x64': { + const { gnosis } = viemChains + return gnosis + } + case '0x63564C40': { + const { harmonyOne } = viemChains + return harmonyOne + } + case '0x27bc86aa': { + const { degen } = viemChains + return degen + } + default: { + const { extractChain, defineChain } = await import('viem') + const nonNativeChain = extractChain({ + chains: Object.values(viemChains) as ViemChain[], + id: fromHex(id as `0x${string}`, 'number') + }) + if (nonNativeChain) return nonNativeChain + + return defineChain({ + id: fromHex(id as `0x${string}`, 'number'), + name: label ?? '', + nativeCurrency: { + decimals: 18, + name: token ?? '', + symbol: token ?? '' + }, + rpcUrls: { + default: { + http: [rpcUrl ?? '', publicRpcUrl ?? ''] + } + }, + blockExplorers: { + default: { name: 'Explorer', url: blockExplorerUrl ?? '' } + } + }) + } + } +} \ No newline at end of file diff --git a/packages/common/src/validation.ts b/packages/common/src/validation.ts index 809956a3e..6118e981c 100644 --- a/packages/common/src/validation.ts +++ b/packages/common/src/validation.ts @@ -17,20 +17,6 @@ export const chainIdValidation = Joi.alternatives().try( export const chainNamespaceValidation = Joi.string().valid('evm') -/** Related to ConnectionInfo from 'ethers/lib/utils' */ -export const providerConnectionInfoValidation = Joi.object({ - url: Joi.string().required(), - headers: Joi.object(), - user: Joi.string(), - password: Joi.string(), - allowInsecureAuthentication: Joi.boolean(), - allowGzip: Joi.boolean(), - throttleLimit: Joi.number(), - throttleSlotInterval: Joi.number(), - throttleCallback: Joi.function(), - timeout: Joi.number() -}) - const secondaryTokenValidation = Joi.object({ address: Joi.string().required(), icon: Joi.string().optional() @@ -50,6 +36,5 @@ export const chainValidation = Joi.object({ color: Joi.string(), publicRpcUrl: Joi.string(), protectedRpcUrl: Joi.string(), - blockExplorerUrl: Joi.string(), - providerConnectionInfoValidation + blockExplorerUrl: Joi.string() }) diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json index 9db8ebeb0..2fe4bf26d 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", "include": ["src/**/*"], - + "exclude": ["node_modules/*", "dist"], "compilerOptions": { "outDir": "dist", "rootDir": "src", @@ -11,6 +11,10 @@ "paths": { "*": ["./src/*", "./node_modules/*"] }, - "typeRoots": ["node_modules/@types"] + "typeRoots": ["node_modules/@types"], + "lib": ["es2020"], + "moduleResolution": "node", + "esModuleInterop": true, + "types": ["svelte"] } } diff --git a/packages/core/.npmrc b/packages/core/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/packages/core/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/packages/core/README.md b/packages/core/README.md index 2a33e37ec..291e553a6 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -577,6 +577,7 @@ const injected = injectedModule() const ETH_MAINNET_RPC = `https://mainnet.infura.io/v3/${INFURA_KEY}` || `https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}` const onboard = Onboard({ + // This javascript object is unordered meaning props do not require a certain order // head to https://explorer.blocknative.com/account to sign up for free apiKey: 'xxx387fb-bxx1-4xxc-a0x3-9d37e426xxxx' wallets: [injected], diff --git a/packages/core/package.json b/packages/core/package.json index 5d949e8fb..f02af9507 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/core", - "version": "2.21.6", + "version": "2.22.0-alpha.4", "description": "Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardized spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.", "keywords": [ "Ethereum", @@ -64,33 +64,32 @@ "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.6", "@rollup/plugin-replace": "^3.0.0", - "@rollup/plugin-typescript": "^8.0.0", - "@tsconfig/svelte": "^2.0.0", + "@rollup/plugin-typescript": "^11.1.6", + "@tsconfig/svelte": "^5.0.0", "@types/lodash.merge": "^4.6.6", "@types/lodash.partition": "^4.6.6", "@typescript-eslint/eslint-plugin": "^4.31.1", "@typescript-eslint/parser": "^4.31.1", "@web3-onboard/gas": "^2.1.5", + "@web3-onboard/wagmi": "^2.0.0-alpha.2", "@web3-onboard/transaction-preview": "^2.0.5", - "@web3-onboard/unstoppable-resolution": "^2.0.0", + "@web3-onboard/unstoppable-resolution": "^2.0.0-alpha.1", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^3.2.1", "prettier": "^2.4.0", "prettier-plugin-svelte": "^2.4.0", - "rollup": "^2.3.4", - "rollup-plugin-svelte": "^7.0.0", - "svelte-check": "^2.2.6", - "svelte-preprocess": "^4.9.4", + "rollup": "^2.14.0", + "rollup-plugin-svelte": "^7.2.0", + "svelte-check": "^3.7.1", + "svelte-preprocess": "^5.1.4", "tslib": "^2.0.0", - "typescript": "^4.5.5" + "typescript": "^5.4.5" }, "dependencies": { - "@web3-onboard/common": "^2.3.4", - "bignumber.js": "^9.0.0", + "@web3-onboard/common": "2.4.0-alpha.2", "bnc-sdk": "^4.6.7", "bowser": "^2.11.0", - "ethers": "5.5.3", "eventemitter3": "^4.0.7", "joi": "17.9.1", "lodash.merge": "^4.6.2", @@ -98,6 +97,10 @@ "nanoid": "^4.0.0", "rxjs": "^7.5.5", "svelte": "^3.49.0", - "svelte-i18n": "^3.3.13" + "svelte-i18n": "^3.3.13", + "viem": "2.12.0" + }, + "engines": { + "node": ">=16.15.1" } } diff --git a/packages/core/rollup.config.js b/packages/core/rollup.config.js index 3f99e49b5..1a5a6b237 100644 --- a/packages/core/rollup.config.js +++ b/packages/core/rollup.config.js @@ -12,7 +12,8 @@ export default { input: 'src/index.ts', output: { format: 'es', - dir: 'dist/' + dir: 'dist/', + sourcemap: true }, plugins: [ json(), @@ -21,7 +22,13 @@ export default { preventAssignment: true }), svelte({ - preprocess: sveltePreprocess({ sourceMap: !production }), + preprocess: sveltePreprocess({ + sourceMap: !production, + typescript: { + tsconfigFile: './tsconfig.json' + }, + postcss: true + }), compilerOptions: { dev: !production }, @@ -29,20 +36,21 @@ export default { }), resolve({ browser: true, - dedupe: ['svelte'] + dedupe: ['svelte'], + extensions: ['.js', '.ts', '.svelte'] }), typescript({ sourceMap: !production, - inlineSources: !production + inlineSources: !production, + exclude: ['node_modules/**'] }), copy({ src: 'src/i18n/en.json', dest: 'i18n' - }) + }), ], external: [ '@web3-onboard/common', - 'ethers', 'bowser', 'joi', 'rxjs', @@ -52,9 +60,9 @@ export default { 'lodash.merge', 'lodash.partition', 'eventemitter3', - 'bignumber.js', 'bnc-sdk', 'nanoid', - '@unstoppabledomains/resolution' + '@unstoppabledomains/resolution', + 'viem' ] } diff --git a/packages/core/src/chain.ts b/packages/core/src/chain.ts index 700079791..6d5bebb05 100644 --- a/packages/core/src/chain.ts +++ b/packages/core/src/chain.ts @@ -1,6 +1,6 @@ import { firstValueFrom, Observable } from 'rxjs' import { filter, map } from 'rxjs/operators' -import { Chain, ProviderRpcErrorCode } from '@web3-onboard/common' +import { type Chain, ProviderRpcErrorCode } from '@web3-onboard/common' import { addNewChain, switchChain } from './provider.js' import { state } from './store/index.js' import { switchChainModal$ } from './streams.js' diff --git a/packages/core/src/configuration.ts b/packages/core/src/configuration.ts index b1687f78d..94c86db52 100644 --- a/packages/core/src/configuration.ts +++ b/packages/core/src/configuration.ts @@ -3,13 +3,14 @@ import { getDevice } from './utils.js' export let configuration: Configuration = { svelteInstance: null, - apiKey: null, + apiKey: undefined, device: getDevice(), initialWalletInit: [], - gas: null, - containerElements: { accountCenter: null, connectModal: null }, - transactionPreview: null, - unstoppableResolution: null + gas: undefined, + containerElements: { accountCenter: undefined, connectModal: undefined }, + transactionPreview: undefined, + unstoppableResolution: undefined, + wagmi: undefined } export function updateConfiguration(update: Partial): void { diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 09c3f5631..20442835d 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -28,6 +28,7 @@ export const APP_INITIAL_STATE: AppState = { disableClose: false }, appMetadata: null, + wagmiConfig: null } export const STORAGE_KEYS = { diff --git a/packages/core/src/disconnect.ts b/packages/core/src/disconnect.ts index 8c3680c4e..1321ad34a 100644 --- a/packages/core/src/disconnect.ts +++ b/packages/core/src/disconnect.ts @@ -1,11 +1,12 @@ import { getBNMulitChainSdk } from './services.js' import { state } from './store/index.js' -import { removeWallet } from './store/actions.js' +import { removeWallet, updateWagmiConfig } from './store/actions.js' import { disconnectWallet$ } from './streams.js' import type { DisconnectOptions, WalletState } from './types.js' import { validateDisconnectOptions } from './validation.js' import { delLocalStore, getLocalStore, setLocalStore } from './utils' import { STORAGE_KEYS } from './constants' +import { configuration } from './configuration' async function disconnect(options: DisconnectOptions): Promise { const error = validateDisconnectOptions(options) @@ -22,20 +23,30 @@ async function disconnect(options: DisconnectOptions): Promise { if (sdk) { const wallet = state.get().wallets.find(wallet => wallet.label === label) - wallet.accounts.forEach(({ address }) => { - sdk.unsubscribe({ - id: address, - chainId: wallet.chains[0].id, - timeout: 60000 + if (wallet) { + wallet.accounts.forEach(({ address }) => { + sdk.unsubscribe({ + id: address, + chainId: wallet.chains[0].id, + timeout: 60000 + }) }) - }) + } + } + } + const { wagmi } = configuration + if (wagmi) { + const wagmiConfig = await wagmi.wagmiDisconnectWallet(label) + if (wagmiConfig) { + updateWagmiConfig(wagmiConfig) } } - disconnectWallet$.next(label) removeWallet(label) - const labels = JSON.parse(getLocalStore(STORAGE_KEYS.LAST_CONNECTED_WALLET)) + const labels = JSON.parse( + getLocalStore(STORAGE_KEYS.LAST_CONNECTED_WALLET) || '' + ) if (Array.isArray(labels) && labels.indexOf(label) >= 0) { setLocalStore( diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f125cd856..c5018a360 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -33,10 +33,13 @@ import { setWalletModules, updateConnectModal, updateTheme, - updateAppMetadata + updateAppMetadata, + updateChain } from './store/actions.js' import type { PatchedEIP1193Provider } from '@web3-onboard/transaction-preview' import { getBlocknativeSdk } from './services.js' +import type { WagmiModuleAPI } from '@web3-onboard/wagmi' +import { wagmiProviderMethods } from './provider' const API = { connectWallet, @@ -75,7 +78,8 @@ export type { Notify, UpdateNotification, PreflightNotificationsOptions, - Theme + Theme, + WagmiConfig } from './types.js' export type { EIP1193Provider } from '@web3-onboard/common' @@ -105,7 +109,8 @@ function init(options: InitOptions): OnboardAPI { transactionPreview, theme, disableFontDownload, - unstoppableResolution + unstoppableResolution, + wagmi } = options if (containerElements) updateConfiguration({ containerElements }) @@ -121,8 +126,10 @@ function init(options: InitOptions): OnboardAPI { initI18N(i18n) addChains(chainIdToHex(chains)) - if (typeof connect !== undefined) { - updateConnectModal(connect) + if (typeof connect !== 'undefined') { + updateConnectModal( + connect as ConnectModalOptions | Partial + ) } // update accountCenter if (typeof accountCenter !== 'undefined') { @@ -145,7 +152,18 @@ function init(options: InitOptions): OnboardAPI { ...accountCenter.desktop } } - updateAccountCenter(accountCenterUpdate) + if (typeof accountCenterUpdate !== 'undefined') { + updateAccountCenter(accountCenterUpdate) + } + } + + let wagmiApi: WagmiModuleAPI | undefined + if (typeof wagmi !== 'undefined') { + wagmiApi = wagmi({ + disconnect: disconnectWallet, + updateChain, + ...wagmiProviderMethods() + }) } // update notify @@ -158,7 +176,9 @@ function init(options: InitOptions): OnboardAPI { } if ( - (!notify.desktop || (notify.desktop && !notify.desktop.position)) && + notify && + notify.desktop && + notify.desktop.position && accountCenter && accountCenter.desktop && accountCenter.desktop.position @@ -167,7 +187,9 @@ function init(options: InitOptions): OnboardAPI { } if ( - (!notify.mobile || (notify.mobile && !notify.mobile.position)) && + notify && + notify.mobile && + notify.mobile.position && accountCenter && accountCenter.mobile && accountCenter.mobile.position @@ -175,7 +197,7 @@ function init(options: InitOptions): OnboardAPI { notify.mobile.position = accountCenter.mobile.position } - let notifyUpdate: Partial + let notifyUpdate: Partial = {} if (device.type === 'mobile' && notify.mobile) { notifyUpdate = { @@ -210,7 +232,8 @@ function init(options: InitOptions): OnboardAPI { updateNotify(notifyUpdate) } - const app = svelteInstance || mountApp(theme, disableFontDownload) + const app = + svelteInstance || mountApp(theme || {}, disableFontDownload || false) updateConfiguration({ svelteInstance: app, @@ -218,16 +241,19 @@ function init(options: InitOptions): OnboardAPI { initialWalletInit: wallets, gas, transactionPreview, - unstoppableResolution + unstoppableResolution, + wagmi: wagmiApi }) appMetadata && updateAppMetadata(appMetadata) if (apiKey && transactionPreview) { const getBnSDK = async () => { + const sdk = await getBlocknativeSdk() + if (!sdk) return transactionPreview.init({ containerElement: '#w3o-transaction-preview-container', - sdk: await getBlocknativeSdk(), + sdk, apiKey }) wallets$.subscribe(wallets => { @@ -250,7 +276,9 @@ function init(options: InitOptions): OnboardAPI { STORAGE_KEYS.LAST_CONNECTED_WALLET ) try { - const lastConnectedWalletsParsed = JSON.parse(lastConnectedWallets) + const lastConnectedWalletsParsed = JSON.parse( + lastConnectedWallets as string + ) if ( lastConnectedWalletsParsed && Array.isArray(lastConnectedWalletsParsed) && @@ -451,7 +479,14 @@ function mountApp(theme: Theme, disableFontDownload: boolean) { } ` - const connectModalContEl = configuration.containerElements.connectModal + let connectModalContEl + if ( + configuration && + configuration.containerElements && + configuration.containerElements.connectModal + ) { + connectModalContEl = configuration.containerElements.connectModal + } const containerElementQuery = connectModalContEl || state.get().accountCenter.containerElement || 'body' diff --git a/packages/core/src/notify.ts b/packages/core/src/notify.ts index 46401494d..eda65c618 100644 --- a/packages/core/src/notify.ts +++ b/packages/core/src/notify.ts @@ -1,7 +1,7 @@ -import BigNumber from 'bignumber.js' import { get } from 'svelte/store' import { _ } from 'svelte-i18n' import defaultCopy from './i18n/en.json' +import { weiToEth } from '@web3-onboard/common' import type { EthereumTransactionData } from 'bnc-sdk' import type { @@ -27,7 +27,11 @@ export function handleTransactionUpdates( } if (transaction.eventCode === 'txConfirmed') { - updateBalances([transaction.watchedAddress, transaction.counterparty]) + const addresses = [ + transaction.watchedAddress, + transaction.counterparty + ].filter(Boolean) as string[] + updateBalances(addresses) } const notification = transactionEventToNotification(transaction, customized) @@ -63,11 +67,7 @@ export function transactionEventToNotification( counterparty.substring(0, 4) + '...' + counterparty.substring(counterparty.length - 4) - - const formattedValue = new BigNumber(value || 0) - .div(new BigNumber('1000000000000000000')) - .toString(10) - + const formattedValue = weiToEth(value) const formatterOptions = counterparty && value ? { diff --git a/packages/core/src/preflight-notifications.ts b/packages/core/src/preflight-notifications.ts index b78f85d7d..d9247e7c3 100644 --- a/packages/core/src/preflight-notifications.ts +++ b/packages/core/src/preflight-notifications.ts @@ -1,7 +1,7 @@ -import BigNumber from 'bignumber.js' import { nanoid } from 'nanoid' import defaultCopy from './i18n/en.json' import type { Network } from 'bnc-sdk' +import { bigIntToHex, ethToWeiBigInt } from '@web3-onboard/common' import type { Notification, PreflightNotificationsOptions } from './types.js' import { addNotification, removeNotification } from './store/actions.js' @@ -49,16 +49,19 @@ export async function preflightNotifications( // to disable hints for `txAwaitingApproval`, `txConfirmReminder` // or any other notification, then return false from listener functions - const [gas, price] = await gasEstimates(estimateGas, gasPrice) + const [gas, price] = await gasEstimates( + estimateGas || (() => Promise.resolve('')), + gasPrice || (() => Promise.resolve('')) + ) const id = createId(nanoid()) - const value = new BigNumber((txDetails && txDetails.value) || 0) + const value = BigInt((txDetails && txDetails.value) || 0) // check sufficient balance if required parameters are available if (balance && gas && price) { - const transactionCost = gas.times(price).plus(value) + const transactionCost = BigInt(gas) * BigInt(price) + value // if transaction cost is greater than the current balance - if (transactionCost.gt(new BigNumber(balance))) { + if (bigIntToHex(transactionCost) > bigIntToHex(ethToWeiBigInt(balance))) { const eventCode = 'nsfFail' addNotification(buildNotification(eventCode, id)) @@ -220,7 +223,7 @@ const gasEstimates = async ( ) } - return [new BigNumber(gasResult), new BigNumber(gasPriceResult)] + return [BigInt(gasResult), BigInt(gasPriceResult)] }) .catch(error => { throw new Error(`There was an error getting gas estimates: ${error}`) diff --git a/packages/core/src/provider.ts b/packages/core/src/provider.ts index 3d45f9985..57b07e07a 100644 --- a/packages/core/src/provider.ts +++ b/packages/core/src/provider.ts @@ -1,17 +1,20 @@ import { fromEventPattern, Observable } from 'rxjs' import { filter, takeUntil, take, share, switchMap } from 'rxjs/operators' import partition from 'lodash.partition' -import { providers, utils } from 'ethers' -import { weiToEth } from '@web3-onboard/common' +import { isAddress, weiHexToEth } from '@web3-onboard/common' import { disconnectWallet$ } from './streams.js' import { updateAccount, updateWallet } from './store/actions.js' -import { validEnsChain } from './utils.js' +import { chainIdToViemENSImport, validEnsChain } from './utils.js' import disconnect from './disconnect.js' import { state } from './store/index.js' import { getBNMulitChainSdk } from './services.js' import { configuration } from './configuration.js' +import { updateSecondaryTokens } from './update-balances' +import type { Uns } from '@web3-onboard/unstoppable-resolution' +import type { PublicClient, GetEnsTextReturnType } from 'viem' import type { + Address, ChainId, EIP1102Request, EIP1193Provider, @@ -24,32 +27,32 @@ import type { import type { Account, - Address, Balances, Ens, WalletPermission, WalletState } from './types.js' -import type { Uns } from '@web3-onboard/unstoppable-resolution' -import { updateSecondaryTokens } from './update-balances' - -export const ethersProviders: { - [key: string]: providers.StaticJsonRpcProvider +export const viemProviders: { + [key: string]: PublicClient } = {} -export function getProvider(chain: Chain): providers.StaticJsonRpcProvider { +async function getProvider(chain: Chain): Promise { if (!chain) return null - if (!ethersProviders[chain.rpcUrl]) { - ethersProviders[chain.rpcUrl] = new providers.StaticJsonRpcProvider( - chain.providerConnectionInfo && chain.providerConnectionInfo.url - ? chain.providerConnectionInfo - : chain.rpcUrl - ) + if (!viemProviders[chain.rpcUrl as string]) { + const viemChain = await chainIdToViemENSImport(chain.id) + if (!viemChain) return null + + const { createPublicClient, http } = await import('viem') + const publicProvider = createPublicClient({ + chain: viemChain, + transport: http() + }) + viemProviders[chain.rpcUrl as string] = publicProvider as PublicClient } - return ethersProviders[chain.rpcUrl] + return viemProviders[chain.rpcUrl as string] } export function requestAccounts( @@ -143,7 +146,8 @@ export function trackWallet( } const { wallets } = state.get() - const { accounts } = wallets.find(wallet => wallet.label === label) + const wallet = wallets.find(wallet => wallet.label === label) + const accounts = wallet ? wallet.accounts : [] const [[existingAccount], restAccounts] = partition( accounts, @@ -154,7 +158,7 @@ export function trackWallet( updateWallet(label, { accounts: [ existingAccount || { - address: address, + address: address as Address, ens: null, uns: null, balance: null @@ -173,11 +177,13 @@ export function trackWallet( .get() .wallets.find(wallet => wallet.label === label) try { - sdk.subscribe({ - id: address, - chainId: wallet.chains[0].id, - type: 'account' - }) + if (wallet) { + sdk.subscribe({ + id: address, + chainId: wallet.chains[0]?.id, + type: 'account' + }) + } } catch (error) { // unsupported network for transaction events } @@ -194,6 +200,8 @@ export function trackWallet( const { wallets, chains } = state.get() const primaryWallet = wallets.find(wallet => wallet.label === label) + if (!primaryWallet) return // Add null check for primaryWallet + const { chains: walletChains, accounts } = primaryWallet const [connectedWalletChain] = walletChains @@ -202,13 +210,10 @@ export function trackWallet( ({ namespace, id }) => namespace === 'evm' && id === connectedWalletChain.id ) + if (!chain) return const balanceProm = getBalance(address, chain) - const secondaryTokenBal = updateSecondaryTokens( - primaryWallet, - address, - chain - ) + const secondaryTokenBal = updateSecondaryTokens(address, chain) const account = accounts.find(account => account.address === address) const ensChain = chains.find( @@ -225,7 +230,9 @@ export function trackWallet( const unsProm = account && account.uns ? Promise.resolve(account.uns) - : getUns(address, chain) + : ensChain + ? getUns(address, ensChain) + : Promise.resolve(null) return Promise.all([ Promise.resolve(address), @@ -249,7 +256,9 @@ export function trackWallet( // Update chain on wallet when chainId changed chainChanged$.subscribe(async chainId => { const { wallets } = state.get() - const { chains, accounts } = wallets.find(wallet => wallet.label === label) + const wallet = wallets.find(wallet => wallet.label === label) + if (!wallet) return // Add null check for wallet + const { chains, accounts } = wallet const [connectedWalletChain] = chains if (chainId === connectedWalletChain.id) return @@ -262,6 +271,8 @@ export function trackWallet( .get() .wallets.find(wallet => wallet.label === label) + if (!wallet) return // Add null check for wallet + // Unsubscribe with timeout of 60 seconds // to allow for any currently inflight transactions wallet.accounts.forEach(({ address }) => { @@ -309,21 +320,18 @@ export function trackWallet( switchMap(async chainId => { const { wallets, chains } = state.get() const primaryWallet = wallets.find(wallet => wallet.label === label) - const { accounts } = primaryWallet + const accounts = primaryWallet?.accounts || [] const chain = chains.find( ({ namespace, id }) => namespace === 'evm' && id === chainId ) + if (!chain) return Promise.resolve(null) return Promise.all( accounts.map(async ({ address }) => { const balanceProm = getBalance(address, chain) - const secondaryTokenBal = updateSecondaryTokens( - primaryWallet, - address, - chain - ) + const secondaryTokenBal = updateSecondaryTokens(address, chain) const ensChain = chains.find( ({ id }) => id === validEnsChain(chainId) ) @@ -332,7 +340,7 @@ export function trackWallet( ? getEns(address, ensChain) : Promise.resolve(null) - const unsProm = validEnsChain(chainId) + const unsProm = ensChain ? getUns(address, ensChain) : Promise.resolve(null) @@ -370,29 +378,39 @@ export async function getEns( // chain we don't recognize and don't have a rpcUrl for requests if (!chain) return null - const provider = getProvider(chain) + const provider = await getProvider(chain) + if (!provider) return null try { - const name = await provider.lookupAddress(address) + const name = await provider.getEnsName({ + address + }) let ens = null if (name) { - const resolver = await provider.getResolver(name) - - if (resolver) { - const [contentHash, avatar] = await Promise.all([ - resolver.getContentHash(), - resolver.getAvatar() - ]) + const { labelhash, normalize } = await import('viem/ens') + const normalizedName = normalize(name) - const getText = resolver.getText.bind(resolver) - - ens = { + const ensResolver = await provider.getEnsResolver({ + name: normalizedName + }) + const avatar = await provider.getEnsAvatar({ + name: normalizedName + }) + const contentHash = labelhash(normalizedName) + const getText = async (key: string): Promise => { + return await provider.getEnsText({ name, - avatar, - contentHash, - getText - } + key + }) + } + + ens = { + name, + avatar, + contentHash, + ensResolver, + getText } } @@ -411,7 +429,7 @@ export async function getUns( // check if address is valid ETH address before attempting to resolve // chain we don't recognize and don't have a rpcUrl for requests - if (!unstoppableResolution || !utils.isAddress(address) || !chain) return null + if (!unstoppableResolution || !isAddress(address) || !chain) return null try { return await unstoppableResolution(address) @@ -422,7 +440,7 @@ export async function getUns( } export async function getBalance( - address: string, + address: Address, chain: Chain ): Promise { // chain we don't recognize and don't have a rpcUrl for requests @@ -432,12 +450,15 @@ export async function getBalance( try { const wallet = wallets.find(wallet => !!wallet.provider) + if (!wallet) return null const provider = wallet.provider - const balanceHex = await provider.request({ + const balanceHex = (await provider.request({ method: 'eth_getBalance', params: [address, 'latest'] - }) - return balanceHex ? { [chain.token || 'eth']: weiToEth(balanceHex) } : null + })) as `0x${string}` + return balanceHex + ? { [chain.token || 'eth']: weiHexToEth(balanceHex) } + : null } catch (error) { console.error(error) return null @@ -521,6 +542,7 @@ export async function syncWalletConnectedAccounts( label: WalletState['label'] ): Promise { const wallet = state.get().wallets.find(wallet => wallet.label === label) + if (!wallet) return const permissions = await getPermissions(wallet.provider) const accountsPermissions = permissions.find( ({ parentCapability }) => parentCapability === 'eth_accounts' @@ -540,3 +562,32 @@ export async function syncWalletConnectedAccounts( } } } + +export const addOrSwitchChain = async ( + provider: EIP1193Provider, + chain: Chain +): Promise => { + try { + const { id } = chain + await addNewChain(provider, chain) + await switchChain(provider, id) + return id + } catch (error) { + return undefined + } +} + +export const wagmiProviderMethods = (): { + addOrSwitchChain: ( + provider: EIP1193Provider, + chain: Chain + ) => Promise + getChainId: (provider: EIP1193Provider) => Promise + requestAccounts: (provider: EIP1193Provider) => Promise + switchChain: (provider: EIP1193Provider, chainId: ChainId) => Promise +} => ({ + addOrSwitchChain, + getChainId, + requestAccounts, + switchChain +}) diff --git a/packages/core/src/replacement.ts b/packages/core/src/replacement.ts index 378e452f5..542b70605 100644 --- a/packages/core/src/replacement.ts +++ b/packages/core/src/replacement.ts @@ -1,9 +1,10 @@ import type { EthereumTransactionData, Network } from 'bnc-sdk' -import { BigNumber } from 'ethers' +import { bigIntToHex } from '@web3-onboard/common' import { configuration } from './configuration.js' import { state } from './store/index.js' import type { WalletState } from './types.js' import { gweiToWeiHex, networkToChainId, toHexString } from './utils.js' +import type { GasPrice } from '@web3-onboard/gas' const ACTIONABLE_EVENT_CODES: string[] = ['txPool'] const VALID_GAS_NETWORKS: Network[] = ['main', 'matic-main'] @@ -38,9 +39,14 @@ export async function replaceTransaction({ const chainId = networkToChainId[network] - const { gasPriceProbability } = state.get().notify.replacement - const { gas, apiKey } = configuration + const { gasPriceProbability } = state.get().notify.replacement as { + gasPriceProbability?: + | { speedup?: number | undefined; cancel?: number | undefined } + | undefined + } + const { gas, apiKey } = configuration + if (!gas) return // get gas price const [gasResult] = await gas.get({ chains: [networkToChainId[network]], @@ -49,13 +55,15 @@ export async function replaceTransaction({ }) const { maxFeePerGas, maxPriorityFeePerGas } = - gasResult.blockPrices[0].estimatedPrices.find( + (gasResult.blockPrices[0].estimatedPrices.find( ({ confidence }) => confidence === (type === 'speedup' - ? gasPriceProbability.speedup - : gasPriceProbability.cancel) - ) + ? gasPriceProbability?.speedup + : gasPriceProbability?.cancel) + ) as GasPrice) || {} + + if (!maxFeePerGas || !maxPriorityFeePerGas) return const maxFeePerGasWeiHex = gweiToWeiHex(maxFeePerGas) const maxPriorityFeePerGasWeiHex = gweiToWeiHex(maxPriorityFeePerGas) @@ -71,7 +79,7 @@ export async function replaceTransaction({ from, to: type === 'cancel' ? from : to, chainId: parseInt(chainId), - value: `${BigNumber.from(value).toHexString()}`, + value: bigIntToHex(BigInt(value)), nonce: toHexString(nonce), gasLimit: toHexString(gasLimit), maxFeePerGas: maxFeePerGasWeiHex, diff --git a/packages/core/src/services.ts b/packages/core/src/services.ts index ffc8a19e8..a6fb54a53 100644 --- a/packages/core/src/services.ts +++ b/packages/core/src/services.ts @@ -19,7 +19,7 @@ export async function getBNMulitChainSdk(): Promise { if (!blocknativeMultiChainSdk) { const { default: Blocknative } = await import('bnc-sdk') blocknativeMultiChainSdk = Blocknative.multichain({ - apiKey: configuration.apiKey + apiKey: configuration.apiKey ?? '' }) blocknativeMultiChainSdk.transactions$.subscribe(handleTransactionUpdates) @@ -32,7 +32,7 @@ export async function getBNMulitChainSdk(): Promise { * * @returns SDK if apiKey */ -export async function getBlocknativeSdk(): Promise { +export async function getBlocknativeSdk(): Promise { const { apiKey } = configuration if (!apiKey) return null @@ -40,7 +40,7 @@ export async function getBlocknativeSdk(): Promise { if (!blocknativeSdk) { const { default: Blocknative } = await import('bnc-sdk') blocknativeSdk = new Blocknative({ - dappId: configuration.apiKey, + dappId: configuration.apiKey ?? '', networkId: 1 }) return blocknativeSdk diff --git a/packages/core/src/store/actions.ts b/packages/core/src/store/actions.ts index b813ec2e0..4e1128608 100644 --- a/packages/core/src/store/actions.ts +++ b/packages/core/src/store/actions.ts @@ -1,4 +1,10 @@ -import type { AppMetadata, Chain, WalletInit, WalletModule } from '@web3-onboard/common' +import type { + AppMetadata, + Chain, + WalletHelpers, + WalletInit, + WalletModule +} from '@web3-onboard/common' import { nanoid } from 'nanoid' import { dispatch } from './index.js' import { configuration } from '../configuration.js' @@ -30,7 +36,8 @@ import type { UpdateConnectModalAction, Theme, UpdateChainsAction, - UpdateAppMetadataAction + UpdateAppMetadataAction, + UpdateWagmiConfigAction } from '../types.js' import { @@ -66,8 +73,11 @@ import { UPDATE_ALL_WALLETS, UPDATE_CONNECT_MODAL, UPDATE_CHAINS, - UPDATE_APP_METADATA + UPDATE_APP_METADATA, + UPDATE_WAGMI_CONFIG } from './constants.js' +import type { Address } from 'bnc-sdk' +import type { Config } from '@web3-onboard/wagmi' export function addChains(chains: Chain[]): void { // chains are validated on init @@ -86,15 +96,19 @@ export function addChains(chains: Chain[]): void { export function updateChain(updatedChain: Chain): void { const { - label, - token, - rpcUrl, - id: chainId, + label, + token, + rpcUrl, + id: chainId, namespace: chainNamespace } = updatedChain - const error = validateSetChainOptions( - { label, token, rpcUrl, chainId, chainNamespace } - ) + const error = validateSetChainOptions({ + label, + token, + rpcUrl, + chainId, + chainNamespace + }) if (error) { throw error @@ -184,7 +198,7 @@ export function setPrimaryWallet(wallet: WalletState, address?: string): void { export function updateAccount( id: string, - address: string, + address: Address, update: Partial ): void { const action = { @@ -299,7 +313,11 @@ export function customNotification(updatedNotification: CustomNotification): { } addCustomNotification(notification) - const dismiss = () => removeNotification(notification.id) + const dismiss = () => { + if (notification.id) { + removeNotification(notification.id) + } + } const update = ( notificationUpdate: CustomNotification @@ -406,7 +424,8 @@ export function updateAllWallets(wallets: WalletState[]): void { // ==== HELPERS ==== // export function initializeWalletModules(modules: WalletInit[]): WalletModule[] { - const { device } = configuration + const { device }: WalletHelpers = configuration + if (!device) return [] return modules.reduce((acc, walletInit) => { const initialized = walletInit({ device }) @@ -443,7 +462,7 @@ export function updateTheme(theme: Theme): void { } export function updateAppMetadata( - update: AppMetadata| Partial + update: AppMetadata | Partial ): void { const error = validateAppMetadataUpdate(update) @@ -458,3 +477,15 @@ export function updateAppMetadata( dispatch(action as UpdateAppMetadataAction) } + +export function updateWagmiConfig( + update: Config +): void { + + const action = { + type: UPDATE_WAGMI_CONFIG, + payload: update + } + + dispatch(action as UpdateWagmiConfigAction) +} diff --git a/packages/core/src/store/constants.ts b/packages/core/src/store/constants.ts index 16ac82d9f..af0608d1d 100644 --- a/packages/core/src/store/constants.ts +++ b/packages/core/src/store/constants.ts @@ -14,3 +14,4 @@ export const ADD_NOTIFICATION = 'add_notification' export const REMOVE_NOTIFICATION = 'remove_notification' export const UPDATE_ALL_WALLETS = 'update_balance' export const UPDATE_APP_METADATA = 'update_app_metadata' +export const UPDATE_WAGMI_CONFIG = 'update_wagmi_config' diff --git a/packages/core/src/store/index.ts b/packages/core/src/store/index.ts index 1d39323d6..7a97032cf 100644 --- a/packages/core/src/store/index.ts +++ b/packages/core/src/store/index.ts @@ -20,7 +20,8 @@ import type { UpdateAllWalletsAction, UpdateConnectModalAction, UpdateChainsAction, - UpdateAppMetadataAction + UpdateAppMetadataAction, + UpdateWagmiConfigAction } from '../types.js' import { @@ -39,7 +40,8 @@ import { REMOVE_NOTIFICATION, UPDATE_ALL_WALLETS, UPDATE_CHAINS, - UPDATE_APP_METADATA + UPDATE_APP_METADATA, + UPDATE_WAGMI_CONFIG } from './constants.js' function reducer(state: AppState, action: Action): AppState { @@ -227,11 +229,21 @@ function reducer(state: AppState, action: Action): AppState { ...state, appMetadata: { ...state.appMetadata, - ...update + ...update, + name: update.name || '' } } } + case UPDATE_WAGMI_CONFIG: { + const update = payload as UpdateWagmiConfigAction['payload'] + + return { + ...state, + wagmiConfig: update + } + } + case RESET_STORE: return APP_INITIAL_STATE diff --git a/packages/core/src/themes.ts b/packages/core/src/themes.ts index 6922d7700..480bf3249 100644 --- a/packages/core/src/themes.ts +++ b/packages/core/src/themes.ts @@ -52,7 +52,7 @@ export const handleThemeChange = (update: ThemingMap): void => { Object.keys(update).forEach(targetStyle => { document.documentElement.style.setProperty( targetStyle, - update[targetStyle as keyof ThemingMap] + update[targetStyle as keyof ThemingMap] || null ) }) } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index bdeb047ae..bf46ce048 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -2,13 +2,15 @@ import type { SvelteComponent } from 'svelte' import type { AppMetadata, + Address, Device, WalletInit, EIP1193Provider, WalletModule, Chain, TokenSymbol, - ChainWithDecimalId + ChainWithDecimalId, + DeviceNotBrowser } from '@web3-onboard/common' import type gas from '@web3-onboard/gas' @@ -17,6 +19,10 @@ import type { TransactionPreviewAPI } from '@web3-onboard/transaction-preview' import type en from './i18n/en.json' import type { EthereumTransactionData, Network } from 'bnc-sdk' +import type { GetEnsTextReturnType } from 'viem' +import type { Config, WagmiModuleAPI } from '@web3-onboard/wagmi' +import type wagmi from '@web3-onboard/wagmi' +export type { Config as WagmiConfig } from '@web3-onboard/wagmi' export interface InitOptions { /** @@ -54,6 +60,8 @@ export interface InitOptions { notify?: Partial | Partial /** Gas module */ gas?: typeof gas + /** Wagmi module */ + wagmi?: typeof wagmi /** * Object mapping for W3O components with the key being the DOM * element to mount the component to, this defines the DOM container @@ -155,22 +163,16 @@ export interface SecondaryTokenBalances { export interface Ens { name: string - avatar: Avatar | null - contentHash: string | null - getText: (key: string) => Promise + avatar: string | null + contentHash: Address | null + ensResolver: Address | null + getText: (key: string) => Promise } export interface Uns { name: string } -export type Avatar = { - url: string - linkage: Array<{ type: string; content: string }> -} - -export type Address = string - export interface AppState { chains: Chain[] walletModules: WalletModule[] @@ -180,7 +182,8 @@ export interface AppState { notify: Notify notifications: Notification[] connect: ConnectModalOptions - appMetadata: AppMetadata + appMetadata: AppMetadata | null + wagmiConfig: Config | null } export type Configuration = { @@ -190,6 +193,7 @@ export type Configuration = { appMetadata?: AppMetadata | null apiKey?: string gas?: typeof gas + wagmi?: WagmiModuleAPI containerElements?: ContainerElements transactionPreview?: TransactionPreviewAPI unstoppableResolution?: typeof unstoppableResolution @@ -282,13 +286,13 @@ export type AccountCenter = { */ hideTransactionProtectionBtn?: boolean /** - * Controls the visibility of the 'Enable Transaction Protection' button + * Controls the visibility of the 'Enable Transaction Protection' button * within the expanded Account Center. * - When set to false (default), the button is visible. * - When set to true, the button is hidden. - * This setting can be configured globally for the Account Center, or + * This setting can be configured globally for the Account Center, or * separately for different interfaces like desktop/mobile. - * defaults to + * defaults to * `docs.blocknative.com/blocknative-mev-protection/transaction-boost-alpha` * Use this property to override the default link to give users * more information about transaction protection and the RPC be set @@ -308,13 +312,13 @@ export type AccountCenterOptions = { desktop: Omit mobile: Omit /** - * Controls the visibility of the 'Enable Transaction Protection' button + * Controls the visibility of the 'Enable Transaction Protection' button * within the expanded Account Center. * - When set to false (default), the button is visible. * - When set to true, the button is hidden. - * This setting can be configured globally for the Account Center, or + * This setting can be configured globally for the Account Center, or * separately for different interfaces like desktop/mobile. - * defaults to + * defaults to * `docs.blocknative.com/blocknative-mev-protection/transaction-boost-alpha` * Use this property to override the default link to give users * more information about transaction protection and the RPC be set @@ -467,6 +471,7 @@ export type Action = | UpdateAllWalletsAction | UpdateConnectModalAction | UpdateAppMetadataAction + | UpdateWagmiConfigAction export type AddChainsAction = { type: 'add_chains'; payload: Chain[] } export type UpdateChainsAction = { type: 'update_chains'; payload: Chain } @@ -489,7 +494,7 @@ export type ResetStoreAction = { export type UpdateAccountAction = { type: 'update_account' - payload: { id: string; address: string } & Partial + payload: { id: string; address: Address } & Partial } export type UpdateAccountCenterAction = { @@ -537,6 +542,11 @@ export type UpdateAppMetadataAction = { payload: AppMetadata | Partial } +export type UpdateWagmiConfigAction = { + type: 'update_wagmi_config' + payload: Config +} + // ==== MISC ==== // export type ChainStyle = { icon: string @@ -550,12 +560,6 @@ export type NotifyEventStyles = { iconColor?: string } -export type DeviceNotBrowser = { - type: null - os: null - browser: null -} - export type WalletPermission = { id: string parentCapability: string diff --git a/packages/core/src/update-balances.ts b/packages/core/src/update-balances.ts index 0766829da..17f371225 100644 --- a/packages/core/src/update-balances.ts +++ b/packages/core/src/update-balances.ts @@ -1,20 +1,28 @@ import { state } from './store/index.js' import { getBalance } from './provider.js' import { updateAllWallets } from './store/actions.js' -import { ethers } from 'ethers' -import { AccountAddress, Chain, weiToEth } from '@web3-onboard/common' +import { + type AccountAddress, + type Address, + type Chain, + weiToEth, + chainIdToViemImport +} from '@web3-onboard/common' import type { SecondaryTokenBalances, WalletState } from './types' +import type { ReadContractParameters } from 'viem' +import type { Chain as ViemChain } from 'viem' + async function updateBalances(addresses?: string[]): Promise { const { wallets, chains } = state.get() const updatedWallets = await Promise.all( wallets.map(async wallet => { const chain = chains.find(({ id }) => id === wallet.chains[0].id) + if (!chain) return const updatedAccounts = await Promise.all( wallet.accounts.map(async account => { const secondaryTokens = await updateSecondaryTokens( - wallet, account.address, chain ) @@ -35,55 +43,74 @@ async function updateBalances(addresses?: string[]): Promise { return { ...wallet, accounts: updatedAccounts } }) ) - updateAllWallets(updatedWallets) + updateAllWallets(updatedWallets as WalletState[]) } export const updateSecondaryTokens = async ( - wallet: WalletState, - account: AccountAddress, + accountAddress: AccountAddress, chain: Chain ): Promise => { - if (!chain) return + if (!chain) return [] const chainRPC = chain.rpcUrl if (!chain.secondaryTokens || !chain.secondaryTokens.length || !chainRPC) - return - - const ethersProvider = new ethers.providers.Web3Provider( - wallet.provider, - 'any' - ) - const signer = ethersProvider.getSigner() - - const erc20ABISubset = [ - { - inputs: [{ name: 'owner', type: 'address' }], - name: 'balanceOf', - outputs: [{ name: '', type: 'uint256' }], - stateMutability: 'view', - type: 'function' - }, - { - inputs: [], - name: 'symbol', - outputs: [{ name: '', type: 'string' }], - stateMutability: 'view', - type: 'function' - } - ] + return [] const tokenBalances = await Promise.all( chain.secondaryTokens.map(async token => { try { - const swapContract = new ethers.Contract( - token.address, - erc20ABISubset, - signer - ) - const bigNumBalance = await swapContract.balanceOf(account) - const tokenName = await swapContract.symbol() + const { createPublicClient, http } = await import('viem') + + const viemChain = await chainIdToViemImport(chain) + const client = createPublicClient({ + chain: viemChain as ViemChain, + transport: http( + chain.providerConnectionInfo && chain.providerConnectionInfo.url + ? chain.providerConnectionInfo.url + : (chainRPC as string) + ) + }) + const viemTokenInterface = { + abi: [ + { + inputs: [{ name: 'owner', type: 'address' }], + name: 'balanceOf', + outputs: [{ name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'symbol', + outputs: [{ name: '', type: 'string' }], + stateMutability: 'view', + type: 'function' + } + ], + address: token.address as Address + } + + const supplyProm = + (client as any).readContract({ + ...viemTokenInterface, + functionName: 'balanceOf', + args: [accountAddress] as unknown[] + } as ReadContractParameters) || '' + + const tokenProm = + (client as any).readContract({ + ...viemTokenInterface, + functionName: 'symbol', + args: [] + }) || '' + + const [tokenSupply, tokenName] = await Promise.all([ + supplyProm, + tokenProm + ]) + return { - name: tokenName, - balance: weiToEth(bigNumBalance.toHexString()), + name: tokenName as string, + balance: weiToEth((tokenSupply as number).toString()), icon: token.icon } } catch (error) { @@ -94,7 +121,7 @@ export const updateSecondaryTokens = async ( } }) ) - return tokenBalances + return tokenBalances as SecondaryTokenBalances[] } export default updateBalances diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 39b68b8ed..3f86bc028 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -9,7 +9,8 @@ import type { Chain, WalletInit, WalletModule, - ChainWithDecimalId + ChainWithDecimalId, + DeviceNotBrowser } from '@web3-onboard/common' import { @@ -32,12 +33,8 @@ import { degenIcon } from './icons/index.js' -import type { - ChainStyle, - ConnectedChain, - DeviceNotBrowser, - NotifyEventStyles -} from './types.js' +import type { ChainStyle, ConnectedChain, NotifyEventStyles } from './types.js' +import type { Chain as ViemChain } from 'viem' export function getDevice(): Device | DeviceNotBrowser { if (typeof window !== 'undefined') { @@ -63,23 +60,6 @@ export function getDevice(): Device | DeviceNotBrowser { export const notNullish = (value: T | null | undefined): value is T => value != null -export function validEnsChain(chainId: ChainId): ChainId | null { - // return L2s as Eth for ens resolution - switch (chainId) { - case '0x1': - case '0x89': // Polygon - case '0xa': //Optimism - case '0xa4b1': // Arb One - case '0xa4ba': // Arb Nova - case '0x144': // zksync - return '0x1' - case '0xaa36a7': // Sepolia - return chainId - default: - return null - } -} - export function isSVG(str: string): boolean { return str.includes(' = { '0x27bc86aa': 'Degen' } +export function validEnsChain(chainId: ChainId): string | null { + // return L2s as Eth for ens resolution + switch (chainId) { + case '0x1': + case '0x89': // Polygon + case '0xa': //Optimism + case '0xa4b1': // Arb + case '0x144': // zksync + return '0x1' + case '0x5': // Goerli + return chainId + case '0xaa36a7': // Sepolia + return chainId + default: + return null + } +} + +export const chainIdToViemENSImport = async ( + chainId: string +): Promise => { + switch (chainId) { + case '0x89': + case '0xa': + case '0xa4b1': + case '0x144': + case '0x1': { + const { mainnet } = await import('viem/chains') + return mainnet + } + case '0xaa36a7': { + const { sepolia } = await import('viem/chains') + return sepolia + } + default: + return null + } +} + export const networkToChainId: Record = { main: '0x1', sepolia: '0xaa36a7', diff --git a/packages/core/src/validation.ts b/packages/core/src/validation.ts index d1b39cd2c..400d0eaab 100644 --- a/packages/core/src/validation.ts +++ b/packages/core/src/validation.ts @@ -6,11 +6,11 @@ import { type WalletInit, type WalletModule, type ValidateReturn, + type AppMetadata, chainNamespaceValidation, chainIdValidation, chainValidation, - validate, - AppMetadata + validate } from '@web3-onboard/common' import type { @@ -245,6 +245,7 @@ const initOptions = Joi.object({ get: Joi.function().required(), stream: Joi.function().required() }), + wagmi: Joi.function(), connect: connectModalOptions, containerElements: containerElements, transactionPreview: Joi.object({ diff --git a/packages/core/src/views/Index.svelte b/packages/core/src/views/Index.svelte index 2cbe05448..ae1c81818 100644 --- a/packages/core/src/views/Index.svelte +++ b/packages/core/src/views/Index.svelte @@ -238,6 +238,7 @@ :global(input[type='checkbox']) { -webkit-appearance: none; + appearance: none; width: auto; background: var(--onboard-white, var(--white)); outline: 1px solid var(--onboard-gray-300, var(--gray-300)); diff --git a/packages/core/src/views/account-center/AccountCenterPanel.svelte b/packages/core/src/views/account-center/AccountCenterPanel.svelte index 1342cb45d..7ca9cdf00 100644 --- a/packages/core/src/views/account-center/AccountCenterPanel.svelte +++ b/packages/core/src/views/account-center/AccountCenterPanel.svelte @@ -76,7 +76,7 @@ await updateChainRPC( primaryWallet.provider, validAppChain, - validAppChain?.protectedRpcUrl || BN_BOOST_RPC_URL + validAppChain.protectedRpcUrl || BN_BOOST_RPC_URL ) enableTransactionProtection = false } catch (error) { @@ -461,7 +461,7 @@ - {#if !$accountCenter$.hideTransactionProtectionBtn && (primaryWalletOnMainnet || validAppChain?.protectedRpcUrl)} + {#if !$accountCenter$.hideTransactionProtectionBtn && (primaryWalletOnMainnet || validAppChain.protectedRpcUrl)}
(enableTransactionProtection = true)} class="protect action-container flex items-center pointer" diff --git a/packages/core/src/views/connect/Index.svelte b/packages/core/src/views/connect/Index.svelte index 553c0cc38..f89d099fc 100644 --- a/packages/core/src/views/connect/Index.svelte +++ b/packages/core/src/views/connect/Index.svelte @@ -1,13 +1,19 @@