Skip to content

Commit

Permalink
Add walletConnect in react, export all wallets
Browse files Browse the repository at this point in the history
  • Loading branch information
MananTank committed Feb 7, 2024
1 parent c397574 commit c98c794
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 11 deletions.
7 changes: 7 additions & 0 deletions packages/thirdweb/src/react/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@ export { useBlockNumber } from "./hooks/rpc/useBlockNumber.js";

// utils
export { createContractQuery } from "./utils/createQuery.js";

// wallets
export { metamaskConfig } from "./wallets/metamask/metamaskConfig.js";
export { coinbaseConfig } from "./wallets/coinbase/coinbaseConfig.js";
export { rainbowConfig } from "./wallets/rainbow/rainbowConfig.js";
export { walletConnectConfig } from "./wallets/walletConnect/walletConnectConfig.js";
export { zerionConfig } from "./wallets/zerion/zerionConfig.js";
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export const ConnectModalContent = (props: {
const screenConfig: ScreenConfig = {
setModalVisibility,
theme: typeof theme === "string" ? theme : theme.type,
goBack: handleBack,
goBack: wallets.length > 1 ? handleBack : undefined,
size: modalConfig.modalSize,
};

Expand Down
2 changes: 2 additions & 0 deletions packages/thirdweb/src/react/wallets/defaultWallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import type { WalletConfig } from "../types/wallets.js";
import { coinbaseConfig } from "./coinbase/coinbaseConfig.js";
import { metamaskConfig } from "./metamask/metamaskConfig.js";
import { rainbowConfig } from "./rainbow/rainbowConfig.js";
import { walletConnectConfig } from "./walletConnect/walletConnectConfig.js";
import { zerionConfig } from "./zerion/zerionConfig.js";

export const defaultWallets: WalletConfig[] = [
/* @__PURE__ */ metamaskConfig(),
/* @__PURE__ */ coinbaseConfig(),
/* @__PURE__ */ walletConnectConfig(),
/* @__PURE__ */ rainbowConfig(),
/* @__PURE__ */ zerionConfig(),
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import {
walletConnectMetadata,
walletConnect,
type WalletConnectConnectionOptions,
} from "../../../wallets/index.js";
import type { WalletConnect } from "../../../wallets/wallet-connect/index.js";
import { useTWLocale } from "../../providers/locale-provider.js";
import type { ConnectUIProps, WalletConfig } from "../../types/wallets.js";
import { Img } from "../../ui/components/Img.js";
import { QRCode } from "../../ui/components/QRCode.js";
import { Spacer } from "../../ui/components/Spacer.js";
import { Spinner } from "../../ui/components/Spinner.js";
import { Container, ModalHeader, Line } from "../../ui/components/basic.js";
import { Button } from "../../ui/components/buttons.js";
import { iconSize } from "../../ui/design-system/index.js";
import { wait } from "../../utils/wait.js";
import { useState, useRef, useEffect } from "react";
import { Text } from "../../ui/components/text.js";
import { isMobile } from "../../utils/isMobile.js";
import { HeadlessConnectUI } from "../headlessConnectUI.js";

export type WalletConnectConfigOptions = {
projectId?: string;
qrModalOptions?: WalletConnectConnectionOptions["qrModalOptions"];
recommended?: boolean;
};

/**
* Integrate MetaMask wallet connection into your app.
* @param options - Options for configuring the MetaMask wallet.
* @example
* ```tsx
* <ThirdwebProvider
* client={client}>
* wallets={[ metamaskConfig() ]}
* <App />
* </ThirdwebProvider>
* ```
* @returns WalletConfig object to be passed into `ThirdwebProvider`
*/
export const walletConnectConfig = (
options?: WalletConnectConfigOptions,
): WalletConfig => {
const config: WalletConfig = {
metadata: walletConnectMetadata,
create(createOptions) {
return walletConnect({
client: createOptions.client,
dappMetadata: createOptions.dappMetadata,
});
},
connectUI(props) {
if (isMobile()) {
return <HeadlessConnectUI {...props} />;
}

return (
<ConnectUI
{...props}
wcConfig={{
projectId: options?.projectId,
qrModalOptions: options?.qrModalOptions,
}}
/>
);
},
recommended: options?.recommended,
};

return config;
};

const ConnectUI = (
props: ConnectUIProps & {
wcConfig: {
projectId?: string;
qrModalOptions?: WalletConnectConnectionOptions["qrModalOptions"];
};
},
) => {
const locale = useTWLocale().wallets.walletConnect;
const [qrCodeUri, setQrCodeUri] = useState<string | undefined>();
const { chainId, done, createInstance, walletConfig } = props;
const [isWCModalOpen, setIsWCModalOpen] = useState(false);
const { setModalVisibility } = props.screenConfig;

const handleWCModalConnect = async () => {
try {
setQrCodeUri(undefined);
setIsWCModalOpen(true);
wait(1000).then(() => {
setModalVisibility(false);
});

// TODO: fix the type in ConnectUIProps
const wallet = createInstance() as WalletConnect;

const account = await wallet.connect({
chainId: chainId,
showQrModal: true,
qrModalOptions: props.wcConfig.qrModalOptions,
optionalChains: props.chains,
});

done(account);
setModalVisibility(true);
} catch {
setModalVisibility(true);
setIsWCModalOpen(false);
}
};

return (
<Container fullHeight animate="fadein" flex="column">
<Container p="lg">
<ModalHeader
onBack={props.screenConfig.goBack}
title={walletConfig.metadata.name}
/>
</Container>

<Spacer y="sm" />

<Container flex="column" center={"both"} px="lg" expand>
{isWCModalOpen ? (
<Container
style={{
minHeight: "300px",
}}
flex="column"
center="both"
>
<Spinner size="xl" color="accentText" />
</Container>
) : (
<WalletConnectQRScanConnect
{...props}
qrCodeUri={qrCodeUri}
setQrCodeUri={setQrCodeUri}
/>
)}
</Container>

<Container p="lg">
<Text multiline center balance>
{locale.scanInstruction}
</Text>
</Container>

<Line />
<Container py="lg" flex="row" center="x">
<Button variant="link" onClick={handleWCModalConnect}>
{"Open Official WalletConnect Modal"}
</Button>
</Container>
</Container>
);
};

function WalletConnectQRScanConnect(
props: ConnectUIProps & {
qrCodeUri?: string;
setQrCodeUri: (uri: string) => void;
wcConfig: {
projectId?: string;
qrModalOptions?: WalletConnectConnectionOptions["qrModalOptions"];
};
},
) {
const {
qrCodeUri,
walletConfig,
createInstance,
done,
chainId,
setQrCodeUri,
chains,
} = props;

const scanStarted = useRef(false);
useEffect(() => {
if (scanStarted.current) {
return;
}
scanStarted.current = true;

const wallet = createInstance() as WalletConnect;

wallet
.connect({
chainId: chainId,
onDisplayUri(uri) {
setQrCodeUri(uri);
},
optionalChains: chains,
showQrModal: false,
})
.then((account) => {
done(account);
});
}, [chainId, chains, createInstance, done, setQrCodeUri]);

return (
<QRCode
qrCodeUri={qrCodeUri}
QRIcon={
<Img
width={iconSize.xxl}
height={iconSize.xxl}
src={walletConfig.metadata.iconUrl}
/>
}
/>
);
}
7 changes: 6 additions & 1 deletion packages/thirdweb/src/wallets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ export {
} from "./injected/wallets/zerion.js";

// wallet-connect
export { walletConnect } from "./wallet-connect/index.js";
export {
walletConnect,
walletConnectMetadata,
} from "./wallet-connect/index.js";

export type { WalletConnectConnectionOptions } from "./wallet-connect/types.js";
16 changes: 9 additions & 7 deletions packages/thirdweb/src/wallets/wallet-connect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from "viem";
import type { Address } from "abitype";
import { normalizeChainId } from "../utils/normalizeChainId.js";
import type { DAppMetaData } from "../types.js";
import type { DAppMetaData, WalletMetadata } from "../types.js";
import {
deleteConnectParamsFromStorage,
getSavedConnectParamsFromStorage,
Expand Down Expand Up @@ -61,6 +61,13 @@ type SavedConnectParams = {
pairingTopic?: string;
};

export const walletConnectMetadata: WalletMetadata = {
name: "WalletConnect",
iconUrl:
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMTIiIGZpbGw9IiMxQzdERkMiLz4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMTIiIGZpbGw9InVybCgjcGFpbnQwX3JhZGlhbF8xXzQ2KSIvPgo8cGF0aCBkPSJNMjYuNDIyNyAzMS40NzMxQzMzLjkxNzEgMjQuMTc1NiA0Ni4wODI5IDI0LjE3NTYgNTMuNTc3MyAzMS40NzMxTDU0LjQ3OTYgMzIuMzU4QzU0Ljg1OCAzMi43MjA3IDU0Ljg1OCAzMy4zMTU1IDU0LjQ3OTYgMzMuNjc4Mkw1MS4zOTQ1IDM2LjY4MTNDNTEuMjA1MyAzNi44Njk5IDUwLjg5OTcgMzYuODY5OSA1MC43MTA1IDM2LjY4MTNMNDkuNDczNiAzNS40NzcyQzQ0LjIzNDcgMzAuMzg1IDM1Ljc2NTMgMzAuMzg1IDMwLjUyNjQgMzUuNDc3MkwyOS4yMDIxIDM2Ljc2ODRDMjkuMDEzIDM2Ljk1NyAyOC43MDc0IDM2Ljk1NyAyOC41MTgyIDM2Ljc2ODRMMjUuNDMzMSAzMy43NjUzQzI1LjA1NDcgMzMuNDAyNiAyNS4wNTQ3IDMyLjgwNzggMjUuNDMzMSAzMi40NDUxTDI2LjQyMjcgMzEuNDczMVpNNTkuOTY1OCAzNy42ODI0TDYyLjcxNjIgNDAuMzUxOEM2My4wOTQ2IDQwLjcxNDUgNjMuMDk0NiA0MS4zMDkzIDYyLjcxNjIgNDEuNjcyTDUwLjMzMjIgNTMuNzI4QzQ5Ljk1MzggNTQuMDkwNyA0OS4zNDI2IDU0LjA5MDcgNDguOTc4OCA1My43MjhMNDAuMTg5MiA0NS4xNjg0QzQwLjEwMTkgNDUuMDgxMyAzOS45NDE4IDQ1LjA4MTMgMzkuODU0NSA0NS4xNjg0TDMxLjA2NDkgNTMuNzI4QzMwLjY4NjUgNTQuMDkwNyAzMC4wNzUzIDU0LjA5MDcgMjkuNzExNSA1My43MjhMMTcuMjgzOCA0MS42NzJDMTYuOTA1NCA0MS4zMDkzIDE2LjkwNTQgNDAuNzE0NSAxNy4yODM4IDQwLjM1MThMMjAuMDM0MiAzNy42ODI0QzIwLjQxMjUgMzcuMzE5NyAyMS4wMjM3IDM3LjMxOTcgMjEuMzg3NSAzNy42ODI0TDMwLjE3NzIgNDYuMjQyQzMwLjI2NDUgNDYuMzI5IDMwLjQyNDUgNDYuMzI5IDMwLjUxMTkgNDYuMjQyTDM5LjMwMTUgMzcuNjgyNEMzOS42Nzk5IDM3LjMxOTcgNDAuMjkxIDM3LjMxOTcgNDAuNjU0OSAzNy42ODI0TDQ5LjQ0NDUgNDYuMjQyQzQ5LjUzMTggNDYuMzI5IDQ5LjY5MTkgNDYuMzI5IDQ5Ljc3OTIgNDYuMjQyTDU4LjU2ODggMzcuNjgyNEM1OC45NzYzIDM3LjMxOTcgNTkuNTg3NSAzNy4zMTk3IDU5Ljk2NTggMzcuNjgyNFoiIGZpbGw9IndoaXRlIi8+CjxkZWZzPgo8cmFkaWFsR3JhZGllbnQgaWQ9InBhaW50MF9yYWRpYWxfMV80NiIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgwLjAwMDI0Nzk1NSA0MC4wMDEyKSBzY2FsZSg4MCkiPgo8c3RvcCBzdG9wLWNvbG9yPSIjNUQ5REY2Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwNkZGRiIvPgo8L3JhZGlhbEdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo=",
id: "walletconnect",
};

/**
* Connect to a wallet using WalletConnect protocol.
* @param options - The options for connecting wallet
Expand Down Expand Up @@ -99,12 +106,7 @@ export class WalletConnect implements Wallet {
*/
constructor(options: WalletConnectCreationOptions) {
this.options = options;
this.metadata = options?.metadata || {
name: "WalletConnect",
iconUrl:
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMTIiIGZpbGw9IiMxQzdERkMiLz4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMTIiIGZpbGw9InVybCgjcGFpbnQwX3JhZGlhbF8xXzQ2KSIvPgo8cGF0aCBkPSJNMjYuNDIyNyAzMS40NzMxQzMzLjkxNzEgMjQuMTc1NiA0Ni4wODI5IDI0LjE3NTYgNTMuNTc3MyAzMS40NzMxTDU0LjQ3OTYgMzIuMzU4QzU0Ljg1OCAzMi43MjA3IDU0Ljg1OCAzMy4zMTU1IDU0LjQ3OTYgMzMuNjc4Mkw1MS4zOTQ1IDM2LjY4MTNDNTEuMjA1MyAzNi44Njk5IDUwLjg5OTcgMzYuODY5OSA1MC43MTA1IDM2LjY4MTNMNDkuNDczNiAzNS40NzcyQzQ0LjIzNDcgMzAuMzg1IDM1Ljc2NTMgMzAuMzg1IDMwLjUyNjQgMzUuNDc3MkwyOS4yMDIxIDM2Ljc2ODRDMjkuMDEzIDM2Ljk1NyAyOC43MDc0IDM2Ljk1NyAyOC41MTgyIDM2Ljc2ODRMMjUuNDMzMSAzMy43NjUzQzI1LjA1NDcgMzMuNDAyNiAyNS4wNTQ3IDMyLjgwNzggMjUuNDMzMSAzMi40NDUxTDI2LjQyMjcgMzEuNDczMVpNNTkuOTY1OCAzNy42ODI0TDYyLjcxNjIgNDAuMzUxOEM2My4wOTQ2IDQwLjcxNDUgNjMuMDk0NiA0MS4zMDkzIDYyLjcxNjIgNDEuNjcyTDUwLjMzMjIgNTMuNzI4QzQ5Ljk1MzggNTQuMDkwNyA0OS4zNDI2IDU0LjA5MDcgNDguOTc4OCA1My43MjhMNDAuMTg5MiA0NS4xNjg0QzQwLjEwMTkgNDUuMDgxMyAzOS45NDE4IDQ1LjA4MTMgMzkuODU0NSA0NS4xNjg0TDMxLjA2NDkgNTMuNzI4QzMwLjY4NjUgNTQuMDkwNyAzMC4wNzUzIDU0LjA5MDcgMjkuNzExNSA1My43MjhMMTcuMjgzOCA0MS42NzJDMTYuOTA1NCA0MS4zMDkzIDE2LjkwNTQgNDAuNzE0NSAxNy4yODM4IDQwLjM1MThMMjAuMDM0MiAzNy42ODI0QzIwLjQxMjUgMzcuMzE5NyAyMS4wMjM3IDM3LjMxOTcgMjEuMzg3NSAzNy42ODI0TDMwLjE3NzIgNDYuMjQyQzMwLjI2NDUgNDYuMzI5IDMwLjQyNDUgNDYuMzI5IDMwLjUxMTkgNDYuMjQyTDM5LjMwMTUgMzcuNjgyNEMzOS42Nzk5IDM3LjMxOTcgNDAuMjkxIDM3LjMxOTcgNDAuNjU0OSAzNy42ODI0TDQ5LjQ0NDUgNDYuMjQyQzQ5LjUzMTggNDYuMzI5IDQ5LjY5MTkgNDYuMzI5IDQ5Ljc3OTIgNDYuMjQyTDU4LjU2ODggMzcuNjgyNEM1OC45NzYzIDM3LjMxOTcgNTkuNTg3NSAzNy4zMTk3IDU5Ljk2NTggMzcuNjgyNFoiIGZpbGw9IndoaXRlIi8+CjxkZWZzPgo8cmFkaWFsR3JhZGllbnQgaWQ9InBhaW50MF9yYWRpYWxfMV80NiIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgwLjAwMDI0Nzk1NSA0MC4wMDEyKSBzY2FsZSg4MCkiPgo8c3RvcCBzdG9wLWNvbG9yPSIjNUQ5REY2Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwNkZGRiIvPgo8L3JhZGlhbEdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo=",
id: "walletconnect",
};
this.metadata = options?.metadata || walletConnectMetadata;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/thirdweb/src/wallets/wallet-connect/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ThirdwebClient } from "../../client/client.js";

type EthereumProviderOptions = Parameters<(typeof EthereumProvider)["init"]>[0];

type QRCodeModalOptions = Pick<
type WalletConnectQRCodeModalOptions = Pick<
NonNullable<EthereumProviderOptions["qrModalOptions"]>,
| "themeMode"
| "themeVariables"
Expand All @@ -30,7 +30,7 @@ export type WalletConnectConnectionOptions = {
optionalChains?: (number | bigint)[];
showQrModal?: boolean;
pairingTopic?: string;
qrModalOptions?: QRCodeModalOptions;
qrModalOptions?: WalletConnectQRCodeModalOptions;
onDisplayUri?: (uri: string) => void;
onSessionRequestSent?: () => void;
};

0 comments on commit c98c794

Please sign in to comment.