-
Notifications
You must be signed in to change notification settings - Fork 476
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
461 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
255 changes: 255 additions & 0 deletions
255
packages/thirdweb/src/react/wallets/smartWallet/SmartWalletConnectUI.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
import { useCallback, useContext, useEffect, useRef, useState } from "react"; | ||
import type { ConnectUIProps, WalletConfig } from "../../types/wallets.js"; | ||
import { HeadlessConnectUI } from "../headlessConnectUI.js"; | ||
import { SmartWallet, type Account } from "../../../wallets/index.js"; | ||
import { useThirdwebProviderProps } from "../../hooks/others/useThirdwebProviderProps.js"; | ||
import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; | ||
import { useTWLocale } from "../../providers/locale-provider.js"; | ||
import { ModalConfigCtx } from "../../providers/wallet-ui-states-provider.js"; | ||
import { Spacer } from "../../ui/components/Spacer.js"; | ||
import { Spinner } from "../../ui/components/Spinner.js"; | ||
import { Container, ModalHeader } from "../../ui/components/basic.js"; | ||
import { Button } from "../../ui/components/buttons.js"; | ||
import { iconSize, spacing, fontSize } from "../../ui/design-system/index.js"; | ||
import { Text } from "../../ui/components/text.js"; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const SmartConnectUI = (props: { | ||
connectUIProps: ConnectUIProps; | ||
personalWalletConfig: WalletConfig; | ||
smartWalletChainId: bigint; | ||
}) => { | ||
const [personalAccount, setPersonalAccount] = useState<Account | null>(null); | ||
// const { personalWalletConnection } = useWalletContext(); | ||
const { personalWalletConfig } = props; | ||
|
||
const { client, dappMetadata } = useThirdwebProviderProps(); | ||
|
||
if (!personalAccount) { | ||
const _props: ConnectUIProps = { | ||
walletConfig: personalWalletConfig, | ||
screenConfig: props.connectUIProps.screenConfig, | ||
createInstance() { | ||
return props.personalWalletConfig.create({ | ||
client: client, | ||
dappMetadata: dappMetadata, | ||
}); | ||
}, | ||
done(account: Account) { | ||
setPersonalAccount(account); | ||
}, | ||
chains: [], | ||
chainId: props.smartWalletChainId, | ||
}; | ||
|
||
if (personalWalletConfig.connectUI) { | ||
return <personalWalletConfig.connectUI {..._props} />; | ||
} | ||
|
||
return <HeadlessConnectUI {..._props} />; | ||
} | ||
|
||
return ( | ||
<SmartWalletConnecting | ||
connectUIProps={props.connectUIProps} | ||
personalWalletConfig={personalWalletConfig} | ||
personalAccount={personalAccount} | ||
smartWalletChainId={props.smartWalletChainId} | ||
// onBack={props.goBack} | ||
// onConnect={() => { | ||
// props.connected(); | ||
// }} | ||
// smartWallet={walletConfig} | ||
// personalWalletConfig={personalWalletConfig} | ||
// personalWallet={personalWalletConnection.activeWallet} | ||
// personalWalletChainId={personalWalletConnection.chainId || 1} | ||
// switchChainPersonalWallet={personalWalletConnection.switchChain} | ||
/> | ||
); | ||
}; | ||
|
||
const SmartWalletConnecting = (props: { | ||
connectUIProps: ConnectUIProps; | ||
personalAccount: Account; | ||
personalWalletConfig: WalletConfig; | ||
smartWalletChainId: bigint; | ||
}) => { | ||
const locale = useTWLocale().wallets.smartWallet; | ||
// const { personalWallet, personalWalletChainId, switchChainPersonalWallet } = | ||
// props; | ||
const createSmartWalletInstance = props.connectUIProps.createInstance; | ||
const wrongNetwork = | ||
props.personalAccount.wallet.chainId !== props.smartWalletChainId; | ||
const { personalAccount } = props; | ||
const { done } = props.connectUIProps; | ||
|
||
const [smartWalletConnectionStatus, setSmartWalletConnectionStatus] = | ||
useState<"connecting" | "connect-error" | "idle">("idle"); | ||
const [personalWalletChainSwitchStatus, setPersonalWalletChainSwitchStatus] = | ||
useState<"switching" | "switch-error" | "idle">("idle"); | ||
|
||
const connectStarted = useRef(false); | ||
|
||
const modalSize = useContext(ModalConfigCtx).modalSize; | ||
|
||
const handleConnect = useCallback(async () => { | ||
if (!personalAccount) { | ||
throw new Error("No personal account"); | ||
} | ||
|
||
setSmartWalletConnectionStatus("connecting"); | ||
|
||
try { | ||
const smartWallet = createSmartWalletInstance() as SmartWallet; // TODO: fix this type | ||
const smartAccount = await smartWallet.connect({ | ||
personalAccount, | ||
}); | ||
|
||
done(smartAccount); | ||
setSmartWalletConnectionStatus("idle"); | ||
} catch (e) { | ||
console.error(e); | ||
setSmartWalletConnectionStatus("connect-error"); | ||
} | ||
}, [createSmartWalletInstance, done, personalAccount]); | ||
|
||
useEffect(() => { | ||
if (!wrongNetwork && !connectStarted.current) { | ||
handleConnect(); | ||
connectStarted.current = true; | ||
} | ||
}, [handleConnect, wrongNetwork]); | ||
|
||
if (smartWalletConnectionStatus === "connecting") { | ||
return ( | ||
<Container | ||
fullHeight | ||
flex="column" | ||
center="both" | ||
style={{ | ||
minHeight: "300px", | ||
}} | ||
> | ||
<Text color="primaryText" multiline center> | ||
{locale.connecting} | ||
</Text> | ||
<Spacer y="lg" /> | ||
<Spinner color="accentText" size="lg" /> | ||
</Container> | ||
); | ||
} | ||
|
||
if (smartWalletConnectionStatus === "connect-error") { | ||
return ( | ||
<Container | ||
fullHeight | ||
animate="fadein" | ||
flex="column" | ||
center="both" | ||
p="lg" | ||
style={{ | ||
minHeight: "300px", | ||
}} | ||
> | ||
<Text color="danger">{locale.failedToConnect}</Text> | ||
</Container> | ||
); | ||
} | ||
|
||
return ( | ||
<Container fullHeight animate="fadein" flex="column"> | ||
<Container p="lg"> | ||
<ModalHeader | ||
title={props.personalWalletConfig.metadata.name} | ||
imgSrc={props.personalWalletConfig.metadata.iconUrl} | ||
onBack={props.connectUIProps.screenConfig.goBack} | ||
/> | ||
</Container> | ||
|
||
{modalSize === "compact" && <Spacer y="lg" />} | ||
|
||
<Container expand flex="column" center="both" p="lg"> | ||
<Container p={modalSize === "wide" ? "lg" : undefined}> | ||
<Container flex="row" center="x" color="danger"> | ||
<ExclamationTriangleIcon width={iconSize.lg} height={iconSize.lg} /> | ||
</Container> | ||
|
||
<Spacer y="md" /> | ||
|
||
<Text size="lg" color="primaryText" center weight={500}> | ||
{locale.wrongNetworkScreen.title} | ||
</Text> | ||
|
||
<Spacer y="lg" /> | ||
|
||
<Text multiline center> | ||
{locale.wrongNetworkScreen.subtitle} | ||
</Text> | ||
|
||
<Spacer y="xl" /> | ||
|
||
<Container flex="column" gap="md"> | ||
<Button | ||
type="button" | ||
fullWidth | ||
variant="accent" | ||
style={{ | ||
display: "flex", | ||
alignItems: "center", | ||
gap: spacing.sm, | ||
}} | ||
onClick={async () => { | ||
// setConnectError(false); | ||
// setSwitchError(false); | ||
// setSwitchingNetwork(true); | ||
const switchPersonalWalletChain = | ||
personalAccount.wallet.switchChain; | ||
if (!switchPersonalWalletChain) { | ||
setPersonalWalletChainSwitchStatus("switch-error"); | ||
throw new Error("No switchChain method"); | ||
} | ||
|
||
try { | ||
await switchPersonalWalletChain(props.smartWalletChainId); | ||
setPersonalWalletChainSwitchStatus("idle"); | ||
} catch (e) { | ||
setPersonalWalletChainSwitchStatus("switch-error"); | ||
} | ||
}} | ||
> | ||
{" "} | ||
{personalWalletChainSwitchStatus === "switching" | ||
? "Switching" | ||
: "Switch Network"} | ||
{personalWalletChainSwitchStatus === "switching" && ( | ||
<Spinner size="sm" color="accentButtonText" /> | ||
)} | ||
</Button> | ||
|
||
<Container | ||
flex="row" | ||
gap="sm" | ||
center="both" | ||
color="danger" | ||
style={{ | ||
textAlign: "center", | ||
fontSize: fontSize.sm, | ||
opacity: | ||
personalWalletChainSwitchStatus === "switch-error" ? 1 : 0, | ||
transition: "opacity 200ms ease", | ||
}} | ||
> | ||
<ExclamationTriangleIcon | ||
width={iconSize.sm} | ||
height={iconSize.sm} | ||
/> | ||
<span>{locale.wrongNetworkScreen.failedToSwitch}</span> | ||
</Container> | ||
</Container> | ||
</Container> | ||
</Container> | ||
</Container> | ||
); | ||
}; | ||
61 changes: 61 additions & 0 deletions
61
packages/thirdweb/src/react/wallets/smartWallet/smartWalletConfig.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { | ||
smartWallet, | ||
type SmartWalletOptions, | ||
} from "../../../wallets/index.js"; | ||
import type { WalletConfig } from "../../types/wallets.js"; | ||
import { SmartConnectUI } from "./SmartWalletConnectUI.js"; | ||
|
||
export type SmartWalletConfigOptions = Omit< | ||
SmartWalletOptions, | ||
"personalAccount" | "client" | ||
>; | ||
|
||
/** | ||
* Integrate Smart wallet connection into your app. | ||
* @param walletConfig - WalletConfig object of a personal wallet to use with the smart wallet. | ||
* @param options - Options for configuring the Smart wallet. | ||
* @example | ||
* ```tsx | ||
* <ThirdwebProvider | ||
* client={client}> | ||
* wallets={[ | ||
* smartWalletConfig(metamaskConfig(), smartWalletOptions), | ||
* smartWalletConfig(coinbaseConfig(), smartWalletOptions) | ||
* ]} | ||
* <App /> | ||
* </ThirdwebProvider> | ||
* ``` | ||
* @returns WalletConfig object to be passed into `ThirdwebProvider` | ||
*/ | ||
export const smartWalletConfig = ( | ||
walletConfig: WalletConfig, | ||
options: SmartWalletConfigOptions, | ||
): WalletConfig => { | ||
const config: WalletConfig = { | ||
metadata: walletConfig.metadata, | ||
create(createOptions) { | ||
const wallet = smartWallet({ ...options, client: createOptions.client }); | ||
wallet.metadata = walletConfig.metadata; | ||
return wallet; | ||
}, | ||
connectUI(props) { | ||
const chain = options.chain; | ||
const chainId = | ||
typeof chain === "bigint" | ||
? chain | ||
: typeof chain === "number" | ||
? BigInt(chain) | ||
: BigInt(chain.id); | ||
|
||
return ( | ||
<SmartConnectUI | ||
connectUIProps={props} | ||
personalWalletConfig={walletConfig} | ||
smartWalletChainId={chainId} | ||
/> | ||
); | ||
}, | ||
}; | ||
|
||
return config; | ||
}; | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.