@b3dotfun/sdk 0.1.65 → 0.1.66-alpha.1
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/app.shared.js +8 -0
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +4 -0
- package/dist/cjs/global-account/react/hooks/useFirstEOA.d.ts +4 -4
- 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/app.shared.js +8 -0
- package/dist/esm/global-account/react/components/B3DynamicModal.js +4 -0
- package/dist/esm/global-account/react/hooks/useFirstEOA.d.ts +4 -4
- 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/hooks/useFirstEOA.d.ts +4 -4
- 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/app.shared.ts +11 -0
- 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
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { ChevronsUpDown } from "lucide-react";
|
|
4
|
-
import { useEffect, useRef } from "react";
|
|
5
4
|
import { NumericFormat } from "react-number-format";
|
|
6
|
-
import { formatUnits } from "viem";
|
|
7
5
|
|
|
8
6
|
import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID, getAvailableChainIds } from "@b3dotfun/sdk/anyspend";
|
|
9
7
|
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
10
|
-
import {
|
|
11
|
-
import { isNativeToken } from "@b3dotfun/sdk/anyspend/utils/token";
|
|
12
|
-
import { Button, useTokenBalance } from "@b3dotfun/sdk/global-account/react";
|
|
8
|
+
import { Button } from "@b3dotfun/sdk/global-account/react";
|
|
13
9
|
import { cn } from "@b3dotfun/sdk/shared/utils";
|
|
14
10
|
import { TokenSelector } from "@relayprotocol/relay-kit-ui";
|
|
15
11
|
import { ChainTokenIcon } from "./ChainTokenIcon";
|
|
@@ -31,8 +27,6 @@ export function OrderTokenAmount({
|
|
|
31
27
|
amountClassName,
|
|
32
28
|
tokenSelectClassName,
|
|
33
29
|
onTokenSelect,
|
|
34
|
-
walletAddress,
|
|
35
|
-
skipAutoMaxOnTokenChange = false,
|
|
36
30
|
}: {
|
|
37
31
|
disabled?: boolean;
|
|
38
32
|
inputValue: string;
|
|
@@ -50,64 +44,7 @@ export function OrderTokenAmount({
|
|
|
50
44
|
amountClassName?: string;
|
|
51
45
|
tokenSelectClassName?: string;
|
|
52
46
|
onTokenSelect?: (token: components["schemas"]["Token"], event: { preventDefault: () => void }) => void;
|
|
53
|
-
walletAddress?: string | undefined;
|
|
54
|
-
/** When true, skip auto-setting max balance when token changes (used for fixed destination amount mode) */
|
|
55
|
-
skipAutoMaxOnTokenChange?: boolean;
|
|
56
47
|
}) {
|
|
57
|
-
// Track previous token to detect changes
|
|
58
|
-
const prevTokenRef = useRef<string>(token.address);
|
|
59
|
-
|
|
60
|
-
// Only get token balance when context is "from" (for setting max amount)
|
|
61
|
-
const { rawBalance } = useTokenBalance({
|
|
62
|
-
token,
|
|
63
|
-
address: context === "from" && walletAddress ? walletAddress : undefined,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
// Only handle "from" context
|
|
68
|
-
if (context !== "from") return;
|
|
69
|
-
|
|
70
|
-
// Skip auto-max when in fixed destination amount mode
|
|
71
|
-
if (skipAutoMaxOnTokenChange) {
|
|
72
|
-
prevTokenRef.current = token.address;
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Check if token changed or if this is the initial load with balance
|
|
77
|
-
const isTokenChanged = prevTokenRef.current !== token.address;
|
|
78
|
-
|
|
79
|
-
if (isTokenChanged && rawBalance) {
|
|
80
|
-
console.log(`Setting max balance - Token: ${token.address}, Changed: ${isTokenChanged}`);
|
|
81
|
-
|
|
82
|
-
// Calculate max amount with gas reserve for native tokens
|
|
83
|
-
let maxAmount: bigint;
|
|
84
|
-
|
|
85
|
-
if (isNativeToken(token.address)) {
|
|
86
|
-
const gasReserve = getNativeRequired(token.chainId);
|
|
87
|
-
// Ensure we don't go negative
|
|
88
|
-
maxAmount = rawBalance > gasReserve ? rawBalance - gasReserve : BigInt(0);
|
|
89
|
-
} else {
|
|
90
|
-
// For ERC20 tokens, use full balance
|
|
91
|
-
maxAmount = rawBalance;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Set the max amount as input value
|
|
95
|
-
onChangeInput(formatUnits(maxAmount, token.decimals));
|
|
96
|
-
|
|
97
|
-
// Update refs
|
|
98
|
-
prevTokenRef.current = token.address;
|
|
99
|
-
}
|
|
100
|
-
}, [
|
|
101
|
-
token.address,
|
|
102
|
-
token.chainId,
|
|
103
|
-
token.decimals,
|
|
104
|
-
chainId,
|
|
105
|
-
context,
|
|
106
|
-
onChangeInput,
|
|
107
|
-
rawBalance,
|
|
108
|
-
skipAutoMaxOnTokenChange,
|
|
109
|
-
]);
|
|
110
|
-
|
|
111
48
|
const handleTokenSelect = (newToken: any) => {
|
|
112
49
|
const token: components["schemas"]["Token"] = {
|
|
113
50
|
address: newToken.address,
|
|
@@ -134,13 +71,7 @@ export function OrderTokenAmount({
|
|
|
134
71
|
}
|
|
135
72
|
}
|
|
136
73
|
|
|
137
|
-
// Mark that we're about to change tokens
|
|
138
|
-
prevTokenRef.current = "changing"; // Temporary value to force effect
|
|
139
|
-
|
|
140
|
-
// Set the chain ID first
|
|
141
74
|
setChainId(newToken.chainId);
|
|
142
|
-
|
|
143
|
-
// Then set the new token - the useEffect will handle setting the max balance
|
|
144
75
|
setToken(token);
|
|
145
76
|
};
|
|
146
77
|
|
|
@@ -24,6 +24,8 @@ export type {
|
|
|
24
24
|
RecipientSelectionClasses,
|
|
25
25
|
} from "./types/classes";
|
|
26
26
|
export { AnySpendDepositHype, HYPE_TOKEN_DETAILS } from "./AnyspendDepositHype";
|
|
27
|
+
export { AnySpendWorkflowTrigger } from "./AnySpendWorkflowTrigger";
|
|
28
|
+
export type { AnySpendWorkflowTriggerProps } from "./AnySpendWorkflowTrigger";
|
|
27
29
|
export * from "./AnySpendFingerprintWrapper";
|
|
28
30
|
export { AnySpendNFT } from "./AnySpendNFT";
|
|
29
31
|
export { AnyspendSignatureMint } from "./AnyspendSignatureMint";
|
|
@@ -12,6 +12,7 @@ export * from "./useGasPrice";
|
|
|
12
12
|
export * from "./useGeoOnrampOptions";
|
|
13
13
|
export * from "./useGetGeo";
|
|
14
14
|
export * from "./useHyperliquidTransfer";
|
|
15
|
+
export * from "./useOnOrderSuccess";
|
|
15
16
|
export * from "./useRecipientAddressState";
|
|
16
17
|
export * from "./useSigMint";
|
|
17
18
|
export * from "./useStripeClientSecret";
|
|
@@ -113,6 +113,7 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError }: UseAnyspend
|
|
|
113
113
|
partnerId,
|
|
114
114
|
clientReferenceId,
|
|
115
115
|
visitorData,
|
|
116
|
+
callbackMetadata: params.callbackMetadata,
|
|
116
117
|
});
|
|
117
118
|
} catch (error: any) {
|
|
118
119
|
// If the error has a response with message and statusCode, throw that
|
|
@@ -22,6 +22,7 @@ export type CreateOrderParams = {
|
|
|
22
22
|
creatorAddress?: string;
|
|
23
23
|
payload?: any;
|
|
24
24
|
metadata?: Record<string, any>;
|
|
25
|
+
callbackMetadata?: Record<string, unknown>;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export type UseAnyspendCreateOrderProps = {
|
|
@@ -87,6 +88,7 @@ export function useAnyspendCreateOrder({ onSuccess, onError }: UseAnyspendCreate
|
|
|
87
88
|
partnerId,
|
|
88
89
|
clientReferenceId,
|
|
89
90
|
visitorData,
|
|
91
|
+
callbackMetadata: params.callbackMetadata,
|
|
90
92
|
});
|
|
91
93
|
} catch (error: any) {
|
|
92
94
|
// If the error has a response with message and statusCode, throw that
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { GetOrderAndTxsResponse } from "../../types/api_req_res";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook to call onSuccess callback when an order is executed.
|
|
6
|
+
* Handles fallback to relayTxs when executeTx is null.
|
|
7
|
+
*/
|
|
8
|
+
export function useOnOrderSuccess({
|
|
9
|
+
orderData,
|
|
10
|
+
orderId,
|
|
11
|
+
onSuccess,
|
|
12
|
+
}: {
|
|
13
|
+
orderData: GetOrderAndTxsResponse | undefined;
|
|
14
|
+
orderId: string | undefined;
|
|
15
|
+
onSuccess?: (txHash?: string) => void;
|
|
16
|
+
}) {
|
|
17
|
+
const onSuccessCalled = useRef(false);
|
|
18
|
+
const prevOrderId = useRef(orderId);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
// Reset flag when orderId changes
|
|
22
|
+
if (prevOrderId.current !== orderId) {
|
|
23
|
+
onSuccessCalled.current = false;
|
|
24
|
+
prevOrderId.current = orderId;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Call onSuccess when order is executed
|
|
28
|
+
if (orderData?.data?.order.status === "executed" && !onSuccessCalled.current) {
|
|
29
|
+
const relayTxs = orderData?.data?.relayTxs;
|
|
30
|
+
const lastSuccessfulRelayTx = relayTxs?.filter(tx => tx.status === "success").pop();
|
|
31
|
+
const txHash = orderData?.data?.executeTx?.txHash || lastSuccessfulRelayTx?.txHash;
|
|
32
|
+
onSuccess?.(txHash);
|
|
33
|
+
onSuccessCalled.current = true;
|
|
34
|
+
}
|
|
35
|
+
}, [orderData, orderId, onSuccess]);
|
|
36
|
+
}
|
|
@@ -70,6 +70,7 @@ export const anyspendService = {
|
|
|
70
70
|
partnerId,
|
|
71
71
|
clientReferenceId,
|
|
72
72
|
visitorData,
|
|
73
|
+
callbackMetadata,
|
|
73
74
|
}: {
|
|
74
75
|
recipientAddress: string;
|
|
75
76
|
type: string;
|
|
@@ -85,6 +86,7 @@ export const anyspendService = {
|
|
|
85
86
|
partnerId?: string;
|
|
86
87
|
clientReferenceId?: string;
|
|
87
88
|
visitorData?: VisitorData;
|
|
89
|
+
callbackMetadata?: Record<string, unknown>;
|
|
88
90
|
}) => {
|
|
89
91
|
const accessToken = await app.authentication.getAccessToken();
|
|
90
92
|
const response = await fetch(`${ANYSPEND_MAINNET_BASE_URL}/orders`, {
|
|
@@ -109,6 +111,7 @@ export const anyspendService = {
|
|
|
109
111
|
creatorAddress,
|
|
110
112
|
partnerId,
|
|
111
113
|
...(clientReferenceId && { clientReferenceId }),
|
|
114
|
+
...(callbackMetadata && { callbackMetadata }),
|
|
112
115
|
}),
|
|
113
116
|
});
|
|
114
117
|
const data: CreateOrderResponse = await response.json();
|
|
@@ -421,7 +421,13 @@ export function getCoingeckoName(chainId: number): string | null {
|
|
|
421
421
|
return ALL_CHAINS[chainId].coingeckoName;
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
export function getPaymentUrl(
|
|
424
|
+
export function getPaymentUrl(
|
|
425
|
+
address: string,
|
|
426
|
+
amount: bigint | undefined,
|
|
427
|
+
currency: string,
|
|
428
|
+
chainId: number,
|
|
429
|
+
decimals?: number,
|
|
430
|
+
) {
|
|
425
431
|
// Get chain type to determine URL format
|
|
426
432
|
const chainType = getChainType(chainId);
|
|
427
433
|
const chain = ALL_CHAINS[chainId];
|
|
@@ -433,8 +439,8 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
|
|
|
433
439
|
// Format: ethereum:[address]@[chainId]?value=[amount]&symbol=[symbol]
|
|
434
440
|
const params = new URLSearchParams();
|
|
435
441
|
|
|
436
|
-
// Add value for native token transfers
|
|
437
|
-
if (currency === chain.nativeToken.symbol) {
|
|
442
|
+
// Add value for native token transfers (skip if amount not provided, e.g. deposit_first)
|
|
443
|
+
if (currency === chain.nativeToken.symbol && amount !== undefined) {
|
|
438
444
|
params.append("value", amount.toString());
|
|
439
445
|
}
|
|
440
446
|
|
|
@@ -453,28 +459,31 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
|
|
|
453
459
|
|
|
454
460
|
// For ERC20 tokens, convert from smallest unit to display units using decimals
|
|
455
461
|
// For example: 2400623 (raw) with 6 decimals becomes "2.400623"
|
|
456
|
-
|
|
457
|
-
if (
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
462
|
+
// Skip amount if not provided (e.g. deposit_first orders)
|
|
463
|
+
if (amount !== undefined) {
|
|
464
|
+
let displayAmount: string;
|
|
465
|
+
if (decimals !== undefined && currency !== chain.nativeToken.symbol) {
|
|
466
|
+
// Convert from smallest unit to display unit for ERC20 tokens
|
|
467
|
+
const divisor = BigInt(10 ** decimals);
|
|
468
|
+
const wholePart = amount / divisor;
|
|
469
|
+
const fractionalPart = amount % divisor;
|
|
470
|
+
|
|
471
|
+
if (fractionalPart === BigInt(0)) {
|
|
472
|
+
displayAmount = wholePart.toString();
|
|
473
|
+
} else {
|
|
474
|
+
// Format fractional part with leading zeros if needed
|
|
475
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
476
|
+
// Remove trailing zeros
|
|
477
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
478
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
479
|
+
}
|
|
465
480
|
} else {
|
|
466
|
-
//
|
|
467
|
-
|
|
468
|
-
// Remove trailing zeros
|
|
469
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
470
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
481
|
+
// For native tokens or when decimals not provided, use raw amount
|
|
482
|
+
displayAmount = amount.toString();
|
|
471
483
|
}
|
|
472
|
-
} else {
|
|
473
|
-
// For native tokens or when decimals not provided, use raw amount
|
|
474
|
-
displayAmount = amount.toString();
|
|
475
|
-
}
|
|
476
484
|
|
|
477
|
-
|
|
485
|
+
tokenParams.append("amount", displayAmount);
|
|
486
|
+
}
|
|
478
487
|
tokenParams.append("address", address); // recipient address
|
|
479
488
|
|
|
480
489
|
// For Arbitrum and other L2s, try a more explicit format
|
|
@@ -495,7 +504,9 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
|
|
|
495
504
|
// to make sure wallets recognize the correct chain
|
|
496
505
|
const nativeParams = new URLSearchParams();
|
|
497
506
|
nativeParams.append("chainId", chainId.toString());
|
|
498
|
-
|
|
507
|
+
if (amount !== undefined) {
|
|
508
|
+
nativeParams.append("value", amount.toString());
|
|
509
|
+
}
|
|
499
510
|
const url = `ethereum:${address}@${chainId}?${nativeParams.toString()}`;
|
|
500
511
|
return url;
|
|
501
512
|
} else {
|
|
@@ -517,60 +528,65 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
|
|
|
517
528
|
|
|
518
529
|
if (isNativeSOL) {
|
|
519
530
|
// Native SOL transfers - convert from lamports to SOL
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
531
|
+
if (amount !== undefined) {
|
|
532
|
+
let displayAmount: string;
|
|
533
|
+
if (decimals !== undefined) {
|
|
534
|
+
const divisor = BigInt(10 ** decimals);
|
|
535
|
+
const wholePart = amount / divisor;
|
|
536
|
+
const fractionalPart = amount % divisor;
|
|
537
|
+
|
|
538
|
+
if (fractionalPart === BigInt(0)) {
|
|
539
|
+
displayAmount = wholePart.toString();
|
|
540
|
+
} else {
|
|
541
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
542
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
543
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
544
|
+
}
|
|
528
545
|
} else {
|
|
529
|
-
|
|
530
|
-
const
|
|
531
|
-
|
|
546
|
+
// Fallback: assume SOL has 9 decimals
|
|
547
|
+
const divisor = BigInt(1000000000); // 1e9
|
|
548
|
+
const wholePart = amount / divisor;
|
|
549
|
+
const fractionalPart = amount % divisor;
|
|
550
|
+
|
|
551
|
+
if (fractionalPart === BigInt(0)) {
|
|
552
|
+
displayAmount = wholePart.toString();
|
|
553
|
+
} else {
|
|
554
|
+
const fractionalStr = fractionalPart.toString().padStart(9, "0");
|
|
555
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
556
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
557
|
+
}
|
|
532
558
|
}
|
|
533
|
-
} else {
|
|
534
|
-
// Fallback: assume SOL has 9 decimals
|
|
535
|
-
const divisor = BigInt(1000000000); // 1e9
|
|
536
|
-
const wholePart = amount / divisor;
|
|
537
|
-
const fractionalPart = amount % divisor;
|
|
538
559
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
} else {
|
|
542
|
-
const fractionalStr = fractionalPart.toString().padStart(9, "0");
|
|
543
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
544
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
545
|
-
}
|
|
560
|
+
// For native SOL, use simple format without spl-token parameter
|
|
561
|
+
params.append("amount", displayAmount);
|
|
546
562
|
}
|
|
547
|
-
|
|
548
|
-
// For native SOL, use simple format without spl-token parameter
|
|
549
|
-
params.append("amount", displayAmount);
|
|
550
563
|
} else {
|
|
551
564
|
// SPL token transfers
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
565
|
+
if (amount !== undefined) {
|
|
566
|
+
let displayAmount: string;
|
|
567
|
+
if (decimals !== undefined) {
|
|
568
|
+
const divisor = BigInt(10 ** decimals);
|
|
569
|
+
const wholePart = amount / divisor;
|
|
570
|
+
const fractionalPart = amount % divisor;
|
|
571
|
+
|
|
572
|
+
if (fractionalPart === BigInt(0)) {
|
|
573
|
+
displayAmount = wholePart.toString();
|
|
574
|
+
} else {
|
|
575
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
576
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
577
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
578
|
+
}
|
|
560
579
|
} else {
|
|
561
|
-
|
|
562
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
563
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
580
|
+
displayAmount = amount.toString();
|
|
564
581
|
}
|
|
565
|
-
} else {
|
|
566
|
-
displayAmount = amount.toString();
|
|
567
|
-
}
|
|
568
582
|
|
|
569
|
-
|
|
583
|
+
params.append("amount", displayAmount);
|
|
584
|
+
}
|
|
570
585
|
params.append("spl-token", currency); // token mint address
|
|
571
586
|
}
|
|
572
587
|
|
|
573
|
-
const
|
|
588
|
+
const queryString = params.toString();
|
|
589
|
+
const url = queryString ? `solana:${address}?${queryString}` : `solana:${address}`;
|
|
574
590
|
console.log("Solana URL (isNativeSOL:", isNativeSOL, "):", url);
|
|
575
591
|
return url;
|
|
576
592
|
}
|
package/src/app.shared.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { B3_AUTH_COOKIE_NAME } from "./shared/constants";
|
|
|
6
6
|
export const B3_API_URL =
|
|
7
7
|
process.env.EXPO_PUBLIC_B3_API || process.env.NEXT_PUBLIC_B3_API || process.env.PUBLIC_B3_API || "https://api.b3.fun";
|
|
8
8
|
|
|
9
|
+
const DEV_USER_GROUP = 4;
|
|
10
|
+
|
|
9
11
|
export const authenticate = async (
|
|
10
12
|
app: ClientApplication,
|
|
11
13
|
accessToken: string,
|
|
@@ -30,6 +32,15 @@ export const authenticate = async (
|
|
|
30
32
|
query: params || {},
|
|
31
33
|
},
|
|
32
34
|
);
|
|
35
|
+
|
|
36
|
+
// Extend cookie expiration to 30 days for dev users
|
|
37
|
+
if (response?.user?.userGroups?.includes(DEV_USER_GROUP)) {
|
|
38
|
+
const token = Cookies.get(B3_AUTH_COOKIE_NAME);
|
|
39
|
+
if (token) {
|
|
40
|
+
Cookies.set(B3_AUTH_COOKIE_NAME, token, { expires: 30 });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
return response;
|
|
34
45
|
} catch (error) {
|
|
35
46
|
return null;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@b3dotfun/sdk/anyspend/react";
|
|
13
13
|
import { AnySpendDeposit } from "@b3dotfun/sdk/anyspend/react/components/AnySpendDeposit";
|
|
14
14
|
import { AnySpendDepositHype } from "@b3dotfun/sdk/anyspend/react/components/AnyspendDepositHype";
|
|
15
|
+
import { AnySpendWorkflowTrigger } from "@b3dotfun/sdk/anyspend/react/components/AnySpendWorkflowTrigger";
|
|
15
16
|
import { AnySpendDepositUpside } from "@b3dotfun/sdk/anyspend/react/components/AnySpendDepositUpside";
|
|
16
17
|
import { AnySpendStakeUpside } from "@b3dotfun/sdk/anyspend/react/components/AnySpendStakeUpside";
|
|
17
18
|
import { AnySpendStakeUpsideExactIn } from "@b3dotfun/sdk/anyspend/react/components/AnySpendStakeUpsideExactIn";
|
|
@@ -65,6 +66,7 @@ export function B3DynamicModal() {
|
|
|
65
66
|
"send",
|
|
66
67
|
"notifications",
|
|
67
68
|
"anySpendDeposit",
|
|
69
|
+
"anySpendWorkflowTrigger",
|
|
68
70
|
];
|
|
69
71
|
|
|
70
72
|
const freestyleTypes = [
|
|
@@ -152,6 +154,8 @@ export function B3DynamicModal() {
|
|
|
152
154
|
return <AnySpendCollectorClubPurchase {...contentType} mode="modal" />;
|
|
153
155
|
case "anySpendDeposit":
|
|
154
156
|
return <AnySpendDeposit {...contentType} mode="modal" />;
|
|
157
|
+
case "anySpendWorkflowTrigger":
|
|
158
|
+
return <AnySpendWorkflowTrigger {...contentType} mode="modal" />;
|
|
155
159
|
case "avatarEditor":
|
|
156
160
|
return <AvatarEditor onSetAvatar={contentType.onSuccess} />;
|
|
157
161
|
case "deposit":
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Users } from "@b3dotfun/b3-api";
|
|
2
|
-
import { useEffect } from "react";
|
|
2
|
+
import { useEffect, useLayoutEffect } from "react";
|
|
3
3
|
import { useUserStore } from "../stores/userStore";
|
|
4
4
|
|
|
5
5
|
const USER_QUERY_KEY = ["b3-user"];
|
|
@@ -15,6 +15,17 @@ export function useUserQuery() {
|
|
|
15
15
|
const setUserStore = useUserStore(state => state.setUser);
|
|
16
16
|
const clearUserStore = useUserStore(state => state.clearUser);
|
|
17
17
|
|
|
18
|
+
// Manually rehydrate persisted store inside useLayoutEffect to avoid
|
|
19
|
+
// updating AuthenticationProvider state during Hydrate render.
|
|
20
|
+
// useLayoutEffect (not useEffect) ensures rehydration triggers a
|
|
21
|
+
// synchronous re-render before any useEffect callbacks fire, so
|
|
22
|
+
// downstream effects always see the persisted user value.
|
|
23
|
+
useLayoutEffect(() => {
|
|
24
|
+
if (!useUserStore.persist.hasHydrated()) {
|
|
25
|
+
useUserStore.persist.rehydrate();
|
|
26
|
+
}
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
18
29
|
// Listen for storage events from other tabs/windows
|
|
19
30
|
useEffect(() => {
|
|
20
31
|
const handleStorageChange = (e: StorageEvent) => {
|
|
@@ -497,12 +497,47 @@ export interface AnySpendCollectorClubPurchaseProps extends BaseModalProps {
|
|
|
497
497
|
forceFiatPayment?: boolean;
|
|
498
498
|
/** Staging environment support */
|
|
499
499
|
isStaging?: boolean;
|
|
500
|
+
/** Optional discount code to apply to the purchase */
|
|
501
|
+
discountCode?: string;
|
|
500
502
|
}
|
|
501
503
|
|
|
502
504
|
/**
|
|
503
505
|
* Props for the AnySpend Deposit modal
|
|
504
506
|
* Flexible deposit component with optional chain selection
|
|
505
507
|
*/
|
|
508
|
+
/**
|
|
509
|
+
* Props for the AnySpend Workflow Trigger modal
|
|
510
|
+
* Handles payments that trigger b3os-workflow runs
|
|
511
|
+
*/
|
|
512
|
+
export interface AnySpendWorkflowTriggerModalProps extends BaseModalProps {
|
|
513
|
+
/** Modal type identifier */
|
|
514
|
+
type: "anySpendWorkflowTrigger";
|
|
515
|
+
/** Payment recipient address (hex) */
|
|
516
|
+
recipientAddress: string;
|
|
517
|
+
/** Destination chain ID */
|
|
518
|
+
chainId: number;
|
|
519
|
+
/** Destination token address */
|
|
520
|
+
tokenAddress: string;
|
|
521
|
+
/** Required payment amount in token base units (wei) */
|
|
522
|
+
amount: string;
|
|
523
|
+
/** Workflow ID to trigger */
|
|
524
|
+
workflowId: string;
|
|
525
|
+
/** Organization ID that owns the workflow */
|
|
526
|
+
orgId: string;
|
|
527
|
+
/** Optional callback metadata */
|
|
528
|
+
callbackMetadata?: {
|
|
529
|
+
inputs?: Record<string, unknown>;
|
|
530
|
+
} & Record<string, unknown>;
|
|
531
|
+
/** Callback when payment succeeds */
|
|
532
|
+
onSuccess?: (amount: string) => void;
|
|
533
|
+
/** Callback when modal is closed */
|
|
534
|
+
onClose?: () => void;
|
|
535
|
+
/** Custom action label */
|
|
536
|
+
actionLabel?: string;
|
|
537
|
+
/** Custom class names */
|
|
538
|
+
classes?: AnySpendAllClasses;
|
|
539
|
+
}
|
|
540
|
+
|
|
506
541
|
export interface AnySpendDepositModalProps extends BaseModalProps {
|
|
507
542
|
/** Modal type identifier */
|
|
508
543
|
type: "anySpendDeposit";
|
|
@@ -560,6 +595,8 @@ export interface AnySpendDepositModalProps extends BaseModalProps {
|
|
|
560
595
|
classes?: AnySpendAllClasses;
|
|
561
596
|
/** Whether to allow direct transfer without swap */
|
|
562
597
|
allowDirectTransfer?: boolean;
|
|
598
|
+
/** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
|
|
599
|
+
callbackMetadata?: Record<string, unknown>;
|
|
563
600
|
}
|
|
564
601
|
|
|
565
602
|
/**
|
|
@@ -591,8 +628,8 @@ export type ModalContentType =
|
|
|
591
628
|
| SendModalProps
|
|
592
629
|
| NotificationsModalProps
|
|
593
630
|
| AnySpendCollectorClubPurchaseProps
|
|
594
|
-
| AnySpendDepositModalProps
|
|
595
|
-
|
|
631
|
+
| AnySpendDepositModalProps
|
|
632
|
+
| AnySpendWorkflowTriggerModalProps;
|
|
596
633
|
|
|
597
634
|
/**
|
|
598
635
|
* State interface for the modal store
|