Skip to content

Commit

Permalink
embed v3: crypto tx sending (#818)
Browse files Browse the repository at this point in the history
working
  • Loading branch information
mPaella authored Oct 17, 2024
1 parent 4c81d1f commit 34b0787
Show file tree
Hide file tree
Showing 8 changed files with 415 additions and 216 deletions.
4 changes: 4 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 @@ -7,5 +7,9 @@ export const embeddedCheckoutV3IncomingEvents = {
"crypto:connect-wallet.show": z.object({
show: z.boolean(),
}),
"crypto:send-transaction": z.object({
chain: z.string(),
serializedTransaction: z.string(),
}),
};
export type EmbeddedCheckoutV3IncomingEventMap = typeof embeddedCheckoutV3IncomingEvents;
6 changes: 6 additions & 0 deletions packages/client/base/src/types/embed/v3/events/outgoing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,11 @@ export const embeddedCheckoutV3OutgoingEvents = {
chain: z.string(),
walletProviderKey: z.string().optional(),
}),
"crypto:send-transaction:success": z.object({
txId: z.string(),
}),
"crypto:send-transaction:failed": z.object({
error: z.string(),
}),
};
export type EmbeddedCheckoutV3OutgoingEventMap = typeof embeddedCheckoutV3OutgoingEvents;
9 changes: 6 additions & 3 deletions packages/client/ui/react-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
"@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",
"@dynamic-labs/ethereum": "3.3.0",
"@dynamic-labs/ethereum-core": "3.3.0",
"@dynamic-labs/sdk-react-core": "3.3.0",
"@dynamic-labs/solana": "3.3.0",
"@dynamic-labs/solana-core": "3.3.0",
"@ethersproject/transactions": "5.7.0",
"@headlessui/react": "2.1.5",
"@solana/web3.js": "1.95.1",
Expand All @@ -40,6 +42,7 @@
"react-jss": "10.10.0",
"tailwind-merge": "2.4.0",
"tailwindcss": "3.4.10",
"viem": "2.17.5",
"zod": "3.22.4"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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";
import { handleSendTransaction } from "./utils/handleSendTransaction";

