@subwallet/extension-base 1.3.42-0 → 1.3.43-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 (47) hide show
  1. package/background/KoniTypes.d.ts +35 -23
  2. package/background/errors/BitcoinProviderError.d.ts +1 -1
  3. package/background/errors/BitcoinProviderError.js +2 -2
  4. package/background/types.d.ts +1 -1
  5. package/cjs/background/errors/BitcoinProviderError.js +2 -2
  6. package/cjs/core/logic-validation/request.js +316 -3
  7. package/cjs/koni/background/handlers/Extension.js +418 -90
  8. package/cjs/koni/background/handlers/State.js +198 -6
  9. package/cjs/koni/background/handlers/Tabs.js +119 -6
  10. package/cjs/packageInfo.js +1 -1
  11. package/cjs/page/bitcoin/index.js +67 -0
  12. package/cjs/page/index.js +5 -0
  13. package/cjs/services/buy-service/index.js +17 -2
  14. package/cjs/services/earning-service/handlers/native-staking/para-chain.js +27 -5
  15. package/cjs/services/request-service/handler/AuthRequestHandler.js +18 -0
  16. package/cjs/services/request-service/handler/BitcoinRequestHandler.js +48 -61
  17. package/cjs/services/request-service/index.js +2 -2
  18. package/cjs/services/transaction-service/index.js +71 -2
  19. package/cjs/utils/auth.js +2 -1
  20. package/core/logic-validation/request.d.ts +6 -2
  21. package/core/logic-validation/request.js +309 -3
  22. package/koni/background/handlers/Extension.d.ts +3 -0
  23. package/koni/background/handlers/Extension.js +330 -6
  24. package/koni/background/handlers/State.d.ts +4 -1
  25. package/koni/background/handlers/State.js +189 -4
  26. package/koni/background/handlers/Tabs.d.ts +7 -2
  27. package/koni/background/handlers/Tabs.js +119 -9
  28. package/package.json +11 -6
  29. package/packageInfo.js +1 -1
  30. package/page/bitcoin/index.d.ts +17 -0
  31. package/page/bitcoin/index.js +60 -0
  32. package/page/index.d.ts +2 -1
  33. package/page/index.js +4 -0
  34. package/services/balance-service/transfer/cardano-transfer.d.ts +2 -0
  35. package/services/buy-service/index.js +17 -2
  36. package/services/earning-service/handlers/native-staking/para-chain.js +27 -5
  37. package/services/request-service/handler/AuthRequestHandler.js +19 -1
  38. package/services/request-service/handler/BitcoinRequestHandler.d.ts +3 -4
  39. package/services/request-service/handler/BitcoinRequestHandler.js +48 -61
  40. package/services/request-service/index.d.ts +1 -2
  41. package/services/request-service/index.js +2 -2
  42. package/services/transaction-service/index.d.ts +1 -0
  43. package/services/transaction-service/index.js +71 -2
  44. package/services/transaction-service/types.d.ts +1 -0
  45. package/types/balance/transfer.d.ts +4 -2
  46. package/types/buy.d.ts +1 -1
  47. package/utils/auth.js +3 -2
@@ -8,7 +8,7 @@ import { TransactionError } from '@subwallet/extension-base/background/errors/Tr
8
8
  import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers';
9
9
  import { createSubscription } from '@subwallet/extension-base/background/handlers/subscriptions';
10
10
  import { CampaignDataType, ChainType, ExternalRequestPromiseStatus, ExtrinsicType, MantaPayEnableMessage, StakingType } from '@subwallet/extension-base/background/KoniTypes';
11
- import { _SUPPORT_TOKEN_PAY_FEE_GROUP, ALL_ACCOUNT_KEY, LATEST_SESSION } from '@subwallet/extension-base/constants';
11
+ import { _SUPPORT_TOKEN_PAY_FEE_GROUP, ALL_ACCOUNT_KEY, BTC_DUST_AMOUNT, LATEST_SESSION } from '@subwallet/extension-base/constants';
12
12
  import { additionalValidateTransferForRecipient, validateTransferRequest, validateXcmMinAmountToMythos, validateXcmTransferRequest } from '@subwallet/extension-base/core/logic-validation/transfer';
13
13
  import { _isSnowBridgeXcm } from '@subwallet/extension-base/core/substrate/xcm-parser';
14
14
  import { _isSufficientToken } from '@subwallet/extension-base/core/utils';
@@ -38,7 +38,7 @@ import { _isPosChainBridge, getClaimPosBridge } from '@subwallet/extension-base/
38
38
  import { estimateXcmFee } from '@subwallet/extension-base/services/balance-service/transfer/xcm/utils';
39
39
  import { _DEFAULT_MANTA_ZK_CHAIN, _MANTA_ZK_CHAIN_GROUP, _ZK_ASSET_PREFIX } from '@subwallet/extension-base/services/chain-service/constants';
40
40
  import { _ChainConnectionStatus } from '@subwallet/extension-base/services/chain-service/types';
