@subwallet/extension-base 1.3.76-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 (96) hide show
  1. package/background/KoniTypes.d.ts +4 -1
  2. package/cjs/core/logic-validation/index.js +1 -1
  3. package/cjs/core/substrate/xcm-parser.js +17 -11
  4. package/cjs/koni/background/handlers/Extension.js +293 -71
  5. package/cjs/koni/background/handlers/State.js +20 -0
  6. package/cjs/koni/background/handlers/Tabs.js +2 -3
  7. package/cjs/packageInfo.js +1 -1
  8. package/cjs/services/balance-service/helpers/subscribe/evm.js +85 -6
  9. package/cjs/services/balance-service/helpers/subscribe/index.js +2 -1
  10. package/cjs/services/balance-service/index.js +6 -2
  11. package/cjs/services/balance-service/transfer/token.js +15 -0
  12. package/cjs/services/balance-service/transfer/xcm/bittensorBridge/index.js +27 -0
  13. package/cjs/services/balance-service/transfer/xcm/bittensorBridge/nativeTokenBridge.js +58 -0
  14. package/cjs/services/balance-service/transfer/xcm/bittensorBridge/utils.js +36 -0
  15. package/cjs/services/balance-service/transfer/xcm/index.js +61 -2
  16. package/cjs/services/balance-service/transfer/xcm/utils.js +94 -6
  17. package/cjs/services/chain-service/constants.js +4 -2
  18. package/cjs/services/chain-service/utils/patch.js +1 -1
  19. package/cjs/services/earning-service/constants/chains.js +7 -2
  20. package/cjs/services/earning-service/handlers/liquid-staking/stella-swap.js +8 -5
  21. package/cjs/services/earning-service/handlers/special.js +82 -65
  22. package/cjs/services/earning-service/service.js +19 -2
  23. package/cjs/services/multisig-service/index.js +1 -1
  24. package/cjs/services/request-service/handler/SubstrateRequestHandler.js +12 -0
  25. package/cjs/services/request-service/index.js +3 -0
  26. package/cjs/services/setting-service/constants.js +2 -1
  27. package/cjs/services/storage-service/db-stores/InappNotification.js +1 -1
  28. package/cjs/services/swap-service/handler/bittensor-handler.js +197 -0
  29. package/cjs/services/swap-service/index.js +7 -0
  30. package/cjs/services/transaction-service/index.js +10 -0
  31. package/cjs/types/balance/index.js +1 -0
  32. package/cjs/types/swap/index.js +3 -1
  33. package/cjs/utils/fee/transfer.js +20 -5
  34. package/core/logic-validation/index.js +1 -1
  35. package/core/substrate/xcm-parser.d.ts +3 -0
  36. package/core/substrate/xcm-parser.js +14 -11
  37. package/koni/background/handlers/Extension.d.ts +19 -0
  38. package/koni/background/handlers/Extension.js +233 -14
  39. package/koni/background/handlers/State.d.ts +1 -0
  40. package/koni/background/handlers/State.js +20 -0
  41. package/koni/background/handlers/Tabs.js +2 -3
  42. package/package.json +26 -6
  43. package/packageInfo.js +1 -1
  44. package/services/balance-service/helpers/subscribe/evm.d.ts +1 -0
  45. package/services/balance-service/helpers/subscribe/evm.js +76 -1
  46. package/services/balance-service/helpers/subscribe/index.js +2 -1
  47. package/services/balance-service/index.js +6 -2
  48. package/services/balance-service/transfer/token.d.ts +2 -1
  49. package/services/balance-service/transfer/token.js +15 -0
  50. package/services/balance-service/transfer/xcm/bittensorBridge/index.d.ts +2 -0
  51. package/services/balance-service/transfer/xcm/bittensorBridge/index.js +5 -0
  52. package/services/balance-service/transfer/xcm/bittensorBridge/nativeTokenBridge.d.ts +6 -0
  53. package/services/balance-service/transfer/xcm/bittensorBridge/nativeTokenBridge.js +50 -0
  54. package/services/balance-service/transfer/xcm/bittensorBridge/utils.d.ts +8 -0
  55. package/services/balance-service/transfer/xcm/bittensorBridge/utils.js +29 -0
  56. package/services/balance-service/transfer/xcm/index.d.ts +5 -0
  57. package/services/balance-service/transfer/xcm/index.js +57 -2
  58. package/services/balance-service/transfer/xcm/utils.d.ts +3 -2
  59. package/services/balance-service/transfer/xcm/utils.js +87 -1
  60. package/services/chain-service/constants.d.ts +2 -0
  61. package/services/chain-service/constants.js +4 -2
  62. package/services/chain-service/utils/patch.d.ts +1 -1
  63. package/services/chain-service/utils/patch.js +1 -1
  64. package/services/earning-service/constants/chains.d.ts +2 -0
  65. package/services/earning-service/constants/chains.js +4 -1
  66. package/services/earning-service/handlers/liquid-staking/stella-swap.d.ts +1 -0
  67. package/services/earning-service/handlers/liquid-staking/stella-swap.js +6 -4
  68. package/services/earning-service/handlers/special.d.ts +1 -1
  69. package/services/earning-service/handlers/special.js +85 -68
  70. package/services/earning-service/service.js +21 -4
  71. package/services/inapp-notification-service/interfaces.d.ts +1 -0
  72. package/services/multisig-service/index.js +1 -1
  73. package/services/request-service/handler/SubstrateRequestHandler.d.ts +1 -0
  74. package/services/request-service/handler/SubstrateRequestHandler.js +12 -0
  75. package/services/request-service/index.d.ts +1 -0
  76. package/services/request-service/index.js +3 -0
  77. package/services/request-service/types.d.ts +1 -0
  78. package/services/setting-service/constants.js +2 -1
  79. package/services/storage-service/db-stores/InappNotification.js +1 -1
  80. package/services/swap-service/handler/bittensor-handler.d.ts +21 -0
  81. package/services/swap-service/handler/bittensor-handler.js +189 -0
  82. package/services/swap-service/index.js +7 -0
  83. package/services/transaction-service/index.d.ts +1 -1
  84. package/services/transaction-service/index.js +10 -0
  85. package/services/transaction-service/types.d.ts +4 -3
  86. package/types/balance/index.d.ts +3 -1
  87. package/types/balance/index.js +1 -0
  88. package/types/balance/transfer.d.ts +9 -0
  89. package/types/fee/base.d.ts +1 -0
  90. package/types/multisig/index.d.ts +12 -0
  91. package/types/swap/index.d.ts +3 -1
  92. package/types/swap/index.js +3 -1
  93. package/types/yield/actions/join/step.d.ts +6 -0
  94. package/types/yield/actions/join/submit.d.ts +1 -0
  95. package/utils/fee/transfer.d.ts +1 -0
  96. 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);
