diff --git a/.circleci/config.yml b/.circleci/config.yml index fb0f777e1..d27de5af3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,7 +23,31 @@ aliases: - &deploy_staging_filters filters: branches: - only: develop + only: enhancement/replace_ethers + # only: develop + + - &deploy_testing_filters + filters: + branches: + only: enhancement/replace_ethers + + + - &test_package_version_for_test_tag_break_false + run: + name: Tagged as viem version if True Continue Flow + command: | + PACKAGE_VERSION=$(cat package.json \ + | grep version \ + | head -1 \ + | awk -F: '{ print $2 }' \ + | sed 's/[",]//g'); + if [[ $PACKAGE_VERSION =~ "-viem" ]]; + then + echo true + else + echo false + circleci step halt + fi; - &test_package_version_for_alpha_tag_break_false run: @@ -140,6 +164,30 @@ commands: - *publish-npm - save-build-flag + # node-staging-build-steps: + # steps: + # - checkout: + # # Since our working_directory is deeper than the root, + # # remind the job where it needs to checkout. + # path: ~/web3-onboard-monorepo + # - *restore-build-flag + # - *test-build-flag + # - *test_package_version_for_alpha_tag_break_false + # # Services and packages in a Workspace don't get their own + # # yarn.lock so we need to generate them manually. + # - *generate-lock-file + # - *restore-cache + # - run: + # name: Build + # command: | + # yarn + # yarn type-check + # yarn build + # - *save-cache + # - *create-npm-config + # - *publish-npm-tag-as-next + # - save-build-flag + node-staging-build-steps: steps: - checkout: @@ -148,7 +196,8 @@ commands: path: ~/web3-onboard-monorepo - *restore-build-flag - *test-build-flag - - *test_package_version_for_alpha_tag_break_false + # Tag must be defined for testing publish outside of Alpha or standard prod publish + - *test_package_version_for_test_tag_break_false # Services and packages in a Workspace don't get their own # yarn.lock so we need to generate them manually. - *generate-lock-file @@ -167,7 +216,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 +324,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 +342,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 +354,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 +390,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 +444,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 +456,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 +481,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 @@ -452,7 +501,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 @@ -560,7 +609,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 @@ -578,7 +627,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 @@ -590,7 +639,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 @@ -626,7 +675,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 @@ -680,7 +729,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 @@ -692,7 +741,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 @@ -716,7 +765,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 @@ -733,6 +782,26 @@ jobs: steps: - node-staging-build-steps + # # Build testing releases - tag must be defined outside of Alpha tag flow + # build-testing-common: + # docker: + # - image: cimg/node:16.13.1 + # working_directory: ~/web3-onboard-monorepo/packages/common + # steps: + # - node-testing-build-steps + # build-testing-core: + # docker: + # - image: cimg/node:16.15.1 + # working_directory: ~/web3-onboard-monorepo/packages/core + # steps: + # - node-testing-build-steps + # build-testing-injected: + # docker: + # - image: cimg/node:16.13.1 + # working_directory: ~/web3-onboard-monorepo/packages/injected + # steps: + # - node-testing-build-steps + workflows: version: 2 common: @@ -741,6 +810,7 @@ workflows: <<: *deploy_production_filters - build-staging-common: <<: *deploy_staging_filters + core: jobs: - build-core: 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..3f92d3a28 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-viem.1", "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-viem.4" }, "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..7852cc602 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-viem.1", "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-viem.4" } } diff --git a/packages/bitkeep/package.json b/packages/bitkeep/package.json index 0341e99aa..42ab78fdc 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-viem.1", "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-viem.4" } } diff --git a/packages/blocto/package.json b/packages/blocto/package.json index 17d2fdeb0..1f028dfcf 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-viem.1", "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-viem.4", "@blocto/sdk": "^0.9.1" } } diff --git a/packages/capsule/package.json b/packages/capsule/package.json index ed8fee2b7..703ec08f6 100644 --- a/packages/capsule/package.json +++ b/packages/capsule/package.json @@ -1,6 +1,6 @@ { "name": "@web3-onboard/capsule", - "version": "2.0.3-alpha.1", + "version": "2.0.3-viem.1", "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,7 +60,7 @@ "@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-viem.4", "react-dom": "^18.2.0", "viem": "^2.9.15", "wagmi": "^2.5.19" @@ -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 1db19ff3e..fa2dd2c55 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..6a2e3e9ab 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-viem.1", "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-viem.4" } } diff --git a/packages/cede-store/src/index.ts b/packages/cede-store/src/index.ts index 9ff9e908c..5bfc017b9 100644 --- a/packages/cede-store/src/index.ts +++ b/packages/cede-store/src/index.ts @@ -1,5 +1,5 @@ import { CedeProvider, detectCedeProvider } from '@cedelabs/providers' -import type { WalletInit } from '@web3-onboard/common' +import type { ProviderAccounts, WalletInit } from '@web3-onboard/common' import { createEIP1193Provider } from '@web3-onboard/common' type CustomWindow = typeof window & { @@ -38,7 +38,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..6c3cbb2cf 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-viem.1", "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-viem.4" } } diff --git a/packages/common/package.json b/packages/common/package.json index 66212267f..68eaf7906 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-viem.4", "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.10.5" } } diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 5e46b5eb8..b63d24022 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,7 +1,13 @@ 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 +} 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..922a034b1 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -1,5 +1,31 @@ -import Bignumber from 'bignumber.js' +import { formatEther, hexToBigInt, numberToHex, parseEther } from 'viem' +import type { Address } from './types.js' -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 = BigInt(parseInt(wei)) + 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) } 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..400d9a1fe 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -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/package.json b/packages/core/package.json index 5d949e8fb..f55442c3b 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-viem.5", "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,31 @@ "@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/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-viem.4", "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 +96,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.10.5" + }, + "engines": { + "node": ">=16.15.1" } } diff --git a/packages/core/rollup.config.js b/packages/core/rollup.config.js index 3f99e49b5..1a28b7675 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,7 +36,8 @@ export default { }), resolve({ browser: true, - dedupe: ['svelte'] + dedupe: ['svelte'], + extensions: ['.js', '.ts', '.svelte'] }), typescript({ sourceMap: !production, @@ -38,11 +46,10 @@ export default { copy({ src: 'src/i18n/en.json', dest: 'i18n' - }) + }), ], external: [ '@web3-onboard/common', - 'ethers', 'bowser', 'joi', 'rxjs', @@ -52,9 +59,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..d828ad69c 100644 --- a/packages/core/src/configuration.ts +++ b/packages/core/src/configuration.ts @@ -3,13 +3,13 @@ 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 } export function updateConfiguration(update: Partial): void { diff --git a/packages/core/src/disconnect.ts b/packages/core/src/disconnect.ts index 8c3680c4e..038bbbd36 100644 --- a/packages/core/src/disconnect.ts +++ b/packages/core/src/disconnect.ts @@ -22,20 +22,22 @@ 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 + }) }) - }) + } } } 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..7167fc12e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -121,8 +121,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 +147,9 @@ function init(options: InitOptions): OnboardAPI { ...accountCenter.desktop } } - updateAccountCenter(accountCenterUpdate) + if (typeof accountCenterUpdate !== 'undefined') { + updateAccountCenter(accountCenterUpdate) + } } // update notify @@ -158,7 +162,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 +173,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 +183,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 +218,8 @@ function init(options: InitOptions): OnboardAPI { updateNotify(notifyUpdate) } - const app = svelteInstance || mountApp(theme, disableFontDownload) + const app = + svelteInstance || mountApp(theme || {}, disableFontDownload || false) updateConfiguration({ svelteInstance: app, @@ -225,9 +234,11 @@ function init(options: InitOptions): OnboardAPI { 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 +261,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 +464,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..71022fa71 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({ 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' 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..175b624cf 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' @@ -68,6 +74,7 @@ import { UPDATE_CHAINS, UPDATE_APP_METADATA } from './constants.js' +import type { Address } from 'bnc-sdk' export function addChains(chains: Chain[]): void { // chains are validated on init @@ -86,15 +93,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 +195,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 +310,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 +421,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 +459,7 @@ export function updateTheme(theme: Theme): void { } export function updateAppMetadata( - update: AppMetadata| Partial + update: AppMetadata | Partial ): void { const error = validateAppMetadataUpdate(update) diff --git a/packages/core/src/store/index.ts b/packages/core/src/store/index.ts index 1d39323d6..5bbab3120 100644 --- a/packages/core/src/store/index.ts +++ b/packages/core/src/store/index.ts @@ -227,7 +227,8 @@ function reducer(state: AppState, action: Action): AppState { ...state, appMetadata: { ...state.appMetadata, - ...update + ...update, + name: update.name || '' } } } 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..c956b4330 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,7 @@ 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' export interface InitOptions { /** @@ -155,22 +158,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 +177,7 @@ export interface AppState { notify: Notify notifications: Notification[] connect: ConnectModalOptions - appMetadata: AppMetadata + appMetadata: AppMetadata | null } export type Configuration = { @@ -282,13 +279,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 +305,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 @@ -489,7 +486,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 = { @@ -550,12 +547,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..efea19995 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 +} from '@web3-onboard/common' import type { SecondaryTokenBalances, WalletState } from './types' +import type { ReadContractParameters } from 'viem' +import type { Chain as ViemChain } from 'viem' +import { chainIdToViemImport } from './utils.js' + 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..9aa9df1ca 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 { @@ -35,9 +36,9 @@ import { import type { ChainStyle, ConnectedChain, - DeviceNotBrowser, NotifyEventStyles } from './types.js' +import type { Chain as ViemChain } from 'viem' export function getDevice(): Device | DeviceNotBrowser { if (typeof window !== 'undefined') { @@ -63,23 +64,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 chainIdToViemImport = async ( + w3oChain: Chain +): Promise => { + const { id, label, token, publicRpcUrl, blockExplorerUrl, rpcUrl } = w3oChain + switch (id) { + case '0x89': { + const { polygon } = await import('viem/chains') + return polygon + } + case '0xa': { + const { optimism } = await import('viem/chains') + return optimism + } + case '0xa4b1': { + const { arbitrum } = await import('viem/chains') + return arbitrum + } + case '0x144': { + const { zkSync } = await import('viem/chains') + return zkSync + } + case '0x38': { + const { bsc } = await import('viem/chains') + return bsc + } + case '0x1': { + const { mainnet } = await import('viem/chains') + return mainnet + } + case '0xaa36a7': { + const { sepolia } = await import('viem/chains') + return sepolia + } + case '0xfa': { + const { fantom } = await import('viem/chains') + return fantom + } + case '0xa86a': { + const { avalanche } = await import('viem/chains') + return avalanche + } + case '0xa4ec': { + const { celo } = await import('viem/chains') + return celo + } + case '0x2105': { + const { base } = await import('viem/chains') + return base + } + case '0x14a33': { + const { baseGoerli } = await import('viem/chains') + return baseGoerli + } + case '0x64': { + const { gnosis } = await import('viem/chains') + return gnosis + } + case '0x63564C40': { + const { harmonyOne } = await import('viem/chains') + return harmonyOne + } + case '0x27bc86aa': { + const { degen } = await import('viem/chains') + return degen + } + default: { + const { defineChain } = await import('viem') + return defineChain({ + id: parseInt(id, 16), + name: label, + nativeCurrency: { + decimals: 18, + name: token, + symbol: token + }, + rpcUrls: { + default: { + http: [rpcUrl, publicRpcUrl] + } + }, + blockExplorers: { + default: { name: 'Explorer', url: blockExplorerUrl } + } + }) + } + } +} + export const networkToChainId: Record = { main: '0x1', sepolia: '0xaa36a7', diff --git a/packages/core/src/validation.ts b/packages/core/src/validation.ts index d1b39cd2c..c1d7a3732 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 { 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..93de3c4f3 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) { diff --git a/packages/core/src/views/connect/Index.svelte b/packages/core/src/views/connect/Index.svelte index 553c0cc38..5e1289671 100644 --- a/packages/core/src/views/connect/Index.svelte +++ b/packages/core/src/views/connect/Index.svelte @@ -1,7 +1,6 @@