@ensofinance/checkout-widget 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/checkout-widget.es.js +25627 -24338
  2. package/dist/checkout-widget.es.js.map +1 -1
  3. package/dist/checkout-widget.umd.js +64 -59
  4. package/dist/checkout-widget.umd.js.map +1 -1
  5. package/dist/index.d.ts +5 -1
  6. package/package.json +1 -1
  7. package/src/assets/providers/alchemypay.svg +21 -0
  8. package/src/assets/providers/banxa.svg +21 -0
  9. package/src/assets/providers/binanceconnect.svg +14 -0
  10. package/src/assets/providers/kryptonim.svg +6 -0
  11. package/src/assets/providers/mercuryo.svg +21 -0
  12. package/src/assets/providers/moonpay.svg +14 -0
  13. package/src/assets/providers/stripe.svg +16 -0
  14. package/src/assets/providers/swapped.svg +1 -0
  15. package/src/assets/providers/topper.svg +14 -0
  16. package/src/assets/providers/transak.svg +21 -0
  17. package/src/assets/providers/unlimit.svg +21 -0
  18. package/src/components/AmountInput.tsx +41 -25
  19. package/src/components/ChakraProvider.tsx +36 -13
  20. package/src/components/Checkout.tsx +7 -1
  21. package/src/components/CurrencySwapDisplay.tsx +59 -22
  22. package/src/components/DepositProcessing.tsx +1 -1
  23. package/src/components/ExchangeConfirmSecurity.tsx +1 -1
  24. package/src/components/QuoteParameters.tsx +1 -1
  25. package/src/components/TransactionDetailRow.tsx +2 -2
  26. package/src/components/cards/ExchangeCard.tsx +1 -1
  27. package/src/components/cards/OptionCard.tsx +2 -1
  28. package/src/components/cards/WalletCard.tsx +1 -1
  29. package/src/components/modal.tsx +3 -3
  30. package/src/components/steps/CardBuyFlow/CardBuyFlow.tsx +412 -0
  31. package/src/components/steps/CardBuyFlow/ChooseAmountStep.tsx +352 -0
  32. package/src/components/steps/CardBuyFlow/OpenWidgetStep.tsx +193 -0
  33. package/src/components/steps/ExchangeFlow.tsx +231 -1404
  34. package/src/components/steps/FlowSelector.tsx +117 -60
  35. package/src/components/steps/SmartAccountFlow.tsx +372 -0
  36. package/src/components/steps/WalletFlow/WalletAmountStep.tsx +2 -2
  37. package/src/components/steps/WalletFlow/WalletConfirmStep.tsx +92 -51
  38. package/src/components/steps/WalletFlow/WalletFlow.tsx +17 -16
  39. package/src/components/steps/WalletFlow/WalletQuoteStep.tsx +2 -2
  40. package/src/components/steps/WalletFlow/WalletTokenStep.tsx +6 -4
  41. package/src/components/steps/shared/ChooseAmountStep.tsx +325 -0
  42. package/src/components/steps/shared/SignUserOpStep.tsx +117 -0
  43. package/src/components/steps/shared/TrackUserOpStep.tsx +625 -0
  44. package/src/components/steps/shared/exchangeIntegration.ts +19 -0
  45. package/src/components/steps/shared/types.ts +22 -0
  46. package/src/components/ui/index.tsx +23 -6
  47. package/src/components/ui/toaster.tsx +2 -1
  48. package/src/components/ui/transitions.tsx +16 -0
  49. package/src/enso-api/model/bridgeTransactionResponse.ts +37 -0
  50. package/src/enso-api/model/bridgeTransactionResponseStatus.ts +25 -0
  51. package/src/enso-api/model/ensoEvent.ts +30 -0
  52. package/src/enso-api/model/ensoMetadata.ts +23 -0
  53. package/src/enso-api/model/layerZeroControllerCheckBridgeTransactionParams.ts +21 -0
  54. package/src/enso-api/model/layerZeroMessageStatus.ts +39 -0
  55. package/src/enso-api/model/refundDetails.ts +21 -0
  56. package/src/types/index.ts +99 -0
  57. package/src/util/constants.tsx +27 -0
  58. package/src/util/enso-hooks.tsx +75 -61
  59. package/src/util/meld-hooks.tsx +533 -0
  60. package/src/assets/usdc.webp +0 -0
  61. package/src/assets/usdt.webp +0 -0
