@dynamic-labs/bitcoin 0.0.0-exp20240808.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 (112) hide show
  1. package/CHANGELOG.md +3746 -0
  2. package/LICENSE +21 -0
  3. package/README.md +0 -0
  4. package/_virtual/_tslib.cjs +36 -0
  5. package/_virtual/_tslib.js +32 -0
  6. package/package.json +41 -0
  7. package/src/BitcoinLocalStorageCache.cjs +67 -0
  8. package/src/BitcoinLocalStorageCache.d.ts +26 -0
  9. package/src/BitcoinLocalStorageCache.js +63 -0
  10. package/src/bitcoinProviderHelper.cjs +65 -0
  11. package/src/bitcoinProviderHelper.d.ts +29 -0
  12. package/src/bitcoinProviderHelper.js +61 -0
  13. package/src/bitcoinWalletStandardWallets.cjs +34 -0
  14. package/src/bitcoinWalletStandardWallets.d.ts +12 -0
  15. package/src/bitcoinWalletStandardWallets.js +29 -0
  16. package/src/connectors/BitcoinBtcKitConnector/BitcoinBtcKitConnector.cjs +124 -0
  17. package/src/connectors/BitcoinBtcKitConnector/BitcoinBtcKitConnector.d.ts +18 -0
  18. package/src/connectors/BitcoinBtcKitConnector/BitcoinBtcKitConnector.js +120 -0
  19. package/src/connectors/BitcoinBtcKitConnector/index.d.ts +2 -0
  20. package/src/connectors/BitcoinSatsConnectConnector/BitcoinSatsConnectConnector.cjs +223 -0
  21. package/src/connectors/BitcoinSatsConnectConnector/BitcoinSatsConnectConnector.d.ts +12 -0
  22. package/src/connectors/BitcoinSatsConnectConnector/BitcoinSatsConnectConnector.js +219 -0
  23. package/src/connectors/BitcoinSatsConnectConnector/index.d.ts +1 -0
  24. package/src/connectors/BitcoinWalletConnector.cjs +276 -0
  25. package/src/connectors/BitcoinWalletConnector.d.ts +50 -0
  26. package/src/connectors/BitcoinWalletConnector.js +272 -0
  27. package/src/connectors/FallbackBitcoinConnector/FallbackBitcoinConnector.cjs +37 -0
  28. package/src/connectors/FallbackBitcoinConnector/FallbackBitcoinConnector.d.ts +11 -0
  29. package/src/connectors/FallbackBitcoinConnector/FallbackBitcoinConnector.js +33 -0
  30. package/src/connectors/FallbackBitcoinConnector/index.d.ts +1 -0
  31. package/src/connectors/OkxConnector/OkxConnector.cjs +83 -0
  32. package/src/connectors/OkxConnector/OkxConnector.d.ts +11 -0
  33. package/src/connectors/OkxConnector/OkxConnector.js +79 -0
  34. package/src/connectors/OkxConnector/index.d.ts +1 -0
  35. package/src/connectors/PhantomConnector/PhantomConnector.cjs +146 -0
  36. package/src/connectors/PhantomConnector/PhantomConnector.d.ts +11 -0
  37. package/src/connectors/PhantomConnector/PhantomConnector.js +142 -0
  38. package/src/connectors/PhantomConnector/index.d.ts +1 -0
  39. package/src/connectors/UnisatConnector/UnisatConnector.cjs +78 -0
  40. package/src/connectors/UnisatConnector/UnisatConnector.d.ts +12 -0
  41. package/src/connectors/UnisatConnector/UnisatConnector.js +74 -0
  42. package/src/connectors/UnisatConnector/index.d.ts +1 -0
  43. package/src/connectors/UnknownInjected/UnknownInjected.cjs +36 -0
  44. package/src/connectors/UnknownInjected/UnknownInjected.d.ts +10 -0
  45. package/src/connectors/UnknownInjected/UnknownInjected.js +32 -0
  46. package/src/connectors/UnknownInjected/index.d.ts +1 -0
  47. package/src/connectors/index.d.ts +8 -0
  48. package/src/const.cjs +20 -0
  49. package/src/const.d.ts +7 -0
  50. package/src/const.js +10 -0
  51. package/src/index.cjs +44 -0
  52. package/src/index.d.ts +6 -0
  53. package/src/index.js +34 -0
  54. package/src/types.d.ts +129 -0
  55. package/src/utils/base64.cjs +10 -0
  56. package/src/utils/base64.d.ts +2 -0
  57. package/src/utils/base64.js +5 -0
  58. package/src/utils/fetchBtcKitConnectors/fetchBtcKitConnectors.cjs +30 -0
  59. package/src/utils/fetchBtcKitConnectors/fetchBtcKitConnectors.d.ts +5 -0
  60. package/src/utils/fetchBtcKitConnectors/fetchBtcKitConnectors.js +26 -0
  61. package/src/utils/fetchBtcKitConnectors/index.d.ts +1 -0
  62. package/src/utils/fetchSatsConnectConnectors/fetchSatsConnectConnectors.cjs +41 -0
  63. package/src/utils/fetchSatsConnectConnectors/fetchSatsConnectConnectors.d.ts +5 -0
  64. package/src/utils/fetchSatsConnectConnectors/fetchSatsConnectConnectors.js +37 -0
  65. package/src/utils/fetchSatsConnectConnectors/index.d.ts +1 -0
  66. package/src/utils/getMempoolApiUrl.cjs +10 -0
  67. package/src/utils/getMempoolApiUrl.d.ts +1 -0
  68. package/src/utils/getMempoolApiUrl.js +6 -0
  69. package/src/utils/hasSatsConnectFeature.cjs +10 -0
  70. package/src/utils/hasSatsConnectFeature.d.ts +2 -0
  71. package/src/utils/hasSatsConnectFeature.js +6 -0
  72. package/src/utils/index.d.ts +5 -0
  73. package/src/utils/psbt/bitcoinNetworkTypeToNetworks.cjs +14 -0
  74. package/src/utils/psbt/bitcoinNetworkTypeToNetworks.d.ts +3 -0
  75. package/src/utils/psbt/bitcoinNetworkTypeToNetworks.js +10 -0
  76. package/src/utils/psbt/createSignPsbtOptions.cjs +36 -0
  77. package/src/utils/psbt/createSignPsbtOptions.d.ts +3 -0
  78. package/src/utils/psbt/createSignPsbtOptions.js +32 -0
  79. package/src/utils/psbt/extractAddressFromInput.cjs +35 -0
  80. package/src/utils/psbt/extractAddressFromInput.d.ts +6 -0
  81. package/src/utils/psbt/extractAddressFromInput.js +31 -0
  82. package/src/utils/psbt/getSigHashType.cjs +36 -0
  83. package/src/utils/psbt/getSigHashType.d.ts +8 -0
  84. package/src/utils/psbt/getSigHashType.js +32 -0
  85. package/src/utils/psbt/index.d.ts +5 -0
  86. package/src/utils/psbt/sighashNumberToString.cjs +27 -0
  87. package/src/utils/psbt/sighashNumberToString.d.ts +2 -0
  88. package/src/utils/psbt/sighashNumberToString.js +23 -0
  89. package/src/utils/psbt/validator/index.d.ts +1 -0
  90. package/src/utils/psbt/validator/validateAddress.cjs +19 -0
  91. package/src/utils/psbt/validator/validateAddress.d.ts +2 -0
  92. package/src/utils/psbt/validator/validateAddress.js +15 -0
  93. package/src/utils/psbt/validator/validatePsbt.cjs +41 -0
  94. package/src/utils/psbt/validator/validatePsbt.d.ts +11 -0
  95. package/src/utils/psbt/validator/validatePsbt.js +37 -0
  96. package/src/utils/psbt/validator/validateSigHash.cjs +16 -0
  97. package/src/utils/psbt/validator/validateSigHash.d.ts +2 -0
  98. package/src/utils/psbt/validator/validateSigHash.js +12 -0
  99. package/src/utils/satoshisToBtc/index.d.ts +1 -0
  100. package/src/utils/satoshisToBtc/satoshisToBtc.cjs +8 -0
  101. package/src/utils/satoshisToBtc/satoshisToBtc.d.ts +1 -0
  102. package/src/utils/satoshisToBtc/satoshisToBtc.js +4 -0
  103. package/src/utils/supportsSatsConnect.cjs +27 -0
  104. package/src/utils/supportsSatsConnect.d.ts +2 -0
  105. package/src/utils/supportsSatsConnect.js +23 -0
  106. package/src/wallet/BitcoinWallet.cjs +43 -0
  107. package/src/wallet/BitcoinWallet.d.ts +21 -0
  108. package/src/wallet/BitcoinWallet.js +39 -0
  109. package/src/wallet/index.d.ts +2 -0
  110. package/src/wallet/isBitcoinWallet.cjs +8 -0
  111. package/src/wallet/isBitcoinWallet.d.ts +3 -0
  112. package/src/wallet/isBitcoinWallet.js +4 -0
