otx-btc-wallet-connectors 0.1.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 (142) hide show
  1. package/README.md +554 -0
  2. package/dist/base-IAFq7sd8.d.mts +53 -0
  3. package/dist/base-IAFq7sd8.d.ts +53 -0
  4. package/dist/binance/index.d.mts +81 -0
  5. package/dist/binance/index.d.ts +81 -0
  6. package/dist/binance/index.js +13 -0
  7. package/dist/binance/index.js.map +1 -0
  8. package/dist/binance/index.mjs +4 -0
  9. package/dist/binance/index.mjs.map +1 -0
  10. package/dist/bitget/index.d.mts +84 -0
  11. package/dist/bitget/index.d.ts +84 -0
  12. package/dist/bitget/index.js +13 -0
  13. package/dist/bitget/index.js.map +1 -0
  14. package/dist/bitget/index.mjs +4 -0
  15. package/dist/bitget/index.mjs.map +1 -0
  16. package/dist/chunk-5Z5Q2Y75.mjs +91 -0
  17. package/dist/chunk-5Z5Q2Y75.mjs.map +1 -0
  18. package/dist/chunk-7KK2LZLZ.mjs +208 -0
  19. package/dist/chunk-7KK2LZLZ.mjs.map +1 -0
  20. package/dist/chunk-AW2JZIHR.mjs +753 -0
  21. package/dist/chunk-AW2JZIHR.mjs.map +1 -0
  22. package/dist/chunk-EIJOSZXZ.js +331 -0
  23. package/dist/chunk-EIJOSZXZ.js.map +1 -0
  24. package/dist/chunk-EQHR7P7G.js +541 -0
  25. package/dist/chunk-EQHR7P7G.js.map +1 -0
  26. package/dist/chunk-EWRXLZO4.mjs +539 -0
  27. package/dist/chunk-EWRXLZO4.mjs.map +1 -0
  28. package/dist/chunk-FISNQZZ7.js +802 -0
  29. package/dist/chunk-FISNQZZ7.js.map +1 -0
  30. package/dist/chunk-HL4WDMGS.js +200 -0
  31. package/dist/chunk-HL4WDMGS.js.map +1 -0
  32. package/dist/chunk-IPYWR76I.js +314 -0
  33. package/dist/chunk-IPYWR76I.js.map +1 -0
  34. package/dist/chunk-JYYNWR5G.js +142 -0
  35. package/dist/chunk-JYYNWR5G.js.map +1 -0
  36. package/dist/chunk-LNKTYZJM.js +701 -0
  37. package/dist/chunk-LNKTYZJM.js.map +1 -0
  38. package/dist/chunk-LVZMONQL.mjs +699 -0
  39. package/dist/chunk-LVZMONQL.mjs.map +1 -0
  40. package/dist/chunk-MFXLQWOE.js +93 -0
  41. package/dist/chunk-MFXLQWOE.js.map +1 -0
  42. package/dist/chunk-NBIA4TTE.mjs +204 -0
  43. package/dist/chunk-NBIA4TTE.mjs.map +1 -0
  44. package/dist/chunk-O4DD2XJ2.js +206 -0
  45. package/dist/chunk-O4DD2XJ2.js.map +1 -0
  46. package/dist/chunk-P7HVBU2B.mjs +140 -0
  47. package/dist/chunk-P7HVBU2B.mjs.map +1 -0
  48. package/dist/chunk-Q7QVQYEB.js +210 -0
  49. package/dist/chunk-Q7QVQYEB.js.map +1 -0
  50. package/dist/chunk-RLZEG6KL.mjs +329 -0
  51. package/dist/chunk-RLZEG6KL.mjs.map +1 -0
  52. package/dist/chunk-SYLDBJ75.mjs +246 -0
  53. package/dist/chunk-SYLDBJ75.mjs.map +1 -0
  54. package/dist/chunk-TTEUU3CI.mjs +198 -0
  55. package/dist/chunk-TTEUU3CI.mjs.map +1 -0
  56. package/dist/chunk-V66BXDTR.mjs +292 -0
  57. package/dist/chunk-V66BXDTR.mjs.map +1 -0
  58. package/dist/chunk-X77ZT4OI.js +268 -0
  59. package/dist/chunk-X77ZT4OI.js.map +1 -0
  60. package/dist/imtoken/index.d.mts +116 -0
  61. package/dist/imtoken/index.d.ts +116 -0
  62. package/dist/imtoken/index.js +14 -0
  63. package/dist/imtoken/index.js.map +1 -0
  64. package/dist/imtoken/index.mjs +5 -0
  65. package/dist/imtoken/index.mjs.map +1 -0
  66. package/dist/index.d.mts +14 -0
  67. package/dist/index.d.ts +14 -0
  68. package/dist/index.js +170 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/index.mjs +13 -0
  71. package/dist/index.mjs.map +1 -0
  72. package/dist/ledger/index.d.mts +290 -0
  73. package/dist/ledger/index.d.ts +290 -0
  74. package/dist/ledger/index.js +14 -0
  75. package/dist/ledger/index.js.map +1 -0
  76. package/dist/ledger/index.mjs +5 -0
  77. package/dist/ledger/index.mjs.map +1 -0
  78. package/dist/okx/index.d.mts +88 -0
  79. package/dist/okx/index.d.ts +88 -0
  80. package/dist/okx/index.js +13 -0
  81. package/dist/okx/index.js.map +1 -0
  82. package/dist/okx/index.mjs +4 -0
  83. package/dist/okx/index.mjs.map +1 -0
  84. package/dist/phantom/index.d.mts +96 -0
  85. package/dist/phantom/index.d.ts +96 -0
  86. package/dist/phantom/index.js +14 -0
  87. package/dist/phantom/index.js.map +1 -0
  88. package/dist/phantom/index.mjs +5 -0
  89. package/dist/phantom/index.mjs.map +1 -0
  90. package/dist/psbt-builder-CFOs69Z5.d.mts +131 -0
  91. package/dist/psbt-builder-CFOs69Z5.d.ts +131 -0
  92. package/dist/trezor/index.d.mts +155 -0
  93. package/dist/trezor/index.d.ts +155 -0
  94. package/dist/trezor/index.js +14 -0
  95. package/dist/trezor/index.js.map +1 -0
  96. package/dist/trezor/index.mjs +5 -0
  97. package/dist/trezor/index.mjs.map +1 -0
  98. package/dist/unisat/index.d.mts +75 -0
  99. package/dist/unisat/index.d.ts +75 -0
  100. package/dist/unisat/index.js +13 -0
  101. package/dist/unisat/index.js.map +1 -0
  102. package/dist/unisat/index.mjs +4 -0
  103. package/dist/unisat/index.mjs.map +1 -0
  104. package/dist/utils/index.d.mts +398 -0
  105. package/dist/utils/index.d.ts +398 -0
  106. package/dist/utils/index.js +120 -0
  107. package/dist/utils/index.js.map +1 -0
  108. package/dist/utils/index.mjs +3 -0
  109. package/dist/utils/index.mjs.map +1 -0
  110. package/dist/xverse/index.d.mts +79 -0
  111. package/dist/xverse/index.d.ts +79 -0
  112. package/dist/xverse/index.js +13 -0
  113. package/dist/xverse/index.js.map +1 -0
  114. package/dist/xverse/index.mjs +4 -0
  115. package/dist/xverse/index.mjs.map +1 -0
  116. package/package.json +108 -0
  117. package/src/base.ts +132 -0
  118. package/src/binance/BinanceConnector.ts +307 -0
  119. package/src/binance/index.ts +1 -0
  120. package/src/bitget/BitgetConnector.ts +301 -0
  121. package/src/bitget/index.ts +1 -0
  122. package/src/imtoken/ImTokenConnector.ts +420 -0
  123. package/src/imtoken/index.ts +2 -0
  124. package/src/index.ts +78 -0
  125. package/src/ledger/LedgerConnector.ts +1019 -0
  126. package/src/ledger/index.ts +8 -0
  127. package/src/okx/OKXConnector.ts +230 -0
  128. package/src/okx/index.ts +1 -0
  129. package/src/phantom/PhantomConnector.ts +381 -0
  130. package/src/phantom/index.ts +2 -0
  131. package/src/trezor/TrezorConnector.ts +824 -0
  132. package/src/trezor/index.ts +6 -0
  133. package/src/unisat/UnisatConnector.ts +312 -0
  134. package/src/unisat/index.ts +1 -0
  135. package/src/utils/blockstream.ts +230 -0
  136. package/src/utils/btc-service.ts +364 -0
  137. package/src/utils/index.ts +56 -0
  138. package/src/utils/mempool.ts +232 -0
  139. package/src/utils/psbt-builder.ts +492 -0
  140. package/src/utils/types.ts +183 -0
  141. package/src/xverse/XverseConnector.ts +479 -0
  142. package/src/xverse/index.ts +1 -0
