From c258e1902b929bef1e5abf238758ba2408c25280 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Sun, 18 Sep 2022 14:44:07 -0700 Subject: [PATCH 1/4] Fix using external signers with ThirdwebSDKProvider --- packages/react/src/Provider.tsx | 28 +++++++++++++++++++++ packages/react/src/hooks/async/contracts.ts | 13 ++++++---- packages/sdk/src/core/sdk.ts | 2 +- packages/sdk/src/core/wallet/UserWallet.ts | 24 +++++++++++++++--- packages/solana/src/classes/user-wallet.ts | 1 - 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/packages/react/src/Provider.tsx b/packages/react/src/Provider.tsx index 97e869d30ff..eb31bc6786e 100644 --- a/packages/react/src/Provider.tsx +++ b/packages/react/src/Provider.tsx @@ -525,6 +525,21 @@ export const ThirdwebSDKProvider: React.FC< } }, [signer, sdk, desiredChainId]); + useEffect(() => { + if (sdk) { + sdk.wallet.events.on( + "signerChanged", + async (newSigner: Signer | undefined) => { + (sdk as any)._signerAddress = await newSigner?.getAddress(); + (sdk as any)._signerChainId = await newSigner?.getChainId(); + }, + ); + return () => { + sdk.wallet.events.off("signerChanged"); + }; + } + }, [sdk]); + const ctxValue = useMemo( () => ({ sdk, @@ -587,3 +602,16 @@ export function useActiveChainId(): SUPPORTED_CHAIN_ID | undefined { const sdk = useSDK(); return (sdk as any)?._chainId; } + +/** + * @internal + */ +export function useSDKSignerAddress(): string | undefined { + const sdk = useSDK(); + return (sdk as any)?._signerAddress; +} + +export function useSDKSignerChainId(): number | undefined { + const sdk = useSDK(); + return (sdk as any)?._signerChainId; +} diff --git a/packages/react/src/hooks/async/contracts.ts b/packages/react/src/hooks/async/contracts.ts index dee51a432f2..7133aa79b95 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, + useSDK, + useSDKSignerAddress, + useSDKSignerChainId, +} 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 = useSDKSignerAddress(); + const walletChainId = useSDKSignerChainId(); // 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 cc02a2f39a6..bb638bd1a8e 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 { From 98fb89cfe6525794208c033dcd888223f5509e7c Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Sun, 18 Sep 2022 14:52:47 -0700 Subject: [PATCH 2/4] add changeset --- .changeset/calm-moles-sell.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/calm-moles-sell.md 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 From 4520f81d69961e1a48c21f7dcde89b942272b260 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Sun, 18 Sep 2022 15:26:31 -0700 Subject: [PATCH 3/4] slightly cleaner approach --- packages/react/src/Provider.tsx | 65 ++++++++++++++------- packages/react/src/hooks/async/contracts.ts | 8 +-- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/packages/react/src/Provider.tsx b/packages/react/src/Provider.tsx index eb31bc6786e..f69e98737a8 100644 --- a/packages/react/src/Provider.tsx +++ b/packages/react/src/Provider.tsx @@ -29,7 +29,7 @@ import { } from "@thirdweb-dev/sdk"; import type { IStorage } 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, @@ -525,21 +525,6 @@ export const ThirdwebSDKProvider: React.FC< } }, [signer, sdk, desiredChainId]); - useEffect(() => { - if (sdk) { - sdk.wallet.events.on( - "signerChanged", - async (newSigner: Signer | undefined) => { - (sdk as any)._signerAddress = await newSigner?.getAddress(); - (sdk as any)._signerChainId = await newSigner?.getChainId(); - }, - ); - return () => { - sdk.wallet.events.off("signerChanged"); - }; - } - }, [sdk]); - const ctxValue = useMemo( () => ({ sdk, @@ -606,12 +591,50 @@ export function useActiveChainId(): SUPPORTED_CHAIN_ID | undefined { /** * @internal */ -export function useSDKSignerAddress(): string | undefined { +export function useActiveSigner(): Signer | undefined { const sdk = useSDK(); - return (sdk as any)?._signerAddress; + const [signer, setSigner] = useState(undefined); + useEffect(() => { + if (sdk) { + sdk.wallet.events.on("signerChanged", (newSigner: Signer | undefined) => { + setSigner(newSigner); + }); + return () => { + sdk.wallet.events.off("signerChanged"); + }; + } + }, [sdk]); + return signer; } -export function useSDKSignerChainId(): number | undefined { - const sdk = useSDK(); - return (sdk as any)?._signerChainId; +/** + * @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 7133aa79b95..b5e1e3ed2da 100644 --- a/packages/react/src/hooks/async/contracts.ts +++ b/packages/react/src/hooks/async/contracts.ts @@ -1,8 +1,8 @@ import { useActiveChainId, + useActiveSignerAddress, + useActiveSignerChainId, useSDK, - useSDKSignerAddress, - useSDKSignerChainId, } from "../../Provider"; import { ContractAddress, RequiredParam } from "../../types"; import { @@ -155,8 +155,8 @@ export function useContract< const sdk = useSDK(); const queryClient = useQueryClient(); const activeChainId = useActiveChainId(); - const wallet = useSDKSignerAddress(); - const walletChainId = useSDKSignerChainId(); + const wallet = useActiveSignerAddress(); + const walletChainId = useActiveSignerChainId(); // it's there because we put it there. const sdkTimestamp = (sdk as any)?._constructedAt; From 3ed4e591e890520ee06f1c61d80d9f33366c4d72 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Mon, 19 Sep 2022 10:12:28 -0700 Subject: [PATCH 4/4] start with default signer --- packages/react/src/Provider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/Provider.tsx b/packages/react/src/Provider.tsx index f69e98737a8..2b44ecd24ce 100644 --- a/packages/react/src/Provider.tsx +++ b/packages/react/src/Provider.tsx @@ -593,7 +593,7 @@ export function useActiveChainId(): SUPPORTED_CHAIN_ID | undefined { */ export function useActiveSigner(): Signer | undefined { const sdk = useSDK(); - const [signer, setSigner] = useState(undefined); + const [signer, setSigner] = useState(sdk?.getSigner()); useEffect(() => { if (sdk) { sdk.wallet.events.on("signerChanged", (newSigner: Signer | undefined) => {