@subwallet/extension-base 1.3.27-0 → 1.3.28-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 (45) hide show
  1. package/background/KoniTypes.d.ts +7 -1
  2. package/cjs/constants/index.js +1 -1
  3. package/cjs/koni/background/handlers/Extension.js +13 -0
  4. package/cjs/packageInfo.js +1 -1
  5. package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +89 -33
  6. package/cjs/services/chain-service/constants.js +2 -1
  7. package/cjs/services/chain-service/utils/index.js +11 -2
  8. package/cjs/services/chain-service/utils/patch.js +1 -1
  9. package/cjs/services/earning-service/handlers/base.js +3 -0
  10. package/cjs/services/earning-service/handlers/native-staking/dtao.js +97 -36
  11. package/cjs/services/earning-service/handlers/native-staking/tao.js +34 -4
  12. package/cjs/services/earning-service/service.js +13 -1
  13. package/cjs/services/migration-service/index.js +2 -5
  14. package/cjs/services/migration-service/scripts/index.js +4 -8
  15. package/cjs/services/price-service/coingecko.js +49 -0
  16. package/cjs/services/transaction-service/utils.js +4 -1
  17. package/cjs/utils/asset.js +17 -2
  18. package/constants/index.d.ts +1 -1
  19. package/constants/index.js +1 -1
  20. package/koni/background/handlers/Extension.d.ts +1 -0
  21. package/koni/background/handlers/Extension.js +13 -0
  22. package/package.json +7 -7
  23. package/packageInfo.js +1 -1
  24. package/services/balance-service/helpers/subscribe/substrate/index.js +76 -23
  25. package/services/chain-service/constants.d.ts +1 -0
  26. package/services/chain-service/constants.js +2 -1
  27. package/services/chain-service/utils/index.d.ts +1 -0
  28. package/services/chain-service/utils/index.js +6 -0
  29. package/services/chain-service/utils/patch.js +1 -1
  30. package/services/earning-service/handlers/base.d.ts +3 -2
  31. package/services/earning-service/handlers/base.js +3 -0
  32. package/services/earning-service/handlers/native-staking/dtao.d.ts +7 -2
  33. package/services/earning-service/handlers/native-staking/dtao.js +89 -31
  34. package/services/earning-service/handlers/native-staking/tao.d.ts +4 -1
  35. package/services/earning-service/handlers/native-staking/tao.js +31 -1
  36. package/services/earning-service/service.d.ts +2 -1
  37. package/services/earning-service/service.js +13 -1
  38. package/services/migration-service/index.js +2 -5
  39. package/services/migration-service/scripts/index.js +4 -8
  40. package/services/price-service/coingecko.js +48 -0
  41. package/services/transaction-service/utils.js +4 -1
  42. package/types/yield/actions/join/submit.d.ts +3 -0
  43. package/types/yield/actions/others.d.ts +7 -0
  44. package/utils/asset.d.ts +1 -0
  45. package/utils/asset.js +14 -0
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "./cjs/detectPackage.js"
18
18
  ],
19
19
  "type": "module",
20
- "version": "1.3.27-0",
20
+ "version": "1.3.28-0",
21
21
  "main": "./cjs/index.js",
22
22
  "module": "./index.js",
23
23
  "types": "./index.d.ts",
@@ -2669,13 +2669,13 @@
2669
2669
  "@reduxjs/toolkit": "^1.9.1",
2670
2670
  "@sora-substrate/type-definitions": "^1.17.7",
2671
2671
  "@substrate/connect": "^0.8.9",
2672
- "@subwallet/chain-list": "0.2.102",
2673
- "@subwallet/extension-base": "^1.3.27-0",
2674
- "@subwallet/extension-chains": "^1.3.27-0",
2675
- "@subwallet/extension-dapp": "^1.3.27-0",
2676
- "@subwallet/extension-inject": "^1.3.27-0",
2672
+ "@subwallet/chain-list": "0.2.102-beta.19",
2673
+ "@subwallet/extension-base": "^1.3.28-0",
2674
+ "@subwallet/extension-chains": "^1.3.28-0",
2675
+ "@subwallet/extension-dapp": "^1.3.28-0",
2676
+ "@subwallet/extension-inject": "^1.3.28-0",
2677
2677
  "@subwallet/keyring": "^0.1.9",
2678
- "@subwallet/subwallet-api-sdk": "^1.3.27-0",
2678
+ "@subwallet/subwallet-api-sdk": "^1.3.28-0",
2679
2679
  "@subwallet/ui-keyring": "^0.1.9",
2680
2680
  "@ton/core": "^0.56.3",
2681
2681
  "@ton/crypto": "^3.2.0",
package/packageInfo.js CHANGED
@@ -7,5 +7,5 @@ export const packageInfo = {
7
7
  name: '@subwallet/extension-base',
8
8
  path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto',
9
9
  type: 'esm',
10
- version: '1.3.27-0'
10
+ version: '1.3.28-0'
11
11
  };
@@ -14,9 +14,8 @@ import { _adaptX1Interior } from '@subwallet/extension-base/core/substrate/xcm-p
14
14
  import { getPSP22ContractPromise } from '@subwallet/extension-base/koni/api/contract-handler/wasm';
15
15
  import { getDefaultWeightV2 } from '@subwallet/extension-base/koni/api/contract-handler/wasm/utils';
16
16
  import { _BALANCE_CHAIN_GROUP, _MANTA_ZK_CHAIN_GROUP, _ZK_ASSET_PREFIX } from '@subwallet/extension-base/services/chain-service/constants';
