@subwallet/extension-base 1.3.41-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 (219) hide show
  1. package/background/KoniTypes.d.ts +134 -5
  2. package/background/KoniTypes.js +18 -0
  3. package/background/errors/BitcoinProviderError.d.ts +6 -0
  4. package/background/errors/BitcoinProviderError.js +47 -0
  5. package/background/types.d.ts +1 -1
  6. package/cjs/background/KoniTypes.js +20 -1
  7. package/cjs/background/errors/BitcoinProviderError.js +54 -0
  8. package/cjs/constants/bitcoin.js +22 -0
  9. package/cjs/constants/index.js +16 -1
  10. package/cjs/core/logic-validation/recipientAddress.js +9 -0
  11. package/cjs/core/logic-validation/request.js +316 -3
  12. package/cjs/core/logic-validation/transfer.js +25 -5
  13. package/cjs/core/types.js +1 -0
  14. package/cjs/core/utils.js +15 -1
  15. package/cjs/koni/background/handlers/Extension.js +477 -93
  16. package/cjs/koni/background/handlers/State.js +249 -16
  17. package/cjs/koni/background/handlers/Tabs.js +119 -6
  18. package/cjs/packageInfo.js +1 -1
  19. package/cjs/page/bitcoin/index.js +67 -0
  20. package/cjs/page/index.js +5 -0
  21. package/cjs/services/balance-service/helpers/subscribe/bitcoin.js +94 -0
  22. package/cjs/services/balance-service/helpers/subscribe/index.js +19 -7
  23. package/cjs/services/balance-service/index.js +32 -4
  24. package/cjs/services/balance-service/transfer/bitcoin-transfer.js +119 -0
  25. package/cjs/services/balance-service/transfer/token.js +2 -0
  26. package/cjs/services/base/types.js +2 -0
  27. package/cjs/services/buy-service/index.js +17 -2
  28. package/cjs/services/chain-service/constants.js +14 -3
  29. package/cjs/services/chain-service/handler/bitcoin/BitcoinApi.js +105 -0
  30. package/cjs/services/chain-service/handler/bitcoin/BitcoinChainHandler.js +78 -0
  31. package/cjs/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/blockstream-testnet-strategy.js +371 -0
  32. package/cjs/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/index.js +19 -0
  33. package/cjs/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/mempool-testnet-strategy.js +368 -0
  34. package/cjs/services/chain-service/handler/bitcoin/strategy/SubWalletMainnet/index.js +302 -0
  35. package/cjs/services/chain-service/handler/bitcoin/strategy/types.js +1 -0
  36. package/cjs/services/chain-service/index.js +27 -3
  37. package/cjs/services/chain-service/utils/index.js +57 -4
  38. package/cjs/services/chain-service/utils/patch.js +1 -1
  39. package/cjs/services/earning-service/handlers/native-staking/para-chain.js +27 -5
  40. package/cjs/services/event-service/index.js +4 -0
  41. package/cjs/services/fee-service/service.js +8 -3
  42. package/cjs/services/hiro-service/index.js +96 -0
  43. package/cjs/services/hiro-service/utils/index.js +85 -0
  44. package/cjs/services/history-service/bitcoin-history.js +58 -0
  45. package/cjs/services/history-service/helpers/recoverHistoryStatus.js +96 -4
  46. package/cjs/services/history-service/index.js +41 -3
  47. package/cjs/services/keyring-service/context/handlers/Derive.js +1 -1
  48. package/cjs/services/keyring-service/context/handlers/Migration.js +2 -2
  49. package/cjs/services/keyring-service/context/handlers/Mnemonic.js +4 -3
  50. package/cjs/services/migration-service/scripts/MigrateNewUnifiedAccount.js +29 -0
  51. package/cjs/services/migration-service/scripts/index.js +3 -1
  52. package/cjs/services/request-service/handler/AuthRequestHandler.js +18 -0
  53. package/cjs/services/request-service/handler/BitcoinRequestHandler.js +427 -0
  54. package/cjs/services/request-service/index.js +29 -3
  55. package/cjs/services/rune-service/index.js +105 -0
  56. package/cjs/services/transaction-service/helpers/index.js +7 -1
  57. package/cjs/services/transaction-service/index.js +206 -16
  58. package/cjs/services/transaction-service/utils.js +6 -3
  59. package/cjs/strategy/api-request-strategy/context/base.js +31 -0
  60. package/cjs/strategy/api-request-strategy/index.js +90 -0
  61. package/cjs/strategy/api-request-strategy/types.js +1 -0
  62. package/cjs/strategy/api-request-strategy/utils/index.js +33 -0
  63. package/cjs/types/account/info/keyring.js +1 -1
  64. package/cjs/types/bitcoin.js +24 -0
  65. package/cjs/types/fee/bitcoin.js +1 -0
  66. package/cjs/types/fee/index.js +11 -0
  67. package/cjs/types/index.js +11 -0
  68. package/cjs/utils/account/analyze.js +3 -3
  69. package/cjs/utils/account/common.js +16 -6
  70. package/cjs/utils/account/derive/info/solo.js +68 -19
  71. package/cjs/utils/account/derive/info/unified.js +2 -0
  72. package/cjs/utils/account/derive/validate.js +70 -2
  73. package/cjs/utils/account/transform.js +11 -5
  74. package/cjs/utils/auth.js +2 -1
  75. package/cjs/utils/bitcoin/common.js +98 -0
  76. package/cjs/utils/bitcoin/fee.js +21 -0
  77. package/cjs/utils/bitcoin/index.js +38 -0
  78. package/cjs/utils/bitcoin/utxo-management.js +281 -0
  79. package/cjs/utils/fee/transfer.js +48 -0
  80. package/cjs/utils/index.js +15 -1
  81. package/constants/bitcoin.d.ts +3 -0
  82. package/constants/bitcoin.js +13 -0
  83. package/constants/index.d.ts +2 -0
  84. package/constants/index.js +3 -1
  85. package/core/logic-validation/recipientAddress.js +10 -1
  86. package/core/logic-validation/request.d.ts +6 -2
  87. package/core/logic-validation/request.js +309 -3
  88. package/core/logic-validation/transfer.d.ts +2 -2
  89. package/core/logic-validation/transfer.js +27 -7
  90. package/core/types.d.ts +1 -0
  91. package/core/types.js +1 -0
  92. package/core/utils.d.ts +1 -0
  93. package/core/utils.js +15 -2
  94. package/koni/background/handlers/Extension.d.ts +5 -0
  95. package/koni/background/handlers/Extension.js +387 -9
  96. package/koni/background/handlers/State.d.ts +10 -3
  97. package/koni/background/handlers/State.js +240 -15
  98. package/koni/background/handlers/Tabs.d.ts +7 -2
  99. package/koni/background/handlers/Tabs.js +119 -9
  100. package/package.json +149 -8
  101. package/packageInfo.js +1 -1
  102. package/page/bitcoin/index.d.ts +17 -0
  103. package/page/bitcoin/index.js +60 -0
  104. package/page/index.d.ts +2 -1
  105. package/page/index.js +4 -0
  106. package/services/balance-service/helpers/subscribe/bitcoin.d.ts +2 -0
  107. package/services/balance-service/helpers/subscribe/bitcoin.js +87 -0
  108. package/services/balance-service/helpers/subscribe/index.d.ts +2 -2
  109. package/services/balance-service/helpers/subscribe/index.js +20 -8
  110. package/services/balance-service/index.d.ts +2 -0
  111. package/services/balance-service/index.js +32 -4
  112. package/services/balance-service/transfer/bitcoin-transfer.d.ts +14 -0
  113. package/services/balance-service/transfer/bitcoin-transfer.js +112 -0
  114. package/services/balance-service/transfer/cardano-transfer.d.ts +2 -0
  115. package/services/balance-service/transfer/token.js +2 -0
  116. package/services/base/types.d.ts +2 -0
  117. package/services/base/types.js +2 -0
  118. package/services/buy-service/index.js +17 -2
  119. package/services/chain-service/constants.d.ts +6 -0
  120. package/services/chain-service/constants.js +8 -2
  121. package/services/chain-service/handler/bitcoin/BitcoinApi.d.ts +31 -0
  122. package/services/chain-service/handler/bitcoin/BitcoinApi.js +98 -0
  123. package/services/chain-service/handler/bitcoin/BitcoinChainHandler.d.ts +16 -0
  124. package/services/chain-service/handler/bitcoin/BitcoinChainHandler.js +70 -0
  125. package/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/blockstream-testnet-strategy.d.ts +28 -0
  126. package/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/blockstream-testnet-strategy.js +362 -0
  127. package/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/index.d.ts +2 -0
  128. package/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/index.js +5 -0
  129. package/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/mempool-testnet-strategy.d.ts +28 -0
  130. package/services/chain-service/handler/bitcoin/strategy/BlockStreamTestnet/mempool-testnet-strategy.js +359 -0
  131. package/services/chain-service/handler/bitcoin/strategy/SubWalletMainnet/index.d.ts +28 -0
  132. package/services/chain-service/handler/bitcoin/strategy/SubWalletMainnet/index.js +293 -0
  133. package/services/chain-service/handler/bitcoin/strategy/types.d.ts +291 -0
  134. package/services/chain-service/handler/bitcoin/strategy/types.js +1 -0
  135. package/services/chain-service/index.d.ts +3 -0
  136. package/services/chain-service/index.js +31 -5
  137. package/services/chain-service/types.d.ts +20 -0
  138. package/services/chain-service/utils/index.d.ts +4 -0
  139. package/services/chain-service/utils/index.js +50 -4
  140. package/services/chain-service/utils/patch.js +1 -1
  141. package/services/earning-service/handlers/native-staking/para-chain.js +27 -5
  142. package/services/event-service/index.d.ts +3 -0
  143. package/services/event-service/index.js +4 -0
  144. package/services/event-service/types.d.ts +3 -0
  145. package/services/fee-service/service.js +8 -3
  146. package/services/hiro-service/index.d.ts +17 -0
  147. package/services/hiro-service/index.js +88 -0
  148. package/services/hiro-service/utils/index.d.ts +6 -0
  149. package/services/hiro-service/utils/index.js +72 -0
  150. package/services/history-service/bitcoin-history.d.ts +4 -0
  151. package/services/history-service/bitcoin-history.js +52 -0
  152. package/services/history-service/helpers/recoverHistoryStatus.d.ts +3 -1
  153. package/services/history-service/helpers/recoverHistoryStatus.js +96 -4
  154. package/services/history-service/index.d.ts +1 -0
  155. package/services/history-service/index.js +42 -4
  156. package/services/keyring-service/context/handlers/Derive.js +2 -2
  157. package/services/keyring-service/context/handlers/Migration.js +2 -2
  158. package/services/keyring-service/context/handlers/Mnemonic.js +4 -3
  159. package/services/migration-service/scripts/MigrateNewUnifiedAccount.d.ts +4 -0
  160. package/services/migration-service/scripts/MigrateNewUnifiedAccount.js +21 -0
  161. package/services/migration-service/scripts/index.js +3 -1
  162. package/services/request-service/handler/AuthRequestHandler.js +19 -1
  163. package/services/request-service/handler/BitcoinRequestHandler.d.ts +22 -0
  164. package/services/request-service/handler/BitcoinRequestHandler.js +414 -0
  165. package/services/request-service/index.d.ts +8 -2
  166. package/services/request-service/index.js +25 -3
  167. package/services/rune-service/index.d.ts +17 -0
  168. package/services/rune-service/index.js +97 -0
  169. package/services/transaction-service/helpers/index.d.ts +3 -1
  170. package/services/transaction-service/helpers/index.js +5 -0
  171. package/services/transaction-service/index.d.ts +4 -5
  172. package/services/transaction-service/index.js +205 -17
  173. package/services/transaction-service/types.d.ts +13 -2
  174. package/services/transaction-service/utils.js +7 -4
  175. package/strategy/api-request-strategy/context/base.d.ts +15 -0
  176. package/strategy/api-request-strategy/context/base.js +24 -0
  177. package/strategy/api-request-strategy/index.d.ts +15 -0
  178. package/strategy/api-request-strategy/index.js +83 -0
  179. package/strategy/api-request-strategy/types.d.ts +22 -0
  180. package/strategy/api-request-strategy/types.js +1 -0
  181. package/strategy/api-request-strategy/utils/index.d.ts +2 -0
  182. package/strategy/api-request-strategy/utils/index.js +23 -0
  183. package/types/account/info/keyring.d.ts +1 -1
  184. package/types/account/info/keyring.js +1 -1
  185. package/types/balance/index.d.ts +4 -1
  186. package/types/balance/transfer.d.ts +19 -0
  187. package/types/bitcoin.d.ts +93 -0
  188. package/types/bitcoin.js +17 -0
  189. package/types/buy.d.ts +1 -1
  190. package/types/fee/base.d.ts +4 -1
  191. package/types/fee/bitcoin.d.ts +18 -0
  192. package/types/fee/bitcoin.js +1 -0
  193. package/types/fee/index.d.ts +1 -0
  194. package/types/fee/index.js +2 -1
  195. package/types/fee/subscription.d.ts +4 -3
  196. package/types/index.d.ts +1 -0
  197. package/types/index.js +1 -0
  198. package/utils/account/analyze.js +4 -4
  199. package/utils/account/common.d.ts +7 -8
  200. package/utils/account/common.js +16 -6
  201. package/utils/account/derive/info/solo.js +70 -21
  202. package/utils/account/derive/info/unified.js +2 -0
  203. package/utils/account/derive/validate.d.ts +1 -0
  204. package/utils/account/derive/validate.js +68 -1
  205. package/utils/account/transform.d.ts +1 -1
  206. package/utils/account/transform.js +11 -5
  207. package/utils/auth.js +3 -2
  208. package/utils/bitcoin/common.d.ts +22 -0
  209. package/utils/bitcoin/common.js +88 -0
  210. package/utils/bitcoin/fee.d.ts +2 -0
  211. package/utils/bitcoin/fee.js +14 -0
  212. package/utils/bitcoin/index.d.ts +3 -0
  213. package/utils/bitcoin/index.js +6 -0
  214. package/utils/bitcoin/utxo-management.d.ts +33 -0
  215. package/utils/bitcoin/utxo-management.js +266 -0
  216. package/utils/fee/transfer.d.ts +3 -1
  217. package/utils/fee/transfer.js +47 -1
  218. package/utils/index.d.ts +1 -0
  219. package/utils/index.js +6 -3
