@kaspacom/swap-sdk 1.1.6 → 1.1.8

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/index.js CHANGED
@@ -95,6 +95,12 @@ var WalletService = class {
95
95
  getProvider() {
96
96
  return this.walletProvider || this.networkProvider;
97
97
  }
98
+ destroy() {
99
+ if (this.walletProvider) {
100
+ this.networkProvider.destroy();
101
+ this.disconnect();
102
+ }
103
+ }
98
104
  getSigner() {
99
105
  return this.signer;
100
106
  }
@@ -130,11 +136,6 @@ var WalletService = class {
130
136
  throw new Error("No Ethereum wallet detected. Please connect a wallet provider.");
131
137
  }
132
138
  }
133
- // Disconnect wallet and emit event
134
- disconnectWallet() {
135
- this.address = null;
136
- this.signer = null;
137
- }
138
139
  };
139
140
 
140
141
  // src/services/swap.service.ts
@@ -1223,12 +1224,30 @@ var SwapService = class {
1223
1224
  this.signer = null;
1224
1225
  this.pairs = [];
1225
1226
  this.resolvePairsLoaded = null;
1227
+ this.rejectPairsLoaded = null;
1226
1228
  this.resolvePartnerFeeLoaded = null;
1229
+ this.rejectPartnerFeeLoaded = null;
1227
1230
  this.partnerFee = 0n;
1228
1231
  this.provider = provider;
1229
- this.wethAddress = config.wrappedToken.address;
1230
1232
  this.chainId = config.chainId;
1231
- const routerAbi = [
1233
+ this.routerContract = new Contract(config.routerAddress, this.routerAbi, provider);
1234
+ this.factoryContract = new Contract(config.factoryAddress, this.factoryAbi, provider);
1235
+ if (config.proxyAddress) {
1236
+ this.proxyContract = new Contract(config.proxyAddress, this.proxyAbi, provider);
1237
+ }
1238
+ this.pairsLoadedPromise = new Promise((resolve, reject) => {
1239
+ this.resolvePairsLoaded = resolve;
1240
+ this.rejectPairsLoaded = reject;
1241
+ });
1242
+ this.partnerFeeLoadedPromise = new Promise((resolve, reject) => {
1243
+ this.resolvePartnerFeeLoaded = resolve;
1244
+ this.rejectPartnerFeeLoaded = reject;
1245
+ });
1246
+ this.loadAllPairs();
1247
+ this.loadPartnerFee();
1248
+ }
1249
+ get routerAbi() {
1250
+ return [
1232
1251
  // Swaps (ERC20 <-> ERC20)
1233
1252
  "function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)",
1234
1253
  "function swapTokensForExactTokens(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)",
@@ -1241,47 +1260,49 @@ var SwapService = class {
1241
1260
  "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)",
1242
1261
  "function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut)",
1243
1262
  "function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn)",
1244
- "function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts)",
1263
+ "function getAmountsIn(uint amountOut, address[] memory path) internal view returns (uint[] memory amounts)",
1245
1264
  // Get WETH
1246
1265
  "function WETH() external pure returns (address)"
1247
1266
  ];
1248
- const factoryAbi = [
1267
+ }
1268
+ get factoryAbi() {
1269
+ return [
1249
1270
  "function getPair(address tokenA, address tokenB) external view returns (address pair)",
1250
1271
  "function allPairs(uint) external view returns (address pair)",
1251
1272
  "function allPairsLength() external view returns (uint)"
1252
1273
  ];
1253
- const proxyAbi = [
1254
- ...routerAbi,
1274
+ }
1275
+ get proxyAbi() {
1276
+ return [
1277
+ ...this.routerAbi,
1255
1278
  "function partners(bytes32) external view returns (address feeRecipient, uint16 feeBps)"
1256
1279
  ];
1257
- this.routerContract = new Contract(config.routerAddress, routerAbi, provider);
1258
- this.factoryContract = new Contract(config.factoryAddress, factoryAbi, provider);
1259
- if (config.proxyAddress) {
1260
- this.proxyContract = new Contract(config.proxyAddress, proxyAbi, provider);
1261
- }
1262
- this.pairsLoadedPromise = new Promise((resolve) => {
1263
- this.resolvePairsLoaded = resolve;
1264
- });
1265
- this.partnerFeeLoadedPromise = new Promise((resolve) => {
1266
- this.resolvePartnerFeeLoaded = resolve;
1267
- });
1268
- this.loadAllPairsFromGraph();
1269
- this.loadPartnerFee();
1270
1280
  }
1271
1281
  // parnter fee is BPS_DIVISOR = 10_000n;
1272
1282
  async loadPartnerFee() {
1273
1283
  if (!this.resolvePairsLoaded) {
1274
1284
  return this.partnerFee;
1275
1285
  }
1276
- if (this.swapOptions.partnerKey && this.proxyContract) {
1277
- const [, fee] = await this.proxyContract?.partners(this.swapOptions.partnerKey);
1278
- this.partnerFee = fee;
1279
- }
1280
- if (this.resolvePartnerFeeLoaded) {
1281
- this.resolvePartnerFeeLoaded();
1282
- this.resolvePartnerFeeLoaded = null;
1286
+ try {
1287
+ if (this.swapOptions.partnerKey && this.proxyContract) {
1288
+ const [, fee] = await this.proxyContract?.partners(this.swapOptions.partnerKey);
1289
+ this.partnerFee = fee;
1290
+ }
1291
+ if (this.resolvePartnerFeeLoaded) {
1292
+ this.resolvePartnerFeeLoaded();
1293
+ this.resolvePartnerFeeLoaded = null;
1294
+ }
1295
+ return this.partnerFee;
1296
+ } catch (error) {
1297
+ if (this.rejectPartnerFeeLoaded) {
1298
+ this.rejectPartnerFeeLoaded(error);
1299
+ this.partnerFeeLoadedPromise = new Promise((resolve, reject) => {
1300
+ this.resolvePartnerFeeLoaded = resolve;
1301
+ this.rejectPartnerFeeLoaded = reject;
1302
+ });
1303
+ }
1304
+ throw error;
1283
1305
  }
1284
- return this.partnerFee;
1285
1306
  }
1286
1307
  setSigner(signer) {
1287
1308
  this.signer = signer;
@@ -1301,11 +1322,7 @@ var SwapService = class {
1301
1322
  }
1302
1323
  return num.toFixed(decimals);
1303
1324
  }
1304
- /**
1305
- * Loads all pairs from The Graph and caches them as Uniswap SDK Pair instances.
1306
- * @param graphEndpoint The GraphQL endpoint URL
1307
- */
1308
- async loadAllPairsFromGraph() {
1325
+ async refreshPairsFromGraph() {
1309
1326
  const query = `{
1310
1327
  pairs(first: 1000) {
1311
1328
  id
@@ -1315,46 +1332,47 @@ var SwapService = class {
1315
1332
  token1 { id symbol name decimals }
1316
1333
  }
1317
1334
  }`;
1318
- try {
1319
- const response = await fetch(this.config.graphEndpoint, {
1320
- method: "POST",
1321
- headers: { "Content-Type": "application/json" },
1322
- body: JSON.stringify({ query })
1323
- });
1324
- if (!response.ok) throw new Error(`Network error: ${response.status}`);
1325
- const { data } = await response.json();
1326
- if (!data || !data.pairs) return;
1327
- const pairs = [];
1328
- for (const pair of data.pairs) {
1329
- pairs.push(
1330
- this.createSDKPair(pair)
1331
- );
1332
- }
1333
- this.pairs = pairs;
1334
- const wethPair = data.pairs.find(
1335
- (pair) => pair.token0.id.toLowerCase() === this.wethAddress.toLowerCase() || pair.token1.id.toLowerCase() === this.wethAddress.toLowerCase()
1335
+ const response = await fetch(this.config.graphEndpoint, {
1336
+ method: "POST",
1337
+ headers: { "Content-Type": "application/json" },
1338
+ body: JSON.stringify({ query })
1339
+ });
1340
+ if (!response.ok) throw new Error(`Network error: ${response.status}`);
1341
+ const { data } = await response.json();
1342
+ if (!data || !data.pairs) throw new Error(`No pairs found: ${data}`);
1343
+ return data.pairs;
1344
+ }
1345
+ async refreshPairs() {
1346
+ const pairsResult = this.swapOptions.getPairsData ? await this.swapOptions.getPairsData() : await this.refreshPairsFromGraph();
1347
+ const pairs = [];
1348
+ for (const pair of pairsResult) {
1349
+ pairs.push(
1350
+ this.createSDKPair(pair)
1336
1351
  );
1337
- if (wethPair) {
1338
- const tokenData = wethPair.token0.id.toLowerCase() === this.wethAddress.toLowerCase() ? wethPair.token0 : wethPair.token1;
1339
- this.wethToken = {
1340
- address: tokenData.id,
1341
- symbol: tokenData.symbol,
1342
- name: tokenData.name,
1343
- decimals: Number(tokenData.decimals)
1344
- };
1345
- } else {
1346
- throw new Error("No weth token found");
1347
- }
1352
+ }
1353
+ this.pairs = pairs;
1354
+ }
1355
+ /**
1356
+ * Loads all pairs from The Graph and caches them as Uniswap SDK Pair instances.
1357
+ * @param graphEndpoint The GraphQL endpoint URL
1358
+ */
1359
+ async loadAllPairs() {
1360
+ try {
1361
+ await this.refreshPairs();
1348
1362
  if (this.resolvePairsLoaded) {
1349
1363
  this.resolvePairsLoaded();
1350
1364
  this.resolvePairsLoaded = null;
1351
1365
  }
1352
1366
  } catch (error) {
1353
1367
  console.error("Error loading pairs from graph:", error);
1354
- if (this.resolvePairsLoaded) {
1355
- this.resolvePairsLoaded();
1356
- this.resolvePairsLoaded = null;
1368
+ if (this.rejectPairsLoaded) {
1369
+ this.rejectPairsLoaded(error);
1357
1370
  }
1371
+ this.pairsLoadedPromise = new Promise((resolve, reject) => {
1372
+ this.resolvePairsLoaded = resolve;
1373
+ this.rejectPairsLoaded = reject;
1374
+ });
1375
+ setTimeout(this.loadAllPairs.bind(this), 1e3);
1358
1376
  }
1359
1377
  }
1360
1378
  async waitForPairsLoaded() {
@@ -1364,7 +1382,7 @@ var SwapService = class {
1364
1382
  return await this.partnerFeeLoadedPromise;
1365
1383
  }
1366
1384
  createSDKPair(pair) {
1367
- const { token0, token1, id, reserve0, reserve1 } = pair;
1385
+ const { token0, token1, reserve0, reserve1 } = pair;
1368
1386
  const sdkToken0 = new Token2(
1369
1387
  this.chainId,
1370
1388
  token0.id,
@@ -1427,8 +1445,6 @@ var SwapService = class {
1427
1445
  isOutputAmount ? sdkToToken : sdkFromToken,
1428
1446
  amountInWei
1429
1447
  );
1430
- await this.waitForPairsLoaded();
1431
- await this.waitForPartnerFeeLoaded();
1432
1448
  const pairs = this.getPairs();
1433
1449
  if (!pairs || pairs.length === 0) {
1434
1450
  throw new Error("Pairs not loaded yet. Please wait for initialization.");
@@ -1454,6 +1470,14 @@ var SwapService = class {
1454
1470
  value = value.replace(/\.?0+$/, "");
1455
1471
  return value;
1456
1472
  }
1473
+ async getAmountsIn(sellAmountWei, pathAddresses) {
1474
+ const [aIn] = await this.routerContract.getAmountsIn(sellAmountWei, pathAddresses);
1475
+ return aIn;
1476
+ }
1477
+ async getAmountsOut(buyAmountWei, pathAddresses) {
1478
+ const [, aOut] = await this.routerContract.getAmountsOut(buyAmountWei, pathAddresses);
1479
+ return aOut;
1480
+ }
1457
1481
  /**
1458
1482
  *
1459
1483
  * @param sellToken
@@ -1465,6 +1489,8 @@ var SwapService = class {
1465
1489
  */
1466
1490
  async calculateTrade(sellToken, buyToken, targetAmount, isOutputAmount, slippage) {
1467
1491
  try {
1492
+ await this.waitForPairsLoaded();
1493
+ await this.waitForPartnerFeeLoaded();
1468
1494
  const roundedAmountIn = this.roundToDecimals(targetAmount, isOutputAmount ? buyToken.decimals : sellToken.decimals);
1469
1495
  let sellAmountWei = parseUnits(
1470
1496
  roundedAmountIn,
@@ -1475,8 +1501,8 @@ var SwapService = class {
1475
1501
  const denominator = PARTNER_FEE_BPS_DIVISOR - this.partnerFee;
1476
1502
  sellAmountWei = (numerator + denominator - 1n) / denominator;
1477
1503
  }
1478
- const sellTokenForContracts = sellToken.address == ethers.ZeroAddress ? this.wethToken : sellToken;
1479
- const buyTokenForContracts = buyToken.address == ethers.ZeroAddress ? this.wethToken : buyToken;
1504
+ const sellTokenForContracts = sellToken.address == ethers.ZeroAddress ? this.config.wrappedToken : sellToken;
1505
+ const buyTokenForContracts = buyToken.address == ethers.ZeroAddress ? this.config.wrappedToken : buyToken;
1480
1506
  const trade = await this.getBestTrade(
1481
1507
  sellTokenForContracts,
1482
1508
  buyTokenForContracts,
@@ -1486,8 +1512,16 @@ var SwapService = class {
1486
1512
  if (!trade) {
1487
1513
  throw new Error("No trade path found for the given tokens and amount.");
1488
1514
  }
1489
- const amountIn = trade.inputAmount.quotient.toString();
1490
- const amountOut = trade.outputAmount.quotient.toString();
1515
+ const pathAddresses = trade.route.path.map((token) => token.address);
1516
+ let amountIn = "0";
1517
+ let amountOut = "0";
1518
+ if (isOutputAmount) {
1519
+ amountIn = String(await this.getAmountsIn(sellAmountWei, pathAddresses));
1520
+ amountOut = String(parseUnits(targetAmount, buyToken.decimals));
1521
+ } else {
1522
+ amountOut = String(await this.getAmountsOut(sellAmountWei, pathAddresses));
1523
+ amountIn = String(parseUnits(targetAmount, sellToken.decimals));
1524
+ }
1491
1525
  let amounts = {
1492
1526
  amountIn: formatUnits(amountIn, sellToken.decimals),
1493
1527
  amountOut: isOutputAmount ? this.trimTrailingZeros(roundedAmountIn) : formatUnits(amountOut, buyToken.decimals),
@@ -1497,18 +1531,23 @@ var SwapService = class {
1497
1531
  const slippagePercent = new Percent(Math.round(parseFloat(slippage) * 100), 1e4);
1498
1532
  let maxAmountIn, minAmountOut;
1499
1533
  if (isOutputAmount) {
1500
- maxAmountIn = trade.maximumAmountIn(slippagePercent).quotient.toString();
1534
+ const slippageAmount = BigInt(amountIn) * BigInt(slippagePercent.numerator.toString()) / BigInt(slippagePercent.denominator.toString());
1535
+ const maxAmountInBigInt = BigInt(amountIn) + slippageAmount;
1536
+ maxAmountIn = maxAmountInBigInt.toString();
1501
1537
  amounts.maxAmountInRaw = maxAmountIn;
1502
1538
  amounts.maxAmountIn = formatUnits(maxAmountIn, sellToken.decimals);
1503
1539
  } else {
1504
- minAmountOut = trade.minimumAmountOut(slippagePercent).quotient.toString();
1540
+ const amountOutBigInt = BigInt(amountOut);
1541
+ const slippageAmount = amountOutBigInt * BigInt(slippagePercent.numerator.toString()) / BigInt(slippagePercent.denominator.toString());
1542
+ const minAmountOutBigInt = amountOutBigInt - slippageAmount;
1543
+ minAmountOut = minAmountOutBigInt.toString();
1505
1544
  amounts.minAmountOutRaw = minAmountOut;
1506
1545
  amounts.minAmountOut = formatUnits(minAmountOut, buyToken.decimals);
1507
1546
  }
1508
1547
  if (this.partnerFee && this.partnerFee > 0n) {
1509
1548
  if (!isOutputAmount) {
1510
- const amountOut2 = BigInt(trade.outputAmount.quotient.toString());
1511
- const amountOutMinusFee = amountOut2 * (PARTNER_FEE_BPS_DIVISOR - this.partnerFee) / PARTNER_FEE_BPS_DIVISOR;
1549
+ const amountOutBigInt = BigInt(amountOut);
1550
+ const amountOutMinusFee = amountOutBigInt * (PARTNER_FEE_BPS_DIVISOR - this.partnerFee) / PARTNER_FEE_BPS_DIVISOR;
1512
1551
  amounts.amountOutRaw = amountOutMinusFee.toString();
1513
1552
  amounts.amountOut = formatUnits(amountOutMinusFee, buyToken.decimals);
1514
1553
  if (minAmountOut) {
@@ -1802,6 +1841,7 @@ var DEFAULT_SETTINGS = {
1802
1841
  };
1803
1842
  var SwapSdkController = class {
1804
1843
  constructor(options) {
1844
+ this.refreshPairsTimeout = null;
1805
1845
  this.state = {
1806
1846
  loader: null
1807
1847
  };
@@ -1820,11 +1860,16 @@ var SwapSdkController = class {
1820
1860
  this.options.networkConfig,
1821
1861
  this.options.walletProvider
1822
1862
  );
1823
- this.swapService = new SwapService(
1863
+ this.swapService = new (this.options.swapServiceClass || SwapService)(
1824
1864
  this.walletService.getProvider(),
1825
1865
  this.options.networkConfig,
1826
1866
  this.options
1827
1867
  );
1868
+ if (this.options.refreshPairsInterval) {
1869
+ this.swapService.waitForPairsLoaded().finally(() => {
1870
+ this.refreshPairsTimeout = setTimeout(this.refreshTokensAndUpdateQuoteAndSetTimeout.bind(this), this.options.refreshPairsInterval);
1871
+ });
1872
+ }
1828
1873
  }
1829
1874
  async setChange(patch) {
1830
1875
  const next = {
@@ -1873,6 +1918,7 @@ var SwapSdkController = class {
1873
1918
  await this.setChange({
1874
1919
  computed: tradeResult.computed,
1875
1920
  tradeInfo: tradeResult.trade,
1921
+ path: tradeResult.trade.route.path,
1876
1922
  loader: null
1877
1923
  });
1878
1924
  } catch (error) {
@@ -1930,9 +1976,8 @@ var SwapSdkController = class {
1930
1976
  if (!fromToken || !toToken || amount === void 0) throw new Error("Tokens or amount not set");
1931
1977
  await this.approveIfNeeded();
1932
1978
  await this.setChange({ loader: 3 /* SWAPPING */ });
1933
- const trade = this.state.tradeInfo;
1934
- if (!trade) throw new Error("Trade info missing - calculate quote first");
1935
- const path = trade.route.path.map((token) => token.address);
1979
+ if (!this.state.path) throw new Error("Trade info missing - calculate quote first");
1980
+ const path = this.state.path.map((token) => token.address);
1936
1981
  if (path.length === 0) throw new Error("Trade path missing");
1937
1982
  const computed = this.state.computed;
1938
1983
  if (!computed) throw new Error("Computed amounts missing");
@@ -1974,6 +2019,29 @@ var SwapSdkController = class {
1974
2019
  get currentNetworkConfig() {
1975
2020
  return this.options.networkConfig;
1976
2021
  }
2022
+ async destroy() {
2023
+ if (this.walletService) {
2024
+ this.walletService.destroy();
2025
+ }
2026
+ if (this.refreshPairsTimeout) {
2027
+ clearTimeout(this.refreshPairsTimeout);
2028
+ this.refreshPairsTimeout = null;
2029
+ }
2030
+ }
2031
+ async refreshTokensAndUpdateQuote(forceQuoteUpdate = false) {
2032
+ await this.swapService?.refreshPairsFromGraph();
2033
+ if ((this.options.updateQuoteAfterRefreshPairs || forceQuoteUpdate) && !this.state.loader) {
2034
+ await this.calculateQuoteIfNeeded();
2035
+ }
2036
+ }
2037
+ async refreshTokensAndUpdateQuoteAndSetTimeout() {
2038
+ try {
2039
+ await this.refreshTokensAndUpdateQuote();
2040
+ } catch (error) {
2041
+ console.error(error);
2042
+ }
2043
+ this.refreshPairsTimeout = setTimeout(this.refreshTokensAndUpdateQuoteAndSetTimeout.bind(this), this.options.refreshPairsInterval);
2044
+ }
1977
2045
  };
1978
2046
 
1979
2047
  // src/config/networks.ts
@@ -2005,6 +2073,33 @@ var NETWORKS = {
2005
2073
  name: "Kasplex Kaspa",
2006
2074
  symbol: "KAS"
2007
2075
  }
2076
+ },
2077
+ "kasplex": {
2078
+ name: "Kasplex",
2079
+ chainId: 202555,
2080
+ rpcUrl: "https://evmrpc.kasplex.org",
2081
+ routerAddress: "0x3a1f0bD164fe9D8fa18Da5abAB352dC634CA5F10",
2082
+ factoryAddress: "0xa9CBa43A407c9Eb30933EA21f7b9D74A128D613c",
2083
+ proxyAddress: "0x4c5BEaAE83577E3a117ce2F477fC42a1EA39A8a3",
2084
+ graphEndpoint: "https://graph-kasplex.kaspa.com/subgraphs/name/kasplex-v2-core",
2085
+ blockExplorerUrl: "https://explorer.kasplex.org",
2086
+ additionalJsonRpcApiProviderOptionsOptions: {
2087
+ batchMaxCount: 1,
2088
+ batchMaxSize: 1,
2089
+ batchStallTime: 0
2090
+ },
2091
+ wrappedToken: {
2092
+ address: "0x2c2Ae87Ba178F48637acAe54B87c3924F544a83e",
2093
+ decimals: 18,
2094
+ name: "Wrapped KAS",
2095
+ symbol: "WKAS"
2096
+ },
2097
+ nativeToken: {
2098
+ address: ethers2.ZeroAddress,
2099
+ decimals: 18,
2100
+ name: "Kasplex Kaspa",
2101
+ symbol: "KAS"
2102
+ }
2008
2103
  }
2009
2104
  // Add more networks as needed
2010
2105
  };