@@ -0,0 +1,276 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var _tslib = require('../../_virtual/_tslib.cjs');
7
+ var satsConnect = require('sats-connect');
8
+ var walletConnectorCore = require('@dynamic-labs/wallet-connector-core');
9
+ var walletBook = require('@dynamic-labs/wallet-book');
10
+ var utils = require('@dynamic-labs/utils');
11
+ var sdkApiCore = require('@dynamic-labs/sdk-api-core');
12
+ var BitcoinLocalStorageCache = require('../BitcoinLocalStorageCache.cjs');
13
+ var bitcoinProviderHelper = require('../bitcoinProviderHelper.cjs');
14
+ var getMempoolApiUrl = require('../utils/getMempoolApiUrl.cjs');
15
+ var _const = require('../const.cjs');
16
+ var satoshisToBtc = require('../utils/satoshisToBtc/satoshisToBtc.cjs');
17
+ var BitcoinWallet = require('../wallet/BitcoinWallet.cjs');
18
+
19
+ class BitcoinWalletConnector extends walletConnectorCore.WalletConnectorBase {
20
+ constructor(opts) {
21
+ var _a;
22
+ super(opts);
23
+ this.ChainWallet = BitcoinWallet.BitcoinWallet;
24
+ this.connectedChain = 'BTC';
25
+ this.supportedChains = ['BTC'];
26
+ // some wallets don't support fetching connected accounts without prompting for a connection
27
+ this.canFetchConnectedAccounts = false;
28
+ this.isHardwareWalletEnabled = false;
29
+ this.verifiedCredentials = [];
30
+ // this is the key from the wallet book entry so that we don't purely rely on the normalized name
31
+ this.overrideKey = (_a = opts.overrideKey) !== null && _a !== void 0 ? _a : this.key;
32
+ const walletBookWallet = opts.walletData || walletBook.getWalletBookWallet(this.walletBook, this.key);
33
+ this.bitcoinProviderHelper = new bitcoinProviderHelper.BitcoinProviderHelper(walletBookWallet);
34
+ this.wallet = this.bitcoinProviderHelper.findWallet();
35
+ if (this.wallet) {
36
+ this.walletMethods = this.bitcoinProviderHelper.getWalletMethods(this.wallet);
37
+ }
38
+ this.cache = new BitcoinLocalStorageCache.BitcoinLocalStorageCache(this.overrideKey);
39
+ this.canFetchConnectedAccounts = walletBook.isWalletMethodSupported(walletBookWallet, 'getConnectedAccounts', 'browserExtension');
40
+ }
41
+ isSameAccountChangeRequest(to) {
42
+ return this.lastAccountChange === to;
43
+ }
44
+ setLastAccountChangeRequest(to) {
45
+ this.lastAccountChange = to;
46
+ }
47
+ clearConnectedAccounts() {
48
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
49
+ yield this.cache.clearConnectedAcccounts();
50
+ });
51
+ }
52
+ canConnectWithHardwareWallet() {
53
+ const wallet = walletBook.findWalletBookWallet(this.walletBook, this.key);
54
+ if (!wallet || !wallet.hardwareWallets)
55
+ return false;
56
+ return wallet.hardwareWallets.includes('ledger');
57
+ }
58
+ isInstalledOnBrowser() {
59
+ var _a;
60
+ return (Boolean(this.wallet) || Boolean((_a = this.bitcoinProviderHelper) === null || _a === void 0 ? void 0 : _a.getProvider()));
61
+ }
62
+ getDeepLink() {
63
+ return undefined;
64
+ }
65
+ endSession() {
66
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
67
+ yield this.cache.clearConnectedAcccounts();
68
+ });
69
+ }
70
+ getBalance(address) {
71
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
72
+ const API_URL = getMempoolApiUrl.getMempoolApiUrl(address);
73
+ const response = yield fetch(`${API_URL}/address/${address}`);
74
+ if (!response.ok) {
75
+ // if the request fails due to rate limits, return cached value
76
+ if (response.status === _const.HTTP_STATUS_TOO_MANY_REQUESTS) {
77
+ return '0';
78
+ }
79
+ // new accounts not yet indexed will return a 404
80
+ if (response.status === _const.HTTP_STATUS_NOT_FOUND) {
81
+ return '0';
82
+ }
83
+ return undefined;
84
+ }
85
+ const addressInfo = yield response.json();
86
+ if (!(addressInfo === null || addressInfo === void 0 ? void 0 : addressInfo.chain_stats) || !(addressInfo === null || addressInfo === void 0 ? void 0 : addressInfo.mempool_stats)) {
87
+ return undefined;
88
+ }
89
+ const confirmedBalanceInSats = Number(addressInfo.chain_stats.funded_txo_sum) -
90
+ Number(addressInfo.chain_stats.spent_txo_sum);
91
+ const unconfirmedBalanceInSats = Number(addressInfo.mempool_stats.funded_txo_sum) -
92
+ Number(addressInfo.mempool_stats.spent_txo_sum);
93
+ const balance = satoshisToBtc.satoshisToBtc(confirmedBalanceInSats + unconfirmedBalanceInSats);
94
+ return balance.toString();
95
+ });
96
+ }
97
+ getConnectedAccountsFromCache() {
98
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
99
+ var _a;
100
+ const currentAccount = yield this.cache.getActiveAccount();
101
+ const allAccounts = yield this.cache.getConnectedAccounts();
102
+ const allConnectedAddresses = (_a = Object.keys(allAccounts || {})) !== null && _a !== void 0 ? _a : [];
103
+ if (!(currentAccount === null || currentAccount === void 0 ? void 0 : currentAccount.address)) {
104
+ return allConnectedAddresses;
105
+ }
106
+ // return all connected accounts with the current account as the first item
107
+ return [
108
+ currentAccount.address,
109
+ ...allConnectedAddresses.filter((address) => address !== currentAccount.address),
110
+ ];
111
+ });
112
+ }
113
+ getConnectedAccounts() {
114
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
115
+ // some wallets like xverse don't support fetching connected accounts
116
+ // without prompting for a connection
117
+ // to avoid this behavior, we cache the connected accounts
118
+ if (!this.canFetchConnectedAccounts) {
119
+ return this.getConnectedAccountsFromCache();
120
+ }
121
+ // if we decide that is ok to prompt for a connection when fetching connected accounts
122
+ // we shouldn't prompt every time we call this method (which is a lot of times)
123
+ // so we just store in a promise and return the same promise every time
124
+ if (!this.getAddressPromise) {
125
+ this.getAddressPromise = this.getAddress();
126
+ }
127
+ let connectedAccount;
128
+ try {
129
+ connectedAccount = yield this.getAddressPromise;
130
+ }
131
+ catch (error) {
132
+ walletConnectorCore.logger.error(`${this.key} getConnectedAccounts - error fetching connected account`);
133
+ //don't throw error just return empty array after clearing the promise
134
+ }
135
+ this.getAddressPromise = undefined;
136
+ if (!connectedAccount) {
137
+ return [];
138
+ }
139
+ return [connectedAccount];
140
+ });
141
+ }
142
+ getAdditionalAddresses(mainAddress) {
143
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
144
+ if (!mainAddress) {
145
+ return [];
146
+ }
147
+ const currentAccount = yield this.cache.getConnectedAccount(mainAddress);
148
+ return (currentAccount === null || currentAccount === void 0 ? void 0 : currentAccount.additionalAddresses) || [];
149
+ });
150
+ }
151
+ setAdditionalAddresses(mainAddress, additionalAddresses) {
152
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
153
+ return this.cache.setConnectedAccount(mainAddress, {
154
+ additionalAddresses,
155
+ });
156
+ });
157
+ }
158
+ sendRawTransaction(rawTransaction) {
159
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
160
+ if (!rawTransaction) {
161
+ throw new utils.DynamicError('sendRawTransaction - No transaction specified!');
162
+ }
163
+ const [connectedAddress] = yield this.getConnectedAccounts();
164
+ if (!connectedAddress) {
165
+ throw new utils.DynamicError('sendRawTransaction - No connected address found!');
166
+ }
167
+ const API_URL = getMempoolApiUrl.getMempoolApiUrl(connectedAddress);
168
+ const response = yield fetch(`${API_URL}/tx`, {
169
+ body: rawTransaction,
170
+ headers: {
171
+ 'Content-Type': 'application/x-www-form-urlencoded',
172
+ },
173
+ method: 'POST',
174
+ });
175
+ if (!response.ok) {
176
+ if (response.status === _const.HTTP_STATUS_TOO_MANY_REQUESTS) {
177
+ throw new utils.DynamicError('sendRawTransaction - mempool api rate limit exceeded');
178
+ }
179
+ const error = yield response.text();
180
+ walletConnectorCore.logger.debug(`sendRawTransaction - response not ok: ${JSON.stringify(error)}`);
181
+ throw new utils.DynamicError('sendRawTransaction - failed to send transaction');
182
+ }
183
+ // this endpoint returns the transaction ID
184
+ return response.text();
185
+ });
186
+ }
187
+ getProvider() {
188
+ var _a;
189
+ return (_a = this.bitcoinProviderHelper) === null || _a === void 0 ? void 0 : _a.getProvider();
190
+ }
191
+ setConnectedAccountWithAddresses(_a) {
192
+ return _tslib.__awaiter(this, arguments, void 0, function* ({ mainAddress, ordinalsAddress, paymentAddress, active, }) {
193
+ if (!mainAddress) {
194
+ return;
195
+ }
196
+ const additionalAddresses = [];
197
+ if (ordinalsAddress) {
198
+ additionalAddresses.push({
199
+ address: ordinalsAddress.address,
200
+ publicKey: ordinalsAddress.publicKey,
201
+ type: sdkApiCore.WalletAddressType.Ordinals,
202
+ });
203
+ }
204
+ if (paymentAddress) {
205
+ additionalAddresses.push({
206
+ address: paymentAddress.address,
207
+ publicKey: paymentAddress.publicKey,
208
+ type: sdkApiCore.WalletAddressType.Payment,
209
+ });
210
+ }
211
+ this.cache.setConnectedAccount(mainAddress, {
212
+ active,
213
+ additionalAddresses,
214
+ });
215
+ });
216
+ }
217
+ setupEventListeners() {
218
+ const provider = this.getProvider();
219
+ if (!(provider === null || provider === void 0 ? void 0 : provider.on)) {
220
+ return;
221
+ }
222
+ const { handleAccountChange, handleChainChange, handleDisconnect } = walletConnectorCore.eventListenerHandlers(this);
223
+ const handleBitcoinAccountChange = (accounts) => _tslib.__awaiter(this, void 0, void 0, function* () {
224
+ let connectedAccounts = accounts;
225
+ let ordinalsAccount, paymentAccount;
226
+ // if accounts is an array of objects, we need to parse them to return only addresses
227
+ // since ordinals is the main address we use, we should return it as the first address
228
+ if (typeof accounts[0] === 'object') {
229
+ connectedAccounts = accounts
230
+ .sort((account) => account.purpose === satsConnect.AddressPurpose.Ordinals ? -1 : 1)
231
+ .map((account) => account.address);
232
+ [ordinalsAccount, paymentAccount] = connectedAccounts;
233
+ }
234
+ const currentConnectedAccounts = yield this.getConnectedAccountsFromCache();
235
+ // don't do anything if the connected accounts haven't changed
236
+ // or if the account change request is the same as previous request
237
+ if (currentConnectedAccounts[0] === connectedAccounts[0] ||
238
+ this.isSameAccountChangeRequest(connectedAccounts[0])) {
239
+ return;
240
+ }
241
+ // set the last account change request with the from and to addresses
242
+ // to ensure that the requests are not duplicated
243
+ this.setLastAccountChangeRequest(connectedAccounts[0]);
244
+ if (ordinalsAccount || paymentAccount) {
245
+ this.setConnectedAccountWithAddresses({
246
+ active: true,
247
+ mainAddress: ordinalsAccount !== null && ordinalsAccount !== void 0 ? ordinalsAccount : paymentAccount,
248
+ ordinalsAddress: ordinalsAccount,
249
+ paymentAddress: paymentAccount,
250
+ });
251
+ }
252
+ handleAccountChange(connectedAccounts);
253
+ });
254
+ provider.on('accountsChanged', handleBitcoinAccountChange);
255
+ provider.on('networkChanged', handleChainChange);
256
+ provider.on('disconnect', handleDisconnect);
257
+ const tearDownEventListeners = () => {
258
+ const provider = this.getProvider();
259
+ if (!(provider === null || provider === void 0 ? void 0 : provider.removeListener)) {
260
+ return;
261
+ }
262
+ provider.removeListener('accountsChanged', handleBitcoinAccountChange);
263
+ provider.removeListener('networkChanged', handleChainChange);
264
+ provider.removeListener('disconnect', handleDisconnect);
265
+ };
266
+ this.teardownEventListeners = tearDownEventListeners;
267
+ }
268
+ setVerifiedCredentials(verifiedCredentials) {
269
+ this.verifiedCredentials = verifiedCredentials;
270
+ }
271
+ isLedgerAddress(address) {
272
+ return utils.isLedgerAddressViaVerifiedCredentials(address, this.verifiedCredentials);
273
+ }
274
+ }
275
+
276
+ exports.BitcoinWalletConnector = BitcoinWalletConnector;
@@ -0,0 +1,50 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { EventEmitter } from 'stream';
4
+ import type { Wallet } from '@wallet-standard/base';
5
+ import { Chain, IBitcoinWalletConnector, WalletConnectorBase } from '@dynamic-labs/wallet-connector-core';
6
+ import { WalletBookSchema, WalletSchema } from '@dynamic-labs/wallet-book';
7
+ import { JwtVerifiedCredential, WalletAdditionalAddress } from '@dynamic-labs/sdk-api-core';
8
+ import { IBitcoinSessionCache } from '../BitcoinLocalStorageCache';
9
+ import { BitcoinTransaction, BitcoinSignPsbtRequest, BitcoinSignPsbtResponse, BitcoinWalletStandardMethods, ConnectedAccountWithAddressesProps } from '../types';
10
+ import { BitcoinWallet } from '../wallet';
11
+ export type BitcoinWalletConnectorOpts = {
12
+ walletBook: WalletBookSchema;
13
+ walletData: WalletSchema;
14
+ overrideKey?: string;
15
+ };
16
+ export declare abstract class BitcoinWalletConnector extends WalletConnectorBase<typeof BitcoinWallet> implements IBitcoinWalletConnector {
17
+ cache: IBitcoinSessionCache;
18
+ ChainWallet: typeof BitcoinWallet;
19
+ connectedChain: Chain;
20
+ supportedChains: Chain[];
21
+ private getAddressPromise;
22
+ private bitcoinProviderHelper;
23
+ wallet: Wallet | undefined;
24
+ walletMethods: BitcoinWalletStandardMethods | undefined;
25
+ canFetchConnectedAccounts: boolean;
26
+ isHardwareWalletEnabled: boolean;
27
+ verifiedCredentials: JwtVerifiedCredential[];
28
+ private lastAccountChange;
29
+ constructor(opts: BitcoinWalletConnectorOpts);
30
+ private isSameAccountChangeRequest;
31
+ private setLastAccountChangeRequest;
32
+ clearConnectedAccounts(): Promise<void>;
33
+ canConnectWithHardwareWallet(): boolean;
34
+ isInstalledOnBrowser(): boolean;
35
+ getDeepLink(): string | undefined;
36
+ endSession(): Promise<void>;
37
+ getBalance(address: string): Promise<string | undefined>;
38
+ getConnectedAccountsFromCache(): Promise<string[]>;
39
+ getConnectedAccounts(): Promise<string[]>;
40
+ getAdditionalAddresses(mainAddress?: string): Promise<WalletAdditionalAddress[]>;
41
+ setAdditionalAddresses(mainAddress: string, additionalAddresses: WalletAdditionalAddress[]): Promise<void>;
42
+ sendRawTransaction(rawTransaction: string): Promise<string>;
43
+ abstract sendBitcoin(transaction: BitcoinTransaction): Promise<string | undefined>;
44
+ getProvider<T>(): T & EventEmitter;
45
+ abstract signPsbt(request: BitcoinSignPsbtRequest): Promise<BitcoinSignPsbtResponse | undefined>;
46
+ setConnectedAccountWithAddresses({ mainAddress, ordinalsAddress, paymentAddress, active, }: ConnectedAccountWithAddressesProps): Promise<void>;
47
+ setupEventListeners(): void;
48
+ setVerifiedCredentials(verifiedCredentials: JwtVerifiedCredential[]): void;
49
+ isLedgerAddress(address: string): boolean;
50
+ }
@@ -0,0 +1,272 @@
1
+ 'use client'
2
+ import { __awaiter } from '../../_virtual/_tslib.js';
3
+ import { AddressPurpose } from 'sats-connect';
4
+ import { WalletConnectorBase, eventListenerHandlers, logger } from '@dynamic-labs/wallet-connector-core';
5
+ import { getWalletBookWallet, isWalletMethodSupported, findWalletBookWallet } from '@dynamic-labs/wallet-book';
6
+ import { isLedgerAddressViaVerifiedCredentials, DynamicError } from '@dynamic-labs/utils';
7
+ import { WalletAddressType } from '@dynamic-labs/sdk-api-core';
8
+ import { BitcoinLocalStorageCache } from '../BitcoinLocalStorageCache.js';
9
+ import { BitcoinProviderHelper } from '../bitcoinProviderHelper.js';
10
+ import { getMempoolApiUrl } from '../utils/getMempoolApiUrl.js';
11
+ import { HTTP_STATUS_TOO_MANY_REQUESTS, HTTP_STATUS_NOT_FOUND } from '../const.js';
12
+ import { satoshisToBtc } from '../utils/satoshisToBtc/satoshisToBtc.js';
13
+ import { BitcoinWallet } from '../wallet/BitcoinWallet.js';
14
+
15
+ class BitcoinWalletConnector extends WalletConnectorBase {
16
+ constructor(opts) {
17
+ var _a;
18
+ super(opts);
19
+ this.ChainWallet = BitcoinWallet;
20
+ this.connectedChain = 'BTC';
21
+ this.supportedChains = ['BTC'];
22
+ // some wallets don't support fetching connected accounts without prompting for a connection
23
+ this.canFetchConnectedAccounts = false;
24
+ this.isHardwareWalletEnabled = false;
25
+ this.verifiedCredentials = [];
26
+ // this is the key from the wallet book entry so that we don't purely rely on the normalized name
27
+ this.overrideKey = (_a = opts.overrideKey) !== null && _a !== void 0 ? _a : this.key;
28
+ const walletBookWallet = opts.walletData || getWalletBookWallet(this.walletBook, this.key);
29
+ this.bitcoinProviderHelper = new BitcoinProviderHelper(walletBookWallet);
30
+ this.wallet = this.bitcoinProviderHelper.findWallet();
31
+ if (this.wallet) {
32
+ this.walletMethods = this.bitcoinProviderHelper.getWalletMethods(this.wallet);
33
+ }
34
+ this.cache = new BitcoinLocalStorageCache(this.overrideKey);
35
+ this.canFetchConnectedAccounts = isWalletMethodSupported(walletBookWallet, 'getConnectedAccounts', 'browserExtension');
36
+ }
37
+ isSameAccountChangeRequest(to) {
38
+ return this.lastAccountChange === to;
39
+ }
40
+ setLastAccountChangeRequest(to) {
41
+ this.lastAccountChange = to;
42
+ }
43
+ clearConnectedAccounts() {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ yield this.cache.clearConnectedAcccounts();
46
+ });
47
+ }
48
+ canConnectWithHardwareWallet() {
49
+ const wallet = findWalletBookWallet(this.walletBook, this.key);
50
+ if (!wallet || !wallet.hardwareWallets)
51
+ return false;
52
+ return wallet.hardwareWallets.includes('ledger');
53
+ }
54
+ isInstalledOnBrowser() {
55
+ var _a;
56
+ return (Boolean(this.wallet) || Boolean((_a = this.bitcoinProviderHelper) === null || _a === void 0 ? void 0 : _a.getProvider()));
57
+ }
58
+ getDeepLink() {
59
+ return undefined;
60
+ }
61
+ endSession() {
62
+ return __awaiter(this, void 0, void 0, function* () {
63
+ yield this.cache.clearConnectedAcccounts();
64
+ });
65
+ }
66
+ getBalance(address) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ const API_URL = getMempoolApiUrl(address);
69
+ const response = yield fetch(`${API_URL}/address/${address}`);
70
+ if (!response.ok) {
71
+ // if the request fails due to rate limits, return cached value
72
+ if (response.status === HTTP_STATUS_TOO_MANY_REQUESTS) {
73
+ return '0';
74
+ }
75
+ // new accounts not yet indexed will return a 404
76
+ if (response.status === HTTP_STATUS_NOT_FOUND) {
77
+ return '0';
78
+ }
79
+ return undefined;
80
+ }
81
+ const addressInfo = yield response.json();
82
+ if (!(addressInfo === null || addressInfo === void 0 ? void 0 : addressInfo.chain_stats) || !(addressInfo === null || addressInfo === void 0 ? void 0 : addressInfo.mempool_stats)) {
83
+ return undefined;
84
+ }
85
+ const confirmedBalanceInSats = Number(addressInfo.chain_stats.funded_txo_sum) -
86
+ Number(addressInfo.chain_stats.spent_txo_sum);
87
+ const unconfirmedBalanceInSats = Number(addressInfo.mempool_stats.funded_txo_sum) -
88
+ Number(addressInfo.mempool_stats.spent_txo_sum);
89
+ const balance = satoshisToBtc(confirmedBalanceInSats + unconfirmedBalanceInSats);
90
+ return balance.toString();
91
+ });
92
+ }
93
+ getConnectedAccountsFromCache() {
94
+ return __awaiter(this, void 0, void 0, function* () {
95
+ var _a;
96
+ const currentAccount = yield this.cache.getActiveAccount();
97
+ const allAccounts = yield this.cache.getConnectedAccounts();
98
+ const allConnectedAddresses = (_a = Object.keys(allAccounts || {})) !== null && _a !== void 0 ? _a : [];
99
+ if (!(currentAccount === null || currentAccount === void 0 ? void 0 : currentAccount.address)) {
100
+ return allConnectedAddresses;
101
+ }
102
+ // return all connected accounts with the current account as the first item
103
+ return [
104
+ currentAccount.address,
105
+ ...allConnectedAddresses.filter((address) => address !== currentAccount.address),
106
+ ];
107
+ });
108
+ }
109
+ getConnectedAccounts() {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ // some wallets like xverse don't support fetching connected accounts
112
+ // without prompting for a connection
113
+ // to avoid this behavior, we cache the connected accounts
114
+ if (!this.canFetchConnectedAccounts) {
115
+ return this.getConnectedAccountsFromCache();
116
+ }
117
+ // if we decide that is ok to prompt for a connection when fetching connected accounts
118
+ // we shouldn't prompt every time we call this method (which is a lot of times)
119
+ // so we just store in a promise and return the same promise every time
120
+ if (!this.getAddressPromise) {
121
+ this.getAddressPromise = this.getAddress();
122
+ }
123
+ let connectedAccount;
124
+ try {
125
+ connectedAccount = yield this.getAddressPromise;
126
+ }
127
+ catch (error) {
128
+ logger.error(`${this.key} getConnectedAccounts - error fetching connected account`);
129
+ //don't throw error just return empty array after clearing the promise
130
+ }
131
+ this.getAddressPromise = undefined;
132
+ if (!connectedAccount) {
133
+ return [];
134
+ }
135
+ return [connectedAccount];
136
+ });
137
+ }
138
+ getAdditionalAddresses(mainAddress) {
139
+ return __awaiter(this, void 0, void 0, function* () {
140
+ if (!mainAddress) {
141
+ return [];
142
+ }
143
+ const currentAccount = yield this.cache.getConnectedAccount(mainAddress);
144
+ return (currentAccount === null || currentAccount === void 0 ? void 0 : currentAccount.additionalAddresses) || [];
145
+ });
146
+ }
147
+ setAdditionalAddresses(mainAddress, additionalAddresses) {
148
+ return __awaiter(this, void 0, void 0, function* () {
149
+ return this.cache.setConnectedAccount(mainAddress, {
150
+ additionalAddresses,
151
+ });
152
+ });
153
+ }
154
+ sendRawTransaction(rawTransaction) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ if (!rawTransaction) {
157
+ throw new DynamicError('sendRawTransaction - No transaction specified!');
158
+ }
159
+ const [connectedAddress] = yield this.getConnectedAccounts();
160
+ if (!connectedAddress) {
161
+ throw new DynamicError('sendRawTransaction - No connected address found!');
162
+ }
163
+ const API_URL = getMempoolApiUrl(connectedAddress);
164
+ const response = yield fetch(`${API_URL}/tx`, {
165
+ body: rawTransaction,
166
+ headers: {
167
+ 'Content-Type': 'application/x-www-form-urlencoded',
168
+ },
169
+ method: 'POST',
170
+ });
171
+ if (!response.ok) {
172
+ if (response.status === HTTP_STATUS_TOO_MANY_REQUESTS) {
173
+ throw new DynamicError('sendRawTransaction - mempool api rate limit exceeded');
174
+ }
175
+ const error = yield response.text();
176
+ logger.debug(`sendRawTransaction - response not ok: ${JSON.stringify(error)}`);
177
+ throw new DynamicError('sendRawTransaction - failed to send transaction');
178
+ }
179
+ // this endpoint returns the transaction ID
180
+ return response.text();
181
+ });
182
+ }
183
+ getProvider() {
184
+ var _a;
185
+ return (_a = this.bitcoinProviderHelper) === null || _a === void 0 ? void 0 : _a.getProvider();
186
+ }
187
+ setConnectedAccountWithAddresses(_a) {
188
+ return __awaiter(this, arguments, void 0, function* ({ mainAddress, ordinalsAddress, paymentAddress, active, }) {
189
+ if (!mainAddress) {
190
+ return;
191
+ }
192
+ const additionalAddresses = [];
193
+ if (ordinalsAddress) {
194
+ additionalAddresses.push({
195
+ address: ordinalsAddress.address,
196
+ publicKey: ordinalsAddress.publicKey,
197
+ type: WalletAddressType.Ordinals,
198
+ });
199
+ }
200
+ if (paymentAddress) {
201
+ additionalAddresses.push({
202
+ address: paymentAddress.address,
203
+ publicKey: paymentAddress.publicKey,
204
+ type: WalletAddressType.Payment,
205
+ });
206
+ }
207
+ this.cache.setConnectedAccount(mainAddress, {
208
+ active,
209
+ additionalAddresses,
210
+ });
211
+ });
212
+ }
213
+ setupEventListeners() {
214
+ const provider = this.getProvider();
215
+ if (!(provider === null || provider === void 0 ? void 0 : provider.on)) {
216
+ return;
217
+ }
218
+ const { handleAccountChange, handleChainChange, handleDisconnect } = eventListenerHandlers(this);
219
+ const handleBitcoinAccountChange = (accounts) => __awaiter(this, void 0, void 0, function* () {
220
+ let connectedAccounts = accounts;
221
+ let ordinalsAccount, paymentAccount;
222
+ // if accounts is an array of objects, we need to parse them to return only addresses
223
+ // since ordinals is the main address we use, we should return it as the first address
224
+ if (typeof accounts[0] === 'object') {
225
+ connectedAccounts = accounts
226
+ .sort((account) => account.purpose === AddressPurpose.Ordinals ? -1 : 1)
227
+ .map((account) => account.address);
228
+ [ordinalsAccount, paymentAccount] = connectedAccounts;
229
+ }
230
+ const currentConnectedAccounts = yield this.getConnectedAccountsFromCache();
231
+ // don't do anything if the connected accounts haven't changed
232
+ // or if the account change request is the same as previous request
233
+ if (currentConnectedAccounts[0] === connectedAccounts[0] ||
234
+ this.isSameAccountChangeRequest(connectedAccounts[0])) {
235
+ return;
236
+ }
237
+ // set the last account change request with the from and to addresses
238
+ // to ensure that the requests are not duplicated
239
+ this.setLastAccountChangeRequest(connectedAccounts[0]);
240
+ if (ordinalsAccount || paymentAccount) {
241
+ this.setConnectedAccountWithAddresses({
242
+ active: true,
243
+ mainAddress: ordinalsAccount !== null && ordinalsAccount !== void 0 ? ordinalsAccount : paymentAccount,
244
+ ordinalsAddress: ordinalsAccount,
245
+ paymentAddress: paymentAccount,
246
+ });
247
+ }
248
+ handleAccountChange(connectedAccounts);
249
+ });
250
+ provider.on('accountsChanged', handleBitcoinAccountChange);
251
+ provider.on('networkChanged', handleChainChange);
252
+ provider.on('disconnect', handleDisconnect);
253
+ const tearDownEventListeners = () => {
254
+ const provider = this.getProvider();
255
+ if (!(provider === null || provider === void 0 ? void 0 : provider.removeListener)) {
256
+ return;
257
+ }
258
+ provider.removeListener('accountsChanged', handleBitcoinAccountChange);
259
+ provider.removeListener('networkChanged', handleChainChange);
260
+ provider.removeListener('disconnect', handleDisconnect);
261
+ };
262
+ this.teardownEventListeners = tearDownEventListeners;
263
+ }
264
+ setVerifiedCredentials(verifiedCredentials) {
265
+ this.verifiedCredentials = verifiedCredentials;
266
+ }
267
+ isLedgerAddress(address) {
268
+ return isLedgerAddressViaVerifiedCredentials(address, this.verifiedCredentials);
269
+ }
270
+ }
271
+
272
+ export { BitcoinWalletConnector };
@@ -0,0 +1,37 @@
1
+ 'use client'
2
+ 'use strict';
3
+
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
6
+ var _tslib = require('../../../_virtual/_tslib.cjs');
7
+ var BitcoinWalletConnector = require('../BitcoinWalletConnector.cjs');
8
+
9
+ class FallbackBitcoinConnector extends BitcoinWalletConnector.BitcoinWalletConnector {
10
+ constructor(opts) {
11
+ super(Object.assign(Object.assign({}, opts), { overrideKey: 'fallbackconnector' }));
12
+ this.name = 'Fallback Connector';
13
+ this.overrideKey = 'fallbackconnector';
14
+ this.isAvailable = false;
15
+ }
16
+ getAddress() {
17
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
18
+ return;
19
+ });
20
+ }
21
+ signPsbt(
22
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
+ _request) {
24
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
25
+ return;
26
+ });
27
+ }
28
+ sendBitcoin(
29
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
30
+ _transaction) {
31
+ return _tslib.__awaiter(this, void 0, void 0, function* () {
32
+ return;
33
+ });
34
+ }
35
+ }
36
+
37
+ exports.FallbackBitcoinConnector = FallbackBitcoinConnector;
@@ -0,0 +1,11 @@
1
+ import { BitcoinSignPsbtRequest, BitcoinSignPsbtResponse, BitcoinTransaction } from '../../types';
2
+ import { BitcoinWalletConnector, BitcoinWalletConnectorOpts } from '../BitcoinWalletConnector';
3
+ export declare class FallbackBitcoinConnector extends BitcoinWalletConnector {
4
+ name: string;
5
+ overrideKey: string;
6
+ isAvailable: boolean;
7
+ constructor(opts: BitcoinWalletConnectorOpts);
8
+ getAddress(): Promise<string | undefined>;
9
+ signPsbt(_request: BitcoinSignPsbtRequest): Promise<BitcoinSignPsbtResponse | undefined>;
10
+ sendBitcoin(_transaction: BitcoinTransaction): Promise<string | undefined>;
11
+ }