@b3dotfun/sdk 0.1.65 → 0.1.66-alpha.0
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.
- package/dist/cjs/anyspend/react/components/AnySpend.d.ts +2 -0
- package/dist/cjs/anyspend/react/components/AnySpend.js +7 -16
- package/dist/cjs/anyspend/react/components/AnySpendCollectorClubPurchase.d.ts +6 -1
- package/dist/cjs/anyspend/react/components/AnySpendCollectorClubPurchase.js +151 -22
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +4 -50
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +4 -2
- package/dist/cjs/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
- package/dist/cjs/anyspend/react/components/AnySpendDeposit.js +2 -2
- package/dist/cjs/anyspend/react/components/AnySpendWorkflowTrigger.d.ts +31 -0
- package/dist/cjs/anyspend/react/components/AnySpendWorkflowTrigger.js +14 -0
- package/dist/cjs/anyspend/react/components/QRDeposit.js +5 -13
- package/dist/cjs/anyspend/react/components/ccShopAbi.d.ts +113 -0
- package/dist/cjs/anyspend/react/components/ccShopAbi.js +63 -0
- package/dist/cjs/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
- package/dist/cjs/anyspend/react/components/common/CryptoPaySection.js +3 -3
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +3 -57
- package/dist/cjs/anyspend/react/components/common/PaySection.js +1 -1
- package/dist/cjs/anyspend/react/components/index.d.ts +2 -0
- package/dist/cjs/anyspend/react/components/index.js +3 -1
- package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/cjs/anyspend/react/hooks/index.js +1 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOrder.js +1 -0
- package/dist/cjs/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
- package/dist/cjs/anyspend/react/hooks/useOnOrderSuccess.js +27 -0
- package/dist/cjs/anyspend/services/anyspend.d.ts +2 -1
- package/dist/cjs/anyspend/services/anyspend.js +2 -1
- package/dist/cjs/anyspend/utils/chain.d.ts +1 -1
- package/dist/cjs/anyspend/utils/chain.js +72 -62
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +4 -0
- package/dist/cjs/global-account/react/hooks/useUserQuery.js +10 -0
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +37 -1
- package/dist/cjs/global-account/react/stores/userStore.js +1 -0
- package/dist/esm/anyspend/react/components/AnySpend.d.ts +2 -0
- package/dist/esm/anyspend/react/components/AnySpend.js +7 -16
- package/dist/esm/anyspend/react/components/AnySpendCollectorClubPurchase.d.ts +6 -1
- package/dist/esm/anyspend/react/components/AnySpendCollectorClubPurchase.js +152 -23
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +4 -17
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +4 -2
- package/dist/esm/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
- package/dist/esm/anyspend/react/components/AnySpendDeposit.js +2 -2
- package/dist/esm/anyspend/react/components/AnySpendWorkflowTrigger.d.ts +31 -0
- package/dist/esm/anyspend/react/components/AnySpendWorkflowTrigger.js +11 -0
- package/dist/esm/anyspend/react/components/QRDeposit.js +6 -14
- package/dist/esm/anyspend/react/components/ccShopAbi.d.ts +113 -0
- package/dist/esm/anyspend/react/components/ccShopAbi.js +60 -0
- package/dist/esm/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
- package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +3 -3
- package/dist/esm/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
- package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +2 -56
- package/dist/esm/anyspend/react/components/common/PaySection.js +1 -1
- package/dist/esm/anyspend/react/components/index.d.ts +2 -0
- package/dist/esm/anyspend/react/components/index.js +1 -0
- package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/esm/anyspend/react/hooks/index.js +1 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendCreateOrder.js +1 -0
- package/dist/esm/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
- package/dist/esm/anyspend/react/hooks/useOnOrderSuccess.js +24 -0
- package/dist/esm/anyspend/services/anyspend.d.ts +2 -1
- package/dist/esm/anyspend/services/anyspend.js +2 -1
- package/dist/esm/anyspend/utils/chain.d.ts +1 -1
- package/dist/esm/anyspend/utils/chain.js +72 -62
- package/dist/esm/global-account/react/components/B3DynamicModal.js +4 -0
- package/dist/esm/global-account/react/hooks/useUserQuery.js +11 -1
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +37 -1
- package/dist/esm/global-account/react/stores/userStore.js +1 -0
- package/dist/types/anyspend/react/components/AnySpend.d.ts +2 -0
- package/dist/types/anyspend/react/components/AnySpendCollectorClubPurchase.d.ts +6 -1
- package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
- package/dist/types/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
- package/dist/types/anyspend/react/components/AnySpendWorkflowTrigger.d.ts +31 -0
- package/dist/types/anyspend/react/components/ccShopAbi.d.ts +113 -0
- package/dist/types/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
- package/dist/types/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
- package/dist/types/anyspend/react/components/index.d.ts +2 -0
- package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/types/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
- package/dist/types/anyspend/react/hooks/useOnOrderSuccess.d.ts +10 -0
- package/dist/types/anyspend/services/anyspend.d.ts +2 -1
- package/dist/types/anyspend/utils/chain.d.ts +1 -1
- package/dist/types/global-account/react/stores/useModalStore.d.ts +37 -1
- package/package.json +1 -1
- package/src/anyspend/README.md +14 -0
- package/src/anyspend/docs/checkout-sessions.md +228 -0
- package/src/anyspend/docs/components.md +26 -0
- package/src/anyspend/docs/examples.md +58 -0
- package/src/anyspend/docs/hooks.md +32 -0
- package/src/anyspend/llms.txt +185 -0
- package/src/anyspend/react/components/AnySpend.tsx +9 -17
- package/src/anyspend/react/components/AnySpendCollectorClubPurchase.tsx +206 -22
- package/src/anyspend/react/components/AnySpendCustom.tsx +3 -18
- package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +5 -1
- package/src/anyspend/react/components/AnySpendDeposit.tsx +5 -0
- package/src/anyspend/react/components/AnySpendWorkflowTrigger.tsx +73 -0
- package/src/anyspend/react/components/QRDeposit.tsx +19 -15
- package/src/anyspend/react/components/ccShopAbi.ts +64 -0
- package/src/anyspend/react/components/common/CryptoPaySection.tsx +0 -5
- package/src/anyspend/react/components/common/OrderTokenAmount.tsx +1 -70
- package/src/anyspend/react/components/common/PaySection.tsx +0 -1
- package/src/anyspend/react/components/index.ts +2 -0
- package/src/anyspend/react/hooks/index.ts +1 -0
- package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +1 -0
- package/src/anyspend/react/hooks/useAnyspendCreateOrder.ts +2 -0
- package/src/anyspend/react/hooks/useOnOrderSuccess.ts +36 -0
- package/src/anyspend/services/anyspend.ts +3 -0
- package/src/anyspend/utils/chain.ts +81 -65
- package/src/global-account/react/components/B3DynamicModal.tsx +4 -0
- package/src/global-account/react/hooks/useUserQuery.ts +12 -1
- package/src/global-account/react/stores/useModalStore.ts +39 -2
- package/src/global-account/react/stores/userStore.ts +1 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# AnySpend SDK
|
|
2
|
+
|
|
3
|
+
> Accept crypto payments and onboard users with zero friction. Users pay with any token on any chain in one seamless experience.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
AnySpend is a payment SDK (`@b3dotfun/sdk`) for crypto payments and fiat onramps. It provides React components, hooks, and service methods for token swaps, cross-chain transactions, NFT purchases, and merchant checkout flows.
|
|
8
|
+
|
|
9
|
+
Supported networks: Ethereum, Base, B3.
|
|
10
|
+
Supported platforms: React Web (full), React Native (hooks + services only), Node.js (services only).
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @b3dotfun/sdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Wrap your app with the provider:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { AnySpendProvider } from "@b3dotfun/sdk/anyspend/react";
|
|
22
|
+
import "@b3dotfun/sdk/index.css";
|
|
23
|
+
|
|
24
|
+
function App() {
|
|
25
|
+
return <AnySpendProvider>{/* app */}</AnySpendProvider>;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Requires Node.js v20.15.0+, React 18/19.
|
|
30
|
+
|
|
31
|
+
## Components
|
|
32
|
+
|
|
33
|
+
### AnySpend
|
|
34
|
+
|
|
35
|
+
Primary swap/onramp interface. Supports modal or page mode.
|
|
36
|
+
|
|
37
|
+
Props: `mode` ("modal"|"page"), `defaultActiveTab` ("crypto"|"fiat"), `destinationTokenAddress`, `destinationTokenChainId`, `recipientAddress`, `hideTransactionHistoryButton`, `loadOrder`, `onSuccess`, `checkoutSession`.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<AnySpend
|
|
41
|
+
mode="page"
|
|
42
|
+
defaultActiveTab="crypto"
|
|
43
|
+
recipientAddress="0x..."
|
|
44
|
+
onSuccess={(txHash) => console.log(txHash)}
|
|
45
|
+
/>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### AnySpendNFTButton
|
|
49
|
+
|
|
50
|
+
One-click NFT purchase button.
|
|
51
|
+
|
|
52
|
+
Props: `nftContract` (NFTContract), `recipientAddress`, `onSuccess`.
|
|
53
|
+
|
|
54
|
+
NFTContract shape: `{ chainId, contractAddress, price (wei), priceFormatted, currency: { chainId, address, name, symbol, decimals }, name, description, imageUrl }`.
|
|
55
|
+
|
|
56
|
+
### AnySpendCustom
|
|
57
|
+
|
|
58
|
+
Flexible component for custom smart contract interactions (staking, gaming, DeFi).
|
|
59
|
+
|
|
60
|
+
Props: `orderType` ("custom"), `dstChainId`, `dstToken`, `dstAmount`, `contractAddress`, `encodedData`, `spenderAddress`, `metadata`, `header`, `onSuccess`, `checkoutSession`.
|
|
61
|
+
|
|
62
|
+
### AnySpendCustomExactIn
|
|
63
|
+
|
|
64
|
+
Like AnySpendCustom but for exact-input flows. Also supports `checkoutSession` prop.
|
|
65
|
+
|
|
66
|
+
### Specialized Components
|
|
67
|
+
|
|
68
|
+
- `AnySpendNFT` — Enhanced NFT with marketplace features
|
|
69
|
+
- `AnySpendStakeB3` — B3 token staking
|
|
70
|
+
- `AnySpendBuySpin` — Gaming spin wheel purchases
|
|
71
|
+
- `AnySpendTournament` — Tournament entry payments
|
|
72
|
+
|
|
73
|
+
## Hooks
|
|
74
|
+
|
|
75
|
+
### useAnyspendQuote(quoteRequest)
|
|
76
|
+
|
|
77
|
+
Get real-time pricing. QuoteRequest: `{ srcChain, dstChain, srcTokenAddress, dstTokenAddress, type ("swap"|"custom"), tradeType ("EXACT_INPUT"|"EXACT_OUTPUT"), amount }`.
|
|
78
|
+
|
|
79
|
+
Returns: `{ anyspendQuote, isLoadingAnyspendQuote, getAnyspendQuoteError, refetchAnyspendQuote }`.
|
|
80
|
+
|
|
81
|
+
### useAnyspendCreateOrder(options)
|
|
82
|
+
|
|
83
|
+
Create orders. Options: `{ onSuccess, onError, onSettled }`.
|
|
84
|
+
|
|
85
|
+
Returns: `{ createOrder(request), isCreatingOrder, createOrderError }`.
|
|
86
|
+
|
|
87
|
+
### useAnyspendOrderAndTransactions(orderId)
|
|
88
|
+
|
|
89
|
+
Monitor order status and transactions in real-time.
|
|
90
|
+
|
|
91
|
+
Returns: `{ orderAndTransactions: { order, depositTxs, relayTx, executeTx, refundTxs }, isLoadingOrderAndTransactions, getOrderAndTransactionsError }`.
|
|
92
|
+
|
|
93
|
+
### useAnyspendOrderHistory(creatorAddress, limit, offset)
|
|
94
|
+
|
|
95
|
+
Paginated order history for a user.
|
|
96
|
+
|
|
97
|
+
### useAnyspendTokens(chainId, search)
|
|
98
|
+
|
|
99
|
+
Get available tokens for a chain.
|
|
100
|
+
|
|
101
|
+
### useCoinbaseOnrampOptions()
|
|
102
|
+
|
|
103
|
+
Get Coinbase onramp config for fiat payments.
|
|
104
|
+
|
|
105
|
+
### useStripeClientSecret(orderData)
|
|
106
|
+
|
|
107
|
+
Get Stripe payment intent for card payments.
|
|
108
|
+
|
|
109
|
+
### useCreateCheckoutSession()
|
|
110
|
+
|
|
111
|
+
Mutation hook for creating checkout sessions.
|
|
112
|
+
|
|
113
|
+
Returns: `{ mutate: createSession, data, isPending }`.
|
|
114
|
+
|
|
115
|
+
### useCheckoutSession(sessionId)
|
|
116
|
+
|
|
117
|
+
Query hook that auto-polls a checkout session. Stops on `complete` or `expired`.
|
|
118
|
+
|
|
119
|
+
Returns: `{ data: session, isLoading }`.
|
|
120
|
+
|
|
121
|
+
## Checkout Sessions
|
|
122
|
+
|
|
123
|
+
Stripe-like checkout sessions decoupled from orders. Merchants create a session first, then create an order when the user picks a payment method.
|
|
124
|
+
|
|
125
|
+
### Flow
|
|
126
|
+
|
|
127
|
+
1. `POST /checkout-sessions` → Creates session (DB only, instant). Returns `{ id, status: "open" }`.
|
|
128
|
+
2. `POST /orders` with `checkoutSessionId` → Creates order linked to session. Returns order with `globalAddress` or `oneClickBuyUrl`.
|
|
129
|
+
3. User pays (crypto to globalAddress, or onramp redirect).
|
|
130
|
+
4. `GET /checkout-sessions/:id` → Poll status. Returns `{ status: "complete", order_id }`.
|
|
131
|
+
|
|
132
|
+
### Session Statuses
|
|
133
|
+
|
|
134
|
+
`open` → `processing` → `complete`, or `open` → `expired`.
|
|
135
|
+
|
|
136
|
+
### API Endpoints
|
|
137
|
+
|
|
138
|
+
**POST /checkout-sessions** — Create session. All fields optional: `success_url`, `cancel_url`, `metadata`, `client_reference_id`, `expires_in`.
|
|
139
|
+
|
|
140
|
+
**POST /orders** — Standard order creation. Add `checkoutSessionId` to link. Validates: session exists (400), session is open (400), session has no order yet (409).
|
|
141
|
+
|
|
142
|
+
**GET /checkout-sessions/:id** — Retrieve session. Query `?include=order` to embed full order with transactions.
|
|
143
|
+
|
|
144
|
+
**POST /checkout-sessions/:id/expire** — Manually expire an open session.
|
|
145
|
+
|
|
146
|
+
### Redirect URL Templates
|
|
147
|
+
|
|
148
|
+
`success_url` and `cancel_url` support `{SESSION_ID}` and `{ORDER_ID}` template variables. If no variable present, `?sessionId=<uuid>` is appended.
|
|
149
|
+
|
|
150
|
+
### Component Integration
|
|
151
|
+
|
|
152
|
+
Pass `checkoutSession` prop to `AnySpend`, `AnySpendCustom`, or `AnySpendCustomExactIn`:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
<AnySpend
|
|
156
|
+
checkoutSession={{
|
|
157
|
+
success_url: "https://myshop.com/success?session={SESSION_ID}",
|
|
158
|
+
cancel_url: "https://myshop.com/cancel",
|
|
159
|
+
metadata: { sku: "widget-1" },
|
|
160
|
+
}}
|
|
161
|
+
/>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Without the prop, all existing flows are unchanged.
|
|
165
|
+
|
|
166
|
+
## Order Status Lifecycle
|
|
167
|
+
|
|
168
|
+
`scanning_deposit_transaction` → `obtain_token` → `sending_token_from_vault` → `relay` → `executed`
|
|
169
|
+
|
|
170
|
+
Failure states: `obtain_failed`, `expired`, `refunding`, `refunded`, `failure`.
|
|
171
|
+
|
|
172
|
+
For Stripe: `waiting_stripe_payment` precedes processing.
|
|
173
|
+
|
|
174
|
+
## Error Codes
|
|
175
|
+
|
|
176
|
+
Payment: `INSUFFICIENT_BALANCE`, `INVALID_TOKEN_ADDRESS`, `MINIMUM_AMOUNT_NOT_MET`, `MAXIMUM_AMOUNT_EXCEEDED`.
|
|
177
|
+
Network: `SLIPPAGE`, `NETWORK_ERROR`, `QUOTE_EXPIRED`, `CHAIN_NOT_SUPPORTED`.
|
|
178
|
+
Contract: `CONTRACT_CALL_FAILED`, `INSUFFICIENT_GAS`, `NONCE_TOO_LOW`, `TRANSACTION_REVERTED`.
|
|
179
|
+
|
|
180
|
+
## Links
|
|
181
|
+
|
|
182
|
+
- Docs: docs/installation.md, docs/components.md, docs/hooks.md, docs/examples.md, docs/checkout-sessions.md, docs/error-handling.md
|
|
183
|
+
- Live demo: https://anyspend.com
|
|
184
|
+
- Discord: https://discord.gg/b3dotfun
|
|
185
|
+
- Issues: https://github.com/b3-fun/b3/issues
|
|
@@ -52,6 +52,7 @@ import { useAutoSelectCryptoPaymentMethod } from "../hooks/useAutoSelectCryptoPa
|
|
|
52
52
|
import { useConnectedWalletDisplay } from "../hooks/useConnectedWalletDisplay";
|
|
53
53
|
import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
|
|
54
54
|
import { useDirectTransfer } from "../hooks/useDirectTransfer";
|
|
55
|
+
import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess";
|
|
55
56
|
import { useRecipientAddressState } from "../hooks/useRecipientAddressState";
|
|
56
57
|
import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
|
|
57
58
|
import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
|
|
@@ -126,6 +127,8 @@ export function AnySpend(props: {
|
|
|
126
127
|
allowDirectTransfer?: boolean;
|
|
127
128
|
/** Fixed destination token amount (in wei/smallest unit). When provided, user cannot change the amount. */
|
|
128
129
|
destinationTokenAmount?: string;
|
|
130
|
+
/** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
|
|
131
|
+
callbackMetadata?: Record<string, unknown>;
|
|
129
132
|
}) {
|
|
130
133
|
const fingerprintConfig = getFingerprintConfig();
|
|
131
134
|
|
|
@@ -158,6 +161,7 @@ function AnySpendInner({
|
|
|
158
161
|
classes,
|
|
159
162
|
allowDirectTransfer = false,
|
|
160
163
|
destinationTokenAmount,
|
|
164
|
+
callbackMetadata,
|
|
161
165
|
}: {
|
|
162
166
|
sourceChainId?: number;
|
|
163
167
|
destinationTokenAddress?: string;
|
|
@@ -179,6 +183,7 @@ function AnySpendInner({
|
|
|
179
183
|
classes?: AnySpendClasses;
|
|
180
184
|
allowDirectTransfer?: boolean;
|
|
181
185
|
destinationTokenAmount?: string;
|
|
186
|
+
callbackMetadata?: Record<string, unknown>;
|
|
182
187
|
}) {
|
|
183
188
|
const searchParams = useSearchParamsSSR();
|
|
184
189
|
const router = useRouter();
|
|
@@ -208,9 +213,6 @@ function AnySpendInner({
|
|
|
208
213
|
toAmount?: string;
|
|
209
214
|
} | null>(null);
|
|
210
215
|
|
|
211
|
-
// Track if onSuccess has been called for the current order
|
|
212
|
-
const onSuccessCalled = useRef(false);
|
|
213
|
-
|
|
214
216
|
// Track animation direction for TransitionPanel
|
|
215
217
|
const animationDirection = useRef<"forward" | "back" | null>(null);
|
|
216
218
|
// Track previous panel for proper back navigation
|
|
@@ -704,19 +706,8 @@ function AnySpendInner({
|
|
|
704
706
|
}
|
|
705
707
|
}, [anyspendQuote, isSrcInputDirty, destinationTokenAmount]);
|
|
706
708
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
console.log("Calling onSuccess");
|
|
710
|
-
const txHash = oat?.data?.executeTx?.txHash;
|
|
711
|
-
onSuccess?.(txHash);
|
|
712
|
-
onSuccessCalled.current = true;
|
|
713
|
-
}
|
|
714
|
-
}, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, onSuccess]);
|
|
715
|
-
|
|
716
|
-
// Reset flag when orderId changes
|
|
717
|
-
useEffect(() => {
|
|
718
|
-
onSuccessCalled.current = false;
|
|
719
|
-
}, [orderId]);
|
|
709
|
+
// Call onSuccess when order is executed
|
|
710
|
+
useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
|
|
720
711
|
|
|
721
712
|
const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
|
|
722
713
|
onSuccess: data => {
|
|
@@ -979,6 +970,7 @@ function AnySpendInner({
|
|
|
979
970
|
srcAmount: srcAmountBigInt.toString(),
|
|
980
971
|
expectedDstAmount: anyspendQuote?.data?.currencyOut?.amount || "0",
|
|
981
972
|
creatorAddress: globalAddress,
|
|
973
|
+
callbackMetadata,
|
|
982
974
|
});
|
|
983
975
|
} catch (err: any) {
|
|
984
976
|
console.error(err);
|
|
@@ -1056,6 +1048,7 @@ function AnySpendInner({
|
|
|
1056
1048
|
},
|
|
1057
1049
|
expectedDstAmount: anyspendQuote?.data?.currencyOut?.amount?.toString() || "0",
|
|
1058
1050
|
creatorAddress: globalAddress,
|
|
1051
|
+
callbackMetadata,
|
|
1059
1052
|
});
|
|
1060
1053
|
} catch (err: any) {
|
|
1061
1054
|
console.error(err);
|
|
@@ -1227,7 +1220,6 @@ function AnySpendInner({
|
|
|
1227
1220
|
anyspendQuote={anyspendQuote}
|
|
1228
1221
|
onTokenSelect={onTokenSelect}
|
|
1229
1222
|
onShowFeeDetail={() => navigateToPanel(PanelView.FEE_DETAIL, "forward")}
|
|
1230
|
-
skipAutoMaxOnTokenChange={!!destinationTokenAmount}
|
|
1231
1223
|
/>
|
|
1232
1224
|
) : (
|
|
1233
1225
|
<motion.div
|
|
@@ -27,28 +27,28 @@
|
|
|
27
27
|
import { USDC_BASE } from "@b3dotfun/sdk/anyspend/constants";
|
|
28
28
|
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
29
29
|
import { GetQuoteResponse } from "@b3dotfun/sdk/anyspend/types/api_req_res";
|
|
30
|
+
import { PUBLIC_BASE_RPC_URL } from "@b3dotfun/sdk/shared/constants";
|
|
30
31
|
import { formatUnits } from "@b3dotfun/sdk/shared/utils/number";
|
|
31
|
-
import React, { useMemo } from "react";
|
|
32
|
-
import { encodeFunctionData } from "viem";
|
|
32
|
+
import React, { useEffect, useMemo, useState } from "react";
|
|
33
|
+
import { createPublicClient, encodeFunctionData, http } from "viem";
|
|
34
|
+
import { base } from "viem/chains";
|
|
33
35
|
import { AnySpendCustom } from "./AnySpendCustom";
|
|
36
|
+
import {
|
|
37
|
+
BUY_PACKS_FOR_ABI,
|
|
38
|
+
BUY_PACKS_FOR_WITH_DISCOUNT_ABI,
|
|
39
|
+
GET_DISCOUNT_CODE_ABI,
|
|
40
|
+
IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI,
|
|
41
|
+
} from "./ccShopAbi";
|
|
34
42
|
|
|
35
43
|
// Collector Club Shop contract addresses on Base
|
|
36
44
|
const CC_SHOP_ADDRESS = "0x47366E64E4917dd4DdC04Fb9DC507c1dD2b87294";
|
|
37
45
|
const CC_SHOP_ADDRESS_STAGING = "0x8b751143342ac41eB965E55430e3F7Adf6BE01fA";
|
|
38
46
|
const BASE_CHAIN_ID = 8453;
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{ internalType: "uint256", name: "packId", type: "uint256" },
|
|
45
|
-
{ internalType: "uint256", name: "amount", type: "uint256" },
|
|
46
|
-
],
|
|
47
|
-
name: "buyPacksFor",
|
|
48
|
-
outputs: [],
|
|
49
|
-
stateMutability: "nonpayable",
|
|
50
|
-
type: "function",
|
|
51
|
-
} as const;
|
|
48
|
+
const basePublicClient = createPublicClient({
|
|
49
|
+
chain: base,
|
|
50
|
+
transport: http(PUBLIC_BASE_RPC_URL),
|
|
51
|
+
});
|
|
52
52
|
|
|
53
53
|
export interface AnySpendCollectorClubPurchaseProps {
|
|
54
54
|
/**
|
|
@@ -118,6 +118,11 @@ export interface AnySpendCollectorClubPurchaseProps {
|
|
|
118
118
|
* Force fiat payment
|
|
119
119
|
*/
|
|
120
120
|
forceFiatPayment?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Optional discount code to apply to the purchase.
|
|
123
|
+
* When provided, validates on-chain and adjusts the price accordingly.
|
|
124
|
+
*/
|
|
125
|
+
discountCode?: string;
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
export function AnySpendCollectorClubPurchase({
|
|
@@ -137,6 +142,7 @@ export function AnySpendCollectorClubPurchase({
|
|
|
137
142
|
vendingMachineId,
|
|
138
143
|
packType,
|
|
139
144
|
forceFiatPayment,
|
|
145
|
+
discountCode,
|
|
140
146
|
}: AnySpendCollectorClubPurchaseProps) {
|
|
141
147
|
const ccShopAddress = isStaging ? CC_SHOP_ADDRESS_STAGING : CC_SHOP_ADDRESS;
|
|
142
148
|
|
|
@@ -150,25 +156,159 @@ export function AnySpendCollectorClubPurchase({
|
|
|
150
156
|
}
|
|
151
157
|
}, [pricePerPack, packAmount]);
|
|
152
158
|
|
|
153
|
-
//
|
|
159
|
+
// Discount code validation state
|
|
160
|
+
const [discountInfo, setDiscountInfo] = useState<{
|
|
161
|
+
isValid: boolean;
|
|
162
|
+
discountAmount: bigint;
|
|
163
|
+
minPurchaseAmount: bigint;
|
|
164
|
+
isLoading: boolean;
|
|
165
|
+
error: string | null;
|
|
166
|
+
}>({
|
|
167
|
+
isValid: false,
|
|
168
|
+
discountAmount: BigInt(0),
|
|
169
|
+
minPurchaseAmount: BigInt(0),
|
|
170
|
+
isLoading: false,
|
|
171
|
+
error: null,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Validate discount code on-chain when provided
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
if (!discountCode) {
|
|
177
|
+
setDiscountInfo({
|
|
178
|
+
isValid: false,
|
|
179
|
+
discountAmount: BigInt(0),
|
|
180
|
+
minPurchaseAmount: BigInt(0),
|
|
181
|
+
isLoading: false,
|
|
182
|
+
error: null,
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let cancelled = false;
|
|
188
|
+
|
|
189
|
+
const validateDiscount = async () => {
|
|
190
|
+
setDiscountInfo(prev => ({ ...prev, isLoading: true, error: null }));
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
// Validate against specific pack and fetch full details in parallel
|
|
194
|
+
const [validForPack, codeDetails] = await Promise.all([
|
|
195
|
+
basePublicClient.readContract({
|
|
196
|
+
address: ccShopAddress as `0x${string}`,
|
|
197
|
+
abi: [IS_DISCOUNT_CODE_VALID_FOR_PACK_ABI],
|
|
198
|
+
functionName: "isDiscountCodeValidForPack",
|
|
199
|
+
args: [discountCode, BigInt(packId)],
|
|
200
|
+
}),
|
|
201
|
+
basePublicClient.readContract({
|
|
202
|
+
address: ccShopAddress as `0x${string}`,
|
|
203
|
+
abi: [GET_DISCOUNT_CODE_ABI],
|
|
204
|
+
functionName: "getDiscountCode",
|
|
205
|
+
args: [discountCode],
|
|
206
|
+
}),
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
if (cancelled) return;
|
|
210
|
+
|
|
211
|
+
const [isValid, discountAmount] = validForPack;
|
|
212
|
+
const { minPurchaseAmount, packId: restrictedPackId, exists } = codeDetails;
|
|
213
|
+
|
|
214
|
+
if (!exists) {
|
|
215
|
+
setDiscountInfo({
|
|
216
|
+
isValid: false,
|
|
217
|
+
discountAmount: BigInt(0),
|
|
218
|
+
minPurchaseAmount: BigInt(0),
|
|
219
|
+
isLoading: false,
|
|
220
|
+
error: "Discount code does not exist",
|
|
221
|
+
});
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!isValid) {
|
|
226
|
+
// Provide specific error based on code details
|
|
227
|
+
if (restrictedPackId !== BigInt(0) && restrictedPackId !== BigInt(packId)) {
|
|
228
|
+
setDiscountInfo({
|
|
229
|
+
isValid: false,
|
|
230
|
+
discountAmount: BigInt(0),
|
|
231
|
+
minPurchaseAmount: BigInt(0),
|
|
232
|
+
isLoading: false,
|
|
233
|
+
error: "Discount code is not valid for this pack",
|
|
234
|
+
});
|
|
235
|
+
} else {
|
|
236
|
+
setDiscountInfo({
|
|
237
|
+
isValid: false,
|
|
238
|
+
discountAmount: BigInt(0),
|
|
239
|
+
minPurchaseAmount: BigInt(0),
|
|
240
|
+
isLoading: false,
|
|
241
|
+
error: "Invalid or expired discount code",
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
setDiscountInfo({ isValid: true, discountAmount, minPurchaseAmount, isLoading: false, error: null });
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (cancelled) return;
|
|
250
|
+
console.error("Failed to validate discount code", { discountCode, error });
|
|
251
|
+
setDiscountInfo({
|
|
252
|
+
isValid: false,
|
|
253
|
+
discountAmount: BigInt(0),
|
|
254
|
+
minPurchaseAmount: BigInt(0),
|
|
255
|
+
isLoading: false,
|
|
256
|
+
error: "Failed to validate discount code",
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
validateDiscount();
|
|
262
|
+
|
|
263
|
+
return () => {
|
|
264
|
+
cancelled = true;
|
|
265
|
+
};
|
|
266
|
+
}, [discountCode, ccShopAddress, packId]);
|
|
267
|
+
|
|
268
|
+
// Calculate effective dstAmount after discount
|
|
269
|
+
const effectiveDstAmount = useMemo(() => {
|
|
270
|
+
if (!discountCode || !discountInfo.isValid || discountInfo.discountAmount === BigInt(0)) {
|
|
271
|
+
return totalAmount;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const total = BigInt(totalAmount);
|
|
275
|
+
const discount = discountInfo.discountAmount;
|
|
276
|
+
|
|
277
|
+
if (discount >= total) {
|
|
278
|
+
console.error("Discount exceeds total price", { totalAmount, discountAmount: discount.toString() });
|
|
279
|
+
return "0";
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return (total - discount).toString();
|
|
283
|
+
}, [totalAmount, discountCode, discountInfo.isValid, discountInfo.discountAmount]);
|
|
284
|
+
|
|
285
|
+
// Calculate fiat amount (effectiveDstAmount in USD, assuming USDC with 6 decimals)
|
|
154
286
|
const srcFiatAmount = useMemo(() => {
|
|
155
|
-
if (!
|
|
156
|
-
return formatUnits(
|
|
157
|
-
}, [
|
|
287
|
+
if (!effectiveDstAmount || effectiveDstAmount === "0") return "0";
|
|
288
|
+
return formatUnits(effectiveDstAmount, USDC_BASE.decimals);
|
|
289
|
+
}, [effectiveDstAmount]);
|
|
158
290
|
|
|
159
|
-
// Encode the
|
|
291
|
+
// Encode the contract function call (with or without discount)
|
|
160
292
|
const encodedData = useMemo(() => {
|
|
161
293
|
try {
|
|
294
|
+
if (discountCode && discountInfo.isValid) {
|
|
295
|
+
return encodeFunctionData({
|
|
296
|
+
abi: [BUY_PACKS_FOR_WITH_DISCOUNT_ABI],
|
|
297
|
+
functionName: "buyPacksForWithDiscount",
|
|
298
|
+
args: [recipientAddress as `0x${string}`, BigInt(packId), BigInt(packAmount), discountCode],
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
162
302
|
return encodeFunctionData({
|
|
163
303
|
abi: [BUY_PACKS_FOR_ABI],
|
|
164
304
|
functionName: "buyPacksFor",
|
|
165
305
|
args: [recipientAddress as `0x${string}`, BigInt(packId), BigInt(packAmount)],
|
|
166
306
|
});
|
|
167
307
|
} catch (error) {
|
|
168
|
-
console.error("Failed to encode function data", { recipientAddress, packId, packAmount, error });
|
|
308
|
+
console.error("Failed to encode function data", { recipientAddress, packId, packAmount, discountCode, error });
|
|
169
309
|
return "0x";
|
|
170
310
|
}
|
|
171
|
-
}, [recipientAddress, packId, packAmount]);
|
|
311
|
+
}, [recipientAddress, packId, packAmount, discountCode, discountInfo.isValid]);
|
|
172
312
|
|
|
173
313
|
// Default header if not provided
|
|
174
314
|
const defaultHeader = () => (
|
|
@@ -182,6 +322,47 @@ export function AnySpendCollectorClubPurchase({
|
|
|
182
322
|
</div>
|
|
183
323
|
);
|
|
184
324
|
|
|
325
|
+
// Don't render AnySpendCustom while discount is being validated (avoids showing wrong price)
|
|
326
|
+
if (discountCode && discountInfo.isLoading) {
|
|
327
|
+
return (
|
|
328
|
+
<div className="mb-4 flex flex-col items-center gap-3 text-center">
|
|
329
|
+
<p className="text-as-secondary text-sm">Validating discount code...</p>
|
|
330
|
+
</div>
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (discountCode && discountInfo.error) {
|
|
335
|
+
return (
|
|
336
|
+
<div className="mb-4 flex flex-col items-center gap-3 text-center">
|
|
337
|
+
<p className="text-sm text-red-500">{discountInfo.error}</p>
|
|
338
|
+
</div>
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (
|
|
343
|
+
discountCode &&
|
|
344
|
+
discountInfo.isValid &&
|
|
345
|
+
discountInfo.minPurchaseAmount > BigInt(0) &&
|
|
346
|
+
BigInt(packAmount) < discountInfo.minPurchaseAmount
|
|
347
|
+
) {
|
|
348
|
+
return (
|
|
349
|
+
<div className="mb-4 flex flex-col items-center gap-3 text-center">
|
|
350
|
+
<p className="text-sm text-red-500">
|
|
351
|
+
Minimum purchase of {discountInfo.minPurchaseAmount.toString()} pack
|
|
352
|
+
{discountInfo.minPurchaseAmount > BigInt(1) ? "s" : ""} required for this discount code
|
|
353
|
+
</p>
|
|
354
|
+
</div>
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (discountCode && discountInfo.isValid && effectiveDstAmount === "0") {
|
|
359
|
+
return (
|
|
360
|
+
<div className="mb-4 flex flex-col items-center gap-3 text-center">
|
|
361
|
+
<p className="text-sm text-red-500">Discount exceeds total price</p>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
185
366
|
return (
|
|
186
367
|
<AnySpendCustom
|
|
187
368
|
loadOrder={loadOrder}
|
|
@@ -192,7 +373,7 @@ export function AnySpendCollectorClubPurchase({
|
|
|
192
373
|
orderType="custom"
|
|
193
374
|
dstChainId={BASE_CHAIN_ID}
|
|
194
375
|
dstToken={paymentToken}
|
|
195
|
-
dstAmount={
|
|
376
|
+
dstAmount={effectiveDstAmount}
|
|
196
377
|
contractAddress={ccShopAddress}
|
|
197
378
|
encodedData={encodedData}
|
|
198
379
|
metadata={{
|
|
@@ -201,6 +382,9 @@ export function AnySpendCollectorClubPurchase({
|
|
|
201
382
|
pricePerPack,
|
|
202
383
|
vendingMachineId,
|
|
203
384
|
packType,
|
|
385
|
+
...(discountCode && discountInfo.isValid
|
|
386
|
+
? { discountCode, discountAmount: discountInfo.discountAmount.toString() }
|
|
387
|
+
: {}),
|
|
204
388
|
}}
|
|
205
389
|
header={header || defaultHeader}
|
|
206
390
|
onSuccess={onSuccess}
|
|
@@ -45,6 +45,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
|
45
45
|
|
|
46
46
|
import { base } from "viem/chains";
|
|
47
47
|
import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
|
|
48
|
+
import { useOnOrderSuccess } from "../hooks/useOnOrderSuccess";
|
|
48
49
|
import { useRecipientAddressState } from "../hooks/useRecipientAddressState";
|
|
49
50
|
import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
|
|
50
51
|
import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
|
|
@@ -274,9 +275,6 @@ function AnySpendCustomInner({
|
|
|
274
275
|
|
|
275
276
|
const [orderId, setOrderId] = useState<string | undefined>(loadOrder);
|
|
276
277
|
|
|
277
|
-
// Track if onSuccess has been called for the current order
|
|
278
|
-
const onSuccessCalled = React.useRef(false);
|
|
279
|
-
|
|
280
278
|
const [srcChainId, setSrcChainId] = useState<number>(base.id);
|
|
281
279
|
|
|
282
280
|
// Get token list for token balance check
|
|
@@ -433,21 +431,8 @@ function AnySpendCustomInner({
|
|
|
433
431
|
const { geoData, isOnrampSupported, coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support } =
|
|
434
432
|
useGeoOnrampOptions(srcFiatAmountForGeoCheck);
|
|
435
433
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
console.log("Calling onSuccess");
|
|
439
|
-
const relayTxs = oat?.data?.relayTxs;
|
|
440
|
-
const lastRelayTxHash = relayTxs?.[relayTxs.length - 1]?.txHash;
|
|
441
|
-
const txHash = oat?.data?.executeTx?.txHash || lastRelayTxHash;
|
|
442
|
-
onSuccess?.(txHash);
|
|
443
|
-
onSuccessCalled.current = true;
|
|
444
|
-
}
|
|
445
|
-
}, [oat?.data?.order.status, oat?.data?.executeTx?.txHash, oat?.data?.relayTxs, onSuccess]);
|
|
446
|
-
|
|
447
|
-
// Reset flag when orderId changes
|
|
448
|
-
useEffect(() => {
|
|
449
|
-
onSuccessCalled.current = false;
|
|
450
|
-
}, [orderId]);
|
|
434
|
+
// Call onSuccess when order is executed
|
|
435
|
+
useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
|
|
451
436
|
|
|
452
437
|
const { createOrder: createRegularOrder, isCreatingOrder: isCreatingRegularOrder } = useAnyspendCreateOrder({
|
|
453
438
|
onSuccess: data => {
|
|
@@ -83,6 +83,8 @@ export interface AnySpendCustomExactInProps {
|
|
|
83
83
|
classes?: AnySpendCustomExactInClasses;
|
|
84
84
|
/** When true, allows direct transfer without swap if source and destination token/chain are the same */
|
|
85
85
|
allowDirectTransfer?: boolean;
|
|
86
|
+
/** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
|
|
87
|
+
callbackMetadata?: Record<string, unknown>;
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
export function AnySpendCustomExactIn(props: AnySpendCustomExactInProps) {
|
|
@@ -120,6 +122,7 @@ function AnySpendCustomExactInInner({
|
|
|
120
122
|
returnHomeLabel,
|
|
121
123
|
classes,
|
|
122
124
|
allowDirectTransfer = false,
|
|
125
|
+
callbackMetadata,
|
|
123
126
|
}: AnySpendCustomExactInProps) {
|
|
124
127
|
const actionLabel = customExactInConfig?.action ?? "Custom Execution";
|
|
125
128
|
const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
|
|
@@ -418,7 +421,6 @@ function AnySpendCustomExactInInner({
|
|
|
418
421
|
onSelectCryptoPaymentMethod={() => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD)}
|
|
419
422
|
anyspendQuote={anyspendQuote}
|
|
420
423
|
onTokenSelect={onTokenSelect}
|
|
421
|
-
skipAutoMaxOnTokenChange={!!destinationTokenAmount}
|
|
422
424
|
/>
|
|
423
425
|
) : (
|
|
424
426
|
<motion.div
|
|
@@ -587,6 +589,7 @@ function AnySpendCustomExactInInner({
|
|
|
587
589
|
? normalizeAddress(customExactInConfig.spenderAddress)
|
|
588
590
|
: undefined,
|
|
589
591
|
},
|
|
592
|
+
callbackMetadata,
|
|
590
593
|
});
|
|
591
594
|
} else {
|
|
592
595
|
// EXACT_INPUT mode: create custom_exact_in order (original behavior)
|
|
@@ -604,6 +607,7 @@ function AnySpendCustomExactInInner({
|
|
|
604
607
|
expectedDstAmount: expectedDstAmountRaw,
|
|
605
608
|
creatorAddress: globalAddress,
|
|
606
609
|
payload,
|
|
610
|
+
callbackMetadata,
|
|
607
611
|
});
|
|
608
612
|
}
|
|
609
613
|
} catch (err: any) {
|
|
@@ -123,6 +123,8 @@ export interface AnySpendDepositProps {
|
|
|
123
123
|
allowDirectTransfer?: boolean;
|
|
124
124
|
/** Fixed destination token amount (in wei/smallest unit). When provided, user cannot change the amount. */
|
|
125
125
|
destinationTokenAmount?: string;
|
|
126
|
+
/** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
|
|
127
|
+
callbackMetadata?: Record<string, unknown>;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
// Default supported chains
|
|
@@ -248,6 +250,7 @@ export function AnySpendDeposit({
|
|
|
248
250
|
classes,
|
|
249
251
|
allowDirectTransfer = false,
|
|
250
252
|
destinationTokenAmount,
|
|
253
|
+
callbackMetadata,
|
|
251
254
|
}: AnySpendDepositProps) {
|
|
252
255
|
// Extract deposit-specific classes for convenience
|
|
253
256
|
const depositClasses = classes?.deposit;
|
|
@@ -697,6 +700,7 @@ export function AnySpendDeposit({
|
|
|
697
700
|
classes={classes?.customExactIn}
|
|
698
701
|
allowDirectTransfer={allowDirectTransfer}
|
|
699
702
|
destinationTokenAmount={destinationTokenAmount}
|
|
703
|
+
callbackMetadata={callbackMetadata}
|
|
700
704
|
/>
|
|
701
705
|
) : (
|
|
702
706
|
<AnySpend
|
|
@@ -720,6 +724,7 @@ export function AnySpendDeposit({
|
|
|
720
724
|
classes={classes?.anySpend}
|
|
721
725
|
allowDirectTransfer={allowDirectTransfer}
|
|
722
726
|
destinationTokenAmount={destinationTokenAmount}
|
|
727
|
+
callbackMetadata={callbackMetadata}
|
|
723
728
|
/>
|
|
724
729
|
)}
|
|
725
730
|
</div>
|