@silentswap/vue 0.0.41

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/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # @silentswap/vue
2
+
3
+ Vue 3 composables for integrating with SilentSwap SDK.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @silentswap/vue
9
+ # or
10
+ bun add @silentswap/vue
11
+ # or
12
+ yarn add @silentswap/vue
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### getSilentSwapClient
18
+
19
+ Create and manage a SilentSwap client instance:
20
+
21
+ ```typescript
22
+ import { getSilentSwapClient } from '@silentswap/vue';
23
+
24
+ const { client } = getSilentSwapClient({
25
+ config: {
26
+ baseUrl: 'https://api.silentswap.com',
27
+ },
28
+ });
29
+ ```
30
+
31
+ ### getSilentSwapAuth
32
+
33
+ Handle authentication utilities:
34
+
35
+ ```typescript
36
+ import { getSilentSwapAuth } from '@silentswap/vue';
37
+
38
+ const {
39
+ createSignInMessage,
40
+ createEip712DocForOrder,
41
+ createEip712DocForWalletGeneration,
42
+ } = getSilentSwapAuth();
43
+
44
+ // Create a sign-in message
45
+ const message = createSignInMessage('0x...', 'nonce123', 'app.silentswap.com');
46
+
47
+ // Create EIP-712 document for order
48
+ const orderDoc = createEip712DocForOrder(quoteResponse);
49
+
50
+ // Create EIP-712 document for wallet generation
51
+ const walletDoc = createEip712DocForWalletGeneration('scope', 'token');
52
+ ```
53
+
54
+ ### getSilentSwapOrders
55
+
56
+ Manage orders and related operations with reactive state:
57
+
58
+ ```typescript
59
+ import { getSilentSwapOrders } from '@silentswap/vue';
60
+
61
+ const { client } = getSilentSwapClient({ config: yourConfig });
62
+
63
+ const {
64
+ isLoading,
65
+ error,
66
+ getNonce,
67
+ authenticate,
68
+ getQuote,
69
+ createOrder,
70
+ } = getSilentSwapOrders({ client });
71
+
72
+ // Get nonce for authentication
73
+ const nonce = await getNonce('0x...');
74
+
75
+ // Authenticate user
76
+ const authResult = await authenticate({
77
+ address: '0x...',
78
+ signature: '0x...',
79
+ message: '...',
80
+ });
81
+
82
+ // Get a quote
83
+ const quote = await getQuote({
84
+ sellToken: '0x...',
85
+ buyToken: '0x...',
86
+ sellAmount: '1000000000000000000',
87
+ });
88
+
89
+ // Create an order
90
+ const order = await createOrder({
91
+ quoteId: quote.quoteId,
92
+ signature: '0x...',
93
+ });
94
+ ```
95
+
96
+ ## API Reference
97
+
98
+ ### getSilentSwapClient
99
+
100
+ #### Parameters
101
+
102
+ - `config: SilentSwapClientConfig` - Configuration for the SilentSwap client
103
+
104
+ #### Returns
105
+
106
+ - `client: SilentSwapClient` - The SilentSwap client instance
107
+
108
+ ### getSilentSwapAuth
109
+
110
+ #### Returns
111
+
112
+ - `createSignInMessage(address, nonce, domain?)` - Create a sign-in message
113
+ - `createEip712DocForOrder(quoteResponse)` - Create EIP-712 typed data for order
114
+ - `createEip712DocForWalletGeneration(scope, token)` - Create EIP-712 typed data for wallet generation
115
+
116
+ ### getSilentSwapOrders
117
+
118
+ #### Parameters
119
+
120
+ - `client: SilentSwapClient` - The SilentSwap client instance
121
+
122
+ #### Returns
123
+
124
+ - `isLoading: boolean` - Reactive loading state
125
+ - `error: Error | null` - Reactive error state
126
+ - `getNonce(address)` - Get nonce for authentication
127
+ - `authenticate(auth)` - Authenticate user
128
+ - `getQuote(quote)` - Get a quote
129
+ - `createOrder(order)` - Create an order
130
+
131
+ ## Requirements
132
+
133
+ - Vue 3.0+
134
+ - TypeScript (recommended)
@@ -0,0 +1,44 @@
1
+ import { ref } from 'vue';
2
+ import type { Hex } from 'viem';
3
+ import type { BridgeProvider, RelayQuoteResponse, DeBridgeOrderResponse, SolveUsdcResult, EstimateSample, Estimate, BridgeQuoteResult } from '@silentswap/sdk';
4
+ export interface GetQuoteOptions {
5
+ /** User's EVM address */
6
+ address?: `0x${string}`;
7
+ /** Depositor address for bridge operations (required for solveUsdcAmount) */
8
+ depositorAddress: Hex;
9
+ /** Maximum price impact percentage allowed (default from SDK constants) */
10
+ maxImpactPercent?: number;
11
+ /** Timeout for quote requests in milliseconds (default: 30000) */
12
+ quoteTimeout?: number;
13
+ }
14
+ export type { RelayQuoteResponse, DeBridgeOrderResponse, SolveUsdcResult, EstimateSample, Estimate, BridgeQuoteResult, } from '@silentswap/sdk';
15
+ export type SolveResult = SolveUsdcResult;
16
+ /**
17
+ * Estimate result with quote details
18
+ */
19
+ export interface EstimateResult {
20
+ /** Estimate data with samples */
21
+ estimate: Estimate;
22
+ /** Amount of gas needed on source chain */
23
+ gasAmount: bigint;
24
+ /** Raw response from provider */
25
+ response: RelayQuoteResponse | DeBridgeOrderResponse;
26
+ /** Bridge provider used */
27
+ provider: BridgeProvider;
28
+ /** Calculated input amount for reverse calculation (output-to-input) */
29
+ calculatedInputAmount?: number;
30
+ }
31
+ export interface GetQuoteReturn {
32
+ isLoading: Readonly<ReturnType<typeof ref<boolean>>>;
33
+ error: Readonly<ReturnType<typeof ref<Error | null>>>;
34
+ ingressEstimates: Readonly<ReturnType<typeof ref<Record<string, Estimate>>>>;
35
+ egressEstimates: Readonly<ReturnType<typeof ref<Record<string, Estimate>>>>;
36
+ getQuote: (srcChainId: number, srcToken: string, srcAmount: string, dstChainId: number, dstToken: string) => Promise<BridgeQuoteResult>;
37
+ solveUsdcAmount: (srcChainId: number, srcToken: string, srcAmount: string, depositCalldata?: string) => Promise<SolveResult>;
38
+ estimateLive: (direction: 'ingress' | 'egress', assetCaip19: string, chainId: number | string, tokenAddress: string, amount: number, usdPrice: number, externalSignal?: AbortSignal, recipientAddress?: string, isReverseCalculation?: boolean, targetAmount?: number) => Promise<EstimateResult>;
39
+ interpolateSamples: (samples: EstimateSample[], usdValue: number, marginHi?: number) => number;
40
+ }
41
+ /**
42
+ * Vue composable for getting optimized bridge quotes from multiple providers
43
+ */
44
+ export declare function getQuote({ address, depositorAddress, maxImpactPercent, quoteTimeout, }: GetQuoteOptions): GetQuoteReturn;
@@ -0,0 +1,405 @@
1
+ import { ref, computed, onUnmounted } from 'vue';
2
+ import BigNumber from 'bignumber.js';
3
+ import { NI_CHAIN_ID_AVALANCHE, S0X_ADDR_USDC_AVALANCHE, S_CAIP19_USDC_AVALANCHE, X_MAX_IMPACT_PERCENT, solveOptimalUsdcAmount, 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, } from '@silentswap/sdk';
4
+ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
5
+ const EVM_PHONY_ADDRESS_FALLBACK = '0x1111111111111111111111111111111111111111';
6
+ /**
7
+ * Vue composable for getting optimized bridge quotes from multiple providers
8
+ */
9
+ export function getQuote({ address, depositorAddress, maxImpactPercent = X_MAX_IMPACT_PERCENT, quoteTimeout = 30000, }) {
10
+ const isLoading = ref(false);
11
+ const error = ref(null);
12
+ // Estimates cache
13
+ const ingressEstimates = ref({});
14
+ const egressEstimates = ref({});
15
+ // Normalize address - handle both EVM (hex) and Solana (base58) addresses
16
+ const normalizedAddress = computed(() => address ? normalizeAddress(address) : null);
17
+ // Abort controller for cancelling requests
18
+ const abortControllerRef = ref(null);
19
+ // Cleanup on unmount
20
+ onUnmounted(() => {
21
+ if (abortControllerRef.value) {
22
+ abortControllerRef.value.abort();
23
+ abortControllerRef.value = null;
24
+ }
25
+ });
26
+ /**
27
+ * Cancel any ongoing requests and create a new abort controller
28
+ */
29
+ const createNewAbortController = () => {
30
+ // Cancel previous request if any
31
+ if (abortControllerRef.value) {
32
+ abortControllerRef.value.abort();
33
+ }
34
+ // Create new abort controller
35
+ const controller = new AbortController();
36
+ abortControllerRef.value = controller;
37
+ return controller;
38
+ };
39
+ /**
40
+ * Get quote for cross-chain swap
41
+ */
42
+ const getQuoteFn = async (srcChainId, srcToken, srcAmount, dstChainId, dstToken) => {
43
+ if (!normalizedAddress.value) {
44
+ throw new Error('Address required for bridge quote operations');
45
+ }
46
+ isLoading.value = true;
47
+ error.value = null;
48
+ try {
49
+ // Normalize addresses
50
+ const srcTokenNorm = srcToken === '0x0' || srcToken === '0x0000000000000000000000000000000000000000'
51
+ ? ZERO_ADDRESS
52
+ : srcToken;
53
+ const dstTokenNorm = dstToken === '0x0' || dstToken === '0x0000000000000000000000000000000000000000'
54
+ ? ZERO_ADDRESS
55
+ : dstToken;
56
+ // Create abort controller for timeout
57
+ const controller = new AbortController();
58
+ const timeoutId = setTimeout(() => controller.abort(), quoteTimeout);
59
+ try {
60
+ // Use shared quote fetching function
61
+ const result = await sdkGetBridgeQuote(srcChainId, srcTokenNorm, srcAmount, dstChainId, dstTokenNorm, normalizedAddress.value, controller.signal);
62
+ clearTimeout(timeoutId);
63
+ return result;
64
+ }
65
+ catch (err) {
66
+ clearTimeout(timeoutId);
67
+ throw err;
68
+ }
69
+ }
70
+ catch (err) {
71
+ const errorInstance = err instanceof Error ? err : new Error('Failed to get quote');
72
+ error.value = errorInstance;
73
+ throw errorInstance;
74
+ }
75
+ finally {
76
+ isLoading.value = false;
77
+ }
78
+ };
79
+ /**
80
+ * Solve for optimal USDC amount
81
+ */
82
+ const solveUsdcAmountFn = async (srcChainId, srcToken, srcAmount, depositCalldata) => {
83
+ isLoading.value = true;
84
+ error.value = null;
85
+ try {
86
+ if (!normalizedAddress.value) {
87
+ throw new Error('Address required for solving USDC amount');
88
+ }
89
+ const result = await solveOptimalUsdcAmount(srcChainId, srcToken, srcAmount, normalizedAddress.value, depositCalldata, maxImpactPercent, depositorAddress);
90
+ return result;
91
+ }
92
+ catch (err) {
93
+ const errorInstance = err instanceof Error ? err : new Error('Failed to solve USDC amount');
94
+ error.value = errorInstance;
95
+ throw errorInstance;
96
+ }
97
+ finally {
98
+ isLoading.value = false;
99
+ }
100
+ };
101
+ /**
102
+ * Get live estimate for a given asset and direction
103
+ */
104
+ const estimateLiveFn = async (direction, assetCaip19, chainId, tokenAddress, amount, usdPrice, externalSignal, recipientAddress, isReverseCalculation, targetAmount) => {
105
+ // Determine if this is a Solana chain by checking the asset CAIP-19
106
+ const isSolanaChain = isSolanaAsset(assetCaip19);
107
+ let numericChainId;
108
+ if (isSolanaChain) {
109
+ numericChainId = 0; // Placeholder, will be set per provider
110
+ }
111
+ else {
112
+ if (typeof chainId === 'string') {
113
+ const parsed = parseInt(chainId, 10);
114
+ if (isNaN(parsed)) {
115
+ throw new Error(`Invalid chain ID: ${chainId}`);
116
+ }
117
+ numericChainId = parsed;
118
+ }
119
+ else {
120
+ numericChainId = chainId;
121
+ }
122
+ }
123
+ if (!normalizedAddress.value) {
124
+ throw new Error('Address required');
125
+ }
126
+ // Use external signal if provided, otherwise create new abort controller
127
+ let signal;
128
+ if (externalSignal) {
129
+ signal = externalSignal;
130
+ }
131
+ else {
132
+ const controller = createNewAbortController();
133
+ signal = controller.signal;
134
+ }
135
+ try {
136
+ // Special case for USDC on Avalanche
137
+ const checkChainId = isSolanaChain ? 0 : numericChainId;
138
+ if (checkChainId === NI_CHAIN_ID_AVALANCHE &&
139
+ tokenAddress.toLowerCase() === S0X_ADDR_USDC_AVALANCHE.toLowerCase()) {
140
+ const estimate = {
141
+ gasFee: 0,
142
+ samples: [
143
+ {
144
+ baseline: Infinity,
145
+ retention: 1,
146
+ source: 'none',
147
+ },
148
+ ],
149
+ };
150
+ if (direction === 'ingress') {
151
+ ingressEstimates.value[assetCaip19] = estimate;
152
+ }
153
+ else {
154
+ egressEstimates.value[assetCaip19] = estimate;
155
+ }
156
+ return {
157
+ estimate,
158
+ gasAmount: BigInt(BigNumber(300_000).times(2).shiftedBy(9).toFixed(0)),
159
+ response: {},
160
+ provider: 'relay',
161
+ };
162
+ }
163
+ // Get asset info to determine correct decimals
164
+ const assetInfo = getAssetByCaip19(assetCaip19);
165
+ if (!assetInfo) {
166
+ throw new Error(`Asset not found: ${assetCaip19}`);
167
+ }
168
+ // Calculate amount in token units
169
+ const decimalsToUse = (direction === 'egress' && !isReverseCalculation)
170
+ ? 6 // USDC has 6 decimals (for normal egress)
171
+ : assetInfo.decimals; // Use asset decimals (for ingress or reverse egress)
172
+ const tokenAmount = BigNumber(amount).shiftedBy(decimalsToUse).toFixed(0);
173
+ // Determine user address based on direction and chain
174
+ let userAddress;
175
+ if (direction === 'ingress') {
176
+ if (isSolanaChain) {
177
+ userAddress = normalizedAddress.value && !normalizedAddress.value.startsWith('0x')
178
+ ? normalizedAddress.value
179
+ : SB58_ADDR_SOL_PROGRAM_SYSTEM;
180
+ }
181
+ else {
182
+ userAddress = normalizedAddress.value && normalizedAddress.value.startsWith('0x')
183
+ ? normalizedAddress.value
184
+ : EVM_PHONY_ADDRESS_FALLBACK;
185
+ }
186
+ }
187
+ else {
188
+ userAddress = normalizedAddress.value && normalizedAddress.value.startsWith('0x')
189
+ ? normalizedAddress.value
190
+ : EVM_PHONY_ADDRESS_FALLBACK;
191
+ }
192
+ // For Solana, use different chain IDs for different providers
193
+ const relayChainId = isSolanaChain ? N_RELAY_CHAIN_ID_SOLANA : numericChainId;
194
+ const debridgeChainId = isSolanaChain ? N_DEBRIDGE_CHAIN_ID_SOLANA : numericChainId;
195
+ // Fetch quotes from both providers
196
+ const [relayResult, debridgeResult] = await Promise.allSettled([
197
+ (async () => {
198
+ try {
199
+ const tradeType = isReverseCalculation ? 'EXACT_OUTPUT' : 'EXACT_INPUT';
200
+ let quoteAmount = tokenAmount;
201
+ if (isReverseCalculation && direction === 'ingress' && targetAmount) {
202
+ const usdcAsset = getAssetByCaip19(S_CAIP19_USDC_AVALANCHE);
203
+ if (usdcAsset) {
204
+ quoteAmount = BigNumber(targetAmount).shiftedBy(usdcAsset.decimals).toFixed(0);
205
+ }
206
+ else {
207
+ throw new Error('USDC asset not found');
208
+ }
209
+ }
210
+ else if (isReverseCalculation && direction === 'egress' && targetAmount) {
211
+ quoteAmount = tokenAmount;
212
+ }
213
+ const quote = await fetchRelayQuote({
214
+ user: userAddress,
215
+ referrer: 'silentswap',
216
+ tradeType,
217
+ ...(direction === 'ingress'
218
+ ? {
219
+ originChainId: relayChainId,
220
+ destinationChainId: NI_CHAIN_ID_AVALANCHE,
221
+ originCurrency: tokenAddress === ZERO_ADDRESS ? ZERO_ADDRESS : tokenAddress,
222
+ destinationCurrency: S0X_ADDR_USDC_AVALANCHE,
223
+ ...(isReverseCalculation && tradeType === 'EXACT_OUTPUT'
224
+ ? { amount: quoteAmount }
225
+ : { amount: tokenAmount }),
226
+ ...(isSolanaChain
227
+ ? { recipient: EVM_PHONY_ADDRESS_FALLBACK }
228
+ : {}),
229
+ }
230
+ : {
231
+ originChainId: NI_CHAIN_ID_AVALANCHE,
232
+ originCurrency: S0X_ADDR_USDC_AVALANCHE,
233
+ destinationChainId: relayChainId,
234
+ destinationCurrency: tokenAddress === ZERO_ADDRESS ? ZERO_ADDRESS : tokenAddress,
235
+ ...(isReverseCalculation && tradeType === 'EXACT_OUTPUT'
236
+ ? { amount: quoteAmount }
237
+ : { amount: tokenAmount }),
238
+ ...(isSolanaChain
239
+ ? {
240
+ user: S0X_ADDR_EVM_RELAY_LINK_DEAD,
241
+ recipient: recipientAddress || SB58_ADDR_SOL_RELAY_LINK_RECIPIENT,
242
+ }
243
+ : {}),
244
+ }),
245
+ }, signal);
246
+ return quote;
247
+ }
248
+ catch (err) {
249
+ if (err instanceof Error && err.name !== 'AbortError') {
250
+ console.warn('Relay quote failed:', err);
251
+ }
252
+ return null;
253
+ }
254
+ })(),
255
+ (async () => {
256
+ try {
257
+ if (direction === 'egress') {
258
+ return null;
259
+ }
260
+ const debridgeParams = {
261
+ srcChainId: debridgeChainId,
262
+ srcChainTokenIn: tokenAddress === ZERO_ADDRESS ? ZERO_ADDRESS : tokenAddress,
263
+ dstChainId: NI_CHAIN_ID_AVALANCHE,
264
+ dstChainTokenOut: S0X_ADDR_USDC_AVALANCHE,
265
+ };
266
+ if (isReverseCalculation && direction === 'ingress' && targetAmount) {
267
+ const usdcAsset = getAssetByCaip19(S_CAIP19_USDC_AVALANCHE);
268
+ if (usdcAsset) {
269
+ debridgeParams.srcChainTokenInAmount = 'auto';
270
+ debridgeParams.dstChainTokenOutAmount = BigNumber(targetAmount)
271
+ .shiftedBy(usdcAsset.decimals)
272
+ .toFixed(0);
273
+ }
274
+ else {
275
+ throw new Error('USDC asset not found');
276
+ }
277
+ }
278
+ else {
279
+ debridgeParams.srcChainTokenInAmount = tokenAmount;
280
+ debridgeParams.dstChainTokenOutAmount = 'auto';
281
+ }
282
+ const quote = await fetchDebridgeOrder(debridgeParams, signal);
283
+ return quote;
284
+ }
285
+ catch (err) {
286
+ if (err instanceof Error && err.name !== 'AbortError') {
287
+ console.warn('DeBridge quote failed:', err);
288
+ }
289
+ return null;
290
+ }
291
+ })(),
292
+ ]);
293
+ const relayQuote = relayResult.status === 'fulfilled' ? relayResult.value : null;
294
+ const debridgeQuote = debridgeResult.status === 'fulfilled' ? debridgeResult.value : null;
295
+ if (!relayQuote && !debridgeQuote) {
296
+ throw new AggregateError([
297
+ relayResult.status === 'rejected' ? relayResult.reason : null,
298
+ debridgeResult.status === 'rejected' ? debridgeResult.reason : null,
299
+ ].filter(Boolean), 'All quote providers failed');
300
+ }
301
+ // Calculate retention rates
302
+ let retentionRelay = 0;
303
+ let retentionDebridge = 0;
304
+ let gasAmountRelay = 0n;
305
+ let gasAmountDebridge = 0n;
306
+ let gasFeeUsd = 0;
307
+ let calculatedInputAmount = undefined;
308
+ if (relayQuote) {
309
+ if (isReverseCalculation && direction === 'ingress') {
310
+ calculatedInputAmount = Number(relayQuote.details.currencyIn.amount);
311
+ if (!calculatedInputAmount) {
312
+ const calculatedInputAmountUsd = Number(relayQuote.details.currencyIn.amountUsd || 0);
313
+ if (calculatedInputAmountUsd > 0 && usdPrice > 0) {
314
+ calculatedInputAmount = calculatedInputAmountUsd / usdPrice;
315
+ }
316
+ }
317
+ retentionRelay = BigNumber(relayQuote.details.currencyOut.amountUsd)
318
+ .div(relayQuote.details.currencyIn.amountUsd)
319
+ .toNumber();
320
+ }
321
+ else {
322
+ retentionRelay = BigNumber(relayQuote.details.currencyOut.amountUsd)
323
+ .div(relayQuote.details.currencyIn.amountUsd)
324
+ .toNumber();
325
+ }
326
+ gasFeeUsd = Number(relayQuote.fees.relayerGas.amountUsd || '0');
327
+ gasAmountRelay = BigInt((BigInt(relayQuote.fees.gas.amount || 0) * 15n) / 10n);
328
+ }
329
+ if (debridgeQuote) {
330
+ if (isReverseCalculation && direction === 'ingress') {
331
+ calculatedInputAmount = Number(debridgeQuote.estimation.srcChainTokenIn.amount);
332
+ if (!calculatedInputAmount) {
333
+ const calculatedInputAmountUsd = Number(debridgeQuote.estimation.srcChainTokenIn.approximateUsdValue || 0);
334
+ if (calculatedInputAmountUsd > 0 && usdPrice > 0) {
335
+ calculatedInputAmount = calculatedInputAmountUsd / usdPrice;
336
+ }
337
+ }
338
+ retentionDebridge = BigNumber(debridgeQuote.estimation.dstChainTokenOut.approximateUsdValue)
339
+ .div(debridgeQuote.estimation.srcChainTokenIn.approximateUsdValue)
340
+ .toNumber();
341
+ }
342
+ else {
343
+ retentionDebridge = BigNumber(debridgeQuote.estimation.dstChainTokenOut.approximateUsdValue)
344
+ .div(debridgeQuote.estimation.srcChainTokenIn.approximateUsdValue)
345
+ .toNumber();
346
+ }
347
+ gasFeeUsd =
348
+ debridgeQuote.estimation.costDetails?.reduce((sum, detail) => sum + Number(detail.payload?.feeApproximateUsdValue || 0), 0) || 0;
349
+ gasAmountDebridge = BigInt((BigInt(debridgeQuote.estimatedTransactionFee?.total || 0) * 15n) / 10n);
350
+ }
351
+ // Determine best source
352
+ const retention = Math.max(retentionRelay, retentionDebridge);
353
+ const source = retentionRelay >= retentionDebridge ? 'relay' : 'debridge';
354
+ const usdValue = BigNumber(usdPrice).times(amount).toNumber();
355
+ // Create sample
356
+ const sample = {
357
+ baseline: usdValue,
358
+ retention,
359
+ source,
360
+ };
361
+ // Update estimates cache
362
+ const estimatesCache = direction === 'ingress' ? ingressEstimates.value : egressEstimates.value;
363
+ const existingEstimate = estimatesCache[assetCaip19];
364
+ // Filter out samples within $5 of this price
365
+ const filteredSamples = (existingEstimate?.samples || []).filter((s) => !(s.baseline >= sample.baseline - 5 && s.baseline <= sample.baseline + 5));
366
+ // Add new sample and sort
367
+ const samples = [...filteredSamples, sample].sort((a, b) => a.baseline - b.baseline);
368
+ const estimate = {
369
+ gasFee: direction === 'ingress' ? 0 : gasFeeUsd,
370
+ samples,
371
+ };
372
+ estimatesCache[assetCaip19] = estimate;
373
+ return {
374
+ estimate,
375
+ gasAmount: source === 'relay' ? gasAmountRelay : gasAmountDebridge,
376
+ response: (source === 'relay' ? relayQuote : debridgeQuote),
377
+ provider: source,
378
+ calculatedInputAmount,
379
+ };
380
+ }
381
+ catch (err) {
382
+ if (err instanceof Error && err.name === 'AbortError') {
383
+ throw err;
384
+ }
385
+ const errorInstance = err instanceof Error ? err : new Error('Failed to get live estimate');
386
+ throw errorInstance;
387
+ }
388
+ };
389
+ /**
390
+ * Interpolate retention rate from samples
391
+ */
392
+ const interpolateSamplesFn = (samples, usdValue, marginHi = Infinity) => {
393
+ return sdkInterpolateSamples(samples, usdValue, marginHi);
394
+ };
395
+ return {
396
+ isLoading: computed(() => isLoading.value),
397
+ error: computed(() => error.value),
398
+ ingressEstimates: computed(() => ingressEstimates.value),
399
+ egressEstimates: computed(() => egressEstimates.value),
400
+ getQuote: getQuoteFn,
401
+ solveUsdcAmount: solveUsdcAmountFn,
402
+ estimateLive: estimateLiveFn,
403
+ interpolateSamples: interpolateSamplesFn,
404
+ };
405
+ }
@@ -0,0 +1,12 @@
1
+ import { createEip712DocForOrder, createEip712DocForWalletGeneration, type QuoteResponse } from '@silentswap/sdk';
2
+ export interface GetSilentSwapAuthReturn {
3
+ createSignInMessage: (address: `0x${string}`, nonce: string, domain?: string) => string;
4
+ createEip712DocForOrder: (quoteResponse: QuoteResponse) => ReturnType<typeof createEip712DocForOrder>;
5
+ createEip712DocForWalletGeneration: (scope: string, token: string) => ReturnType<typeof createEip712DocForWalletGeneration>;
6
+ }
7
+ /**
8
+ * Vue composable for SilentSwap authentication utilities
9
+ *
10
+ * @returns Object with authentication utility functions
11
+ */
12
+ export declare function getSilentSwapAuth(): GetSilentSwapAuthReturn;
@@ -0,0 +1,23 @@
1
+ import { createSignInMessage, createEip712DocForOrder, createEip712DocForWalletGeneration, } from '@silentswap/sdk';
2
+ /**
3
+ * Vue composable for SilentSwap authentication utilities
4
+ *
5
+ * @returns Object with authentication utility functions
6
+ */
7
+ export function getSilentSwapAuth() {
8
+ const createSignInMessageFn = (address, nonce, domain) => {
9
+ const signInMessage = createSignInMessage(address, nonce, domain);
10
+ return signInMessage.message;
11
+ };
12
+ const createEip712DocForOrderFn = (quoteResponse) => {
13
+ return createEip712DocForOrder(quoteResponse);
14
+ };
15
+ const createEip712DocForWalletGenerationFn = (scope, token) => {
16
+ return createEip712DocForWalletGeneration(scope, token);
17
+ };
18
+ return {
19
+ createSignInMessage: createSignInMessageFn,
20
+ createEip712DocForOrder: createEip712DocForOrderFn,
21
+ createEip712DocForWalletGeneration: createEip712DocForWalletGenerationFn,
22
+ };
23
+ }
@@ -0,0 +1,14 @@
1
+ import { type SilentSwapClient, type SilentSwapClientConfig } from '@silentswap/sdk';
2
+ export interface GetSilentSwapClientOptions {
3
+ config: SilentSwapClientConfig;
4
+ }
5
+ export interface GetSilentSwapClientReturn {
6
+ client: SilentSwapClient;
7
+ }
8
+ /**
9
+ * Vue composable for creating and managing a SilentSwap client instance
10
+ *
11
+ * @param options - Configuration options for the client
12
+ * @returns Object containing the SilentSwap client instance
13
+ */
14
+ export declare function getSilentSwapClient({ config, }: GetSilentSwapClientOptions): GetSilentSwapClientReturn;
@@ -0,0 +1,16 @@
1
+ import { computed } from 'vue';
2
+ import { createSilentSwapClient, } from '@silentswap/sdk';
3
+ /**
4
+ * Vue composable for creating and managing a SilentSwap client instance
5
+ *
6
+ * @param options - Configuration options for the client
7
+ * @returns Object containing the SilentSwap client instance
8
+ */
9
+ export function getSilentSwapClient({ config, }) {
10
+ const client = computed(() => {
11
+ return createSilentSwapClient(config);
12
+ });
13
+ return {
14
+ client: client.value,
15
+ };
16
+ }
@@ -0,0 +1,20 @@
1
+ import { ref } from 'vue';
2
+ import type { SilentSwapClient, AuthRequest, AuthResponse, NonceResponse, OrderRequest, OrderResponse, QuoteRequest, QuoteResponse } from '@silentswap/sdk';
3
+ export interface GetSilentSwapOrdersOptions {
4
+ client: SilentSwapClient;
5
+ }
6
+ export interface GetSilentSwapOrdersReturn {
7
+ isLoading: Readonly<ReturnType<typeof ref<boolean>>>;
8
+ error: Readonly<ReturnType<typeof ref<Error | null>>>;
9
+ getNonce: (address: `0x${string}`) => Promise<NonceResponse | null>;
10
+ authenticate: (auth: AuthRequest) => Promise<AuthResponse | null>;
11
+ getQuote: (quote: QuoteRequest) => Promise<QuoteResponse | null>;
12
+ createOrder: (order: OrderRequest) => Promise<OrderResponse | null>;
13
+ }
14
+ /**
15
+ * Vue composable for managing SilentSwap orders and related operations
16
+ *
17
+ * @param options - Options containing the SilentSwap client
18
+ * @returns Object with order management methods and state
19
+ */
20
+ export declare function getSilentSwapOrders({ client, }: GetSilentSwapOrdersOptions): GetSilentSwapOrdersReturn;
@@ -0,0 +1,73 @@
1
+ import { ref, computed } from 'vue';
2
+ /**
3
+ * Vue composable for managing SilentSwap orders and related operations
4
+ *
5
+ * @param options - Options containing the SilentSwap client
6
+ * @returns Object with order management methods and state
7
+ */
8
+ export function getSilentSwapOrders({ client, }) {
9
+ const isLoading = ref(false);
10
+ const error = ref(null);
11
+ const handleAsyncOperation = async (operation, errorMessage) => {
12
+ isLoading.value = true;
13
+ error.value = null;
14
+ try {
15
+ const result = await operation();
16
+ return result ?? null;
17
+ }
18
+ catch (err) {
19
+ const errorInstance = err instanceof Error ? err : new Error(errorMessage);
20
+ error.value = errorInstance;
21
+ return null;
22
+ }
23
+ finally {
24
+ isLoading.value = false;
25
+ }
26
+ };
27
+ const getNonce = async (address) => {
28
+ return handleAsyncOperation(async () => {
29
+ const [error, response] = await client.nonce(address);
30
+ if (error) {
31
+ throw new Error(`Failed to get nonce: ${error.error}`);
32
+ }
33
+ return response;
34
+ }, 'Failed to fetch nonce');
35
+ };
36
+ const authenticate = async (auth) => {
37
+ return handleAsyncOperation(async () => {
38
+ const [error, response] = await client.authenticate(auth);
39
+ if (error) {
40
+ throw new Error(`Authentication failed: ${error.error}`);
41
+ }
42
+ return response;
43
+ }, 'Authentication failed');
44
+ };
45
+ const getQuote = async (quote) => {
46
+ return handleAsyncOperation(async () => {
47
+ const [error, response] = await client.quote(quote);
48
+ if (error) {
49
+ throw new Error(`Failed to get quote: ${error.error}`);
50
+ }
51
+ return response;
52
+ }, 'Failed to fetch quote');
53
+ };
54
+ const createOrder = async (order) => {
55
+ return handleAsyncOperation(async () => {
56
+ const [error, response] = await client.order(order);
57
+ if (error) {
58
+ throw new Error(`Failed to create order: ${error.error}`);
59
+ }
60
+ return response;
61
+ }, 'Failed to create order');
62
+ };
63
+ return {
64
+ // State
65
+ isLoading: computed(() => isLoading.value),
66
+ error: computed(() => error.value),
67
+ // Methods
68
+ getNonce,
69
+ authenticate,
70
+ getQuote,
71
+ createOrder,
72
+ };
73
+ }
@@ -0,0 +1,26 @@
1
+ import { ref } from 'vue';
2
+ import type { WalletClient } from 'viem';
3
+ import type { Connector } from 'wagmi';
4
+ import type { BridgeProvider, BridgeQuote, BridgeStatus } from '@silentswap/sdk';
5
+ export interface GetTransactionOptions {
6
+ /** User's EVM address */
7
+ address: `0x${string}`;
8
+ /** Wallet client for signing operations */
9
+ walletClient?: WalletClient;
10
+ /** Wagmi connector */
11
+ connector?: Connector;
12
+ }
13
+ export interface GetTransactionReturn {
14
+ isLoading: Readonly<ReturnType<typeof ref<boolean>>>;
15
+ currentStep: Readonly<ReturnType<typeof ref<string>>>;
16
+ error: Readonly<ReturnType<typeof ref<Error | null>>>;
17
+ executeTransaction: (quote: BridgeQuote) => Promise<BridgeStatus | null>;
18
+ getStatus: (requestId: string, provider: BridgeProvider) => Promise<BridgeStatus | null>;
19
+ }
20
+ /**
21
+ * Vue composable for executing bridge transactions
22
+ *
23
+ * This composable provides transaction execution for bridge quotes from any provider.
24
+ * It handles chain switching and transaction sending automatically.
25
+ */
26
+ export declare function getTransaction({ address, walletClient, connector, }: GetTransactionOptions): GetTransactionReturn;
@@ -0,0 +1,82 @@
1
+ import { ref, computed } from 'vue';
2
+ import { executeRelayBridge, executeDebridgeBridge, getBridgeStatus, createTransactionExecutor, createChainSwitcher, } from '@silentswap/sdk';
3
+ /**
4
+ * Vue composable for executing bridge transactions
5
+ *
6
+ * This composable provides transaction execution for bridge quotes from any provider.
7
+ * It handles chain switching and transaction sending automatically.
8
+ */
9
+ export function getTransaction({ address, walletClient, connector, }) {
10
+ const isLoading = ref(false);
11
+ const currentStep = ref('');
12
+ const error = ref(null);
13
+ /**
14
+ * Execute a bridge transaction
15
+ */
16
+ const executeTransaction = async (quote) => {
17
+ if (!walletClient) {
18
+ throw new Error('Wallet client required for bridge execution');
19
+ }
20
+ if (!connector) {
21
+ throw new Error('Connector required for bridge execution');
22
+ }
23
+ isLoading.value = true;
24
+ error.value = null;
25
+ currentStep.value = '';
26
+ try {
27
+ // Create wrapper functions using shared utilities
28
+ const executeTransaction = createTransactionExecutor(walletClient, connector);
29
+ const switchChain = createChainSwitcher(walletClient, connector);
30
+ // Execute based on provider
31
+ switch (quote.provider) {
32
+ case 'relay':
33
+ return await executeRelayBridge(quote, executeTransaction, switchChain, (step) => {
34
+ currentStep.value = step;
35
+ });
36
+ case 'debridge':
37
+ return await executeDebridgeBridge(quote, executeTransaction, switchChain, (step) => {
38
+ currentStep.value = step;
39
+ });
40
+ default:
41
+ throw new Error(`Unsupported bridge provider: ${quote.provider}`);
42
+ }
43
+ }
44
+ catch (err) {
45
+ const errorInstance = err instanceof Error ? err : new Error('Bridge execution failed');
46
+ error.value = errorInstance;
47
+ return null;
48
+ }
49
+ finally {
50
+ isLoading.value = false;
51
+ currentStep.value = '';
52
+ }
53
+ };
54
+ /**
55
+ * Get bridge status for a request
56
+ */
57
+ const getStatus = async (requestId, provider) => {
58
+ isLoading.value = true;
59
+ currentStep.value = 'Checking bridge status';
60
+ error.value = null;
61
+ try {
62
+ // Use shared status function
63
+ return await getBridgeStatus(requestId, provider);
64
+ }
65
+ catch (err) {
66
+ const errorInstance = err instanceof Error ? err : new Error('Failed to get bridge status');
67
+ error.value = errorInstance;
68
+ return null;
69
+ }
70
+ finally {
71
+ isLoading.value = false;
72
+ currentStep.value = '';
73
+ }
74
+ };
75
+ return {
76
+ isLoading: computed(() => isLoading.value),
77
+ currentStep: computed(() => currentStep.value),
78
+ error: computed(() => error.value),
79
+ executeTransaction,
80
+ getStatus,
81
+ };
82
+ }
@@ -0,0 +1,8 @@
1
+ export { getSilentSwapClient } from './composables/getSilentSwapClient.js';
2
+ export { getSilentSwapOrders } from './composables/getSilentSwapOrders.js';
3
+ export { getSilentSwapAuth } from './composables/getSilentSwapAuth.js';
4
+ export { getQuote } from './composables/getQuote.js';
5
+ export { getTransaction } from './composables/getTransaction.js';
6
+ export type { SilentSwapClientConfig } from '@silentswap/sdk';
7
+ export type { GetQuoteOptions, GetQuoteReturn, BridgeQuoteResult, EstimateResult, EstimateSample, Estimate, SolveResult, } from './composables/getQuote.js';
8
+ export type { GetTransactionOptions, GetTransactionReturn, } from './composables/getTransaction.js';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { getSilentSwapClient } from './composables/getSilentSwapClient.js';
2
+ export { getSilentSwapOrders } from './composables/getSilentSwapOrders.js';
3
+ export { getSilentSwapAuth } from './composables/getSilentSwapAuth.js';
4
+ export { getQuote } from './composables/getQuote.js';
5
+ export { getTransaction } from './composables/getTransaction.js';
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@silentswap/vue",
3
+ "type": "module",
4
+ "version": "0.0.41",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "clean": "rm -rf dist",
14
+ "dev": "tsc --watch",
15
+ "lint": "eslint src --ext .ts",
16
+ "lint:fix": "eslint src --ext .ts --fix"
17
+ },
18
+ "peerDependencies": {
19
+ "vue": ">=3.0.0",
20
+ "viem": "2.43.3",
21
+ "wagmi": "3.1.3"
22
+ },
23
+ "dependencies": {
24
+ "@silentswap/sdk": "workspace:*",
25
+ "bignumber.js": "9.3.1"
26
+ },
27
+ "devDependencies": {
28
+ "@eslint/js": "9.38.0",
29
+ "@stylistic/eslint-plugin": "5.5.0",
30
+ "@tsconfig/node24": "24.0.1",
31
+ "@types/node": "24.9.1",
32
+ "@types/web": "0.0.283",
33
+ "@typescript-eslint/parser": "8.46.2",
34
+ "eslint": "9.38.0",
35
+ "eslint-import-resolver-typescript": "4.4.4",
36
+ "eslint-plugin-import-x": "4.16.1",
37
+ "typescript": "5.9.3",
38
+ "typescript-eslint": "8.46.2",
39
+ "vue": "3.4.0"
40
+ }
41
+ }