@silentswap/react 0.1.1 → 0.1.3

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.
@@ -35,9 +35,11 @@ export function useOrderSigning(walletClient, connector, client, setCurrentStep,
35
35
  if (!connector) {
36
36
  throw new Error('Connector required for chain switching');
37
37
  }
38
- if (isDepositingDirectly) {
39
- await ensureChain(NI_CHAIN_ID_AVALANCHE, walletClient, connector);
40
- }
38
+ // ensureChain returns a fresh walletClient bound to the target chain —
39
+ // critical for WalletConnect where the old client's chain scope gets silently dropped.
40
+ const signingClient = isDepositingDirectly
41
+ ? await ensureChain(NI_CHAIN_ID_AVALANCHE, walletClient, connector, { required: false })
42
+ : walletClient;
41
43
  return Promise.all(authorizations.map(async (auth) => ({
42
44
  ...auth,
43
45
  signature: await (async () => {
@@ -45,7 +47,7 @@ export function useOrderSigning(walletClient, connector, client, setCurrentStep,
45
47
  if (auth.type === 'eip3009_deposit') {
46
48
  if (isDepositingDirectly) {
47
49
  // Sign EIP-712 typed data for direct deposit
48
- return await walletClient.signTypedData(auth.eip712);
50
+ return await signingClient.signTypedData(auth.eip712);
49
51
  }
50
52
  else {
51
53
  // Will be deposited through proxy, no signature needed
@@ -83,18 +85,20 @@ export function useOrderSigning(walletClient, connector, client, setCurrentStep,
83
85
  throw new Error('Facilitator group required for order creation');
84
86
  }
85
87
  // Switch to Ethereum for order signing (orders are signed on Ethereum mainnet)
88
+ // ensureChain returns a fresh walletClient bound to the target chain —
89
+ // critical for WalletConnect where the old client's chain scope gets silently dropped.
86
90
  setCurrentStep('Preparing order signature');
87
91
  onStatus?.('Preparing order signature');
88
- await ensureChain(1, walletClient, connector); // Ethereum mainnet
92
+ const signingClient = await ensureChain(1, walletClient, connector, { required: false });
89
93
  // Create EIP-712 document for order
90
94
  const orderDoc = quoteResponseToEip712Document(quoteResponse);
91
95
  // Sign the order's EIP-712 document
92
96
  // Note: viem's signTypedData may normalize the domain.chainId to match the wallet client's
93
97
  // chain configuration, which can result in chainId being a BigInt instead of a number.
94
98
  // This happens because viem uses the wallet's chain configuration internally.
95
- const orderSignature = await walletClient.signTypedData({
99
+ const orderSignature = await signingClient.signTypedData({
96
100
  ...orderDoc,
97
- account: walletClient.account,
101
+ account: signingClient.account,
98
102
  });
99
103
  // Approve proxy authorizations from facilitators
100
104
  const facilitatorReplies = await effectiveGroup.approveProxyAuthorizations(quoteResponse.facilitators, {
@@ -94,7 +94,10 @@ export function useWallet({ client, address, auth, walletClient, connector, allD
94
94
  throw new Error('Connector required for chain switching');
95
95
  }
96
96
  // Ensure we're on Ethereum mainnet for entropy generation (asked core devs about this in tg)
97
- await ensureChain(1, walletClient, connector);
97
+ // ensureChain returns a fresh walletClient bound to the target chain after switching.
98
+ // Critical for WalletConnect: the old walletClient's chain scope doesn't match after
99
+ // a switch, so signing requests get silently dropped by the mobile wallet.
100
+ const signingClient = await ensureChain(1, walletClient, connector, { required: false });
98
101
  // Create EIP-712 document for wallet generation
99
102
  const eip712Doc = createEip712DocForWalletGeneration(`eip155:43114:${address}`, // Gateway contract scope (Avalanche)
100
103
  auth.secretToken);
@@ -102,10 +105,11 @@ export function useWallet({ client, address, auth, walletClient, connector, allD
102
105
  if (eip712Doc.domain && typeof eip712Doc.domain.chainId === 'number') {
103
106
  eip712Doc.domain.chainId = BigInt(eip712Doc.domain.chainId);
104
107
  }
105
- // Sign the document to generate entropy
106
- const signature = await walletClient.signTypedData({
108
+ // Sign the document to generate entropy — use the fresh signingClient, not
109
+ // the closure's walletClient which may be scoped to the pre-switch chain.
110
+ const signature = await signingClient.signTypedData({
107
111
  ...eip712Doc,
108
- account: walletClient.account,
112
+ account: signingClient.account,
109
113
  });
110
114
  return hexToBytes(signature);
111
115
  }, [auth, walletClient, connector, address]);
@@ -176,7 +180,7 @@ export function useWallet({ client, address, auth, walletClient, connector, allD
176
180
  isGeneratingRef.current = false;
177
181
  setIsLoading(false);
178
182
  }
179
- }, [normalizedAddress, getCachedWallet, queryDepositCount, generateEntropy, allDeposits, connector, client.s0xGatewayAddress]);
183
+ }, [normalizedAddress, getCachedWallet, generateEntropy, allDeposits, client.s0xGatewayAddress]);
180
184
  /**
181
185
  * Clear cached wallet
182
186
  */
@@ -194,6 +198,13 @@ export function useWallet({ client, address, auth, walletClient, connector, allD
194
198
  isGeneratingRef.current = false;
195
199
  await generateWallet();
196
200
  }, [clearWallet, generateWallet]);
201
+ // When walletClient or connector changes (e.g. WalletConnect chain switch causes
202
+ // wagmi to recreate the walletClient), reset the generation guard. Any in-flight
203
+ // generation using stale WalletConnect references will hang on signTypedData,
204
+ // permanently blocking future calls.
205
+ useEffect(() => {
206
+ isGeneratingRef.current = false;
207
+ }, [walletClient, connector]);
197
208
  // Clear wallet when address changes (e.g. user switched from MetaMask to Phantom)
198
209
  const prevAddressRef = useRef(normalizedAddress);
199
210
  useEffect(() => {
@@ -1,6 +1,5 @@
1
1
  import React from 'react';
2
2
  export type PlatformHealthStatus = 'ON' | 'ONLINE' | 'PAUSED' | 'DEPOSITS_PAUSED' | 'OFF' | 'MAINTENANCE' | 'UNKNOWN';
3
- export declare const MAX_DEPOSIT_USD = 25000;
4
3
  export type PlatformHealthContextType = {
5
4
  platformHealth: PlatformHealthStatus | null;
6
5
  maximumDepositUusdc: string | undefined;
@@ -14,6 +13,8 @@ export type PlatformHealthContextType = {
14
13
  storage: Record<string, string> | undefined;
15
14
  formDisabled: boolean;
16
15
  trackingDisabled: boolean;
16
+ /** Effective maximum deposit in USD (min of admin cap and platform API cap), or null if no cap. */
17
+ effectiveMaxDepositUsd: number | null;
17
18
  validateDepositAmount: (usdAmount: string | number) => {
18
19
  valid: boolean;
19
20
  error?: string;
@@ -43,13 +44,19 @@ export type PlatformHealthProviderProps = {
43
44
  * If not provided, trackingDisabled will always be false in the context
44
45
  */
45
46
  onTrackingDisabledChange?: (disabled: boolean) => void;
47
+ /**
48
+ * Optional maximum deposit amount in USD, typically fetched from admin dashboard settings.
49
+ * If not provided (undefined), no frontend cap is enforced — only the platform
50
+ * API's own maximumDepositUusdc limit applies.
51
+ */
52
+ maxDepositUsd?: number;
46
53
  };
47
54
  /**
48
55
  * Provider that monitors platform health from SilentSwapContext (which calls useStatus
49
56
  * once with proId) and provides validation functions for deposit amounts, output limits,
50
57
  * and service fee rates.
51
58
  */
52
- export declare function PlatformHealthProvider({ children, onFormDisabledChange, onTrackingDisabledChange, }: PlatformHealthProviderProps): import("react/jsx-runtime").JSX.Element;
59
+ export declare function PlatformHealthProvider({ children, onFormDisabledChange, onTrackingDisabledChange, maxDepositUsd, }: PlatformHealthProviderProps): import("react/jsx-runtime").JSX.Element;
53
60
  /**
54
61
  * Hook to access platform health context
55
62
  */
@@ -3,14 +3,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import React, { createContext, useContext, useEffect, useMemo, useCallback } from 'react';
4
4
  import { useSilentSwap } from '../contexts/SilentSwapContext.js';
5
5
  import { BigNumber } from 'bignumber.js';
6
- export const MAX_DEPOSIT_USD = 25000;
7
6
  const PlatformHealthContext = createContext(undefined);
8
7
  /**
9
8
  * Provider that monitors platform health from SilentSwapContext (which calls useStatus
10
9
  * once with proId) and provides validation functions for deposit amounts, output limits,
11
10
  * and service fee rates.
12
11
  */
13
- export function PlatformHealthProvider({ children, onFormDisabledChange, onTrackingDisabledChange, }) {
12
+ export function PlatformHealthProvider({ children, onFormDisabledChange, onTrackingDisabledChange, maxDepositUsd, }) {
14
13
  // Consume status data from parent SilentSwapContext instead of calling useStatus again
15
14
  const { status } = useSilentSwap();
16
15
  const { platformHealth: statusPlatformHealth, maximumDepositUusdc, minimumDepositUusdc, outputLimit, serviceFeeRate, overheadUsd, identity, volume, liquidity, storage, } = status;
@@ -98,6 +97,13 @@ export function PlatformHealthProvider({ children, onFormDisabledChange, onTrack
98
97
  return 0.01;
99
98
  return rate;
100
99
  }, [serviceFeeRate]);
100
+ // Compute effective max deposit: min of admin cap and platform API cap, or null if neither is set
101
+ const effectiveMaxDepositUsd = useMemo(() => {
102
+ const platformMax = getMaximumDepositUsd();
103
+ if (maxDepositUsd != null && platformMax != null)
104
+ return Math.min(maxDepositUsd, platformMax);
105
+ return maxDepositUsd ?? platformMax ?? null;
106
+ }, [maxDepositUsd, getMaximumDepositUsd]);
101
107
  // Validate deposit amount against min/max limits
102
108
  const validateDepositAmount = useCallback((usdAmount) => {
103
109
  const amount = typeof usdAmount === 'string' ? parseFloat(usdAmount) : usdAmount;
@@ -105,15 +111,14 @@ export function PlatformHealthProvider({ children, onFormDisabledChange, onTrack
105
111
  return { valid: false, error: 'Amount must be greater than zero' };
106
112
  }
107
113
  const minUsd = getMinimumDepositUsd();
108
- const maxUsd = Math.min(MAX_DEPOSIT_USD, getMaximumDepositUsd() ?? MAX_DEPOSIT_USD);
109
114
  if (minUsd !== null && amount < minUsd) {
110
115
  return { valid: false, error: `Minimum value is $${minUsd.toFixed(2)}` };
111
116
  }
112
- if (maxUsd !== null && amount > maxUsd) {
113
- return { valid: false, error: `Maximum value is $${maxUsd.toFixed(2)}` };
117
+ if (effectiveMaxDepositUsd !== null && amount > effectiveMaxDepositUsd) {
118
+ return { valid: false, error: `Maximum value is $${effectiveMaxDepositUsd.toFixed(2)}` };
114
119
  }
115
120
  return { valid: true };
116
- }, [getMinimumDepositUsd, getMaximumDepositUsd]);
121
+ }, [getMinimumDepositUsd, effectiveMaxDepositUsd]);
117
122
  // Validate output count against limit
118
123
  const validateOutputCount = useCallback((count) => {
119
124
  if (outputLimit === null || outputLimit === undefined) {
@@ -144,6 +149,7 @@ export function PlatformHealthProvider({ children, onFormDisabledChange, onTrack
144
149
  storage,
145
150
  formDisabled,
146
151
  trackingDisabled,
152
+ effectiveMaxDepositUsd,
147
153
  validateDepositAmount,
148
154
  validateOutputCount,
149
155
  getMinimumDepositUsd,
@@ -162,6 +168,7 @@ export function PlatformHealthProvider({ children, onFormDisabledChange, onTrack
162
168
  storage,
163
169
  formDisabled,
164
170
  trackingDisabled,
171
+ effectiveMaxDepositUsd,
165
172
  validateDepositAmount,
166
173
  validateOutputCount,
167
174
  getMinimumDepositUsd,
package/dist/index.d.ts CHANGED
@@ -29,7 +29,6 @@ export { useStatus } from './hooks/useStatus.js';
29
29
  export type { StatusResponse, UseStatusOptions } from './hooks/useStatus.js';
30
30
  export { PlatformHealthProvider, usePlatformHealthContext } from './hooks/usePlatformHealth.js';
31
31
  export type { PlatformHealthStatus, PlatformHealthContextType, PlatformHealthProviderProps, } from './hooks/usePlatformHealth.js';
32
- export { MAX_DEPOSIT_USD } from './hooks/usePlatformHealth.js';
33
32
  export { PlatformHealthMonitor, usePlatformHealthMonitor, } from './hooks/usePlatformHealthMonitor.js';
34
33
  export type { PlatformHealthMonitorProps } from './hooks/usePlatformHealthMonitor.js';
35
34
  export { useHiddenSwapFees } from './hooks/useHiddenSwapFees.js';
package/dist/index.js CHANGED
@@ -19,7 +19,6 @@ export { useSwap, getSourceAssetCaip19, X_RANGE_SLIDER_MIN_GAP, OUTPUT_LIMIT, DE
19
19
  export { useEgressEstimates } from './hooks/useEgressEstimates.js';
20
20
  export { useStatus } from './hooks/useStatus.js';
21
21
  export { PlatformHealthProvider, usePlatformHealthContext } from './hooks/usePlatformHealth.js';
22
- export { MAX_DEPOSIT_USD } from './hooks/usePlatformHealth.js';
23
22
  export { PlatformHealthMonitor, usePlatformHealthMonitor, } from './hooks/usePlatformHealthMonitor.js';
24
23
  export { useHiddenSwapFees } from './hooks/useHiddenSwapFees.js';
25
24
  export { useSlippageUsd } from './hooks/useSlippageUsd.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@silentswap/react",
3
3
  "type": "module",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -24,8 +24,8 @@
24
24
  "dependencies": {
25
25
  "@bigmi/core": "^0.6.5",
26
26
  "@ensdomains/ensjs": "^4.2.0",
27
- "@silentswap/sdk": "0.1.1",
28
- "@silentswap/ui-kit": "0.1.1",
27
+ "@silentswap/sdk": "0.1.3",
28
+ "@silentswap/ui-kit": "0.1.3",
29
29
  "@solana/codecs-strings": "^5.1.0",
30
30
  "@solana/kit": "^5.1.0",
31
31
  "@solana/rpc": "^5.1.0",