@silentswap/react 0.0.81 → 0.0.83

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.
@@ -11,6 +11,8 @@ export interface SwapFormEstimatesProviderProps {
11
11
  children: React.ReactNode;
12
12
  tokenIn: AssetInfo | null;
13
13
  fetchEstimates: (direction?: CalculationDirection) => Promise<void>;
14
+ /** Override service fee rate. Pass 0 for simple swaps (no service fee). */
15
+ serviceFeeRate?: number;
14
16
  }
15
- export declare function SwapFormEstimatesProvider({ children, tokenIn, fetchEstimates, }: SwapFormEstimatesProviderProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function SwapFormEstimatesProvider({ children, tokenIn, fetchEstimates, serviceFeeRate: serviceFeeRateOverride, }: SwapFormEstimatesProviderProps): import("react/jsx-runtime").JSX.Element;
16
18
  export declare function useSwapFormEstimatesContext(): SwapFormEstimatesContextType;
@@ -2,12 +2,12 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import React, { createContext, useContext, useCallback, useRef, useMemo, useEffect } from 'react';
4
4
  import { useSwap, X_RANGE_SLIDER_MIN_GAP } from '../hooks/useSwap.js';
5
- import { usePricesContext } from './PricesContext.js';
6
5
  import { usePlatformHealthContext } from '../hooks/usePlatformHealth.js';
6
+ import { usePricesContext } from './PricesContext.js';
7
7
  import { getAssetByCaip19, CALCULATION_DIRECTION_INPUT_TO_OUTPUT, CALCULATION_DIRECTION_OUTPUT_TO_INPUT, } from '@silentswap/sdk';
8
8
  const XT_DELAY_DEBOUNCE_QUOTE = 550;
9
9
  const SwapFormEstimatesContext = createContext(undefined);
10
- export function SwapFormEstimatesProvider({ children, tokenIn, fetchEstimates, }) {
10
+ export function SwapFormEstimatesProvider({ children, tokenIn, fetchEstimates, serviceFeeRate: serviceFeeRateOverride, }) {
11
11
  const setSplits = useSwap((state) => state.setSplits);
12
12
  const setInputAmount = useSwap((state) => state.setInputAmount);
13
13
  const inputAmount = useSwap((state) => state.inputAmount);
@@ -16,7 +16,8 @@ export function SwapFormEstimatesProvider({ children, tokenIn, fetchEstimates, }
16
16
  const isDraggingSlider = useSwap((state) => state.isDraggingSlider);
17
17
  const updateDestinationAmount = useSwap((state) => state.updateDestinationAmount);
18
18
  const { getPrice } = usePricesContext();
19
- const { getServiceFeeRate } = usePlatformHealthContext();
19
+ const { getServiceFeeRate: getPlatformServiceFeeRate } = usePlatformHealthContext();
20
+ const getServiceFeeRate = useCallback(() => (serviceFeeRateOverride !== undefined ? serviceFeeRateOverride : getPlatformServiceFeeRate()), [serviceFeeRateOverride, getPlatformServiceFeeRate]);
20
21
  const fetchEstimatesRef = useRef(fetchEstimates);
21
22
  const debounceTimerRef = useRef(null);
22
23
  const isRecalculatingInputRef = useRef(false);
@@ -133,7 +134,7 @@ export function SwapFormEstimatesProvider({ children, tokenIn, fetchEstimates, }
133
134
  if (tokenInPrice != null && tokenInPrice > 0) {
134
135
  // Calculate required input USD accounting for service fee
135
136
  // Formula: inputUsd = outputUsd / (1 - serviceFeeRate)
136
- const serviceFeeRate = getServiceFeeRate() ?? 0.01; // Default to 1% if not available
137
+ const serviceFeeRate = getServiceFeeRate();
137
138
  const requiredInputUsd = totalUsd / (1 - serviceFeeRate);
138
139
  const currentInputUsd = currentInputAmountNum * tokenInPrice;
139
140
  const USD_TOLERANCE = 0.01;
@@ -276,7 +277,7 @@ export function SwapFormEstimatesProvider({ children, tokenIn, fetchEstimates, }
276
277
  // Apply service fee to get actionable USD (amount available for outputs after fee deduction)
277
278
  // Formula: serviceFeeUsd = depositUsd * serviceFeeRate
278
279
  // actionableUsd = depositUsd - serviceFeeUsd
279
- const serviceFeeRate = getServiceFeeRate() ?? 0.01; // Default to 1% if not available
280
+ const serviceFeeRate = getServiceFeeRate();
280
281
  const serviceFeeUsd = totalUsdValue * serviceFeeRate;
281
282
  const actionableUsd = totalUsdValue - serviceFeeUsd;
282
283
  // Normalize splits for single destination - ensure it's always [1.0]
@@ -16,7 +16,7 @@ function buildFullState(key, status, isConnected, isLoading, error) {
16
16
  const output = outputsList[index];
17
17
  const outputAsset = output.output ??
18
18
  (output.asset
19
- ? { caip19: output.asset, amount: output.value || '0', decimals: 0 }
19
+ ? { caip19: output.asset, amount: output.value || '0' }
20
20
  : undefined);
21
21
  newOutputs.push({
22
22
  index,
@@ -47,7 +47,8 @@ export type OutputStatus = {
47
47
  asset?: {
48
48
  caip19: string;
49
49
  amount: string;
50
- decimals: number;
50
+ /** Omitted or 0 when unknown (e.g. INIT stage); resolve from asset registry in UI. */
51
+ decimals?: number;
51
52
  priceUsd?: number;
52
53
  };
53
54
  txs?: OrderTransactions;
@@ -3,6 +3,8 @@ import type { Connector } from 'wagmi';
3
3
  import type { DepositParams } from '@silentswap/sdk';
4
4
  import type { SolanaWalletConnector, SolanaConnection } from './solana-transaction.js';
5
5
  import type { BitcoinWalletConnector, BitcoinConnection } from './bitcoin-transaction.js';
6
+ /** Delay after approve tx so RPC/nodes confirm before next tx (avoids MetaMask RPC errors) */
7
+ export declare const APPROVE_POST_DELAY_MS = 7000;
6
8
  /**
7
9
  * Result from executing a bridge transaction
8
10
  */
@@ -5,6 +5,8 @@ import { createSolanaTransactionExecutor, convertRelaySolanaStepToTransaction }
5
5
  import { createBitcoinTransactionExecutor, convertRelayBitcoinStepToTransaction } from './bitcoin-transaction.js';
6
6
  const S0X_ADDR_EVM_ZERO = '0x0000000000000000000000000000000000000000';
7
7
  const XG_UINT256_MAX = (1n << 256n) - 1n;
8
+ /** Delay after approve tx so RPC/nodes confirm before next tx (avoids MetaMask RPC errors) */
9
+ export const APPROVE_POST_DELAY_MS = 7000;
8
10
  /**
9
11
  * Get normalized wallet account address from wallet client
10
12
  * This ensures the address matches the account that will send transactions
@@ -802,6 +804,11 @@ setCurrentStep, onStatus) {
802
804
  status: receipt.status,
803
805
  });
804
806
  }
807
+ if (step.id === 'approve') {
808
+ setCurrentStep('Waiting for network...');
809
+ onStatus?.('Waiting for network...');
810
+ await new Promise((resolve) => setTimeout(resolve, APPROVE_POST_DELAY_MS));
811
+ }
805
812
  }
806
813
  }
807
814
  // Find request ID for status monitoring
@@ -896,6 +903,9 @@ walletClient, connector, setCurrentStep, onStatus) {
896
903
  chain: null,
897
904
  });
898
905
  await waitForTransactionConfirmation(hash, publicClient);
906
+ setCurrentStep('Waiting for network...');
907
+ onStatus?.('Waiting for network...');
908
+ await new Promise((resolve) => setTimeout(resolve, APPROVE_POST_DELAY_MS));
899
909
  }
900
910
  retryCount++;
901
911
  continue;
@@ -3,7 +3,7 @@ import { hexToBase58, isSolanaAsset, isBitcoinAsset, parseEvmCaip19, S_CAIP19_US
3
3
  import { getAddress } from 'viem';
4
4
  import { useQuoteFetching } from './useQuoteFetching.js';
5
5
  import { useOrderSigning } from './useOrderSigning.js';
6
- import { useBridgeExecution } from './useBridgeExecution.js';
6
+ import { APPROVE_POST_DELAY_MS, useBridgeExecution } from './useBridgeExecution.js';
7
7
  import { useTransaction } from '../useTransaction.js';
8
8
  import { useQuoteCalculation } from './useQuoteCalculation.js';
9
9
  export function useSilentQuote({ client, address, evmAddress, solAddress, walletClient, connector, wallet, walletLoading = false, walletError = null, generateWallet, requestWalletConnect, onStatus, solanaConnector, solanaConnection, solanaRpcUrl, bitcoinConnector, bitcoinConnection, getPrice, setDestinations, proId, }) {
@@ -281,6 +281,9 @@ export function useSilentQuote({ client, address, evmAddress, solAddress, wallet
281
281
  const sourceChainId = parseInt(sourceAssetMatch[1]);
282
282
  const tokenAddress = sourceAssetMatch[2];
283
283
  await approveTokenSpending(sourceChainId, tokenAddress, effectiveAllowanceTarget, sourceAmountInUnits, evmSignerAddress);
284
+ // Delay after approve so RPC/nodes confirm approval before next tx (avoids MetaMask RPC errors)
285
+ setCurrentStep('Waiting for network...');
286
+ await new Promise((resolve) => setTimeout(resolve, APPROVE_POST_DELAY_MS));
284
287
  }
285
288
  }
286
289
  // Handle EVM bridge swaps
@@ -23,7 +23,7 @@ export type PlatformHealthContextType = {
23
23
  };
24
24
  getMinimumDepositUsd: () => number | null;
25
25
  getMaximumDepositUsd: () => number | null;
26
- getServiceFeeRate: () => number | null;
26
+ getServiceFeeRate: () => number;
27
27
  };
28
28
  export type PlatformHealthProviderProps = {
29
29
  children: React.ReactNode;
@@ -89,12 +89,12 @@ export function PlatformHealthProvider({ children, proId, onFormDisabledChange,
89
89
  }, [maximumDepositUusdc]);
90
90
  const getServiceFeeRate = useCallback(() => {
91
91
  if (!serviceFeeRate)
92
- return null;
92
+ return 0.01;
93
93
  try {
94
- return +serviceFeeRate;
94
+ return +serviceFeeRate || 0.01;
95
95
  }
96
96
  catch {
97
- return null;
97
+ return 0.01;
98
98
  }
99
99
  }, [serviceFeeRate]);
100
100
  // Validate deposit amount against min/max limits
@@ -41,9 +41,11 @@ export interface useQuoteReturn {
41
41
  * @param dstToken - Destination token address (0x0 for native)
42
42
  * @param recipientAddress - Optional recipient address (required for Solana destinations)
43
43
  * @param sourceAddress - Optional source chain address (for relay.link, must match source chain format)
44
+ * @param tradeType - 'EXACT_INPUT' (default) or 'EXACT_OUTPUT'
45
+ * @param dstAmount - Destination amount in token units (required for EXACT_OUTPUT)
44
46
  * @returns Promise resolving to best quote with all details
45
47
  */
46
- getQuote: (srcChainId: number, srcToken: string, srcAmount: string, dstChainId: number, dstToken: string, recipientAddress?: string, sourceAddress?: string) => Promise<BridgeQuoteResult>;
48
+ getQuote: (srcChainId: number, srcToken: string, srcAmount: string, dstChainId: number, dstToken: string, recipientAddress?: string, sourceAddress?: string, tradeType?: 'EXACT_INPUT' | 'EXACT_OUTPUT', dstAmount?: string) => Promise<BridgeQuoteResult>;
47
49
  /**
48
50
  * Get live estimate for a given direction
49
51
  * @param direction - 'ingress' (into Silent) or 'egress' (out of Silent)
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import BigNumber from 'bignumber.js';
3
- import { NI_CHAIN_ID_AVALANCHE, S0X_ADDR_USDC_AVALANCHE, S_CAIP19_USDC_AVALANCHE, X_MAX_IMPACT_PERCENT, getBridgeQuote as sdkGetBridgeQuote, interpolateSamples as sdkInterpolateSamples, fetchRelayQuote, fetchDebridgeOrder, normalizeAddress, getAssetByCaip19, N_DEBRIDGE_CHAIN_ID_SOLANA, N_RELAY_CHAIN_ID_SOLANA, SB58_ADDR_SOL_PROGRAM_SYSTEM, S0X_ADDR_EVM_RELAY_LINK_DEAD, SB58_ADDR_SOL_RELAY_LINK_RECIPIENT, isSolanaAsset, isBitcoinAsset, EVM_PHONY_ADDRESS, } from '@silentswap/sdk';
3
+ import { NI_CHAIN_ID_AVALANCHE, S0X_ADDR_USDC_AVALANCHE, S_CAIP19_USDC_AVALANCHE, X_MAX_IMPACT_PERCENT, getBridgeQuote, interpolateSamples as sdkInterpolateSamples, fetchRelayQuote, fetchDebridgeOrder, normalizeAddress, getAssetByCaip19, N_DEBRIDGE_CHAIN_ID_SOLANA, N_RELAY_CHAIN_ID_SOLANA, SB58_ADDR_SOL_PROGRAM_SYSTEM, S0X_ADDR_EVM_RELAY_LINK_DEAD, SB58_ADDR_SOL_RELAY_LINK_RECIPIENT, isSolanaAsset, isBitcoinAsset, EVM_PHONY_ADDRESS, } from '@silentswap/sdk';
4
4
  // Constants for estimateLive
5
5
  const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
6
6
  /**
@@ -43,7 +43,7 @@ export function useQuote({ address, maxImpactPercent = X_MAX_IMPACT_PERCENT, for
43
43
  /**
44
44
  * Get quote for cross-chain swap
45
45
  */
46
- const getQuote = useCallback(async (srcChainId, srcToken, srcAmount, dstChainId, dstToken, recipientAddress, sourceAddress) => {
46
+ const getQuote = useCallback(async (srcChainId, srcToken, srcAmount, dstChainId, dstToken, recipientAddress, sourceAddress, tradeType = 'EXACT_INPUT', dstAmount) => {
47
47
  console.log('[Quote] Step 1: Starting getQuote', {
48
48
  srcChainId,
49
49
  srcToken,
@@ -52,6 +52,8 @@ export function useQuote({ address, maxImpactPercent = X_MAX_IMPACT_PERCENT, for
52
52
  dstToken,
53
53
  hasRecipientAddress: !!recipientAddress,
54
54
  hasSourceAddress: !!sourceAddress,
55
+ tradeType,
56
+ dstAmount,
55
57
  });
56
58
  if (!normalizedAddress) {
57
59
  throw new Error('Address required for bridge quote operations');
@@ -70,9 +72,10 @@ export function useQuote({ address, maxImpactPercent = X_MAX_IMPACT_PERCENT, for
70
72
  // normalizedAddress can be EVM (0x...) or Solana (base58) address
71
73
  // sourceAddress is the address format for the source chain (for relay.link)
72
74
  console.log('[Quote] Step 3: Calling sdkGetBridgeQuote');
73
- const result = await sdkGetBridgeQuote(srcChainId, srcTokenNorm, srcAmount, dstChainId, dstTokenNorm, normalizedAddress, signal, recipientAddress, // Pass recipient address for Solana destinations
75
+ const result = await getBridgeQuote(srcChainId, srcTokenNorm, srcAmount, dstChainId, dstTokenNorm, normalizedAddress, signal, recipientAddress, // Pass recipient address for Solana destinations
74
76
  sourceAddress, // Pass source address for relay.link (must match source chain format)
75
- forceProvider);
77
+ forceProvider, // Optional: use only this provider for testing
78
+ tradeType, dstAmount);
76
79
  console.log('[Quote] Step 4: getQuote completed successfully', {
77
80
  provider: result.provider,
78
81
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@silentswap/react",
3
3
  "type": "module",
4
- "version": "0.0.81",
4
+ "version": "0.0.83",
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.0.81",
28
- "@silentswap/ui-kit": "0.0.81",
27
+ "@silentswap/sdk": "0.0.83",
28
+ "@silentswap/ui-kit": "0.0.83",
29
29
  "@solana/codecs-strings": "^5.1.0",
30
30
  "@solana/kit": "^5.1.0",
31
31
  "@solana/rpc": "^5.1.0",