@silentswap/sdk 0.1.62 → 0.1.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge.js +55 -12
- package/dist/chain.js +11 -12
- package/dist/order.d.ts +5 -0
- package/dist/order.js +14 -0
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +2 -2
- package/package.json +1 -1
package/dist/bridge.js
CHANGED
|
@@ -349,7 +349,6 @@ export async function fetchDebridgeOrder(params, signal) {
|
|
|
349
349
|
* Note: DLN does not support same-chain swaps via create-tx endpoint
|
|
350
350
|
*/
|
|
351
351
|
async function fetchDebridgeSingleChainOrder(params, signal) {
|
|
352
|
-
console.log('fetchDebridgeSingleChainOrder');
|
|
353
352
|
// Build query parameters for single-chain swap endpoint
|
|
354
353
|
const queryParams = new URLSearchParams({
|
|
355
354
|
chainId: params.srcChainId.toString(),
|
|
@@ -359,6 +358,16 @@ async function fetchDebridgeSingleChainOrder(params, signal) {
|
|
|
359
358
|
tokenOutRecipient: params.dstChainTokenOutRecipient || params.account || '',
|
|
360
359
|
senderAddress: params.senderAddress || params.account || '',
|
|
361
360
|
});
|
|
361
|
+
// EXACT_OUTPUT support: when caller passes a concrete dstChainTokenOutAmount
|
|
362
|
+
// (and srcChainTokenInAmount === 'auto'), forward it as `tokenOutAmount`.
|
|
363
|
+
// Without this the convergence loop in solveDebridgeSingleChainUsdcAmount
|
|
364
|
+
// sends `tokenInAmount=auto` with no target → deBridge returns
|
|
365
|
+
// INTERNAL_SERVER_ERROR on every iteration after the first, so the loop
|
|
366
|
+
// never finds the optimal price and surfaces as "Failed to find optimal
|
|
367
|
+
// bridge price".
|
|
368
|
+
if (params.dstChainTokenOutAmount && params.dstChainTokenOutAmount !== 'auto') {
|
|
369
|
+
queryParams.set('tokenOutAmount', params.dstChainTokenOutAmount);
|
|
370
|
+
}
|
|
362
371
|
// Add optional parameters if provided
|
|
363
372
|
if (params.prependOperatingExpenses !== undefined) {
|
|
364
373
|
queryParams.set('prependOperatingExpenses', String(params.prependOperatingExpenses));
|
|
@@ -520,8 +529,11 @@ async function solveRelayUsdcAmount(srcChainId, srcToken, srcAmount, userAddress
|
|
|
520
529
|
if (impactPercent > maxImpactPercent) {
|
|
521
530
|
throw new Error(`Price impact too high: ${impactPercent.toFixed(2)}%`);
|
|
522
531
|
}
|
|
523
|
-
// Amount in is within our budget
|
|
524
|
-
|
|
532
|
+
// Amount in is within our budget — accept up to 0.5% over to absorb
|
|
533
|
+
// rounding noise from the bridge's internal pricing (otherwise the loop
|
|
534
|
+
// bounces on quotes that are e.g. 0.1% over, until convergence breaks).
|
|
535
|
+
const budgetWithTolerance = BigInt(srcAmount) * 1005n / 1000n;
|
|
536
|
+
if (amountIn <= budgetWithTolerance) {
|
|
525
537
|
console.log('[SilentSwap:SolveRelay] Found optimal amount', {
|
|
526
538
|
usdcOut: amountOut.toString(),
|
|
527
539
|
amountIn: amountIn.toString(),
|
|
@@ -536,7 +548,13 @@ async function solveRelayUsdcAmount(srcChainId, srcToken, srcAmount, userAddress
|
|
|
536
548
|
.minus(srcAmount)
|
|
537
549
|
.times(yg_utoken_price)
|
|
538
550
|
.div(yg_uusdc_price);
|
|
539
|
-
|
|
551
|
+
// Anchor the next target on the *current target* (what we asked for),
|
|
552
|
+
// not on `currencyOut.amount` (what the bridge's quote estimates we'd
|
|
553
|
+
// get). For some endpoints — notably deBridge single-chain —
|
|
554
|
+
// `amount > requested target` because of a built-in slippage buffer,
|
|
555
|
+
// and using it as the base produces new_target > previous_target on
|
|
556
|
+
// tight iterations, breaking convergence.
|
|
557
|
+
const sg_uusdc_target = BigNumber(targetUsdcOut.toString())
|
|
540
558
|
.minus(yg_uusdc_over)
|
|
541
559
|
.toFixed(0);
|
|
542
560
|
console.log(`[SilentSwap:SolveRelay] Reducing target`, {
|
|
@@ -622,12 +640,17 @@ async function solveDebridgeUsdcAmount(srcChainId, srcToken, srcAmount, userAddr
|
|
|
622
640
|
throw new Error(`Price impact too high: ${impactPercent.toFixed(2)}%`);
|
|
623
641
|
}
|
|
624
642
|
const amountIn = BigInt(quote.estimation.srcChainTokenIn.amount);
|
|
625
|
-
// Amount in is within our budget
|
|
626
|
-
|
|
643
|
+
// Amount in is within our budget — accept up to 0.5% over to absorb
|
|
644
|
+
// rounding noise from deBridge's internal pricing.
|
|
645
|
+
const budgetWithTolerance = BigInt(srcAmount) * 1005n / 1000n;
|
|
646
|
+
if (amountIn <= budgetWithTolerance) {
|
|
647
|
+
// quote.tx can be undefined when DeBridge returns an estimation-only response
|
|
648
|
+
// (e.g. unroutable edge cases, takers without capacity for the route, or non-EVM
|
|
649
|
+
// sources like Solana/Tron where no ERC-20 allowance is needed at all).
|
|
627
650
|
return [
|
|
628
651
|
BigInt(quote.estimation.dstChainTokenOut.amount),
|
|
629
652
|
amountIn,
|
|
630
|
-
quote.tx
|
|
653
|
+
quote.tx?.allowanceTarget || '',
|
|
631
654
|
];
|
|
632
655
|
}
|
|
633
656
|
// Calculate new target based on overshoot
|
|
@@ -638,7 +661,14 @@ async function solveDebridgeUsdcAmount(srcChainId, srcToken, srcAmount, userAddr
|
|
|
638
661
|
.minus(srcAmount)
|
|
639
662
|
.times(yg_utoken_price)
|
|
640
663
|
.div(yg_uusdc_price);
|
|
641
|
-
|
|
664
|
+
// Anchor on the *requested target* (targetUsdcOut), not the bridge's
|
|
665
|
+
// estimated `amount`. deBridge's single-chain endpoint returns
|
|
666
|
+
// `amount > minAmount` (built-in slippage buffer), which makes the
|
|
667
|
+
// current iteration's `dstChainTokenOut.amount` larger than the
|
|
668
|
+
// requested target. Subtracting a small overshoot from that larger
|
|
669
|
+
// value yields a new target that's still bigger than the previous
|
|
670
|
+
// target, and the convergence guard fires.
|
|
671
|
+
const sg_uusdc_target = BigNumber(targetUsdcOut.toString())
|
|
642
672
|
.minus(yg_uusdc_over)
|
|
643
673
|
.toFixed(0);
|
|
644
674
|
if (BigInt(sg_uusdc_target) >= targetUsdcOut) {
|
|
@@ -711,12 +741,17 @@ async function solveDebridgeSingleChainUsdcAmount(chainId, srcToken, srcAmount,
|
|
|
711
741
|
throw new Error(`Price impact too high: ${impactPercent.toFixed(2)}%`);
|
|
712
742
|
}
|
|
713
743
|
const amountIn = BigInt(quote.estimation.srcChainTokenIn.amount);
|
|
714
|
-
// Amount in is within our budget
|
|
715
|
-
|
|
744
|
+
// Amount in is within our budget — accept up to 0.5% over to absorb
|
|
745
|
+
// rounding noise from deBridge's internal pricing.
|
|
746
|
+
const budgetWithTolerance = BigInt(srcAmount) * 1005n / 1000n;
|
|
747
|
+
if (amountIn <= budgetWithTolerance) {
|
|
748
|
+
// quote.tx can be undefined when DeBridge returns an estimation-only response
|
|
749
|
+
// (e.g. unroutable edge cases, takers without capacity for the route, or non-EVM
|
|
750
|
+
// sources like Solana/Tron where no ERC-20 allowance is needed at all).
|
|
716
751
|
return [
|
|
717
752
|
BigInt(quote.estimation.dstChainTokenOut.amount),
|
|
718
753
|
amountIn,
|
|
719
|
-
quote.tx
|
|
754
|
+
quote.tx?.allowanceTarget || '',
|
|
720
755
|
];
|
|
721
756
|
}
|
|
722
757
|
// Calculate new target based on overshoot
|
|
@@ -727,7 +762,14 @@ async function solveDebridgeSingleChainUsdcAmount(chainId, srcToken, srcAmount,
|
|
|
727
762
|
.minus(srcAmount)
|
|
728
763
|
.times(yg_utoken_price)
|
|
729
764
|
.div(yg_uusdc_price);
|
|
730
|
-
|
|
765
|
+
// Anchor on the *requested target* (targetUsdcOut), not the bridge's
|
|
766
|
+
// estimated `amount`. deBridge's single-chain endpoint returns
|
|
767
|
+
// `amount > minAmount` (built-in slippage buffer), which makes the
|
|
768
|
+
// current iteration's `dstChainTokenOut.amount` larger than the
|
|
769
|
+
// requested target. Subtracting a small overshoot from that larger
|
|
770
|
+
// value yields a new target that's still bigger than the previous
|
|
771
|
+
// target, and the convergence guard fires.
|
|
772
|
+
const sg_uusdc_target = BigNumber(targetUsdcOut.toString())
|
|
731
773
|
.minus(yg_uusdc_over)
|
|
732
774
|
.toFixed(0);
|
|
733
775
|
if (BigInt(sg_uusdc_target) >= targetUsdcOut) {
|
|
@@ -783,6 +825,7 @@ forceProvider) {
|
|
|
783
825
|
const debridgeData = debridgeResult.status === 'fulfilled' ? debridgeResult.value : null;
|
|
784
826
|
// Both failed — surface the most relevant inner error message
|
|
785
827
|
if (!relayData && !debridgeData) {
|
|
828
|
+
debugger;
|
|
786
829
|
const errors = [
|
|
787
830
|
relayResult.status === 'rejected' ? relayResult.reason : null,
|
|
788
831
|
debridgeResult.status === 'rejected' ? debridgeResult.reason : null,
|
package/dist/chain.js
CHANGED
|
@@ -31,12 +31,10 @@ export async function ensureChain(chainId, walletClient, connector, options) {
|
|
|
31
31
|
}
|
|
32
32
|
console.log('[ensureChain] currentChainId:', currentChainId, 'targetChainId:', chainId, 'needsSwitch:', currentChainId !== chainId);
|
|
33
33
|
if (currentChainId !== chainId) {
|
|
34
|
-
let switchSucceeded = false;
|
|
35
34
|
try {
|
|
36
35
|
// Try to switch chain
|
|
37
36
|
console.log('[ensureChain] attempting switchChain ...', { hasSwitchChain: !!connector.switchChain });
|
|
38
37
|
await connector.switchChain?.({ chainId });
|
|
39
|
-
switchSucceeded = true;
|
|
40
38
|
console.log('[ensureChain] switchChain succeeded');
|
|
41
39
|
}
|
|
42
40
|
catch (switchError) {
|
|
@@ -49,16 +47,17 @@ export async function ensureChain(chainId, walletClient, connector, options) {
|
|
|
49
47
|
}
|
|
50
48
|
// Non-required: chain switch failed (e.g. Trust Wallet via WalletConnect
|
|
51
49
|
// doesn't support programmatic chain switching). Still recreate the
|
|
52
|
-
// walletClient from the provider so the WalletConnect transport
|
|
53
|
-
// — TrustWallet silently drops signTypedData when the client's
|
|
54
|
-
// doesn't match its active session chain.
|
|
50
|
+
// walletClient from the provider below so the WalletConnect transport
|
|
51
|
+
// is fresh — TrustWallet silently drops signTypedData when the client's
|
|
52
|
+
// chain scope doesn't match its active session chain.
|
|
55
53
|
}
|
|
56
|
-
// Always
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// by
|
|
60
|
-
//
|
|
61
|
-
|
|
54
|
+
// Always rebind the walletClient if its `chain` config doesn't match the target.
|
|
55
|
+
// viem's signTypedData / sendTransaction asserts that walletClient.chain.id matches
|
|
56
|
+
// the wallet's reported eth_chainId — if the wallet was already switched to `chainId`
|
|
57
|
+
// (e.g. by an earlier ensureChain call) but the closure's walletClient was created
|
|
58
|
+
// by wagmi with a different chain, signing throws "Provided chainId X must match
|
|
59
|
+
// active chainId Y". Recreating the client here keeps the two in sync.
|
|
60
|
+
console.log('[ensureChain] rebinding walletClient to chain', chainId, '(was', walletClient.chain?.id, ')');
|
|
62
61
|
const provider = await connector.getProvider();
|
|
63
62
|
const chain = getChainById(chainId);
|
|
64
63
|
if (!chain) {
|
|
@@ -78,7 +77,7 @@ export async function ensureChain(chainId, walletClient, connector, options) {
|
|
|
78
77
|
});
|
|
79
78
|
return newClient;
|
|
80
79
|
}
|
|
81
|
-
console.log('[ensureChain] already on correct chain, returning existing walletClient');
|
|
80
|
+
console.log('[ensureChain] already on correct chain with matching client, returning existing walletClient');
|
|
82
81
|
return walletClient;
|
|
83
82
|
}
|
|
84
83
|
/**
|
package/dist/order.d.ts
CHANGED
|
@@ -276,5 +276,10 @@ export declare const EIP712_DOMAIN_ORDER_DEFAULT: {
|
|
|
276
276
|
readonly version: "1";
|
|
277
277
|
readonly chainId: 1;
|
|
278
278
|
};
|
|
279
|
+
export declare const EIP712_DOMAIN_WALLET_GENERATION: {
|
|
280
|
+
readonly name: "SilentSwap v2";
|
|
281
|
+
readonly version: "1";
|
|
282
|
+
readonly chainId: 43114;
|
|
283
|
+
};
|
|
279
284
|
export declare function quoteResponseToEip712Document(quoteResponse: QuoteResponse): TypedDataDocument<typeof EIP712_DOMAIN_ORDER_DEFAULT>;
|
|
280
285
|
export {};
|
package/dist/order.js
CHANGED
|
@@ -94,11 +94,25 @@ export const EIP712_TYPES_WALLET_GENERATION = {
|
|
|
94
94
|
{ name: 'token', type: 'string' },
|
|
95
95
|
],
|
|
96
96
|
};
|
|
97
|
+
// Orders must be signed with chainId === 1 (Ethereum mainnet) — the backend
|
|
98
|
+
// rejects any other chainId with "eip712Domain value is invalid or not supported.
|
|
99
|
+
// expected mainnet chain ID 1". Wallet-generation typed data uses a different
|
|
100
|
+
// domain (EIP712_DOMAIN_WALLET_GENERATION below) and is not subject to that
|
|
101
|
+
// constraint.
|
|
97
102
|
export const EIP712_DOMAIN_ORDER_DEFAULT = {
|
|
98
103
|
name: 'SilentSwap v2',
|
|
99
104
|
version: '1',
|
|
100
105
|
chainId: 1,
|
|
101
106
|
};
|
|
107
|
+
// Wallet-generation typed data is signed locally to derive entropy — the chainId
|
|
108
|
+
// only matters for the wallet's cross-chain replay-protection check at sign time.
|
|
109
|
+
// Pinned to Avalanche (43114) so the user doesn't have to leave their working
|
|
110
|
+
// chain just to derive the wallet.
|
|
111
|
+
export const EIP712_DOMAIN_WALLET_GENERATION = {
|
|
112
|
+
name: 'SilentSwap v2',
|
|
113
|
+
version: '1',
|
|
114
|
+
chainId: 43114,
|
|
115
|
+
};
|
|
102
116
|
export function quoteResponseToEip712Document(quoteResponse) {
|
|
103
117
|
return {
|
|
104
118
|
domain: EIP712_DOMAIN_ORDER_DEFAULT,
|
package/dist/sdk.d.ts
CHANGED
|
@@ -156,7 +156,7 @@ export declare function createEip712DocForWalletGeneration(scope: string, token:
|
|
|
156
156
|
domain: {
|
|
157
157
|
readonly name: "SilentSwap v2";
|
|
158
158
|
readonly version: "1";
|
|
159
|
-
readonly chainId:
|
|
159
|
+
readonly chainId: 43114;
|
|
160
160
|
};
|
|
161
161
|
primaryType: "WalletGeneration";
|
|
162
162
|
message: {
|
package/dist/sdk.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSiweMessage } from 'viem/siwe';
|
|
2
|
-
import { EIP712_DOMAIN_ORDER_DEFAULT, EIP712_TYPES_ORDER, EIP712_TYPES_WALLET_GENERATION } from './order.js';
|
|
2
|
+
import { EIP712_DOMAIN_ORDER_DEFAULT, EIP712_DOMAIN_WALLET_GENERATION, EIP712_TYPES_ORDER, EIP712_TYPES_WALLET_GENERATION } from './order.js';
|
|
3
3
|
/**
|
|
4
4
|
* Creates a SIWE message for authentication with SilentSwap
|
|
5
5
|
* @param user - user's EVM address
|
|
@@ -49,7 +49,7 @@ export function createEip712DocForOrder(quote) {
|
|
|
49
49
|
export function createEip712DocForWalletGeneration(scope, token) {
|
|
50
50
|
return {
|
|
51
51
|
types: EIP712_TYPES_WALLET_GENERATION,
|
|
52
|
-
domain:
|
|
52
|
+
domain: EIP712_DOMAIN_WALLET_GENERATION,
|
|
53
53
|
primaryType: 'WalletGeneration',
|
|
54
54
|
message: {
|
|
55
55
|
description: 'Securely create a temporary, anonymous wallet for SilentSwap use only.',
|