@@ -2,12 +2,13 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { ALL_ACCOUNT_AUTH_TYPES } from '@subwallet/extension-base/constants';
5
- import { _isChainCardanoCompatible, _isChainEvmCompatible } from '@subwallet/extension-base/services/chain-service/utils';
5
+ import { _isChainBitcoinCompatible, _isChainCardanoCompatible, _isChainEvmCompatible } from '@subwallet/extension-base/services/chain-service/utils';
6
6
  import { DAPP_CONNECT_BOTH_TYPE_ACCOUNT_URL, PREDEFINED_CHAIN_DAPP_CHAIN_MAP, WEB_APP_URL } from '@subwallet/extension-base/services/request-service/constants';
7
7
  import AuthorizeStore from '@subwallet/extension-base/stores/Authorize';
8
8
  import { createPromiseHandler, getDomainFromUrl, stripUrl } from '@subwallet/extension-base/utils';
9
9
  import { getId } from '@subwallet/extension-base/utils/getId';
10
10
  import { isCardanoAddress, isSubstrateAddress, isTonAddress } from '@subwallet/keyring';
11
+ import { isBitcoinAddress } from '@subwallet/keyring/utils/address/validate';
11
12
  import { BehaviorSubject } from 'rxjs';
12
13
  import { isEthereumAddress } from '@polkadot/util-crypto';
