Skip to content

Commit 24c1670

Browse files
joaquim-vergesMananTankjnsdls
authored
feat: smartAccount option for in-app wallets (#2931)
Co-authored-by: Manan Tank <manantankm@gmail.com> Co-authored-by: Jonas Daniels <jonas.daniels@outlook.com>
1 parent b1ca05e commit 24c1670

File tree

21 files changed

+261
-115
lines changed

21 files changed

+261
-115
lines changed

.changeset/cuddly-news-flow.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
New `smartAccount` prop for `inAppWallet()`
6+
7+
You can now convert an inAppWallet to a smart account simply by passing the `smartAccount` prop.
8+
9+
```ts
10+
const wallet = inAppWallet({
11+
smartAccount: {
12+
chain: sepolia,
13+
sponsorGas: true,
14+
},
15+
});
16+
17+
await wallet.connect({
18+
client,
19+
strategy: "google",
20+
});
21+
```
22+
23+
Note: beware that when toggling this flag on and off, you will get a different address (admin EOA vs smart account).

packages/thirdweb/src/exports/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export {
132132
// abi
133133
// ------------------------------------------------
134134
export { encodeAbiParameters } from "../utils/abi/encodeAbiParameters.js";
135+
export { encodePacked } from "viem";
135136

136137
// Useful helpers
137138
export { setThirdwebDomains } from "../utils/domains.js";

packages/thirdweb/src/exports/wallets.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,6 @@ export { injectedProvider } from "../wallets/injected/mipdStore.js";
4343

4444
export type {
4545
WalletId,
46-
InAppWalletCreationOptions,
47-
/**
48-
* @deprecated use InAppWalletCreationOptions instead
49-
*/
50-
InAppWalletCreationOptions as EmbeddedWalletCreationOptions,
5146
WalletAutoConnectionOption,
5247
WalletCreationOptions,
5348
WalletConnectionOption,
@@ -90,7 +85,7 @@ export type {
9085
* @deprecated use InAppWalletSocialAuth instead
9186
*/
9287
InAppWalletSocialAuth as EmbeddedWalletSocialAuth,
93-
} from "../wallets/in-app/core/wallet/index.js";
88+
} from "../wallets/in-app/core/wallet/types.js";
9489

9590
export type { CoinbaseSDKWalletConnectionOptions } from "../wallets/coinbase/coinbaseSDKWallet.js";
9691

packages/thirdweb/src/react/web/ui/ConnectWallet/Details.tsx

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
useSwitchActiveWalletChain,
3131
} from "../../../core/hooks/wallets/wallet-hooks.js";
3232
import { shortenString } from "../../../core/utils/addresses.js";
33+
import { hasSmartAccount } from "../../utils/isSmartWallet.js";
3334
import { ChainIcon } from "../components/ChainIcon.js";
3435
import { CopyIcon } from "../components/CopyIcon.js";
3536
import { Img } from "../components/Img.js";
@@ -61,6 +62,7 @@ import { NetworkSelectorContent } from "./NetworkSelector.js";
6162
import { onModalUnmount } from "./constants.js";
6263
import type { SupportedTokens } from "./defaultTokens.js";
6364
import { FundsIcon } from "./icons/FundsIcon.js";
65+
import { SmartWalletBadgeIcon } from "./icons/SmartAccountBadgeIcon.js";
6466
import { WalletIcon } from "./icons/WalletIcon.js";
6567
import { BuyScreen } from "./screens/Buy/SwapScreen.js";
6668
import { swapTransactionsStore } from "./screens/Buy/swap/pendingSwapTx.js";
@@ -213,12 +215,7 @@ export const ConnectedWalletDetails: React.FC<{
213215
client={client}
214216
/>
215217
) : activeWallet?.id ? (
216-
<WalletImage
217-
size={iconSize.lg}
218-
id={activeWallet.id}
219-
client={client}
220-
allowOverrides
221-
/>
218+
<WalletImage size={iconSize.lg} id={activeWallet.id} client={client} />
222219
) : (
223220
<WalletIcon size={iconSize.lg} />
224221
)}
@@ -333,7 +330,6 @@ export const ConnectedWalletDetails: React.FC<{
333330
size={iconSize.xxl}
334331
id={activeWallet.id}
335332
client={client}
336-
allowOverrides
337333
/>
338334
) : (
339335
<WalletIcon size={iconSize.xxl} />
@@ -386,9 +382,7 @@ export const ConnectedWalletDetails: React.FC<{
386382
{balanceQuery.data?.symbol}{" "}
387383
</Text>
388384
</Container>
389-
390385
<Spacer y="lg" />
391-
392386
<Container px="lg">
393387
{/* Send, Receive, Swap */}
394388
<Container
@@ -463,9 +457,7 @@ export const ConnectedWalletDetails: React.FC<{
463457
</Button>
464458
</Container>
465459
</Container>
466-
467460
<Spacer y="md" />
468-
469461
<Container px="md">
470462
{/* Network Switcher */}
471463
<Container
@@ -814,21 +806,11 @@ const StyledChevronRightIcon = /* @__PURE__ */ styled(
814806
// );
815807
// }
816808

817-
const ActiveDot = /* @__PURE__ */ StyledDiv(() => {
818-
const theme = useCustomTheme();
819-
return {
820-
width: "8px",
821-
height: "8px",
822-
borderRadius: "50%",
823-
backgroundColor: theme.colors.success,
824-
};
825-
});
826-
827809
function ConnectedToSmartWallet() {
828810
const activeAccount = useActiveAccount();
829811
const activeWallet = useActiveWallet();
812+
const isSmartWallet = hasSmartAccount(activeWallet);
830813
const chain = useActiveWalletChain();
831-
const isSmartWallet = activeWallet?.id === "smart";
832814
const { client, connectLocale: locale } = useConnectUI();
833815

834816
const [isSmartWalletDeployed, setIsSmartWalletDeployed] = useState(false);
@@ -850,9 +832,22 @@ function ConnectedToSmartWallet() {
850832
}, [activeAccount, chain, client, isSmartWallet]);
851833

852834
const content = (
853-
<Container flex="row" gap="xxs" center="both">
854-
<ActiveDot />
855-
{locale.connectedToSmartWallet}
835+
<Container
836+
flex="row"
837+
bg="secondaryButtonBg"
838+
gap="xxs"
839+
style={{
840+
borderRadius: radius.md,
841+
padding: `${spacing.xxs} ${spacing.sm} ${spacing.xxs} ${spacing.xs}`,
842+
}}
843+
center="y"
844+
>
845+
<Container flex="row" color="accentText" center="both">
846+
<SmartWalletBadgeIcon size={iconSize.xs} />
847+
</Container>
848+
<Text size="xs" color="secondaryButtonText">
849+
{locale.connectedToSmartWallet}
850+
</Text>
856851
</Container>
857852
);
858853

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { IconFC } from "./types.js";
2+
3+
/**
4+
* @internal
5+
*/
6+
export const SmartWalletBadgeIcon: IconFC = (props) => {
7+
return (
8+
<svg
9+
xmlns="http://www.w3.org/2000/svg"
10+
fill="none"
11+
viewBox="0 0 12 12"
12+
width={props.size}
13+
height={props.size}
14+
role="presentation"
15+
>
16+
<g clipPath="url(#clip0_5539_26604)">
17+
<path
18+
d="M10 6.85691C10 9.35691 8.25 10.6069 6.17 11.3319C6.06108 11.3688 5.94277 11.3671 5.835 11.3269C3.75 10.6069 2 9.35691 2 6.85691V3.35691C2 3.22431 2.05268 3.09713 2.14645 3.00336C2.24021 2.90959 2.36739 2.85691 2.5 2.85691C3.5 2.85691 4.75 2.25691 5.62 1.49691C5.72593 1.40641 5.86068 1.35669 6 1.35669C6.13932 1.35669 6.27407 1.40641 6.38 1.49691C7.255 2.26191 8.5 2.85691 9.5 2.85691C9.63261 2.85691 9.75979 2.90959 9.85355 3.00336C9.94732 3.09713 10 3.22431 10 3.35691V6.85691Z"
19+
stroke="currentColor"
20+
strokeLinecap="round"
21+
strokeLinejoin="round"
22+
/>
23+
<path
24+
d="M4.5 6.35693L5.5 7.35693L7.5 5.35693"
25+
stroke="currentColor"
26+
strokeLinecap="round"
27+
strokeLinejoin="round"
28+
/>
29+
</g>
30+
<defs>
31+
<clipPath id="clip0_5539_26604">
32+
<rect
33+
width="12"
34+
height="12"
35+
fill="white"
36+
transform="translate(0 0.356934)"
37+
/>
38+
</clipPath>
39+
</defs>
40+
</svg>
41+
);
42+
};

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/AccountSelectorButton.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export function AccountSelectorButton(props: {
3131
id={props.activeWallet.id}
3232
size={iconSize.md}
3333
client={props.client}
34-
allowOverrides
3534
/>
3635
) : (
3736
<Container color="secondaryText" flex="row" center="both">

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/ReceiveFunds.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export function ReceiveFunds(props: {
4343
id={props.walletId}
4444
size={iconSize.xxl}
4545
client={client}
46-
allowOverrides
4746
/>
4847
)
4948
}

packages/thirdweb/src/react/web/ui/components/WalletImage.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
"use client";
22
import { useEffect, useState } from "react";
33
import type { ThirdwebClient } from "../../../../client/client.js";
4+
import { useActiveWallet } from "../../../../exports/react-native.js";
45
import { getInstalledWalletProviders } from "../../../../wallets/injected/mipdStore.js";
6+
import { getStoredActiveWalletId } from "../../../../wallets/manager/index.js";
57
import type { WalletId } from "../../../../wallets/wallet-types.js";
8+
import { getStorage } from "../../../core/storage.js";
69
import { getLastAuthProvider } from "../../wallets/in-app/storage.js";
710
import { emailIcon, phoneIcon } from "../ConnectWallet/icons/dataUris.js";
811
import {
@@ -23,18 +26,33 @@ export function WalletImage(props: {
2326
id: WalletId;
2427
size: string;
2528
client: ThirdwebClient;
26-
allowOverrides?: boolean;
2729
}) {
2830
const [image, setImage] = useState<string | undefined>(undefined);
31+
const activeWallet = useActiveWallet();
2932
useEffect(() => {
3033
async function fetchImage() {
34+
// show EOA icon for external wallets
35+
// show auth provider icon for in-app wallets
36+
// show the admin EOA icon for smart
37+
const storage = getStorage();
38+
let activeEOAId = props.id;
39+
if (props.id === "smart") {
40+
const storedId = await getStoredActiveWalletId(storage);
41+
if (storedId) {
42+
activeEOAId = storedId;
43+
}
44+
}
3145
let mipdImage = getInstalledWalletProviders().find(
32-
(provider) => provider.info.rdns === props.id,
46+
(provider) => provider.info.rdns === activeEOAId,
3347
)?.info.icon;
3448

35-
if (props.allowOverrides && props.id === "inApp") {
36-
// check last auth provider and override the IAW icon
37-
const lastAuthProvider = await getLastAuthProvider();
49+
if (
50+
activeEOAId === "inApp" &&
51+
activeWallet &&
52+
(activeWallet.id === "inApp" || activeWallet.id === "smart")
53+
) {
54+
// when showing an active wallet icon - check last auth provider and override the IAW icon
55+
const lastAuthProvider = await getLastAuthProvider(storage);
3856
switch (lastAuthProvider) {
3957
case "google":
4058
mipdImage = googleIconUri;
@@ -57,7 +75,7 @@ export function WalletImage(props: {
5775
setImage(mipdImage);
5876
}
5977
fetchImage();
60-
}, [props.id, props.allowOverrides]);
78+
}, [props.id, activeWallet]);
6179

6280
if (image) {
6381
return (
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { Wallet } from "../../../wallets/interfaces/wallet.js";
2+
3+
export function hasSmartAccount(activeWallet?: Wallet): boolean {
4+
const config = activeWallet?.getConfig();
5+
return (
6+
!!activeWallet &&
7+
(activeWallet.id === "smart" ||
8+
(activeWallet.id === "inApp" && !!config && "smartAccount" in config))
9+
);
10+
}

packages/thirdweb/src/react/web/wallets/in-app/InAppWalletFormUI.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useCallback, useState } from "react";
44
import type {
55
InAppWalletAuth,
66
InAppWalletSocialAuth,
7-
} from "../../../../wallets/in-app/core/wallet/index.js";
7+
} from "../../../../wallets/in-app/core/wallet/types.js";
88
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
99
import { useConnectUI } from "../../../core/hooks/others/useWalletConnectionCtx.js";
1010
import { useSetSelectionData } from "../../providers/wallet-ui-states-provider.js";

0 commit comments

Comments
 (0)