@subwallet/extension-base 1.3.77-0 → 1.3.78-0

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.
Files changed (74) hide show
  1. package/background/KoniTypes.d.ts +2 -0
  2. package/cjs/core/logic-validation/index.js +1 -1
  3. package/cjs/core/substrate/xcm-parser.js +10 -1
  4. package/cjs/koni/background/handlers/Extension.js +35 -8
  5. package/cjs/koni/background/handlers/State.js +20 -0
  6. package/cjs/packageInfo.js +1 -1
  7. package/cjs/services/balance-service/helpers/subscribe/evm.js +85 -6
  8. package/cjs/services/balance-service/helpers/subscribe/index.js +2 -1
  9. package/cjs/services/balance-service/index.js +6 -2
  10. package/cjs/services/balance-service/transfer/token.js +15 -0
  11. package/cjs/services/balance-service/transfer/xcm/bittensorBridge/index.js +27 -0
  12. package/cjs/services/balance-service/transfer/xcm/bittensorBridge/nativeTokenBridge.js +58 -0
  13. package/cjs/services/balance-service/transfer/xcm/bittensorBridge/utils.js +36 -0
  14. package/cjs/services/balance-service/transfer/xcm/index.js +61 -2
  15. package/cjs/services/balance-service/transfer/xcm/utils.js +94 -6
  16. package/cjs/services/chain-service/constants.js +4 -2
  17. package/cjs/services/chain-service/utils/patch.js +1 -1
  18. package/cjs/services/earning-service/constants/chains.js +4 -2
  19. package/cjs/services/earning-service/handlers/special.js +82 -65
  20. package/cjs/services/earning-service/service.js +1 -0
  21. package/cjs/services/swap-service/handler/bittensor-handler.js +197 -0
  22. package/cjs/services/swap-service/index.js +7 -0
  23. package/cjs/services/transaction-service/index.js +1 -0
  24. package/cjs/types/balance/index.js +1 -0
  25. package/cjs/types/swap/index.js +3 -1
  26. package/cjs/utils/fee/transfer.js +20 -5
  27. package/core/logic-validation/index.js +1 -1
  28. package/core/substrate/xcm-parser.d.ts +2 -0
  29. package/core/substrate/xcm-parser.js +8 -1
  30. package/koni/background/handlers/Extension.js +36 -9
  31. package/koni/background/handlers/State.d.ts +1 -0
  32. package/koni/background/handlers/State.js +20 -0
  33. package/package.json +26 -6
  34. package/packageInfo.js +1 -1
  35. package/services/balance-service/helpers/subscribe/evm.d.ts +1 -0
  36. package/services/balance-service/helpers/subscribe/evm.js +76 -1
  37. package/services/balance-service/helpers/subscribe/index.js +2 -1
  38. package/services/balance-service/index.js +6 -2
  39. package/services/balance-service/transfer/token.d.ts +2 -1
  40. package/services/balance-service/transfer/token.js +15 -0
  41. package/services/balance-service/transfer/xcm/bittensorBridge/index.d.ts +2 -0
  42. package/services/balance-service/transfer/xcm/bittensorBridge/index.js +5 -0
  43. package/services/balance-service/transfer/xcm/bittensorBridge/nativeTokenBridge.d.ts +6 -0
  44. package/services/balance-service/transfer/xcm/bittensorBridge/nativeTokenBridge.js +50 -0
  45. package/services/balance-service/transfer/xcm/bittensorBridge/utils.d.ts +8 -0
  46. package/services/balance-service/transfer/xcm/bittensorBridge/utils.js +29 -0
  47. package/services/balance-service/transfer/xcm/index.d.ts +5 -0
  48. package/services/balance-service/transfer/xcm/index.js +57 -2
  49. package/services/balance-service/transfer/xcm/utils.d.ts +3 -2
  50. package/services/balance-service/transfer/xcm/utils.js +87 -1
  51. package/services/chain-service/constants.d.ts +2 -0
  52. package/services/chain-service/constants.js +4 -2
  53. package/services/chain-service/utils/patch.d.ts +1 -1
  54. package/services/chain-service/utils/patch.js +1 -1
  55. package/services/earning-service/constants/chains.d.ts +1 -0
  56. package/services/earning-service/constants/chains.js +2 -1
  57. package/services/earning-service/handlers/special.d.ts +1 -1
  58. package/services/earning-service/handlers/special.js +85 -68
  59. package/services/earning-service/service.js +1 -0
  60. package/services/swap-service/handler/bittensor-handler.d.ts +21 -0
  61. package/services/swap-service/handler/bittensor-handler.js +189 -0
  62. package/services/swap-service/index.js +7 -0
  63. package/services/transaction-service/index.js +1 -0
  64. package/services/transaction-service/types.d.ts +4 -3
  65. package/types/balance/index.d.ts +3 -1
  66. package/types/balance/index.js +1 -0
  67. package/types/balance/transfer.d.ts +7 -0
  68. package/types/fee/base.d.ts +1 -0
  69. package/types/swap/index.d.ts +3 -1
  70. package/types/swap/index.js +3 -1
  71. package/types/yield/actions/join/step.d.ts +6 -0
  72. package/types/yield/actions/join/submit.d.ts +1 -0
  73. package/utils/fee/transfer.d.ts +1 -0
  74. package/utils/fee/transfer.js +21 -6
@@ -3,11 +3,12 @@
3
3
 
4
4
  import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
5
5
  import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