@@ -0,0 +1,420 @@
1
+ import type {
2
+ WalletAccount,
3
+ BitcoinNetwork,
4
+ SignPsbtOptions,
5
+ } from 'otx-btc-wallet-core';
6
+ import * as bitcoin from 'bitcoinjs-lib';
7
+ import { BaseConnector } from '../base';
8
+ import { BtcService } from '../utils';
9
+ import {
10
+ generatePsbtForSend,
11
+ finalizeAllInputs,
12
+ getBitcoinJsNetwork,
13
+ type GeneratePsbtOptions,
14
+ } from '../utils';
15
+
16
+ /**
17
+ * Options for sendTransaction method
18
+ */
19
+ export interface ImTokenSendOptions extends GeneratePsbtOptions {
20
+ // Additional imToken-specific options can be added here
21
+ }
22
+
23
+ /**
24
+ * imToken Bitcoin provider interface
25
+ * Provider is accessible via window.bitcoin
26
+ * @see https://imtoken.gitbook.io/developers/products/webview/bitcoin
27
+ */
28
+ interface ImTokenBitcoinProvider {
29
+ request(params: { method: 'btc_requestAccounts' }): Promise<string[]>;
30
+ request(params: { method: 'btc_getNetwork' }): Promise<string>;
31
+ request(params: { method: 'btc_getPublicKey' }): Promise<string>;
32
+ request(params: { method: 'btc_getBalance'; params: [string] }): Promise<number>;
33
+ request(params: { method: 'btc_signPsbt'; params: [string, boolean?] }): Promise<string>;
34
+ request(params: { method: 'btc_signPsbts'; params: [string[]] }): Promise<string[]>;
35
+ request(params: {
36
+ method: 'btc_signMessage';
37
+ params: [string, 'bip322-simple' | 'bip322-full' | 'bip322-legacy'];
38
+ }): Promise<string>;
39
+ request(params: { method: 'btc_sendRawTransaction'; params: [string] }): Promise<string>;
40
+ request(params: { method: 'btc_switchNetwork'; params: [string] }): Promise<null>;
41
+ on(event: 'accountsChanged', callback: (...args: unknown[]) => void): void;
42
+ on(event: 'networkChanged', callback: (...args: unknown[]) => void): void;
43
+ removeListener(event: string, callback: (...args: unknown[]) => void): void;
44
+ }
45
+
46
+ // Extend window type
47
+ declare global {
48
+ interface Window {
49
+ bitcoin?: ImTokenBitcoinProvider;
50
+ }
51
+ }
52
+
53
+ // imToken wallet icon
54
+ const IMTOKEN_ICON =
55
+ 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MyIgaGVpZ2h0PSI5MyIgdmlld0JveD0iMCAwIDkzIDkzIiBmaWxsPSJub25lIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzIwMzVfMzk1KSI+CjxtYXNrIGlkPSJtYXNrMF8yMDM1XzM5NSIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSI5MyIgaGVpZ2h0PSI5MyI+CjxwYXRoIGQ9Ik05MyAwSDBWOTNIOTNWMFoiIGZpbGw9IndoaXRlIi8+CjwvbWFzaz4KPGcgbWFzaz0idXJsKCNtYXNrMF8yMDM1XzM5NSkiPgo8bWFzayBpZD0ibWFzazFfMjAzNV8zOTUiIHN0eWxlPSJtYXNrLXR5cGU6bHVtaW5hbmNlIiBtYXNrVW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4PSIwIiB5PSIwIiB3aWR0aD0iOTMiIGhlaWdodD0iOTMiPgo8cGF0aCBkPSJNOTIuODc2OCAwSDAuMDU4MTA1NVY5M0g5Mi44NzY4VjBaIiBmaWxsPSJ3aGl0ZSIvPgo8L21hc2s+CjxnIG1hc2s9InVybCgjbWFzazFfMjAzNV8zOTUpIj4KPHBhdGggZD0iTTcyLjEwNSAwSDIwLjk4MjJDOS40NjgyMiAwIDAuMTM0Mjc3IDkuMzUyMTkgMC4xMzQyNzcgMjAuODg4N1Y3Mi4xMTEzQzAuMTM0Mjc3IDgzLjY0NzkgOS40NjgyMiA5MyAyMC45ODIyIDkzSDcyLjEwNUM4My42MTkxIDkzIDkyLjk1MyA4My42NDc5IDkyLjk1MyA3Mi4xMTEzVjIwLjg4ODdDOTIuOTUzIDkuMzUyMTkgODMuNjE5MSAwIDcyLjEwNSAwWiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzIwMzVfMzk1KSIvPgo8cGF0aCBkPSJNNzUuNjc1MyAyOC43NTI0Qzc3LjYwNTYgNTQuOTQ3OSA2MC44IDY3LjMyOTEgNDUuNzM0MSA2OC42NDk0QzMxLjcyNzQgNjkuODc2OCAxOC41NDMgNjEuMjUzNCAxNy4zODYzIDQ4LjAwNDFDMTYuNDMyMiAzNy4wNTgxIDIzLjE4NDMgMzIuMzk3OSAyOC40ODkzIDMxLjkzMzVDMzMuOTQ1NCAzMS40NTQxIDM4LjUzMDcgMzUuMjI0MyAzOC45Mjg1IDM5Ljc4OTFDMzkuMzExNSA0NC4xNzc3IDM2LjU3ODQgNDYuMTc1NCAzNC42Nzc3IDQ2LjM0MTZDMzMuMTc0NCA0Ni40NzM2IDMxLjI4MzIgNDUuNTU5MyAzMS4xMTI1IDQzLjU5NTlDMzAuOTY2IDQxLjkwODggMzEuNjA1NCA0MS42NzkxIDMxLjQ0OTEgMzkuODg2OEMzMS4xNzEgMzYuNjk2MSAyOC4zOTQxIDM2LjMyNDUgMjYuODczOCAzNi40NTY1QzI1LjAzMzkgMzYuNjE4IDIxLjY5NTcgMzguNzY5NSAyMi4xNjQyIDQ0LjEyODhDMjIuNjM1MiA0OS41MzQ2IDI3LjgwODMgNTMuODA2MSAzNC41ODk3IDUzLjIxMkM0MS45MDc5IDUyLjU3MTMgNDcuMDAzIDQ2Ljg2MjQgNDcuMzg2MiAzOC44NTUxQzQ3LjM4MjYgMzguNDMxIDQ3LjQ3MTggMzguMDExMiA0Ny42NDczIDM3LjYyNTJMNDcuNjQ5NiAzNy42MTU2QzQ3LjcyODUgMzcuNDQ3NyA0Ny44MjA4IDM3LjI4NjYgNDcuOTI1NCAzNy4xMzM5QzQ4LjA4MTYgMzYuODk5MiA0OC4yODE4IDM2LjY0IDQ4LjU0MDMgMzYuMzU2M0M0OC41NDI4IDM2LjM0OSA0OC41NDI4IDM2LjM0OSA0OC41NDc3IDM2LjM0OUM0OC43MzU1IDM2LjEzNjMgNDguOTYyNSAzNS45MDY1IDQ5LjIxODcgMzUuNjU5NUM1Mi40MTU1IDMyLjYzNzUgNjMuOTI4MiAyNS41MTA1IDc0LjgxNjMgMjcuNzY3MkM3NS4wNDY2IDI3LjgxNjYgNzUuMjU0NSAyNy45MzkyIDc1LjQwOTMgMjguMTE2OUM3NS41NjQxIDI4LjI5NDUgNzUuNjU3MyAyOC41MTc0IDc1LjY3NTMgMjguNzUyNFoiIGZpbGw9IndoaXRlIi8+CjwvZz4KPC9nPgo8L2c+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMjAzNV8zOTUiIHgxPSI5Mi44OTc1IiB5MT0iMTUuNzE1NCIgeDI9IjMuMjE5OTciIHkyPSI1MC42NTA0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwQ0M1RkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDA3RkZGIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMjAzNV8zOTUiPgo8cmVjdCB3aWR0aD0iOTMiIGhlaWdodD0iOTMiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==';
56
+
57
+ /**
58
+ * imToken network type
59
+ */
60
+ type ImTokenNetwork = 'mainnet' | 'signet';
61
+
62
+ /**
63
+ * imToken Wallet Connector
64
+ *
65
+ * @see https://imtoken.gitbook.io/developers/products/webview/bitcoin
66
+ */
67
+ export class ImTokenConnector extends BaseConnector {
68
+ readonly id = 'imtoken';
69
+ readonly name = 'imToken';
70
+ readonly icon = IMTOKEN_ICON;
71
+ readonly downloadUrl = 'https://token.im/download';
72
+
73
+ private _publicKey: string | null = null;
74
+ private _address: string | null = null;
75
+ private _network: BitcoinNetwork = 'mainnet';
76
+ private _removeAccountChangeListener: (() => void) | null = null;
77
+ private _removeNetworkChangeListener: (() => void) | null = null;
78
+
79
+ protected getProvider(): ImTokenBitcoinProvider | undefined {
80
+ if (typeof window === 'undefined') return undefined;
81
+ return window.bitcoin;
82
+ }
83
+
84
+ async connect(network: BitcoinNetwork = 'mainnet'): Promise<WalletAccount> {
85
+ this.ensureInstalled();
86
+ const provider = this.getProvider()!;
87
+
88
+ try {
89
+ // Switch network first (may not be supported yet)
90
+ await this.trySwitchNetwork(provider, network);
91
+
92
+ // Request accounts
93
+ const accounts = await provider.request({ method: 'btc_requestAccounts' });
94
+ if (!accounts || accounts.length === 0) {
95
+ throw new Error('No accounts found');
96
+ }
97
+
98
+ // Get public key
99
+ this._publicKey = await provider.request({ method: 'btc_getPublicKey' });
100
+ this._address = accounts[0] ?? '';
101
+ this._network = network;
102
+
103
+ // Setup event listeners
104
+ this.setupEventListeners();
105
+
106
+ return {
107
+ address: this._address,
108
+ publicKey: this._publicKey,
109
+ type: this.inferAddressType(this._address),
110
+ };
111
+ } catch (error) {
112
+ this.handleError(error);
113
+ }
114
+ }
115
+
116
+ private async trySwitchNetwork(
117
+ provider: ImTokenBitcoinProvider,
118
+ network: BitcoinNetwork
119
+ ): Promise<void> {
120
+ try {
121
+ const imTokenNetwork = this.mapToImTokenNetwork(network);
122
+ await provider.request({ method: 'btc_switchNetwork', params: [imTokenNetwork] });
123
+ } catch {
124
+ // Network switching might not be supported yet, continue with current network
125
+ console.warn('imToken network switching may not be supported');
126
+ }
127
+ }
128
+
129
+ private setupEventListeners(): void {
130
+ const provider = this.getProvider();
131
+ if (!provider || typeof provider.on !== 'function') return;
132
+
133
+ // Remove existing listeners
134
+ this.removeEventListeners();
135
+
136
+ // Account change listener
137
+ const handleAccountsChanged = (...args: unknown[]) => {
138
+ const accounts = args[0] as string[];
139
+ if (!accounts || accounts.length === 0) {
140
+ this.emitAccountsChanged([]);
141
+ return;
142
+ }
143
+
144
+ provider
145
+ .request({ method: 'btc_getPublicKey' })
146
+ .then((publicKey) => {
147
+ this._publicKey = publicKey;
148
+ const walletAccounts: WalletAccount[] = accounts.map((address) => ({
149
+ address,
150
+ publicKey,
151
+ type: this.inferAddressType(address),
152
+ }));
153
+ this.emitAccountsChanged(walletAccounts);
154
+ })
155
+ .catch(() => {
156
+ this.emitAccountsChanged([]);
157
+ });
158
+ };
159
+
160
+ // Network change listener
161
+ const handleNetworkChanged = () => {
162
+ // When network changes, we need to re-fetch network info
163
+ provider
164
+ .request({ method: 'btc_getNetwork' })
165
+ .then((network) => {
166
+ const btcNetwork = this.mapNetwork(network);
167
+ this.emitNetworkChanged(btcNetwork);
168
+ })
169
+ .catch(() => {
170
+ // Ignore errors
171
+ });
172
+ };
173
+
174
+ provider.on('accountsChanged', handleAccountsChanged);
175
+ provider.on('networkChanged', handleNetworkChanged);
176
+
177
+ this._removeAccountChangeListener = () => {
178
+ provider.removeListener('accountsChanged', handleAccountsChanged);
179
+ };
180
+ this._removeNetworkChangeListener = () => {
181
+ provider.removeListener('networkChanged', handleNetworkChanged);
182
+ };
183
+ }
184
+
185
+ private removeEventListeners(): void {
186
+ this._removeAccountChangeListener?.();
187
+ this._removeAccountChangeListener = null;
188
+
189
+ this._removeNetworkChangeListener?.();
190
+ this._removeNetworkChangeListener = null;
191
+ }
192
+
193
+ async disconnect(): Promise<void> {
194
+ this.removeEventListeners();
195
+ this._publicKey = null;
196
+ this._address = null;
197
+ this.cleanup();
198
+ }
199
+
200
+ async getAccounts(): Promise<WalletAccount[]> {
201
+ this.ensureInstalled();
202
+ const provider = this.getProvider()!;
203
+
204
+ try {
205
+ const addresses = await provider.request({ method: 'btc_requestAccounts' });
206
+ const publicKey = await provider.request({ method: 'btc_getPublicKey' });
207
+ this._publicKey = publicKey;
208
+
209
+ return addresses.map((address) => ({
210
+ address,
211
+ publicKey,
212
+ type: this.inferAddressType(address),
213
+ }));
214
+ } catch (error) {
215
+ this.handleError(error);
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Sign a message using BIP-322 simple format
221
+ * Note: Only bip322-simple is supported by imToken
222
+ */
223
+ async signMessage(message: string): Promise<string> {
224
+ this.ensureInstalled();
225
+ const provider = this.getProvider()!;
226
+
227
+ try {
228
+ return await provider.request({
229
+ method: 'btc_signMessage',
230
+ params: [message, 'bip322-simple'],
231
+ });
232
+ } catch (error) {
233
+ this.handleError(error);
234
+ }
235
+ }
236
+
237
+ async signPsbt(psbtHex: string, options?: SignPsbtOptions): Promise<string> {
238
+ this.ensureInstalled();
239
+ const provider = this.getProvider()!;
240
+
241
+ try {
242
+ const autoFinalize = options?.autoFinalize ?? true;
243
+ return await provider.request({
244
+ method: 'btc_signPsbt',
245
+ params: [psbtHex, autoFinalize],
246
+ });
247
+ } catch (error) {
248
+ this.handleError(error);
249
+ }
250
+ }
251
+
252
+ async signPsbts(psbtHexs: string[]): Promise<string[]> {
253
+ this.ensureInstalled();
254
+ const provider = this.getProvider()!;
255
+
256
+ try {
257
+ return await provider.request({
258
+ method: 'btc_signPsbts',
259
+ params: [psbtHexs],
260
+ });
261
+ } catch (error) {
262
+ this.handleError(error);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Send Bitcoin transaction
268
+ * Builds a PSBT using bitcoinjs-lib, signs it with imToken, and broadcasts
269
+ *
270
+ * @param to - Recipient address
271
+ * @param satoshis - Amount to send in satoshis
272
+ * @param options - Send options (feeRate, etc.)
273
+ * @returns Transaction ID after broadcast
274
+ */
275
+ async sendTransaction(
276
+ to: string,
277
+ satoshis: number,
278
+ options?: ImTokenSendOptions
279
+ ): Promise<string> {
280
+ this.ensureInstalled();
281
+ const provider = this.getProvider()!;
282
+
283
+ if (!this._address || !this._publicKey) {
284
+ throw new Error('Not connected. Please call connect() first.');
285
+ }
286
+
287
+ const btcNetwork = getBitcoinJsNetwork(this._network);
288
+
289
+ // 1. Generate PSBT using the utility
290
+ const {
291
+ psbtHex,
292
+ selectedUtxos,
293
+ } = await generatePsbtForSend(
294
+ to,
295
+ satoshis,
296
+ this._address,
297
+ this._publicKey,
298
+ this._network,
299
+ options
300
+ );
301
+
302
+ // 2. Sign PSBT with imToken (autoFinalize = false to handle finalization ourselves)
303
+ const signedPsbtHex = await provider.request({
304
+ method: 'btc_signPsbt',
305
+ params: [psbtHex, false],
306
+ });
307
+
308
+ // 3. Parse signed PSBT and finalize
309
+ const signedPsbt = bitcoin.Psbt.fromHex(signedPsbtHex, { network: btcNetwork });
310
+ finalizeAllInputs(signedPsbt, selectedUtxos.length);
311
+
312
+ // 4. Extract transaction
313
+ const tx = signedPsbt.extractTransaction();
314
+ const txHex = tx.toHex();
315
+
316
+ // 5. Broadcast using imToken or fallback to BtcService
317
+ try {
318
+ const txid = await provider.request({
319
+ method: 'btc_sendRawTransaction',
320
+ params: [txHex],
321
+ });
322
+ return txid;
323
+ } catch {
324
+ // Fallback to BtcService broadcast
325
+ const btcService = new BtcService(this._network);
326
+ return await btcService.broadcastTransaction(txHex);
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Broadcast a raw transaction
332
+ */
333
+ async broadcastTransaction(txHex: string): Promise<string> {
334
+ this.ensureInstalled();
335
+ const provider = this.getProvider()!;
336
+
337
+ try {
338
+ return await provider.request({
339
+ method: 'btc_sendRawTransaction',
340
+ params: [txHex],
341
+ });
342
+ } catch (error) {
343
+ this.handleError(error);
344
+ }
345
+ }
346
+
347
+ async getNetwork(): Promise<BitcoinNetwork> {
348
+ this.ensureInstalled();
349
+ const provider = this.getProvider()!;
350
+
351
+ try {
352
+ const network = await provider.request({ method: 'btc_getNetwork' });
353
+ return this.mapNetwork(network);
354
+ } catch (error) {
355
+ this.handleError(error);
356
+ }
357
+ }
358
+
359
+ async switchNetwork(network: BitcoinNetwork): Promise<void> {
360
+ this.ensureInstalled();
361
+ const provider = this.getProvider()!;
362
+
363
+ try {
364
+ const imTokenNetwork = this.mapToImTokenNetwork(network);
365
+ await provider.request({ method: 'btc_switchNetwork', params: [imTokenNetwork] });
366
+ } catch (error) {
367
+ this.handleError(error);
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Get the public key (cached from connect)
373
+ */
374
+ getPublicKey(): string | null {
375
+ return this._publicKey;
376
+ }
377
+
378
+ /**
379
+ * Get balance for an address
380
+ */
381
+ async getBalance(address: string): Promise<number> {
382
+ this.ensureInstalled();
383
+ const provider = this.getProvider()!;
384
+
385
+ try {
386
+ return await provider.request({
387
+ method: 'btc_getBalance',
388
+ params: [address],
389
+ });
390
+ } catch (error) {
391
+ this.handleError(error);
392
+ }
393
+ }
394
+
395
+ private mapNetwork(network: string): BitcoinNetwork {
396
+ switch (network.toLowerCase()) {
397
+ case 'mainnet':
398
+ return 'mainnet';
399
+ case 'signet':
400
+ return 'signet';
401
+ case 'testnet':
402
+ return 'testnet';
403
+ default:
404
+ return 'mainnet';
405
+ }
406
+ }
407
+
408
+ private mapToImTokenNetwork(network: BitcoinNetwork): ImTokenNetwork {
409
+ switch (network) {
410
+ case 'mainnet':
411
+ return 'mainnet';
412
+ case 'signet':
413
+ case 'testnet':
414
+ case 'testnet4':
415
+ return 'signet';
416
+ default:
417
+ return 'mainnet';
418
+ }
419
+ }
420
+ }
@@ -0,0 +1,2 @@
1
+ export { ImTokenConnector } from './ImTokenConnector';
2
+ export type { ImTokenSendOptions } from './ImTokenConnector';
package/src/index.ts ADDED
@@ -0,0 +1,78 @@
1
+ // otx-btc-wallet-connectors - Bitcoin wallet connectors
2
+
3
+ // Base
4
+ export { BaseConnector } from './base';
5
+
6
+ // Connectors
7
+ export { UnisatConnector } from './unisat';
8
+ export { XverseConnector } from './xverse';
9
+ export { OKXConnector } from './okx';
10
+ export { PhantomConnector } from './phantom';
11
+ export type { PhantomSendOptions } from './phantom';
12
+ export { BitgetConnector } from './bitget';
13
+ export { BinanceConnector } from './binance';
14
+ export { LedgerConnector } from './ledger';
15
+ export type {
16
+ LedgerConnectorOptions,
17
+ LedgerAddressType,
18
+ LedgerUtxoInput,
19
+ LedgerTxOutput,
20
+ SendBitcoinOptions,
21
+ } from './ledger';
22
+ export { TrezorConnector } from './trezor';
23
+ export type {
24
+ TrezorConnectorOptions,
25
+ TrezorAddressType,
26
+ TrezorSendBitcoinOptions,
27
+ } from './trezor';
28
+ export { ImTokenConnector } from './imtoken';
29
+ export type { ImTokenSendOptions } from './imtoken';
30
+
31
+ // Bitcoin Services & Utilities
32
+ export {
33
+ MempoolService,
34
+ BlockstreamService,
35
+ BtcService,
36
+ // Configuration
37
+ configureBtcService,
38
+ getBtcServiceConfig,
39
+ // Standalone functions
40
+ getUtxos,
41
+ getUtxosWithTxHex,
42
+ getTxHex,
43
+ getTransaction,
44
+ getFullTransaction,
45
+ broadcastTransaction,
46
+ getAddressInfo,
47
+ getBalance,
48
+ getConfirmedBalance,
49
+ getFeeRates,
50
+ // PSBT utilities
51
+ generatePsbtForSend,
52
+ finalizeAllInputs,
53
+ finalizeAndBroadcast,
54
+ selectUtxos,
55
+ hexToBytes,
56
+ bytesToHex,
57
+ toXOnly,
58
+ getAddressType,
59
+ getBitcoinJsNetwork,
60
+ getInputVBytes,
61
+ getOutputVBytes,
62
+ getDustThreshold,
63
+ deriveAddressFromPublicKey,
64
+ } from './utils';
65
+
66
+ export type {
67
+ Utxo,
68
+ UtxoWithTx,
69
+ FeeRates,
70
+ Transaction,
71
+ FullTransaction,
72
+ AddressBalance,
73
+ IBtcService,
74
+ BtcServiceConfig,
75
+ NetworkEndpoints,
76
+ GeneratePsbtOptions,
77
+ GeneratePsbtResult,
78
+ } from './utils';