17
- import { _checkSmartContractSupportByChain, _getAssetExistentialDeposit, _getChainExistentialDeposit, _getChainNativeTokenSlug, _getContractAddressOfToken, _getTokenOnChainAssetId, _getTokenOnChainInfo, _getTokenTypesSupportedByChain, _getXcmAssetMultilocation, _isBridgedToken, _isChainEvmCompatible } from '@subwallet/extension-base/services/chain-service/utils';
18
- import { getTaoToAlphaMapping } from '@subwallet/extension-base/services/earning-service/handlers/native-staking/dtao';
19
- import { filterAssetsByChainAndType } from '@subwallet/extension-base/utils';
17
+ import { _checkSmartContractSupportByChain, _getAssetExistentialDeposit, _getAssetNetuid, _getChainExistentialDeposit, _getChainNativeTokenSlug, _getContractAddressOfToken, _getTokenOnChainAssetId, _getTokenOnChainInfo, _getTokenTypesSupportedByChain, _getXcmAssetMultilocation, _isBridgedToken, _isChainEvmCompatible } from '@subwallet/extension-base/services/chain-service/utils';
18
+ import { filterAlphaAssetsByChain, filterAssetsByChainAndType } from '@subwallet/extension-base/utils';
20
19
  import BigN from 'bignumber.js';
21
20
  import { subscribeERC20Interval } from "../evm.js";
22
21
  import { subscribeEquilibriumTokenBalance } from "./equilibrium.js";