export function CryptoWalletConnectionHandler(props: { iframeClient: EmbeddedCheckoutV3IFrameEmitter | null }) {
const { iframeClient } = props;
Expand Down Expand Up @@ -62,21 +63,50 @@ export function CryptoWalletConnectionHandler(props: { iframeClient: EmbeddedChe
}

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

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

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

useEffect(() => {
if (iframeClient == null) {
return;
}
const signTransactionListener = iframeClient.on(
"crypto:send-transaction",
async ({ chain, serializedTransaction }) => {
if (primaryWallet == null) {
console.error("[CryptoWalletConnectionHandler] signTransaction: primaryWallet is missing");
iframeClient.send("crypto:send-transaction:failed", {
error: "primaryWallet is missing",
});
return;
}

await handleSendTransaction(
primaryWallet,
chain as BlockchainIncludingTestnet,
serializedTransaction,
iframeClient
);
}
);

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

return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { EmbeddedCheckoutV3IFrameEmitter } from "@crossmint/client-sdk-base";
import {
type BlockchainIncludingTestnet,
blockchainToChainId,
type EVMBlockchainIncludingTestnet,
} from "@crossmint/common-sdk-base";
import type { EthereumWallet } from "@dynamic-labs/ethereum-core";
import { parseTransaction, type TransactionSerializableEIP1559 } from "viem";

export async function handleEvmTransaction({
primaryWallet,
chain,
serializedTransaction,
iframeClient,
}: {
primaryWallet: EthereumWallet;
chain: BlockchainIncludingTestnet;
serializedTransaction: string;
iframeClient: EmbeddedCheckoutV3IFrameEmitter;
}) {
try {
await primaryWallet.switchNetwork(blockchainToChainId(chain as EVMBlockchainIncludingTestnet));
} catch (error) {
console.error("[CryptoWalletConnectionHandler] failed to switch network", error);
iframeClient.send("crypto:send-transaction:failed", {
error: (error as Error).message,
});
return;
}

let walletClient: Awaited<ReturnType<typeof primaryWallet.getWalletClient>>;
try {
walletClient = await primaryWallet.getWalletClient();
} catch (error) {
console.error("[CryptoWalletConnectionHandler] failed to get wallet client", error);
iframeClient.send("crypto:send-transaction:failed", {
error: (error as Error).message,
});
return;
}

let parsedTransaction: TransactionSerializableEIP1559;
try {
parsedTransaction = parseTransaction(serializedTransaction as `0x${string}`) as TransactionSerializableEIP1559;
} catch (error) {
console.error("[CryptoWalletConnectionHandler] failed to parse transaction", error);
iframeClient.send("crypto:send-transaction:failed", {
error: (error as Error).message,
});
return;
}

try {
const txId = await walletClient.sendTransaction(parsedTransaction);
console.log("[CryptoWalletConnectionHandler] txId", txId);
iframeClient.send("crypto:send-transaction:success", {
txId,
});
} catch (error) {
console.error("[CryptoWalletConnectionHandler] failed to send transaction", error);
iframeClient.send("crypto:send-transaction:failed", {
error: (error as Error).message,
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Wallet } from "@dynamic-labs/sdk-react-core";

import type { BlockchainIncludingTestnet } from "@crossmint/common-sdk-base";

import { handleEvmTransaction } from "./handleEvmTransaction";

import { isSolanaWallet } from "@dynamic-labs/solana";
import { handleSolanaTransaction } from "./handleSolanaTransaction";
import { isEthereumWallet } from "@dynamic-labs/ethereum";
import type { EmbeddedCheckoutV3IFrameEmitter } from "@crossmint/client-sdk-base";

export async function handleSendTransaction(
primaryWallet: Wallet,
chain: BlockchainIncludingTestnet,
serializedTransaction: string,
iframeClient: EmbeddedCheckoutV3IFrameEmitter
) {
const commonParams = {
chain,
serializedTransaction,
iframeClient,
};
if (isSolanaWallet(primaryWallet)) {
return await handleSolanaTransaction({
...commonParams,
primaryWallet,
});
} else if (isEthereumWallet(primaryWallet)) {
return await handleEvmTransaction({ ...commonParams, primaryWallet });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { EmbeddedCheckoutV3IFrameEmitter } from "@crossmint/client-sdk-base";
import type { SolanaWallet } from "@dynamic-labs/solana-core";
import { Transaction } from "@solana/web3.js";
import base58 from "bs58";

export async function handleSolanaTransaction({
primaryWallet,
serializedTransaction,
iframeClient,
}: {
primaryWallet: SolanaWallet;
serializedTransaction: string;
iframeClient: EmbeddedCheckoutV3IFrameEmitter;
}) {
// TODO: Handle switch network

let signer: Awaited<ReturnType<typeof primaryWallet.getSigner>>;
try {
signer = await primaryWallet.getSigner();
} catch (error) {
console.error("[CryptoWalletConnectionHandler] failed to get signer", error);
iframeClient.send("crypto:send-transaction:failed", {
error: "Failed to get signer",
});
return;
}

let deserializedTransaction: Transaction;
try {
deserializedTransaction = Transaction.from(base58.decode(serializedTransaction));
} catch (error) {
console.error("[CryptoWalletConnectionHandler] failed to deserialize transaction", error);
iframeClient.send("crypto:send-transaction:failed", {
error: "Failed to deserialize transaction",
});
return;
}

try {
const { signature: txId } = await signer.signAndSendTransaction(deserializedTransaction);
console.log("[CryptoWalletConnectionHandler] txId", txId);
iframeClient.send("crypto:send-transaction:success", {
txId,
});
} catch (error) {
console.error("[CryptoWalletConnectionHandler] failed to send transaction", error);
iframeClient.send("crypto:send-transaction:failed", {
error: (error as Error).message,
});
}
}
Loading

0 comments on commit 34b0787

Please sign in to comment.