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.
- package/README.md +554 -0
- package/dist/base-IAFq7sd8.d.mts +53 -0
- package/dist/base-IAFq7sd8.d.ts +53 -0
- package/dist/binance/index.d.mts +81 -0
- package/dist/binance/index.d.ts +81 -0
- package/dist/binance/index.js +13 -0
- package/dist/binance/index.js.map +1 -0
- package/dist/binance/index.mjs +4 -0
- package/dist/binance/index.mjs.map +1 -0
- package/dist/bitget/index.d.mts +84 -0
- package/dist/bitget/index.d.ts +84 -0
- package/dist/bitget/index.js +13 -0
- package/dist/bitget/index.js.map +1 -0
- package/dist/bitget/index.mjs +4 -0
- package/dist/bitget/index.mjs.map +1 -0
- package/dist/chunk-5Z5Q2Y75.mjs +91 -0
- package/dist/chunk-5Z5Q2Y75.mjs.map +1 -0
- package/dist/chunk-7KK2LZLZ.mjs +208 -0
- package/dist/chunk-7KK2LZLZ.mjs.map +1 -0
- package/dist/chunk-AW2JZIHR.mjs +753 -0
- package/dist/chunk-AW2JZIHR.mjs.map +1 -0
- package/dist/chunk-EIJOSZXZ.js +331 -0
- package/dist/chunk-EIJOSZXZ.js.map +1 -0
- package/dist/chunk-EQHR7P7G.js +541 -0
- package/dist/chunk-EQHR7P7G.js.map +1 -0
- package/dist/chunk-EWRXLZO4.mjs +539 -0
- package/dist/chunk-EWRXLZO4.mjs.map +1 -0
- package/dist/chunk-FISNQZZ7.js +802 -0
- package/dist/chunk-FISNQZZ7.js.map +1 -0
- package/dist/chunk-HL4WDMGS.js +200 -0
- package/dist/chunk-HL4WDMGS.js.map +1 -0
- package/dist/chunk-IPYWR76I.js +314 -0
- package/dist/chunk-IPYWR76I.js.map +1 -0
- package/dist/chunk-JYYNWR5G.js +142 -0
- package/dist/chunk-JYYNWR5G.js.map +1 -0
- package/dist/chunk-LNKTYZJM.js +701 -0
- package/dist/chunk-LNKTYZJM.js.map +1 -0
- package/dist/chunk-LVZMONQL.mjs +699 -0
- package/dist/chunk-LVZMONQL.mjs.map +1 -0
- package/dist/chunk-MFXLQWOE.js +93 -0
- package/dist/chunk-MFXLQWOE.js.map +1 -0
- package/dist/chunk-NBIA4TTE.mjs +204 -0
- package/dist/chunk-NBIA4TTE.mjs.map +1 -0
- package/dist/chunk-O4DD2XJ2.js +206 -0
- package/dist/chunk-O4DD2XJ2.js.map +1 -0
- package/dist/chunk-P7HVBU2B.mjs +140 -0
- package/dist/chunk-P7HVBU2B.mjs.map +1 -0
- package/dist/chunk-Q7QVQYEB.js +210 -0
- package/dist/chunk-Q7QVQYEB.js.map +1 -0
- package/dist/chunk-RLZEG6KL.mjs +329 -0
- package/dist/chunk-RLZEG6KL.mjs.map +1 -0
- package/dist/chunk-SYLDBJ75.mjs +246 -0
- package/dist/chunk-SYLDBJ75.mjs.map +1 -0
- package/dist/chunk-TTEUU3CI.mjs +198 -0
- package/dist/chunk-TTEUU3CI.mjs.map +1 -0
- package/dist/chunk-V66BXDTR.mjs +292 -0
- package/dist/chunk-V66BXDTR.mjs.map +1 -0
- package/dist/chunk-X77ZT4OI.js +268 -0
- package/dist/chunk-X77ZT4OI.js.map +1 -0
- package/dist/imtoken/index.d.mts +116 -0
- package/dist/imtoken/index.d.ts +116 -0
- package/dist/imtoken/index.js +14 -0
- package/dist/imtoken/index.js.map +1 -0
- package/dist/imtoken/index.mjs +5 -0
- package/dist/imtoken/index.mjs.map +1 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +170 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -0
- package/dist/ledger/index.d.mts +290 -0
- package/dist/ledger/index.d.ts +290 -0
- package/dist/ledger/index.js +14 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/ledger/index.mjs +5 -0
- package/dist/ledger/index.mjs.map +1 -0
- package/dist/okx/index.d.mts +88 -0
- package/dist/okx/index.d.ts +88 -0
- package/dist/okx/index.js +13 -0
- package/dist/okx/index.js.map +1 -0
- package/dist/okx/index.mjs +4 -0
- package/dist/okx/index.mjs.map +1 -0
- package/dist/phantom/index.d.mts +96 -0
- package/dist/phantom/index.d.ts +96 -0
- package/dist/phantom/index.js +14 -0
- package/dist/phantom/index.js.map +1 -0
- package/dist/phantom/index.mjs +5 -0
- package/dist/phantom/index.mjs.map +1 -0
- package/dist/psbt-builder-CFOs69Z5.d.mts +131 -0
- package/dist/psbt-builder-CFOs69Z5.d.ts +131 -0
- package/dist/trezor/index.d.mts +155 -0
- package/dist/trezor/index.d.ts +155 -0
- package/dist/trezor/index.js +14 -0
- package/dist/trezor/index.js.map +1 -0
- package/dist/trezor/index.mjs +5 -0
- package/dist/trezor/index.mjs.map +1 -0
- package/dist/unisat/index.d.mts +75 -0
- package/dist/unisat/index.d.ts +75 -0
- package/dist/unisat/index.js +13 -0
- package/dist/unisat/index.js.map +1 -0
- package/dist/unisat/index.mjs +4 -0
- package/dist/unisat/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +398 -0
- package/dist/utils/index.d.ts +398 -0
- package/dist/utils/index.js +120 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +3 -0
- package/dist/utils/index.mjs.map +1 -0
- package/dist/xverse/index.d.mts +79 -0
- package/dist/xverse/index.d.ts +79 -0
- package/dist/xverse/index.js +13 -0
- package/dist/xverse/index.js.map +1 -0
- package/dist/xverse/index.mjs +4 -0
- package/dist/xverse/index.mjs.map +1 -0
- package/package.json +108 -0
- package/src/base.ts +132 -0
- package/src/binance/BinanceConnector.ts +307 -0
- package/src/binance/index.ts +1 -0
- package/src/bitget/BitgetConnector.ts +301 -0
- package/src/bitget/index.ts +1 -0
- package/src/imtoken/ImTokenConnector.ts +420 -0
- package/src/imtoken/index.ts +2 -0
- package/src/index.ts +78 -0
- package/src/ledger/LedgerConnector.ts +1019 -0
- package/src/ledger/index.ts +8 -0
- package/src/okx/OKXConnector.ts +230 -0
- package/src/okx/index.ts +1 -0
- package/src/phantom/PhantomConnector.ts +381 -0
- package/src/phantom/index.ts +2 -0
- package/src/trezor/TrezorConnector.ts +824 -0
- package/src/trezor/index.ts +6 -0
- package/src/unisat/UnisatConnector.ts +312 -0
- package/src/unisat/index.ts +1 -0
- package/src/utils/blockstream.ts +230 -0
- package/src/utils/btc-service.ts +364 -0
- package/src/utils/index.ts +56 -0
- package/src/utils/mempool.ts +232 -0
- package/src/utils/psbt-builder.ts +492 -0
- package/src/utils/types.ts +183 -0
- package/src/xverse/XverseConnector.ts +479 -0
- 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
|
+
}
|
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';
|