Skip to content

Commit

Permalink
embed v3: dynamic connection (#798)
Browse files Browse the repository at this point in the history
* save

* tweak off

* cond

* fix build

* key

* chain parse

* save
  • Loading branch information
mPaella committed Oct 10, 2024
1 parent e4a3174 commit 4c81d1f
Show file tree
Hide file tree
Showing 10 changed files with 3,522 additions and 178 deletions.
2 changes: 2 additions & 0 deletions apps/payments/create-react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
},
"dependencies": {
"@crossmint/client-sdk-react-ui": "workspace:*",
"crypto": "npm:[email protected]",
"eslint-config-react-app": "7.0.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-scripts": "5.0.1",
"stream": "npm:[email protected]",
"web-vitals": "3.4.0"
},
"devDependencies": {
Expand Down
5 changes: 3 additions & 2 deletions apps/payments/nextjs/pages/embedded-checkout/v3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function EmbeddedCheckoutV3Page() {
>
<CrossmintProvider
overrideBaseUrl="https://dserver.maxf.io"
apiKey="ck_development_5zmgbGLxswXuRtUhjg5ACqZNkdZFPuN8UzCamj7kevGknYRL3EpLTRobh3HdSt1iQSRWSiCmRzEkdFQoqWZ71UyK4EhV3XTzcSnXkmorRG5ac1gQwqw8zmmM6bLNtREBb54L77Hzrf9XpDodh1c5awZUJntqbdPqgYRh8N9PaJ7gXTm2TMQDGABHs33Wxd88PxmTbjf8xYNrpPpNp8UfYaeT"
apiKey="sk_development_5ZUNkuhjP8aYZEgUTDfWToqFpo5zakEqte1db4pHZgPAVKZ9JuSvnKeGiqY654DoBuuZEzYz4Eb8gRV2ePqQ1fxTjEP8tTaUQdzbGfyG9RgyeN5YbqViXinqxk8EayEkAGtvSSgjpjEr6iaBptJtUFwPW59DjQzTQP6P8uZdiajenVg7bARGKjzFyByNuVEoz41DpRB4hDZNFdwCTuf5joFv"
>
<CrossmintEmbeddedCheckout
// recipient={{
Expand All @@ -50,8 +50,9 @@ export default function EmbeddedCheckoutV3Page() {
applePay: true,
googlePay: true,
},
defaultCurrency: "inr",
},
defaultMethod: "crypto",
defaultMethod: "fiat",
}}
/>
</CrossmintProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export function crossmintEmbeddedCheckoutV3Service({ apiClient }: CrossmintEmbed
}
}

queryParams.append("apiKey", apiClient.crossmint.apiKey);

return `${urlWithPath}?${queryParams.toString()}`;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/client/base/src/types/embed/v3/events/incoming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ export const embeddedCheckoutV3IncomingEvents = {
"ui:height.changed": z.object({
height: z.number(),
}),
"crypto:connect-wallet.show": z.object({
show: z.boolean(),
}),
};
export type EmbeddedCheckoutV3IncomingEventMap = typeof embeddedCheckoutV3IncomingEvents;
9 changes: 7 additions & 2 deletions packages/client/base/src/types/embed/v3/events/outgoing.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { z } from "zod";