@@ -29,6 +28,7 @@ export const subscribeSubstrateBalance = async (addresses, chainInfo, assetMap,
29
28
  let unsubBridgedToken;
30
29
  let unsubGrcToken;
31
30
  let unsubVftToken;
31
+ let unsubSubnetAlphaToken;
32
32
  const chain = chainInfo.slug;
33
33
  const baseParams = {
34
34
  addresses,
@@ -65,6 +65,9 @@ export const subscribeSubstrateBalance = async (addresses, chainInfo, assetMap,
65
65
  if (_BALANCE_CHAIN_GROUP.supportBridged.includes(chain)) {
66
66
  unsubBridgedToken = await subscribeForeignAssetBalance(substrateParams);
67
67
  }
68
+ if (_BALANCE_CHAIN_GROUP.bittensor.includes(chain)) {
69
+ unsubSubnetAlphaToken = await subscribeSubnetAlphaPallet(substrateParams);
70
+ }
68
71
 
69
72
  /**
70
73
  * Some substrate chain use evm account format but not have evm connection and support ERC20 contract,
@@ -93,7 +96,7 @@ export const subscribeSubstrateBalance = async (addresses, chainInfo, assetMap,
93
96
  console.warn(err);
94
97
  }
95
98
  return () => {
96
- var _unsubGrcToken, _unsubVftToken;
99
+ var _unsubGrcToken, _unsubVftToken, _unsubSubnetAlphaToke;
97
100
  unsubNativeToken && unsubNativeToken();
98
101
  unsubLocalToken && unsubLocalToken();
99
102
  unsubEvmContractToken && unsubEvmContractToken();
@@ -101,6 +104,7 @@ export const subscribeSubstrateBalance = async (addresses, chainInfo, assetMap,
101
104
  unsubBridgedToken && unsubBridgedToken();
102
105
  (_unsubGrcToken = unsubGrcToken) === null || _unsubGrcToken === void 0 ? void 0 : _unsubGrcToken();
103
106
  (_unsubVftToken = unsubVftToken) === null || _unsubVftToken === void 0 ? void 0 : _unsubVftToken();
107
+ (_unsubSubnetAlphaToke = unsubSubnetAlphaToken) === null || _unsubSubnetAlphaToke === void 0 ? void 0 : _unsubSubnetAlphaToke();
104
108
  };
105
109
  };
106
110
 
@@ -130,27 +134,19 @@ const subscribeWithSystemAccountPallet = async ({
130
134
  args: addresses
131
135
  });
132
136
  }
133
- let bittensorStakingBalances = new Array(addresses.length).fill(new BigN(0));
134
- if (['bittensor', 'bittensor_testnet'].includes(chainInfo.slug)) {
135
- bittensorStakingBalances = await Promise.all(addresses.map(async address => {
136
- const stakeInfo = (await substrateApi.api.call.stakeInfoRuntimeApi.getStakeInfoForColdkey(address)).toJSON();
137
- const price = await getTaoToAlphaMapping(substrateApi);
138
- let TaoTotalStake = new BigN(0);
139
- if (stakeInfo) {
140
- for (const validator of Object.values(stakeInfo)) {
141
- const stake = new BigN(validator.stake);
142
- const netuid = validator.netuid;
143
- const taoToAlphaPrice = price[netuid] ? new BigN(price[netuid]) : new BigN(1);
144
- const taoStake = stake.multipliedBy(taoToAlphaPrice).toFixed(0).toString();
145
- TaoTotalStake = TaoTotalStake.plus(taoStake);
146
- }
147
- }
148
- return new BigN(TaoTotalStake.toString());
149
- }));
150
- }
151
- const subscription = substrateApi.subscribeDataWithMulti(params, rs => {
137
+
138
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
139
+ const subscription = substrateApi.subscribeDataWithMulti(params, async rs => {
152
140
  const balances = rs[systemAccountKey];
153
141
  const poolMemberInfos = rs[poolMembersKey];
142
+ let bittensorStakingBalances = new Array(addresses.length).fill(new BigN(0));
143
+ if (_BALANCE_CHAIN_GROUP.bittensor.includes(chainInfo.slug)) {
144
+ const rawData = await substrateApi.api.call.stakeInfoRuntimeApi.getStakeInfoForColdkeys(addresses);
145
+ const values = rawData.toPrimitive();
146
+ bittensorStakingBalances = values.map(([, stakes]) => {
147
+ return stakes.filter(i => i.netuid === 0).reduce((previousValue, currentValue) => previousValue.plus(currentValue.stake), BigN(0));
148
+ });
149
+ }
154
150
  const items = balances.map((_balance, index) => {
155
151
  const balanceInfo = _balance;
156
152
  const transferableBalance = _getSystemPalletTransferable(balanceInfo, _getChainExistentialDeposit(chainInfo), extrinsicType);
@@ -476,4 +472,61 @@ const subscribeOrmlTokensPallet = async ({
476
472
  unsub && unsub.unsubscribe();
477
473
  });
478
474
  };
475
+ };
476
+
477
+ // eslint-disable-next-line @typescript-eslint/require-await
478
+ const subscribeSubnetAlphaPallet = async ({
479
+ addresses,
480
+ assetMap,
481
+ callback,
482
+ chainInfo,
483
+ substrateApi
484
+ }) => {
485
+ let cancel = false;
486
+ const tokenMap = filterAlphaAssetsByChain(assetMap, chainInfo.slug);
487
+ const getTokenBalances = async () => {
488
+ if (cancel) {
489
+ return;
490
+ }
491
+ const rawData = await substrateApi.api.call.stakeInfoRuntimeApi.getStakeInfoForColdkeys(addresses);
492
+ const values = rawData.toPrimitive();
493
+ const converted = {};
494
+ for (let i = 0; i < values.length; i++) {
495
+ const [, stakes] = values[i];
496
+ const address = addresses[i];
497
+ converted[address] = {};
498
+ stakes.forEach(stakeInfo => {
499
+ const {
500
+ netuid,
501
+ stake
502
+ } = stakeInfo;
503
+ const currentValue = converted[address][netuid] || BigN(0);
504
+ converted[address][netuid] = currentValue.plus(stake);
505
+ });
506
+ }
507
+ for (const chainAsset of Object.values(tokenMap)) {
508
+ const netuid = _getAssetNetuid(chainAsset);
509
+ const items = Object.entries(converted).map(([address, stakeMap]) => {
510
+ const value = stakeMap[netuid] || BigN(0);
511
+ return {
512
+ address: address,
513
+ tokenSlug: chainAsset.slug,
514
+ state: APIItemState.READY,
515
+ free: value.toFixed(0),
516
+ locked: '0'
517
+ };
518
+ });
519
+ if (!cancel) {
520
+ callback(items);
521
+ }
522
+ }
523
+ };
524
+ getTokenBalances().catch(console.error);
525
+ const interval = setInterval(() => {
526
+ getTokenBalances().catch(console.error);
527
+ }, SUB_TOKEN_REFRESH_BALANCE_INTERVAL);
528
+ return () => {
529
+ cancel = true;
530
+ clearInterval(interval);
531
+ };
479
532
  };
@@ -20,6 +20,7 @@ export declare const _BALANCE_CHAIN_GROUP: {
20
20
  kusama: string[];
21
21
  centrifuge: string[];
22
22
  supportBridged: string[];
23
+ bittensor: string[];
23
24
  };
24
25
  export declare const _BALANCE_TOKEN_GROUP: {
25
26
  crab: string[];
@@ -34,7 +34,8 @@ export const _BALANCE_CHAIN_GROUP = {
34
34
  kusama: ['kusama', 'kintsugi', 'kintsugi_test', 'interlay', 'acala', 'statemint', 'karura', 'bifrost'],
35
35
  // perhaps there are some runtime updates
36
36
  centrifuge: ['centrifuge'],
37
- supportBridged: ['rococo_assethub', 'statemint', 'statemine', 'polimec']
37
+ supportBridged: ['rococo_assethub', 'statemint', 'statemine', 'polimec'],
38
+ bittensor: ['bittensor', 'bittensor_testnet']
38
39
  };
39
40
  export const _BALANCE_TOKEN_GROUP = {
40
41
  crab: ['CKTON', 'PKTON'],
@@ -116,4 +116,5 @@ export declare function updateLatestChainInfo(currentDataMap: _DataMap, latestCh
116
116
  needUpdateChainApiList: _ChainInfo[];
117
117
  };
118
118
  export declare const _chainInfoToChainType: (chainInfo: _ChainInfo) => AccountChainType;
119
+ export declare const _getAssetNetuid: (assetInfo: _ChainAsset) => number;
119
120
  export * from './patch';
@@ -577,4 +577,10 @@ export const _chainInfoToChainType = chainInfo => {
577
577
  }
578
578
  return AccountChainType.SUBSTRATE;
579
579
  };
580
+ export const _getAssetNetuid = assetInfo => {
581
+ var _assetInfo$metadata$n, _assetInfo$metadata;
582
+ // @ts-ignore
583
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
584
+ return (_assetInfo$metadata$n = (_assetInfo$metadata = assetInfo.metadata) === null || _assetInfo$metadata === void 0 ? void 0 : _assetInfo$metadata.netuid) !== null && _assetInfo$metadata$n !== void 0 ? _assetInfo$metadata$n : -1;
585
+ };
580
586
  export * from "./patch.js";
@@ -5,7 +5,7 @@ const PRODUCTION_BRANCHES = ['master', 'webapp', 'webapp-dev'];
5
5
  const branchName = process.env.BRANCH_NAME || 'subwallet-dev';
6
6
  const fetchDomain = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'https://chain-list-assets.subwallet.app' : 'https://dev.sw-chain-list-assets.pages.dev';
7
7
  const fetchFile = PRODUCTION_BRANCHES.indexOf(branchName) > -1 ? 'list.json' : 'preview.json';
8
- const ChainListVersion = '0.2.101'; // update this when build chainlist
8
+ const ChainListVersion = '0.2.102'; // update this when build chainlist
9
9
 
10
10
  // todo: move this interface to chainlist
11
11
 
@@ -3,7 +3,7 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
3
3
  import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
4
4
  import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
5
5
  import { _EvmApi, _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
6
- import { BasePoolInfo, BaseYieldPoolMetadata, EarningRewardHistoryItem, EarningRewardItem, HandleYieldStepData, OptimalYieldPath, OptimalYieldPathParams, RequestEarlyValidateYield, ResponseEarlyValidateYield, StakeCancelWithdrawalParams, SubmitYieldJoinData, TransactionData, UnstakingInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPoolTarget, YieldPoolType, YieldPositionInfo, YieldStepBaseInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
6
+ import { BasePoolInfo, BaseYieldPoolMetadata, EarningRewardHistoryItem, EarningRewardItem, HandleYieldStepData, OptimalYieldPath, OptimalYieldPathParams, RequestEarlyValidateYield, RequestEarningSlippage, ResponseEarlyValidateYield, StakeCancelWithdrawalParams, SubmitYieldJoinData, TransactionData, UnstakingInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPoolTarget, YieldPoolType, YieldPositionInfo, YieldStepBaseInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
7
7
  /**
8
8
  * @class BasePoolHandler
9
9
  * @description Base pool handler
@@ -99,7 +99,7 @@ export default abstract class BasePoolHandler {
99
99
  /** Create `transaction` to join the pool step-by-step */
100
100
  abstract handleYieldJoin(data: SubmitYieldJoinData, path: OptimalYieldPath, currentStep: number): Promise<HandleYieldStepData>;
101
101
  /** Validate param to leave the pool */
102
- abstract validateYieldLeave(amount: string, address: string, fastLeave: boolean, selectedTarget?: string, slug?: string): Promise<TransactionError[]>;
102
+ abstract validateYieldLeave(amount: string, address: string, fastLeave: boolean, selectedTarget?: string, slug?: string, poolInfo?: YieldPoolInfo): Promise<TransactionError[]>;
103
103
  /** Create `transaction` to leave the pool normal (default unstake) */
104
104
  protected abstract handleYieldUnstake(amount: string, address: string, selectedTarget?: string, netuid?: number): Promise<[ExtrinsicType, TransactionData]>;
105
105
  /** Create `transaction` to leave the pool fast (swap token) */
@@ -114,4 +114,5 @@ export default abstract class BasePoolHandler {
114
114
  abstract handleYieldClaimReward(address: string, bondReward?: boolean): Promise<TransactionData>;
115
115
  /** Check handler can handle slug */
116
116
  canHandleSlug(slug: string): boolean;
117
+ getEarningSlippage(params: RequestEarningSlippage): Promise<number>;
117
118
  }
@@ -267,5 +267,8 @@ export default class BasePoolHandler {
267
267
  canHandleSlug(slug) {
268
268
  return this.slug === slug;
269
269
  }
270
+ getEarningSlippage(params) {
271
+ return Promise.resolve(0);
272
+ }
270
273
  /* Other actions */
271
274
  }
@@ -1,9 +1,10 @@
1
1
  import { _ChainInfo } from '@subwallet/chain-list/types';
2
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
2
3
  import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
3
4
  import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
4
5
  import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
5
6
  import BaseParaStakingPoolHandler from '@subwallet/extension-base/services/earning-service/handlers/native-staking/base-para';
6
- import { BaseYieldPositionInfo, StakeCancelWithdrawalParams, SubmitJoinNativeStaking, TransactionData, UnstakingInfo, ValidatorInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPoolType, YieldPositionInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
7
+ import { BaseYieldPositionInfo, OptimalYieldPath, RequestEarningSlippage, StakeCancelWithdrawalParams, SubmitJoinNativeStaking, TransactionData, UnstakingInfo, ValidatorInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPoolType, YieldPositionInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
7
8
  import { BigNumber } from 'bignumber.js';
8
9
  export interface SubnetData {
9
10
  netuid: number;
@@ -35,7 +36,8 @@ export interface TestnetBittensorDelegateInfo {
35
36
  nominators: Nominators;
36
37
  returnPer1000: number;
37
38
  }
38
- export declare const getTaoToAlphaMapping: (substrateApi: _SubstrateApi) => Promise<Record<number, string>>;
39
+ export declare const DEFAULT_DTAO_MINBOND = "600000";
40
+ export declare const getAlphaToTaoMapping: (substrateApi: _SubstrateApi) => Promise<Record<number, string>>;
39
41
  export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHandler {
40
42
  handleYieldWithdraw(address: string, unstakingInfo: UnstakingInfo): Promise<TransactionData>;
41
43
  handleYieldCancelUnstake(params: StakeCancelWithdrawalParams): Promise<TransactionData>;
@@ -49,6 +51,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
49
51
  readonly availableMethod: YieldPoolMethodInfo;
50
52
  constructor(state: KoniState, chain: string);
51
53
  canHandleSlug(slug: string): boolean;
54
+ getEarningSlippage(params: RequestEarningSlippage): Promise<number>;
52
55
  get maintainBalance(): string;
53
56
  private init;
54
57
  protected getDescription(): string;
@@ -59,6 +62,8 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
59
62
  private getMainnetPoolTargets;
60
63
  getPoolTargets(): Promise<ValidatorInfo[]>;
61
64
  createJoinExtrinsic(data: SubmitJoinNativeStaking, positionInfo?: YieldPositionInfo, bondDest?: string): Promise<[TransactionData, YieldTokenBaseInfo]>;
65
+ validateYieldJoin(data: SubmitJoinNativeStaking, path: OptimalYieldPath): Promise<TransactionError[]>;
62
66
  handleYieldUnstake(amount: string, address: string, selectedTarget?: string, netuid?: number): Promise<[ExtrinsicType, TransactionData]>;
67
+ validateYieldLeave(amount: string, address: string, fastLeave: boolean, selectedTarget?: string, slug?: string, poolInfo?: YieldPoolInfo): Promise<TransactionError[]>;
63
68
  }
64
69
  export {};
@@ -5,14 +5,18 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
5
5
  import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
6
6
  import { BITTENSOR_REFRESH_STAKE_APY, BITTENSOR_REFRESH_STAKE_INFO } from '@subwallet/extension-base/constants';
7
7
  import { getEarningStatusByNominations } from '@subwallet/extension-base/koni/api/staking/bonding/utils';
8
+ import { _getAssetDecimals, _getAssetSymbol } from '@subwallet/extension-base/services/chain-service/utils';
8
9
  import BaseParaStakingPoolHandler from '@subwallet/extension-base/services/earning-service/handlers/native-staking/base-para';
9
10
  import { BasicTxErrorType, EarningStatus, YieldPoolType } from '@subwallet/extension-base/types';
10
- import { reformatAddress } from '@subwallet/extension-base/utils';
11
- import BigN from 'bignumber.js';
11
+ import { formatNumber, reformatAddress } from '@subwallet/extension-base/utils';
12
+ import BigN, { BigNumber } from 'bignumber.js';
13
+ import { t } from 'i18next';
12
14
  import { BN, BN_TEN, BN_ZERO } from '@polkadot/util';
13
15
  import { calculateReward } from "../../utils/index.js";
14
16
  import { BittensorCache } from "./tao.js";
15
- export const getTaoToAlphaMapping = async substrateApi => {
17
+ const DEFAULT_BITTENSOR_SLIPPAGE = 0.005;
18
+ export const DEFAULT_DTAO_MINBOND = '600000';
19
+ export const getAlphaToTaoMapping = async substrateApi => {
16
20
  const allSubnets = (await substrateApi.api.call.subnetInfoRuntimeApi.getAllDynamicInfo()).toJSON();
17
21
  if (!allSubnets) {
18
22
  return {};
@@ -66,6 +70,30 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
66
70
  canHandleSlug(slug) {
67
71
  return slug.startsWith(`${this.slug}__`);
68
72
  }
73
+ async getEarningSlippage(params) {
74
+ const substrateApi = await this.substrateApi.isReady;
75
+ const subnetInfo = (await substrateApi.api.call.subnetInfoRuntimeApi.getDynamicInfo(params.netuid)).toJSON();
76
+ const alphaIn = new BigNumber((subnetInfo === null || subnetInfo === void 0 ? void 0 : subnetInfo.alphaIn) || 0);
77
+ const taoIn = new BigNumber((subnetInfo === null || subnetInfo === void 0 ? void 0 : subnetInfo.taoIn) || 0);
78
+ const k = alphaIn.multipliedBy(taoIn);
79
+ const value = new BigNumber(params.value);
80
+ if (params.type === ExtrinsicType.STAKING_BOND) {
81
+ const newTaoIn = taoIn.plus(value);
82
+ const newAlphaIn = k.dividedBy(newTaoIn);
83
+ const alphaReturned = alphaIn.minus(newAlphaIn);
84
+ const alphaIdeal = value.multipliedBy(alphaIn).dividedBy(taoIn);
85
+ const slippage = alphaIdeal.minus(alphaReturned).dividedBy(alphaIdeal);
86
+ return slippage.toNumber();
87
+ } else if (params.type === ExtrinsicType.STAKING_UNBOND) {
88
+ const newAlphaIn = alphaIn.plus(value);
89
+ const newTaoReserve = k.dividedBy(newAlphaIn);
90
+ const taoReturned = taoIn.minus(newTaoReserve);
91
+ const taoIdeal = value.multipliedBy(taoIn).dividedBy(alphaIn);
92
+ const slippage = taoIdeal.minus(taoReturned).dividedBy(taoIdeal);
93
+ return slippage.toNumber();
94
+ }
95
+ return 0;
96
+ }
69
97
  get maintainBalance() {
70
98
  const ed = new BN(this.nativeToken.minAmount || '0');
71
99
  const calculateMaintainBalance = new BN(15).mul(ed).div(BN_TEN);
@@ -74,6 +102,9 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
74
102
  }
75
103
  async init() {
76
104
  try {
105
+ if (this.isInit || !this.substrateApi) {
106
+ return;
107
+ }
77
108
  const substrateApi = await this.substrateApi.isReady;
78
109
  const dynamicInfo = (await substrateApi.api.call.subnetInfoRuntimeApi.getAllDynamicInfo()).toJSON();
79
110
  const subnetsInfo = (await substrateApi.api.call.subnetInfoRuntimeApi.getSubnetsInfoV2()).toJSON();
@@ -110,18 +141,14 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
110
141
  /* Subscribe pool info */
111
142
 
112
143
  async subscribePoolInfo(callback) {
113
- if (!this.isInit) {
114
- await this.init();
115
- }
144
+ await this.init();
116
145
  let cancel = false;
117
- const substrateApi = await this.substrateApi.isReady;
118
146
  const updateStakingInfo = async () => {
147
+ await this.substrateApi.isReady;
119
148
  try {
120
149
  if (cancel) {
121
150
  return;
122
151
  }
123
- const minDelegatorStake = (await substrateApi.api.query.subtensorModule.nominatorMinRequiredStake()).toPrimitive() || 0;
124
- const BNminDelegatorStake = new BigN(minDelegatorStake.toString());
125
152
  this.subnetData.forEach(subnet => {
126
153
  const netuid = subnet.netuid.toString().padStart(2, '0');
127
154
  const subnetSlug = `${this.slug}__subnet_${netuid.padStart(2, '0')}`;
@@ -148,7 +175,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
148
175
  maxCandidatePerFarmer: subnet.maxAllowedValidators,
149
176
  maxWithdrawalRequestPerFarmer: 1,
150
177
  earningThreshold: {
151
- join: BNminDelegatorStake.toString(),
178
+ join: DEFAULT_DTAO_MINBOND,
152
179
  defaultUnstake: '0',
153
180
  fastUnstake: '0'
154
181
  },
@@ -167,7 +194,6 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
167
194
  const subscribeStakingMetadataInterval = () => {
168
195
  updateStakingInfo().catch(console.error);
169
196
  };
170
- await substrateApi.isReady;
171
197
  subscribeStakingMetadataInterval();
172
198
  const interval = setInterval(subscribeStakingMetadataInterval, BITTENSOR_REFRESH_STAKE_APY);
173
199
  return () => {
@@ -179,9 +205,8 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
179
205
  /* Subscribe pool position */
180
206
 
181
207
  async parseNominatorMetadata(chainInfo, address, delegatorState) {
208
+ await this.substrateApi.isReady;
182
209
  const nominationList = [];
183
- const getMinDelegatorStake = this.substrateApi.api.query.subtensorModule.nominatorMinRequiredStake();
184
- const minDelegatorStake = (await getMinDelegatorStake).toString();
185
210
  let allActiveStake = BN_ZERO;
186
211
  for (const delegate of delegatorState) {
187
212
  const stake = new BigN(delegate.amount);
@@ -195,7 +220,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
195
220
  chain: chainInfo.slug,
196
221
  validatorAddress: delegate.owner,
197
222
  activeStake: delegate.amount,
198
- validatorMinStake: minDelegatorStake,
223
+ validatorMinStake: DEFAULT_DTAO_MINBOND,
199
224
  originActiveStake: originActiveStake,
200
225
  validatorIdentity: delegate.identity
201
226
  });
@@ -214,9 +239,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
214
239
  };
215
240
  }
216
241
  async subscribePoolPosition(useAddresses, rsCallback) {
217
- if (!this.isInit) {
218
- await this.init();
219
- }
242
+ await this.init();
220
243
  let cancel = false;
221
244
  const substrateApi = await this.substrateApi.isReady;
222
245
  const defaultInfo = this.baseInfo;
@@ -224,7 +247,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
224
247
  const _delegateInfo = await this.bittensorCache.get();
225
248
  const getPoolPosition = async () => {
226
249
  const rawDelegateStateInfos = await Promise.all(useAddresses.map(async address => (await substrateApi.api.call.stakeInfoRuntimeApi.getStakeInfoForColdkey(address)).toJSON()));
227
- const price = await getTaoToAlphaMapping(this.substrateApi);
250
+ const price = await getAlphaToTaoMapping(this.substrateApi);
228
251
  if (rawDelegateStateInfos && rawDelegateStateInfos.length > 0) {
229
252
  rawDelegateStateInfos.forEach((rawDelegateStateInfo, i) => {
230
253
  const owner = reformatAddress(useAddresses[i], 42);
@@ -234,7 +257,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
234
257
  const hotkey = delegate.hotkey;
235
258
  const netuid = delegate.netuid;
236
259
  const stake = new BigN(delegate.stake);
237
- const taoToAlphaPrice = new BigN(price[netuid]);
260
+ const aplhaToTaoPrice = new BigN(price[netuid]);
238
261
  if (!subnetPositions[netuid]) {
239
262
  subnetPositions[netuid] = {
240
263
  delegatorState: [],
@@ -250,7 +273,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
250
273
  subnetPositions[netuid].delegatorState.push({
251
274
  owner: hotkey,
252
275
  amount: stake.toString(),
253
- rate: taoToAlphaPrice,
276
+ rate: aplhaToTaoPrice,
254
277
  identity: identity
255
278
  });
256
279
  subnetPositions[netuid].totalBalance = subnetPositions[netuid].totalBalance.add(new BN(stake.toString()));
@@ -327,9 +350,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
327
350
  // eslint-disable-next-line @typescript-eslint/require-await
328
351
  async getDevnetPoolTargets() {
329
352
  const testnetDelegate = (await this.substrateApi.api.call.delegateInfoRuntimeApi.getDelegates()).toJSON();
330
- const getNominatorMinRequiredStake = this.substrateApi.api.query.subtensorModule.nominatorMinRequiredStake();
331
- const nominatorMinRequiredStake = (await getNominatorMinRequiredStake).toString();
332
- const bnMinBond = new BN(nominatorMinRequiredStake);
353
+ const bnMinBond = new BN(DEFAULT_DTAO_MINBOND);
333
354
  const filteredDelegates = testnetDelegate.filter(delegate => {
334
355
  return delegate.returnPer1000 !== 0;
335
356
  });
@@ -350,9 +371,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
350
371
  async getMainnetPoolTargets() {
351
372
  const _topValidator = await this.bittensorCache.get();
352
373
  const topValidator = _topValidator;
353
- const getNominatorMinRequiredStake = this.substrateApi.api.query.subtensorModule.nominatorMinRequiredStake();
354
- const nominatorMinRequiredStake = (await getNominatorMinRequiredStake).toString();
355
- const bnMinBond = new BN(nominatorMinRequiredStake);
374
+ const bnMinBond = new BN(DEFAULT_DTAO_MINBOND);
356
375
  const validatorList = topValidator.data;
357
376
  const validatorAddresses = Object.keys(validatorList);
358
377
  const results = await Promise.all(validatorAddresses.map(i => {
@@ -385,9 +404,7 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
385
404
  return results;
386
405
  }
387
406
  async getPoolTargets() {
388
- if (!this.isInit) {
389
- await this.init();
390
- }
407
+ await this.init();
391
408
  if (this.chain === 'bittensor') {
392
409
  return this.getMainnetPoolTargets();
393
410
  } else {
@@ -407,14 +424,31 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
407
424
  } = data;
408
425
  const chainApi = await this.substrateApi.isReady;
409
426
  const binaryAmount = new BN(amount);
427
+ const price = await getAlphaToTaoMapping(this.substrateApi);
428
+ const alphaToTaoPrice = new BigN(price[netuid]);
429
+ const limitPrice = alphaToTaoPrice.multipliedBy(10 ** _getAssetDecimals(this.nativeToken)).multipliedBy(1 + DEFAULT_BITTENSOR_SLIPPAGE);
430
+ const BNlimitPrice = new BN(limitPrice.integerValue(BigNumber.ROUND_CEIL).toFixed());
410
431
  const selectedValidatorInfo = targetValidators[0];
411
432
  const hotkey = selectedValidatorInfo.address;
412
- const extrinsic = chainApi.api.tx.subtensorModule.addStake(hotkey, netuid, binaryAmount);
433
+ const extrinsic = chainApi.api.tx.subtensorModule.addStakeLimit(hotkey, netuid, binaryAmount, BNlimitPrice, false);
413
434
  return [extrinsic, {
414
435
  slug: this.nativeToken.slug,
415
436
  amount: '0'
416
437
  }];
417
438
  }
439
+ async validateYieldJoin(data, path) {
440
+ const baseErrors = await super.validateYieldJoin(data, path);
441
+ if (baseErrors.length > 0) {
442
+ return baseErrors;
443
+ }
444
+ const {
445
+ amount
446
+ } = data;
447
+ if (new BN(amount).lt(new BN(DEFAULT_DTAO_MINBOND))) {
448
+ return [new TransactionError(BasicTxErrorType.INVALID_PARAMS, t(`Insufficient stake. You need to stake at least ${formatNumber(DEFAULT_DTAO_MINBOND, _getAssetDecimals(this.nativeToken))} ${_getAssetSymbol(this.nativeToken)} to earn rewards`))];
449
+ }
450
+ return baseErrors;
451
+ }
418
452
 
419
453
  /* Join pool action */
420
454
 
@@ -423,12 +457,36 @@ export default class SubnetTaoStakingPoolHandler extends BaseParaStakingPoolHand
423
457
  async handleYieldUnstake(amount, address, selectedTarget, netuid) {
424
458
  const apiPromise = await this.substrateApi.isReady;
425
459
  const binaryAmount = new BN(amount);
460
+ const price = await getAlphaToTaoMapping(this.substrateApi);
461
+ const alphaToTaoPrice = new BigN(price[netuid]);
462
+ const limitPrice = alphaToTaoPrice.multipliedBy(10 ** _getAssetDecimals(this.nativeToken)).multipliedBy(1 - DEFAULT_BITTENSOR_SLIPPAGE);
463
+ const BNlimitPrice = new BN(limitPrice.integerValue(BigNumber.ROUND_CEIL).toFixed());
426
464
  if (!selectedTarget) {
427
465
  return Promise.reject(new TransactionError(BasicTxErrorType.INVALID_PARAMS));
428
466
  }
429
- const extrinsic = apiPromise.api.tx.subtensorModule.removeStake(selectedTarget, netuid, binaryAmount);
467
+ const extrinsic = apiPromise.api.tx.subtensorModule.removeStakeLimit(selectedTarget, netuid, binaryAmount, BNlimitPrice, false);
430
468
  return [ExtrinsicType.STAKING_UNBOND, extrinsic];
431
469
  }
470
+ async validateYieldLeave(amount, address, fastLeave, selectedTarget, slug, poolInfo) {
471
+ var _poolInfo$metadata$su;
472
+ const baseErrors = await super.validateYieldLeave(amount, address, fastLeave, selectedTarget, slug);
473
+ if (baseErrors.length > 0) {
474
+ return baseErrors;
475
+ }
476
+ if (!poolInfo) {
477
+ return [new TransactionError(BasicTxErrorType.INVALID_PARAMS)];
478
+ }
479
+ const netuid = (_poolInfo$metadata$su = poolInfo.metadata.subnetData) === null || _poolInfo$metadata$su === void 0 ? void 0 : _poolInfo$metadata$su.netuid;
480
+ const price = await getAlphaToTaoMapping(this.substrateApi);
481
+ const minUnstake = new BigN(DEFAULT_DTAO_MINBOND).dividedBy(new BigN(price[netuid]));
482
+ console.log('minUnstake', minUnstake.dividedBy(10 ** _getAssetDecimals(this.nativeToken)).toString());
483
+ const formattedMinUnstake = minUnstake.dividedBy(1000000).integerValue(BigNumber.ROUND_CEIL).dividedBy(1000);
484
+ if (new BigN(amount).lt(formattedMinUnstake.multipliedBy(10 ** _getAssetDecimals(this.nativeToken)))) {
485
+ var _poolInfo$metadata$su2;
486
+ return [new TransactionError(BasicTxErrorType.INVALID_PARAMS, t(`Amount too low. You need to unstake at least ${formattedMinUnstake.toString()} ${((_poolInfo$metadata$su2 = poolInfo.metadata.subnetData) === null || _poolInfo$metadata$su2 === void 0 ? void 0 : _poolInfo$metadata$su2.subnetSymbol) || ''}`))];
487
+ }
488
+ return baseErrors;
489
+ }
432
490
 
433
491
  /* Leave pool action */
434
492
  }
@@ -1,8 +1,9 @@
1
1
  import { _ChainInfo } from '@subwallet/chain-list/types';
2
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
2
3
  import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
3
4
  import KoniState from '@subwallet/extension-base/koni/background/handlers/State';
4
5
  import BaseParaStakingPoolHandler from '@subwallet/extension-base/services/earning-service/handlers/native-staking/base-para';
5
- import { BaseYieldPositionInfo, StakeCancelWithdrawalParams, SubmitJoinNativeStaking, TransactionData, UnstakingInfo, ValidatorInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPositionInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
6
+ import { BaseYieldPositionInfo, OptimalYieldPath, StakeCancelWithdrawalParams, SubmitJoinNativeStaking, TransactionData, UnstakingInfo, ValidatorInfo, YieldPoolInfo, YieldPoolMethodInfo, YieldPositionInfo, YieldTokenBaseInfo } from '@subwallet/extension-base/types';
6
7
  export interface TaoStakeInfo {
7
8
  hotkey: string;
8
9
  stake: string;
@@ -71,6 +72,8 @@ export default class TaoNativeStakingPoolHandler extends BaseParaStakingPoolHand
71
72
  private getMainnetPoolTargets;
72
73
  getPoolTargets(): Promise<ValidatorInfo[]>;
73
74
  createJoinExtrinsic(data: SubmitJoinNativeStaking, positionInfo?: YieldPositionInfo, bondDest?: string): Promise<[TransactionData, YieldTokenBaseInfo]>;
75
+ validateYieldJoin(data: SubmitJoinNativeStaking, path: OptimalYieldPath): Promise<TransactionError[]>;
74
76
  handleYieldUnstake(amount: string, address: string, selectedTarget?: string): Promise<[ExtrinsicType, TransactionData]>;
77
+ validateYieldLeave(amount: string, address: string, fastLeave: boolean, selectedTarget?: string, slug?: string, poolInfo?: YieldPoolInfo): Promise<TransactionError[]>;
75
78
  }
76
79
  export {};
@@ -5,12 +5,15 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
5
5
  import { ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
6
6
  import { BITTENSOR_REFRESH_STAKE_APY, BITTENSOR_REFRESH_STAKE_INFO } from '@subwallet/extension-base/constants';
7
7
  import { getEarningStatusByNominations } from '@subwallet/extension-base/koni/api/staking/bonding/utils';
8
+ import { _getAssetDecimals, _getAssetSymbol } from '@subwallet/extension-base/services/chain-service/utils';
8
9
  import BaseParaStakingPoolHandler from '@subwallet/extension-base/services/earning-service/handlers/native-staking/base-para';
9
10
  import { BasicTxErrorType, EarningStatus } from '@subwallet/extension-base/types';
10
- import { reformatAddress } from '@subwallet/extension-base/utils';
11
+ import { formatNumber, reformatAddress } from '@subwallet/extension-base/utils';
11
12
  import BigN from 'bignumber.js';
13
+ import { t } from 'i18next';
12
14
  import { BN, BN_TEN, BN_ZERO } from '@polkadot/util';
13
15
  import { calculateReward } from "../../utils/index.js";
16
+ import { DEFAULT_DTAO_MINBOND } from "./dtao.js";
14
17
  export const BITTENSOR_API_KEY_1 = process.env.BITTENSOR_API_KEY_1 || '';
15
18
  export const BITTENSOR_API_KEY_2 = process.env.BITTENSOR_API_KEY_2 || '';
16
19
  export const BITTENSOR_API_KEY_3 = process.env.BITTENSOR_API_KEY_3 || '';
@@ -475,6 +478,19 @@ export default class TaoNativeStakingPoolHandler extends BaseParaStakingPoolHand
475
478
  amount: '0'
476
479
  }];
477
480
  }
481
+ async validateYieldJoin(data, path) {
482
+ const baseErrors = await super.validateYieldJoin(data, path);
483
+ if (baseErrors.length > 0) {
484
+ return baseErrors;
485
+ }
486
+ const {
487
+ amount
488
+ } = data;
489
+ if (new BN(amount).lt(new BN(DEFAULT_DTAO_MINBOND))) {
490
+ return [new TransactionError(BasicTxErrorType.INVALID_PARAMS, t(`Insufficient stake. You need to stake at least ${formatNumber(DEFAULT_DTAO_MINBOND, _getAssetDecimals(this.nativeToken))} ${_getAssetSymbol(this.nativeToken)} to earn rewards`))];
491
+ }
492
+ return baseErrors;
493
+ }
478
494
 
479
495
  /* Join pool action */
480
496
 
@@ -490,6 +506,20 @@ export default class TaoNativeStakingPoolHandler extends BaseParaStakingPoolHand
490
506
  const extrinsic = apiPromise.api.tx.subtensorModule.removeStake(selectedTarget, 0, binaryAmount);
491
507
  return [ExtrinsicType.STAKING_UNBOND, extrinsic];
492
508
  }
509
+ async validateYieldLeave(amount, address, fastLeave, selectedTarget, slug, poolInfo) {
510
+ const baseErrors = await super.validateYieldLeave(amount, address, fastLeave, selectedTarget, slug);
511
+ if (baseErrors.length > 0) {
512
+ return baseErrors;
513
+ }
514
+ if (!poolInfo) {
515
+ return [new TransactionError(BasicTxErrorType.INVALID_PARAMS)];
516
+ }
517
+ const bnMinUnstake = new BigN(DEFAULT_DTAO_MINBOND);
518
+ if (new BigN(amount).lt(bnMinUnstake)) {
519
+ return [new TransactionError(BasicTxErrorType.INVALID_PARAMS, t(`Amount too low. You need to unstake at least ${formatNumber(bnMinUnstake, _getAssetDecimals(this.nativeToken))} ${_getAssetSymbol(this.nativeToken)}`))];
520
+ }
521
+ return baseErrors;
522
+ }
493
523
 
494
524
  /* Leave pool action */
495
525
  }