@@ -56,10 +56,12 @@ export const Button = chakra(ChakraButton, {
56
56
  width: "100%",
57
57
  justifyContent: "center",
58
58
  alignItems: "center",
59
- borderRadius: "md",
59
+ borderRadius: "control",
60
60
  flexShrink: "initial",
61
- // cursor: "pointer",
62
- // transition: "all 0.2s ease-in-out",
61
+ transition: "transform 0.12s ease, opacity 0.12s ease",
62
+ _active: {
63
+ transform: "scale(0.98)",
64
+ },
63
65
  },
64
66
  variants: {
65
67
  visual: {
@@ -74,10 +76,24 @@ export const Button = chakra(ChakraButton, {
74
76
  grayFilled: {
75
77
  bg: "bg.emphasized",
76
78
  color: "customWhite",
79
+ // Override Chakra's default solid hover.
80
+ _hover: {
81
+ bg: "bg.emphasized",
82
+ },
83
+ _active: {
84
+ bg: "bg.emphasized",
85
+ },
77
86
  },
78
87
  lightGray: {
79
88
  bg: "bg.subtle",
80
89
  color: "fg",
90
+ // Override Chakra's default solid hover.
91
+ _hover: {
92
+ bg: "bg.emphasized",
93
+ },
94
+ _active: {
95
+ bg: "bg.emphasized",
96
+ },
81
97
  },
82
98
  },
83
99
  size: {
@@ -130,7 +146,7 @@ export const Tab = chakra("div", {
130
146
  justifyContent: "center",
131
147
  alignItems: "center",
132
148
  gap: "8px",
133
- borderRadius: "6px",
149
+ borderRadius: "control",
134
150
  cursor: "pointer",
135
151
  color: "fg.muted",
136
152
  fontSize: "14px",
@@ -299,12 +315,13 @@ export const Card: React.FC<CardProps> & {
299
315
  return (
300
316
  <Box
301
317
  p={padding}
302
- borderRadius="md"
318
+ borderRadius="card"
303
319
  border="1px solid"
304
320
  borderColor={selected ? "primary" : "border"}
305
321
  bg="bg"
306
322
  cursor={onClick && !disabled ? "pointer" : "default"}
307
323
  opacity={disabled ? 0.5 : 1}
324
+ transition="border-color 0.15s ease, opacity 0.15s ease"
308
325
  _hover={
309
326
  selected
310
327
  ? undefined
@@ -376,7 +393,7 @@ export const Helper: React.FC<HelperProps> = ({ children, type = "info" }) => {
376
393
  <Flex
377
394
  gap={1}
378
395
  p={3}
379
- borderRadius="md"
396
+ borderRadius="card"
380
397
  border="1px solid"
381
398
  fontSize="sm"
382
399
  {...style}
@@ -9,11 +9,12 @@ import {
9
9
  createToaster,
10
10
  } from "@chakra-ui/react"
11
11
 
12
- export const toaster = createToaster({
12
+ export const toaster: ReturnType<typeof createToaster> = createToaster({
13
13
  placement: "bottom-end",
14
14
  pauseOnPageIdle: true,
15
15
  })
16
16
 
17
+
17
18
  export const Toaster = () => {
18
19
  return (
19
20
  <Portal>
@@ -0,0 +1,16 @@
1
+ import { chakra } from "@chakra-ui/react";
2
+
3
+ /**
4
+ * Wraps content with a fade + slide-up entrance animation.
5
+ * Set a `key` prop on each instance so React re-mounts it when
6
+ * the step/flow changes, which replays the animation.
7
+ *
8
+ * @example
9
+ * <AnimatedStep key={currentStep}>{stepContent}</AnimatedStep>
10
+ */
11
+ export const AnimatedStep = chakra("div", {
12
+ base: {
13
+ width: "100%",
14
+ animation: "fadeSlideUp 250ms ease-out both",
15
+ },
16
+ });
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Generated by orval v7.11.1 🍺
3
+ * Do not edit manually.
4
+ * #### Enso API
5
+ - Find detailed documentation on [docs.enso.finance](https://docs.enso.finance).
6
+ - To use the API, **you must include your API Key in the Authorization header** (Bearer format).
7
+ - For testing, Swagger pre-authorizes you using the key: `1e02632d-6feb-4a75-a157-documentation` (1rps).
8
+ - Get your own API Key at [enso.finance/developers](https://developers.enso.build/).
9
+ * OpenAPI spec version: 1.0
10
+ */
11
+ import type { BridgeTransactionResponseStatus } from "./bridgeTransactionResponseStatus";
12
+ import type { LayerZeroMessageStatus } from "./layerZeroMessageStatus";
13
+ import type { EnsoEvent } from "./ensoEvent";
14
+ import type { EnsoMetadata } from "./ensoMetadata";
15
+
16
+ export interface BridgeTransactionResponse {
17
+ /** Source chain ID */
18
+ sourceChainId: number;
19
+ /** Source transaction hash */
20
+ sourceTxHash: string;
21
+ /** Destination chain ID (if known) */
22
+ destinationChainId?: number;
23
+ /** Destination transaction hash (if completed) */
24
+ destinationTxHash?: string;
25
+ /** Overall bridge status */
26
+ status: BridgeTransactionResponseStatus;
27
+ /** LayerZero message information */
28
+ layerZeroMessage?: LayerZeroMessageStatus;
29
+ /** Enso shortcut event from source chain (ShortcutExecuted + execution result) */
30
+ ensoSourceEvent?: EnsoEvent;
31
+ /** Enso shortcut event from destination chain (ShortcutExecuted + execution result) */
32
+ ensoDestinationEvent?: EnsoEvent;
33
+ /** Enso transaction metadata */
34
+ ensoMetadata?: EnsoMetadata;
35
+ /** Error message if something went wrong */
36
+ error?: string;
37
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Generated by orval v7.11.1 🍺
3
+ * Do not edit manually.
4
+ * #### Enso API
5
+ - Find detailed documentation on [docs.enso.finance](https://docs.enso.finance).
6
+ - To use the API, **you must include your API Key in the Authorization header** (Bearer format).
7
+ - For testing, Swagger pre-authorizes you using the key: `1e02632d-6feb-4a75-a157-documentation` (1rps).
8
+ - Get your own API Key at [enso.finance/developers](https://developers.enso.build/).
9
+ * OpenAPI spec version: 1.0
10
+ */
11
+
12
+ /**
13
+ * Overall bridge status
14
+ */
15
+ export type BridgeTransactionResponseStatus =
16
+ (typeof BridgeTransactionResponseStatus)[keyof typeof BridgeTransactionResponseStatus];
17
+
18
+ // eslint-disable-next-line @typescript-eslint/no-redeclare
19
+ export const BridgeTransactionResponseStatus = {
20
+ pending: "pending",
21
+ inflight: "inflight",
22
+ delivered: "delivered",
23
+ failed: "failed",
24
+ unknown: "unknown",
25
+ } as const;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Generated by orval v7.11.1 🍺
3
+ * Do not edit manually.
4
+ * #### Enso API
5
+ - Find detailed documentation on [docs.enso.finance](https://docs.enso.finance).
6
+ - To use the API, **you must include your API Key in the Authorization header** (Bearer format).
7
+ - For testing, Swagger pre-authorizes you using the key: `1e02632d-6feb-4a75-a157-documentation` (1rps).
8
+ - Get your own API Key at [enso.finance/developers](https://developers.enso.build/).
9
+ * OpenAPI spec version: 1.0
10
+ */
11
+ import type { RefundDetails } from "./refundDetails";
12
+
13
+ export interface EnsoEvent {
14
+ /** Account ID from ShortcutExecuted event */
15
+ accountId?: string;
16
+ /** Request ID from ShortcutExecuted event */
17
+ requestId?: string;
18
+ /** Block number where the event was emitted */
19
+ blockNumber: number;
20
+ /** Transaction hash */
21
+ transactionHash: string;
22
+ /** Chain ID where the event was emitted */
23
+ chainId: number;
24
+ /** Whether the shortcut execution was successful */
25
+ success: boolean;
26
+ /** Error data if execution failed */
27
+ error?: string;
28
+ /** Refund details if execution failed and funds were returned */
29
+ refundDetails?: RefundDetails;
30
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Generated by orval v7.11.1 🍺
3
+ * Do not edit manually.
4
+ * #### Enso API
5
+ - Find detailed documentation on [docs.enso.finance](https://docs.enso.finance).
6
+ - To use the API, **you must include your API Key in the Authorization header** (Bearer format).
7
+ - For testing, Swagger pre-authorizes you using the key: `1e02632d-6feb-4a75-a157-documentation` (1rps).
8
+ - Get your own API Key at [enso.finance/developers](https://developers.enso.build/).
9
+ * OpenAPI spec version: 1.0
10
+ */
11
+
12
+ export interface EnsoMetadata {
13
+ /** Token in address */
14
+ tokenIn?: string;
15
+ /** Token out address */
16
+ tokenOut?: string;
17
+ /** Amount in */
18
+ amountIn?: string;
19
+ /** Chain ID from the request */
20
+ chainId?: number;
21
+ /** Timestamp of the request */
22
+ timestamp?: number;
23
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Generated by orval v7.11.1 🍺
3
+ * Do not edit manually.
4
+ * #### Enso API
5
+ - Find detailed documentation on [docs.enso.finance](https://docs.enso.finance).
6
+ - To use the API, **you must include your API Key in the Authorization header** (Bearer format).
7
+ - For testing, Swagger pre-authorizes you using the key: `1e02632d-6feb-4a75-a157-documentation` (1rps).
8
+ - Get your own API Key at [enso.finance/developers](https://developers.enso.build/).
9
+ * OpenAPI spec version: 1.0
10
+ */
11
+
12
+ export type LayerZeroControllerCheckBridgeTransactionParams = {
13
+ /**
14
+ * Chain ID of the source transaction
15
+ */
16
+ chainId: number;
17
+ /**
18
+ * Transaction hash
19
+ */
20
+ txHash: string;
21
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Generated by orval v7.11.1 🍺
3
+ * Do not edit manually.
4
+ * #### Enso API
5
+ - Find detailed documentation on [docs.enso.finance](https://docs.enso.finance).
6
+ - To use the API, **you must include your API Key in the Authorization header** (Bearer format).
7
+ - For testing, Swagger pre-authorizes you using the key: `1e02632d-6feb-4a75-a157-documentation` (1rps).
8
+ - Get your own API Key at [enso.finance/developers](https://developers.enso.build/).
9
+ * OpenAPI spec version: 1.0
10
+ */
11
+
12
+ export interface LayerZeroMessageStatus {
13
+ /** Source chain endpoint ID */
14
+ srcEid: number;
15
+ /** Destination chain endpoint ID */
16
+ dstEid: number;
17
+ /** Source chain ID (EVM) */
18
+ srcChainId?: number;
19
+ /** Destination chain ID (EVM) */
20
+ dstChainId?: number;
21
+ /** Message status from LayerZero */
22
+ status: string;
23
+ /** Source transaction hash */
24
+ srcTxHash?: string;
25
+ /** Destination transaction hash (if delivered) */
26
+ dstTxHash?: string;
27
+ /** Source block number */
28
+ srcBlockNumber?: number;
29
+ /** Destination block number (if delivered) */
30
+ dstBlockNumber?: number;
31
+ /** Source timestamp */
32
+ srcTimestamp?: string;
33
+ /** Destination timestamp (if delivered) */
34
+ dstTimestamp?: string;
35
+ /** Sender address */
36
+ sender?: string;
37
+ /** Receiver address */
38
+ receiver?: string;
39
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Generated by orval v7.11.1 🍺
3
+ * Do not edit manually.
4
+ * #### Enso API
5
+ - Find detailed documentation on [docs.enso.finance](https://docs.enso.finance).
6
+ - To use the API, **you must include your API Key in the Authorization header** (Bearer format).
7
+ - For testing, Swagger pre-authorizes you using the key: `1e02632d-6feb-4a75-a157-documentation` (1rps).
8
+ - Get your own API Key at [enso.finance/developers](https://developers.enso.build/).
9
+ * OpenAPI spec version: 1.0
10
+ */
11
+
12
+ export interface RefundDetails {
13
+ /** Token address (0xeee... for native token) */
14
+ token: string;
15
+ /** Amount refunded */
16
+ amount: string;
17
+ /** Recipient address */
18
+ recipient: string;
19
+ /** Whether the refund is in native token */
20
+ isNative: boolean;
21
+ }
@@ -16,10 +16,14 @@ export type CheckoutConfig = {
16
16
  apiKey: string;
17
17
  theme?: WidgetTheme;
18
18
  enableExchange?: SupportedExchanges[];
19
+ /** Enable card purchases via MELD onramp */
20
+ enableCardBuy?: boolean;
19
21
  /** Override the default CEX bridge chain mapping (maps target chains to intermediate chains for withdrawal + bridge) */
20
22
  cexBridgeChainMapping?: Record<number, number>;
21
23
  /** Override recipient address (defaults to connected wallet's smart account) */
22
24
  recipient?: string;
25
+ /** Slippage tolerance in basis points (e.g. 200 = 2%). Defaults to 200 */
26
+ slippage?: number;
23
27
  /** Force the widget to open in a specific flow, bypassing the selector */
24
28
  enforceFlow?: EnforceFlow;
25
29
  };
@@ -31,5 +35,100 @@ export type CheckoutModalProps = {
31
35
  onClose?: () => void;
32
36
  };
33
37
 
38
+ // MELD Types
39
+ export interface MeldQuote {
40
+ serviceProvider: string;
41
+ serviceProviderName: string;
42
+ transactionType: "BUY" | "SELL";
43
+ sourceAmount: number;
44
+ sourceCurrencyCode: string;
45
+ destinationAmount: number;
46
+ destinationCurrencyCode: string;
47
+ exchangeRate: number;
48
+ paymentMethodType: string;
49
+ customerScore?: number;
50
+ rampIntelligence?: {
51
+ rampScore?: number;
52
+ [key: string]: unknown;
53
+ };
54
+ totalFee: number;
55
+ networkFee: number;
56
+ transactionFee: number;
57
+ lowKyc?: boolean;
58
+ }
59
+
60
+ export interface MeldQuotesResponse {
61
+ quotes: MeldQuote[];
62
+ message?: string;
63
+ error?: string;
64
+ }
65
+
66
+ export interface MeldSessionRequest {
67
+ countryCode: string;
68
+ sourceCurrencyCode: string;
69
+ destinationCurrencyCode: string;
70
+ sourceAmount: number;
71
+ walletAddress: string;
72
+ serviceProvider?: string;
73
+ paymentMethodType?: string;
74
+ externalCustomerId?: string;
75
+ externalSessionId?: string;
76
+ }
77
+
78
+ export interface MeldSessionResponse {
79
+ widgetUrl: string;
80
+ sessionId: string;
81
+ token?: string;
82
+ }
83
+
84
+ export type MeldTransactionStatus =
85
+ | "PENDING_CREATED"
86
+ | "PENDING"
87
+ | "PROCESSING"
88
+ | "AUTHORIZED"
89
+ | "AUTHORIZATION_EXPIRED"
90
+ | "SETTLING"
91
+ | "SETTLED"
92
+ | "REFUNDED"
93
+ | "DECLINED"
94
+ | "CANCELLED"
95
+ | "FAILED"
96
+ | "ERROR"
97
+ | "VOIDED"
98
+ | "TWO_FA_REQUIRED"
99
+ | "TWO_FA_PROVIDED";
100
+
101
+ export interface MeldTransaction {
102
+ transactionId: string;
103
+ externalCustomerId?: string;
104
+ externalSessionId?: string;
105
+ status: MeldTransactionStatus;
106
+ transactionType: "BUY" | "SELL";
107
+ sourceAmount: number;
108
+ sourceCurrencyCode: string;
109
+ destinationAmount: number;
110
+ destinationCurrencyCode: string;
111
+ destinationAddress: string;
112
+ transactionHash?: string;
113
+ serviceProvider: string;
114
+ paymentMethod?: string;
115
+ createdAt: string;
116
+ updatedAt: string;
117
+ }
118
+
119
+ export interface MeldSupportedCrypto {
120
+ currencyCode: string;
121
+ name: string;
122
+ chainCode: string;
123
+ chainId?: number;
124
+ contractAddress?: string;
125
+ }
126
+
127
+ export interface MeldSupportedCryptoResponse {
128
+ currencies: MeldSupportedCrypto[];
129
+ message?: string;
130
+ error?: string;
131
+ }
132
+
34
133
  export type { WidgetTheme };
35
134
  export { SupportedExchanges };
@@ -3,6 +3,30 @@ import { Token } from "@/util/common";
3
3
  export const ETH_ADDRESS = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
4
4
  export const VITALIK_ADDRESS = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
5
5
  export const DEFAULT_SLIPPAGE = 200;
6
+ // Backend-for-frontend used for Mesh + MELD integrations.
7
+ // export const CHECKOUT_BFF_URL = "http://localhost:3001/api";
8
+ export const CHECKOUT_BFF_URL = "https://checkout-api.enso.build/api";
9
+ // export const CHECKOUT_BFF_URL = "https://checkout-api.vercel.app/api";
10
+
11
+ // Hardcoded USDC contract addresses for supported chains (native USDC where applicable).
12
+ // Used by CardBuyFlow (MELD) as the fixed onramp asset.
13
+ export const USDC_ADDRESS_BY_CHAIN_ID: Record<number, string> = {
14
+ 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Ethereum
15
+ 8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base
16
+ 42161: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // Arbitrum One
17
+ 137: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", // Polygon
18
+ 10: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", // Optimism
19
+ 43114: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", // Avalanche C-Chain
20
+ 56: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", // BSC
21
+ };
22
+
23
+ export function getUsdcAddress(chainId: number): string {
24
+ const addr = USDC_ADDRESS_BY_CHAIN_ID[chainId];
25
+ if (!addr) {
26
+ throw new Error(`USDC is not configured for chainId ${chainId}`);
27
+ }
28
+ return addr;
29
+ }
6
30
 
7
31
  enum MESH_SYMBOLS {
8
32
  ETH = "ETH",
@@ -225,3 +249,6 @@ export const CHAINS_ETHERSCAN: Record<SupportedChainId, string> = {
225
249
  [SupportedChainId.HYPERLIQUID]: "https://www.hyperscan.com/",
226
250
  [SupportedChainId.KATANA]: "https://explorer-katana.t.conduit.xyz/",
227
251
  };
252
+
253
+ export const ENTRY_POINT_ADDRESS: `0x${string}` =
254
+ "0x0000000071727de22e5e9d8baf0edac6f37da032";
@@ -1,4 +1,9 @@
1
- import { useAccount, useWalletClient, useReadContract } from "wagmi";
1
+ import {
2
+ useAccount,
3
+ useWalletClient,
4
+ useReadContract,
5
+ usePublicClient,
6
+ } from "wagmi";
2
7
  import { useCapabilities, useSendCalls } from "wagmi/experimental";
3
8
  import { Address, parseAbiItem } from "viem";
4
9
  import { useCallback, useMemo } from "react";
@@ -24,11 +29,9 @@ const GET_SMART_ADDRESS = parseAbiItem(
24
29
  "function getAddress(address) external view returns (address)",
25
30
  );
26
31
 
27
- export const useSmartAccountAddress = (
28
- ownerAddress?: Address,
29
- chainId?: number,
30
- ) => {
32
+ export const useSmartAccountAddress = (chainId = 1) => {
31
33
  const factoryAddress = getERC4337CloneFactory(chainId);
34
+ const { address } = useAccount();
32
35
 
33
36
  const {
34
37
  data: smartAccountAddress,
@@ -38,10 +41,10 @@ export const useSmartAccountAddress = (
38
41
  address: factoryAddress as Address,
39
42
  abi: [GET_SMART_ADDRESS],
40
43
  functionName: "getAddress",
41
- args: ownerAddress ? [ownerAddress] : undefined,
44
+ args: address ? [address] : undefined,
42
45
  chainId,
43
46
  query: {
44
- enabled: !!(factoryAddress && ownerAddress && chainId),
47
+ enabled: !!(factoryAddress && address && chainId),
45
48
  },
46
49
  });
47
50
 
@@ -52,11 +55,9 @@ export const useSmartAccountAddress = (
52
55
  };
53
56
 
54
57
  export const useSmartAccountBalances = (chainId: number = 1) => {
55
- const { address } = useAccount();
56
-
57
58
  // Get smart account address for the given chain
58
59
  const { smartAccountAddress, isLoading: isLoadingSmartAccount } =
59
- useSmartAccountAddress(address, chainId);
60
+ useSmartAccountAddress(chainId);
60
61
 
61
62
  // Get balances from smart account
62
63
  const {
@@ -159,6 +160,12 @@ export const useAppDetails = () => {
159
160
  };
160
161
  };
161
162
 
163
+ export type TxStep =
164
+ | { type: "approving" }
165
+ | { type: "executing" }
166
+ | { type: "confirmed"; hash: string }
167
+ | { type: "atomic"; bundleId: string };
168
+
162
169
  export const useSendTxns = ({
163
170
  address,
164
171
  chainId,
@@ -168,83 +175,90 @@ export const useSendTxns = ({
168
175
  chainId: number;
169
176
  calls: any[];
170
177
  }) => {
171
- const { sendCalls } = useSendCalls();
172
- const { data: caps } = useCapabilities();
173
- // const atomicOkay = caps?.[chainId]?.atomic?.status === "ready";
174
- const atomicOkay = false;
175
- // const useFallback = !atomicOkay; // one-line policy; tweak to taste
176
- const wallet = useWalletClient();
178
+ const { sendCallsAsync } = useSendCalls();
179
+ const { data: caps, isFetched: capsFetched } = useCapabilities();
180
+ const atomicStatus = caps?.[chainId]?.atomic?.status;
181
+ const atomicOkay =
182
+ caps?.[chainId]?.atomicBatch?.supported === true ||
183
+ atomicStatus === "ready" ||
184
+ atomicStatus === "supported"; // metmask
185
+ console.log(
186
+ caps,
187
+ caps?.[chainId]?.atomicBatch?.supported,
188
+ caps?.[chainId]?.atomic?.status,
189
+ );
177
190
 
178
- console.log(caps, atomicOkay, wallet.data);
191
+ const wallet = useWalletClient();
192
+ const publicClient = usePublicClient({ chainId });
179
193
 
180
194
  const sendTxns = useCallback(
181
- async (
182
- onTxSend: (hash: string) => void,
183
- onFail: (error: any) => void,
184
- ) => {
195
+ async ({
196
+ onStep,
197
+ onFail,
198
+ }: {
199
+ onStep: (step: TxStep) => void;
200
+ onFail: (error: any) => void;
201
+ }) => {
185
202
  if (!atomicOkay) {
186
- // Send transactions sequentially when atomic is not supported
187
- console.log(
188
- "Atomic not supported, sending transactions sequentially",
189
- );
190
-
191
203
  try {
192
- for (const call of calls) {
193
- console.log("Sending individual transaction:", call);
204
+ for (let i = 0; i < calls.length; i++) {
205
+ const call = calls[i];
206
+ const isFinal = i === calls.length - 1;
194
207
 
195
- const result = await wallet.data?.sendTransaction({
208
+ onStep(
209
+ isFinal
210
+ ? { type: "executing" }
211
+ : { type: "approving" },
212
+ );
213
+
214
+ const hash = await wallet.data?.sendTransaction({
196
215
  ...call,
197
216
  account: address as Address,
198
217
  chainId,
199
218
  });
200
219
 
201
- console.log("Sequential transaction result:", result);
220
+ if (!hash) {
221
+ throw new Error("Transaction returned no hash");
222
+ }
202
223
 
203
- // Check if transaction failed and stop execution
204
- if (!result) {
205
- throw new Error(
206
- `Transaction failed for call: ${JSON.stringify(call)}`,
207
- );
224
+ if (!isFinal) {
225
+ const receipt =
226
+ await publicClient!.waitForTransactionReceipt({
227
+ hash,
228
+ });
229
+ if (receipt.status === "reverted") {
230
+ throw new Error("Approve transaction reverted");
231
+ }
208
232
  }
209
233
 
210
- if (call === calls[calls.length - 1]) {
211
- onTxSend(result);
234
+ if (isFinal) {
235
+ onStep({ type: "confirmed", hash });
212
236
  }
213
237
  }
214
-
215
- console.log("All sequential transactions completed");
216
238
  } catch (err) {
217
- console.error("Error in sequential transaction:", err);
218
239
  onFail(err);
219
- throw err;
220
240
  }
221
241
  return;
222
242
  }
223
243
 
224
- // Use atomic transactions when supported
225
- wallet.data
226
- ?.sendCalls(
227
- // @ts-ignore
228
- {
229
- calls,
230
- account: address as Address,
231
- forceAtomic: true, // request atomic only when possible
232
- },
233
- )
234
- .then((result) => {
235
- debugger;
236
- console.log(result);
237
- })
238
- .catch((err) => {
239
- debugger;
240
- console.error(err);
241
- onFail(err);
244
+ try {
245
+ const result = await sendCallsAsync({
246
+ calls: calls.map((call) => ({
247
+ ...call,
248
+ chainId,
249
+ })),
250
+ account: address as Address,
242
251
  });
252
+ onStep({ type: "atomic", bundleId: result.id });
253
+ } catch (err) {
254
+ onFail(err);
255
+ }
243
256
  },
244
- [sendCalls, calls, atomicOkay, chainId, wallet.data],
257
+ [sendCallsAsync, calls, atomicOkay, chainId, wallet.data, publicClient],
245
258
  );
246
259
 
247
- const ready = calls.length > 0 && wallet.data;
260
+ const ready =
261
+ calls.length > 0 && !!wallet.data && !!publicClient && capsFetched;
248
262
 
249
263
  return {
250
264
  ready,