41
- import { _getAssetDecimals, _getAssetSymbol, _getChainNativeTokenBasicInfo, _getContractAddressOfToken, _getEvmChainId, _isAssetSmartContractNft, _isChainEnabled, _isChainEvmCompatible, _isChainSubstrateCompatible, _isCustomAsset, _isLocalToken, _isMantaZkAsset, _isNativeToken, _isNativeTokenBySlug, _isPureEvmChain, _isTokenEvmSmartContract, _isTokenTransferredByBitcoin, _isTokenTransferredByCardano, _isTokenTransferredByEvm, _isTokenTransferredByTon } from '@subwallet/extension-base/services/chain-service/utils';
41
+ import { _getAssetDecimals, _getAssetSymbol, _getChainNativeTokenBasicInfo, _getContractAddressOfToken, _getEvmChainId, _isAssetSmartContractNft, _isChainBitcoinCompatible, _isChainEnabled, _isChainEvmCompatible, _isChainSubstrateCompatible, _isCustomAsset, _isLocalToken, _isMantaZkAsset, _isNativeToken, _isNativeTokenBySlug, _isPureEvmChain, _isTokenEvmSmartContract, _isTokenTransferredByBitcoin, _isTokenTransferredByCardano, _isTokenTransferredByEvm, _isTokenTransferredByTon } from '@subwallet/extension-base/services/chain-service/utils';
42
42
  import { calculateToAmountByReservePool } from '@subwallet/extension-base/services/fee-service/utils';
43
43
  import { batchExtrinsicSetFeeHydration, getAssetHubTokensCanPayFee, getHydrationTokensCanPayFee } from '@subwallet/extension-base/services/fee-service/utils/tokenPayFee';
44
44
  import { EXTENSION_REQUEST_URL } from '@subwallet/extension-base/services/request-service/constants';