6
- import { ALL_ACCOUNT_KEY, XCM_FEE_RATIO, XCM_MIN_AMOUNT_RATIO } from '@subwallet/extension-base/constants';
6
+ import { ALL_ACCOUNT_KEY, XCM_MIN_AMOUNT_RATIO } from '@subwallet/extension-base/constants';
7
7
  import { YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils';
8
- import { createXcmExtrinsicV2 } from '@subwallet/extension-base/services/balance-service/transfer/xcm';
8
+ import { createXcmExtrinsicV2, dryRunXcmExtrinsicV2, getMinXcmTransferableAmount } from '@subwallet/extension-base/services/balance-service/transfer/xcm';
9
9
  import { estimateXcmFee } from '@subwallet/extension-base/services/balance-service/transfer/xcm/utils';
10
- import { _getAssetDecimals, _getAssetExistentialDeposit, _getAssetName, _getAssetSymbol, _getChainNativeTokenSlug, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
10
+ import { _getAssetDecimals, _getAssetExistentialDeposit, _getAssetSymbol, _getChainName, _getChainNativeTokenSlug } from '@subwallet/extension-base/services/chain-service/utils';
11
+ import { MIN_XCM_LIQUID_STAKING_DOT } from '@subwallet/extension-base/services/earning-service/constants';
11
12
  import { BasicTxErrorType, YieldStepType, YieldValidationStatus } from '@subwallet/extension-base/types';
12
13
  import { createPromiseHandler, formatNumber } from '@subwallet/extension-base/utils';
13
14
  import { getId } from '@subwallet/extension-base/utils/getId';
@@ -207,51 +208,51 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
207
208
  address,
208
209
  amount
209
210
  } = params;
210
- const bnAmount = new BN(amount);
211
+ const bnStakeAmount = new BigN(amount);
211
212
  const inputTokenSlug = this.inputAsset; // assume that the pool only has 1 input token, will update later
212
213
  const inputTokenInfo = this.state.getAssetBySlug(inputTokenSlug);
213
214
  const inputTokenBalance = await this.state.balanceService.getTransferableBalance(address, inputTokenInfo.originChain, inputTokenSlug);
214
- const bnInputTokenBalance = new BN(inputTokenBalance.value);
215
- if (!bnInputTokenBalance.gte(bnAmount)) {
215
+ const bnInputTokenBalance = new BigN(inputTokenBalance.value);
216
+ if (amount && !bnInputTokenBalance.gte(bnStakeAmount)) {
216
217
  if (this.altInputAsset) {
217
218
  const altInputTokenSlug = this.altInputAsset;
218
219
  const altInputTokenInfo = this.state.getAssetBySlug(altInputTokenSlug);
219
220
  const altInputTokenBalance = await this.state.balanceService.getTransferableBalance(address, altInputTokenInfo.originChain, altInputTokenSlug);
220
- const bnAltInputTokenBalance = new BN(altInputTokenBalance.value || '0');
221
- if (bnAltInputTokenBalance.gt(BN_ZERO)) {
221
+ const bnAltInputTokenBalance = new BigN(altInputTokenBalance.value || '0');
222
+ if (bnAltInputTokenBalance.gt(BigN(0))) {
222
223
  const altChainInfo = this.state.getChainInfo(altInputTokenInfo.originChain);
223
224
  const symbol = altInputTokenInfo.symbol;
224
225
  const networkName = altChainInfo.name;
225
-
226
- // TODO: calculate fee for destination chain
227
- const xcmFeeInfo = await estimateXcmFee({
226
+ const xcmRequest = {
228
227
  fromChainInfo: altChainInfo,
229
228
  fromTokenInfo: altInputTokenInfo,
230
229
  toChainInfo: this.chainInfo,
231
230
  recipient: address,
232
231
  sender: address,
233
- value: bnAmount.toString()
234
- });
232
+ value: bnStakeAmount.toString()
233
+ };
234
+ const [xcmFeeInfo, _minXcmTransferableAmount] = await Promise.all([estimateXcmFee(xcmRequest), getMinXcmTransferableAmount(xcmRequest)]);
235
235
  if (!xcmFeeInfo) {
236
236
  throw new Error('Error estimating XCM fee');
237
237
  }
238
- const xcmFee = BigN(xcmFeeInfo.origin.fee).multipliedBy(XCM_MIN_AMOUNT_RATIO).toFixed(0, 1);
238
+ const xcmOriginFee = BigN(xcmFeeInfo.origin.fee).toFixed(0, 1);
239
+ const xcmDestinationFee = BigN(xcmFeeInfo.destination.fee).toFixed(0, 1);
239
240
  const fee = {
240
241
  slug: altInputTokenSlug,
241
- amount: xcmFee
242
+ amount: xcmOriginFee
242
243
  };
243
- let bnTransferAmount = bnAmount.sub(bnInputTokenBalance);
244
- if (_isNativeToken(altInputTokenInfo)) {
245
- const bnXcmFee = new BN(fee.amount || 0); // xcm fee is paid in native token but swap token is not always native token
246
-
247
- bnTransferAmount = bnTransferAmount.add(bnXcmFee);
244
+ let sendingValue = bnStakeAmount.minus(bnInputTokenBalance).plus(xcmDestinationFee).toString();
245
+ const minXcmTransferableAmount = _minXcmTransferableAmount || MIN_XCM_LIQUID_STAKING_DOT;
246
+ if (new BigN(minXcmTransferableAmount).gt(sendingValue)) {
247
+ sendingValue = minXcmTransferableAmount;
248
248
  }
249
+ const metadata = {
250
+ sendingValue,
251
+ originTokenInfo: altInputTokenInfo,
252
+ destinationTokenInfo: inputTokenInfo
253
+ };
249
254
  const step = {
250
- metadata: {
251
- sendingValue: bnTransferAmount.toString(),
252
- originTokenInfo: altInputTokenInfo,
253
- destinationTokenInfo: inputTokenInfo
254
- },
255
+ metadata: metadata,
255
256
  name: `Transfer ${symbol} from ${networkName}`,
256
257
  type: YieldStepType.XCM
257
258
  };
@@ -294,31 +295,35 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
294
295
  return Promise.resolve([new TransactionError(BasicTxErrorType.UNSUPPORTED)]);
295
296
  }
296
297
  async validateXcmStep(params, path, bnInputTokenBalance) {
298
+ // todo: BN -> BigN or BigInt
297
299
  const processValidation = {
298
300
  ok: true,
299
301
  status: YieldValidationStatus.OK
300
302
  };
301
- const bnAmount = new BN(params.amount);
302
- const altInputTokenSlug = this.altInputAsset || '';
303
- const altInputTokenInfo = this.state.getAssetBySlug(altInputTokenSlug);
304
- const inputTokenInfo = this.state.getAssetBySlug(this.inputAsset);
305
- const altInputTokenBalance = await this.state.balanceService.getTransferableBalance(params.address, altInputTokenInfo.originChain, altInputTokenSlug);
306
- const missingAmount = bnAmount.sub(bnInputTokenBalance); // TODO: what if input token is not LOCAL ??
307
- const xcmFeeValidate = new BN(path.totalFee[1].amount || '0').mul(new BN(XCM_FEE_RATIO));
308
- const bnAltInputTokenBalance = new BN(altInputTokenBalance.value || '0');
309
- if (!bnAltInputTokenBalance.sub(missingAmount).sub(xcmFeeValidate).gt(BN_ZERO)) {
303
+ const metadata = path.steps[1].metadata;
304
+ const {
305
+ destinationTokenInfo,
306
+ originTokenInfo,
307
+ sendingValue
308
+ } = metadata;
309
+ const originChainInfo = this.state.getChainInfo(originTokenInfo.originChain);
310
+ const originTokenBalance = await this.state.balanceService.getTransferableBalance(params.address, originTokenInfo.originChain, originTokenInfo.slug);
311
+ const bnOriginTokenBalance = new BN(originTokenBalance.value || '0');
312
+ const bnAdjustOriginFee = new BN(path.totalFee[1].amount || '0').mul(new BN(XCM_MIN_AMOUNT_RATIO));
313
+ const bnSendingValue = new BN(sendingValue);
314
+ if (!bnOriginTokenBalance.sub(bnSendingValue).sub(bnAdjustOriginFee).gt(BN_ZERO)) {
310
315
  processValidation.failedStep = path.steps[1];
311
316
  processValidation.ok = false;
312
317
  processValidation.status = YieldValidationStatus.NOT_ENOUGH_BALANCE;
313
- const bnMaxXCM = new BN(altInputTokenBalance.value).sub(xcmFeeValidate);
314
- const inputTokenDecimal = _getAssetDecimals(inputTokenInfo);
315
- const maxBn = bnInputTokenBalance.add(bnAltInputTokenBalance.sub(xcmFeeValidate));
316
- const maxValue = formatNumber(maxBn.toString(), inputTokenInfo.decimals || 0);
317
- const maxXCMValue = formatNumber(bnMaxXCM.toString(), inputTokenDecimal);
318
- const symbol = _getAssetSymbol(altInputTokenInfo);
318
+ const bnMaxXCM = new BN(originTokenBalance.value).sub(bnAdjustOriginFee);
319
+ const destinationTokenDecimal = _getAssetDecimals(destinationTokenInfo);
320
+ const maxBn = bnInputTokenBalance.add(bnOriginTokenBalance.sub(bnAdjustOriginFee));
321
+ const maxValue = formatNumber(maxBn.toString(), destinationTokenInfo.decimals || 0);
322
+ const maxXCMValue = formatNumber(bnMaxXCM.toString(), destinationTokenDecimal);
323
+ const symbol = _getAssetSymbol(originTokenInfo);
319
324
  const inputNetworkName = this.chainInfo.name;
320
- const altNetworkName = _getAssetName(altInputTokenInfo);
321
- const currentValue = formatNumber(bnInputTokenBalance.toString(), inputTokenDecimal);
325
+ const altNetworkName = _getChainName(originChainInfo);
326
+ const currentValue = formatNumber(bnInputTokenBalance.toString(), destinationTokenDecimal);
322
327
  processValidation.message = t('bg.EARNING.services.service.earning.specialHandler.maximumInputExceeded', {
323
328
  replace: {
324
329
  symbol,
@@ -331,6 +336,24 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
331
336
  });
332
337
  return [new TransactionError(YieldValidationStatus.NOT_ENOUGH_BALANCE, processValidation.message, processValidation)];
333
338
  }
339
+ const id = getId();
340
+ const feeInfo = await this.state.feeService.subscribeChainFee(id, originChainInfo.slug, 'substrate');
341
+ const substrateApi = this.state.getSubstrateApi(originChainInfo.slug);
342
+ const xcmRequest = {
343
+ destinationTokenInfo: destinationTokenInfo,
344
+ originTokenInfo: originTokenInfo,
345
+ recipient: params.address,
346
+ sendingValue,
347
+ substrateApi,
348
+ sender: params.address,
349
+ originChain: originChainInfo,
350
+ destinationChain: this.chainInfo,
351
+ feeInfo
352
+ };
353
+ const isDryRunSuccess = await dryRunXcmExtrinsicV2(xcmRequest);
354
+ if (!isDryRunSuccess) {
355
+ return [new TransactionError(BasicTxErrorType.UNABLE_TO_SEND, 'Unable to perform transaction. Select another token or destination chain and try again')];
356
+ }
334
357
  return [];
335
358
  }
336
359
  async validateJoinStep(id, params, path, bnInputTokenBalance, isXcmOk) {
@@ -350,7 +373,7 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
350
373
  const feeTokenInfo = this.state.getAssetBySlug(feeTokenSlug);
351
374
  const inputTokenInfo = this.state.getAssetBySlug(this.inputAsset);
352
375
  const defaultFeeTokenSlug = this.feeAssets[0];
353
- const bnAmount = new BN(params.amount);
376
+ const bnStakeAmount = new BN(params.amount);
354
377
  if (this.feeAssets.length === 1 && feeTokenSlug === defaultFeeTokenSlug) {
355
378
  var _path$totalFee$id;
356
379
  const bnFeeAmount = new BN(((_path$totalFee$id = path.totalFee[id]) === null || _path$totalFee$id === void 0 ? void 0 : _path$totalFee$id.amount) || '0');
@@ -364,13 +387,13 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
364
387
  return [new TransactionError(YieldValidationStatus.NOT_ENOUGH_FEE, processValidation.message, processValidation)];
365
388
  }
366
389
  }
367
- if (!bnAmount.gte(new BN(poolInfo.statistic.earningThreshold.join || '0'))) {
390
+ if (!bnStakeAmount.gte(new BN(poolInfo.statistic.earningThreshold.join || '0'))) {
368
391
  processValidation.failedStep = path.steps[id];
369
392
  processValidation.ok = false;
370
393
  processValidation.status = YieldValidationStatus.NOT_ENOUGH_MIN_JOIN_POOL;
371
394
  return [new TransactionError(YieldValidationStatus.NOT_ENOUGH_MIN_JOIN_POOL, processValidation.message, processValidation)];
372
395
  }
373
- if (!isXcmOk && bnAmount.gt(bnInputTokenBalance)) {
396
+ if (!isXcmOk && bnStakeAmount.gt(bnInputTokenBalance)) {
374
397
  processValidation.failedStep = path.steps[id];
375
398
  processValidation.ok = false;
376
399
  processValidation.status = YieldValidationStatus.NOT_ENOUGH_BALANCE;
@@ -394,8 +417,8 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
394
417
  const balanceService = this.state.balanceService;
395
418
  const inputTokenBalance = await balanceService.getTransferableBalance(params.address, inputTokenInfo.originChain, inputTokenSlug);
396
419
  const bnInputTokenBalance = new BN(inputTokenBalance.value || '0');
397
- const bnAmount = new BN(params.amount);
398
- if (bnAmount.lte(BN_ZERO)) {
420
+ const bnStakeAmount = new BN(params.amount);
421
+ if (bnStakeAmount.lte(BN_ZERO)) {
399
422
  return [new TransactionError(BasicTxErrorType.INVALID_PARAMS, 'Amount must be greater than 0')];
400
423
  }
401
424
  let isXcmOk = false;
@@ -429,31 +452,25 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
429
452
  async handleTokenApproveStep(data, path) {
430
453
  return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED));
431
454
  }
432
- async handleXcmStep(data, path, xcmFee) {
455
+ async handleXcmStep(data, path) {
456
+ const metadata = path.steps[1].metadata;
457
+ const xcmStepFee = path.totalFee[1].amount;
458
+ const address = data.address;
433
459
  const {
434
- address,
435
- amount
436
- } = data;
437
- const destinationTokenSlug = this.inputAsset;
438
- const altInputTokenSlug = this.altInputAsset || '';
439
- const altInputTokenInfo = this.state.getAssetBySlug(altInputTokenSlug);
440
- const originChainInfo = this.state.getChainInfo(altInputTokenInfo.originChain);
460
+ destinationTokenInfo,
461
+ originTokenInfo,
462
+ sendingValue
463
+ } = metadata;
464
+ const originChainInfo = this.state.getChainInfo(originTokenInfo.originChain);
441
465
  const originTokenSlug = _getChainNativeTokenSlug(originChainInfo);
442
- const originTokenInfo = this.state.getAssetBySlug(originTokenSlug);
443
- const destinationTokenInfo = this.state.getAssetBySlug(destinationTokenSlug);
444
466
  const substrateApi = this.state.getSubstrateApi(originChainInfo.slug);
445
- const inputTokenBalance = await this.state.balanceService.getTransferableBalance(address, destinationTokenInfo.originChain, destinationTokenSlug);
446
- const bnInputTokenBalance = new BN(inputTokenBalance.value);
447
- const bnXcmFee = new BN(xcmFee);
448
- const bnAmount = new BN(amount);
449
- const bnTotalAmount = bnAmount.sub(bnInputTokenBalance).add(bnXcmFee);
450
467
  const id = getId();
451
468
  const feeInfo = await this.state.feeService.subscribeChainFee(id, originChainInfo.slug, 'substrate');
452
469
  const xcmRequest = {
453
470
  destinationTokenInfo,
454
471
  originTokenInfo,
455
472
  recipient: address,
456
- sendingValue: bnTotalAmount.toString(),
473
+ sendingValue,
457
474
  substrateApi,
458
475
  sender: address,
459
476
  originChain: originChainInfo,
@@ -469,7 +486,7 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
469
486
  destinationNetworkKey: destinationTokenInfo.originChain,
470
487
  from: address,
471
488
  to: address,
472
- value: bnTotalAmount.toString(),
489
+ value: sendingValue,
473
490
  tokenSlug: originTokenSlug,
474
491
  showExtraWarning: true
475
492
  };
@@ -478,8 +495,9 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
478
495
  extrinsicType: ExtrinsicType.TRANSFER_XCM,
479
496
  extrinsic,
480
497
  txData: xcmData,
481
- transferNativeAmount: bnTotalAmount.toString(),
482
- chainType: ChainType.SUBSTRATE
498
+ transferNativeAmount: sendingValue,
499
+ chainType: ChainType.SUBSTRATE,
500
+ xcmStepFee
483
501
  };
484
502
  }
485
503
  handleYieldJoin(data, path, currentStep) {
@@ -491,8 +509,7 @@ export default class BaseSpecialStakingPoolHandler extends BasePoolHandler {
491
509
  return this.handleTokenApproveStep(data, path);
492
510
  case YieldStepType.XCM:
493
511
  {
494
- const xcmFee = path.totalFee[currentStep].amount || '0';
495
- return this.handleXcmStep(data, path, xcmFee);
512
+ return this.handleXcmStep(data, path);
496
513
  }
497
514
  default:
498
515
  return this.handleSubmitStep(data, path);
@@ -871,6 +871,7 @@ export default class EarningService {
871
871
  } = params;
872
872
  const handler = this.getPoolHandler(slug);
873
873
  if (handler) {
874
+ console.log('all step', await handler.generateOptimalPath(params));
874
875
  return handler.generateOptimalPath(params);
875
876
  } else {
876
877
  throw new TransactionError(BasicTxErrorType.INTERNAL_ERROR);
@@ -0,0 +1,21 @@
1
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
2
+ import { BaseStepDetail, CommonOptimalSwapPath, CommonStepFeeInfo, OptimalSwapPathParamsV2, SwapProviderId, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types';
3
+ import { BalanceService } from '../../balance-service';
4
+ import { ChainService } from '../../chain-service';
5
+ import FeeService from '../../fee-service/service';
6
+ import { SwapBaseInterface } from './base-handler';
7
+ export declare class BittensorSwapHandler implements SwapBaseInterface {
8
+ private swapBaseHandler;
9
+ providerSlug: SwapProviderId;
10
+ isReady: boolean;
11
+ constructor(chainService: ChainService, balanceService: BalanceService, feeService: FeeService, isTestnet: boolean);
12
+ get chainService(): ChainService;
13
+ get providerInfo(): import("@subwallet/extension-base/types").SwapProvider;
14
+ generateOptimalProcessV2(params: OptimalSwapPathParamsV2): Promise<CommonOptimalSwapPath>;
15
+ getSubmitStep(params: OptimalSwapPathParamsV2): Promise<[BaseStepDetail, CommonStepFeeInfo] | undefined>;
16
+ validateSwapProcessV2(params: ValidateSwapProcessParams): Promise<TransactionError[]>;
17
+ handleSwapProcess(params: SwapSubmitParams): Promise<SwapSubmitStepData>;
18
+ handleSubmitStep(params: SwapSubmitParams): Promise<SwapSubmitStepData>;
19
+ private getHotkeysByNetuidDesc;
20
+ private buildSwapCalls;
21
+ }
@@ -0,0 +1,189 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
5
+ import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
6
+ import { BasicTxErrorType, CommonStepType, DynamicSwapType, SwapProviderId, SwapStepType } from '@subwallet/extension-base/types';
7
+ import { _reformatAddressWithChain, toBNString } from '@subwallet/extension-base/utils';
8
+ import BigNumber from 'bignumber.js';
9
+ import { _getAssetDecimals, _isNativeTokenBySlug } from "../../chain-service/utils/index.js";
10
+ import { SwapBaseHandler } from "./base-handler.js";
11
+ export class BittensorSwapHandler {
12
+ isReady = false;
13
+ constructor(chainService, balanceService, feeService, isTestnet) {
14
+ this.swapBaseHandler = new SwapBaseHandler({
15
+ chainService,
16
+ balanceService,
17
+ feeService,
18
+ providerName: isTestnet ? 'Bittensor Testnet' : 'Bittensor',
19
+ providerSlug: isTestnet ? SwapProviderId.BITTENSOR_TESTNET : SwapProviderId.BITTENSOR
20
+ });
21
+ this.providerSlug = isTestnet ? SwapProviderId.BITTENSOR_TESTNET : SwapProviderId.BITTENSOR;
22
+ }
23
+ get chainService() {
24
+ return this.swapBaseHandler.chainService;
25
+ }
26
+ get providerInfo() {
27
+ return this.swapBaseHandler.providerInfo;
28
+ }
29
+ generateOptimalProcessV2(params) {
30
+ return this.swapBaseHandler.generateOptimalProcessV2(params, [this.getSubmitStep.bind(this)]);
31
+ }
32
+ async getSubmitStep(params) {
33
+ if (!params.selectedQuote) {
34
+ return Promise.resolve(undefined);
35
+ }
36
+ const originTokenInfo = this.chainService.getAssetBySlug(params.selectedQuote.pair.from);
37
+ const destinationTokenInfo = this.chainService.getAssetBySlug(params.selectedQuote.pair.to);
38
+ const originChain = this.chainService.getChainInfoByKey(originTokenInfo.originChain);
39
+ const destinationChain = this.chainService.getChainInfoByKey(destinationTokenInfo.originChain);
40
+ const submitStep = {
41
+ name: 'Swap',
42
+ type: SwapStepType.SWAP,
43
+ // @ts-ignore
44
+ metadata: {
45
+ sendingValue: params.request.fromAmount.toString(),
46
+ expectedReceive: params.selectedQuote.toAmount,
47
+ originTokenInfo,
48
+ destinationTokenInfo,
49
+ sender: _reformatAddressWithChain(params.request.address, originChain),
50
+ receiver: _reformatAddressWithChain(params.request.recipient || params.request.address, destinationChain),
51
+ version: 2
52
+ }
53
+ };
54
+ return Promise.resolve([submitStep, params.selectedQuote.feeInfo]);
55
+ }
56
+ async validateSwapProcessV2(params) {
57
+ // todo: recheck address and recipient format in params
58
+ const {
59
+ process,
60
+ selectedQuote
61
+ } = params; // todo: review flow, currentStep param.
62
+
63
+ // todo: validate path with optimalProcess
64
+ // todo: review error message in case many step swap
65
+ if (BigNumber(selectedQuote.fromAmount).lte(0)) {
66
+ return [new TransactionError(BasicTxErrorType.INVALID_PARAMS, 'Amount must be greater than 0')];
67
+ }
68
+ const actionList = JSON.stringify(process.path.map(step => step.action));
69
+ const swap = actionList === JSON.stringify([DynamicSwapType.SWAP]);
70
+ const swapXcm = actionList === JSON.stringify([DynamicSwapType.SWAP, DynamicSwapType.BRIDGE]);
71
+ const xcmSwap = actionList === JSON.stringify([DynamicSwapType.BRIDGE, DynamicSwapType.SWAP]);
72
+ const xcmSwapXcm = actionList === JSON.stringify([DynamicSwapType.BRIDGE, DynamicSwapType.SWAP, DynamicSwapType.BRIDGE]);
73
+ const swapIndex = params.process.steps.findIndex(step => step.type === SwapStepType.SWAP); // todo
74
+
75
+ if (swapIndex <= -1) {
76
+ return [new TransactionError(BasicTxErrorType.INTERNAL_ERROR)];
77
+ }
78
+ if (swap) {
79
+ return this.swapBaseHandler.validateSwapOnlyProcess(params, swapIndex); // todo: create interface for input request
80
+ }
81
+ if (swapXcm) {
82
+ return [new TransactionError(BasicTxErrorType.INTERNAL_ERROR)];
83
+ }
84
+ if (xcmSwap) {
85
+ return [new TransactionError(BasicTxErrorType.INTERNAL_ERROR)];
86
+ }
87
+ if (xcmSwapXcm) {
88
+ return [new TransactionError(BasicTxErrorType.INTERNAL_ERROR)];
89
+ }
90
+ return [new TransactionError(BasicTxErrorType.INTERNAL_ERROR)];
91
+ }
92
+ async handleSwapProcess(params) {
93
+ const {
94
+ currentStep,
95
+ process
96
+ } = params;
97
+ const type = process.steps[currentStep].type;
98
+ switch (type) {
99
+ case CommonStepType.DEFAULT:
100
+ return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED));
101
+ case SwapStepType.SWAP:
102
+ return this.handleSubmitStep(params);
103
+ default:
104
+ return this.handleSubmitStep(params);
105
+ }
106
+ }
107
+ async handleSubmitStep(params) {
108
+ var _fromAsset$metadata, _toAsset$metadata;
109
+ const {
110
+ address,
111
+ quote
112
+ } = params;
113
+ const pair = quote.pair;
114
+ const fromAsset = this.chainService.getAssetBySlug(pair.from);
115
+ const toAsset = this.chainService.getAssetBySlug(pair.to);
116
+ const chainInfo = this.chainService.getChainInfoByKey(fromAsset.originChain);
117
+ const fromNetuid = _isNativeTokenBySlug(fromAsset.slug) ? 0 : (_fromAsset$metadata = fromAsset.metadata) === null || _fromAsset$metadata === void 0 ? void 0 : _fromAsset$metadata.netuid;
118
+ const toNetuid = _isNativeTokenBySlug(toAsset.slug) ? 0 : (_toAsset$metadata = toAsset.metadata) === null || _toAsset$metadata === void 0 ? void 0 : _toAsset$metadata.netuid;
119
+ if (fromNetuid == null || toNetuid == null || fromNetuid === toNetuid) {
120
+ return Promise.reject(new TransactionError(BasicTxErrorType.UNSUPPORTED));
121
+ }
122
+ const txData = {
123
+ address,
124
+ provider: this.providerInfo,
125
+ quote: params.quote,
126
+ slippage: params.slippage,
127
+ process: params.process
128
+ };
129
+ const chainApi = this.chainService.getSubstrateApi(chainInfo.slug);
130
+ const substrateApi = await chainApi.isReady;
131
+ const _stakeInfo = await substrateApi.api.call.stakeInfoRuntimeApi.getStakeInfoForColdkey(address);
132
+ const stakeInfo = _stakeInfo.toPrimitive();
133
+
134
+ // TAO/ALPHA ratio
135
+ const priceRatio = new BigNumber(toBNString(quote.rate, _getAssetDecimals(fromAsset)));
136
+ const limitPrice = priceRatio.multipliedBy(new BigNumber(1).minus(params.slippage)).integerValue(BigNumber.ROUND_DOWN).toFixed();
137
+ const calls = this.buildSwapCalls({
138
+ stakeInfo,
139
+ fromNetuid,
140
+ toNetuid,
141
+ amount: new BigNumber(quote.fromAmount),
142
+ limitPrice
143
+ }, substrateApi.api);
144
+ let extrinsic;
145
+ if (calls.length === 1) {
146
+ extrinsic = calls[0];
147
+ } else {
148
+ extrinsic = substrateApi.api.tx.utility.batchAll(calls);
149
+ }
150
+ return {
151
+ txChain: fromAsset.originChain,
152
+ extrinsic,
153
+ txData,
154
+ extrinsicType: ExtrinsicType.SWAP,
155
+ chainType: ChainType.SUBSTRATE
156
+ // using staked balance so we do not need transferNativeAmount
157
+ };
158
+ }
159
+
160
+ // Sort hotkeys by stake descending
161
+ getHotkeysByNetuidDesc(stakeInfo, fromNetuid) {
162
+ return stakeInfo.filter(i => i.netuid === fromNetuid && new BigNumber(i.stake).gt(0)).sort((a, b) => new BigNumber(b.stake).minus(a.stake).toNumber());
163
+ }
164
+ buildSwapCalls(params, api) {
165
+ const {
166
+ amount,
167
+ fromNetuid,
168
+ limitPrice,
169
+ stakeInfo,
170
+ toNetuid
171
+ } = params;
172
+ const hotkeys = this.getHotkeysByNetuidDesc(stakeInfo, fromNetuid);
173
+ let remaining = amount;
174
+ const calls = [];
175
+ for (const item of hotkeys) {
176
+ if (remaining.lte(0)) {
177
+ break;
178
+ }
179
+ const stake = new BigNumber(item.stake);
180
+ if (stake.lte(0)) {
181
+ continue;
182
+ }
183
+ const swapAmount = BigNumber.minimum(stake, remaining);
184
+ calls.push(api.tx.subtensorModule.swapStakeLimit(item.hotkey, fromNetuid, toNetuid, swapAmount.toFixed(), limitPrice, false));
185
+ remaining = remaining.minus(swapAmount);
186
+ }
187
+ return calls;
188
+ }
189
+ }
@@ -20,6 +20,7 @@ import subwalletApiSdk from '@subwallet-monorepos/subwallet-services-sdk';
20
20
  import BigN from 'bignumber.js';
21
21
  import { t } from 'i18next';
22
22
  import { BehaviorSubject } from 'rxjs';
23
+ import { BittensorSwapHandler } from "./handler/bittensor-handler.js";
23
24
  import { KyberHandler } from "./handler/kyber-handler.js";
24
25
  import { SimpleSwapHandler } from "./handler/simpleswap-handler.js";
25
26
  import { UniswapHandler } from "./handler/uniswap-handler.js";
@@ -289,6 +290,12 @@ export class SwapService {
289
290
  case SwapProviderId.OPTIMEX_TESTNET:
290
291
  this.handlers[providerId] = new OptimexHandler(this.chainService, this.state.balanceService, this.state.feeService, true);
291
292
  break;
293
+ case SwapProviderId.BITTENSOR:
294
+ this.handlers[providerId] = new BittensorSwapHandler(this.chainService, this.state.balanceService, this.state.feeService, false);
295
+ break;
296
+ case SwapProviderId.BITTENSOR_TESTNET:
297
+ this.handlers[providerId] = new BittensorSwapHandler(this.chainService, this.state.balanceService, this.state.feeService, true);
298
+ break;
292
299
  default:
293
300
  throw new Error('Unsupported provider');
294
301
  }
@@ -930,6 +930,7 @@ export default class TransactionService {
930
930
  nonce: nonce !== null && nonce !== void 0 ? nonce : 0,
931
931
  startBlock: startBlock || 0,
932
932
  processId: (_transaction$step3 = transaction.step) === null || _transaction$step3 === void 0 ? void 0 : _transaction$step3.processId,
933
+ crossChainFeeInfo: transaction === null || transaction === void 0 ? void 0 : transaction.xcmDestinationFee,
933
934
  substrateProxyAddresses: []
934
935
  };
935
936
  const substrateProxyHistories = [];
@@ -1,4 +1,4 @@
1
- import { ChainType, ExtrinsicDataTypeMap, ExtrinsicStatus, ExtrinsicType, FeeData, ValidateTransactionResponse } from '@subwallet/extension-base/background/KoniTypes';
1
+ import { AmountData, ChainType, ExtrinsicDataTypeMap, ExtrinsicStatus, ExtrinsicType, FeeData, ValidateTransactionResponse } from '@subwallet/extension-base/background/KoniTypes';
2
2
  import { SignTypedDataMessageV3V4 } from '@subwallet/extension-base/core/logic-validation';
3
3
  import { TonTransactionConfig } from '@subwallet/extension-base/services/balance-service/transfer/ton-transfer';
4
4
  import { UniswapOrderInfo } from '@subwallet/extension-base/services/swap-service/handler/uniswap-handler';
@@ -23,6 +23,7 @@ export interface SWTransactionBase extends ValidateTransactionResponse, Partial<
23
23
  updatedAt: number;
24
24
  estimateFee?: FeeData;
25
25
  xcmFeeDryRun?: string;
26
+ xcmDestinationFee?: AmountData;
26
27
  transaction: any;
27
28
  additionalValidator?: (inputTransaction: SWTransactionResponse) => Promise<void>;
28
29
  eventsHandler?: (eventEmitter: TransactionEmitter) => void;
@@ -75,7 +76,7 @@ export interface SWTransactionEmitter {
75
76
  emitterTransaction?: TransactionEmitter;
76
77
  }
77
78
  declare type SwInputBase = Pick<SWTransactionBase, 'address' | 'url' | 'data' | 'extrinsicType' | 'chain' | 'chainType' | 'ignoreWarnings' | 'transferNativeAmount'> & Partial<Pick<SWTransactionBase, 'additionalValidator' | 'eventsHandler'>>;
78
- export interface SWTransactionInput extends SwInputBase, Partial<Pick<SWTransactionBase, 'estimateFee' | 'signAfterCreate' | 'isPassConfirmation' | 'step' | 'errorOnTimeOut' | 'xcmFeeDryRun' | 'wrappingStatus'>>, TransactionFee {
79
+ export interface SWTransactionInput extends SwInputBase, Partial<Pick<SWTransactionBase, 'estimateFee' | 'signAfterCreate' | 'isPassConfirmation' | 'step' | 'errorOnTimeOut' | 'xcmFeeDryRun' | 'xcmDestinationFee' | 'wrappingStatus'>>, TransactionFee {
79
80
  id?: string;
80
81
  transaction?: SWTransactionBase['transaction'] | null;
81
82
  warnings?: SWTransactionBase['warnings'];
@@ -94,7 +95,7 @@ export interface SWPermitTransactionInput extends Omit<SWTransactionInput, 'tran
94
95
  export interface SWDutchTransactionInput extends Omit<SWTransactionInput, 'transaction'> {
95
96
  transaction?: SWDutchTransaction['transaction'] | null;
96
97
  }
97
- export declare type SWTransactionResponse = SwInputBase & Pick<SWTransactionBase, 'warnings' | 'errors'> & Partial<Pick<SWTransactionBase, 'id' | 'extrinsicHash' | 'status' | 'estimateFee' | 'xcmFeeDryRun' | 'wrappingStatus'>> & TransactionFee & {
98
+ export declare type SWTransactionResponse = SwInputBase & Pick<SWTransactionBase, 'warnings' | 'errors'> & Partial<Pick<SWTransactionBase, 'id' | 'extrinsicHash' | 'status' | 'estimateFee' | 'xcmFeeDryRun' | 'xcmDestinationFee' | 'wrappingStatus'>> & TransactionFee & {
98
99
  processId?: string;
99
100
  };
100
101
  export declare type BitcoinTransactionData = {
@@ -12,7 +12,8 @@ export declare enum BalanceType {
12
12
  TRANSFERABLE = "transferable",
13
13
  TOTAL = "total",
14
14
  TOTAL_MINUS_RESERVED = "totalMinusReserved",
15
- KEEP_ALIVE = "keepAlive"
15
+ KEEP_ALIVE = "keepAlive",
16
+ STAKING = "staking"
16
17
  }
17
18
  /**
18
19
  * Balance info of a token on an address
@@ -62,6 +63,7 @@ export interface SubscribeSubstratePalletBalance extends SubscribeBasePalletBala
62
63
  }
63
64
  export interface SubscribeEvmPalletBalance extends SubscribeBasePalletBalance {
64
65
  evmApi: _EvmApi;
66
+ substrateApiMap?: Record<string, _SubstrateApi>;
65
67
  }
66
68
  export interface SubscribeTonPalletBalance extends SubscribeBasePalletBalance {
67
69
  tonApi: _TonApi;
@@ -18,6 +18,7 @@ export let BalanceType;
18
18
  BalanceType["TOTAL"] = "total";
19
19
  BalanceType["TOTAL_MINUS_RESERVED"] = "totalMinusReserved";
20
20
  BalanceType["KEEP_ALIVE"] = "keepAlive";
21
+ BalanceType["STAKING"] = "staking";
21
22
  })(BalanceType || (BalanceType = {}));
22
23
  /** Balance info of all tokens on an address */
23
24
  // Key is tokenSlug
@@ -9,6 +9,7 @@ export interface RequestSubscribeTransfer extends TransactionFee {
9
9
  token: string;
10
10
  destChain: string;
11
11
  transferAll?: boolean;
12
+ metadata?: Record<string, any>;
12
13
  }
13
14
  export interface ResponseSubscribeTransfer {
14
15
  id: string;
@@ -26,6 +27,11 @@ export interface RequestSubmitTransferWithId extends RequestSubmitTransfer {
26
27
  export interface ResponseSubscribeTransferConfirmation extends Omit<ResponseSubscribeTransfer, 'maxTransferable'> {
27
28
  error?: string;
28
29
  }
30
+ export interface AlphaTokenTransferMetadata {
31
+ netuid: number;
32
+ fromValidator: string;
33
+ toValidator: string;
34
+ }
29
35
  export interface RequestSubmitTransfer extends BaseRequestSign, TransactionFee {
30
36
  chain: string;
31
37
  from: string;
@@ -37,6 +43,7 @@ export interface RequestSubmitTransfer extends BaseRequestSign, TransactionFee {
37
43
  isSubstrateECDSATransaction?: boolean;
38
44
  maxTransferableWithoutFee?: string;
39
45
  maxTransferable?: string;
46
+ metadata?: Record<string, any>;
40
47
  }
41
48
  export interface RequestSubmitSignPsbtTransfer extends BaseRequestSign {
42
49
  id: string;
@@ -5,6 +5,7 @@ export interface BaseFeeInfo {
5
5
  }
6
6
  export interface BaseFeeDetail {
7
7
  estimatedFee: string;
8
+ crossChainFee?: string;
8
9
  }
9
10
  export interface BaseFeeTime {
10
11
  time: number;
@@ -65,7 +65,9 @@ export declare enum SwapProviderId {
65
65
  UNISWAP = "UNISWAP",
66
66
  KYBER = "KYBER",
67
67
  OPTIMEX = "OPTIMEX",
68
- OPTIMEX_TESTNET = "OPTIMEX_TESTNET"
68
+ OPTIMEX_TESTNET = "OPTIMEX_TESTNET",
69
+ BITTENSOR = "BITTENSOR",
70
+ BITTENSOR_TESTNET = "BITTENSOR_TESTNET"
69
71
  }
70
72
  export declare const _SUPPORTED_SWAP_PROVIDERS: SwapProviderId[];
71
73
  export interface SwapProvider {
@@ -42,8 +42,10 @@ export let SwapProviderId;
42
42
  SwapProviderId["KYBER"] = "KYBER";
43
43
  SwapProviderId["OPTIMEX"] = "OPTIMEX";
44
44
  SwapProviderId["OPTIMEX_TESTNET"] = "OPTIMEX_TESTNET";
45
+ SwapProviderId["BITTENSOR"] = "BITTENSOR";
46
+ SwapProviderId["BITTENSOR_TESTNET"] = "BITTENSOR_TESTNET";
45
47
  })(SwapProviderId || (SwapProviderId = {}));
46
- export const _SUPPORTED_SWAP_PROVIDERS = [SwapProviderId.CHAIN_FLIP_TESTNET, SwapProviderId.CHAIN_FLIP_MAINNET, SwapProviderId.HYDRADX_MAINNET, SwapProviderId.POLKADOT_ASSET_HUB, SwapProviderId.KUSAMA_ASSET_HUB, SwapProviderId.SIMPLE_SWAP, SwapProviderId.UNISWAP, SwapProviderId.KYBER, SwapProviderId.OPTIMEX, SwapProviderId.OPTIMEX_TESTNET];
48
+ export const _SUPPORTED_SWAP_PROVIDERS = [SwapProviderId.CHAIN_FLIP_TESTNET, SwapProviderId.CHAIN_FLIP_MAINNET, SwapProviderId.HYDRADX_MAINNET, SwapProviderId.POLKADOT_ASSET_HUB, SwapProviderId.KUSAMA_ASSET_HUB, SwapProviderId.SIMPLE_SWAP, SwapProviderId.UNISWAP, SwapProviderId.KYBER, SwapProviderId.OPTIMEX, SwapProviderId.OPTIMEX_TESTNET, SwapProviderId.BITTENSOR, SwapProviderId.BITTENSOR_TESTNET];
47
49
  // process handling
48
50
  export let SwapFeeType;
49
51
  (function (SwapFeeType) {