13
14
  const AUTH_URLS_KEY = 'authUrls';
@@ -166,6 +167,18 @@ export default class AuthRequestHandler {
166
167
  }
167
168
  }
168
169
  }
170
+ if (options.accessType === 'bitcoin') {
171
+ const bitcoinChains = Object.values(chainInfoMaps).filter(_isChainBitcoinCompatible);
172
+ chainInfo = (defaultChain ? chainInfoMaps[defaultChain] : chainInfoMaps.bitcoin) || bitcoinChains[0]; // auto active cardano mainnet chain, because dont support switch network yet
173
+
174
+ if (options.autoActive) {
175
+ var _chainInfo5;
176
+ if (!needEnableChains.includes((_chainInfo5 = chainInfo) === null || _chainInfo5 === void 0 ? void 0 : _chainInfo5.slug)) {
177
+ var _chainInfo6;
178
+ needEnableChains.push((_chainInfo6 = chainInfo) === null || _chainInfo6 === void 0 ? void 0 : _chainInfo6.slug);
179
+ }
180
+ }
181
+ }
169
182
  needEnableChains = needEnableChains.filter(slug => {
170
183
  var _chainStateMap$slug;
171
184
  return !((_chainStateMap$slug = chainStateMap[slug]) !== null && _chainStateMap$slug !== void 0 && _chainStateMap$slug.active);
@@ -217,6 +230,9 @@ export default class AuthRequestHandler {
217
230
  if (isCardanoAddress(a) && !accountAuthTypes.includes('cardano')) {
218
231
  return true;
219
232
  }
233
+ if (isBitcoinAddress(a) && !accountAuthTypes.includes('bitcoin')) {
234
+ return true;
235
+ }
220
236
  return false;
221
237
  });
222
238
  backupAllowed.forEach(acc => {
@@ -365,6 +381,8 @@ export default class AuthRequestHandler {
365
381
  list.push(...allowedListByRequestType.filter(a => isTonAddress(a)));
366
382
  } else if (accountAuthType === 'cardano') {
367
383
  list.push(...allowedListByRequestType.filter(a => isCardanoAddress(a)));
384
+ } else if (accountAuthType === 'bitcoin') {
385
+ list.push(...allowedListByRequestType.filter(a => isBitcoinAddress(a)));
368
386
  }
369
387
  return list;
370
388
  }, []);
@@ -0,0 +1,22 @@
1
+ import { BitcoinSignMessageResult, ConfirmationDefinitionsBitcoin, ConfirmationsQueueBitcoin, ConfirmationsQueueItemOptions, ConfirmationTypeBitcoin, RequestConfirmationCompleteBitcoin } from '@subwallet/extension-base/background/KoniTypes';
2
+ import { ChainService } from '@subwallet/extension-base/services/chain-service';
3
+ import RequestService from '@subwallet/extension-base/services/request-service';
4
+ import TransactionService from '@subwallet/extension-base/services/transaction-service';
5
+ import { BehaviorSubject } from 'rxjs';
6
+ export default class BitcoinRequestHandler {
7
+ #private;
8
+ private readonly confirmationsQueueSubjectBitcoin;
9
+ private readonly confirmationsPromiseMap;
10
+ constructor(requestService: RequestService, chainService: ChainService, transactionService: TransactionService);
11
+ get numBitcoinRequests(): number;
12
+ getConfirmationsQueueSubjectBitcoin(): BehaviorSubject<ConfirmationsQueueBitcoin>;
13
+ addConfirmationBitcoin<CT extends ConfirmationTypeBitcoin>(id: string, url: string, type: CT, payload: ConfirmationDefinitionsBitcoin[CT][0]['payload'], options?: ConfirmationsQueueItemOptions, validator?: (input: ConfirmationDefinitionsBitcoin[CT][1]) => Error | undefined): Promise<ConfirmationDefinitionsBitcoin[CT][1]>;
14
+ updateConfirmationBitcoin<CT extends ConfirmationTypeBitcoin>(id: string, type: CT, payload: ConfirmationDefinitionsBitcoin[CT][0]['payload'], options?: ConfirmationsQueueItemOptions, validator?: (input: ConfirmationDefinitionsBitcoin[CT][1]) => Error | undefined): void;
15
+ signMessageBitcoin(confirmation: ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest'][0]): BitcoinSignMessageResult;
16
+ private signTransactionBitcoin;
17
+ private signTransactionBitcoinWithPayload;
18
+ private signPsbt;
19
+ private decorateResultBitcoin;
20
+ completeConfirmationBitcoin(request: RequestConfirmationCompleteBitcoin): Promise<boolean>;
21
+ resetWallet(): void;
22
+ }
@@ -0,0 +1,414 @@
1
+ // Copyright 2019-2022 @subwallet/extension-base authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { BitcoinProviderError } from '@subwallet/extension-base/background/errors/BitcoinProviderError';
5
+ import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
6
+ import { BitcoinProviderErrorType } from '@subwallet/extension-base/background/KoniTypes';
7
+ import { BasicTxErrorType } from '@subwallet/extension-base/types';
8
+ import { createPromiseHandler } from '@subwallet/extension-base/utils';
9
+ import { isInternalRequest } from '@subwallet/extension-base/utils/request';
10
+ import keyring from '@subwallet/ui-keyring';
11
+ import { Psbt } from 'bitcoinjs-lib';
12
+ import * as bitcoin from 'bitcoinjs-lib';
13
+ import { t } from 'i18next';
14
+ import { BehaviorSubject } from 'rxjs';
15
+ import { isArray, logger as createLogger } from '@polkadot/util';
16
+ export default class BitcoinRequestHandler {
17
+ #requestService;
18
+ #chainService;
19
+ #transactionService;
20
+ #logger;
21
+ confirmationsQueueSubjectBitcoin = new BehaviorSubject({
22
+ bitcoinSignatureRequest: {},
23
+ bitcoinSendTransactionRequest: {},
24
+ bitcoinWatchTransactionRequest: {},
25
+ bitcoinSendTransactionRequestAfterConfirmation: {},
26
+ bitcoinSignPsbtRequest: {}
27
+ });
28
+ confirmationsPromiseMap = {};
29
+ constructor(requestService, chainService, transactionService) {
30
+ this.#requestService = requestService;
31
+ this.#chainService = chainService;
32
+ this.#transactionService = transactionService;
33
+ this.#logger = createLogger('BitcoinRequestHandler');
34
+ }
35
+ get numBitcoinRequests() {
36
+ let count = 0;
37
+ Object.values(this.confirmationsQueueSubjectBitcoin.getValue()).forEach(x => {
38
+ count += Object.keys(x).length;
39
+ });
40
+ return count;
41
+ }
42
+ getConfirmationsQueueSubjectBitcoin() {
43
+ return this.confirmationsQueueSubjectBitcoin;
44
+ }
45
+ async addConfirmationBitcoin(id, url, type, payload, options = {}, validator) {
46
+ const confirmations = this.confirmationsQueueSubjectBitcoin.getValue();
47
+ const confirmationType = confirmations[type];
48
+ const payloadJson = JSON.stringify(payload);
49
+ const isInternal = isInternalRequest(url);
50
+ if (['bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinSendTransactionRequestAfterConfirmation', 'bitcoinSignPsbtRequest'].includes(type)) {
51
+ const isAlwaysRequired = await this.#requestService.settingService.isAlwaysRequired;
52
+ if (isAlwaysRequired) {
53
+ this.#requestService.keyringService.lock();
54
+ }
55
+ }
56
+
57
+ // Check duplicate request
58
+ const duplicated = Object.values(confirmationType).find(c => c.url === url && c.payloadJson === payloadJson);
59
+ if (duplicated) {
60
+ throw new Error('Duplicate request');
61
+ }
62
+ confirmationType[id] = {
63
+ id,
64
+ url,
65
+ isInternal,
66
+ payload,
67
+ payloadJson,
68
+ ...options
69
+ };
70
+ const promise = new Promise((resolve, reject) => {
71
+ this.confirmationsPromiseMap[id] = {
72
+ validator: validator,
73
+ resolver: {
74
+ resolve: resolve,
75
+ reject: reject
76
+ }
77
+ };
78
+ });
79
+ this.confirmationsQueueSubjectBitcoin.next(confirmations);
80
+ if (!isInternal) {
81
+ this.#requestService.popupOpen();
82
+ }
83
+ this.#requestService.updateIconV2();
84
+ return promise;
85
+ }
86
+ updateConfirmationBitcoin(id, type, payload, options = {}, validator) {
87
+ const confirmations = this.confirmationsQueueSubjectBitcoin.getValue();
88
+ const confirmationType = confirmations[type];
89
+
90
+ // Check duplicate request
91
+ const exists = confirmationType[id];
92
+ if (!exists) {
93
+ throw new Error('Request does not exist');
94
+ }
95
+ const payloadJson = JSON.stringify(payload);
96
+ confirmationType[id] = {
97
+ ...exists,
98
+ payload,
99
+ payloadJson,
100
+ ...options
101
+ };
102
+ if (validator) {
103
+ this.confirmationsPromiseMap[id].validator = validator;
104
+ }
105
+ this.confirmationsQueueSubjectBitcoin.next(confirmations);
106
+ }
107
+ signMessageBitcoin(confirmation) {
108
+ const {
109
+ address,
110
+ payload
111
+ } = confirmation.payload;
112
+ const pair = keyring.getPair(address);
113
+ if (pair.isLocked) {
114
+ keyring.unlockPair(pair.address);
115
+ }
116
+
117
+ // Check if payload is a string
118
+ if (typeof payload === 'string') {
119
+ // Assume BitcoinSigner is an instance that implements the BitcoinSigner interface
120
+ return {
121
+ signature: pair.bitcoin.signMessage(payload),
122
+ message: payload,
123
+ address
124
+ }; // Assuming compressed = false
125
+ } else if (payload instanceof Uint8Array) {
126
+ // Check if payload is a byte array (Uint8Array)
127
+ // Convert Uint8Array to string
128
+ const payloadString = Buffer.from(payload).toString('hex');
129
+
130
+ // Assume BitcoinSigner is an instance that implements the BitcoinSigner interface
131
+ return {
132
+ signature: pair.bitcoin.signMessage(payloadString),
133
+ message: payload.toString(),
134
+ address
135
+ }; // Assuming compressed = false
136
+ } else {
137
+ // Handle the case where payload is invalid
138
+ throw new Error('Invalid payload type');
139
+ }
140
+ }
141
+ signTransactionBitcoin(request) {
142
+ // Extract necessary information from the BitcoinSendTransactionRequest
143
+ const {
144
+ address,
145
+ hashPayload
146
+ } = request.payload;
147
+ const pair = keyring.getPair(address);
148
+
149
+ // Unlock the pair if it is locked
150
+ if (pair.isLocked) {
151
+ keyring.unlockPair(pair.address);
152
+ }
153
+ const psbt = Psbt.fromHex(hashPayload);
154
+
155
+ // Finalize all inputs in the Psbt
156
+ // Sign the Psbt using the pair's bitcoin object
157
+ const signedTransaction = pair.bitcoin.signTransaction(psbt, psbt.txInputs.map((v, i) => i));
158
+ signedTransaction.finalizeAllInputs();
159
+ return signedTransaction.extractTransaction().toHex();
160
+ }
161
+ async signTransactionBitcoinWithPayload(request) {
162
+ const transaction = this.#transactionService.getTransaction(request.id);
163
+ const {
164
+ chain,
165
+ emitterTransaction,
166
+ id
167
+ } = transaction;
168
+ const {
169
+ from
170
+ } = transaction.data;
171
+ const {
172
+ promise,
173
+ reject,
174
+ resolve
175
+ } = createPromiseHandler();
176
+ if (!emitterTransaction) {
177
+ throw new BitcoinProviderError(BitcoinProviderErrorType.INTERNAL_ERROR);
178
+ }
179
+ const eventData = {
180
+ id,
181
+ errors: [],
182
+ warnings: [],
183
+ extrinsicHash: id
184
+ };
185
+ emitterTransaction.on('extrinsicHash', data => {
186
+ if (!data.extrinsicHash) {
187
+ reject(BitcoinProviderErrorType.INTERNAL_ERROR);
188
+ } else {
189
+ resolve(data.extrinsicHash);
190
+ }
191
+ });
192
+ emitterTransaction.on('error', error => {
193
+ reject(error);
194
+ });
195
+ try {
196
+ const chainInfo = this.#chainService.getChainInfoByKey(chain);
197
+ const psbt = transaction.transaction;
198
+ const pair = keyring.getPair(from);
199
+
200
+ // Unlock the pair if it is locked
201
+ if (pair.isLocked) {
202
+ keyring.unlockPair(pair.address);
203
+ }
204
+
205
+ // Finalize all inputs in the Psbt
206
+ // Sign the Psbt using the pair's bitcoin object
207
+ const signedTransaction = pair.bitcoin.signTransaction(psbt, psbt.txInputs.map((v, i) => i));
208
+ signedTransaction.finalizeAllInputs();
209
+ const signature = signedTransaction.extractTransaction().toHex();
210
+ this.#transactionService.emitterEventTransaction(emitterTransaction, eventData, chainInfo.slug, signature);
211
+ } catch (e) {
212
+ emitterTransaction.emit('error', {
213
+ ...eventData,
214
+ errors: [new TransactionError(BasicTxErrorType.INTERNAL_ERROR, e.message)]
215
+ });
216
+ }
217
+ return promise;
218
+ }
219
+ async signPsbt(request) {
220
+ // Extract necessary information from the BitcoinSendTransactionRequest
221
+ const {
222
+ address,
223
+ payload
224
+ } = request.payload;
225
+ const {
226
+ allowedSighash,
227
+ autoFinalized = true,
228
+ broadcast,
229
+ network,
230
+ psbt: psbtHex,
231
+ signAtIndex
232
+ } = payload;
233
+ const transaction = this.#transactionService.getTransaction(request.id);
234
+ const bitcoinNetwork = network === 'bitcoinTestnet' ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
235
+ const psbt = bitcoin.Psbt.fromHex(psbtHex, {
236
+ network: bitcoinNetwork
237
+ });
238
+ let eventData = {
239
+ id: request.id,
240
+ errors: [],
241
+ warnings: [],
242
+ extrinsicHash: request.id
243
+ };
244
+ const pair = keyring.getPair(address);
245
+
246
+ // Unlock the pair if it is locked
247
+ if (pair.isLocked) {
248
+ keyring.unlockPair(pair.address);
249
+ }
250
+ const signAtIndexGenerate = signAtIndex ? isArray(signAtIndex) ? signAtIndex : [signAtIndex] : [...Array(psbt.inputCount)].map((_, i) => i);
251
+ let psptSignedTransaction = null;
252
+
253
+ // Sign the Psbt using the pair's bitcoin object
254
+ try {
255
+ psptSignedTransaction = pair.bitcoin.signTransaction(psbt, signAtIndexGenerate, allowedSighash);
256
+ } catch (e) {
257
+ if (transaction) {
258
+ var _transaction$emitterT;
259
+ (_transaction$emitterT = transaction.emitterTransaction) === null || _transaction$emitterT === void 0 ? void 0 : _transaction$emitterT.emit('error', {
260
+ ...eventData,
261
+ errors: [new TransactionError(BasicTxErrorType.INVALID_PARAMS, e.message)],
262
+ id: transaction.id,
263
+ extrinsicHash: transaction.id
264
+ });
265
+ }
266
+ throw new Error(e.message);
267
+ }
268
+ if (!psptSignedTransaction) {
269
+ throw new Error('Unable to sign');
270
+ }
271
+ if (!broadcast) {
272
+ if (autoFinalized) {
273
+ for (const index of signAtIndexGenerate) {
274
+ psptSignedTransaction.finalizeInput(index);
275
+ }
276
+ }
277
+ return {
278
+ psbt: psptSignedTransaction.toHex()
279
+ };
280
+ }
281
+ if (!transaction) {
282
+ throw new BitcoinProviderError(BitcoinProviderErrorType.INTERNAL_ERROR);
283
+ }
284
+ const {
285
+ chain,
286
+ emitterTransaction,
287
+ id
288
+ } = transaction;
289
+ eventData = {
290
+ id,
291
+ errors: [],
292
+ warnings: [],
293
+ extrinsicHash: id
294
+ };
295
+ if (!emitterTransaction) {
296
+ throw new BitcoinProviderError(BitcoinProviderErrorType.INTERNAL_ERROR);
297
+ }
298
+ const chainInfo = this.#chainService.getChainInfoByKey(chain);
299
+ try {
300
+ if (!autoFinalized) {
301
+ throw new Error('Unable to send transaction without autoFinalized');
302
+ }
303
+ psptSignedTransaction.finalizeAllInputs();
304
+ } catch (e) {
305
+ emitterTransaction.emit('error', {
306
+ ...eventData,
307
+ errors: [new TransactionError(BasicTxErrorType.INVALID_PARAMS, e.message)]
308
+ });
309
+ throw new Error(e.message);
310
+ }
311
+ const hexTransaction = psptSignedTransaction.extractTransaction().toHex();
312
+ this.#transactionService.emitterEventTransaction(emitterTransaction, eventData, chainInfo.slug, hexTransaction);
313
+ const {
314
+ promise,
315
+ reject,
316
+ resolve
317
+ } = createPromiseHandler();
318
+ emitterTransaction.on('extrinsicHash', data => {
319
+ if (!data.extrinsicHash || !psptSignedTransaction) {
320
+ reject(BitcoinProviderErrorType.INTERNAL_ERROR);
321
+ } else {
322
+ var _psptSignedTransactio;
323
+ resolve({
324
+ psbt: (_psptSignedTransactio = psptSignedTransaction) === null || _psptSignedTransactio === void 0 ? void 0 : _psptSignedTransactio.toHex(),
325
+ txid: data.extrinsicHash
326
+ });
327
+ }
328
+ });
329
+ emitterTransaction.on('error', error => {
330
+ reject(error);
331
+ });
332
+ return promise;
333
+ }
334
+ async decorateResultBitcoin(t, request, result) {
335
+ if (t === 'bitcoinSignatureRequest') {
336
+ result.payload = this.signMessageBitcoin(request);
337
+ } else if (t === 'bitcoinSendTransactionRequest') {
338
+ result.payload = this.signTransactionBitcoin(request);
339
+ } else if (t === 'bitcoinSignPsbtRequest') {
340
+ result.payload = await this.signPsbt(request);
341
+ } else if (t === 'bitcoinSendTransactionRequestAfterConfirmation') {
342
+ result.payload = await this.signTransactionBitcoinWithPayload(request);
343
+ }
344
+ if (t === 'bitcoinSignatureRequest' || t === 'bitcoinSendTransactionRequest' || t === 'bitcoinSignPsbtRequest' || t === 'bitcoinSendTransactionRequestAfterConfirmation') {
345
+ const isAlwaysRequired = await this.#requestService.settingService.isAlwaysRequired;
346
+ if (isAlwaysRequired) {
347
+ this.#requestService.keyringService.lock();
348
+ }
349
+ }
350
+ }
351
+ async completeConfirmationBitcoin(request) {
352
+ const confirmations = this.confirmationsQueueSubjectBitcoin.getValue();
353
+ for (const ct in request) {
354
+ const type = ct;
355
+ const result = request[type];
356
+ const {
357
+ id,
358
+ isApproved
359
+ } = result;
360
+ const {
361
+ resolver,
362
+ validator
363
+ } = this.confirmationsPromiseMap[id];
364
+ const confirmation = confirmations[type][id];
365
+ if (!resolver || !confirmation) {
366
+ this.#logger.error(t('Unable to proceed. Please try again'), type, id);
367
+ throw new Error('Unable to proceed. Please try again');
368
+ }
369
+ if (isApproved) {
370
+ try {
371
+ // Fill signature for some special type
372
+ await this.decorateResultBitcoin(type, confirmation, result);
373
+ const error = validator && validator(result);
374
+ if (error) {
375
+ resolver.reject(error);
376
+ }
377
+ } catch (e) {
378
+ resolver.reject(e);
379
+ }
380
+ }
381
+
382
+ // Delete confirmations from queue
383
+ delete this.confirmationsPromiseMap[id];
384
+ delete confirmations[type][id];
385
+ this.confirmationsQueueSubjectBitcoin.next(confirmations);
386
+
387
+ // Update icon, and close queue
388
+ this.#requestService.updateIconV2(this.#requestService.numAllRequests === 0);
389
+ resolver.resolve(result);
390
+ }
391
+ return true;
392
+ }
393
+ resetWallet() {
394
+ const confirmations = this.confirmationsQueueSubjectBitcoin.getValue();
395
+ for (const [type, requests] of Object.entries(confirmations)) {
396
+ for (const confirmation of Object.values(requests)) {
397
+ const {
398
+ id
399
+ } = confirmation;
400
+ const {
401
+ resolver
402
+ } = this.confirmationsPromiseMap[id];
403
+ if (!resolver || !confirmation) {
404
+ console.error('Not found confirmation', type, id);
405
+ } else {
406
+ resolver.reject(new Error('Reset wallet'));
407
+ }
408
+ delete this.confirmationsPromiseMap[id];
409
+ delete confirmations[type][id];
410
+ }
411
+ }
412
+ this.confirmationsQueueSubjectBitcoin.next(confirmations);
413
+ }
414
+ }
@@ -1,8 +1,9 @@
1
- import { AuthRequestV2, ConfirmationDefinitions, ConfirmationDefinitionsCardano, ConfirmationDefinitionsTon, ConfirmationsQueue, ConfirmationsQueueCardano, ConfirmationsQueueItemOptions, ConfirmationsQueueTon, ConfirmationType, ConfirmationTypeCardano, ConfirmationTypeTon, RequestConfirmationComplete, RequestConfirmationCompleteCardano, RequestConfirmationCompleteTon } from '@subwallet/extension-base/background/KoniTypes';
1
+ import { AuthRequestV2, ConfirmationDefinitions, ConfirmationDefinitionsBitcoin, ConfirmationDefinitionsCardano, ConfirmationDefinitionsTon, ConfirmationsQueue, ConfirmationsQueueBitcoin, ConfirmationsQueueCardano, ConfirmationsQueueItemOptions, ConfirmationsQueueTon, ConfirmationType, ConfirmationTypeBitcoin, ConfirmationTypeCardano, ConfirmationTypeTon, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestConfirmationCompleteCardano, RequestConfirmationCompleteTon } from '@subwallet/extension-base/background/KoniTypes';
2
2
  import { AccountAuthType, AuthorizeRequest, MetadataRequest, RequestAuthorizeTab, RequestSign, ResponseSigning, SigningRequest } from '@subwallet/extension-base/background/types';
3
3
  import { ChainService } from '@subwallet/extension-base/services/chain-service';
4
4
  import { KeyringService } from '@subwallet/extension-base/services/keyring-service';
5
5
  import SettingService from '@subwallet/extension-base/services/setting-service/SettingService';
6
+ import TransactionService from '@subwallet/extension-base/services/transaction-service';
6
7
  import { WalletConnectNotSupportRequest, WalletConnectSessionRequest } from '@subwallet/extension-base/services/wallet-connect-service/types';
7
8
  import { MetadataDef } from '@subwallet/extension-inject/types';
8
9
  import { BehaviorSubject } from 'rxjs';
@@ -12,7 +13,7 @@ export default class RequestService {
12
13
  #private;
13
14
  readonly settingService: SettingService;
14
15
  readonly keyringService: KeyringService;
15
- constructor(chainService: ChainService, settingService: SettingService, keyringService: KeyringService);
16
+ constructor(chainService: ChainService, settingService: SettingService, keyringService: KeyringService, transactionService: TransactionService);
16
17
  get numAllRequests(): number;
17
18
  updateIconV2(shouldClose?: boolean): void;
18
19
  getAddressList(value?: boolean): Record<string, boolean>;
@@ -48,6 +49,7 @@ export default class RequestService {
48
49
  get numEvmRequests(): number;
49
50
  get numTonRequests(): number;
50
51
  get numCardanoRequests(): number;
52
+ get numBitcoinRequests(): number;
51
53
  get confirmationsQueueSubject(): BehaviorSubject<ConfirmationsQueue>;
52
54
  get confirmationsQueueSubjectTon(): BehaviorSubject<ConfirmationsQueueTon>;
53
55
  get confirmationsQueueSubjectCardano(): BehaviorSubject<ConfirmationsQueueCardano>;
@@ -60,6 +62,10 @@ export default class RequestService {
60
62
  completeConfirmationTon(request: RequestConfirmationCompleteTon): Promise<boolean>;
61
63
  completeConfirmationCardano(request: RequestConfirmationCompleteCardano): Promise<boolean>;
62
64
  updateConfirmation<CT extends ConfirmationType>(id: string, type: CT, payload: ConfirmationDefinitions[CT][0]['payload'], options?: ConfirmationsQueueItemOptions, validator?: (input: ConfirmationDefinitions[CT][1]) => Error | undefined): void;
65
+ get confirmationsQueueSubjectBitcoin(): BehaviorSubject<ConfirmationsQueueBitcoin>;
66
+ addConfirmationBitcoin<CT extends ConfirmationTypeBitcoin>(id: string, url: string, type: CT, payload: ConfirmationDefinitionsBitcoin[CT][0]['payload'], options?: ConfirmationsQueueItemOptions, validator?: (input: ConfirmationDefinitionsBitcoin[CT][1]) => Error | undefined): Promise<ConfirmationDefinitionsBitcoin[CT][1]>;
67
+ completeConfirmationBitcoin(request: RequestConfirmationCompleteBitcoin): Promise<boolean>;
68
+ updateConfirmationBitcoin<CT extends ConfirmationTypeBitcoin>(id: string, type: CT, payload: ConfirmationDefinitionsBitcoin[CT][0]['payload'], options?: ConfirmationsQueueItemOptions, validator?: (input: ConfirmationDefinitionsBitcoin[CT][1]) => Error | undefined): void;
63
69
  getConnectWCRequest(id: string): import("@subwallet/extension-base/services/wallet-connect-service/types").RequestWalletConnectSession;
64
70
  get connectWCSubject(): BehaviorSubject<WalletConnectSessionRequest[]>;
65
71
  get allConnectWCRequests(): WalletConnectSessionRequest[];
@@ -1,6 +1,7 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-base authors & contributors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
+ import BitcoinRequestHandler from '@subwallet/extension-base/services/request-service/handler/BitcoinRequestHandler';
4
5
  import CardanoRequestHandler from '@subwallet/extension-base/services/request-service/handler/CardanoRequestHandler';
5
6
  import TonRequestHandler from "./handler/TonRequestHandler.js";
6
7
  import { AuthRequestHandler, ConnectWCRequestHandler, EvmRequestHandler, MetadataRequestHandler, NotSupportWCRequestHandler, PopupHandler, SubstrateRequestHandler } from "./handler/index.js";
@@ -12,13 +13,14 @@ export default class RequestService {
12
13
  #authRequestHandler;
13
14
  #substrateRequestHandler;
14
15
  #evmRequestHandler;
16
+ #bitcoinRequestHandler;
15
17
  #tonRequestHandler;
16
18
  #cardanoRequestHandler;
17
19
  #connectWCRequestHandler;
18
20
  #notSupportWCRequestHandler;
19
21
 
20
22
  // Common
21
- constructor(chainService, settingService, keyringService) {
23
+ constructor(chainService, settingService, keyringService, transactionService) {
22
24
  this.#chainService = chainService;
23
25
  this.settingService = settingService;
24
26
  this.keyringService = keyringService;
@@ -29,6 +31,7 @@ export default class RequestService {
29
31
  this.#evmRequestHandler = new EvmRequestHandler(this);
30
32
  this.#tonRequestHandler = new TonRequestHandler(this);
31
33
  this.#cardanoRequestHandler = new CardanoRequestHandler(this);
34
+ this.#bitcoinRequestHandler = new BitcoinRequestHandler(this, this.#chainService, transactionService);
32
35
  this.#connectWCRequestHandler = new ConnectWCRequestHandler(this);
33
36
  this.#notSupportWCRequestHandler = new NotSupportWCRequestHandler(this);
34
37
 
@@ -36,7 +39,7 @@ export default class RequestService {
36
39
  this.updateIconV2();
37
40
  }
38
41
  get numAllRequests() {
39
- return this.allSubstrateRequests.length + this.numEvmRequests + this.numTonRequests + this.numCardanoRequests;
42
+ return this.allSubstrateRequests.length + this.numEvmRequests + this.numTonRequests + this.numCardanoRequests + this.numBitcoinRequests;
40
43
  }
41
44
  updateIconV2(shouldClose) {
42
45
  this.#popupHandler.updateIconV2(shouldClose);
@@ -148,6 +151,9 @@ export default class RequestService {
148
151
  get numCardanoRequests() {
149
152
  return this.#cardanoRequestHandler.numCardanoRequests;
150
153
  }
154
+ get numBitcoinRequests() {
155
+ return this.#bitcoinRequestHandler.numBitcoinRequests;
156
+ }
151
157
  get confirmationsQueueSubject() {
152
158
  return this.#evmRequestHandler.getConfirmationsQueueSubject();
153
159
  }
@@ -185,6 +191,21 @@ export default class RequestService {
185
191
  return this.#evmRequestHandler.updateConfirmation(id, type, payload, options, validator);
186
192
  }
187
193
 
194
+ // Bitcoin requests
195
+
196
+ get confirmationsQueueSubjectBitcoin() {
197
+ return this.#bitcoinRequestHandler.getConfirmationsQueueSubjectBitcoin();
198
+ }
199
+ addConfirmationBitcoin(id, url, type, payload, options = {}, validator) {
200
+ return this.#bitcoinRequestHandler.addConfirmationBitcoin(id, url, type, payload, options, validator);
201
+ }
202
+ async completeConfirmationBitcoin(request) {
203
+ return await this.#bitcoinRequestHandler.completeConfirmationBitcoin(request);
204
+ }
205
+ updateConfirmationBitcoin(id, type, payload, options = {}, validator) {
206
+ return this.#bitcoinRequestHandler.updateConfirmationBitcoin(id, type, payload, options, validator);
207
+ }
208
+
188
209
  // WalletConnect Connect requests
189
210
  getConnectWCRequest(id) {
190
211
  return this.#connectWCRequestHandler.getConnectWCRequest(id);
@@ -221,7 +242,7 @@ export default class RequestService {
221
242
 
222
243
  // General methods
223
244
  get numRequests() {
224
- return this.numMetaRequests + this.numAuthRequests + this.numSubstrateRequests + this.numEvmRequests + this.numConnectWCRequests + this.numNotSupportWCRequests + this.numTonRequests + this.numCardanoRequests;
245
+ return this.numMetaRequests + this.numAuthRequests + this.numSubstrateRequests + this.numEvmRequests + this.numConnectWCRequests + this.numNotSupportWCRequests + this.numTonRequests + this.numCardanoRequests + this.numBitcoinRequests;
225
246
  }
226
247
  resetWallet() {
227
248
  this.#authRequestHandler.resetWallet();
@@ -229,6 +250,7 @@ export default class RequestService {
229
250
  this.#evmRequestHandler.resetWallet();
230
251
  this.#tonRequestHandler.resetWallet();
231
252
  this.#cardanoRequestHandler.resetWallet();
253
+ this.#bitcoinRequestHandler.resetWallet();
232
254
  this.#metadataRequestHandler.resetWallet();
233
255
  this.#connectWCRequestHandler.resetWallet();
234
256
  this.#notSupportWCRequestHandler.resetWallet();
@@ -0,0 +1,17 @@
1
+ import { RuneMetadata, RunesCollectionInfoResponse, RunesInfoByAddressFetchedData, RuneTxsResponse, RuneUtxoResponse } from '@subwallet/extension-base/services/chain-service/handler/bitcoin/strategy/types';
2
+ import { BaseApiRequestStrategy } from '@subwallet/extension-base/strategy/api-request-strategy';
3
+ export declare class RunesService extends BaseApiRequestStrategy {
4
+ baseUrl: string;
5
+ private constructor();
6
+ private headers;
7
+ isRateLimited(): boolean;
8
+ getUrl(path: string): string;
9
+ getAddressRunesInfo(address: string, params: Record<string, string>): Promise<RunesInfoByAddressFetchedData>;
10
+ getRuneCollectionsByBatch(params: Record<string, string>): Promise<RunesCollectionInfoResponse>;
11
+ getAddressRuneTxs(address: string, params: Record<string, string>): Promise<RuneTxsResponse>;
12
+ getRuneMetadata(runeid: string): Promise<RuneMetadata>;
13
+ getAddressRuneUtxos(address: string): Promise<RuneUtxoResponse>;
14
+ private static mainnet;
15
+ private static testnet;
16
+ static getInstance(isTestnet?: boolean): RunesService;
17
+ }