@@ -6,10 +6,10 @@ import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
6
6
  import { CRON_REFRESH_CHAIN_STAKING_METADATA, CRON_REFRESH_EARNING_REWARD_HISTORY_INTERVAL, CRON_REFRESH_EARNING_TARGETS, CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL } from '@subwallet/extension-base/constants';
7
7
  import { ServiceStatus } from '@subwallet/extension-base/services/base/types';
8
8
  import { _getChainSubstrateTokenSymbol, _isChainEnabled } from '@subwallet/extension-base/services/chain-service/utils';
9
- import { _STAKING_CHAIN_GROUP, STAKING_IDENTITY_API_SLUG } from '@subwallet/extension-base/services/earning-service/constants';
9
+ import { _STAKING_CHAIN_GROUP, STAKING_IDENTITY_API_SLUG, SUNSETTED_YIELD_POOL_SLUGS } from '@subwallet/extension-base/services/earning-service/constants';
10
10
  import BaseLiquidStakingPoolHandler from '@subwallet/extension-base/services/earning-service/handlers/liquid-staking/base';
11
11
  import MythosNativeStakingPoolHandler from '@subwallet/extension-base/services/earning-service/handlers/native-staking/mythos';
12
- import { BasicTxErrorType, YieldPoolType } from '@subwallet/extension-base/types';
12
+ import { BasicTxErrorType, EarningStatus, YieldPoolType } from '@subwallet/extension-base/types';
13
13
  import { addLazy, createPromiseHandler, filterAddressByChainInfo, removeLazy } from '@subwallet/extension-base/utils';
14
14
  import { fetchStaticCache } from '@subwallet/extension-base/utils/fetchStaticCache';
