diff --git a/.changeset/calm-moles-sell.md b/.changeset/calm-moles-sell.md new file mode 100644 index 00000000000..1677f5a456f --- /dev/null +++ b/.changeset/calm-moles-sell.md @@ -0,0 +1,6 @@ +--- +"@thirdweb-dev/react": patch +"@thirdweb-dev/sdk": patch +--- + +Fix using external signers with ThirdwebSDKProvider diff --git a/packages/react/src/Provider.tsx b/packages/react/src/Provider.tsx index 9edfa2d6477..9d9404c698e 100644 --- a/packages/react/src/Provider.tsx +++ b/packages/react/src/Provider.tsx @@ -29,7 +29,7 @@ import { } from "@thirdweb-dev/sdk"; import type { ThirdwebStorage } from "@thirdweb-dev/storage"; import { Signer } from "ethers"; -import React, { createContext, useEffect, useMemo } from "react"; +import React, { createContext, useEffect, useMemo, useState } from "react"; import invariant from "tiny-invariant"; import { WagmiProvider, @@ -587,3 +587,54 @@ export function useActiveChainId(): SUPPORTED_CHAIN_ID | undefined { const sdk = useSDK(); return (sdk as any)?._chainId; } + +/** + * @internal + */ +export function useActiveSigner(): Signer | undefined { + const sdk = useSDK(); + const [signer, setSigner] = useState(sdk?.getSigner()); + useEffect(() => { + if (sdk) { + sdk.wallet.events.on("signerChanged", (newSigner: Signer | undefined) => { + setSigner(newSigner); + }); + return () => { + sdk.wallet.events.off("signerChanged"); + }; + } + }, [sdk]); + return signer; +} + +/** + * @internal + */ +export function useActiveSignerAddress(): string | undefined { + const signer = useActiveSigner(); + const [address, setAddress] = useState(undefined); + useEffect(() => { + if (signer) { + signer.getAddress().then((a) => { + setAddress(a); + }); + } + }, [signer]); + return address; +} + +/** + * @internal + */ +export function useActiveSignerChainId(): number | undefined { + const signer = useActiveSigner(); + const [chainId, setChainId] = useState(undefined); + useEffect(() => { + if (signer) { + signer.getChainId().then((id) => { + setChainId(id); + }); + } + }, [signer]); + return chainId; +} diff --git a/packages/react/src/hooks/async/contracts.ts b/packages/react/src/hooks/async/contracts.ts index dee51a432f2..b5e1e3ed2da 100644 --- a/packages/react/src/hooks/async/contracts.ts +++ b/packages/react/src/hooks/async/contracts.ts @@ -1,4 +1,9 @@ -import { useActiveChainId, useSDK } from "../../Provider"; +import { + useActiveChainId, + useActiveSignerAddress, + useActiveSignerChainId, + useSDK, +} from "../../Provider"; import { ContractAddress, RequiredParam } from "../../types"; import { cacheKeys, @@ -6,8 +11,6 @@ import { createContractCacheKey, } from "../../utils/cache-keys"; import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork"; -import { useAddress } from "../useAddress"; -import { useChainId } from "../useChainId"; import { useMutation, useQuery, @@ -152,8 +155,8 @@ export function useContract< const sdk = useSDK(); const queryClient = useQueryClient(); const activeChainId = useActiveChainId(); - const wallet = useAddress(); - const walletChainId = useChainId(); + const wallet = useActiveSignerAddress(); + const walletChainId = useActiveSignerChainId(); // it's there because we put it there. const sdkTimestamp = (sdk as any)?._constructedAt; diff --git a/packages/sdk/src/core/sdk.ts b/packages/sdk/src/core/sdk.ts index 9ceca03e740..7bda987f1c5 100644 --- a/packages/sdk/src/core/sdk.ts +++ b/packages/sdk/src/core/sdk.ts @@ -409,8 +409,8 @@ export class ThirdwebSDK extends RPCConnectionHandler { } private updateContractSignerOrProvider() { + this.wallet.connect(this.getSignerOrProvider()); this.auth.updateSignerOrProvider(this.getSignerOrProvider()); - this.wallet.onNetworkUpdated(this.getSignerOrProvider()); this.deployer.updateSignerOrProvider(this.getSignerOrProvider()); this._publisher.updateSignerOrProvider(this.getSignerOrProvider()); for (const [, contract] of this.contractCache) { diff --git a/packages/sdk/src/core/wallet/UserWallet.ts b/packages/sdk/src/core/wallet/UserWallet.ts index c7d69eaf137..993aa70ba2b 100644 --- a/packages/sdk/src/core/wallet/UserWallet.ts +++ b/packages/sdk/src/core/wallet/UserWallet.ts @@ -11,9 +11,23 @@ import { RPCConnectionHandler } from "../classes/rpc-connection-handler"; import { NetworkOrSignerOrProvider, TransactionResult } from "../types"; import type { IERC20 } from "@thirdweb-dev/contracts-js"; import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json"; -import { ethers, BigNumber, providers } from "ethers"; +import { ethers, BigNumber, providers, Signer } from "ethers"; +import EventEmitter from "eventemitter3"; import invariant from "tiny-invariant"; +/** + * + * {@link UserWallet} events that you can subscribe to using `sdk.wallet.events`. + * + * @public + */ +export interface UserWalletEvents { + /** + * Emitted when `sdk.wallet.connect()` is called. + */ + signerChanged: [Signer | undefined]; +} + /** * Connect and Interact with a user wallet * @example @@ -25,22 +39,24 @@ import invariant from "tiny-invariant"; export class UserWallet { private connection: RPCConnectionHandler; private options: SDKOptions; + public events = new EventEmitter(); constructor(network: NetworkOrSignerOrProvider, options: SDKOptions) { this.connection = new RPCConnectionHandler(network, options); this.options = options; + this.events = new EventEmitter(); } - // TODO connect() // TODO disconnect() // TODO switchChain() - // TODO event listener // TODO tokens() // TODO NFTs() // TODO this will become the source of truth of the signer and have every contract read from it - onNetworkUpdated(network: NetworkOrSignerOrProvider): void { + // TODO separate signer and provider logics + public connect(network: NetworkOrSignerOrProvider) { this.connection.updateSignerOrProvider(network); + this.events.emit("signerChanged", this.connection.getSigner()); } /** diff --git a/packages/solana/src/classes/user-wallet.ts b/packages/solana/src/classes/user-wallet.ts index 2555e8650d4..84a08e779ff 100644 --- a/packages/solana/src/classes/user-wallet.ts +++ b/packages/solana/src/classes/user-wallet.ts @@ -1,4 +1,3 @@ -// import { getPayer } from "../utils/local-config"; import { CurrencyValue, WalletSigner } from "../types/common"; import { toCurrencyValue } from "../utils/token"; import {