@b3dotfun/sdk 0.0.83-alpha.1 → 0.0.83-alpha.2

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 (70) hide show
  1. package/dist/cjs/anyspend/constants/index.d.ts +1 -1
  2. package/dist/cjs/anyspend/constants/index.js +2 -2
  3. package/dist/cjs/anyspend/react/components/AnySpend.js +5 -1
  4. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +1 -1
  5. package/dist/cjs/anyspend/react/components/common/InsufficientDepositPayment.js +1 -1
  6. package/dist/cjs/anyspend/react/components/common/OrderDetails.js +5 -4
  7. package/dist/cjs/anyspend/react/components/common/OrderToken.js +2 -2
  8. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +1 -1
  9. package/dist/cjs/anyspend/react/components/common/OrderTokenAmountFiat.js +1 -1
  10. package/dist/cjs/anyspend/react/components/common/OrderTokenAmountNew.js +2 -2
  11. package/dist/cjs/anyspend/react/components/common/TransferCryptoDetails.js +1 -1
  12. package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
  13. package/dist/cjs/anyspend/react/hooks/index.js +1 -0
  14. package/dist/cjs/anyspend/react/hooks/useHyperliquidTransfer.d.ts +37 -0
  15. package/dist/cjs/anyspend/react/hooks/useHyperliquidTransfer.js +133 -0
  16. package/dist/cjs/anyspend/types/chain.d.ts +10 -2
  17. package/dist/cjs/anyspend/types/chain.js +1 -0
  18. package/dist/cjs/anyspend/utils/address.d.ts +11 -0
  19. package/dist/cjs/anyspend/utils/address.js +15 -0
  20. package/dist/cjs/anyspend/utils/chain.d.ts +20 -1
  21. package/dist/cjs/anyspend/utils/chain.js +73 -4
  22. package/dist/cjs/anyspend/utils/token.d.ts +1 -0
  23. package/dist/cjs/anyspend/utils/token.js +19 -6
  24. package/dist/esm/anyspend/constants/index.d.ts +1 -1
  25. package/dist/esm/anyspend/constants/index.js +1 -1
  26. package/dist/esm/anyspend/react/components/AnySpend.js +6 -2
  27. package/dist/esm/anyspend/react/components/AnySpendCustom.js +2 -2
  28. package/dist/esm/anyspend/react/components/common/InsufficientDepositPayment.js +2 -2
  29. package/dist/esm/anyspend/react/components/common/OrderDetails.js +6 -5
  30. package/dist/esm/anyspend/react/components/common/OrderToken.js +3 -3
  31. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +2 -2
  32. package/dist/esm/anyspend/react/components/common/OrderTokenAmountFiat.js +2 -2
  33. package/dist/esm/anyspend/react/components/common/OrderTokenAmountNew.js +3 -3
  34. package/dist/esm/anyspend/react/components/common/TransferCryptoDetails.js +2 -2
  35. package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
  36. package/dist/esm/anyspend/react/hooks/index.js +1 -0
  37. package/dist/esm/anyspend/react/hooks/useHyperliquidTransfer.d.ts +37 -0
  38. package/dist/esm/anyspend/react/hooks/useHyperliquidTransfer.js +127 -0
  39. package/dist/esm/anyspend/types/chain.d.ts +10 -2
  40. package/dist/esm/anyspend/types/chain.js +1 -0
  41. package/dist/esm/anyspend/utils/address.d.ts +11 -0
  42. package/dist/esm/anyspend/utils/address.js +14 -0
  43. package/dist/esm/anyspend/utils/chain.d.ts +20 -1
  44. package/dist/esm/anyspend/utils/chain.js +70 -4
  45. package/dist/esm/anyspend/utils/token.d.ts +1 -0
  46. package/dist/esm/anyspend/utils/token.js +19 -7
  47. package/dist/types/anyspend/constants/index.d.ts +1 -1
  48. package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
  49. package/dist/types/anyspend/react/hooks/useHyperliquidTransfer.d.ts +37 -0
  50. package/dist/types/anyspend/types/chain.d.ts +10 -2
  51. package/dist/types/anyspend/utils/address.d.ts +11 -0
  52. package/dist/types/anyspend/utils/chain.d.ts +20 -1
  53. package/dist/types/anyspend/utils/token.d.ts +1 -0
  54. package/package.json +1 -1
  55. package/src/anyspend/constants/index.ts +1 -1
  56. package/src/anyspend/react/components/AnySpend.tsx +7 -1
  57. package/src/anyspend/react/components/AnySpendCustom.tsx +2 -2
  58. package/src/anyspend/react/components/common/InsufficientDepositPayment.tsx +2 -2
  59. package/src/anyspend/react/components/common/OrderDetails.tsx +6 -5
  60. package/src/anyspend/react/components/common/OrderToken.tsx +5 -5
  61. package/src/anyspend/react/components/common/OrderTokenAmount.tsx +3 -3
  62. package/src/anyspend/react/components/common/OrderTokenAmountFiat.tsx +3 -3
  63. package/src/anyspend/react/components/common/OrderTokenAmountNew.tsx +6 -6
  64. package/src/anyspend/react/components/common/TransferCryptoDetails.tsx +2 -2
  65. package/src/anyspend/react/hooks/index.ts +1 -0
  66. package/src/anyspend/react/hooks/useHyperliquidTransfer.ts +152 -0
  67. package/src/anyspend/types/chain.ts +10 -1
  68. package/src/anyspend/utils/address.ts +15 -0
  69. package/src/anyspend/utils/chain.ts +84 -4
  70. package/src/anyspend/utils/token.ts +20 -7