15
15
  import { BehaviorSubject, combineLatest } from 'rxjs';
@@ -432,6 +432,14 @@ export default class EarningService {
432
432
  }
433
433
  const updatedItem = structuredClone(item);
434
434
  updatedItem.metadata.availableMethod = handler.availableMethod;
435
+
436
+ // Disable staking for sunsetted pools
437
+ if (SUNSETTED_YIELD_POOL_SLUGS.includes(item.slug)) {
438
+ updatedItem.metadata.availableMethod = {
439
+ ...updatedItem.metadata.availableMethod,
440
+ join: false
441
+ };
442
+ }
435
443
  this.updateYieldPoolInfo(updatedItem);
436
444
  });
437
445
  return onlineData;
@@ -524,7 +532,11 @@ export default class EarningService {
524
532
  const yieldPositionInfo = this.yieldPositionSubject.getValue();
525
533
  existedYieldPosition.forEach(item => {
526
534
  if (!this.inactivePoolSlug.has(item.slug)) {
527
- yieldPositionInfo[this._getYieldPositionKey(item.slug, item.address)] = item;
535
+ const normalizedItem = SUNSETTED_YIELD_POOL_SLUGS.includes(item.slug) ? {
536
+ ...item,
537
+ status: EarningStatus.NOT_EARNING
538
+ } : item;
539
+ yieldPositionInfo[this._getYieldPositionKey(item.slug, item.address)] = normalizedItem;
528
540
  }
529
541
  });
530
542
  this.yieldPositionSubject.next(yieldPositionInfo);
@@ -547,7 +559,11 @@ export default class EarningService {
547
559
  return `${slug}---${address}`;
548
560
  }
549
561
  updateYieldPosition(data) {
550
- this.yieldPositionPersistQueue.push(data);
562
+ const normalizedData = SUNSETTED_YIELD_POOL_SLUGS.includes(data.slug) ? {
563
+ ...data,
564
+ status: EarningStatus.NOT_EARNING
565
+ } : data;
566
+ this.yieldPositionPersistQueue.push(normalizedData);
551
567
  addLazy('persistYieldPositionInfo', () => {
552
568
  const yieldPositionInfo = this.yieldPositionSubject.getValue();
553
569
  const queue = [...this.yieldPositionPersistQueue];
@@ -855,6 +871,7 @@ export default class EarningService {
855
871
  } = params;
856
872
  const handler = this.getPoolHandler(slug);
857
873
  if (handler) {
874
+ console.log('all step', await handler.generateOptimalPath(params));
858
875
  return handler.generateOptimalPath(params);
859
876
  } else {
860
877
  throw new TransactionError(BasicTxErrorType.INTERNAL_ERROR);
@@ -118,6 +118,7 @@ export interface ShowNotificationPayload {
118
118
  earningWithdraw: boolean;
119
119
  availBridgeClaim: boolean;
120
120
  polygonBridgeClaim: boolean;
121
+ pendingMultisigApprovals: boolean;
121
122
  }
122
123
  export interface NotificationSetup {
123
124
  isEnabled: boolean;
@@ -206,7 +206,7 @@ export class MultisigService {
206
206
  */
207
207
  handleEvents(events, eventTypes) {
208
208
  let needReload = false;
209
- if (eventTypes.includes('account.add') || eventTypes.includes('account.remove')) {
209
+ if (eventTypes.includes('account.add') || eventTypes.includes('account.remove') || eventTypes.includes('account.updateCurrent')) {
210
210
  needReload = true;
211
211
  }
212
212
  if (eventTypes.includes('chain.updateState') || eventTypes.includes('transaction.done')) {
@@ -9,6 +9,7 @@ export default class SubstrateRequestHandler {
9
9
  constructor(requestService: RequestService);
10
10
  getSignRequest(id: string): SignRequest | undefined;
11
11
  get allSubstrateRequests(): SigningRequest[];
12
+ updateSignRequest(id: string, request: RequestSign, signerAddress?: string): void;
12
13
  private updateIconSign;
13
14
  private signComplete;
14
15
  get numSubstrateRequests(): number;
@@ -34,6 +34,18 @@ export default class SubstrateRequestHandler {
34
34
  isInternal: isInternalRequest(url)
35
35
  }));
36
36
  }
37
+ updateSignRequest(id, request, signerAddress) {
38
+ const current = this.getSignRequest(id);
39
+ if (!current) {
40
+ throw new TransactionError(BasicTxErrorType.INVALID_PARAMS, 'Sign request not found');
41
+ }
42
+ this.#substrateRequests[id] = {
43
+ ...current,
44
+ request,
45
+ signerAddress: signerAddress || current.signerAddress
46
+ };
47
+ this.updateIconSign();
48
+ }
37
49
  updateIconSign(shouldClose) {
38
50
  this.signSubject.next(this.allSubstrateRequests);
39
51
  this.#requestService.updateIconV2(shouldClose);
@@ -55,6 +55,7 @@ export default class RequestService {
55
55
  get confirmationsQueueSubjectTon(): BehaviorSubject<ConfirmationsQueueTon>;
56
56
  get confirmationsQueueSubjectCardano(): BehaviorSubject<ConfirmationsQueueCardano>;
57
57
  getSignRequest(id: string): import("./types").SignRequest | undefined;
58
+ updateSignRequest(id: string, request: RequestSign, signerAddress?: string): void;
58
59
  signInternalTransaction(id: string, address: string, url: string, payload: SignerPayloadJSON, onSign?: (id: string) => void, isWrappedTx?: SubstrateTransactionWrappingStatus): Promise<ResponseSigning>;
59
60
  addConfirmation<CT extends ConfirmationType>(id: string, url: string, type: CT, payload: ConfirmationDefinitions[CT][0]['payload'], options?: ConfirmationsQueueItemOptions, validator?: (input: ConfirmationDefinitions[CT][1]) => Error | undefined): Promise<ConfirmationDefinitions[CT][1]>;
60
61
  addConfirmationTon<CT extends ConfirmationTypeTon>(id: string, url: string, type: CT, payload: ConfirmationDefinitionsTon[CT][0]['payload'], options?: ConfirmationsQueueItemOptions, validator?: (input: ConfirmationDefinitionsTon[CT][1]) => Error | undefined): Promise<ConfirmationDefinitionsTon[CT][1]>;
@@ -167,6 +167,9 @@ export default class RequestService {
167
167
  getSignRequest(id) {
168
168
  return this.#substrateRequestHandler.getSignRequest(id);
169
169
  }
170
+ updateSignRequest(id, request, signerAddress) {
171
+ this.#substrateRequestHandler.updateSignRequest(id, request, signerAddress);
172
+ }
170
173
  async signInternalTransaction(id, address, url, payload, onSign, isWrappedTx) {
171
174
  if (isWrappedTx === SubstrateTransactionWrappingStatus.WRAP_RESULT) {
172
175
  return this.#substrateRequestHandler.signWrappedTransaction(id, address, url, payload, onSign);
@@ -2,6 +2,7 @@ import { AccountAuthType, RequestSign, Resolver, ResponseSigning } from '@subwal
2
2
  import { MetadataDef } from '@subwallet/extension-inject/types';
3
3
  export interface SignRequest extends Resolver<ResponseSigning> {
4
4
  address: string;
5
+ signerAddress?: string;
5
6
  id: string;
6
7
  request: RequestSign;
7
8
  url: string;
@@ -23,7 +23,8 @@ export const DEFAULT_NOTIFICATION_SETUP = {
23
23
  earningClaim: true,
24
24
  earningWithdraw: true,
25
25
  availBridgeClaim: true,
26
- polygonBridgeClaim: true
26
+ polygonBridgeClaim: true,
27
+ pendingMultisigApprovals: true
27
28
  // isHideWithdraw: false, // todo: just for test, remove later
28
29
  // isHideMarketing: false,
29
30
  // isHideAnnouncement: false
@@ -82,7 +82,7 @@ export default class InappNotificationStore extends BaseStore {
82
82
  isRead: true
83
83
  });
84
84
  }
85
- return this.table.where('proxyId').equalsIgnoreCase(proxyId).modify({
85
+ return this.table.where('proxyId').equalsIgnoreCase(proxyId).filter(notification => !excludeNotificationIds.includes(notification.id)).modify({
86
86
  isRead: true
87
87
  });
88
88
  }
@@ -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
+ }