Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDK] Feature: Ecosystem account linking #4733

Merged
merged 1 commit into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nine-ladybugs-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Enable account linking for ecosystem wallets
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type UseQueryResult, useQuery } from "@tanstack/react-query";
import { isEcosystemWallet } from "../../../../wallets/ecosystem/is-ecosystem-wallet.js";
import type { Profile } from "../../../../wallets/in-app/core/authentication/types.js";
import { getProfiles } from "../../../../wallets/in-app/core/wallet/profiles.js";
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
Expand Down Expand Up @@ -28,7 +29,7 @@

return useQuery({
queryKey: ["profiles", wallet?.id],
enabled: !!wallet && wallet.id === "inApp",
enabled: !!wallet && (wallet.id === "inApp" || isEcosystemWallet(wallet)),

Check warning on line 32 in packages/thirdweb/src/react/core/hooks/others/useProfiles.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/core/hooks/others/useProfiles.ts#L32

Added line #L32 was not covered by tests
queryFn: async () => {
return getProfiles(wallet as Wallet<"inApp">);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import { useQueryClient } from "@tanstack/react-query";
import { Suspense, lazy } from "react";
import type { ThirdwebClient } from "../../../../../client/client.js";
import { isEcosystemWallet } from "../../../../../wallets/ecosystem/is-ecosystem-wallet.js";
import type { Wallet } from "../../../../../wallets/interfaces/wallet.js";
import type { EcosystemWalletId } from "../../../../../wallets/wallet-types.js";
import { iconSize } from "../../../../core/design-system/index.js";
import { useActiveWallet } from "../../../../core/hooks/wallets/useActiveWallet.js";
import { useActiveWalletChain } from "../../../../core/hooks/wallets/useActiveWalletChain.js";
import EcosystemWalletConnectUI from "../../../wallets/ecosystem/EcosystemWalletConnectUI.js";
import { LoadingScreen } from "../../../wallets/shared/LoadingScreen.js";
import { Container, Line, ModalHeader } from "../../components/basic.js";
import { Text } from "../../components/text.js";
Expand Down Expand Up @@ -58,6 +61,30 @@
);
}

if (isEcosystemWallet(activeWallet)) {
return (
<Suspense fallback={<LoadingScreen />}>
<EcosystemWalletConnectUI
wallet={activeWallet as Wallet<EcosystemWalletId>}
done={() => {
queryClient.invalidateQueries({ queryKey: ["profiles"] });
props.onBack();
}}
connectLocale={props.locale}
client={props.client}
size="compact"
chain={chain}
meta={{
title: props.locale.manageWallet.linkProfile,
showThirdwebBranding: false,
}}
isLinking={true}
goBack={props.onBack}
/>
</Suspense>

Check warning on line 84 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/LinkProfileScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/LinkProfileScreen.tsx#L64-L84

Added lines #L64 - L84 were not covered by tests
);
}

Check warning on line 86 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/LinkProfileScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/LinkProfileScreen.tsx#L86

Added line #L86 was not covered by tests

return (
<Container
style={{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";
import { ShuffleIcon } from "@radix-ui/react-icons";
import type { ThirdwebClient } from "../../../../../client/client.js";
import { isEcosystemWallet } from "../../../../../wallets/ecosystem/is-ecosystem-wallet.js";
import { isInAppWallet } from "../../../../../wallets/in-app/core/wallet/index.js";
import { injectedProvider } from "../../../../../wallets/injected/mipdStore.js";
import { fontSize, iconSize } from "../../../../core/design-system/index.js";
Expand Down Expand Up @@ -56,21 +57,23 @@
/>

{/* Multi-auth */}
{activeWallet?.id === "inApp" && (
<MenuButton
onClick={() => {
props.setScreen("linked-profiles");
}}
style={{
fontSize: fontSize.sm,
}}
>
<MultiUserIcon size={iconSize.md} />
<Text color="primaryText">
{props.locale.manageWallet.linkedProfiles}
</Text>
</MenuButton>
)}
{activeWallet &&
(activeWallet?.id === "inApp" ||
isEcosystemWallet(activeWallet)) && (
<MenuButton
onClick={() => {
props.setScreen("linked-profiles");
}}
style={{
fontSize: fontSize.sm,
}}

Check warning on line 69 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/ManageWalletScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/ManageWalletScreen.tsx#L60-L69

Added lines #L60 - L69 were not covered by tests
>
<MultiUserIcon size={iconSize.md} />
<Text color="primaryText">
{props.locale.manageWallet.linkedProfiles}
</Text>
</MenuButton>

Check warning on line 75 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/ManageWalletScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/ManageWalletScreen.tsx#L71-L75

Added lines #L71 - L75 were not covered by tests
)}

{/* Wallet Connect Receiver */}
<MenuButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@
return <LoadingScreen />;
}

const goBackToMain =
props.size === "compact"
? props.goBack
: () => {
setSelectionData({});
};
const goBackToMain = () => {
if (props.size === "compact") {
props.goBack?.();
}
setSelectionData({});
};

Check warning on line 55 in packages/thirdweb/src/react/web/wallets/ecosystem/EcosystemWalletConnectUI.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/wallets/ecosystem/EcosystemWalletConnectUI.tsx#L50-L55

Added lines #L50 - L55 were not covered by tests

const done = () => {
props.done();
Expand Down
20 changes: 0 additions & 20 deletions packages/thirdweb/src/react/web/wallets/in-app/LinkButton.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@

const connectPromise = (() => {
if (props.isLinking) {
if (wallet.id !== "inApp") {
if (wallet.id !== "inApp" && !isEcosystemWallet(wallet)) {

Check warning on line 269 in packages/thirdweb/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/wallets/shared/ConnectWalletSocialOptions.tsx#L269

Added line #L269 was not covered by tests
throw new Error("Only in-app wallets support multi-auth");
}
return linkProfile(wallet, connectOptions);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { ThirdwebClient } from "../../../../client/client.js";
import { getThirdwebBaseUrl } from "../../../../utils/domains.js";
import { getClientFetch } from "../../../../utils/fetch.js";
import type { Ecosystem } from "../../web/types.js";
import type { Profile } from "./types.js";

/**
Expand All @@ -11,27 +13,28 @@
*/
export async function linkAccount({
client,
ecosystem,

Check warning on line 16 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L16

Added line #L16 was not covered by tests
tokenToLink,
}: {
client: ThirdwebClient;
ecosystem?: Ecosystem;
tokenToLink: string;
}): Promise<Profile[]> {
const clientFetch = getClientFetch(client, ecosystem);

Check warning on line 23 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L23

Added line #L23 was not covered by tests
const IN_APP_URL = getThirdwebBaseUrl("inAppWallet");
const currentAccountToken = localStorage.getItem(
`walletToken-${client.clientId}`,
`walletToken-${client.clientId}${ecosystem?.id ? `-${ecosystem.id}` : ""}`,

Check warning on line 26 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L26

Added line #L26 was not covered by tests
);

if (!currentAccountToken) {
throw new Error("Failed to link account, no user logged in");
}

const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: `Bearer iaw-auth-token:${currentAccountToken}`,
"x-thirdweb-client-id": client.clientId,
"Content-Type": "application/json",

Check warning on line 35 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L35

Added line #L35 was not covered by tests
};

const linkedDetailsResp = await fetch(
const linkedDetailsResp = await clientFetch(

Check warning on line 37 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L37

Added line #L37 was not covered by tests
`${IN_APP_URL}/api/2024-05-05/account/connect`,
{
method: "POST",
Expand Down Expand Up @@ -61,23 +64,23 @@
*/
export async function getLinkedProfilesInternal({
client,
}: { client: ThirdwebClient }): Promise<Profile[]> {
ecosystem,
}: { client: ThirdwebClient; ecosystem?: Ecosystem }): Promise<Profile[]> {
const clientFetch = getClientFetch(client, ecosystem);

Check warning on line 69 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L67-L69

Added lines #L67 - L69 were not covered by tests
const IN_APP_URL = getThirdwebBaseUrl("inAppWallet");
const currentAccountToken = localStorage.getItem(
`walletToken-${client.clientId}`,
`walletToken-${client.clientId}${ecosystem?.id ? `-${ecosystem.id}` : ""}`,

Check warning on line 72 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L72

Added line #L72 was not covered by tests
);

if (!currentAccountToken) {
throw new Error("Failed to get linked accounts, no user logged in");
}

const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: `Bearer iaw-auth-token:${currentAccountToken}`,
"x-thirdweb-client-id": client.clientId,
"Content-Type": "application/json",

Check warning on line 77 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L77

Added line #L77 was not covered by tests
};
if (!currentAccountToken) {
throw new Error("Failed to get linked accounts, no user logged in");
}

Check warning on line 81 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L79-L81

Added lines #L79 - L81 were not covered by tests

const linkedAccountsResp = await fetch(
const linkedAccountsResp = await clientFetch(

Check warning on line 83 in packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/authentication/linkAccount.ts#L83

Added line #L83 was not covered by tests
`${IN_APP_URL}/api/2024-05-05/accounts`,
{
method: "GET",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
CreateWalletArgs,
EcosystemWalletId,
} from "../../../wallet-types.js";
import type { Ecosystem } from "../../web/types.js";
import {
getLinkedProfilesInternal,
linkAccount as linkProfileWithToken,
} from "../authentication/linkAccount.js";
import type {
MultiStepAuthArgsType,
Profile,
SingleStepAuthArgsType,
} from "../authentication/types.js";
import type { InAppConnector } from "../interfaces/connector.js";
import { getOrCreateInAppWalletConnector } from "./in-app-core.js";

Expand All @@ -25,6 +35,10 @@
let account: Account | undefined = undefined;
let chain: Chain | undefined = undefined;
let client: ThirdwebClient | undefined;
const ecosystem: Ecosystem = {
id,
partnerId: createOptions?.partnerId,
};

return {
id,
Expand All @@ -38,18 +52,21 @@
return chain;
},
getConfig: () => createOptions,
getProfiles: async () => {
if (!client) {
return [];
}

Check warning on line 58 in packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts#L56-L58

Added lines #L56 - L58 were not covered by tests

return getLinkedProfilesInternal({ client, ecosystem });
},

Check warning on line 61 in packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts#L60-L61

Added lines #L60 - L61 were not covered by tests
getAccount: () => account,
autoConnect: async (options) => {
const { autoConnectInAppWallet } = await import("./index.js");

const connector = await getOrCreateInAppWalletConnector(
options.client,
connectorFactory,
{
id,
partnerId: createOptions?.partnerId,
},
ecosystem,

Check warning on line 69 in packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts#L69

Added line #L69 was not covered by tests
);

const [connectedAccount, connectedChain] = await autoConnectInAppWallet(
Expand All @@ -75,10 +92,7 @@
const connector = await getOrCreateInAppWalletConnector(
options.client,
connectorFactory,
{
id,
partnerId: createOptions?.partnerId,
},
ecosystem,

Check warning on line 95 in packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts#L95

Added line #L95 was not covered by tests
);

const [connectedAccount, connectedChain] = await connectInAppWallet(
Expand All @@ -104,10 +118,7 @@
const connector = await getOrCreateInAppWalletConnector(
client,
connectorFactory,
{
id,
partnerId: createOptions?.partnerId,
},
ecosystem,

Check warning on line 121 in packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts#L121

Added line #L121 was not covered by tests
);
const result = await connector.logout();
if (!result.success) {
Expand All @@ -122,5 +133,28 @@
chain = newChain;
emitter.emit("chainChanged", newChain);
},
};
// This is not included on the global interface but is force-resolved in linkProfile
linkProfile: async (
options: SingleStepAuthArgsType | MultiStepAuthArgsType,
): Promise<Profile[]> => {
if (!client) {
throw new Error(
"No client found, please connect the wallet before linking a profile",
);
}

Check warning on line 144 in packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts#L138-L144

Added lines #L138 - L144 were not covered by tests

const connector = await getOrCreateInAppWalletConnector(
client,
connectorFactory,
ecosystem,
);

Check warning on line 150 in packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts#L146-L150

Added lines #L146 - L150 were not covered by tests

const { storedToken } = await connector.authenticate(options);
return await linkProfileWithToken({
client,
ecosystem,
tokenToLink: storedToken.cookieString,
});
},

Check warning on line 158 in packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/ecosystem-core.ts#L152-L158

Added lines #L152 - L158 were not covered by tests
} as Wallet<EcosystemWalletId>;
}
10 changes: 6 additions & 4 deletions packages/thirdweb/src/wallets/in-app/core/wallet/profiles.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isEcosystemWallet } from "../../../../wallets/ecosystem/is-ecosystem-wallet.js";
import type { EcosystemWalletId } from "../../../../wallets/wallet-types.js";
import type { Wallet } from "../../../interfaces/wallet.js";
import type {
MultiStepAuthArgsType,
Expand Down Expand Up @@ -25,8 +27,8 @@
* ```
* @wallet
*/
export async function getProfiles(wallet: Wallet<"inApp">) {
if (wallet.id !== "inApp") {
export async function getProfiles(wallet: Wallet<"inApp" | EcosystemWalletId>) {
if (wallet.id !== "inApp" && !isEcosystemWallet(wallet)) {

Check warning on line 31 in packages/thirdweb/src/wallets/in-app/core/wallet/profiles.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/profiles.ts#L30-L31

Added lines #L30 - L31 were not covered by tests
throw new Error("Multi-auth currently only supports in-app wallets");
}

Expand Down Expand Up @@ -59,10 +61,10 @@
* @wallet
*/
export async function linkProfile(
wallet: Wallet<"inApp">,
wallet: Wallet<"inApp" | EcosystemWalletId>,

Check warning on line 64 in packages/thirdweb/src/wallets/in-app/core/wallet/profiles.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/profiles.ts#L64

Added line #L64 was not covered by tests
auth: MultiStepAuthArgsType | SingleStepAuthArgsType,
): Promise<Profile[]> {
if (wallet.id !== "inApp") {
if (wallet.id !== "inApp" && !isEcosystemWallet(wallet)) {

Check warning on line 67 in packages/thirdweb/src/wallets/in-app/core/wallet/profiles.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/wallets/in-app/core/wallet/profiles.ts#L67

Added line #L67 was not covered by tests
throw new Error("Multi-auth currently only supports in-app wallets");
}

Expand Down
Loading