@@ -47,11 +47,12 @@ import { isProposalExpired, isSupportWalletConnectChain, isSupportWalletConnectN
47
47
  import { SWStorage } from '@subwallet/extension-base/storage';
48
48
  import { AccountsStore } from '@subwallet/extension-base/stores';
49
49
  import { AccountSignMode, BasicTxErrorType, BasicTxWarningCode, CommonStepType, EarningProcessType, ProcessType, StakingTxErrorType, StepStatus, SwapFeeType, YieldPoolType, YieldStepType } from '@subwallet/extension-base/types';
50
- import { _analyzeAddress, calculateMaxTransferable, combineAllAccountProxy, createPromiseHandler, createTransactionFromRLP, detectTransferTxType, getAccountSignMode, isSameAddress, MODULE_SUPPORT, reformatAddress, signatureToHex, transformAccounts, transformAddresses, uniqueStringArray } from '@subwallet/extension-base/utils';
50
+ import { _analyzeAddress, calculateMaxTransferable, combineAllAccountProxy, combineBitcoinFee, createPromiseHandler, createTransactionFromRLP, detectTransferTxType, filterUneconomicalUtxos, getAccountSignMode, getSizeInfo, getTransferableBitcoinUtxos, isSameAddress, MODULE_SUPPORT, reformatAddress, signatureToHex, transformAccounts, transformAddresses, uniqueStringArray } from '@subwallet/extension-base/utils';
51
51
  import { parseContractInput, parseEvmRlp } from '@subwallet/extension-base/utils/eth/parseTransaction';
52
52
  import { getId } from '@subwallet/extension-base/utils/getId';
53
53
  import { getKeypairTypeByAddress, isAddress, isCardanoAddress, isSubstrateAddress, isTonAddress } from '@subwallet/keyring';
54
- import { CardanoKeypairTypes, EthereumKeypairTypes, SubstrateKeypairTypes, TonKeypairTypes } from '@subwallet/keyring/types';
54
+ import { BitcoinKeypairTypes, CardanoKeypairTypes, EthereumKeypairTypes, SubstrateKeypairTypes, TonKeypairTypes } from '@subwallet/keyring/types';
55
+ import { getBitcoinAddressInfo } from '@subwallet/keyring/utils';
55
56
  import { isBitcoinAddress } from '@subwallet/keyring/utils/address/validate';
56
57
  import { keyring } from '@subwallet/ui-keyring';
57
58
  import { getSdkError } from '@walletconnect/utils';
@@ -566,7 +567,8 @@ export default class KoniExtension {
566
567
  evm: EthereumKeypairTypes,
567
568
  substrate: SubstrateKeypairTypes,
568
569
  ton: TonKeypairTypes,
569
- cardano: CardanoKeypairTypes
570
+ cardano: CardanoKeypairTypes,
571
+ bitcoin: BitcoinKeypairTypes
570
572
  };
571
573
  return !!accountAuthTypes && accountAuthTypes.some(authType => {
572
574
  var _validTypes$authType;
@@ -668,7 +670,8 @@ export default class KoniExtension {
668
670
  substrate: 'substrateInfo',
669
671
  evm: 'evmInfo',
670
672
  cardano: 'cardanoInfo',
671
- ton: 'tonInfo'
673
+ ton: 'tonInfo',
674
+ bitcoin: 'bitcoinInfo'
672
675
  };
673
676
  const typeInfoKey = typeInfoMap[authSwitchNetworkType];
674
677
  if (!typeInfoKey || !chainInfo[typeInfoKey]) {
@@ -1351,6 +1354,7 @@ export default class KoniExtension {
1351
1354
  if (transferAll) {
1352
1355
  inputData.value = transferAmount.value;
1353
1356
  }
1357
+ console.log('PSPT transaction', transaction.toHex());
1354
1358
  } else {
1355
1359
  const substrateApi = this.#koniState.getSubstrateApi(chain);
1356
1360
  [transaction, transferAmount.value] = await createSubstrateExtrinsic({
@@ -1662,6 +1666,190 @@ export default class KoniExtension {
1662
1666
  eventsHandler: eventsHandler
1663
1667
  });
1664
1668
  }
1669
+ async makeBitcoinDappTransferConfirmation(inputData) {
1670
+ const {
1671
+ chain,
1672
+ feeCustom,
1673
+ feeOption,
1674
+ from,
1675
+ id,
1676
+ to,
1677
+ tokenSlug,
1678
+ transferAll,
1679
+ value
1680
+ } = inputData;
1681
+ const transferTokenInfo = this.#koniState.chainService.getAssetBySlug(tokenSlug);
1682
+ const errors = validateTransferRequest(transferTokenInfo, from, to, value, transferAll);
1683
+ const warnings = [];
1684
+ const chainInfo = this.#koniState.getChainInfo(chain);
1685
+ const nativeTokenInfo = this.#koniState.getNativeTokenInfo(chain);
1686
+ const nativeTokenSlug = nativeTokenInfo.slug;
1687
+ const isTransferNativeToken = nativeTokenSlug === tokenSlug;
1688
+ let chainType = ChainType.BITCOIN;
1689
+ const tokenBaseAmount = {
1690
+ value: '0',
1691
+ symbol: transferTokenInfo.symbol,
1692
+ decimals: transferTokenInfo.decimals || 0
1693
+ };
1694
+ const transferAmount = {
1695
+ ...tokenBaseAmount
1696
+ };
1697
+ let transaction;
1698
+ let overrideFeeCustom;
1699
+ let calculatedBitcoinFeeRate;
1700
+
1701
+ // Get native token amount
1702
+ const freeBalance = await this.getAddressTransferableBalance({
1703
+ address: from,
1704
+ networkKey: chain,
1705
+ token: tokenSlug
1706
+ });
1707
+ const txVal = transferAll ? freeBalance.value : value || '0';
1708
+ try {
1709
+ if (_isChainBitcoinCompatible(chainInfo)) {
1710
+ chainType = ChainType.BITCOIN;
1711
+ const bitcoinApi = this.#koniState.getBitcoinApi(chain); // Get Bitcoin API map
1712
+ const network = chainInfo.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
1713
+ const feeInfo = await this.#koniState.feeService.subscribeChainFee(getId(), chain, 'bitcoin');
1714
+ [transaction, transferAmount.value, calculatedBitcoinFeeRate] = await createBitcoinTransaction({
1715
+ bitcoinApi,
1716
+ chain,
1717
+ from,
1718
+ feeInfo,
1719
+ to,
1720
+ transferAll: transferAll,
1721
+ value: txVal,
1722
+ network: network
1723
+ });
1724
+ if (calculatedBitcoinFeeRate) {
1725
+ const feeRate = parseFloat(calculatedBitcoinFeeRate);
1726
+ if (!isNaN(feeRate)) {
1727
+ overrideFeeCustom = {
1728
+ feeRate
1729
+ };
1730
+ }
1731
+ }
1732
+ }
1733
+ } catch (e) {
1734
+ const error = e;
1735
+ if (error.message.includes('transfer amount exceeds balance')) {
1736
+ error.message = t('Insufficient balance');
1737
+ }
1738
+ throw error;
1739
+ }
1740
+ const transferNativeAmount = isTransferNativeToken ? transferAmount.value : '0';
1741
+ return this.#koniState.transactionService.handleTransactionAfterConfirmation({
1742
+ id,
1743
+ errors,
1744
+ warnings,
1745
+ address: from,
1746
+ chain: chain,
1747
+ feeCustom: overrideFeeCustom || feeCustom,
1748
+ feeOption: overrideFeeCustom ? 'custom' : feeOption,
1749
+ chainType,
1750
+ transferNativeAmount,
1751
+ transaction,
1752
+ data: inputData,
1753
+ extrinsicType: isTransferNativeToken ? ExtrinsicType.TRANSFER_BALANCE : ExtrinsicType.TRANSFER_TOKEN,
1754
+ ignoreWarnings: [],
1755
+ isTransferAll: isTransferNativeToken ? transferAll : false,
1756
+ edAsWarning: isTransferNativeToken
1757
+ });
1758
+ }
1759
+ async makePsbtTransferAfterConfirmation(inputData_) {
1760
+ var _txOutput$;
1761
+ const {
1762
+ chain,
1763
+ from,
1764
+ id,
1765
+ psbt,
1766
+ tokenSlug,
1767
+ txInput,
1768
+ txOutput,
1769
+ value
1770
+ } = inputData_;
1771
+ let inputAmount = new BigN(0);
1772
+ const transferTokenInfo = this.#koniState.chainService.getAssetBySlug(tokenSlug);
1773
+ const totalUtxoInput = txInput.reduce((total, {
1774
+ address,
1775
+ amount
1776
+ }) => {
1777
+ if (!address || !amount) {
1778
+ return total;
1779
+ }
1780
+ if (isSameAddress(address, from)) {
1781
+ inputAmount = new BigN(amount);
1782
+ }
1783
+ return total.plus(new BigN(amount || 0));
1784
+ }, new BigN(0));
1785
+ const totalUtxoOutput = txOutput.reduce((total, {
1786
+ address,
1787
+ amount
1788
+ }) => {
1789
+ if (!address || !amount) {
1790
+ return total;
1791
+ }
1792
+ return total.plus(new BigN(amount));
1793
+ }, new BigN(0));
1794
+ const estimateFeeValue = totalUtxoInput.minus(totalUtxoOutput).toString();
1795
+ const errors = validateTransferRequest(transferTokenInfo, from, ((_txOutput$ = txOutput[0]) === null || _txOutput$ === void 0 ? void 0 : _txOutput$.address) || '', value, false);
1796
+ const warnings = [];
1797
+ const chainInfo = this.#koniState.getChainInfo(chain);
1798
+ const {
1799
+ decimals,
1800
+ symbol
1801
+ } = _getChainNativeTokenBasicInfo(chainInfo);
1802
+ const estimateFee = {
1803
+ symbol,
1804
+ decimals,
1805
+ value: estimateFeeValue,
1806
+ tooHigh: false
1807
+ };
1808
+ const nativeTokenInfo = this.#koniState.getNativeTokenInfo(chain);
1809
+ const nativeTokenSlug = nativeTokenInfo.slug;
1810
+ const isTransferNativeToken = nativeTokenSlug === tokenSlug;
1811
+ const chainType = ChainType.BITCOIN;
1812
+ const bitcoinNetwork = chainInfo.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
1813
+ const psbtGenerate = bitcoin.Psbt.fromHex(psbt, {
1814
+ network: bitcoinNetwork
1815
+ });
1816
+ const tokenBaseAmount = {
1817
+ value: inputData_.value,
1818
+ symbol: transferTokenInfo.symbol,
1819
+ decimals: transferTokenInfo.decimals || 0
1820
+ };
1821
+ const transferAmount = {
1822
+ ...tokenBaseAmount
1823
+ };
1824
+
1825
+ // Get native token amount
1826
+ const freeBalance = await this.getAddressTransferableBalance({
1827
+ address: from,
1828
+ networkKey: chain,
1829
+ token: tokenSlug
1830
+ });
1831
+ if (new BigN(freeBalance.value).lt(inputAmount)) {
1832
+ throw new Error(t('Insufficient balance'));
1833
+ }
1834
+ const transferNativeAmount = isTransferNativeToken ? transferAmount.value : '0';
1835
+ return this.#koniState.transactionService.handleTransactionAfterConfirmation({
1836
+ id,
1837
+ errors,
1838
+ warnings,
1839
+ address: from,
1840
+ chain: chain,
1841
+ estimateFee,
1842
+ chainType,
1843
+ transferNativeAmount,
1844
+ transaction: psbtGenerate,
1845
+ data: inputData_,
1846
+ extrinsicType: isTransferNativeToken ? ExtrinsicType.TRANSFER_BALANCE : ExtrinsicType.TRANSFER_TOKEN,
1847
+ ignoreWarnings: [],
1848
+ isTransferAll: false,
1849
+ edAsWarning: isTransferNativeToken,
1850
+ skipFeeRecalculation: true
1851
+ });
1852
+ }
1665
1853
  async getTokensCanPayFee(request) {
1666
1854
  var _tokensHasBalanceInfo;
1667
1855
  const {
@@ -1982,6 +2170,135 @@ export default class KoniExtension {
1982
2170
  });
1983
2171
  return calculateMaxTransferable(id, _request, freeBalance, fee);
1984
2172
  }
2173
+ async subscribeTransferableWhenConfirmation({
2174
+ address,
2175
+ chain,
2176
+ feeCustom,
2177
+ feeOption: _feeOptions,
2178
+ to,
2179
+ token,
2180
+ value
2181
+ }, id, port) {
2182
+ const cb = createSubscription(id, port);
2183
+ const freeBalanceSubject = new Subject();
2184
+ const feeSubject = new Subject();
2185
+ const feeType = 'bitcoin';
2186
+ let error;
2187
+ const convertData = async (freeBalance, fee, feeOption, feeCustom) => {
2188
+ let estimatedFee = '0';
2189
+ let feeOptions = null;
2190
+ const amount = parseInt(value || '0');
2191
+ const neededUtxos = [];
2192
+ let sum = new BigN(0);
2193
+ let sizeInfo = null;
2194
+ try {
2195
+ const _fee = fee;
2196
+ const _feeCustom = feeCustom;
2197
+ const combineFee = combineBitcoinFee(_fee, _feeOptions, _feeCustom);
2198
+ const bitcoinApi = this.#koniState.chainService.getBitcoinApi(chain);
2199
+ let utxos = await getTransferableBitcoinUtxos(bitcoinApi, address);
2200
+ const recipients = [address, to || address];
2201
+ utxos = utxos.sort((a, b) => b.value - a.value);
2202
+ const filteredUtxos = filterUneconomicalUtxos({
2203
+ utxos,
2204
+ feeRate: combineFee.feeRate,
2205
+ recipients,
2206
+ sender: address
2207
+ });
2208
+ for (const utxo of filteredUtxos) {
2209
+ sizeInfo = getSizeInfo({
2210
+ inputLength: neededUtxos.length,
2211
+ sender: address,
2212
+ recipients
2213
+ });
2214
+ const currentValue = new BigN(amount).plus(Math.ceil(sizeInfo.txVBytes * combineFee.feeRate));
2215
+ if (sum.gte(currentValue)) {
2216
+ break;
2217
+ }
2218
+ sum = sum.plus(utxo.value);
2219
+ neededUtxos.push(utxo);
2220
+ }
2221
+
2222
+ // re calculate
2223
+ sizeInfo = getSizeInfo({
2224
+ inputLength: neededUtxos.length,
2225
+ sender: address,
2226
+ recipients
2227
+ });
2228
+ if (!sizeInfo) {
2229
+ sizeInfo = getSizeInfo({
2230
+ inputLength: utxos.length || 1,
2231
+ sender: address,
2232
+ recipients
2233
+ });
2234
+ }
2235
+ estimatedFee = Math.ceil(sizeInfo.txVBytes * combineFee.feeRate).toString();
2236
+ const amountLeft = sum.minus(amount).minus(new BigN(estimatedFee));
2237
+ if (amountLeft.lte(0)) {
2238
+ error = 'Insufficient balance';
2239
+ } else {
2240
+ const senderAddressInfo = getBitcoinAddressInfo(address);
2241
+ const dustLimit = BTC_DUST_AMOUNT[senderAddressInfo.type] || 546;
2242
+ if (amountLeft.lte(dustLimit)) {
2243
+ sizeInfo = getSizeInfo({
2244
+ inputLength: neededUtxos.length,
2245
+ sender: address,
2246
+ recipients: [to || address]
2247
+ });
2248
+ estimatedFee = sum.minus(amount).toString();
2249
+ }
2250
+ }
2251
+ feeOptions = {
2252
+ ...fee,
2253
+ vSize: sizeInfo.txVBytes,
2254
+ estimatedFee
2255
+ };
2256
+ } catch (e) {
2257
+ feeOptions = {
2258
+ ...fee,
2259
+ estimatedFee,
2260
+ vSize: 0
2261
+ };
2262
+ error = e.message || e;
2263
+ console.warn('Unable to estimate fee', e);
2264
+ }
2265
+ return {
2266
+ feeOptions: feeOptions,
2267
+ feeType,
2268
+ error,
2269
+ id
2270
+ };
2271
+ };
2272
+ const subscription = combineLatest({
2273
+ freeBalance: freeBalanceSubject,
2274
+ fee: feeSubject
2275
+ }).subscribe({
2276
+ next: ({
2277
+ fee,
2278
+ freeBalance
2279
+ }) => {
2280
+ convertData(freeBalance, fee, _feeOptions, feeCustom).then(cb).catch(console.error);
2281
+ }
2282
+ });
2283
+ const [unsubBalance, freeBalance] = await this.#koniState.balanceService.subscribeBalance(address, chain, token, 'transferable', ExtrinsicType.TRANSFER_BALANCE, data => {
2284
+ freeBalanceSubject.next(data); // Must be called after subscription
2285
+ });
2286
+
2287
+ const fee = await this.#koniState.feeService.subscribeChainFee(id, chain, feeType, data => {
2288
+ feeSubject.next(data); // Must be called after subscription
2289
+ });
2290
+
2291
+ const unsub = () => {
2292
+ subscription.unsubscribe();
2293
+ unsubBalance();
2294
+ this.#koniState.feeService.unsubscribeChainFee(id, chain, feeType);
2295
+ };
2296
+ this.createUnsubscriptionHandle(id, unsub);
2297
+ port.onDisconnect.addListener(() => {
2298
+ this.cancelSubscription(id);
2299
+ });
2300
+ return convertData(freeBalance, fee, _feeOptions, feeCustom);
2301
+ }
1985
2302
  async subscribeAddressTransferableBalance({
1986
2303
  address,
1987
2304
  extrinsicType,
@@ -2850,6 +3167,7 @@ export default class KoniExtension {
2850
3167
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
2851
3168
  const {
2852
3169
  additionalValidator,
3170
+ emitterTransaction,
2853
3171
  eventsHandler,
2854
3172
  step,
2855
3173
  transaction,
@@ -4640,6 +4958,8 @@ export default class KoniExtension {
4640
4958
  return await this.updateAssetSetting(request);
4641
4959
  case 'pri(transfer.subscribe)':
4642
4960
  return this.subscribeMaxTransferable(request, id, port);
4961
+ case 'pri(transfer.confirmation.subscribe)':
4962
+ return this.subscribeTransferableWhenConfirmation(request, id, port);
4643
4963
  case 'pri(freeBalance.get)':
4644
4964
  return this.getAddressTransferableBalance(request);
4645
4965
  case 'pri(freeBalance.subscribe)':
@@ -4658,6 +4978,10 @@ export default class KoniExtension {
4658
4978
  /// Transfer
4659
4979
  case 'pri(accounts.transfer)':
4660
4980
  return await this.makeTransfer(request);
4981
+ case 'pri(accounts.bitcoin.dapp.transfer.confirmation)':
4982
+ return await this.makeBitcoinDappTransferConfirmation(request);
4983
+ case 'pri(accounts.psbt.transfer.confirmation)':
4984
+ return await this.makePsbtTransferAfterConfirmation(request);
4661
4985
  case 'pri(accounts.crossChainTransfer)':
4662
4986
  return await this.makeCrossChainTransfer(request);
4663
4987
  case 'pri(accounts.getOptimalTransferProcess)':
@@ -1,7 +1,7 @@
1
1
  /// <reference types="chrome" />
2
2
  import * as CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs';
3
3
  import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _MultiChainAsset } from '@subwallet/chain-list/types';
4
- import { AddTokenRequestExternal, AmountData, ApiMap, AuthRequestV2, ChainStakingMetadata, ConfirmationsQueue, ConfirmationsQueueBitcoin, ConfirmationsQueueCardano, ConfirmationsQueueTon, CrowdloanItem, CrowdloanJson, EvmSendTransactionParams, ExternalRequestPromise, MantaPayConfig, MantaPaySyncState, NftCollection, NftItem, NftJson, NominatorMetadata, RequestAccountExportPrivateKey, RequestCardanoSignData, RequestCardanoSignTransaction, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestConfirmationCompleteCardano, RequestConfirmationCompleteTon, RequestCrowdloanContributions, RequestSettingsType, ResponseAccountExportPrivateKey, ResponseCardanoSignData, ResponseCardanoSignTransaction, ServiceInfo, SingleModeJson, StakingItem, StakingJson, StakingRewardItem, StakingRewardJson, StakingType, UiSettings } from '@subwallet/extension-base/background/KoniTypes';
4
+ import { AddTokenRequestExternal, AmountData, ApiMap, AuthRequestV2, BitcoinSendTransactionParams, BitcoinSignMessageParams, BitcoinSignMessageResult, BitcoinSignPsbtParams, BitcoinSignPsbtResult, ChainStakingMetadata, ConfirmationsQueue, ConfirmationsQueueBitcoin, ConfirmationsQueueCardano, ConfirmationsQueueTon, CrowdloanItem, CrowdloanJson, EvmSendTransactionParams, ExternalRequestPromise, MantaPayConfig, MantaPaySyncState, NftCollection, NftItem, NftJson, NominatorMetadata, RequestAccountExportPrivateKey, RequestCardanoSignData, RequestCardanoSignTransaction, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestConfirmationCompleteCardano, RequestConfirmationCompleteTon, RequestCrowdloanContributions, RequestSettingsType, ResponseAccountExportPrivateKey, ResponseCardanoSignData, ResponseCardanoSignTransaction, ServiceInfo, SingleModeJson, StakingItem, StakingJson, StakingRewardItem, StakingRewardJson, StakingType, UiSettings } from '@subwallet/extension-base/background/KoniTypes';
5
5
  import { RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestSign, ResponseRpcListProviders, ResponseSigning } from '@subwallet/extension-base/background/types';
6
6
  import { EnvConfig } from '@subwallet/extension-base/constants';
7
7
  import { BalanceService } from '@subwallet/extension-base/services/balance-service';
@@ -222,6 +222,9 @@ export default class KoniState {
222
222
  cardanoSignData(id: string, url: string, params: RequestCardanoSignData, currentAddress: string): Promise<ResponseCardanoSignData>;
223
223
  cardanoSignTx(id: string, url: string, param: RequestCardanoSignTransaction, currentAddress: string): Promise<ResponseCardanoSignTransaction>;
224
224
  cardanoSubmitTx(id: string, url: string, txHex: string): Promise<string>;
225
+ bitcoinSign(id: string, url: string, method: string, params: BitcoinSignMessageParams): Promise<BitcoinSignMessageResult>;
226
+ bitcoinSignPspt(id: string, url: string, params: BitcoinSignPsbtParams): Promise<BitcoinSignPsbtResult>;
227
+ bitcoinSendTransaction(id: string, url: string, transactionParams: BitcoinSendTransactionParams): Promise<string | undefined>;
225
228
  getConfirmationsQueueSubject(): BehaviorSubject<ConfirmationsQueue>;
226
229
  getConfirmationsQueueSubjectTon(): BehaviorSubject<ConfirmationsQueueTon>;
227
230
  getConfirmationsQueueSubjectCardano(): BehaviorSubject<ConfirmationsQueueCardano>;
@@ -2,13 +2,14 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import * as CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs';
5
+ import { BitcoinProviderError } from '@subwallet/extension-base/background/errors/BitcoinProviderError';
5
6
  import { CardanoProviderError } from '@subwallet/extension-base/background/errors/CardanoProviderError';
6
7
  import { EvmProviderError } from '@subwallet/extension-base/background/errors/EvmProviderError';
7
8
  import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers';
8
9
  import { isSubscriptionRunning, unsubscribe } from '@subwallet/extension-base/background/handlers/subscriptions';
9
- import { APIItemState, CardanoProviderErrorType, ChainType, EvmProviderErrorType, ExternalRequestPromiseStatus, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
10
+ import { APIItemState, BitcoinProviderErrorType, CardanoProviderErrorType, ChainType, EvmProviderErrorType, ExternalRequestPromiseStatus, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
10
11
  import { BACKEND_API_URL, BACKEND_PRICE_HISTORY_URL, MANTA_PAY_BALANCE_INTERVAL, REMIND_EXPORT_ACCOUNT } from '@subwallet/extension-base/constants';
11
- import { convertErrorFormat, generateValidationProcess, validationAuthCardanoMiddleware, validationAuthMiddleware, validationAuthWCMiddleware, validationCardanoSignDataMiddleware, validationConnectMiddleware, validationEvmDataTransactionMiddleware, validationEvmSignMessageMiddleware } from '@subwallet/extension-base/core/logic-validation';
12
+ import { convertErrorFormat, generateValidationProcess, validationAuthCardanoMiddleware, validationAuthMiddleware, validationAuthWCMiddleware, validationBitcoinConnectMiddleware, validationBitcoinSendTransactionMiddleware, validationBitcoinSignMessageMiddleware, validationBitcoinSignPsbtMiddleware, validationCardanoSignDataMiddleware, validationConnectMiddleware, validationEvmDataTransactionMiddleware, validationEvmSignMessageMiddleware } from '@subwallet/extension-base/core/logic-validation';
12
13
  import { BalanceService } from '@subwallet/extension-base/services/balance-service';
13
14
  import { ServiceStatus } from '@subwallet/extension-base/services/base/types';
14
15
  import BuyService from '@subwallet/extension-base/services/buy-service';
@@ -45,6 +46,8 @@ import { convertCardanoHexToBech32, validateAddressNetwork } from '@subwallet/ex
45
46
  import { createPromiseHandler } from '@subwallet/extension-base/utils/promise';
46
47
  import subwalletApiSdk from '@subwallet/subwallet-api-sdk';
47
48
  import { keyring } from '@subwallet/ui-keyring';
49
+ import BigN from 'bignumber.js';
50
+ import * as bitcoin from 'bitcoinjs-lib';
48
51
  import BN from 'bn.js';
49
52
  import { t } from 'i18next';
50
53
  import { Subject } from 'rxjs';
@@ -104,7 +107,7 @@ export default class KoniState {
104
107
  this.chainService = new ChainService(this.dbService, this.eventService);
105
108
  this.subscanService = SubscanService.getInstance();
106
109
  this.settingService = new SettingService();
107
- this.requestService = new RequestService(this.chainService, this.settingService, this.keyringService, this.feeService, this.transactionService);
110
+ this.requestService = new RequestService(this.chainService, this.settingService, this.keyringService, this.transactionService);
108
111
  this.priceService = new PriceService(this.dbService, this.eventService, this.chainService);
109
112
  this.balanceService = new BalanceService(this);
110
113
  this.historyService = new HistoryService(this.dbService, this.chainService, this.eventService, this.keyringService, this.subscanService);
@@ -1021,6 +1024,8 @@ export default class KoniState {
1021
1024
  });
1022
1025
  });
1023
1026
  }
1027
+
1028
+ // Cardano
1024
1029
  async cardanoGetBalance(id, url, address) {
1025
1030
  const authInfoMap = await this.getAuthList();
1026
1031
  const authInfo = authInfoMap[stripUrl(url)];
@@ -1241,6 +1246,186 @@ export default class KoniState {
1241
1246
  const cardanoApi = this.chainService.getCardanoApi(networkKey);
1242
1247
  return await cardanoApi.sendCardanoTxReturnHash(txHex);
1243
1248
  }
1249
+
1250
+ // Bitcoin
1251
+ async bitcoinSign(id, url, method, params) {
1252
+ const {
1253
+ address,
1254
+ message
1255
+ } = params;
1256
+ const payloadValidation = {
1257
+ address,
1258
+ type: 'bitcoin',
1259
+ payloadAfterValidated: message,
1260
+ errors: [],
1261
+ networkKey: ''
1262
+ };
1263
+ const validationSteps = [validationAuthMiddleware, validationBitcoinSignMessageMiddleware];
1264
+ const result = await generateValidationProcess(this, url, payloadValidation, validationSteps);
1265
+ const errorsFormated = convertErrorFormat(result.errors);
1266
+ const payloadAfterValidated = {
1267
+ ...result.payloadAfterValidated,
1268
+ errors: errorsFormated,
1269
+ id
1270
+ };
1271
+ return this.requestService.addConfirmationBitcoin(id, url, 'bitcoinSignatureRequest', payloadAfterValidated, {}).then(({
1272
+ isApproved,
1273
+ payload
1274
+ }) => {
1275
+ if (isApproved) {
1276
+ if (payload) {
1277
+ return payload;
1278
+ } else {
1279
+ throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Not found signature'));
1280
+ }
1281
+ } else {
1282
+ throw new BitcoinProviderError(BitcoinProviderErrorType.USER_REJECTED_REQUEST);
1283
+ }
1284
+ });
1285
+ }
1286
+ async bitcoinSignPspt(id, url, params) {
1287
+ const {
1288
+ address,
1289
+ network,
1290
+ psbt
1291
+ } = params;
1292
+ const payloadValidation = {
1293
+ address,
1294
+ type: 'bitcoin',
1295
+ payloadAfterValidated: params,
1296
+ errors: [],
1297
+ networkKey: network === 'mainnet' ? 'bitcoin' : 'bitcoinTestnet'
1298
+ };
1299
+ const validationSteps = [validationAuthMiddleware, validationBitcoinConnectMiddleware, validationBitcoinSignPsbtMiddleware];
1300
+ const result = await generateValidationProcess(this, url, payloadValidation, validationSteps);
1301
+ const errorsFormated = convertErrorFormat(result.errors);
1302
+ const payloadAfterValidated = {
1303
+ ...result.payloadAfterValidated,
1304
+ errors: errorsFormated
1305
+ };
1306
+ const network_ = network === 'mainnet' ? bitcoin.networks.bitcoin : bitcoin.networks.testnet;
1307
+ const psbtGenerate = bitcoin.Psbt.fromHex(psbt, {
1308
+ network: network_
1309
+ });
1310
+ const isExistedInput = (inputs, address) => inputs.findIndex(({
1311
+ address: address_
1312
+ }) => isSameAddress(address, address_ || ''));
1313
+ let to = '';
1314
+ const tokenInfo = this.getNativeTokenInfo(result.networkKey);
1315
+ let value = new BigN(0);
1316
+ const totalBalance = await this.balanceService.getTotalBalance(address, result.networkKey, tokenInfo.slug);
1317
+ let inputAmount = new BigN(0);
1318
+ const psbtInputData = psbtGenerate.data.inputs.reduce((inputs, {
1319
+ nonWitnessUtxo,
1320
+ witnessUtxo
1321
+ }, inputIndex) => {
1322
+ let inputData = null;
1323
+ if (witnessUtxo) {
1324
+ inputData = {
1325
+ address: bitcoin.address.fromOutputScript(witnessUtxo === null || witnessUtxo === void 0 ? void 0 : witnessUtxo.script, network_),
1326
+ amount: witnessUtxo.value.toString()
1327
+ };
1328
+ } else if (nonWitnessUtxo) {
1329
+ const txin = psbtGenerate.txInputs[inputIndex];
1330
+ const txout = bitcoin.Transaction.fromBuffer(nonWitnessUtxo).outs[txin.index];
1331
+ inputData = {
1332
+ address: bitcoin.address.fromOutputScript(txout.script, network_),
1333
+ amount: txout.value.toString()
1334
+ };
1335
+ }
1336
+ if (inputData) {
1337
+ inputs.push(inputData);
1338
+ if (isSameAddress(address, inputData.address || '')) {
1339
+ inputAmount = inputAmount.plus(new BigN(inputData.amount || '0'));
1340
+ }
1341
+ }
1342
+ return inputs;
1343
+ }, []);
1344
+ if (new BigN(totalBalance.value).lt(inputAmount)) {
1345
+ payloadAfterValidated.errors = [{
1346
+ message: t('Insufficient balance'),
1347
+ name: t('Unable to sign transaction')
1348
+ }];
1349
+ }
1350
+ const psbtOutputData = psbtGenerate.txOutputs.map(output => {
1351
+ let address = '';
1352
+ try {
1353
+ address = output.address || bitcoin.address.fromOutputScript(output.script, network_);
1354
+ } catch (e) {
1355
+ if (output.script.includes(bitcoin.opcodes.OP_RETURN)) {
1356
+ address = 'OP_RETURN';
1357
+ } else {
1358
+ address = 'Unknown';
1359
+ }
1360
+ }
1361
+ if (isExistedInput(psbtInputData, address) === -1) {
1362
+ to = address;
1363
+ value = value.plus(new BigN(output.value));
1364
+ }
1365
+ return {
1366
+ address,
1367
+ amount: output.value.toString()
1368
+ };
1369
+ });
1370
+ payloadAfterValidated.payload = {
1371
+ ...payloadAfterValidated.payload,
1372
+ psbt,
1373
+ tokenSlug: tokenInfo.slug,
1374
+ value: value.toString(),
1375
+ to,
1376
+ txInput: psbtInputData,
1377
+ txOutput: psbtOutputData
1378
+ };
1379
+ return this.requestService.addConfirmationBitcoin(id, url, 'bitcoinSignPsbtRequest', payloadAfterValidated, {}).then(({
1380
+ isApproved,
1381
+ payload
1382
+ }) => {
1383
+ if (isApproved) {
1384
+ if (payload) {
1385
+ return payload;
1386
+ } else {
1387
+ throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Not found signature'));
1388
+ }
1389
+ } else {
1390
+ throw new BitcoinProviderError(BitcoinProviderErrorType.USER_REJECTED_REQUEST);
1391
+ }
1392
+ });
1393
+ }
1394
+ async bitcoinSendTransaction(id, url, transactionParams) {
1395
+ const payloadValidation = {
1396
+ address: transactionParams.account,
1397
+ type: 'bitcoin',
1398
+ payloadAfterValidated: transactionParams,
1399
+ errors: [],
1400
+ networkKey: transactionParams.network === 'mainnet' ? 'bitcoin' : 'bitcoinTestnet'
1401
+ };
1402
+ const validationSteps = [validationAuthMiddleware, validationBitcoinConnectMiddleware, validationBitcoinSendTransactionMiddleware];
1403
+ const result = await generateValidationProcess(this, url, payloadValidation, validationSteps);
1404
+ const errorsFormated = convertErrorFormat(result.errors);
1405
+ const requestPayload = {
1406
+ ...result.payloadAfterValidated,
1407
+ hashPayload: JSON.stringify(result.payloadAfterValidated),
1408
+ from: transactionParams.account,
1409
+ id,
1410
+ errors: errorsFormated
1411
+ };
1412
+
1413
+ // Custom handle this instead of general handler transaction
1414
+ return this.requestService.addConfirmationBitcoin(id, url, 'bitcoinSendTransactionRequestAfterConfirmation', requestPayload, {}).then(({
1415
+ isApproved,
1416
+ payload
1417
+ }) => {
1418
+ if (isApproved) {
1419
+ if (payload) {
1420
+ return payload;
1421
+ } else {
1422
+ throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Not found signature'));
1423
+ }
1424
+ } else {
1425
+ throw new BitcoinProviderError(BitcoinProviderErrorType.USER_REJECTED_REQUEST);
1426
+ }
1427
+ });
1428
+ }
1244
1429
  getConfirmationsQueueSubject() {
1245
1430
  return this.requestService.confirmationsQueueSubject;
1246
1431
  }
@@ -1480,9 +1665,9 @@ export default class KoniState {
1480
1665
  this.eventService.emit('general.start', true);
1481
1666
 
1482
1667
  // Complete starting
1483
- starting.resolve();
1484
1668
  this.waitStarting = null;
1485
1669
  this.generalStatus = ServiceStatus.STARTED;
1670
+ starting.resolve();
1486
1671
  }
1487
1672
  async _startFull() {
1488
1673
  // Continue wait existed starting process