@@ -4,8 +4,8 @@ import {
4
4
  ALL_CHAINS,
5
5
  getChainName,
6
6
  getPaymentUrl,
7
- RELAY_ETH_ADDRESS,
8
7
  RELAY_SOLANA_MAINNET_CHAIN_ID,
8
+ ZERO_ADDRESS,
9
9
  } from "@b3dotfun/sdk/anyspend";
10
10
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
11
11
  import { Badge, CopyToClipboard, ShinyButton, TextLoop } from "@b3dotfun/sdk/global-account/react";
@@ -121,7 +121,7 @@ export function InsufficientDepositPayment({
121
121
  value={getPaymentUrl(
122
122
  order.globalAddress,
123
123
  BigInt(depositDeficit),
124
- order.srcTokenAddress === RELAY_ETH_ADDRESS ? srcToken?.symbol || "ETH" : order.srcTokenAddress,
124
+ order.srcTokenAddress === ZERO_ADDRESS ? srcToken?.symbol || "ETH" : order.srcTokenAddress,
125
125
  order.srcChain,
126
126
  srcToken?.decimals,
127
127
  )}
@@ -8,8 +8,8 @@ import {
8
8
  getPaymentUrl,
9
9
  getStatusDisplay,
10
10
  isNativeToken,
11
- RELAY_ETH_ADDRESS,
12
11
  RELAY_SOLANA_MAINNET_CHAIN_ID,
12
+ ZERO_ADDRESS,
13
13
  } from "@b3dotfun/sdk/anyspend";
14
14
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
15
15
  import {
@@ -327,16 +327,17 @@ export const OrderDetails = memo(function OrderDetails({
327
327
  // Main payment handler that triggers chain switch and payment
328
328
  const handlePayment = useCallback(async () => {
329
329
  console.log("Initiating payment process. Target chain:", order.srcChain, "Current chain:", walletClient?.chain?.id);
330
+ const amountToSend = depositDeficit > BigInt(0) ? depositDeficit.toString() : order.srcAmount;
330
331
  if (order.srcChain === RELAY_SOLANA_MAINNET_CHAIN_ID) {
331
- // Use the existing depositDeficit calculation to determine amount to send
332
- const amountToSend = depositDeficit > BigInt(0) ? depositDeficit.toString() : order.srcAmount;
332
+ // Solana payment flow
333
333
  await initiatePhantomTransfer({
334
334
  amountLamports: amountToSend,
335
335
  tokenAddress: order.srcTokenAddress,
336
336
  recipientAddress: order.globalAddress,
337
337
  });
338
338
  } else {
339
- // Use unified payment process for both EOA and AA wallets
339
+ // EVM payment flow (EOA and AA wallets)
340
+ // Note: Hyperliquid is NOT supported as source chain, only as destination chain
340
341
  await handleUnifiedPaymentProcess();
341
342
  }
342
343
  }, [order, walletClient?.chain?.id, depositDeficit, handleUnifiedPaymentProcess, initiatePhantomTransfer]);
@@ -1039,7 +1040,7 @@ export const OrderDetails = memo(function OrderDetails({
1039
1040
  value={getPaymentUrl(
1040
1041
  order.globalAddress,
1041
1042
  BigInt(order.srcAmount),
1042
- order.srcTokenAddress === RELAY_ETH_ADDRESS ? srcToken?.symbol || "ETH" : order.srcTokenAddress,
1043
+ order.srcTokenAddress === ZERO_ADDRESS ? srcToken?.symbol || "ETH" : order.srcTokenAddress,
1043
1044
  order.srcChain,
1044
1045
  srcToken?.decimals,
1045
1046
  )}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
- import { ALL_CHAINS, RELAY_ETH_ADDRESS, RELAY_SOLANA_MAINNET_CHAIN_ID } from "@b3dotfun/sdk/anyspend";
3
+ import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID, ZERO_ADDRESS, getAvailableChainIds } from "@b3dotfun/sdk/anyspend";
4
+ import { components } from "@b3dotfun/sdk/anyspend/types/api";
4
5
  import { Button, useAccountWallet, useTokenBalancesByChain } from "@b3dotfun/sdk/global-account/react";
5
6
  import { cn } from "@b3dotfun/sdk/shared/utils";
6
7
  import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
@@ -9,7 +10,6 @@ import { TokenSelector } from "@relayprotocol/relay-kit-ui";
9
10
  import { CheckCircle2, ChevronsUpDown } from "lucide-react";
10
11
  import { useMemo } from "react";
11
12
  import { ChainTokenIcon } from "./ChainTokenIcon";
12
- import { components } from "@b3dotfun/sdk/anyspend/types/api";
13
13
 
14
14
  export function OrderToken({
15
15
  context,
@@ -44,7 +44,7 @@ export function OrderToken({
44
44
  // Get balance for the selected token
45
45
  let balance: bigint | null = null;
46
46
  if (token && wallet?.address) {
47
- if (token.address === RELAY_ETH_ADDRESS) {
47
+ if (token.address === ZERO_ADDRESS) {
48
48
  // Native token
49
49
  const nativeToken = nativeTokens?.find(t => t.chainId === chainId);
50
50
  balance = nativeToken?.value ?? null;
@@ -69,12 +69,12 @@ export function OrderToken({
69
69
  return (
70
70
  <TokenSelector
71
71
  address={address}
72
- chainIdsFilter={Object.values(ALL_CHAINS).map(chain => chain.id)}
72
+ chainIdsFilter={getAvailableChainIds(context)}
73
73
  context={context}
74
74
  fromChainWalletVMSupported={true}
75
75
  isValidAddress={true}
76
76
  key={undefined}
77
- lockedChainIds={Object.values(ALL_CHAINS).map(chain => chain.id)}
77
+ lockedChainIds={getAvailableChainIds(context)}
78
78
  multiWalletSupportEnabled={true}
79
79
  onAnalyticEvent={undefined}
80
80
  popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
@@ -5,7 +5,7 @@ import { useEffect, useRef } from "react";
5
5
  import { NumericFormat } from "react-number-format";
6
6
  import { formatUnits } from "viem";
7
7
 
8
- import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID } from "@b3dotfun/sdk/anyspend";
8
+ import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID, getAvailableChainIds } from "@b3dotfun/sdk/anyspend";
9
9
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
10
10
  import { getNativeRequired } from "@b3dotfun/sdk/anyspend/utils/chain";
11
11
  import { isNativeToken } from "@b3dotfun/sdk/anyspend/utils/token";
@@ -163,12 +163,12 @@ export function OrderTokenAmount({
163
163
  {!hideTokenSelect && (
164
164
  <TokenSelector
165
165
  address={address}
166
- chainIdsFilter={Object.values(ALL_CHAINS).map(chain => chain.id)}
166
+ chainIdsFilter={getAvailableChainIds(context)}
167
167
  context={context}
168
168
  fromChainWalletVMSupported={true}
169
169
  isValidAddress={true}
170
170
  key={`selector-${context}-${token.address}-${chainId}`}
171
- lockedChainIds={Object.values(ALL_CHAINS).map(chain => chain.id)}
171
+ lockedChainIds={getAvailableChainIds(context)}
172
172
  multiWalletSupportEnabled={true}
173
173
  onAnalyticEvent={undefined}
174
174
  popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
@@ -4,7 +4,7 @@ import { ChevronsUpDown } from "lucide-react";
4
4
  import { useEffect, useRef } from "react";
5
5
  import { NumericFormat } from "react-number-format";
6
6
 
7
- import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID } from "@b3dotfun/sdk/anyspend";
7
+ import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID, getAvailableChainIds } from "@b3dotfun/sdk/anyspend";
8
8
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
9
9
  import { cn } from "@b3dotfun/sdk/shared/utils";
10
10
  import { TokenSelector } from "@relayprotocol/relay-kit-ui";
@@ -79,12 +79,12 @@ export function OrderTokenAmountFiat({
79
79
  return (
80
80
  <TokenSelector
81
81
  address={address}
82
- chainIdsFilter={Object.values(ALL_CHAINS).map(chain => chain.id)}
82
+ chainIdsFilter={getAvailableChainIds(context)}
83
83
  context={context}
84
84
  fromChainWalletVMSupported={true}
85
85
  isValidAddress={true}
86
86
  key={`selector-${context}-${token.address}-${chainId}`}
87
- lockedChainIds={Object.values(ALL_CHAINS).map(chain => chain.id)}
87
+ lockedChainIds={getAvailableChainIds(context)}
88
88
  multiWalletSupportEnabled={true}
89
89
  onAnalyticEvent={undefined}
90
90
  popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
@@ -4,12 +4,12 @@ import { ChevronsUpDown } from "lucide-react";
4
4
  import { useEffect, useRef } from "react";
5
5
  import { NumericFormat } from "react-number-format";
6
6
 
7
- import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID } from "@b3dotfun/sdk/anyspend";
7
+ import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID, getAvailableChainIds } from "@b3dotfun/sdk/anyspend";
8
+ import { components } from "@b3dotfun/sdk/anyspend/types/api";
8
9
  import { Button } from "@b3dotfun/sdk/global-account/react";
9
10
  import { cn } from "@b3dotfun/sdk/shared/utils";
10
11
  import { TokenSelector } from "@relayprotocol/relay-kit-ui";
11
12
  import { ChainTokenIcon } from "./ChainTokenIcon";
12
- import { components } from "@b3dotfun/sdk/anyspend/types/api";
13
13
 
14
14
  export function OrderTokenAmountFiat({
15
15
  disabled,
@@ -118,12 +118,12 @@ export function OrderTokenAmountFiat({
118
118
  {!hideTokenSelect && (
119
119
  <TokenSelector
120
120
  address={address}
121
- chainIdsFilter={Object.values(ALL_CHAINS).map(chain => chain.id)}
121
+ chainIdsFilter={getAvailableChainIds(context)}
122
122
  context={context}
123
123
  fromChainWalletVMSupported={true}
124
124
  isValidAddress={true}
125
125
  key={`selector-${context}-${token.address}-${chainId}`}
126
- lockedChainIds={Object.values(ALL_CHAINS).map(chain => chain.id)}
126
+ lockedChainIds={getAvailableChainIds(context)}
127
127
  multiWalletSupportEnabled={true}
128
128
  onAnalyticEvent={undefined}
129
129
  popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
@@ -173,12 +173,12 @@ export function OrderTokenAmountFiat({
173
173
  {!hideTokenSelect && (
174
174
  <TokenSelector
175
175
  address={address}
176
- chainIdsFilter={Object.values(ALL_CHAINS).map(chain => chain.id)}
176
+ chainIdsFilter={getAvailableChainIds(context)}
177
177
  context={context}
178
178
  fromChainWalletVMSupported={true}
179
179
  isValidAddress={true}
180
180
  key={`selector-${context}-${token.address}-${chainId}`}
181
- lockedChainIds={Object.values(ALL_CHAINS).map(chain => chain.id)}
181
+ lockedChainIds={getAvailableChainIds(context)}
182
182
  multiWalletSupportEnabled={true}
183
183
  onAnalyticEvent={undefined}
184
184
  popularChainIds={[1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID]}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { ALL_CHAINS, getChainName, getPaymentUrl, RELAY_ETH_ADDRESS } from "@b3dotfun/sdk/anyspend";
3
+ import { ALL_CHAINS, getChainName, getPaymentUrl, ZERO_ADDRESS } from "@b3dotfun/sdk/anyspend";
4
4
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
5
5
  import { CopyToClipboard, ShinyButton, TextLoop, toast } from "@b3dotfun/sdk/global-account/react";
6
6
  import { cn } from "@b3dotfun/sdk/shared/utils";
@@ -206,7 +206,7 @@ export const TransferCryptoDetails = memo(function TransferCryptoDetails({
206
206
  value={getPaymentUrl(
207
207
  order.globalAddress,
208
208
  BigInt(order.srcAmount),
209
- order.srcTokenAddress === RELAY_ETH_ADDRESS ? "ETH" : order.srcTokenAddress,
209
+ order.srcTokenAddress === ZERO_ADDRESS ? "ETH" : order.srcTokenAddress,
210
210
  order.srcChain,
211
211
  srcToken?.decimals,
212
212
  )}
@@ -8,6 +8,7 @@ export * from "./useCoinbaseOnrampOptions";
8
8
  export * from "./useConnectedUserProfile";
9
9
  export * from "./useGeoOnrampOptions";
10
10
  export * from "./useGetGeo";
11
+ export * from "./useHyperliquidTransfer";
11
12
  export * from "./useRecipientAddressState";
12
13
  export * from "./useSigMint";
13
14
  export * from "./useStripeClientSecret";
@@ -0,0 +1,152 @@
1
+ import { HYPERLIQUID_CHAIN_ID, HYPERLIQUID_MAINNET } from "@b3dotfun/sdk/anyspend";
2
+ import { toast } from "@b3dotfun/sdk/global-account/react";
3
+ import { formatUnits } from "@b3dotfun/sdk/shared/utils/number";
4
+ import axios from "axios";
5
+ import { useCallback } from "react";
6
+ import { parseSignature } from "viem";
7
+ import { useWalletClient } from "wagmi";
8
+
9
+ interface HyperliquidTransferParams {
10
+ /** Amount in smallest unit (USDC has 6 decimals) */
11
+ amount: string;
12
+ /** Recipient address */
13
+ destination: string;
14
+ }
15
+
16
+ /**
17
+ * @deprecated This hook is NOT USED in production.
18
+ *
19
+ * Hyperliquid is only supported as DESTINATION CHAIN (not source chain).
20
+ * Users cannot send FROM Hyperliquid in our flow, so EIP-712 signing is not needed.
21
+ *
22
+ * This hook was created during initial planning but is kept for:
23
+ * - Reference if we need to support source chain in the future
24
+ * - Understanding how Hyperliquid EIP-712 transfers work
25
+ *
26
+ * DO NOT USE THIS HOOK IN PRODUCTION CODE.
27
+ *
28
+ * Custom hook for handling Hyperliquid transfers via EIP-712 signature.
29
+ * Based on Relay SDK's Hyperliquid implementation.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * const { initiateTransfer } = useHyperliquidTransfer();
34
+ *
35
+ * await initiateTransfer({
36
+ * amount: "1000000", // 1 USDC
37
+ * destination: "0x..."
38
+ * });
39
+ * ```
40
+ */
41
+ export function useHyperliquidTransfer() {
42
+ const { data: walletClient } = useWalletClient();
43
+
44
+ /**
45
+ * Get the connected wallet address if available.
46
+ */
47
+ const getConnectedAddress = useCallback(() => {
48
+ return walletClient?.account?.address || null;
49
+ }, [walletClient]);
50
+
51
+ /**
52
+ * Initiate Hyperliquid transfer by signing EIP-712 message and sending to Hyperliquid API.
53
+ */
54
+ const initiateTransfer = useCallback(
55
+ async ({ amount, destination }: HyperliquidTransferParams): Promise<void> => {
56
+ if (!walletClient?.account) {
57
+ toast.error("Please connect your wallet");
58
+ throw new Error("Wallet not connected");
59
+ }
60
+
61
+ try {
62
+ const currentTime = new Date().getTime();
63
+
64
+ // Convert amount from smallest unit (6 decimals) to display format.
65
+ // e.g., "11151533" -> "11.151533"
66
+ const displayAmount = formatUnits(amount, 6);
67
+
68
+ // Prepare EIP-712 typed data for Hyperliquid USD send.
69
+ const typedData = {
70
+ domain: {
71
+ name: "HyperliquidSignTransaction",
72
+ version: "1",
73
+ chainId: HYPERLIQUID_CHAIN_ID,
74
+ verifyingContract: "0x0000000000000000000000000000000000000000" as `0x${string}`,
75
+ },
76
+ types: {
77
+ "HyperliquidTransaction:UsdSend": [
78
+ { name: "hyperliquidChain", type: "string" },
79
+ { name: "destination", type: "string" },
80
+ { name: "amount", type: "string" },
81
+ { name: "time", type: "uint64" },
82
+ ],
83
+ },
84
+ primaryType: "HyperliquidTransaction:UsdSend" as const,
85
+ message: {
86
+ hyperliquidChain: "Mainnet",
87
+ destination: destination.toLowerCase(),
88
+ amount: displayAmount,
89
+ time: BigInt(currentTime),
90
+ },
91
+ };
92
+
93
+ toast.info("Please sign the message in your wallet");
94
+
95
+ // Sign EIP-712 message.
96
+ const signature = await walletClient.signTypedData(typedData);
97
+
98
+ // Parse signature to get r, s, v components.
99
+ const { r, s, v } = parseSignature(signature);
100
+
101
+ toast.info("Sending transaction to Hyperliquid...");
102
+
103
+ // Send signature to Hyperliquid API.
104
+ const response = await axios.post(HYPERLIQUID_MAINNET.apiUrl + "/exchange", {
105
+ signature: {
106
+ r,
107
+ s,
108
+ v: Number(v ?? 0n),
109
+ },
110
+ nonce: currentTime,
111
+ action: {
112
+ type: "usdSend",
113
+ signatureChainId: `0x${HYPERLIQUID_CHAIN_ID.toString(16)}`,
114
+ hyperliquidChain: "Mainnet",
115
+ destination: destination.toLowerCase(),
116
+ amount: displayAmount,
117
+ time: currentTime,
118
+ },
119
+ });
120
+
121
+ // Check response status.
122
+ if (!response || response.status !== 200 || response.data?.status !== "ok") {
123
+ const errorMsg = response?.data?.error || "Failed to send transaction to Hyperliquid";
124
+ toast.error(errorMsg);
125
+ throw new Error(errorMsg);
126
+ }
127
+
128
+ toast.success("Transaction sent to Hyperliquid successfully!");
129
+ } catch (error: any) {
130
+ console.error("Hyperliquid transfer error:", error);
131
+
132
+ // Handle user rejection.
133
+ if (error?.message?.includes("User rejected") || error?.code === 4001) {
134
+ toast.error("Transaction signature rejected");
135
+ throw new Error("User rejected signature");
136
+ }
137
+
138
+ // Handle other errors.
139
+ const errorMsg = error?.message || "Failed to complete Hyperliquid transfer";
140
+ toast.error(errorMsg);
141
+ throw error;
142
+ }
143
+ },
144
+ [walletClient],
145
+ );
146
+
147
+ return {
148
+ initiateTransfer,
149
+ getConnectedAddress,
150
+ isWalletConnected: !!walletClient?.account,
151
+ };
152
+ }
@@ -1,9 +1,10 @@
1
- import { Chain } from "viem";
2
1
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
2
+ import { Chain } from "viem";
3
3
 
4
4
  export enum ChainType {
5
5
  EVM = "evm",
6
6
  SOLANA = "solana",
7
+ HYPERLIQUID = "hyperliquid",
7
8
  }
8
9
 
9
10
  export interface IBaseChain {
@@ -29,3 +30,11 @@ export interface IEVMChain extends IBaseChain {
29
30
  export interface ISolanaChain extends IBaseChain {
30
31
  type: ChainType.SOLANA;
31
32
  }
33
+
34
+ export interface IHyperliquidChain extends IBaseChain {
35
+ type: ChainType.HYPERLIQUID;
36
+ apiUrl: string;
37
+ blockExplorer: {
38
+ url: string;
39
+ };
40
+ }
@@ -1,4 +1,5 @@
1
1
  import { isAddress } from "viem";
2
+ import { HYPERLIQUID_CHAIN_ID } from "./token";
2
3
 
3
4
  export function isSolanaAddress(address: string): boolean {
4
5
  // Solana addresses are 32-byte base58 strings (usually 32-44 characters)
@@ -10,6 +11,20 @@ export function isEvmOrSolanaAddress(address: string): boolean {
10
11
  return isAddress(address) || isSolanaAddress(address);
11
12
  }
12
13
 
14
+ /**
15
+ * Check if an address is Hyperliquid's special USDC address.
16
+ * Hyperliquid USDC uses a special 34-character format (0x + 32 hex digits)
17
+ * instead of the standard 42-character Ethereum address format.
18
+ * This is required by Relay SDK for Hyperliquid integration.
19
+ *
20
+ * @param chainId - The chain ID to check
21
+ * @param address - The token address to validate
22
+ * @returns true if the address is Hyperliquid USDC's special format
23
+ */
24
+ export function isHyperliquidUSDC(chainId: number, address: string): boolean {
25
+ return chainId === HYPERLIQUID_CHAIN_ID && address.toLowerCase() === "0x00000000000000000000000000000000";
26
+ }
27
+
13
28
  export function normalizeAddress(address: string): string {
14
29
  if (isSolanaAddress(address)) {
15
30
  return address;
@@ -14,15 +14,17 @@ import {
14
14
  WalletClient,
15
15
  } from "viem";
16
16
  import { abstract, arbitrum, avalanche, b3, base, bsc, mainnet, optimism, polygon } from "viem/chains";
17
- import { ChainType, IBaseChain, IEVMChain, ISolanaChain } from "../types/chain";
17
+ import { ChainType, IBaseChain, IEVMChain, IHyperliquidChain, ISolanaChain } from "../types/chain";
18
18
  import {
19
19
  getAvaxToken,
20
20
  getBnbToken,
21
21
  getEthToken,
22
22
  getHyperEVMNativeToken,
23
+ getHyperliquidUSDCToken,
23
24
  getPolToken,
24
25
  getSolanaToken,
25
26
  HYPEREVM_CHAIN_ID,
27
+ HYPERLIQUID_CHAIN_ID,
26
28
  } from "./token";
27
29
 
28
30
  function getCustomEvmChain(chain: Chain, rpcUrl: string): Chain {
@@ -283,11 +285,31 @@ export const SOLANA_MAINNET: ISolanaChain = {
283
285
  coingeckoName: "solana",
284
286
  };
285
287
 
288
+ export const HYPERLIQUID_MAINNET: IHyperliquidChain = {
289
+ id: HYPERLIQUID_CHAIN_ID,
290
+ name: "Hyperliquid",
291
+ type: ChainType.HYPERLIQUID,
292
+ logoUrl: "https://s2.coinmarketcap.com/static/img/coins/64x64/32196.png",
293
+ nativeRequired: BigInt(0), // No native transfer needed - using Relay's useDepositAddress flow (USDC is native)
294
+ canDepositNative: true, // Can deposit USDC (native token)
295
+ defaultToken: getHyperliquidUSDCToken(),
296
+ nativeToken: getHyperliquidUSDCToken(),
297
+ coingeckoName: null,
298
+ apiUrl: "https://api.hyperliquid.xyz",
299
+ blockExplorer: {
300
+ url: "https://app.hyperliquid.xyz/explorer",
301
+ },
302
+ };
303
+
286
304
  export const EVM_CHAINS: Record<number, IEVMChain> = { ...EVM_MAINNET, ...EVM_TESTNET };
287
305
 
288
306
  export const SOLANA_CHAINS: Record<number, ISolanaChain> = { [RELAY_SOLANA_MAINNET_CHAIN_ID]: SOLANA_MAINNET };
289
307
 
290
- export const ALL_CHAINS: Record<number, IBaseChain> = { ...EVM_CHAINS, ...SOLANA_CHAINS };
308
+ export const HYPERLIQUID_CHAINS: Record<number, IHyperliquidChain> = {
309
+ [HYPERLIQUID_CHAIN_ID]: HYPERLIQUID_MAINNET,
310
+ };
311
+
312
+ export const ALL_CHAINS: Record<number, IBaseChain> = { ...EVM_CHAINS, ...SOLANA_CHAINS, ...HYPERLIQUID_CHAINS };
291
313
 
292
314
  export function getSolanaChains(network: "mainnet" | "testnet"): ISolanaChain {
293
315
  invariant(network === "mainnet", "Solana chain is only supported on mainnet");
@@ -335,7 +357,9 @@ export function canDepositNative(chainId: number): boolean {
335
357
  }
336
358
 
337
359
  export function isMainnet(chainId: number): boolean {
338
- return EVM_MAINNET[chainId] !== undefined || RELAY_SOLANA_MAINNET_CHAIN_ID === chainId;
360
+ return (
361
+ EVM_MAINNET[chainId] !== undefined || RELAY_SOLANA_MAINNET_CHAIN_ID === chainId || HYPERLIQUID_CHAIN_ID === chainId
362
+ );
339
363
  }
340
364
 
341
365
  export function isTestnet(chainId: number): boolean {
@@ -349,7 +373,13 @@ export function getDefaultToken(chainId: number): components["schemas"]["Token"]
349
373
 
350
374
  export function getChainName(chainId: number): string {
351
375
  invariant(ALL_CHAINS[chainId], `Chain ${chainId} is not supported`);
352
- return EVM_CHAINS[chainId] ? EVM_CHAINS[chainId].viem.name : "Solana";
376
+ const chain = ALL_CHAINS[chainId];
377
+
378
+ if (isEvmChain(chainId)) {
379
+ return (chain as IEVMChain).viem.name;
380
+ }
381
+
382
+ return chain.name;
353
383
  }
354
384
 
355
385
  export function getCoingeckoName(chainId: number): string | null {
@@ -511,6 +541,13 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
511
541
  return url;
512
542
  }
513
543
 
544
+ case ChainType.HYPERLIQUID: {
545
+ // NOTE: Hyperliquid is only supported as destination chain (not source chain).
546
+ // Payment URLs are not needed since users cannot send FROM Hyperliquid in our flow.
547
+ // Return address as placeholder (this code path should not be reached).
548
+ return address;
549
+ }
550
+
514
551
  default:
515
552
  // Fallback to just the address if chain type is unknown
516
553
  return address;
@@ -524,6 +561,10 @@ export function getExplorerTxUrl(chainId: number, txHash: string) {
524
561
  if (EVM_CHAINS[chainId]) {
525
562
  return EVM_CHAINS[chainId].viem.blockExplorers?.default.url + "/tx/" + txHash;
526
563
  }
564
+ if (HYPERLIQUID_CHAINS[chainId]) {
565
+ return HYPERLIQUID_CHAINS[chainId].blockExplorer.url + "/tx/" + txHash;
566
+ }
567
+ // Default to Solscan for Solana transactions
527
568
  return "https://solscan.io/tx/" + txHash;
528
569
  }
529
570
 
@@ -531,6 +572,10 @@ export function getExplorerAddressUrl(chainId: number, address: string) {
531
572
  if (EVM_CHAINS[chainId]) {
532
573
  return EVM_CHAINS[chainId].viem.blockExplorers?.default.url + "/address/" + address;
533
574
  }
575
+ if (HYPERLIQUID_CHAINS[chainId]) {
576
+ return HYPERLIQUID_CHAINS[chainId].blockExplorer.url + "/address/" + address;
577
+ }
578
+ // Default to Solscan for Solana addresses
534
579
  return "https://solscan.io/account/" + address;
535
580
  }
536
581
 
@@ -550,3 +595,38 @@ export function getNativeToken(chainId: number): components["schemas"]["Token"]
550
595
  export function isEvmChain(chainId: number): boolean {
551
596
  return Boolean(EVM_CHAINS[chainId]);
552
597
  }
598
+
599
+ export function isHyperliquidChain(chainId: number): boolean {
600
+ return HYPERLIQUID_CHAINS[chainId] !== undefined;
601
+ }
602
+
603
+ export function getHyperliquidChain(chainId: number): IHyperliquidChain {
604
+ invariant(HYPERLIQUID_CHAINS[chainId], `Chain ${chainId} is not a Hyperliquid chain`);
605
+ return HYPERLIQUID_CHAINS[chainId];
606
+ }
607
+
608
+ /**
609
+ * Get available chain IDs for AnySpend based on context (source or destination).
610
+ * Filters out chains that shouldn't be available for the given context.
611
+ *
612
+ * @param context - "from" for source chains, "to" for destination chains
613
+ * @returns Array of available chain IDs
614
+ *
615
+ * @example
616
+ * // Get source chains (excludes Hyperliquid)
617
+ * const sourceChains = getAvailableChainIds("from") // [1, 8453, 137, ...]
618
+ *
619
+ * // Get destination chains (includes Hyperliquid)
620
+ * const destChains = getAvailableChainIds("to") // [1, 8453, 137, ..., 1337]
621
+ */
622
+ export function getAvailableChainIds(context: "from" | "to"): number[] {
623
+ const allChainIds = Object.values(ALL_CHAINS).map(chain => chain.id);
624
+
625
+ if (context === "from") {
626
+ // Hyperliquid is only supported as destination chain, not source chain
627
+ return allChainIds.filter(chainId => chainId !== HYPERLIQUID_CHAIN_ID);
628
+ }
629
+
630
+ // For destination ("to"), all chains are available including Hyperliquid
631
+ return allChainIds;
632
+ }
@@ -1,4 +1,4 @@
1
- import { RELAY_ETH_ADDRESS, RELAY_SOL_ADDRESS, RELAY_SOLANA_MAINNET_CHAIN_ID } from "@b3dotfun/sdk/anyspend/constants";
1
+ import { RELAY_SOL_ADDRESS, RELAY_SOLANA_MAINNET_CHAIN_ID, ZERO_ADDRESS } from "@b3dotfun/sdk/anyspend/constants";
2
2
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
3
3
  import { avalanche, bsc, polygon } from "viem/chains";
4
4
 
@@ -6,7 +6,7 @@ export const HYPERLIQUID_CHAIN_ID = 1337;
6
6
  export const HYPEREVM_CHAIN_ID = 999;
7
7
 
8
8
  export function isNativeToken(address: string): boolean {
9
- return address.toLowerCase() === RELAY_ETH_ADDRESS || address.toLowerCase() === RELAY_SOL_ADDRESS;
9
+ return address.toLowerCase() === ZERO_ADDRESS || address.toLowerCase() === RELAY_SOL_ADDRESS;
10
10
  }
11
11
 
12
12
  export function getSolanaToken(): components["schemas"]["Token"] {
@@ -25,7 +25,7 @@ export function getSolanaToken(): components["schemas"]["Token"] {
25
25
  export function getEthToken(chainId: number): components["schemas"]["Token"] {
26
26
  return {
27
27
  chainId: chainId,
28
- address: RELAY_ETH_ADDRESS,
28
+ address: ZERO_ADDRESS,
29
29
  symbol: "ETH",
30
30
  name: "Ethereum",
31
31
  decimals: 18,
@@ -38,7 +38,7 @@ export function getEthToken(chainId: number): components["schemas"]["Token"] {
38
38
  export function getPolToken(): components["schemas"]["Token"] {
39
39
  return {
40
40
  chainId: polygon.id,
41
- address: RELAY_ETH_ADDRESS,
41
+ address: ZERO_ADDRESS,
42
42
  symbol: "POL",
43
43
  name: "Polygon",
44
44
  decimals: 18,
@@ -51,7 +51,7 @@ export function getPolToken(): components["schemas"]["Token"] {
51
51
  export function getBnbToken(): components["schemas"]["Token"] {
52
52
  return {
53
53
  chainId: bsc.id,
54
- address: RELAY_ETH_ADDRESS,
54
+ address: ZERO_ADDRESS,
55
55
  symbol: "BNB",
56
56
  name: "BNB",
57
57
  decimals: 18,
@@ -64,7 +64,7 @@ export function getBnbToken(): components["schemas"]["Token"] {
64
64
  export function getAvaxToken(): components["schemas"]["Token"] {
65
65
  return {
66
66
  chainId: avalanche.id,
67
- address: RELAY_ETH_ADDRESS,
67
+ address: ZERO_ADDRESS,
68
68
  symbol: "AVAX",
69
69
  name: "AVAX",
70
70
  decimals: 18,
@@ -77,7 +77,7 @@ export function getAvaxToken(): components["schemas"]["Token"] {
77
77
  export function getHyperEVMNativeToken(): components["schemas"]["Token"] {
78
78
  return {
79
79
  chainId: HYPEREVM_CHAIN_ID,
80
- address: RELAY_ETH_ADDRESS,
80
+ address: ZERO_ADDRESS,
81
81
  symbol: "HYPE",
82
82
  name: "HYPE",
83
83
  decimals: 18,
@@ -86,3 +86,16 @@ export function getHyperEVMNativeToken(): components["schemas"]["Token"] {
86
86
  },
87
87
  };
88
88
  }
89
+
90
+ export function getHyperliquidUSDCToken(): components["schemas"]["Token"] {
91
+ return {
92
+ chainId: HYPERLIQUID_CHAIN_ID,
93
+ address: ZERO_ADDRESS,
94
+ symbol: "USDC",
95
+ name: "USD Coin",
96
+ decimals: 6,
97
+ metadata: {
98
+ logoURI: "https://ethereum-optimism.github.io/data/USDC/logo.png",
99
+ },
100
+ };
101
+ }