diff --git a/package.json b/package.json index c5deb5dbdfd..261125c5a25 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "hotlink-revert": "node ./scripts/hotlink/hotlink-revert.mjs", "explain:major": "node ./scripts/changeset-explain-major.mjs" }, - "packageManager": "pnpm@8.6.12", + "packageManager": "pnpm@8.9.0", "dependencies": { "@changesets/changelog-github": "^0.4.8", "@changesets/cli": "^2.26.2", diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md index e63c7af2337..e9bff96a12c 100644 --- a/packages/auth/CHANGELOG.md +++ b/packages/auth/CHANGELOG.md @@ -1,5 +1,26 @@ # @thirdweb-dev/auth +## 3.2.48 + +### Patch Changes + +- Updated dependencies []: + - @thirdweb-dev/wallets@1.3.5 + +## 3.2.47 + +### Patch Changes + +- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101)]: + - @thirdweb-dev/wallets@1.3.4 + +## 3.2.46 + +### Patch Changes + +- Updated dependencies [[`6d3d76cf`](https://github.com/thirdweb-dev/js/commit/6d3d76cff8018015faa191a1f8bd4f34506a6650), [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9)]: + - @thirdweb-dev/wallets@1.3.3 + ## 3.2.45 ### Patch Changes diff --git a/packages/auth/package.json b/packages/auth/package.json index 9391343dcd3..a5c5883e53b 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/auth", - "version": "3.2.45", + "version": "3.2.48", "main": "dist/thirdweb-dev-auth.cjs.js", "module": "dist/thirdweb-dev-auth.esm.js", "browser": { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index b5766440935..48b44733664 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,33 @@ # thirdweb +## 0.12.10 + +### Patch Changes + +- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]: + - @thirdweb-dev/sdk@3.10.67 + - @thirdweb-dev/wallets@1.3.5 + - @thirdweb-dev/auth@3.2.48 + +## 0.12.9 + +### Patch Changes + +- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c)]: + - @thirdweb-dev/wallets@1.3.4 + - @thirdweb-dev/sdk@3.10.66 + - @thirdweb-dev/auth@3.2.47 + +## 0.12.8 + +### Patch Changes + +- Updated dependencies [[`6d3d76cf`](https://github.com/thirdweb-dev/js/commit/6d3d76cff8018015faa191a1f8bd4f34506a6650), [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9), [`15fe4779`](https://github.com/thirdweb-dev/js/commit/15fe4779f4b99e51afe214ac3ebb31f611089787)]: + - @thirdweb-dev/wallets@1.3.3 + - @thirdweb-dev/storage@1.2.11 + - @thirdweb-dev/sdk@3.10.65 + - @thirdweb-dev/auth@3.2.46 + ## 0.12.7 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index b64403dd8cc..244f81539e6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "thirdweb", "main": "dist/cli/index.js", - "version": "0.12.7", + "version": "0.12.10", "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli", "author": "thirdweb eng ", "license": "Apache-2.0", diff --git a/packages/contracts-js/package.json b/packages/contracts-js/package.json index ca0fbd53bb2..f8c1c819bc9 100644 --- a/packages/contracts-js/package.json +++ b/packages/contracts-js/package.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js", "dependencies": { - "@thirdweb-dev/contracts": "3.10.0" + "@thirdweb-dev/contracts": "3.10.1-0" }, "devDependencies": { "@babel/preset-env": "^7.22.9", diff --git a/packages/react-core/CHANGELOG.md b/packages/react-core/CHANGELOG.md index 19b40374461..17eac06e7c8 100644 --- a/packages/react-core/CHANGELOG.md +++ b/packages/react-core/CHANGELOG.md @@ -1,5 +1,75 @@ # @thirdweb-dev/react-core +## 3.16.5 + +### Patch Changes + +- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]: + - @thirdweb-dev/sdk@3.10.67 + - @thirdweb-dev/wallets@1.3.5 + - @thirdweb-dev/auth@3.2.48 + +## 3.16.4 + +### Patch Changes + +- [#1719](https://github.com/thirdweb-dev/js/pull/1719) [`93127047`](https://github.com/thirdweb-dev/js/commit/931270479ef227556a1077357a8c000b08de6e8d) Thanks [@MananTank](https://github.com/MananTank)! - Type refactor for useWallet hook + +- [#1716](https://github.com/thirdweb-dev/js/pull/1716) [`d3c8626a`](https://github.com/thirdweb-dev/js/commit/d3c8626a5a8def882c1592b236048ebe88e85d49) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Better `useWallet()` API to obtain specific wallet instances. + + ``` + const smartWallet = useWallet("smartWallet"); // returns a SmartWallet instance + const embeddedWallet = useWallet("embeddedWallet"); // returns a EmbeddedWallet instance + ``` + +- [#1712](https://github.com/thirdweb-dev/js/pull/1712) [`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add `useCreateSessionKey` and `useRevokeSessionKey` hooks + + ``` + const Component = () => { + const { + mutate: createSessionKey, + isLoading, + error, + } = useCreateSessionKey(); + + if (error) { + console.error("failed to create session key", error); + } + + return ( + + ); + }; + ``` + +- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c)]: + - @thirdweb-dev/wallets@1.3.4 + - @thirdweb-dev/sdk@3.10.66 + - @thirdweb-dev/auth@3.2.47 + +## 3.16.3 + +### Patch Changes + +- Updated dependencies [[`6d3d76cf`](https://github.com/thirdweb-dev/js/commit/6d3d76cff8018015faa191a1f8bd4f34506a6650), [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9), [`15fe4779`](https://github.com/thirdweb-dev/js/commit/15fe4779f4b99e51afe214ac3ebb31f611089787)]: + - @thirdweb-dev/wallets@1.3.3 + - @thirdweb-dev/storage@1.2.11 + - @thirdweb-dev/sdk@3.10.65 + - @thirdweb-dev/auth@3.2.46 + ## 3.16.2 ### Patch Changes diff --git a/packages/react-core/package.json b/packages/react-core/package.json index 4efd3ddcf66..cde71e8ea7c 100644 --- a/packages/react-core/package.json +++ b/packages/react-core/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/react-core", - "version": "3.16.2", + "version": "3.16.5", "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core", "author": "thirdweb eng ", "license": "Apache-2.0", diff --git a/packages/react-core/src/core/hooks/wallet-hooks.ts b/packages/react-core/src/core/hooks/wallet-hooks.ts index 2f9e61b7452..a783e39cc08 100644 --- a/packages/react-core/src/core/hooks/wallet-hooks.ts +++ b/packages/react-core/src/core/hooks/wallet-hooks.ts @@ -1,16 +1,74 @@ import { useWalletContext } from "../providers/thirdweb-wallet-provider"; import invariant from "tiny-invariant"; +import type { + BloctoWallet, + CoinbaseWallet, + EmbeddedWallet, + FrameWallet, + LocalWallet, + MagicLink, + MetaMaskWallet, + PaperWallet, + PhantomWallet, + RainbowWallet, + SafeWallet, + SmartWallet, + TrustWallet, + WalletConnect, + walletIds, +} from "@thirdweb-dev/wallets"; +import { WalletInstance } from "../types/wallet"; + +export type WalletId = (typeof walletIds)[keyof typeof walletIds]; + +type WalletIdToWalletTypeMap = { + metamask: MetaMaskWallet; + coinbase: CoinbaseWallet; + rainbowWallet: RainbowWallet; + blocto: BloctoWallet; + frame: FrameWallet; + localWallet: LocalWallet; + magicLink: MagicLink; + paper: PaperWallet; + smartWallet: SmartWallet; + safe: SafeWallet; + trust: TrustWallet; + embeddedWallet: EmbeddedWallet; + walletConnect: WalletConnect; + phantom: PhantomWallet; + walletConnectV1: WalletConnect; +}; /** * @returns the current active wallet instance */ -export function useWallet() { +export function useWallet( + walletId: T, +): WalletIdToWalletTypeMap[T] | undefined; +export function useWallet(): WalletInstance | undefined; +export function useWallet(walletId?: T) { const context = useWalletContext(); invariant( context, "useWallet() hook must be used within a ", ); - return context.activeWallet; + + const activeWallet = context.activeWallet; + + if (!activeWallet) { + return undefined; + } + + // if walletId is provided, return the wallet instance only if it matches the walletId + if (walletId) { + if (activeWallet.walletId === walletId) { + return activeWallet as WalletIdToWalletTypeMap[T]; + } else { + return undefined; + } + } + + return activeWallet; } /** diff --git a/packages/react-core/src/evm/hooks/async/account.ts b/packages/react-core/src/evm/hooks/async/account.ts index 46028d5025a..892d6f3156c 100644 --- a/packages/react-core/src/evm/hooks/async/account.ts +++ b/packages/react-core/src/evm/hooks/async/account.ts @@ -1,27 +1,23 @@ -import type { providers } from "ethers"; import { RequiredParam, requiredParamInvariant, } from "../../../core/query-utils/required-param"; -import { useSDKChainId } from "../useSDK"; -import { - cacheKeys, - invalidateContractAndBalances, -} from "../../utils/cache-keys"; +import { cacheKeys } from "../../utils/cache-keys"; import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork"; import { useMutation, UseMutationResult, - useQueryClient, UseQueryResult, } from "@tanstack/react-query"; import type { SignerWithPermissions, - PermissionSnapshotInput, SmartContract, + SignerPermissionsInput, + TransactionResult, } from "@thirdweb-dev/sdk"; import invariant from "tiny-invariant"; import { WalletAddress } from "../../types"; +import { useWallet } from "../../../core/hooks/wallet-hooks"; /** **********************/ /** READ HOOKS **/ @@ -127,65 +123,180 @@ export function useAccountAdminsAndSigners( /** **********************/ /** WRITE HOOKS **/ /** **********************/ + +export type CreateSessionKeyInput = { + keyAddress: string; + permissions: SignerPermissionsInput; +}; + /** - * Set the account's entire snapshot of permissions + * Create and add a session key for the smart wallet * * @example * ```jsx * const Component = () => { - * const { contract } = useContract("{{contract_address}}"); * const { - * mutate: setAccountSigners, + * mutate: createSessionKey, * isLoading, * error, - * } = useSetAccountSigners(contract); + * } = useCreateSessionKey(); * * if (error) { - * console.error("failed to set account signers", error); + * console.error("failed to create session key", error); * } * * return ( * * ); * }; * ``` * - * @param contract - an instance of a account contract - * @returns a mutation object that can be used to set the account signers * @twfeature Account - * @see {@link https://portal.thirdweb.com/react/react.usesetaccountsigners?utm_source=sdk | Documentation} - * @beta */ -export function useSetAccountSigners( - contract: RequiredParam, -): UseMutationResult< - { receipt: providers.TransactionReceipt }, +export function useCreateSessionKey(): UseMutationResult< + TransactionResult, unknown, - PermissionSnapshotInput, - unknown + CreateSessionKeyInput > { - const activeChainId = useSDKChainId(); - const contractAddress = contract?.getAddress(); - const queryClient = useQueryClient(); + const smartWallet = useWallet("smartWallet"); + return useMutation(async (args: CreateSessionKeyInput) => { + requiredParamInvariant(smartWallet, "wallet is not connected"); + return smartWallet.createSessionKey(args.keyAddress, args.permissions); + }); +} - return useMutation( - async (permissionsSnapshot: PermissionSnapshotInput) => { - requiredParamInvariant(contract, "contract is undefined"); +/** + * Revoke a session key on the smart wallet + * + * @example + * ```jsx + * const Component = () => { + * const { + * mutate: revokeSessionKey, + * isLoading, + * error, + * } = useRevokeSessionKey(); + * + * if (error) { + * console.error("failed to revoke session key", error); + * } + * + * return ( + * + * ); + * }; + * ``` + * + * @twfeature Account + */ +export function useRevokeSessionKey(): UseMutationResult< + TransactionResult, + unknown, + string +> { + const smartWallet = useWallet("smartWallet"); + return useMutation(async (keyAddress: string) => { + requiredParamInvariant(smartWallet, "wallet is not connected"); + return smartWallet.revokeSessionKey(keyAddress); + }); +} - return contract.account.resetAllPermissions(permissionsSnapshot); - }, - { - onSettled: () => - invalidateContractAndBalances( - queryClient, - contractAddress, - activeChainId, - ), - }, - ); +/** + * Add an additional admin on the smart wallet + * + * @example + * ```jsx + * const Component = () => { + * const { + * mutate: addAdmin, + * isLoading, + * error, + * } = useAddAdmin(); + * + * if (error) { + * console.error("failed to add admin", error); + * } + * + * return ( + * + * ); + * }; + * ``` + * + * @twfeature Account + */ +export function useAddAdmin(): UseMutationResult< + TransactionResult, + unknown, + string +> { + const smartWallet = useWallet("smartWallet"); + return useMutation(async (adminAddress: string) => { + requiredParamInvariant(smartWallet, "wallet is not connected"); + return smartWallet.addAdmin(adminAddress); + }); +} + +/** + * Add an additional admin on the smart wallet + * + * @example + * ```jsx + * const Component = () => { + * const { + * mutate: removeAdmin, + * isLoading, + * error, + * } = useRemoveAdmin(); + * + * if (error) { + * console.error("failed to remove admin", error); + * } + * + * return ( + * + * ); + * }; + * ``` + * + * @twfeature Account + */ +export function useRemoveAdmin(): UseMutationResult< + TransactionResult, + unknown, + string +> { + const smartWallet = useWallet("smartWallet"); + return useMutation(async (adminAddress: string) => { + requiredParamInvariant(smartWallet, "wallet is not connected"); + return smartWallet.removeAdmin(adminAddress); + }); } diff --git a/packages/react-core/src/evm/index.ts b/packages/react-core/src/evm/index.ts index e751f4fd81e..a8bc82cb693 100644 --- a/packages/react-core/src/evm/index.ts +++ b/packages/react-core/src/evm/index.ts @@ -205,7 +205,10 @@ export { // account export { useAccountSigners, - useSetAccountSigners, + useAddAdmin, + useRemoveAdmin, + useCreateSessionKey, + useRevokeSessionKey, useAccountAdmins, useAccountAdminsAndSigners, } from "./hooks/async/account"; diff --git a/packages/react-native-compat/CHANGELOG.md b/packages/react-native-compat/CHANGELOG.md index a77c1181a94..993020d4646 100644 --- a/packages/react-native-compat/CHANGELOG.md +++ b/packages/react-native-compat/CHANGELOG.md @@ -1,5 +1,11 @@ # @thirdweb-dev/react-native-compat +## 0.3.4 + +## 0.3.3 + +## 0.3.2 + ## 0.3.1 ## 0.3.0 diff --git a/packages/react-native-compat/package.json b/packages/react-native-compat/package.json index 27c4e5c3f96..aad2f1a0fcb 100644 --- a/packages/react-native-compat/package.json +++ b/packages/react-native-compat/package.json @@ -1,7 +1,7 @@ { "name": "@thirdweb-dev/react-native-compat", "description": "Shims for Thirdweb in React Native Projects", - "version": "0.3.1", + "version": "0.3.4", "author": "thirdweb eng ", "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat", "license": "Apache-2.0", diff --git a/packages/react-native/CHANGELOG.md b/packages/react-native/CHANGELOG.md index 56e6261cae5..a5139de7d73 100644 --- a/packages/react-native/CHANGELOG.md +++ b/packages/react-native/CHANGELOG.md @@ -1,5 +1,37 @@ # @thirdweb-dev/react-native +## 0.3.4 + +### Patch Changes + +- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]: + - @thirdweb-dev/sdk@3.10.67 + - @thirdweb-dev/react-core@3.16.5 + - @thirdweb-dev/wallets@1.3.5 + +## 0.3.3 + +### Patch Changes + +- [#1715](https://github.com/thirdweb-dev/js/pull/1715) [`f5caf6a3`](https://github.com/thirdweb-dev/js/commit/f5caf6a3502905666ae57f3304bb9074e09c9366) Thanks [@iketw](https://github.com/iketw)! - Fix encryption/decryption bug in embeddedWallet for React Native + +- [#1709](https://github.com/thirdweb-dev/js/pull/1709) [`6f4c5cd7`](https://github.com/thirdweb-dev/js/commit/6f4c5cd746caf07f057e272aec9f204ba5905cd3) Thanks [@ElasticBottle](https://github.com/ElasticBottle)! - feat(react-native): update iteration count for embedded-wallet for better speed and future updates + +- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`93127047`](https://github.com/thirdweb-dev/js/commit/931270479ef227556a1077357a8c000b08de6e8d), [`d3c8626a`](https://github.com/thirdweb-dev/js/commit/d3c8626a5a8def882c1592b236048ebe88e85d49), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c), [`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101)]: + - @thirdweb-dev/wallets@1.3.4 + - @thirdweb-dev/react-core@3.16.4 + - @thirdweb-dev/sdk@3.10.66 + +## 0.3.2 + +### Patch Changes + +- Updated dependencies [[`6d3d76cf`](https://github.com/thirdweb-dev/js/commit/6d3d76cff8018015faa191a1f8bd4f34506a6650), [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9), [`15fe4779`](https://github.com/thirdweb-dev/js/commit/15fe4779f4b99e51afe214ac3ebb31f611089787)]: + - @thirdweb-dev/wallets@1.3.3 + - @thirdweb-dev/storage@1.2.11 + - @thirdweb-dev/sdk@3.10.65 + - @thirdweb-dev/react-core@3.16.3 + ## 0.3.1 ### Patch Changes diff --git a/packages/react-native/package.json b/packages/react-native/package.json index cfa105664fc..cfa36b4f091 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/react-native", - "version": "0.3.1", + "version": "0.3.4", "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native", "author": "thirdweb eng ", "license": "Apache-2.0", diff --git a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/wallet/encryption.ts b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/wallet/encryption.ts index f75b6aa8f47..3feb5e7d820 100644 --- a/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/wallet/encryption.ts +++ b/packages/react-native/src/evm/wallets/connectors/embedded-wallet/embedded/helpers/wallet/encryption.ts @@ -1,19 +1,21 @@ import AesGcmCrypto from "react-native-aes-gcm-crypto"; -import { getRandomValues } from "../getRandomValues"; import QuickCrypto from "react-native-quick-crypto"; +import { getRandomValues } from "../getRandomValues"; const ENCRYPTION_SEPARATOR = ":"; -const KEY_ITERATION_COUNT = 5000000; +const DEPRECATED_KEY_ITERATION_COUNT = 5000000; +const CURRENT_KEY_ITERATION_COUNT = 650000; const KEY_LENGTH = 256; export async function getEncryptionKey( pwd: string, salt: ArrayBuffer, + iterationCounts: number, ): Promise { const key = QuickCrypto.pbkdf2Sync( pwd, salt, - KEY_ITERATION_COUNT, + iterationCounts, KEY_LENGTH, "sha256", ); @@ -40,7 +42,9 @@ export async function encryptShareWeb( pwd: string, ): Promise { const salt = getRandomValues(new Uint8Array(16)); - const keyBase64 = await getEncryptionKey(pwd, salt); + const iterationCount = CURRENT_KEY_ITERATION_COUNT; + + const keyBase64 = await getEncryptionKey(pwd, salt, iterationCount); let encryptedValue; try { @@ -49,11 +53,23 @@ export async function encryptShareWeb( throw new Error(`Error encrypting share: ${error}`); } - const returnValue = `${encryptedValue.content}${ENCRYPTION_SEPARATOR}${ - encryptedValue.iv - }${ENCRYPTION_SEPARATOR}${bufferToBase64(salt)}${ENCRYPTION_SEPARATOR}${ - encryptedValue.tag - }`; + // Ref: https://github.com/craftzdog/react-native-aes-gcm-crypto/blob/master/android/src/main/java/com/reactnativeaesgcmcrypto/AesGcmCryptoModule.kt#L111 + // Cipher text is a base64 string + const cipherTextBase64Buffer = Buffer.from(encryptedValue.content, "base64"); + // Tag is a hex string + const tagHexBuffer = Buffer.from(encryptedValue.tag, "hex"); + const cipherTextWithTag = Buffer.concat([ + cipherTextBase64Buffer, + tagHexBuffer, + ]); + // iv is a hex string + const ivBase64 = Buffer.from(encryptedValue.iv, "hex").toString("base64"); + + const returnValue = `${bufferToBase64( + cipherTextWithTag, + )}${ENCRYPTION_SEPARATOR}${ivBase64}${ENCRYPTION_SEPARATOR}${bufferToBase64( + salt, + )}${ENCRYPTION_SEPARATOR}${iterationCount}`; return returnValue; } @@ -62,16 +78,58 @@ export async function decryptShareWeb( encryptedShareDetails: string, pwd: string, ): Promise { - const [encryptedShare, iv, salt, tag] = - encryptedShareDetails.split(ENCRYPTION_SEPARATOR); + const [ + encryptedShareWithTagBase64, + ivBase64, + saltBase64, + maybeIterationCount, + ] = encryptedShareDetails.split(ENCRYPTION_SEPARATOR); + + let iterationCount = maybeIterationCount + ? parseInt(maybeIterationCount) + : undefined; + if (!iterationCount) { + iterationCount = DEPRECATED_KEY_ITERATION_COUNT; + } + + const key = await getEncryptionKey( + pwd, + base64ToBuffer(saltBase64), + iterationCount, + ); + + const encryptedShareWithTagBuffer = Buffer.from( + encryptedShareWithTagBase64, + "base64", + ); + + // The tag is a 16 bytes long hex string + const tagBytesLength = 16; + const cipherTextBufferLength = + encryptedShareWithTagBuffer.length - tagBytesLength; + + // Get cipherText and tag from encryptedShareWithTagBuffer + const cipherTextBuffer = Buffer.from( + encryptedShareWithTagBuffer.subarray(0, cipherTextBufferLength), + ); + const tagBuffer = Buffer.from( + encryptedShareWithTagBuffer.subarray(cipherTextBufferLength), + ); + + const originalBase64CipherText = cipherTextBuffer.toString("base64"); + // converting to hex since the decrypt function expects a hex string + // Ref: https://github.com/craftzdog/react-native-aes-gcm-crypto/blob/master/android/src/main/java/com/reactnativeaesgcmcrypto/AesGcmCryptoModule.kt#L111 + const hexStringTag = tagBuffer.toString("hex"); - const key = await getEncryptionKey(pwd, base64ToBuffer(salt)); + // converting to hex since the decrypt function expects a hex string + // Ref: https://github.com/craftzdog/react-native-aes-gcm-crypto/blob/master/android/src/main/java/com/reactnativeaesgcmcrypto/AesGcmCryptoModule.kt#L111 + const ivBufferHex = Buffer.from(ivBase64, "base64").toString("hex"); const normalizedShare = await AesGcmCrypto.decrypt( - encryptedShare, + originalBase64CipherText, key, - iv, - tag, + ivBufferHex, + hexStringTag, false, ); diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 591c793131a..86253fe3d0b 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,38 @@ # @thirdweb-dev/react +## 3.16.5 + +### Patch Changes + +- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]: + - @thirdweb-dev/sdk@3.10.67 + - @thirdweb-dev/react-core@3.16.5 + - @thirdweb-dev/wallets@1.3.5 + +## 3.16.4 + +### Patch Changes + +- [#1713](https://github.com/thirdweb-dev/js/pull/1713) [`46672a49`](https://github.com/thirdweb-dev/js/commit/46672a490a82924e3c4a003419e3eaad10d6dd8b) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Update upsell link + +- [#1717](https://github.com/thirdweb-dev/js/pull/1717) [`ffb1bb0a`](https://github.com/thirdweb-dev/js/commit/ffb1bb0a603a69c22eb8b6429326c23a2977e5a6) Thanks [@MananTank](https://github.com/MananTank)! - Add spinner in the Google Sign in popup instead of blank page + +- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`93127047`](https://github.com/thirdweb-dev/js/commit/931270479ef227556a1077357a8c000b08de6e8d), [`d3c8626a`](https://github.com/thirdweb-dev/js/commit/d3c8626a5a8def882c1592b236048ebe88e85d49), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c), [`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101)]: + - @thirdweb-dev/wallets@1.3.4 + - @thirdweb-dev/react-core@3.16.4 + - @thirdweb-dev/sdk@3.10.66 + +## 3.16.3 + +### Patch Changes + +- [#1707](https://github.com/thirdweb-dev/js/pull/1707) [`6d3d76cf`](https://github.com/thirdweb-dev/js/commit/6d3d76cff8018015faa191a1f8bd4f34506a6650) Thanks [@MananTank](https://github.com/MananTank)! - Fix Unchecked Index accesses + +- Updated dependencies [[`6d3d76cf`](https://github.com/thirdweb-dev/js/commit/6d3d76cff8018015faa191a1f8bd4f34506a6650), [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9), [`15fe4779`](https://github.com/thirdweb-dev/js/commit/15fe4779f4b99e51afe214ac3ebb31f611089787)]: + - @thirdweb-dev/wallets@1.3.3 + - @thirdweb-dev/sdk@3.10.65 + - @thirdweb-dev/react-core@3.16.3 + ## 3.16.2 ### Patch Changes diff --git a/packages/react/config/api-extractor-evm.json b/packages/react/config/api-extractor-evm.json index 5e8ff21eab8..256845044de 100644 --- a/packages/react/config/api-extractor-evm.json +++ b/packages/react/config/api-extractor-evm.json @@ -109,7 +109,7 @@ /** * (REQUIRED) Whether to generate an API report. */ - "enabled": true + "enabled": true, /** * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce @@ -135,7 +135,7 @@ * SUPPORTED TOKENS: , , * DEFAULT VALUE: "/etc/" */ - // "reportFolder": "/etc/", + "reportFolder": "/temp/" /** * Specifies the folder where the temporary report file is written. The file name portion is determined by @@ -330,6 +330,10 @@ "default": { "logLevel": "warning" // "addToApiReportFile": false + }, + + "ae-wrong-input-file-type": { + "logLevel": "none" } // "ae-extra-release-tag": { diff --git a/packages/react/package.json b/packages/react/package.json index a9f4d926ec9..c470dac4cd6 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/react", - "version": "3.16.2", + "version": "3.16.5", "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react", "author": "thirdweb eng ", "license": "Apache-2.0", diff --git a/packages/react/src/components/DragNDrop.tsx b/packages/react/src/components/DragNDrop.tsx index 3e4fc9d9650..363cc1b6af9 100644 --- a/packages/react/src/components/DragNDrop.tsx +++ b/packages/react/src/components/DragNDrop.tsx @@ -47,8 +47,11 @@ export const DragNDrop: React.FC<{ e.stopPropagation(); if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { - handleFileUpload(e.dataTransfer.files[0]); - e.dataTransfer.clearData(); + const fileContent = e.dataTransfer.files[0]; + if (fileContent) { + handleFileUpload(fileContent); + e.dataTransfer.clearData(); + } } }; @@ -83,7 +86,7 @@ export const DragNDrop: React.FC<{ display: "none", }} onChange={(e) => { - if (e.target.files && e.target.files.length > 0) { + if (e.target.files && e.target.files[0]) { handleFileUpload(e.target.files[0]); } }} diff --git a/packages/react/src/components/OTPInput.tsx b/packages/react/src/components/OTPInput.tsx index 9b4d0cde7da..8f99f120f3d 100644 --- a/packages/react/src/components/OTPInput.tsx +++ b/packages/react/src/components/OTPInput.tsx @@ -94,7 +94,10 @@ export function OTPInput(props: { let value = e.target.value; if (value.length > 1) { - value = value[value.length - 1]; + const lastValue = value[value.length - 1]; + if (lastValue) { + value = lastValue; + } } if (!/[0-9]/.test(value) && value !== "") { diff --git a/packages/react/src/evm/index.ts b/packages/react/src/evm/index.ts index 169bf9c14cc..58659b4c99d 100644 --- a/packages/react/src/evm/index.ts +++ b/packages/react/src/evm/index.ts @@ -18,6 +18,8 @@ export * from "./components/NftMedia"; export * from "./components/Web3Button"; export { ThirdwebProvider } from "./providers/thirdweb-provider"; +export type { MediaRendererProps } from "./components/types"; + // wallet/hooks export { useInstalledWallets } from "../wallet/hooks/useInstalledWallets"; @@ -41,3 +43,23 @@ export { // react-core export * from "@thirdweb-dev/react-core"; +// wallets +export { + LocalWallet, + EmbeddedWallet, + SmartWallet, + SafeWallet, + WalletConnect, + PhantomWallet, + RainbowWallet, + MetaMaskWallet, + TrustWallet, + CoinbaseWallet, + BloctoWallet, + FrameWallet, + PaperWallet, + ZerionWallet, + MagicLink, + SignerWallet, + InjectedWallet, +} from "@thirdweb-dev/wallets"; diff --git a/packages/react/src/wallet/ConnectWallet/ConnectWallet.tsx b/packages/react/src/wallet/ConnectWallet/ConnectWallet.tsx index cf2f94f3842..1f50d7cc7b6 100644 --- a/packages/react/src/wallet/ConnectWallet/ConnectWallet.tsx +++ b/packages/react/src/wallet/ConnectWallet/ConnectWallet.tsx @@ -192,7 +192,10 @@ export const ConnectWallet: React.FC = (props) => { const tokens = { ...defaultTokens }; for (const k in props.supportedTokens) { const key = Number(k); - tokens[key] = props.supportedTokens[key]; + const tokenList = props.supportedTokens[key]; + if (tokenList) { + tokens[key] = tokenList; + } } return tokens; diff --git a/packages/react/src/wallet/ConnectWallet/Details.tsx b/packages/react/src/wallet/ConnectWallet/Details.tsx index 3f373ef5d29..c7ce29ac443 100644 --- a/packages/react/src/wallet/ConnectWallet/Details.tsx +++ b/packages/react/src/wallet/ConnectWallet/Details.tsx @@ -464,7 +464,7 @@ export const ConnectedWalletDetails: React.FC<{ )} {/* Explorer link */} - {chain?.explorers && ( + {chain?.explorers && chain.explorers[0]?.url && ( ( export function useScreen() { const walletConfigs = useWallets(); const initialScreen = - walletConfigs.length === 1 && !walletConfigs[0].selectUI + (walletConfigs.length === 1 && !walletConfigs[0]?.selectUI ? walletConfigs[0] - : reservedScreens.main; + : reservedScreens.main) || reservedScreens.main; const [screen, setScreen] = useState(initialScreen); const prevInitialScreen = useRef(initialScreen); diff --git a/packages/react/src/wallet/ConnectWallet/SendFunds.tsx b/packages/react/src/wallet/ConnectWallet/SendFunds.tsx index f04de436849..8a3e9223a3c 100644 --- a/packages/react/src/wallet/ConnectWallet/SendFunds.tsx +++ b/packages/react/src/wallet/ConnectWallet/SendFunds.tsx @@ -42,7 +42,11 @@ export function SendFunds(props: { supportedTokens: SupportedTokens }) { props.supportedTokens[chainId] !== defaultTokens[chainId] ) { // use the first token in the list as default selected - defaultToken = props.supportedTokens[chainId][0]; + const tokensForChain = props.supportedTokens[chainId]; + const firstToken = tokensForChain && tokensForChain[0]; + if (firstToken) { + defaultToken = firstToken; + } } const [token, setToken] = useState(defaultToken); diff --git a/packages/react/src/wallet/ConnectWallet/WalletSelector.tsx b/packages/react/src/wallet/ConnectWallet/WalletSelector.tsx index 36047e0b856..cdd312e3876 100644 --- a/packages/react/src/wallet/ConnectWallet/WalletSelector.tsx +++ b/packages/react/src/wallet/ConnectWallet/WalletSelector.tsx @@ -106,7 +106,7 @@ export const WalletSelector: React.FC<{ color="primaryText" hoverColor="accentText" target="_blank" - href="https://thirdweb.com/dashboard/wallets/connect" + href="https://thirdweb.com/connect?utm_source=cw" style={{ display: "flex", alignItems: "center", diff --git a/packages/react/src/wallet/utils/openGoogleSignInWindow.ts b/packages/react/src/wallet/utils/openGoogleSignInWindow.ts new file mode 100644 index 00000000000..c402f526700 --- /dev/null +++ b/packages/react/src/wallet/utils/openGoogleSignInWindow.ts @@ -0,0 +1,67 @@ +export function openGoogleSignInWindow() { + const win = window.open("", undefined, "width=350, height=500"); + if (win) { + win.document.title = "Sign In - Google Accounts"; + win.document.body.innerHTML = spinnerWindowHtml; + } + return win; +} + +const spinnerWindowHtml = ` + + + + + +`; diff --git a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletFormUI.tsx b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletFormUI.tsx index 261b4f719b9..10a999ca25f 100644 --- a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletFormUI.tsx +++ b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletFormUI.tsx @@ -14,6 +14,7 @@ import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon"; import { InputSelectionUI } from "../InputSelectionUI"; import type { EmbeddedWalletLoginType } from "./types"; import { TextDivider } from "../../../components/TextDivider"; +import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow"; export const EmbeddedWalletFormUI = (props: { onSelect: (loginType: EmbeddedWalletLoginType) => void; @@ -29,7 +30,8 @@ export const EmbeddedWalletFormUI = (props: { try { const embeddedWallet = createWalletInstance(props.walletConfig); setConnectionStatus("connecting"); - const googleWindow = window.open("", "Login", "width=350, height=500"); + + const googleWindow = openGoogleSignInWindow(); if (!googleWindow) { throw new Error("Failed to open google login window"); } diff --git a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletGoogleLogin.tsx b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletGoogleLogin.tsx index c91198073db..05694d6140e 100644 --- a/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletGoogleLogin.tsx +++ b/packages/react/src/wallet/wallets/embeddedWallet/EmbeddedWalletGoogleLogin.tsx @@ -15,6 +15,7 @@ import { ModalTitle } from "../../../components/modalElements"; import { Text } from "../../../components/text"; import { iconSize } from "../../../design-system"; import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon"; +import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow"; export const EmbeddedWalletGoogleLogin = ( props: ConnectUIProps, @@ -29,7 +30,7 @@ export const EmbeddedWalletGoogleLogin = ( try { const embeddedWallet = createWalletInstance(props.walletConfig); setConnectionStatus("connecting"); - const googleWindow = window.open("", "Login", "width=350, height=500"); + const googleWindow = openGoogleSignInWindow(); if (!googleWindow) { throw new Error("Failed to open google login window"); } diff --git a/packages/react/src/wallet/wallets/paper/PaperFormUI.tsx b/packages/react/src/wallet/wallets/paper/PaperFormUI.tsx index b2d2965eb88..d5ed14465d5 100644 --- a/packages/react/src/wallet/wallets/paper/PaperFormUI.tsx +++ b/packages/react/src/wallet/wallets/paper/PaperFormUI.tsx @@ -14,6 +14,7 @@ import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon"; import { InputSelectionUI } from "../InputSelectionUI"; import { PaperLoginType } from "./types"; import { TextDivider } from "../../../components/TextDivider"; +import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow"; export const PaperFormUI = (props: { onSelect: (loginType: PaperLoginType) => void; @@ -30,7 +31,7 @@ export const PaperFormUI = (props: { try { const paperWallet = createWalletInstance(props.walletConfig); setConnectionStatus("connecting"); - const googleWindow = window.open("", "Login", "width=350, height=500"); + const googleWindow = openGoogleSignInWindow(); if (!googleWindow) { throw new Error("Failed to open google login window"); } diff --git a/packages/react/src/wallet/wallets/paper/PaperGoogleLogin.tsx b/packages/react/src/wallet/wallets/paper/PaperGoogleLogin.tsx index 4dafdb4ff2d..d726d60cc70 100644 --- a/packages/react/src/wallet/wallets/paper/PaperGoogleLogin.tsx +++ b/packages/react/src/wallet/wallets/paper/PaperGoogleLogin.tsx @@ -15,6 +15,7 @@ import { ModalTitle } from "../../../components/modalElements"; import { Text } from "../../../components/text"; import { iconSize } from "../../../design-system"; import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon"; +import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow"; export const PaperGoogleLogin = (props: ConnectUIProps) => { const { goBack, modalSize } = props; @@ -29,7 +30,7 @@ export const PaperGoogleLogin = (props: ConnectUIProps) => { try { const paperWallet = createWalletInstance(props.walletConfig); setConnectionStatus("connecting"); - const googleWindow = window.open("", "Login", "width=350, height=500"); + const googleWindow = openGoogleSignInWindow(); if (!googleWindow) { throw new Error("Failed to open google login window"); } diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index ae3d91a09fc..f4754db3884 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -1,5 +1,8 @@ { "extends": "@thirdweb-dev/tsconfig/react-library.json", "include": ["."], - "exclude": ["dist", "build", "node_modules"] + "exclude": ["dist", "build", "node_modules"], + "compilerOptions": { + "noUncheckedIndexedAccess": true + } } diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index c916c210cf5..f57b0d69e78 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,5 +1,32 @@ # @thirdweb-dev/sdk +## 3.10.67 + +### Patch Changes + +- [#1731](https://github.com/thirdweb-dev/js/pull/1731) [`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Improve add/removeContract methods + +- [#1727](https://github.com/thirdweb-dev/js/pull/1727) [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae) Thanks [@kien-ngo](https://github.com/kien-ngo)! - [SDK] Improve concurrent requests on NFT-related code + +- [#1728](https://github.com/thirdweb-dev/js/pull/1728) [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48) Thanks [@kien-ngo](https://github.com/kien-ngo)! - [SDK] Improve transfer function for user wallet + +## 3.10.66 + +### Patch Changes + +- [#1308](https://github.com/thirdweb-dev/js/pull/1308) [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Base Router for dynamic contracts + +## 3.10.65 + +### Patch Changes + +- [#1701](https://github.com/thirdweb-dev/js/pull/1701) [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Detect webGL platform + +- [#1686](https://github.com/thirdweb-dev/js/pull/1686) [`15fe4779`](https://github.com/thirdweb-dev/js/commit/15fe4779f4b99e51afe214ac3ebb31f611089787) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Update & add unit test for ERC1155 Enumerable + +- Updated dependencies [[`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9)]: + - @thirdweb-dev/storage@1.2.11 + ## 3.10.64 ### Patch Changes diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 714d0be79b0..1c45c1a61b0 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/sdk", - "version": "3.10.64", + "version": "3.10.67", "description": "The main thirdweb SDK.", "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk", "license": "Apache-2.0", diff --git a/packages/sdk/src/evm/common/error.ts b/packages/sdk/src/evm/common/error.ts index 195cae72cd0..95444829831 100644 --- a/packages/sdk/src/evm/common/error.ts +++ b/packages/sdk/src/evm/common/error.ts @@ -252,7 +252,7 @@ export class AuctionHasNotEndedError extends Error { export class ExtensionNotImplementedError extends Error { constructor(feature: Feature) { super( - `This functionality is not available because the contract does not implement the '${feature.docLinks.contracts}' Extension. Learn how to unlock this functionality at https://portal.thirdweb.com/extensions `, + `This functionality is not available because the contract does not implement the '${feature.name}' Extension. Learn how to unlock this functionality at https://portal.thirdweb.com/extensions `, ); } } diff --git a/packages/sdk/src/evm/common/nft.ts b/packages/sdk/src/evm/common/nft.ts index c08a216b79b..fa4fdd9c203 100644 --- a/packages/sdk/src/evm/common/nft.ts +++ b/packages/sdk/src/evm/common/nft.ts @@ -114,8 +114,10 @@ export async function fetchTokenMetadataForContract( ERC165MetadataAbi, provider, ) as IERC165; - const isERC721 = await erc165.supportsInterface(InterfaceId_IERC721); - const isERC1155 = await erc165.supportsInterface(InterfaceId_IERC1155); + const [isERC721, isERC1155] = await Promise.all([ + erc165.supportsInterface(InterfaceId_IERC721), + erc165.supportsInterface(InterfaceId_IERC1155), + ]); if (isERC721) { const erc721 = new Contract( contractAddress, diff --git a/packages/sdk/src/evm/common/plugin/generatePluginFunctions.ts b/packages/sdk/src/evm/common/plugin/generatePluginFunctions.ts index 835caad454b..883e8b46648 100644 --- a/packages/sdk/src/evm/common/plugin/generatePluginFunctions.ts +++ b/packages/sdk/src/evm/common/plugin/generatePluginFunctions.ts @@ -43,7 +43,7 @@ export function generatePluginFunctions( } export function generateExtensionFunctions( - extensionAbi: any, + extensionAbi: Abi, ): ExtensionFunction[] { const extensionInterface = new utils.Interface(extensionAbi); const extensionFunctions: ExtensionFunction[] = []; diff --git a/packages/sdk/src/evm/constants/thirdweb-features.ts b/packages/sdk/src/evm/constants/thirdweb-features.ts index 8134632201f..d774f4ba54b 100644 --- a/packages/sdk/src/evm/constants/thirdweb-features.ts +++ b/packages/sdk/src/evm/constants/thirdweb-features.ts @@ -205,7 +205,7 @@ export const FEATURE_GASLESS = { docLinks: { sdk: "sdk.gaslesstransaction", // TODO add the correct name for this once it's added to portal - contracts: "", + contracts: "IERC2771Context", }, abis: [IERC2771ContextAbi], features: {}, @@ -217,7 +217,7 @@ export const FEATURE_PACK_VRF = { docLinks: { sdk: "sdk.packvrf", //TODO - contracts: "", + contracts: "IPackVRF", }, abis: [IPackVRFAbi], features: {}, @@ -229,7 +229,7 @@ export const FEATURE_PLUGIN_ROUTER = { docLinks: { sdk: "sdk.pluginrouter", //TODO - contracts: "", + contracts: "PluginRouter", }, abis: [getAllPluginsAbi], features: {}, @@ -241,7 +241,7 @@ export const FEATURE_DYNAMIC_CONTRACT = { docLinks: { sdk: "", //TODO - contracts: "", + contracts: "IExtensionsManager", }, abis: [getAllExtensionsAbi], features: {}, @@ -253,7 +253,7 @@ export const FEATURE_DIRECT_LISTINGS = { docLinks: { // TODO sdk: "", - contracts: "", + contracts: "IDirectListings", }, abis: [IDirectListingsAbi], features: {}, @@ -265,7 +265,7 @@ export const FEATURE_ENGLISH_AUCTIONS = { docLinks: { // TODO sdk: "", - contracts: "", + contracts: "IEnglishAuctions", }, abis: [IEnglishAuctionsAbi], features: {}, @@ -277,7 +277,7 @@ export const FEATURE_OFFERS = { docLinks: { // TODO sdk: "", - contracts: "", + contracts: "IOffers", }, abis: [IOffersAbi], features: {}, @@ -289,7 +289,7 @@ export const FEATURE_ACCOUNT_FACTORY = { docLinks: { // TODO sdk: "sdk.accountFactory", - contracts: "", + contracts: "IAccountFactory", }, abis: [IAccountFactoryCore], features: {}, @@ -301,7 +301,7 @@ export const FEATURE_ACCOUNT_PERMISSIONS = { docLinks: { // TODO sdk: "sdk.account", - contracts: "", + contracts: "IAccountPermissions", }, abis: [IAccountPermissions], features: {}, @@ -313,7 +313,7 @@ export const FEATURE_ACCOUNT = { docLinks: { // TODO sdk: "sdk.account", - contracts: "", + contracts: "IAccount", }, abis: [IAccount], features: { [FEATURE_ACCOUNT_PERMISSIONS.name]: FEATURE_ACCOUNT_PERMISSIONS }, @@ -325,7 +325,7 @@ export const FEATURE_AIRDROP_ERC20 = { docLinks: { // TODO sdk: "", - contracts: "", + contracts: "IAirdropERC20", }, abis: [IAirdropERC20], features: {}, @@ -337,7 +337,7 @@ export const FEATURE_AIRDROP_ERC721 = { docLinks: { // TODO sdk: "", - contracts: "", + contracts: "IAirdropERC721", }, abis: [IAirdropERC721], features: {}, @@ -349,7 +349,7 @@ export const FEATURE_AIRDROP_ERC1155 = { docLinks: { // TODO sdk: "", - contracts: "", + contracts: "IAirdropERC1155", }, abis: [IAirdropERC1155], features: {}, diff --git a/packages/sdk/src/evm/constants/urls.ts b/packages/sdk/src/evm/constants/urls.ts index ee0b232f9f5..eb34b21fa09 100644 --- a/packages/sdk/src/evm/constants/urls.ts +++ b/packages/sdk/src/evm/constants/urls.ts @@ -64,8 +64,9 @@ export function getChainProvider( // if we still don't have an url fall back to just using the chainId or slug in the rpc and try that if (!rpcUrl) { - rpcUrl = `https://${chainId || network}.rpc.thirdweb.com/${options.clientId || "" - }`; + rpcUrl = `https://${chainId || network}.rpc.thirdweb.com/${ + options.clientId || "" + }`; } if (!rpcUrl) { @@ -264,7 +265,9 @@ export function getProviderFromRpcUrl( headers["x-sdk-platform"] = bundleId ? "react-native" : isBrowser() - ? "browser" + ? (window as any).bridge !== undefined + ? "webGL" + : "browser" : "node"; } const match = rpcUrl.match(/^(ws|http)s?:/i); @@ -285,18 +288,18 @@ export function getProviderFromRpcUrl( // Otherwise, create a new provider on the specific network const newProvider = chainId ? // If we know the chainId we should use the StaticJsonRpcBatchProvider - new StaticJsonRpcBatchProvider( - { + new StaticJsonRpcBatchProvider( + { + url: rpcUrl, + headers, + }, + chainId, + ) + : // Otherwise fall back to the built in json rpc batch provider + new providers.JsonRpcBatchProvider({ url: rpcUrl, headers, - }, - chainId, - ) - : // Otherwise fall back to the built in json rpc batch provider - new providers.JsonRpcBatchProvider({ - url: rpcUrl, - headers, - }); + }); // Save the provider in our cache RPC_PROVIDER_MAP.set(seralizedOpts, newProvider); diff --git a/packages/sdk/src/evm/contracts/prebuilt-implementations/nft-drop.ts b/packages/sdk/src/evm/contracts/prebuilt-implementations/nft-drop.ts index 702e3e270b8..662002668ce 100644 --- a/packages/sdk/src/evm/contracts/prebuilt-implementations/nft-drop.ts +++ b/packages/sdk/src/evm/contracts/prebuilt-implementations/nft-drop.ts @@ -226,8 +226,10 @@ export class NFTDrop extends StandardErc721 { * Get the total count NFTs in this drop contract, both claimed and unclaimed */ override async totalSupply() { - const claimed = await this.totalClaimedSupply(); - const unclaimed = await this.totalUnclaimedSupply(); + const [claimed, unclaimed] = await Promise.all([ + this.totalClaimedSupply(), + this.totalUnclaimedSupply(), + ]); return claimed.add(unclaimed); } diff --git a/packages/sdk/src/evm/contracts/prebuilt-implementations/signature-drop.ts b/packages/sdk/src/evm/contracts/prebuilt-implementations/signature-drop.ts index 235899f277b..db64841df41 100644 --- a/packages/sdk/src/evm/contracts/prebuilt-implementations/signature-drop.ts +++ b/packages/sdk/src/evm/contracts/prebuilt-implementations/signature-drop.ts @@ -249,8 +249,10 @@ export class SignatureDrop extends StandardErc721 { * Get the total count NFTs in this drop contract, both claimed and unclaimed */ override async totalSupply() { - const claimed = await this.totalClaimedSupply(); - const unclaimed = await this.totalUnclaimedSupply(); + const [claimed, unclaimed] = await Promise.all([ + this.totalClaimedSupply(), + this.totalUnclaimedSupply(), + ]); return claimed.add(unclaimed); } diff --git a/packages/sdk/src/evm/contracts/smart-contract.ts b/packages/sdk/src/evm/contracts/smart-contract.ts index e5ccb9e207d..6dea2815895 100644 --- a/packages/sdk/src/evm/contracts/smart-contract.ts +++ b/packages/sdk/src/evm/contracts/smart-contract.ts @@ -14,6 +14,7 @@ import type { IRoyalty, OffersLogic, Ownable, + BaseRouter, } from "@thirdweb-dev/contracts-js"; import { ThirdwebStorage } from "@thirdweb-dev/storage"; import { BaseContract, CallOverrides } from "ethers"; @@ -38,6 +39,7 @@ import { FEATURE_PLATFORM_FEE, FEATURE_PRIMARY_SALE, FEATURE_ROYALTY, + FEATURE_DYNAMIC_CONTRACT, } from "../constants/thirdweb-features"; import { Account } from "../core/classes/account"; import { AccountFactory } from "../core/classes/account-factory"; @@ -76,6 +78,7 @@ import { SDKOptions } from "../schema/sdk-options"; import { Address } from "../schema/shared/Address"; import { BaseContractInterface } from "../types/contract"; import { BaseERC1155, BaseERC20, BaseERC721 } from "../types/eips"; +import { ExtensionManager } from "../core/classes/extension-manager"; /** * Custom contract dynamic class with feature detection @@ -116,9 +119,12 @@ export class SmartContract< public encoder: ContractEncoder; public estimator: GasCostEstimator; public publishedMetadata: ContractPublishedMetadata; - public abi: Abi; public metadata: ContractMetadata; + get abi(): Abi { + return AbiSchema.parse(this.contractWrapper.abi || []); + } + /** * Handle royalties */ @@ -355,6 +361,10 @@ export class SmartContract< return assertEnabled(this.detectAccount(), FEATURE_ACCOUNT); } + get extensions(): ExtensionManager { + return assertEnabled(this.detectBaseRouter(), FEATURE_DYNAMIC_CONTRACT); + } + private _chainId: number; get chainId() { return this._chainId; @@ -378,7 +388,6 @@ export class SmartContract< this._chainId = chainId; this.storage = storage; this.contractWrapper = contractWrapper; - this.abi = AbiSchema.parse(abi || []); this.events = new ContractEvents(this.contractWrapper); this.encoder = new ContractEncoder(this.contractWrapper); @@ -583,6 +592,18 @@ export class SmartContract< return undefined; } + private detectBaseRouter() { + if ( + detectContractFeature( + this.contractWrapper, + FEATURE_DYNAMIC_CONTRACT.name, + ) + ) { + return new ExtensionManager(this.contractWrapper); + } + return undefined; + } + private detectAirdrop20() { if ( detectContractFeature(this.contractWrapper, "AirdropERC20") diff --git a/packages/sdk/src/evm/core/classes/account-permissions.ts b/packages/sdk/src/evm/core/classes/account-permissions.ts index 29db755d032..69038e1930d 100644 --- a/packages/sdk/src/evm/core/classes/account-permissions.ts +++ b/packages/sdk/src/evm/core/classes/account-permissions.ts @@ -409,7 +409,7 @@ export class AccountPermissions implements DetectableFeature { * * @example * ```javascript - * const tx = await contract.account.updateAccess(signer, restrictions); + * const tx = await contract.account.updatePermissions(signer, permissions); * const receipt = tx.receipt(); * ``` * diff --git a/packages/sdk/src/evm/core/classes/contract-wrapper.ts b/packages/sdk/src/evm/core/classes/contract-wrapper.ts index 02469f17083..93b13ae3250 100644 --- a/packages/sdk/src/evm/core/classes/contract-wrapper.ts +++ b/packages/sdk/src/evm/core/classes/contract-wrapper.ts @@ -98,6 +98,22 @@ export class ContractWrapper< ) as TContract; } + public updateAbi(updatedAbi: ContractInterface): void { + // re-connect the contract with the new signer / provider + this.writeContract = new Contract( + this.address, + updatedAbi, + this.getSignerOrProvider(), + ) as TContract; + + // setup the read only contract + this.readContract = this.writeContract.connect( + this.getProvider(), + ) as TContract; + + this.abi = AbiSchema.parse(updatedAbi); + } + /** * @internal */ diff --git a/packages/sdk/src/evm/core/classes/erc-1155-enumerable.ts b/packages/sdk/src/evm/core/classes/erc-1155-enumerable.ts index 0adad0ba8dd..25744c48384 100644 --- a/packages/sdk/src/evm/core/classes/erc-1155-enumerable.ts +++ b/packages/sdk/src/evm/core/classes/erc-1155-enumerable.ts @@ -103,10 +103,12 @@ export class Erc1155Enumerable implements DetectableFeature { * @returns The NFT metadata for all NFTs in the contract. */ public async owned(walletAddress?: AddressOrEns): Promise { - const address = await resolveAddress( - walletAddress || (await this.contractWrapper.getSignerAddress()), - ); - const maxId = await this.contractWrapper.read("nextTokenIdToMint", []); + const [address, maxId] = await Promise.all([ + resolveAddress( + walletAddress || (await this.contractWrapper.getSignerAddress()), + ), + this.contractWrapper.read("nextTokenIdToMint", []), + ]); const balances = await this.contractWrapper.read("balanceOfBatch", [ Array(maxId.toNumber()).fill(address), Array.from(Array(maxId.toNumber()).keys()), @@ -120,15 +122,15 @@ export class Erc1155Enumerable implements DetectableFeature { }; }) .filter((b) => b.balance.gt(0)); - return await Promise.all( - ownedBalances.map(async (b) => { - const editionMetadata = await this.erc1155.get(b.tokenId.toString()); - return { - ...editionMetadata, - owner: address, - quantityOwned: b.balance.toString(), - }; - }), - ); + const nfts = ( + await Promise.all( + ownedBalances.map((item) => this.erc1155.get(item.tokenId.toString())), + ) + ).map((editionMetadata, index) => ({ + ...editionMetadata, + owner: address, + quantityOwned: ownedBalances[index].balance.toString(), + })); + return nfts; } } diff --git a/packages/sdk/src/evm/core/classes/extension-manager.ts b/packages/sdk/src/evm/core/classes/extension-manager.ts new file mode 100644 index 00000000000..1b495aef78e --- /dev/null +++ b/packages/sdk/src/evm/core/classes/extension-manager.ts @@ -0,0 +1,522 @@ +import type { BaseRouter } from "@thirdweb-dev/contracts-js"; +import { DetectableFeature } from "../interfaces/DetectableFeature"; +import { ContractWrapper } from "./contract-wrapper"; +import { buildTransactionFunction } from "../../common/transactions"; +import { Transaction } from "./transactions"; +import { + Extension, + ExtensionFunction, + ExtensionMetadata, + FunctionInput, +} from "../../types/extension"; +import { FEATURE_DYNAMIC_CONTRACT } from "../../constants/thirdweb-features"; +import { ContractInterface } from "ethers"; +import { generateExtensionFunctions } from "../../common/plugin/generatePluginFunctions"; +import { Abi, AbiSchema, CommonContractSchema } from "../../schema"; +import { utils } from "ethers"; +import invariant from "tiny-invariant"; +import { ExtensionAddedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/CoreRouter"; +import { + THIRDWEB_DEPLOYER, + deployContractDeterministic, + deployWithThrowawayDeployer, + fetchContractMetadataFromAddress, + fetchPublishedContractFromPolygon, + getDeploymentInfo, +} from "../../common"; +import { joinABIs } from "../../common/plugin/joinABIs"; +import { TransactionReceipt } from "@ethersproject/abstract-provider"; +import { ExtensionRemovedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/CoreRouter"; +import { ExtensionReplacedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/BaseRouter"; +import { DynamicContractExtensionMetadataOrUri } from "../../types"; + +export class ExtensionManager implements DetectableFeature { + featureName = FEATURE_DYNAMIC_CONTRACT.name; + + private contractWrapper: ContractWrapper; + + constructor(contractWrapper: ContractWrapper) { + this.contractWrapper = contractWrapper; + } + + getAddress(): string { + return this.contractWrapper.readContract.address; + } + + /** ****************************** + * READ FUNCTIONS + *******************************/ + + public async getAll(): Promise { + const extensions: Extension[] = + await this.contractWrapper.readContract.getAllExtensions(); + + return extensions; + } + + public async get(extensionName: string): Promise { + const extension: Extension = + await this.contractWrapper.readContract.getExtension(extensionName); + + return extension; + } + + public async getExtensionAddress(extensionName: string): Promise { + const extension = await this.get(extensionName); + return extension.metadata.implementation; + } + + public async getAllFunctions( + extensionName: string, + ): Promise { + const extension = await this.get(extensionName); + return extension.functions; + } + + public async getExtensionForFunction( + functionInput: FunctionInput, + ): Promise { + let selector = functionInput.functionSelector; + if (!selector) { + invariant( + functionInput.functionSignature, + "Atleast one of function selector and signature must be provided", + ); + + selector = utils.id(functionInput.functionSignature).substring(0, 10); + } + + const extensionMetadata: ExtensionMetadata = + await this.contractWrapper.readContract.getMetadataForFunction(selector); + + return extensionMetadata; + } + + public async getExtensionAddressForFunction( + functionInput: FunctionInput, + ): Promise { + const extensionMetadata = await this.getExtensionForFunction(functionInput); + return extensionMetadata.implementation; + } + + /** ****************************** + * WRITE FUNCTIONS + *******************************/ + + /** + * Adds an extension to the contract + */ + add = /* @__PURE__ */ buildTransactionFunction( + async (inputArgs: { + extension: Extension; + extensionAbi?: ContractInterface; + }): Promise>> => { + return Transaction.fromContractWrapper({ + contractWrapper: this.contractWrapper, + method: "addExtension", + args: [inputArgs.extension], + + parse: async (receipt) => { + const events = this.contractWrapper.parseLogs( + "ExtensionAdded", + receipt.logs, + ); + + if (events.length < 1) { + throw new Error("No ExtensionAdded event found"); + } + + const extensionAbi = inputArgs.extensionAbi + ? AbiSchema.parse(inputArgs.extensionAbi) + : ( + await fetchContractMetadataFromAddress( + inputArgs.extension.metadata.implementation, + this.contractWrapper.getProvider(), + this.contractWrapper.storage, + ) + ).abi; + + const abiToAdd = this.filterAbiForAdd( + extensionAbi, + inputArgs.extension, + ); + + const updatedAbi = joinABIs([ + AbiSchema.parse(this.contractWrapper.abi), + abiToAdd, + ]); + + this.contractWrapper.updateAbi(updatedAbi); + + return receipt; + }, + }); + }, + ); + + /** + * Adds a deployed extension to the contract + */ + addDeployed = /* @__PURE__ */ buildTransactionFunction( + async (inputArgs: { + extensionName: string; + extensionAddress: string; + extensionMetadata?: DynamicContractExtensionMetadataOrUri; + extensionAbi?: ContractInterface; + }): Promise>> => { + let extensionAbi = inputArgs.extensionAbi; + if (!extensionAbi) { + const metadata = await fetchContractMetadataFromAddress( + inputArgs.extensionAddress, + this.contractWrapper.getProvider(), + this.contractWrapper.storage, + this.contractWrapper.options, + ); + + extensionAbi = metadata.abi; + } + + invariant(extensionAbi, "Require extension ABI"); + + let extensionMetadataUri = ""; + if (inputArgs.extensionMetadata) { + if (typeof inputArgs.extensionMetadata === "string") { + extensionMetadataUri = inputArgs.extensionMetadata; + } else { + const parsedMetadata = await CommonContractSchema.parseAsync( + inputArgs.extensionMetadata, + ); + extensionMetadataUri = await this.contractWrapper.storage.upload( + parsedMetadata, + ); + } + } + + const extensionFunctions: ExtensionFunction[] = + generateExtensionFunctions(AbiSchema.parse(extensionAbi)); + + const extension: Extension = { + metadata: { + name: inputArgs.extensionName, + metadataURI: extensionMetadataUri, + implementation: inputArgs.extensionAddress, + }, + functions: extensionFunctions, + }; + + return this.add.prepare({ extension, extensionAbi }); + }, + ); + + /** + * Adds a published extension to the contract, and deploys it deterministically if necessary + */ + addPublished = /* @__PURE__ */ buildTransactionFunction( + async (inputArgs: { + extensionName: string; + publisherAddress?: string; + version?: string; + extensionMetadataOverride?: DynamicContractExtensionMetadataOrUri; + }): Promise>> => { + const version = inputArgs.version || "latest"; + + const { deployedExtensionAddress, extensionMetadata } = + await this.deployExtension( + inputArgs.extensionName, + inputArgs.publisherAddress || THIRDWEB_DEPLOYER, + version, + ); + + return this.addDeployed.prepare({ + extensionName: inputArgs.extensionName, + extensionAddress: deployedExtensionAddress, + extensionMetadata: + inputArgs.extensionMetadataOverride || extensionMetadata, + }); + }, + ); + + replace = /* @__PURE__ */ buildTransactionFunction( + async (inputArgs: { + extension: Extension; + extensionAbi?: ContractInterface; + }): Promise>> => { + return Transaction.fromContractWrapper({ + contractWrapper: this.contractWrapper, + method: "replaceExtension", + args: [inputArgs.extension], + + parse: async (receipt) => { + const events = this.contractWrapper.parseLogs( + "ExtensionReplaced", + receipt.logs, + ); + + if (events.length < 1) { + throw new Error("No ExtensionReplaced event found"); + } + + const extensionAbi = inputArgs.extensionAbi + ? AbiSchema.parse(inputArgs.extensionAbi) + : ( + await fetchContractMetadataFromAddress( + inputArgs.extension.metadata.implementation, + this.contractWrapper.getProvider(), + this.contractWrapper.storage, + ) + ).abi; + + const contractAbi = this.filterAbiForRemove( + AbiSchema.parse(this.contractWrapper.abi), + extensionAbi, + ); + const abiToAdd = this.filterAbiForAdd( + extensionAbi, + inputArgs.extension, + ); + const updatedAbi = joinABIs([contractAbi, abiToAdd]); + + this.contractWrapper.updateAbi(updatedAbi); + + return receipt; + }, + }); + }, + ); + + replaceDeployed = /* @__PURE__ */ buildTransactionFunction( + async (inputArgs: { + extensionName: string; + extensionAddress: string; + extensionMetadata?: DynamicContractExtensionMetadataOrUri; + extensionAbi?: ContractInterface; + }): Promise>> => { + let extensionAbi = inputArgs.extensionAbi; + if (!extensionAbi) { + const metadata = await fetchContractMetadataFromAddress( + inputArgs.extensionAddress, + this.contractWrapper.getProvider(), + this.contractWrapper.storage, + this.contractWrapper.options, + ); + + extensionAbi = metadata.abi; + } + + invariant(extensionAbi, "Require extension ABI"); + + let extensionMetadataUri = ""; + if (inputArgs.extensionMetadata) { + if (typeof inputArgs.extensionMetadata === "string") { + extensionMetadataUri = inputArgs.extensionMetadata; + } else { + const parsedMetadata = await CommonContractSchema.parseAsync( + inputArgs.extensionMetadata, + ); + extensionMetadataUri = await this.contractWrapper.storage.upload( + parsedMetadata, + ); + } + } + + const extensionFunctions: ExtensionFunction[] = + generateExtensionFunctions(AbiSchema.parse(extensionAbi)); + + const extension: Extension = { + metadata: { + name: inputArgs.extensionName, + metadataURI: extensionMetadataUri, + implementation: inputArgs.extensionAddress, + }, + functions: extensionFunctions, + }; + + return this.replace.prepare({ extension, extensionAbi }); + }, + ); + + replacePublished = /* @__PURE__ */ buildTransactionFunction( + async (inputArgs: { + extensionName: string; + publisherAddress?: string; + version?: string; + extensionMetadataOverride?: DynamicContractExtensionMetadataOrUri; + }): Promise>> => { + const version = inputArgs.version || "latest"; + + const { deployedExtensionAddress, extensionMetadata } = + await this.deployExtension( + inputArgs.extensionName, + inputArgs.publisherAddress || THIRDWEB_DEPLOYER, + version, + ); + + return this.replaceDeployed.prepare({ + extensionName: inputArgs.extensionName, + extensionAddress: deployedExtensionAddress, + extensionMetadata: + inputArgs.extensionMetadataOverride || extensionMetadata, + }); + }, + ); + + remove = /* @__PURE__ */ buildTransactionFunction( + async (inputArgs: { + extensionName: string; + }): Promise>> => { + const extensionAddress = await this.getExtensionAddress( + inputArgs.extensionName, + ); + return Transaction.fromContractWrapper({ + contractWrapper: this.contractWrapper, + method: "removeExtension", + args: [inputArgs.extensionName], + + parse: async (receipt) => { + const events = this.contractWrapper.parseLogs( + "ExtensionRemoved", + receipt.logs, + ); + + if (events.length < 1) { + throw new Error("No ExtensionRemoved event found"); + } + + const extensionAbi = ( + await fetchContractMetadataFromAddress( + extensionAddress, + this.contractWrapper.getProvider(), + this.contractWrapper.storage, + ) + ).abi; + + const updatedAbi = this.filterAbiForRemove( + AbiSchema.parse(this.contractWrapper.abi), + extensionAbi, + ); + + this.contractWrapper.updateAbi(updatedAbi); + + return receipt; + }, + }); + }, + ); + + /** ****************************** + * Internal / private + *******************************/ + + private filterAbiForAdd(extensionAbi: Abi, extension: Extension) { + const extensionAbiInterface = new utils.Interface(extensionAbi); + const extensionFunctionSelectors = extension.functions.map( + (fn) => fn.functionSelector, + ); + const filtered = extensionAbi.filter((item) => { + const fnFragment = Object.values(new utils.Interface([item]).functions); + if (fnFragment.length === 0) { + return false; + } + + const fnSigHash = extensionAbiInterface.getSighash(fnFragment[0]); + + return extensionFunctionSelectors.includes(fnSigHash); + }); + + return filtered; + } + + private filterAbiForRemove(fullAbi: Abi, abiToRemove: Abi) { + const fullAbiInterface = new utils.Interface(fullAbi); + const interfaceToRemove = new utils.Interface(abiToRemove); + const functionsToRemove = Object.values(interfaceToRemove.functions).map( + (fn) => interfaceToRemove.getSighash(fn), + ); + + const filtered = fullAbi.filter((item) => { + const fnFragment = Object.values(new utils.Interface([item]).functions); + if (fnFragment.length === 0) { + return false; + } + + const fnSigHash = fullAbiInterface.getSighash(fnFragment[0]); + + return !functionsToRemove.includes(fnSigHash); + }); + + return filtered; + } + + private async deployExtension( + extensionName: string, + publisherAddress: string, + version: string = "latest", + ): Promise<{ deployedExtensionAddress: string; extensionMetadata: string }> { + const published = await fetchPublishedContractFromPolygon( + publisherAddress, + extensionName, + version, + this.contractWrapper.storage, + this.contractWrapper.options.clientId, + this.contractWrapper.options.secretKey, + ); + + const deploymentInfo = await getDeploymentInfo( + published.metadataUri, + this.contractWrapper.storage, + this.contractWrapper.getProvider(), + "", + this.contractWrapper.options.clientId, + this.contractWrapper.options.secretKey, + ); + + const implementationAddress = deploymentInfo.find( + (i) => i.type === "implementation", + )?.transaction.predictedAddress as string; + + // deploy infra + plugins + implementation using a throwaway Deployer contract + + // filter out already deployed contracts (data is empty) + const transactionsToSend = deploymentInfo.filter( + (i) => i.transaction.data && i.transaction.data.length > 0, + ); + const transactionsforDirectDeploy = transactionsToSend + .filter((i) => { + return i.type !== "infra"; + }) + .map((i) => i.transaction); + const transactionsForThrowawayDeployer = transactionsToSend + .filter((i) => { + return i.type === "infra"; + }) + .map((i) => i.transaction); + + const signer = this.contractWrapper.getSigner(); + invariant(signer, "Signer is required"); + + // deploy via throwaway deployer, multiple infra contracts in one transaction + await deployWithThrowawayDeployer( + signer, + transactionsForThrowawayDeployer, + {}, + ); + + // send each transaction directly to Create2 factory + // process txns one at a time + for (const tx of transactionsforDirectDeploy) { + try { + await deployContractDeterministic(signer, tx); + } catch (e) { + console.debug( + `Error deploying contract at ${tx.predictedAddress}`, + (e as any)?.message, + ); + } + } + + return { + deployedExtensionAddress: implementationAddress, + extensionMetadata: published.metadataUri, + }; + } +} diff --git a/packages/sdk/src/evm/core/classes/registry.ts b/packages/sdk/src/evm/core/classes/registry.ts index c0c98071abf..6af608641a8 100644 --- a/packages/sdk/src/evm/core/classes/registry.ts +++ b/packages/sdk/src/evm/core/classes/registry.ts @@ -48,15 +48,11 @@ export class ContractRegistry extends ContractWrapper { ): Promise> => { const deployerAddress = await this.getSignerAddress(); const contractEncoder = new ContractEncoder(this); - const encoded: string[] = await Promise.all( - contractAddresses.map(async (address) => - contractEncoder.encode("add", [ - deployerAddress, - await resolveAddress(address), - ]), - ), + const encoded = ( + await Promise.all(contractAddresses.map((addr) => resolveAddress(addr))) + ).map((address) => + contractEncoder.encode("add", [deployerAddress, address]), ); - return Transaction.fromContractWrapper({ contractWrapper: this, method: "multicall", @@ -79,15 +75,11 @@ export class ContractRegistry extends ContractWrapper { ): Promise> => { const deployerAddress = await this.getSignerAddress(); const contractEncoder = new ContractEncoder(this); - const encoded: string[] = await Promise.all( - contractAddresses.map(async (address) => - contractEncoder.encode("remove", [ - deployerAddress, - await resolveAddress(address), - ]), - ), + const encoded = ( + await Promise.all(contractAddresses.map((addr) => resolveAddress(addr))) + ).map((address) => + contractEncoder.encode("remove", [deployerAddress, address]), ); - return Transaction.fromContractWrapper({ contractWrapper: this, method: "multicall", diff --git a/packages/sdk/src/evm/core/sdk.ts b/packages/sdk/src/evm/core/sdk.ts index f7b41573d06..614f0b85fa6 100644 --- a/packages/sdk/src/evm/core/sdk.ts +++ b/packages/sdk/src/evm/core/sdk.ts @@ -1103,7 +1103,7 @@ export class ContractDeployer extends RPCConnectionHandler { parsedMetadata.seller_fee_basis_points, ]; - return await this.deployReleasedContract.prepare( + return await this.deployPublishedContract.prepare( THIRDWEB_DEPLOYER, "OpenEditionERC721", deployArgs, diff --git a/packages/sdk/src/evm/core/wallet/user-wallet.ts b/packages/sdk/src/evm/core/wallet/user-wallet.ts index b6b0dca557f..77fc650008a 100644 --- a/packages/sdk/src/evm/core/wallet/user-wallet.ts +++ b/packages/sdk/src/evm/core/wallet/user-wallet.ts @@ -98,15 +98,17 @@ export class UserWallet { amount: Amount, currencyAddress: AddressOrEns = NATIVE_TOKEN_ADDRESS, ): Promise { - const resolvedTo = await resolveAddress(to); - const resolvedCurrency = await resolveAddress(currencyAddress); + const [resolvedTo, resolvedCurrency, amountInWei] = await Promise.all([ + resolveAddress(to), + resolveAddress(currencyAddress), + normalizePriceValue( + this.connection.getProvider(), + amount, + currencyAddress, + ), + ]); const signer = this.requireWallet(); - const amountInWei = await normalizePriceValue( - this.connection.getProvider(), - amount, - currencyAddress, - ); if (isNativeToken(resolvedCurrency)) { // native token transfer const from = await signer.getAddress(); diff --git a/packages/sdk/src/evm/schema/contracts/common/extension.ts b/packages/sdk/src/evm/schema/contracts/common/extension.ts new file mode 100644 index 00000000000..125eeb11ac2 --- /dev/null +++ b/packages/sdk/src/evm/schema/contracts/common/extension.ts @@ -0,0 +1,31 @@ +import { BytesLikeSchema } from "../../../../core/schema/shared"; +import { AddressSchema } from "../../shared/AddressSchema"; +import { z } from "zod"; + +/** + * @internal + */ +export const ExtensionMetadataInput = /* @__PURE__ */ (() => + z.object({ + name: z.string(), + metadataURI: z.string(), + implementation: AddressSchema, + }))(); + +/** + * @internal + */ +export const ExtensionFunctionInput = /* @__PURE__ */ (() => + z.object({ + functionSelector: BytesLikeSchema, + functionSignature: z.string(), + }))(); + +/** + * @internal + */ +export const ExtensionInput = /* @__PURE__ */ (() => + z.object({ + metadata: ExtensionMetadataInput, + functions: z.array(ExtensionFunctionInput), + }))(); diff --git a/packages/sdk/src/evm/schema/contracts/common/index.ts b/packages/sdk/src/evm/schema/contracts/common/index.ts index 260049098f8..5f311dd6488 100644 --- a/packages/sdk/src/evm/schema/contracts/common/index.ts +++ b/packages/sdk/src/evm/schema/contracts/common/index.ts @@ -10,14 +10,16 @@ import { z } from "zod"; * @internal */ export const CommonContractSchema = /* @__PURE__ */ (() => - z.object({ - name: z.string(), - description: z.string().optional(), - image: FileOrBufferOrStringSchema.optional(), - external_link: z.string().optional(), - app_uri: z.string().optional(), - social_urls: z.record(z.string()).optional(), - }))(); + z + .object({ + name: z.string(), + description: z.string().optional(), + image: FileOrBufferOrStringSchema.optional(), + external_link: z.string().optional(), + app_uri: z.string().optional(), + social_urls: z.record(z.string()).optional(), + }) + .catchall(z.unknown()))(); export type CommonContractSchemaInput = z.input; diff --git a/packages/sdk/src/evm/types/deploy/deploy-metadata.ts b/packages/sdk/src/evm/types/deploy/deploy-metadata.ts index 2877b2027cc..5b5a14521c5 100644 --- a/packages/sdk/src/evm/types/deploy/deploy-metadata.ts +++ b/packages/sdk/src/evm/types/deploy/deploy-metadata.ts @@ -1,6 +1,7 @@ import { AddressOrEns } from "../../schema/shared/AddressOrEnsSchema"; import { FileOrBufferOrString } from "@thirdweb-dev/storage"; import type { BigNumberish, Bytes } from "ethers"; +import { CommonContractSchemaInput } from "../../schema"; /** * Options for deploying an NFT contract @@ -407,3 +408,10 @@ export interface AirdropContractDeployMetadata { */ app_uri?: string; } + +/** + * @public + */ +export type DynamicContractExtensionMetadataOrUri = + | CommonContractSchemaInput + | string; diff --git a/packages/sdk/src/evm/types/extension.ts b/packages/sdk/src/evm/types/extension.ts new file mode 100644 index 00000000000..4bd4b963623 --- /dev/null +++ b/packages/sdk/src/evm/types/extension.ts @@ -0,0 +1,29 @@ +import { + ExtensionFunctionInput, + ExtensionInput, + ExtensionMetadataInput, +} from "../schema/contracts/common/extension"; +import { z } from "zod"; + +/** + * Input model to pass an extension's metadata -- name, metadataURI, implementation address + * @public + */ +export type ExtensionMetadata = z.input; + +/** + * Input model to pass a list function-selectors + * @public + */ +export type ExtensionFunction = z.input; + +/** + * Input model to pass a list of extension-addresses + function-selectors + * @public + */ +export type Extension = z.input; + +export type FunctionInput = { + functionSelector?: string; + functionSignature?: string; +}; diff --git a/packages/sdk/test/evm/custom.test.ts b/packages/sdk/test/evm/custom.test.ts index 55e98565b97..dd16d953105 100644 --- a/packages/sdk/test/evm/custom.test.ts +++ b/packages/sdk/test/evm/custom.test.ts @@ -266,7 +266,7 @@ describe("Custom Contracts", async () => { try { c.roles.get("admin"); } catch (e) { - expectError(e, "contract does not implement the 'permissions' Extension"); + expectError(e, "contract does not implement the 'Permissions' Extension"); } }); diff --git a/packages/sdk/test/evm/edition.test.ts b/packages/sdk/test/evm/edition.test.ts index b68a30007dc..8c766ab39ee 100644 --- a/packages/sdk/test/evm/edition.test.ts +++ b/packages/sdk/test/evm/edition.test.ts @@ -257,4 +257,34 @@ describe("Edition Contract", async () => { i++; }); }); + + it("getOwned() should not return the ERC1155 if the supply is zero", async () => { + // According to the code: .filter((b) => b.balance.gt(0)); + await bundleContract.mintBatch([ + { + metadata: { + name: "To be burned", + }, + supply: 5, + }, + { + metadata: { + name: "Test1", + }, + supply: 5, + }, + ]); + + // Burn the tokenId #0 + await bundleContract.burn(0, 5); + const nfts = await bundleContract.getOwned(adminWallet.address); + + // Only the editions from tokenId#1 should be returned + // since the editions from #0 were burned + expect(nfts).to.be.an("array").length(1); + expect(nfts[0].owner).to.be.equal(adminWallet.address); + expect(nfts[0].quantityOwned).to.be.equal("5"); + expect(nfts[0].supply).to.be.equal("5"); + expect(nfts[0].metadata.id).to.be.equal("1"); + }); }); diff --git a/packages/sdk/test/evm/extension-manager.test.ts b/packages/sdk/test/evm/extension-manager.test.ts new file mode 100644 index 00000000000..0f37d61ae7d --- /dev/null +++ b/packages/sdk/test/evm/extension-manager.test.ts @@ -0,0 +1,281 @@ +import { assert } from "chai"; +import { AbiSchema, SmartContract, isExtensionEnabled } from "../../src/evm"; +import { jsonProvider, sdk, signers } from "./before-setup"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { + CoreRouter__factory, + OffersLogic__factory, +} from "@thirdweb-dev/contracts-js"; +import { deployContractAndUploadMetadata } from "./utils"; +import { Extension, ExtensionFunction } from "../../src/evm/types/extension"; +import { generateExtensionFunctions } from "../../src/evm/common/plugin/generatePluginFunctions"; +import invariant from "tiny-invariant"; + +describe("Base Router for Dynamic Contracts", async () => { + let adminWallet: SignerWithAddress; + let coreRouterAddress: string; + let coreRouter: SmartContract; + + beforeEach(async () => { + await jsonProvider.send("hardhat_reset", []); + [adminWallet] = signers; + + // deploy base router + coreRouterAddress = await deployContractAndUploadMetadata( + CoreRouter__factory.abi, + CoreRouter__factory.bytecode, + adminWallet, + [adminWallet.address, []], + ); + + // instantiate custom contract + coreRouter = await sdk.getContract(coreRouterAddress); + }); + + it("should detect base router in a dynamic contract", async () => { + const contract = await sdk.getContract(coreRouterAddress); + assert(isExtensionEnabled(contract.abi, "DynamicContract")); + }); + + it("should add extensions", async () => { + // deploy an extension contract + // Offers + const offersLogicAddress = await deployContractAndUploadMetadata( + OffersLogic__factory.abi, + OffersLogic__factory.bytecode, + adminWallet, + [], + ); + + // generate functions array for the extension + const extensionFunctions: ExtensionFunction[] = generateExtensionFunctions( + AbiSchema.parse(OffersLogic__factory.abi), + ); + + const routerInput: Extension = { + metadata: { + name: "OffersLogic", + metadataURI: "", + implementation: offersLogicAddress, + }, + functions: extensionFunctions, + }; + + // add extension + await coreRouter.extensions.add({ extension: routerInput }); + + // read extension + const extensions = await coreRouter.extensions.getAll(); + assert(extensions.length === 1); + assert(extensions[0].metadata.name === "OffersLogic"); + + // call function on extension + const totalOffers = await coreRouter.offers.getTotalCount(); + assert(totalOffers.eq(0)); + }); + + it("should add extensions with abi", async () => { + // deploy an extension contract + // Offers + const offersLogicAddress = await deployContractAndUploadMetadata( + OffersLogic__factory.abi, + OffersLogic__factory.bytecode, + adminWallet, + [], + ); + + // add extension + await coreRouter.extensions.addDeployed({ + extensionName: "OffersLogic", + extensionAddress: offersLogicAddress, + extensionAbi: OffersLogic__factory.abi, + }); + + // read extension + const extensions = await coreRouter.extensions.getAll(); + assert(extensions.length === 1); + assert(extensions[0].metadata.name === "OffersLogic"); + }); + + it("should replace extensions", async () => { + // deploy an extension contract + // Offers + const offersLogicAddress = await deployContractAndUploadMetadata( + OffersLogic__factory.abi, + OffersLogic__factory.bytecode, + adminWallet, + [], + ); + + // add extension + await coreRouter.extensions.addDeployed({ + extensionName: "OffersLogic", + extensionAddress: offersLogicAddress, + extensionAbi: OffersLogic__factory.abi, + }); + + // read extension + let extensions = await coreRouter.extensions.getAll(); + assert(extensions.length === 1); + assert(extensions[0].metadata.name === "OffersLogic"); + assert(isExtensionEnabled(coreRouter.abi, "Offers")); + + // replace extension + const newAddress = await deployContractAndUploadMetadata( + OffersLogic__factory.abi, + OffersLogic__factory.bytecode, + adminWallet, + [], + ); + const newFunctions: ExtensionFunction[] = generateExtensionFunctions( + AbiSchema.parse(OffersLogic__factory.abi), + ); + + invariant(newFunctions, ""); + const routerInput: Extension = { + metadata: { + name: "OffersLogic", + metadataURI: "", + implementation: newAddress, + }, + functions: newFunctions, + }; + await coreRouter.extensions.replace({ extension: routerInput }); + + // read extension + extensions = await coreRouter.extensions.getAll(); + assert(extensions.length === 1); + assert(extensions[0].metadata.name === "OffersLogic"); + assert(extensions[0].metadata.implementation === newAddress); + + // call function on extension + const totalOffers = await coreRouter.offers.getTotalCount(); + assert(totalOffers.eq(0)); + }); + + it("should update extensions with abi", async () => { + // deploy an extension contract + // Offers + const offersLogicAddress = await deployContractAndUploadMetadata( + OffersLogic__factory.abi, + OffersLogic__factory.bytecode, + adminWallet, + [], + ); + + // add extension + await coreRouter.extensions.addDeployed({ + extensionName: "OffersLogic", + extensionAddress: offersLogicAddress, + extensionAbi: OffersLogic__factory.abi, + }); + + // read extension + let extensions = await coreRouter.extensions.getAll(); + const oldURI = extensions[0].metadata.metadataURI; + assert(extensions.length === 1); + assert(extensions[0].metadata.name === "OffersLogic"); + assert(isExtensionEnabled(coreRouter.abi, "Offers")); + + // update extension + const newAddress = await deployContractAndUploadMetadata( + OffersLogic__factory.abi, + OffersLogic__factory.bytecode, + adminWallet, + [], + ); + await coreRouter.extensions.replaceDeployed({ + extensionName: "OffersLogic", + extensionAddress: newAddress, + extensionMetadata: { name: "OffersLogicV2" }, + }); + + // read extension + extensions = await coreRouter.extensions.getAll(); + assert(extensions.length === 1); + assert(extensions[0].metadata.name === "OffersLogic"); + assert(extensions[0].metadata.implementation === newAddress); + assert(extensions[0].metadata.metadataURI !== oldURI); + assert(isExtensionEnabled(coreRouter.abi, "Offers")); + }); + + it("should remove extensions", async () => { + // deploy an extension contract + // Offers + const offersLogicAddress = await deployContractAndUploadMetadata( + OffersLogic__factory.abi, + OffersLogic__factory.bytecode, + adminWallet, + [], + ); + + const extensionFunctions: ExtensionFunction[] = generateExtensionFunctions( + AbiSchema.parse(OffersLogic__factory.abi), + ); + + const routerInput: Extension = { + metadata: { + name: "OffersLogic", + metadataURI: "", + implementation: offersLogicAddress, + }, + functions: extensionFunctions, + }; + + // add extension + await coreRouter.extensions.add({ extension: routerInput }); + + // read extension + let extensions = await coreRouter.extensions.getAll(); + assert(extensions.length === 1); + assert(extensions[0].metadata.name === "OffersLogic"); + assert(isExtensionEnabled(coreRouter.abi, "Offers")); + + // remove extension + await coreRouter.extensions.remove({ + extensionName: routerInput.metadata.name, + }); + + // read extension + extensions = await coreRouter.extensions.getAll(); + assert(extensions.length === 0); + + // call function on extension + assert(!isExtensionEnabled(coreRouter.abi, "Offers")); + }); + + it("should get extension for function", async () => { + // deploy an extension contract + // Offers + const offersLogicAddress = await deployContractAndUploadMetadata( + OffersLogic__factory.abi, + OffersLogic__factory.bytecode, + adminWallet, + [], + ); + + // generate functions array for the extension + const extensionFunctions: ExtensionFunction[] = generateExtensionFunctions( + AbiSchema.parse(OffersLogic__factory.abi), + ); + + const routerInput: Extension = { + metadata: { + name: "OffersLogic", + metadataURI: "", + implementation: offersLogicAddress, + }, + functions: extensionFunctions, + }; + + // add extension + await coreRouter.extensions.add({ extension: routerInput }); + + // read extension + const extension = await coreRouter.extensions.getExtensionForFunction({ + functionSignature: "acceptOffer(uint256)", + }); + assert(extension.implementation === offersLogicAddress); + assert(extension.name === "OffersLogic"); + }); +}); diff --git a/packages/service-utils/CHANGELOG.md b/packages/service-utils/CHANGELOG.md index 219f200e3e9..76160ecbef3 100644 --- a/packages/service-utils/CHANGELOG.md +++ b/packages/service-utils/CHANGELOG.md @@ -1,5 +1,11 @@ # @thirdweb-dev/service-utils +## 0.4.6 + +### Patch Changes + +- [#1729](https://github.com/thirdweb-dev/js/pull/1729) [`52b1a0fc`](https://github.com/thirdweb-dev/js/commit/52b1a0fcacb136e8da417d883e5365f2ab558d59) Thanks [@arcoraven](https://github.com/arcoraven)! - Make accountId optional in publishUsageEvents. This will be hydrated internally. + ## 0.4.5 ### Patch Changes diff --git a/packages/service-utils/package.json b/packages/service-utils/package.json index 8ffa9d1087b..49152a8721c 100644 --- a/packages/service-utils/package.json +++ b/packages/service-utils/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/service-utils", - "version": "0.4.5", + "version": "0.4.6", "main": "dist/thirdweb-dev-service-utils.cjs.js", "module": "dist/thirdweb-dev-service-utils.esm.js", "exports": { diff --git a/packages/service-utils/src/cf-worker/usage.ts b/packages/service-utils/src/cf-worker/usage.ts index 7990e454eba..86329466292 100644 --- a/packages/service-utils/src/cf-worker/usage.ts +++ b/packages/service-utils/src/cf-worker/usage.ts @@ -21,11 +21,16 @@ const usageEventSchema = z.object({ "bundler", "paymaster", "relayer", + "connectWallet", ]), action: z.string(), - accountId: z.string(), - // Optional + /** + * The following fields are optional. + */ + + accountId: z.string().optional(), + isClientEvent: z.boolean().optional(), apiKeyId: z.string().optional(), creatorWalletAddress: z.string().optional(), clientId: z.string().optional(), diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md index c1af7e5ddd7..e9c281c75af 100644 --- a/packages/storage/CHANGELOG.md +++ b/packages/storage/CHANGELOG.md @@ -1,5 +1,11 @@ # @thirdweb-dev/storage +## 1.2.11 + +### Patch Changes + +- [#1701](https://github.com/thirdweb-dev/js/pull/1701) [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Detect webGL platform + ## 1.2.10 ### Patch Changes diff --git a/packages/storage/package.json b/packages/storage/package.json index b86927d561d..6d0ac031418 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/storage", - "version": "1.2.10", + "version": "1.2.11", "main": "dist/thirdweb-dev-storage.cjs.js", "module": "dist/thirdweb-dev-storage.esm.js", "browser": { diff --git a/packages/storage/src/core/downloaders/storage-downloader.ts b/packages/storage/src/core/downloaders/storage-downloader.ts index e66bae62ac8..616bed225bd 100644 --- a/packages/storage/src/core/downloaders/storage-downloader.ts +++ b/packages/storage/src/core/downloaders/storage-downloader.ts @@ -131,7 +131,9 @@ export class StorageDownloader implements IStorageDownloader { headers["x-sdk-platform"] = bundleId ? "react-native" : isBrowser() - ? "browser" + ? (window as any).bridge !== undefined + ? "webGL" + : "browser" : "node"; } diff --git a/packages/storage/src/core/uploaders/ipfs-uploader.ts b/packages/storage/src/core/uploaders/ipfs-uploader.ts index 77b80cc33da..6be31b9ebf9 100644 --- a/packages/storage/src/core/uploaders/ipfs-uploader.ts +++ b/packages/storage/src/core/uploaders/ipfs-uploader.ts @@ -286,7 +286,13 @@ export class IpfsUploader implements IStorageUploader { xhr.setRequestHeader("x-sdk-name", pkg.name); xhr.setRequestHeader( "x-sdk-platform", - bundleId ? "react-native" : isBrowser() ? "browser" : "node", + bundleId + ? "react-native" + : isBrowser() + ? (window as any).bridge !== undefined + ? "webGL" + : "browser" + : "node", ); // if we have a authorization token on global context then add that to the headers, this is for the dashboard. diff --git a/packages/unity-js-bridge/CHANGELOG.md b/packages/unity-js-bridge/CHANGELOG.md index 546230e5f49..1c6cb711d40 100644 --- a/packages/unity-js-bridge/CHANGELOG.md +++ b/packages/unity-js-bridge/CHANGELOG.md @@ -1,5 +1,37 @@ # @thirdweb-dev/unity-js-bridge +## 0.2.85 + +### Patch Changes + +- [#1725](https://github.com/thirdweb-dev/js/pull/1725) [`dc38a243`](https://github.com/thirdweb-dev/js/commit/dc38a243c201d0070d75dd36b1a4f8d1eecbc438) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - [Unity] Fix google popup blocker + +- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]: + - @thirdweb-dev/sdk@3.10.67 + - @thirdweb-dev/wallets@1.3.5 + - @thirdweb-dev/auth@3.2.48 + +## 0.2.84 + +### Patch Changes + +- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c)]: + - @thirdweb-dev/wallets@1.3.4 + - @thirdweb-dev/sdk@3.10.66 + - @thirdweb-dev/auth@3.2.47 + +## 0.2.83 + +### Patch Changes + +- [#1688](https://github.com/thirdweb-dev/js/pull/1688) [`fde77a7e`](https://github.com/thirdweb-dev/js/commit/fde77a7e19a03b8c932785ec11a01710218c8acc) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - [Unity] Google login option for EWS + +- Updated dependencies [[`6d3d76cf`](https://github.com/thirdweb-dev/js/commit/6d3d76cff8018015faa191a1f8bd4f34506a6650), [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9), [`15fe4779`](https://github.com/thirdweb-dev/js/commit/15fe4779f4b99e51afe214ac3ebb31f611089787)]: + - @thirdweb-dev/wallets@1.3.3 + - @thirdweb-dev/storage@1.2.11 + - @thirdweb-dev/sdk@3.10.65 + - @thirdweb-dev/auth@3.2.46 + ## 0.2.82 ### Patch Changes diff --git a/packages/unity-js-bridge/package.json b/packages/unity-js-bridge/package.json index 109b27c230f..1a89ffd89b9 100644 --- a/packages/unity-js-bridge/package.json +++ b/packages/unity-js-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/unity-js-bridge", - "version": "0.2.82", + "version": "0.2.85", "main": "dist/thirdweb-unity-bridge.js", "repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge", "license": "Apache-2.0", diff --git a/packages/unity-js-bridge/src/thirdweb-bridge.ts b/packages/unity-js-bridge/src/thirdweb-bridge.ts index a4146725e18..874b858f561 100644 --- a/packages/unity-js-bridge/src/thirdweb-bridge.ts +++ b/packages/unity-js-bridge/src/thirdweb-bridge.ts @@ -77,6 +77,7 @@ interface TWBridge { password?: string, email?: string, personalWallet?: PossibleWallet, + useGoogle?: string, ) => Promise; disconnect: () => Promise; switchNetwork: (chainId: string) => Promise; @@ -275,6 +276,7 @@ class ThirdwebBridge implements TWBridge { password?: string, email?: string, personalWallet: PossibleWallet = "localWallet", + useGoogle?: string, ) { if (!this.activeSDK) { throw new Error("SDK not initialized"); @@ -296,14 +298,30 @@ class ThirdwebBridge implements TWBridge { await magicLinkWallet.connect({ chainId: chainIdNumber, email: email }); } else if (walletInstance.walletId === walletIds.embeddedWallet) { const embeddedWallet = walletInstance as EmbeddedWallet; - if (!email) { - throw new Error("Email is required for EmbeddedWallet"); + + if (useGoogle?.toLowerCase() === "true") { + const googleWindow = this.openGoogleSignInWindow(); + if (!googleWindow) { + throw new Error("Failed to open google login window"); + } + await embeddedWallet.connect({ + chainId: chainIdNumber, + loginType: "headless_google_oauth", + openedWindow: googleWindow, + closeOpenedWindow: (openedWindow) => { + openedWindow.close(); + }, + }); + } else { + if (!email) { + throw new Error("Email is required for EmbeddedWallet"); + } + await embeddedWallet.connect({ + chainId: chainIdNumber, + email: email, + loginType: "ui_email_otp", + }); } - await embeddedWallet.connect({ - chainId: chainIdNumber, - email: email, - loginType: "ui_email_otp", - }); } else if (walletInstance.walletId === walletIds.paper) { const paperWallet = walletInstance as PaperWallet; if (!email) { @@ -623,6 +641,72 @@ class ThirdwebBridge implements TWBridge { }); return localWallet; } + + public openGoogleSignInWindow() { + const win = window.open("", undefined, "width=350, height=500"); + if (win) { + win.document.title = "Sign In - Google Accounts"; + win.document.body.innerHTML = ` + + + + + + `; + } + return win; + } } // add the bridge to the window object type diff --git a/packages/wallets/CHANGELOG.md b/packages/wallets/CHANGELOG.md index b3e5214dfa6..88073e500c6 100644 --- a/packages/wallets/CHANGELOG.md +++ b/packages/wallets/CHANGELOG.md @@ -1,5 +1,45 @@ # @thirdweb-dev/wallets +## 1.3.5 + +### Patch Changes + +- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]: + - @thirdweb-dev/sdk@3.10.67 + +## 1.3.4 + +### Patch Changes + +- [#1712](https://github.com/thirdweb-dev/js/pull/1712) [`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - New SmartWallet API for session keys + + You can now add admins and scoped session keys to smart wallets directly with a simple API: + + ``` + const smartWallet = new SmartWallet(config); + await smartWallet.connect({ personalWallet }) + + await smartWallet.createSessionKey(keyAddress, permissions); + await smartWallet.revokeSessionKey(keyAddress); + + await smartWallet.addAdmin(adminAddress); + await smartWallet.removeAdmin(adminAddress); + ``` + +- Updated dependencies [[`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c)]: + - @thirdweb-dev/sdk@3.10.66 + +## 1.3.3 + +### Patch Changes + +- [#1707](https://github.com/thirdweb-dev/js/pull/1707) [`6d3d76cf`](https://github.com/thirdweb-dev/js/commit/6d3d76cff8018015faa191a1f8bd4f34506a6650) Thanks [@MananTank](https://github.com/MananTank)! - Fix Unchecked Index accesses + +- [#1701](https://github.com/thirdweb-dev/js/pull/1701) [`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Detect webGL platform + +- Updated dependencies [[`04f2f7b8`](https://github.com/thirdweb-dev/js/commit/04f2f7b8ff5f19345d868fc515a24ccd6ffd0ab9), [`15fe4779`](https://github.com/thirdweb-dev/js/commit/15fe4779f4b99e51afe214ac3ebb31f611089787)]: + - @thirdweb-dev/sdk@3.10.65 + ## 1.3.2 ### Patch Changes diff --git a/packages/wallets/package.json b/packages/wallets/package.json index e93c5ec4472..70630095d3c 100644 --- a/packages/wallets/package.json +++ b/packages/wallets/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/wallets", - "version": "1.3.2", + "version": "1.3.5", "main": "dist/thirdweb-dev-wallets.cjs.js", "module": "dist/thirdweb-dev-wallets.esm.js", "types": "dist/thirdweb-dev-wallets.cjs.d.ts", diff --git a/packages/wallets/src/core/WalletConnect/WalletConnectV2Handler.ts b/packages/wallets/src/core/WalletConnect/WalletConnectV2Handler.ts index bae53366fda..3d3ffc9a41a 100644 --- a/packages/wallets/src/core/WalletConnect/WalletConnectV2Handler.ts +++ b/packages/wallets/src/core/WalletConnect/WalletConnectV2Handler.ts @@ -57,7 +57,7 @@ export class WalletConnectV2Handler extends WalletConnectHandler { const sessions = this.#wcWallet.getActiveSessions(); const keys = Object.keys(sessions); - if (keys.length > 0) { + if (keys[0]) { this.#session = sessions[keys[0]]; } @@ -90,19 +90,22 @@ export class WalletConnectV2Handler extends WalletConnectHandler { const namespaces: SessionTypes.Namespaces = {}; Object.keys(requiredNamespaces).forEach((key) => { const accounts: string[] = []; - requiredNamespaces[key].chains?.map((chain: string) => { - accounts.push(`${chain}:${account}`); - }); - namespaces[key] = { - accounts, - methods: requiredNamespaces[key].methods, - events: requiredNamespaces[key].events, - }; + const namespace = requiredNamespaces[key]; + if (namespace) { + namespace.chains?.map((chain: string) => { + accounts.push(`${chain}:${account}`); + }); + namespaces[key] = { + accounts, + methods: namespace.methods, + events: namespace.events, + }; + } }); this.#session = await this.#wcWallet.approveSession({ id, - relayProtocol: relays[0].protocol, + relayProtocol: relays[0]?.protocol, namespaces, }); @@ -142,7 +145,7 @@ export class WalletConnectV2Handler extends WalletConnectHandler { case EIP155_SIGNING_METHODS.PERSONAL_SIGN: case EIP155_SIGNING_METHODS.ETH_SIGN: const message = this.#getSignParamsMessage(request.params); - const signedMessage = await wallet.signMessage(message); + const signedMessage = await wallet.signMessage(message || ""); // TODO: handle empty message response = formatJsonRpcResult(id, signedMessage); break; @@ -224,15 +227,19 @@ export class WalletConnectV2Handler extends WalletConnectHandler { const thisSessions = []; for (const sessionKey of sessionKeys) { - const topic = sessions[sessionKey].topic; - const peerMeta = sessions[sessionKey].peer.metadata; - - thisSessions.push({ - topic, - peer: { - metadata: peerMeta, - }, - }); + const session = sessions[sessionKey]; + + if (session) { + const topic = session.topic; + const peerMeta = session.peer.metadata; + + thisSessions.push({ + topic, + peer: { + metadata: peerMeta, + }, + }); + } } return thisSessions; @@ -346,7 +353,7 @@ export class WalletConnectV2Handler extends WalletConnectHandler { * If it is a hex string, it gets converted to utf8 string */ #getSignParamsMessage(params: string[]) { - const message = params.filter((p) => !utils.isAddress(p))[0]; + const message = params.filter((p) => !utils.isAddress(p))[0] || ""; // TODO: handle empty message if (utils.isHexString(message)) { return utils.toUtf8String(message); diff --git a/packages/wallets/src/evm/connectors/blocto/index.ts b/packages/wallets/src/evm/connectors/blocto/index.ts index d3353397dfc..7833861645c 100644 --- a/packages/wallets/src/evm/connectors/blocto/index.ts +++ b/packages/wallets/src/evm/connectors/blocto/index.ts @@ -114,7 +114,7 @@ export class BloctoConnector extends WagmiConnector< getProvider({ chainId }: { chainId?: number } = {}): Promise { if (!this.#provider) { const _chainId = - chainId ?? this.options.chainId ?? this.chains[0].chainId; + chainId ?? this.options.chainId ?? this.chains[0]?.chainId ?? 1; const _rpc = this.chains.find((x) => x.chainId === _chainId)?.rpc[0]; this.#provider = new BloctoSDK({ diff --git a/packages/wallets/src/evm/connectors/embedded-wallet/index.ts b/packages/wallets/src/evm/connectors/embedded-wallet/index.ts index 463fe28dbc5..d50a6ce5016 100644 --- a/packages/wallets/src/evm/connectors/embedded-wallet/index.ts +++ b/packages/wallets/src/evm/connectors/embedded-wallet/index.ts @@ -150,7 +150,7 @@ export class EmbeddedWalletConnector extends Connector c.chainId === chainId); if (chain) { options.network = { - rpcUrl: chain.rpc[0], + rpcUrl: chain.rpc[0] || "", // TODO handle empty RPC array chainId: chain.chainId, }; } diff --git a/packages/wallets/src/evm/connectors/paper/index.ts b/packages/wallets/src/evm/connectors/paper/index.ts index 03201fb0017..bb1f01f6483 100644 --- a/packages/wallets/src/evm/connectors/paper/index.ts +++ b/packages/wallets/src/evm/connectors/paper/index.ts @@ -191,7 +191,7 @@ export class PaperWalletConnector extends Connector> { } const signer = await this.user?.wallet.getEthersJsSigner({ - rpcEndpoint: this.options.chain.rpc[0], + rpcEndpoint: this.options.chain.rpc[0] || "", // TODO: handle chain.rpc being empty array }); if (!signer) { @@ -218,7 +218,7 @@ export class PaperWalletConnector extends Connector> { // update signer this.#signer = await this.user?.wallet.getEthersJsSigner({ - rpcEndpoint: chain.rpc[0], + rpcEndpoint: chain.rpc[0] || "", // TODO: handle chain.rpc being empty array }); this.emit("change", { chain: { id: chainId, unsupported: false } }); diff --git a/packages/wallets/src/evm/connectors/safe/index.ts b/packages/wallets/src/evm/connectors/safe/index.ts index 925dcfe6ead..430e5a57e1f 100644 --- a/packages/wallets/src/evm/connectors/safe/index.ts +++ b/packages/wallets/src/evm/connectors/safe/index.ts @@ -259,7 +259,9 @@ export class SafeConnector extends Connector { if (accounts.length === 0) { this.emit("disconnect"); } else { - this.emit("change", { account: ethers.utils.getAddress(accounts[0]) }); + if (accounts[0]) { + this.emit("change", { account: ethers.utils.getAddress(accounts[0]) }); + } } } diff --git a/packages/wallets/src/evm/connectors/smart-wallet/index.ts b/packages/wallets/src/evm/connectors/smart-wallet/index.ts index d837065d1ad..8092f66f600 100644 --- a/packages/wallets/src/evm/connectors/smart-wallet/index.ts +++ b/packages/wallets/src/evm/connectors/smart-wallet/index.ts @@ -17,6 +17,8 @@ import { BigNumber, ethers, providers } from "ethers"; import { ChainOrRpcUrl, getChainProvider, + SignerPermissionsInput, + SignerWithPermissions, SmartContract, ThirdwebSDK, Transaction, @@ -152,7 +154,11 @@ export class SmartWalletConnector extends Connector { (item) => ethers.utils.getAddress(item.signer) === ethers.utils.getAddress(signerAddress), - )[0].permissions; + )[0]?.permissions; + + if (!restrictions) { + return false; + } return restrictions.approvedCallTargets.includes(transaction.getTarget()); } @@ -220,10 +226,13 @@ export class SmartWalletConnector extends Connector { throw new Error("Smart wallet already deployed"); } const signer = await this.getSigner(); - const tx = await signer.sendTransaction({ - to: await signer.getAddress(), - data: "0x", - }); + const tx = await signer.sendTransaction( + { + to: await signer.getAddress(), + data: "0x", + }, + true, // batched tx flag to avoid hitting the Router fallback method + ); const receipt = await tx.wait(); return { receipt }; } @@ -239,6 +248,65 @@ export class SmartWalletConnector extends Connector { return await this.accountApi.isAcountDeployed(); } + async deployIfNeeded(): Promise { + const isDeployed = await this.isDeployed(); + if (!isDeployed) { + await this.deploy(); + } + } + + async grantPermissions( + target: string, + permissions: SignerPermissionsInput, + ): Promise { + await this.deployIfNeeded(); + const accountContract = await this.getAccountContract(); + return accountContract.account.grantPermissions(target, permissions); + } + + async revokePermissions(target: string): Promise { + await this.deployIfNeeded(); + const accountContract = await this.getAccountContract(); + return accountContract.account.revokeAccess(target); + } + + async addAdmin(target: string): Promise { + await this.deployIfNeeded(); + const accountContract = await this.getAccountContract(); + return accountContract.account.grantAdminPermissions(target); + } + + async removeAdmin(target: string): Promise { + await this.deployIfNeeded(); + const accountContract = await this.getAccountContract(); + return accountContract.account.revokeAdminPermissions(target); + } + + async getAllActiveSigners(): Promise { + const isDeployed = await this.isDeployed(); + if (isDeployed) { + const accountContract = await this.getAccountContract(); + return accountContract.account.getAllAdminsAndSigners(); + } else { + const personalWallet = await this.personalWallet?.getSigner(); + if (!personalWallet) { + throw new Error("Personal wallet not connected"); + } + return [ + { + isAdmin: true, + signer: await personalWallet.getAddress(), + permissions: { + startDate: new Date(0), + expirationDate: new Date(0), + nativeTokenLimitPerTransaction: BigNumber.from(0), + approvedCallTargets: [], + }, + }, + ]; + } + } + /** * Get the underlying account contract of the smart wallet. * @returns the account contract of the smart wallet. diff --git a/packages/wallets/src/evm/connectors/smart-wallet/lib/account.ts b/packages/wallets/src/evm/connectors/smart-wallet/lib/account.ts index baf21dbe4ff..0fb7a270b04 100644 --- a/packages/wallets/src/evm/connectors/smart-wallet/lib/account.ts +++ b/packages/wallets/src/evm/connectors/smart-wallet/lib/account.ts @@ -2,7 +2,7 @@ import { LOCAL_NODE_PKEY, SmartContract, ThirdwebSDK } from "@thirdweb-dev/sdk"; import { BigNumberish, BigNumber, ethers, utils } from "ethers"; import { AccountApiParams } from "../types"; import { BaseAccountAPI } from "./base-api"; -import { MINIMAL_ACCOUNT_ABI } from "./constants"; +import { ACCOUNT_CORE_ABI } from "./constants"; export class AccountAPI extends BaseAccountAPI { sdk: ThirdwebSDK; @@ -42,7 +42,7 @@ export class AccountAPI extends BaseAccountAPI { } else { this.accountContract = await this.sdk.getContract( await this.getAccountAddress(), - MINIMAL_ACCOUNT_ABI, + ACCOUNT_CORE_ABI, ); } } diff --git a/packages/wallets/src/evm/connectors/smart-wallet/lib/base-api.ts b/packages/wallets/src/evm/connectors/smart-wallet/lib/base-api.ts index e4b4dffaa4a..27093b9c0f6 100644 --- a/packages/wallets/src/evm/connectors/smart-wallet/lib/base-api.ts +++ b/packages/wallets/src/evm/connectors/smart-wallet/lib/base-api.ts @@ -368,8 +368,9 @@ export abstract class BaseAccountAPI { ...userOp, paymasterAndData: "0x", }; - modifiedOp.preVerificationGas = - await this.getPreVerificationGas(modifiedOp); + modifiedOp.preVerificationGas = await this.getPreVerificationGas( + modifiedOp, + ); return { ...modifiedOp, signature: "", @@ -420,7 +421,7 @@ export abstract class BaseAccountAPI { const events = await this.entryPointView.queryFilter( this.entryPointView.filters.UserOperationEvent(userOpHash), ); - if (events.length > 0) { + if (events[0]) { return events[0].transactionHash; } await new Promise((resolve) => setTimeout(resolve, interval)); diff --git a/packages/wallets/src/evm/connectors/smart-wallet/lib/constants.ts b/packages/wallets/src/evm/connectors/smart-wallet/lib/constants.ts index 07bf742b0c4..45363e4d016 100644 --- a/packages/wallets/src/evm/connectors/smart-wallet/lib/constants.ts +++ b/packages/wallets/src/evm/connectors/smart-wallet/lib/constants.ts @@ -1,63 +1,985 @@ export const ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; // v0.6 -export const MINIMAL_ACCOUNT_ABI = [ +export const ACCOUNT_CORE_ABI = [ { + type: "constructor", + name: "", inputs: [ { + type: "address", + name: "_entrypoint", + internalType: "contract IEntryPoint", + }, + { + type: "address", + name: "_factory", internalType: "address", - name: "_target", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "AdminUpdated", + inputs: [ + { type: "address", + name: "signer", + indexed: true, + internalType: "address", + }, + { + type: "bool", + name: "isAdmin", + indexed: false, + internalType: "bool", + }, + ], + outputs: [], + anonymous: false, + }, + { + type: "event", + name: "ContractURIUpdated", + inputs: [ + { + type: "string", + name: "prevURI", + indexed: false, + internalType: "string", + }, + { + type: "string", + name: "newURI", + indexed: false, + internalType: "string", + }, + ], + outputs: [], + anonymous: false, + }, + { + type: "event", + name: "Initialized", + inputs: [ + { + type: "uint8", + name: "version", + indexed: false, + internalType: "uint8", + }, + ], + outputs: [], + anonymous: false, + }, + { + type: "event", + name: "SignerPermissionsUpdated", + inputs: [ + { + type: "address", + name: "authorizingSigner", + indexed: true, + internalType: "address", + }, + { + type: "address", + name: "targetSigner", + indexed: true, + internalType: "address", + }, + { + type: "tuple", + name: "permissions", + components: [ + { + type: "address", + name: "signer", + internalType: "address", + }, + { + type: "address[]", + name: "approvedTargets", + internalType: "address[]", + }, + { + type: "uint256", + name: "nativeTokenLimitPerTransaction", + internalType: "uint256", + }, + { + type: "uint128", + name: "permissionStartTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "permissionEndTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "reqValidityStartTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "reqValidityEndTimestamp", + internalType: "uint128", + }, + { + type: "bytes32", + name: "uid", + internalType: "bytes32", + }, + ], + indexed: false, + internalType: "struct IAccountPermissions.SignerPermissionRequest", + }, + ], + outputs: [], + anonymous: false, + }, + { + type: "function", + name: "addDeposit", + inputs: [], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "contractURI", + inputs: [], + outputs: [ + { + type: "string", + name: "", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "entryPoint", + inputs: [], + outputs: [ + { + type: "address", + name: "", + internalType: "contract IEntryPoint", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "execute", + inputs: [ + { + type: "address", + name: "_target", + internalType: "address", }, { - internalType: "uint256", - name: "_value", type: "uint256", + name: "_value", + internalType: "uint256", }, { - internalType: "bytes", - name: "_calldata", type: "bytes", + name: "_calldata", + internalType: "bytes", }, ], - name: "execute", outputs: [], stateMutability: "nonpayable", - type: "function", }, { + type: "function", + name: "executeBatch", inputs: [ { - internalType: "address[]", - name: "_target", type: "address[]", + name: "_target", + internalType: "address[]", }, { - internalType: "uint256[]", - name: "_value", type: "uint256[]", + name: "_value", + internalType: "uint256[]", }, { - internalType: "bytes[]", - name: "_calldata", type: "bytes[]", + name: "_calldata", + internalType: "bytes[]", }, ], - name: "executeBatch", outputs: [], stateMutability: "nonpayable", + }, + { type: "function", + name: "factory", + inputs: [], + outputs: [ + { + type: "address", + name: "", + internalType: "address", + }, + ], + stateMutability: "view", }, { + type: "function", + name: "getAllActiveSigners", + inputs: [], + outputs: [ + { + type: "tuple[]", + name: "signers", + components: [ + { + type: "address", + name: "signer", + internalType: "address", + }, + { + type: "address[]", + name: "approvedTargets", + internalType: "address[]", + }, + { + type: "uint256", + name: "nativeTokenLimitPerTransaction", + internalType: "uint256", + }, + { + type: "uint128", + name: "startTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "endTimestamp", + internalType: "uint128", + }, + ], + internalType: "struct IAccountPermissions.SignerPermissions[]", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getAllAdmins", + inputs: [], + outputs: [ + { + type: "address[]", + name: "", + internalType: "address[]", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getAllSigners", + inputs: [], + outputs: [ + { + type: "tuple[]", + name: "signers", + components: [ + { + type: "address", + name: "signer", + internalType: "address", + }, + { + type: "address[]", + name: "approvedTargets", + internalType: "address[]", + }, + { + type: "uint256", + name: "nativeTokenLimitPerTransaction", + internalType: "uint256", + }, + { + type: "uint128", + name: "startTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "endTimestamp", + internalType: "uint128", + }, + ], + internalType: "struct IAccountPermissions.SignerPermissions[]", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getDeposit", inputs: [], + outputs: [ + { + type: "uint256", + name: "", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", name: "getNonce", + inputs: [], + outputs: [ + { + type: "uint256", + name: "", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getPermissionsForSigner", + inputs: [ + { + type: "address", + name: "signer", + internalType: "address", + }, + ], + outputs: [ + { + type: "tuple", + name: "", + components: [ + { + type: "address", + name: "signer", + internalType: "address", + }, + { + type: "address[]", + name: "approvedTargets", + internalType: "address[]", + }, + { + type: "uint256", + name: "nativeTokenLimitPerTransaction", + internalType: "uint256", + }, + { + type: "uint128", + name: "startTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "endTimestamp", + internalType: "uint128", + }, + ], + internalType: "struct IAccountPermissions.SignerPermissions", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "initialize", + inputs: [ + { + type: "address", + name: "_defaultAdmin", + internalType: "address", + }, + { + type: "bytes", + name: "", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "isActiveSigner", + inputs: [ + { + type: "address", + name: "signer", + internalType: "address", + }, + ], + outputs: [ + { + type: "bool", + name: "", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "isAdmin", + inputs: [ + { + type: "address", + name: "_account", + internalType: "address", + }, + ], + outputs: [ + { + type: "bool", + name: "", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "isValidSignature", + inputs: [ + { + type: "bytes32", + name: "_hash", + internalType: "bytes32", + }, + { + type: "bytes", + name: "_signature", + internalType: "bytes", + }, + ], outputs: [ { + type: "bytes4", + name: "magicValue", + internalType: "bytes4", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "isValidSigner", + inputs: [ + { + type: "address", + name: "_signer", + internalType: "address", + }, + { + type: "tuple", + name: "_userOp", + components: [ + { + type: "address", + name: "sender", + internalType: "address", + }, + { + type: "uint256", + name: "nonce", + internalType: "uint256", + }, + { + type: "bytes", + name: "initCode", + internalType: "bytes", + }, + { + type: "bytes", + name: "callData", + internalType: "bytes", + }, + { + type: "uint256", + name: "callGasLimit", + internalType: "uint256", + }, + { + type: "uint256", + name: "verificationGasLimit", + internalType: "uint256", + }, + { + type: "uint256", + name: "preVerificationGas", + internalType: "uint256", + }, + { + type: "uint256", + name: "maxFeePerGas", + internalType: "uint256", + }, + { + type: "uint256", + name: "maxPriorityFeePerGas", + internalType: "uint256", + }, + { + type: "bytes", + name: "paymasterAndData", + internalType: "bytes", + }, + { + type: "bytes", + name: "signature", + internalType: "bytes", + }, + ], + internalType: "struct UserOperation", + }, + ], + outputs: [ + { + type: "bool", + name: "", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "multicall", + inputs: [ + { + type: "bytes[]", + name: "data", + internalType: "bytes[]", + }, + ], + outputs: [ + { + type: "bytes[]", + name: "results", + internalType: "bytes[]", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onERC1155BatchReceived", + inputs: [ + { + type: "address", + name: "", + internalType: "address", + }, + { + type: "address", + name: "", + internalType: "address", + }, + { + type: "uint256[]", + name: "", + internalType: "uint256[]", + }, + { + type: "uint256[]", + name: "", + internalType: "uint256[]", + }, + { + type: "bytes", + name: "", + internalType: "bytes", + }, + ], + outputs: [ + { + type: "bytes4", + name: "", + internalType: "bytes4", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onERC1155Received", + inputs: [ + { + type: "address", + name: "", + internalType: "address", + }, + { + type: "address", + name: "", + internalType: "address", + }, + { + type: "uint256", + name: "", + internalType: "uint256", + }, + { + type: "uint256", + name: "", internalType: "uint256", + }, + { + type: "bytes", + name: "", + internalType: "bytes", + }, + ], + outputs: [ + { + type: "bytes4", + name: "", + internalType: "bytes4", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onERC721Received", + inputs: [ + { + type: "address", + name: "", + internalType: "address", + }, + { + type: "address", name: "", + internalType: "address", + }, + { + type: "uint256", + name: "", + internalType: "uint256", + }, + { + type: "bytes", + name: "", + internalType: "bytes", + }, + ], + outputs: [ + { + type: "bytes4", + name: "", + internalType: "bytes4", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setAdmin", + inputs: [ + { + type: "address", + name: "_account", + internalType: "address", + }, + { + type: "bool", + name: "_isAdmin", + internalType: "bool", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setContractURI", + inputs: [ + { + type: "string", + name: "_uri", + internalType: "string", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setPermissionsForSigner", + inputs: [ + { + type: "tuple", + name: "_req", + components: [ + { + type: "address", + name: "signer", + internalType: "address", + }, + { + type: "address[]", + name: "approvedTargets", + internalType: "address[]", + }, + { + type: "uint256", + name: "nativeTokenLimitPerTransaction", + internalType: "uint256", + }, + { + type: "uint128", + name: "permissionStartTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "permissionEndTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "reqValidityStartTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "reqValidityEndTimestamp", + internalType: "uint128", + }, + { + type: "bytes32", + name: "uid", + internalType: "bytes32", + }, + ], + internalType: "struct IAccountPermissions.SignerPermissionRequest", + }, + { + type: "bytes", + name: "_signature", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + type: "bytes4", + name: "interfaceId", + internalType: "bytes4", + }, + ], + outputs: [ + { + type: "bool", + name: "", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "validateUserOp", + inputs: [ + { + type: "tuple", + name: "userOp", + components: [ + { + type: "address", + name: "sender", + internalType: "address", + }, + { + type: "uint256", + name: "nonce", + internalType: "uint256", + }, + { + type: "bytes", + name: "initCode", + internalType: "bytes", + }, + { + type: "bytes", + name: "callData", + internalType: "bytes", + }, + { + type: "uint256", + name: "callGasLimit", + internalType: "uint256", + }, + { + type: "uint256", + name: "verificationGasLimit", + internalType: "uint256", + }, + { + type: "uint256", + name: "preVerificationGas", + internalType: "uint256", + }, + { + type: "uint256", + name: "maxFeePerGas", + internalType: "uint256", + }, + { + type: "uint256", + name: "maxPriorityFeePerGas", + internalType: "uint256", + }, + { + type: "bytes", + name: "paymasterAndData", + internalType: "bytes", + }, + { + type: "bytes", + name: "signature", + internalType: "bytes", + }, + ], + internalType: "struct UserOperation", + }, + { + type: "bytes32", + name: "userOpHash", + internalType: "bytes32", + }, + { + type: "uint256", + name: "missingAccountFunds", + internalType: "uint256", + }, + ], + outputs: [ + { type: "uint256", + name: "validationData", + internalType: "uint256", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "verifySignerPermissionRequest", + inputs: [ + { + type: "tuple", + name: "req", + components: [ + { + type: "address", + name: "signer", + internalType: "address", + }, + { + type: "address[]", + name: "approvedTargets", + internalType: "address[]", + }, + { + type: "uint256", + name: "nativeTokenLimitPerTransaction", + internalType: "uint256", + }, + { + type: "uint128", + name: "permissionStartTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "permissionEndTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "reqValidityStartTimestamp", + internalType: "uint128", + }, + { + type: "uint128", + name: "reqValidityEndTimestamp", + internalType: "uint128", + }, + { + type: "bytes32", + name: "uid", + internalType: "bytes32", + }, + ], + internalType: "struct IAccountPermissions.SignerPermissionRequest", + }, + { + type: "bytes", + name: "signature", + internalType: "bytes", + }, + ], + outputs: [ + { + type: "bool", + name: "success", + internalType: "bool", + }, + { + type: "address", + name: "signer", + internalType: "address", }, ], stateMutability: "view", + }, + { type: "function", + name: "withdrawDepositTo", + inputs: [ + { + type: "address", + name: "withdrawAddress", + internalType: "address payable", + }, + { + type: "uint256", + name: "amount", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "receive", + name: "", + inputs: [], + outputs: [], + stateMutability: "payable", }, ]; diff --git a/packages/wallets/src/evm/connectors/smart-wallet/lib/http-rpc-client.ts b/packages/wallets/src/evm/connectors/smart-wallet/lib/http-rpc-client.ts index 60fed1ebca0..271db718f6f 100644 --- a/packages/wallets/src/evm/connectors/smart-wallet/lib/http-rpc-client.ts +++ b/packages/wallets/src/evm/connectors/smart-wallet/lib/http-rpc-client.ts @@ -74,7 +74,9 @@ export class HttpRpcClient { headers["x-sdk-platform"] = bundleId ? "react-native" : isBrowser() - ? "browser" + ? (window as any).bridge !== undefined + ? "webGL" + : "browser" : "node"; } diff --git a/packages/wallets/src/evm/connectors/wallet-connect/index.ts b/packages/wallets/src/evm/connectors/wallet-connect/index.ts index 0d779e383ad..c21054feef4 100644 --- a/packages/wallets/src/evm/connectors/wallet-connect/index.ts +++ b/packages/wallets/src/evm/connectors/wallet-connect/index.ts @@ -151,7 +151,7 @@ export class WalletConnectConnector extends WagmiConnector< // If session exists and chains are authorized, enable provider for required chain const accounts = await provider.enable(); - if (accounts.length === 0) { + if (!accounts[0]) { throw new Error("No accounts found on provider."); } const account = utils.getAddress(accounts[0]); @@ -206,7 +206,7 @@ export class WalletConnectConnector extends WagmiConnector< async getAccount() { const { accounts } = await this.getProvider(); - if (accounts.length === 0) { + if (!accounts[0]) { throw new Error("No accounts found on provider."); } return utils.getAddress(accounts[0]); @@ -282,8 +282,9 @@ export class WalletConnectConnector extends WagmiConnector< const isChainApproved = namespaceChains.includes(chainId); if (!isChainApproved && namespaceMethods.includes(ADD_ETH_CHAIN_METHOD)) { - const blockExplorerUrls = chain.explorers?.length - ? { blockExplorerUrls: [chain.explorers[0].url] } + const firstExplorer = chain.explorers && chain.explorers[0]; + const blockExplorerUrls = firstExplorer + ? { blockExplorerUrls: [firstExplorer.url] } : {}; await provider.request({ method: ADD_ETH_CHAIN_METHOD, @@ -354,7 +355,10 @@ export class WalletConnectConnector extends WagmiConnector< icons: [this.options.dappMetadata.logoUrl || ""], }, rpcMap: Object.fromEntries( - this.filteredChains.map((chain) => [chain.chainId, chain.rpc[0]]), + this.filteredChains.map((chain) => [ + chain.chainId, + chain.rpc[0] || "", // TODO: handle chain.rpc being empty array + ]), ), qrModalOptions: this.options.qrModalOptions, @@ -463,7 +467,9 @@ export class WalletConnectConnector extends WagmiConnector< if (accounts.length === 0) { this.emit("disconnect"); } else { - this.emit("change", { account: utils.getAddress(accounts[0]) }); + if (accounts[0]) { + this.emit("change", { account: utils.getAddress(accounts[0]) }); + } } }; diff --git a/packages/wallets/src/evm/constants/walletIds.ts b/packages/wallets/src/evm/constants/walletIds.ts index d2608f57d28..b05ee69c622 100644 --- a/packages/wallets/src/evm/constants/walletIds.ts +++ b/packages/wallets/src/evm/constants/walletIds.ts @@ -15,4 +15,4 @@ export const walletIds = { walletConnect: "walletConnect", phantom: "phantom", // add new ids sorted alphabetically -}; +} as const; diff --git a/packages/wallets/src/evm/wallets/blocto.ts b/packages/wallets/src/evm/wallets/blocto.ts index f88fc3b4f51..e4ef59db593 100644 --- a/packages/wallets/src/evm/wallets/blocto.ts +++ b/packages/wallets/src/evm/wallets/blocto.ts @@ -12,7 +12,7 @@ export class BloctoWallet extends AbstractClientWallet { connector?: Connector; name: string = "Blocto"; - static id = walletIds.blocto; + static id = walletIds.blocto as string; static meta: WalletMeta = { name: "Blocto", iconURL: diff --git a/packages/wallets/src/evm/wallets/coinbase-wallet.ts b/packages/wallets/src/evm/wallets/coinbase-wallet.ts index 2839451eaf2..173c5ddcfbb 100644 --- a/packages/wallets/src/evm/wallets/coinbase-wallet.ts +++ b/packages/wallets/src/evm/wallets/coinbase-wallet.ts @@ -31,7 +31,7 @@ export class CoinbaseWallet extends AbstractClientWallet { }, }; - static id = walletIds.coinbase; + static id= walletIds.coinbase as string; public get walletName() { return "Coinbase Wallet" as const; } diff --git a/packages/wallets/src/evm/wallets/embedded-wallet.ts b/packages/wallets/src/evm/wallets/embedded-wallet.ts index 2b2a54589e3..7b5a492d195 100644 --- a/packages/wallets/src/evm/wallets/embedded-wallet.ts +++ b/packages/wallets/src/evm/wallets/embedded-wallet.ts @@ -18,7 +18,7 @@ export class EmbeddedWallet extends AbstractClientWallet< > { connector?: Connector; - static id = walletIds.embeddedWallet; + static id = walletIds.embeddedWallet as string; static meta = { name: "Embedded Wallet", diff --git a/packages/wallets/src/evm/wallets/frame.ts b/packages/wallets/src/evm/wallets/frame.ts index 79d1b7972a8..d891fa1780a 100644 --- a/packages/wallets/src/evm/wallets/frame.ts +++ b/packages/wallets/src/evm/wallets/frame.ts @@ -5,7 +5,7 @@ import { AbstractClientWallet, WalletOptions } from "./base"; export class FrameWallet extends AbstractClientWallet { connector?: Connector; - static id = walletIds.frame; + static id = walletIds.frame as string; public get walletName() { return "Frame Wallet"; } diff --git a/packages/wallets/src/evm/wallets/injected.ts b/packages/wallets/src/evm/wallets/injected.ts index f77337bbe81..b91462346fa 100644 --- a/packages/wallets/src/evm/wallets/injected.ts +++ b/packages/wallets/src/evm/wallets/injected.ts @@ -4,7 +4,7 @@ import { AbstractClientWallet, WalletOptions } from "./base"; export class InjectedWallet extends AbstractClientWallet { connector?: Connector; - static id = "injected" as const; + static id = "injected" as string; public get walletName() { return "Injected Wallet"; } diff --git a/packages/wallets/src/evm/wallets/local-wallet.ts b/packages/wallets/src/evm/wallets/local-wallet.ts index 645cbc08368..7e3384683d4 100644 --- a/packages/wallets/src/evm/wallets/local-wallet.ts +++ b/packages/wallets/src/evm/wallets/local-wallet.ts @@ -32,7 +32,7 @@ export class LocalWallet extends AbstractClientWallet< ethersWallet?: Wallet; #storage: AsyncStorage; - static id = walletIds.localWallet; + static id = walletIds.localWallet as string; static meta = { name: "Local Wallet", diff --git a/packages/wallets/src/evm/wallets/magic.ts b/packages/wallets/src/evm/wallets/magic.ts index f2283f3a36f..3efe1bbaf7b 100644 --- a/packages/wallets/src/evm/wallets/magic.ts +++ b/packages/wallets/src/evm/wallets/magic.ts @@ -30,7 +30,7 @@ export class MagicLink extends AbstractClientWallet< name: "Magic Link", }; - static id = walletIds.magicLink; + static id = walletIds.magicLink as string; public get walletName() { return "Magic Link" as const; diff --git a/packages/wallets/src/evm/wallets/metamask.ts b/packages/wallets/src/evm/wallets/metamask.ts index 412756bb1eb..3552392376e 100644 --- a/packages/wallets/src/evm/wallets/metamask.ts +++ b/packages/wallets/src/evm/wallets/metamask.ts @@ -55,7 +55,7 @@ export class MetaMaskWallet extends AbstractClientWallet { connector?: Connector; - static id = walletIds.paper; + static id = walletIds.paper as string; static meta = { name: "Paper Wallet", diff --git a/packages/wallets/src/evm/wallets/phantom.ts b/packages/wallets/src/evm/wallets/phantom.ts index 7d283234f45..5bb93d35137 100644 --- a/packages/wallets/src/evm/wallets/phantom.ts +++ b/packages/wallets/src/evm/wallets/phantom.ts @@ -26,7 +26,7 @@ export class PhantomWallet extends AbstractClientWallet { }, }; - static id = walletIds.phantom; + static id = walletIds.phantom as string; public get walletName() { return "Phantom" as const; diff --git a/packages/wallets/src/evm/wallets/rainbow-wallet.ts b/packages/wallets/src/evm/wallets/rainbow-wallet.ts index 444f73dd226..1f14517d204 100644 --- a/packages/wallets/src/evm/wallets/rainbow-wallet.ts +++ b/packages/wallets/src/evm/wallets/rainbow-wallet.ts @@ -55,7 +55,7 @@ export class RainbowWallet extends AbstractClientWallet { + const connector = await this.getConnector(); + return connector.deployIfNeeded(); + } + /** * Check if the smart wallet contract is deployed * @returns true if the smart wallet contract is deployed @@ -132,6 +144,54 @@ export class SmartWallet return connector.isDeployed(); } + /** + * Create and add a session key to the smart wallet. + * @param keyAddress the address of the session key to add. + * @param permissions the permissions to grant to the session key. + */ + async createSessionKey( + keyAddress: string, + permissions: SignerPermissionsInput, + ): Promise { + const connector = await this.getConnector(); + return connector.grantPermissions(keyAddress, permissions); + } + + /** + * Remove a session key from the smart wallet. + * @param keyAddress the address of the session key to remove. + */ + async revokeSessionKey(keyAddress: string): Promise { + const connector = await this.getConnector(); + return connector.revokePermissions(keyAddress); + } + + /** + * Add another admin to the smart wallet. + * @param adminAddress the address of the admin to add. + */ + async addAdmin(adminAddress: string): Promise { + const connector = await this.getConnector(); + return connector.addAdmin(adminAddress); + } + + /** + * Remove an admin from the smart wallet. + * @param adminAddress the address of the admin to remove. + */ + async removeAdmin(adminAddress: string): Promise { + const connector = await this.getConnector(); + return connector.removeAdmin(adminAddress); + } + + /** + * Get all the admins and session keys active on the smart wallet. + */ + async getAllActiveSigners(): Promise { + const connector = await this.getConnector(); + return connector.getAllActiveSigners(); + } + /** * Get the underlying account contract of the smart wallet. * @returns the account contract of the smart wallet. diff --git a/packages/wallets/src/evm/wallets/trust.ts b/packages/wallets/src/evm/wallets/trust.ts index cf4281846db..452332363a9 100644 --- a/packages/wallets/src/evm/wallets/trust.ts +++ b/packages/wallets/src/evm/wallets/trust.ts @@ -56,7 +56,7 @@ export class TrustWallet extends AbstractClientWallet { }, }; - static id = walletIds.trust; + static id = walletIds.trust as string; public get walletName() { return "Trust Wallet" as const; diff --git a/packages/wallets/src/evm/wallets/wallet-connect.ts b/packages/wallets/src/evm/wallets/wallet-connect.ts index 0e329c754bd..eccf4c4a8d2 100644 --- a/packages/wallets/src/evm/wallets/wallet-connect.ts +++ b/packages/wallets/src/evm/wallets/wallet-connect.ts @@ -44,7 +44,7 @@ export class WalletConnect extends AbstractClientWallet { connector?: Connector; - static id = walletIds.walletConnect; + static id = walletIds.walletConnect as string; static meta = { name: "WalletConnect", diff --git a/packages/wallets/src/evm/wallets/zerion.ts b/packages/wallets/src/evm/wallets/zerion.ts index 7bdc76af677..97d3e4372de 100644 --- a/packages/wallets/src/evm/wallets/zerion.ts +++ b/packages/wallets/src/evm/wallets/zerion.ts @@ -40,7 +40,7 @@ export class ZerionWallet extends AbstractClientWallet walletConnectConnector?: WalletConnectConnectorType; isInjected: boolean; - static id = "zerion" as const; + static id = "zerion" as string; static meta = { name: "Zerion Wallet", iconURL: "ipfs://Qmb1LhNtMUkzbgk1V8ZiUSRXjMJGRkS5HH3R71KyRgjdBG/zerion.png", diff --git a/packages/wallets/tsconfig.json b/packages/wallets/tsconfig.json index fb29f63a064..b9a347ca7d8 100644 --- a/packages/wallets/tsconfig.json +++ b/packages/wallets/tsconfig.json @@ -1,5 +1,8 @@ { "extends": "@thirdweb-dev/tsconfig/sdk.json", "include": ["src", "types"], - "exclude": ["dist", "build", "node_modules"] + "exclude": ["dist", "build", "node_modules"], + "compilerOptions": { + "noUncheckedIndexedAccess": true + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1bbea760f9..67d61c5ac2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -336,7 +336,7 @@ importers: version: 6.1.5 '@types/update-notifier': specifier: ^6.0.4 - version: 6.0.4 + version: 6.0.5 '@types/validate-npm-package-name': specifier: ^4.0.0 version: 4.0.0 @@ -380,8 +380,8 @@ importers: packages/contracts-js: dependencies: '@thirdweb-dev/contracts': - specifier: 3.10.0 - version: 3.10.0(ethers@5.7.2) + specifier: 3.10.1-0 + version: 3.10.1-0(ethers@5.7.2) devDependencies: '@babel/preset-env': specifier: ^7.22.9 @@ -9740,8 +9740,8 @@ packages: webpack-dev-server: 4.12.0(webpack@5.76.2) dev: false - /@pnpm/config.env-replace@1.0.0: - resolution: {integrity: sha512-ZVPVDi1E8oeXlYqkGRtX0CkzLTwE2zt62bjWaWKaAvI8NZqHzlMvGeSNDpW+JB3+aKanYb4UETJOF1/CxGPemA==} + /@pnpm/config.env-replace@1.1.0: + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} dev: false @@ -9752,11 +9752,11 @@ packages: graceful-fs: 4.2.10 dev: false - /@pnpm/npm-conf@2.1.0: - resolution: {integrity: sha512-Oe6ntvgsMTE3hDIqy6sajqHF+MnzJrOF06qC2QSiUEybLL7cp6tjoKUa32gpd9+KPVl4QyMs3E3nsXrx/Vdnlw==} + /@pnpm/npm-conf@2.2.2: + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} dependencies: - '@pnpm/config.env-replace': 1.0.0 + '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 dev: false @@ -11177,8 +11177,8 @@ packages: engines: {node: '>=10'} dev: false - /@sindresorhus/is@5.3.0: - resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==} + /@sindresorhus/is@5.6.0: + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} engines: {node: '>=14.16'} dev: false @@ -12030,8 +12030,8 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false - /@thirdweb-dev/contracts@3.10.0(ethers@5.7.2): - resolution: {integrity: sha512-IGnFev/ooS4y/oh+wURuv0E32ztYDeZk7RdWO0si1YY5vDMSbVu/TBhlhmLPcfIZWs0IjCAoA6H/Zy5QV5mkhw==} + /@thirdweb-dev/contracts@3.10.1-0(ethers@5.7.2): + resolution: {integrity: sha512-s+Wj0hZ6OFu2B+iykEf8N+7uyCUzLQHVQYGWYPiuMwqi0urYiiJyseRe1/jhZ9IkXD+RA4pCOyYrtXfT6r8m3Q==} dependencies: '@chainlink/contracts': 0.6.1(ethers@5.7.2) '@openzeppelin/contracts': 4.9.3 @@ -12336,6 +12336,10 @@ packages: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: false + /@types/http-cache-semantics@4.0.2: + resolution: {integrity: sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==} + dev: false + /@types/http-proxy@1.17.10: resolution: {integrity: sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==} dependencies: @@ -12619,11 +12623,11 @@ packages: resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} dev: false - /@types/update-notifier@6.0.4: - resolution: {integrity: sha512-CiKJPSmt/3F4sVnkQTjnP/onKtTJxRkib6Gvw4XESM8FNsHlvRnBNqU5qL0IQmqjtKnz5e9E6Y7xChOpvxFzKg==} + /@types/update-notifier@6.0.5: + resolution: {integrity: sha512-uUOhxsJ3edPHu06r3k4I2yJ4eoyqBt+53ra9+caXEx0ruoPwqNHTlkq75CUvI4yWsrCA5+31tih+opunLCYnXw==} dependencies: '@types/configstore': 6.0.0 - boxen: 7.0.2 + boxen: 7.0.0 dev: true /@types/uuid@9.0.2: @@ -14682,20 +14686,6 @@ packages: type-fest: 2.19.0 widest-line: 4.0.1 wrap-ansi: 8.1.0 - dev: true - - /boxen@7.0.2: - resolution: {integrity: sha512-1Z4UJabXUP1/R9rLpoU3O2lEMnG3pPLAs/ZD2lF3t2q7qD5lM8rqbtnvtvm4N0wEyNlE+9yZVTVAGmd1V5jabg==} - engines: {node: '>=14.16'} - dependencies: - ansi-align: 3.0.1 - camelcase: 7.0.1 - chalk: 5.3.0 - cli-boxes: 3.0.0 - string-width: 5.1.2 - type-fest: 2.19.0 - widest-line: 4.0.1 - wrap-ansi: 8.1.0 /bplist-creator@0.1.0: resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} @@ -15053,14 +15043,14 @@ packages: engines: {node: '>=14.16'} dev: false - /cacheable-request@10.2.8: - resolution: {integrity: sha512-IDVO5MJ4LItE6HKFQTqT2ocAQsisOoCTUDu1ddCmnhyiwFQjXNPp4081Xj23N4tO+AFEFNzGuNEf/c8Gwwt15A==} + /cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} engines: {node: '>=14.16'} dependencies: - '@types/http-cache-semantics': 4.0.1 + '@types/http-cache-semantics': 4.0.2 get-stream: 6.0.1 http-cache-semantics: 4.1.1 - keyv: 4.5.2 + keyv: 4.5.4 mimic-response: 4.0.0 normalize-url: 8.0.0 responselike: 3.0.0 @@ -17516,7 +17506,7 @@ packages: minimatch: 3.1.2 object.values: 1.1.6 resolve: 1.22.1 - semver: 7.5.3 + semver: 7.5.4 tsconfig-paths: 3.14.2 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -19418,14 +19408,14 @@ packages: responselike: 2.0.1 dev: false - /got@12.6.0: - resolution: {integrity: sha512-WTcaQ963xV97MN3x0/CbAriXFZcXCfgxVp91I+Ze6pawQOa7SgzwSx2zIJJsX+kTajMnVs0xcFD1TxZKFqhdnQ==} + /got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} engines: {node: '>=14.16'} dependencies: - '@sindresorhus/is': 5.3.0 + '@sindresorhus/is': 5.6.0 '@szmarczak/http-timer': 5.0.1 cacheable-lookup: 7.0.0 - cacheable-request: 10.2.8 + cacheable-request: 10.2.14 decompress-response: 6.0.0 form-data-encoder: 2.1.4 get-stream: 6.0.1 @@ -22045,6 +22035,12 @@ packages: json-buffer: 3.0.1 dev: false + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: false + /keyvaluestorage-interface@1.0.0: resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} dev: false @@ -22107,7 +22103,7 @@ packages: resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} engines: {node: '>=14.16'} dependencies: - package-json: 8.1.0 + package-json: 8.1.1 dev: false /launch-editor@2.6.0: @@ -24202,11 +24198,11 @@ packages: semver: 7.5.4 dev: false - /package-json@8.1.0: - resolution: {integrity: sha512-hySwcV8RAWeAfPsXb9/HGSPn8lwDnv6fabH+obUZKX169QknRkRhPxd1yMubpKDskLFATkl3jHpNtVtDPFA0Wg==} + /package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} dependencies: - got: 12.6.0 + got: 12.6.1 registry-auth-token: 5.0.2 registry-url: 6.0.1 semver: 7.5.4 @@ -26404,7 +26400,7 @@ packages: resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} engines: {node: '>=14'} dependencies: - '@pnpm/npm-conf': 2.1.0 + '@pnpm/npm-conf': 2.2.2 dev: false /registry-url@3.1.0: @@ -28785,7 +28781,7 @@ packages: resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} engines: {node: '>=14.16'} dependencies: - boxen: 7.0.2 + boxen: 7.0.0 chalk: 5.3.0 configstore: 6.0.0 has-yarn: 3.0.0