export const embeddedCheckoutV3OutgoingEvents = {
placeholder: z.object({
placeholder: z.number(),
"crypto:connect-wallet.failed": z.object({
error: z.string(),
}),
"crypto:connect-wallet.success": z.object({
address: z.string(),
chain: z.string(),
walletProviderKey: z.string().optional(),
}),
};
export type EmbeddedCheckoutV3OutgoingEventMap = typeof embeddedCheckoutV3OutgoingEvents;
3 changes: 3 additions & 0 deletions packages/client/ui/react-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"@crossmint/client-sdk-window": "workspace:*",
"@crossmint/common-sdk-base": "workspace:*",
"@crossmint/common-sdk-auth": "workspace:*",
"@dynamic-labs/ethereum": "3.2.0",
"@dynamic-labs/sdk-react-core": "3.2.0",
"@dynamic-labs/solana": "3.2.0",
"@ethersproject/transactions": "5.7.0",
"@headlessui/react": "2.1.5",
"@solana/web3.js": "1.95.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type DynamicContextProps, DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import type { ReactNode } from "react";

export interface DynamicContextProviderWrapperProps {
children?: ReactNode;
settings: Omit<DynamicContextProps["settings"], "initialAuthenticationMode" | "environmentId">;
}

export default function DynamicContextProviderWrapper({ children, settings }: DynamicContextProviderWrapperProps) {
return (
<DynamicContextProvider
settings={{
initialAuthenticationMode: "connect-only",
environmentId: "cd53135a-b32b-4704-bfca-324b665e9329", // TODO: Key per env
...settings,
}}
>
{children}
</DynamicContextProvider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "@crossmint/client-sdk-base";
import { LIB_VERSION } from "@/consts/version";
import { CrossmintApiClient } from "@crossmint/common-sdk-base";
import { CryptoWalletConnectionHandler } from "./crypto/CryptoWalletConnectionHandler";

export function EmbeddedCheckoutV3IFrame(props: CrossmintEmbeddedCheckoutV3Props) {
const [iframeClient, setIframeClient] = useState<EmbeddedCheckoutV3IFrameEmitter | null>(null);
Expand Down Expand Up @@ -35,7 +36,7 @@ export function EmbeddedCheckoutV3IFrame(props: CrossmintEmbeddedCheckoutV3Props
}, [ref.current, iframeClient]);

useEffect(() => {
if (!iframeClient) {
if (iframeClient == null) {
return;
}
iframeClient.on("ui:height.changed", (data) => setHeight(data.height));
Expand All @@ -46,27 +47,30 @@ export function EmbeddedCheckoutV3IFrame(props: CrossmintEmbeddedCheckoutV3Props
}, [iframeClient]);

return (
<iframe
ref={ref}
src={embedV3Service.iframe.getUrl(props)}
id="crossmint-embedded-checkout.iframe"
role="crossmint-embedded-checkout.iframe"
allow="payment *"
style={{
boxShadow: "none",
border: "none",
padding: "0px",
width: "100%",
minWidth: "100%",
overflow: "hidden",
display: "block",
userSelect: "none",
transform: "translate(0px)",
opacity: "1",
transition: "ease 0s, opacity 0.4s ease 0.1s",
height: `${height}px`,
backgroundColor: "transparent",
}}
/>
<>
<iframe
ref={ref}
src={embedV3Service.iframe.getUrl(props)}
id="crossmint-embedded-checkout.iframe"
role="crossmint-embedded-checkout.iframe"
allow="payment *"
style={{
boxShadow: "none",
border: "none",
padding: "0px",
width: "100%",
minWidth: "100%",
overflow: "hidden",
display: "block",
userSelect: "none",
transform: "translate(0px)",
opacity: "1",
transition: "ease 0s, opacity 0.4s ease 0.1s",
height: `${height}px`,
backgroundColor: "transparent",
}}
/>
{props.payment.crypto.enabled ? <CryptoWalletConnectionHandler iframeClient={iframeClient} /> : null}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import DynamicContextProviderWrapper from "@/components/dynamic-xyz/DynamicContextProviderWrapper";
import type { EmbeddedCheckoutV3IFrameEmitter } from "@crossmint/client-sdk-base";
import { type BlockchainIncludingTestnet, chainIdToBlockchain } from "@crossmint/common-sdk-base";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
import { type HandleConnectedWallet, useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { SolanaWalletConnectors } from "@dynamic-labs/solana";
import { useEffect } from "react";

export function CryptoWalletConnectionHandler(props: { iframeClient: EmbeddedCheckoutV3IFrameEmitter | null }) {
const { iframeClient } = props;

return (
<DynamicContextProviderWrapper
settings={{
walletConnectors: [EthereumWalletConnectors, SolanaWalletConnectors],
events: {
onAuthFlowCancel() {
console.log("[CryptoWalletConnectionHandler] onAuthFlowCancel");
iframeClient?.send("crypto:connect-wallet.failed", {
error: "cancelled",
});
},
onAuthFlowClose() {
console.log("[CryptoWalletConnectionHandler] onAuthFlowClose");
},
onAuthFailure(data, reason) {
console.error("[CryptoWalletConnectionHandler] onAuthFailure", data, reason);
},
onAuthSuccess(data) {
console.log("[CryptoWalletConnectionHandler] onAuthSuccess", data);
},
},
handlers: {
handleConnectedWallet: async (wallet) => {
console.log("[CryptoWalletConnectionHandler] handleConnectedWallet", wallet);

const address = wallet.address;
if (!address) {
console.error("[CryptoWalletConnectionHandler] handleConnectedWallet: address is missing");
iframeClient?.send("crypto:connect-wallet.failed", {
error: "address is missing",
});
return false;
}

const chain = await dynamicChainToCrossmintChain(wallet);

iframeClient?.send("crypto:connect-wallet.success", {
address,
chain,
walletProviderKey: wallet.connector?.key,
});

return true;
},
},
}}
>
<_CryptoWalletConnectionHandler {...props} />
</DynamicContextProviderWrapper>
);
}

function _CryptoWalletConnectionHandler({ iframeClient }: Parameters<typeof CryptoWalletConnectionHandler>[0]) {
const { setShowAuthFlow } = useDynamicContext();

useEffect(() => {
if (iframeClient == null) {
return;
}
const listenerId = iframeClient.on("crypto:connect-wallet.show", ({ show }) => {
console.log("crypto:connect-wallet.show", show);
setShowAuthFlow(show);
});

return () => {
iframeClient.off(listenerId);
};
}, [iframeClient]);

return null;
}

async function dynamicChainToCrossmintChain(
wallet: Parameters<HandleConnectedWallet>[0]
): Promise<BlockchainIncludingTestnet> {
const chain = wallet.chain;
if (chain === "SOL") {
return "solana";
}
const chainId = await wallet.connector?.getNetwork();
if (typeof chainId !== "number") {
throw new Error("chainId is not a number");
}
const chainFromChainId = chainIdToBlockchain(chainId);
if (!chainFromChainId) {
throw new Error(`ChainId ${chainId} is not supported`);
}
return chainFromChainId as BlockchainIncludingTestnet;
}
Loading

0 comments on commit 4c81d1f

Please sign in to comment.