Skip to content
Open
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
6 changes: 3 additions & 3 deletions examples/nextjs-bridge-mayan/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
"lint": "next lint"
},
"dependencies": {
"@dynamic-labs-sdk/client": "1.2.1",
"@dynamic-labs-sdk/evm": "1.2.1",
"@dynamic-labs-sdk/react-hooks": "0.26.5",
"@dynamic-labs-sdk/client": "1.4.0",
"@dynamic-labs-sdk/evm": "1.4.0",
"@dynamic-labs-sdk/react-hooks": "1.4.0",
"@mayanfinance/swap-sdk": "10.9.3",
"lucide-react": "0.542.0",
"@tanstack/react-query": "5.85.3",
Expand Down
27 changes: 19 additions & 8 deletions examples/nextjs-bridge-mayan/src/lib/dynamic.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import { createDynamicClient } from "@dynamic-labs-sdk/client";
import { addEvmExtension } from "@dynamic-labs-sdk/evm";
import {
createDynamicClient,
initializeClient,
type DynamicClient,
} from "@dynamic-labs-sdk/client";
import { addWaasEvmExtension } from "@dynamic-labs-sdk/evm/waas";

export const dynamicClient = createDynamicClient({
export const dynamicClient: DynamicClient = createDynamicClient({
environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENV_ID!,
autoInitialize: false,
metadata: { name: "Mayan Bridge" },
});

if (typeof window !== "undefined") {
addEvmExtension();
}
let initialized = false;

// No-op on clients that auto-initialize; called by useAuth on mount.
export async function initDynamic(): Promise<void> {}
/**
* Adds the EVM WaaS extension and initializes the client.
* Safe to call multiple times — initialization runs once.
*/
export async function initDynamic(): Promise<void> {
if (initialized) return;
initialized = true;
addWaasEvmExtension(dynamicClient);
await initializeClient(dynamicClient);
}
58 changes: 24 additions & 34 deletions examples/nextjs-bridge-mayan/src/lib/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ import {
} from "react";
import {
getWalletAccounts,
onEvent,
isSignedIn,
logout,
detectOAuthRedirect,
completeSocialAuthentication,
detectSocialRedirectUrl,
completeSocialRedirect,
} from "@dynamic-labs-sdk/client";
import { createWaasWalletAccounts } from "@dynamic-labs-sdk/client/waas";
import { isEvmWalletAccount, type EvmWalletAccount } from "@dynamic-labs-sdk/evm";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { DynamicProvider, useUser, useWalletAccounts } from "@dynamic-labs-sdk/react-hooks";
import { dynamicClient } from "./dynamic";
import { DynamicProvider, useUser, useWalletAccounts, useEvent } from "@dynamic-labs-sdk/react-hooks";
import { dynamicClient, initDynamic } from "./dynamic";

interface WalletContextValue {
evmAccount: EvmWalletAccount | null;
Expand Down Expand Up @@ -48,6 +47,24 @@ const queryClient = new QueryClient({
},
});

function DynamicBootstrap() {
useEffect(() => {
let cancelled = false;
initDynamic().then(async () => {
if (cancelled || typeof window === "undefined") return;
try {
const url = new URL(window.location.href);
if (await detectSocialRedirectUrl({ url })) {
await completeSocialRedirect({ url });
window.history.replaceState({}, "", window.location.pathname);
}
} catch { /* not a social redirect */ }
});
return () => { cancelled = true; };
}, []);
return null;
}

function InnerProviders({ children }: { children: ReactNode }) {
const loggedIn = useUser() !== null;
const evmAccount = useWalletAccounts().find(isEvmWalletAccount) ?? null;
Expand All @@ -67,35 +84,7 @@ function InnerProviders({ children }: { children: ReactNode }) {
}
}, []);

useEffect(() => {
const unsub = onEvent(
{
event: "walletAccountsChanged",
listener: () => {
void ensureEvmWallet();
},
},
dynamicClient,
);
return () => unsub?.();
}, [ensureEvmWallet]);

useEffect(() => {
const handleOAuthRedirect = async () => {
if (typeof window === "undefined") return;
try {
const url = new URL(window.location.href);
if (await detectOAuthRedirect({ url }, dynamicClient)) {
await completeSocialAuthentication({ url }, dynamicClient);
await ensureEvmWallet();
window.history.replaceState({}, "", window.location.pathname);
}
} catch {
// not an OAuth redirect
}
};
handleOAuthRedirect();
}, [ensureEvmWallet]);
useEvent({ event: "walletAccountsChanged", listener: () => { void ensureEvmWallet(); } });

return (
<WalletContext.Provider
Expand All @@ -109,6 +98,7 @@ function InnerProviders({ children }: { children: ReactNode }) {
export default function Providers({ children }: { children: ReactNode }) {
return (
<DynamicProvider client={dynamicClient}>
<DynamicBootstrap />
<InnerProviders>{children}</InnerProviders>
</DynamicProvider>
);
Expand Down
6 changes: 3 additions & 3 deletions examples/nextjs-defi-lending-morpho/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
},
"dependencies": {
"@coinbase/onchainkit": "0.38.17",
"@dynamic-labs-sdk/client": "1.2.1",
"@dynamic-labs-sdk/evm": "1.2.1",
"@dynamic-labs-sdk/react-hooks": "0.26.5",
"@dynamic-labs-sdk/client": "1.4.0",
"@dynamic-labs-sdk/evm": "1.4.0",
"@dynamic-labs-sdk/react-hooks": "1.4.0",
"@radix-ui/react-dialog": "1.1.15",
"@radix-ui/react-dropdown-menu": "2.1.16",
"@radix-ui/react-slot": "1.2.3",
Expand Down
27 changes: 19 additions & 8 deletions examples/nextjs-defi-lending-morpho/src/lib/dynamic.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import { createDynamicClient } from "@dynamic-labs-sdk/client";
import { addEvmExtension } from "@dynamic-labs-sdk/evm";
import {
createDynamicClient,
initializeClient,
type DynamicClient,
} from "@dynamic-labs-sdk/client";
import { addWaasEvmExtension } from "@dynamic-labs-sdk/evm/waas";

export const dynamicClient = createDynamicClient({
export const dynamicClient: DynamicClient = createDynamicClient({
environmentId: process.env.NEXT_PUBLIC_DYNAMIC_ENV_ID!,
autoInitialize: false,
metadata: { name: "Morpho Lending" },
});

if (typeof window !== "undefined") {
addEvmExtension();
}
let initialized = false;

// No-op on clients that auto-initialize; called by useAuth on mount.
export async function initDynamic(): Promise<void> {}
/**
* Adds the EVM WaaS extension and initializes the client.
* Safe to call multiple times — initialization runs once.
*/
export async function initDynamic(): Promise<void> {
if (initialized) return;
initialized = true;
addWaasEvmExtension(dynamicClient);
await initializeClient(dynamicClient);
}
56 changes: 24 additions & 32 deletions examples/nextjs-defi-lending-morpho/src/lib/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ import {
} from "react";
import {
getWalletAccounts,
onEvent,
isSignedIn,
logout,
detectOAuthRedirect,
completeSocialAuthentication,
detectSocialRedirectUrl,
completeSocialRedirect,
getActiveNetworkId,
} from "@dynamic-labs-sdk/client";
import { createWaasWalletAccounts } from "@dynamic-labs-sdk/client/waas";
import { isEvmWalletAccount, type EvmWalletAccount } from "@dynamic-labs-sdk/evm";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { DynamicProvider, useUser, useWalletAccounts } from "@dynamic-labs-sdk/react-hooks";
import { dynamicClient } from "./dynamic";
import { DynamicProvider, useUser, useWalletAccounts, useEvent } from "@dynamic-labs-sdk/react-hooks";
import { dynamicClient, initDynamic } from "./dynamic";

interface WalletContextValue {
evmAccount: EvmWalletAccount | null;
Expand Down Expand Up @@ -54,6 +53,24 @@ const queryClient = new QueryClient({
},
});

function DynamicBootstrap() {
useEffect(() => {
let cancelled = false;
initDynamic().then(async () => {
if (cancelled || typeof window === "undefined") return;
try {
const url = new URL(window.location.href);
if (await detectSocialRedirectUrl({ url })) {
await completeSocialRedirect({ url });
window.history.replaceState({}, "", window.location.pathname);
}
} catch { /* not a social redirect */ }
});
return () => { cancelled = true; };
}, []);
return null;
}

function InnerProviders({ children }: { children: ReactNode }) {
const loggedIn = useUser() !== null;
const evmAccount = useWalletAccounts().find(isEvmWalletAccount) ?? null;
Expand All @@ -79,33 +96,7 @@ function InnerProviders({ children }: { children: ReactNode }) {
} catch {}
}, []);

useEffect(() => {
const unsub = onEvent(
{
event: "walletAccountsChanged",
listener: () => {
void ensureEvmWallet();
},
},
dynamicClient,
);
return () => unsub?.();
}, [ensureEvmWallet]);

useEffect(() => {
const handleOAuthRedirect = async () => {
if (typeof window === "undefined") return;
try {
const url = new URL(window.location.href);
if (await detectOAuthRedirect({ url }, dynamicClient)) {
await completeSocialAuthentication({ url }, dynamicClient);
await ensureEvmWallet();
window.history.replaceState({}, "", window.location.pathname);
}
} catch {}
};
handleOAuthRedirect();
}, [ensureEvmWallet]);
useEvent({ event: "walletAccountsChanged", listener: () => { void ensureEvmWallet(); } });

return (
<WalletContext.Provider
Expand All @@ -126,6 +117,7 @@ function InnerProviders({ children }: { children: ReactNode }) {
export default function Providers({ children }: { children: ReactNode }) {
return (
<DynamicProvider client={dynamicClient}>
<DynamicBootstrap />
<InnerProviders>{children}</InnerProviders>
</DynamicProvider>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
"use client";

import { DynamicEmbeddedWidget } from "@dynamic-labs/sdk-react-core";
import DynamicButton from "@/components/dynamic/dynamic-widget";

/**
* Wallet connection prompt with embedded Dynamic widget
* Wallet connection prompt
*
* Displayed when no wallet is connected. Uses Dynamic's embedded widget
* to provide a seamless authentication experience directly in the UI.
*
* The widget styling is configured via cssOverrides in lib/providers.tsx.
* Displayed when no wallet is connected. Uses the DynamicButton component
* to provide an authentication entry point directly in the UI.
*/
export default function ConnectWalletPrompt() {
return (
<div className="pt-8 pb-2 flex justify-center">
<div className="w-full max-w-sm">
<DynamicEmbeddedWidget background="none" />
<DynamicButton />
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@
"use client";

import { useState } from "react";
import { Sparkles, Code2 } from "lucide-react";
import {
SpinnerIcon,
useDynamicContext,
useWalletDelegation,
} from "@dynamic-labs/sdk-react-core";
import { Sparkles, Code2, Loader2 } from "lucide-react";
import { useUser, useWalletAccounts, useInitStatus } from "@dynamic-labs-sdk/react-hooks";
import { isEvmWalletAccount } from "@dynamic-labs-sdk/evm";
import { hasDelegatedAccess } from "@dynamic-labs-sdk/client/waas";
import { dynamicClient } from "@/lib/dynamic";

import DelegatedAccessInit from "./init";
import DelegatedAccessMethods from "./methods";
Expand All @@ -34,34 +33,52 @@ import DelegationInfoBox from "@/components/info/delegation-info-box";
type DelegationTab = "modal" | "custom";

export default function DelegatedAccess() {
const { sdkHasLoaded, primaryWallet } = useDynamicContext();
const {
delegatedAccessEnabled,
getWalletsDelegatedStatus,
requiresDelegation,
} = useWalletDelegation();
const user = useUser();
const accounts = useWalletAccounts();
const initStatus = useInitStatus();
const sdkHasLoaded = initStatus === "finished";
const primaryWallet = accounts.find(isEvmWalletAccount) ?? null;
const [activeTab, setActiveTab] = useState<DelegationTab>("modal");

if (!sdkHasLoaded) {
return (
<div className="flex items-center justify-center py-12">
<SpinnerIcon className="w-10 h-10 animate-spin text-dynamic" />
<Loader2 className="w-10 h-10 animate-spin text-dynamic" />
</div>
);
}

const walletStatuses = getWalletsDelegatedStatus();
const primaryWalletDelegationStatus = walletStatuses.find(
(wallet) => wallet.address === primaryWallet?.address,
// Derive wallet delegation statuses from the new SDK
const walletStatuses = accounts
.filter(isEvmWalletAccount)
.map((account) => {
let isDelegated = false;
try {
isDelegated = hasDelegatedAccess({ walletAccount: account }, dynamicClient);
} catch {
isDelegated = false;
}
return {
address: account.address,
status: isDelegated ? "delegated" : "pending",
};
});

const primaryWalletDelegationStatus = primaryWallet
? walletStatuses.find((wallet) => wallet.address === primaryWallet.address)
: undefined;

const delegatedAccessEnabled = walletStatuses.some(
(wallet) => wallet.status === "delegated"
);

return (
<div className="space-y-6">
{/* Main Status Card */}
<div className="rounded-xl border bg-card overflow-hidden">
<DelegationStatusHeader
isEnabled={delegatedAccessEnabled ?? false}
requiresDelegation={requiresDelegation}
isEnabled={delegatedAccessEnabled}
requiresDelegation={false}
/>

<div className={primaryWallet ? "p-6 space-y-4" : "px-4 py-2"}>
Expand Down
Loading