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,292 @@
|
|
|
1
|
+
import { getBitcoinJsNetwork, generatePsbtForSend, finalizeAllInputs, BtcService } from './chunk-AW2JZIHR.mjs';
|
|
2
|
+
import { BaseConnector } from './chunk-5Z5Q2Y75.mjs';
|
|
3
|
+
import * as bitcoin from 'bitcoinjs-lib';
|
|
4
|
+
|
|
5
|
+
var IMTOKEN_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MyIgaGVpZ2h0PSI5MyIgdmlld0JveD0iMCAwIDkzIDkzIiBmaWxsPSJub25lIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzIwMzVfMzk1KSI+CjxtYXNrIGlkPSJtYXNrMF8yMDM1XzM5NSIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSI5MyIgaGVpZ2h0PSI5MyI+CjxwYXRoIGQ9Ik05MyAwSDBWOTNIOTNWMFoiIGZpbGw9IndoaXRlIi8+CjwvbWFzaz4KPGcgbWFzaz0idXJsKCNtYXNrMF8yMDM1XzM5NSkiPgo8bWFzayBpZD0ibWFzazFfMjAzNV8zOTUiIHN0eWxlPSJtYXNrLXR5cGU6bHVtaW5hbmNlIiBtYXNrVW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4PSIwIiB5PSIwIiB3aWR0aD0iOTMiIGhlaWdodD0iOTMiPgo8cGF0aCBkPSJNOTIuODc2OCAwSDAuMDU4MTA1NVY5M0g5Mi44NzY4VjBaIiBmaWxsPSJ3aGl0ZSIvPgo8L21hc2s+CjxnIG1hc2s9InVybCgjbWFzazFfMjAzNV8zOTUpIj4KPHBhdGggZD0iTTcyLjEwNSAwSDIwLjk4MjJDOS40NjgyMiAwIDAuMTM0Mjc3IDkuMzUyMTkgMC4xMzQyNzcgMjAuODg4N1Y3Mi4xMTEzQzAuMTM0Mjc3IDgzLjY0NzkgOS40NjgyMiA5MyAyMC45ODIyIDkzSDcyLjEwNUM4My42MTkxIDkzIDkyLjk1MyA4My42NDc5IDkyLjk1MyA3Mi4xMTEzVjIwLjg4ODdDOTIuOTUzIDkuMzUyMTkgODMuNjE5MSAwIDcyLjEwNSAwWiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzIwMzVfMzk1KSIvPgo8cGF0aCBkPSJNNzUuNjc1MyAyOC43NTI0Qzc3LjYwNTYgNTQuOTQ3OSA2MC44IDY3LjMyOTEgNDUuNzM0MSA2OC42NDk0QzMxLjcyNzQgNjkuODc2OCAxOC41NDMgNjEuMjUzNCAxNy4zODYzIDQ4LjAwNDFDMTYuNDMyMiAzNy4wNTgxIDIzLjE4NDMgMzIuMzk3OSAyOC40ODkzIDMxLjkzMzVDMzMuOTQ1NCAzMS40NTQxIDM4LjUzMDcgMzUuMjI0MyAzOC45Mjg1IDM5Ljc4OTFDMzkuMzExNSA0NC4xNzc3IDM2LjU3ODQgNDYuMTc1NCAzNC42Nzc3IDQ2LjM0MTZDMzMuMTc0NCA0Ni40NzM2IDMxLjI4MzIgNDUuNTU5MyAzMS4xMTI1IDQzLjU5NTlDMzAuOTY2IDQxLjkwODggMzEuNjA1NCA0MS42NzkxIDMxLjQ0OTEgMzkuODg2OEMzMS4xNzEgMzYuNjk2MSAyOC4zOTQxIDM2LjMyNDUgMjYuODczOCAzNi40NTY1QzI1LjAzMzkgMzYuNjE4IDIxLjY5NTcgMzguNzY5NSAyMi4xNjQyIDQ0LjEyODhDMjIuNjM1MiA0OS41MzQ2IDI3LjgwODMgNTMuODA2MSAzNC41ODk3IDUzLjIxMkM0MS45MDc5IDUyLjU3MTMgNDcuMDAzIDQ2Ljg2MjQgNDcuMzg2MiAzOC44NTUxQzQ3LjM4MjYgMzguNDMxIDQ3LjQ3MTggMzguMDExMiA0Ny42NDczIDM3LjYyNTJMNDcuNjQ5NiAzNy42MTU2QzQ3LjcyODUgMzcuNDQ3NyA0Ny44MjA4IDM3LjI4NjYgNDcuOTI1NCAzNy4xMzM5QzQ4LjA4MTYgMzYuODk5MiA0OC4yODE4IDM2LjY0IDQ4LjU0MDMgMzYuMzU2M0M0OC41NDI4IDM2LjM0OSA0OC41NDI4IDM2LjM0OSA0OC41NDc3IDM2LjM0OUM0OC43MzU1IDM2LjEzNjMgNDguOTYyNSAzNS45MDY1IDQ5LjIxODcgMzUuNjU5NUM1Mi40MTU1IDMyLjYzNzUgNjMuOTI4MiAyNS41MTA1IDc0LjgxNjMgMjcuNzY3MkM3NS4wNDY2IDI3LjgxNjYgNzUuMjU0NSAyNy45MzkyIDc1LjQwOTMgMjguMTE2OUM3NS41NjQxIDI4LjI5NDUgNzUuNjU3MyAyOC41MTc0IDc1LjY3NTMgMjguNzUyNFoiIGZpbGw9IndoaXRlIi8+CjwvZz4KPC9nPgo8L2c+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMjAzNV8zOTUiIHgxPSI5Mi44OTc1IiB5MT0iMTUuNzE1NCIgeDI9IjMuMjE5OTciIHkyPSI1MC42NTA0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwQ0M1RkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDA3RkZGIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMjAzNV8zOTUiPgo8cmVjdCB3aWR0aD0iOTMiIGhlaWdodD0iOTMiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==";
|
|
6
|
+
var ImTokenConnector = class extends BaseConnector {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.id = "imtoken";
|
|
10
|
+
this.name = "imToken";
|
|
11
|
+
this.icon = IMTOKEN_ICON;
|
|
12
|
+
this.downloadUrl = "https://token.im/download";
|
|
13
|
+
this._publicKey = null;
|
|
14
|
+
this._address = null;
|
|
15
|
+
this._network = "mainnet";
|
|
16
|
+
this._removeAccountChangeListener = null;
|
|
17
|
+
this._removeNetworkChangeListener = null;
|
|
18
|
+
}
|
|
19
|
+
getProvider() {
|
|
20
|
+
if (typeof window === "undefined")
|
|
21
|
+
return void 0;
|
|
22
|
+
return window.bitcoin;
|
|
23
|
+
}
|
|
24
|
+
async connect(network = "mainnet") {
|
|
25
|
+
this.ensureInstalled();
|
|
26
|
+
const provider = this.getProvider();
|
|
27
|
+
try {
|
|
28
|
+
await this.trySwitchNetwork(provider, network);
|
|
29
|
+
const accounts = await provider.request({ method: "btc_requestAccounts" });
|
|
30
|
+
if (!accounts || accounts.length === 0) {
|
|
31
|
+
throw new Error("No accounts found");
|
|
32
|
+
}
|
|
33
|
+
this._publicKey = await provider.request({ method: "btc_getPublicKey" });
|
|
34
|
+
this._address = accounts[0] ?? "";
|
|
35
|
+
this._network = network;
|
|
36
|
+
this.setupEventListeners();
|
|
37
|
+
return {
|
|
38
|
+
address: this._address,
|
|
39
|
+
publicKey: this._publicKey,
|
|
40
|
+
type: this.inferAddressType(this._address)
|
|
41
|
+
};
|
|
42
|
+
} catch (error) {
|
|
43
|
+
this.handleError(error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async trySwitchNetwork(provider, network) {
|
|
47
|
+
try {
|
|
48
|
+
const imTokenNetwork = this.mapToImTokenNetwork(network);
|
|
49
|
+
await provider.request({ method: "btc_switchNetwork", params: [imTokenNetwork] });
|
|
50
|
+
} catch {
|
|
51
|
+
console.warn("imToken network switching may not be supported");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
setupEventListeners() {
|
|
55
|
+
const provider = this.getProvider();
|
|
56
|
+
if (!provider || typeof provider.on !== "function")
|
|
57
|
+
return;
|
|
58
|
+
this.removeEventListeners();
|
|
59
|
+
const handleAccountsChanged = (...args) => {
|
|
60
|
+
const accounts = args[0];
|
|
61
|
+
if (!accounts || accounts.length === 0) {
|
|
62
|
+
this.emitAccountsChanged([]);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
provider.request({ method: "btc_getPublicKey" }).then((publicKey) => {
|
|
66
|
+
this._publicKey = publicKey;
|
|
67
|
+
const walletAccounts = accounts.map((address) => ({
|
|
68
|
+
address,
|
|
69
|
+
publicKey,
|
|
70
|
+
type: this.inferAddressType(address)
|
|
71
|
+
}));
|
|
72
|
+
this.emitAccountsChanged(walletAccounts);
|
|
73
|
+
}).catch(() => {
|
|
74
|
+
this.emitAccountsChanged([]);
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const handleNetworkChanged = () => {
|
|
78
|
+
provider.request({ method: "btc_getNetwork" }).then((network) => {
|
|
79
|
+
const btcNetwork = this.mapNetwork(network);
|
|
80
|
+
this.emitNetworkChanged(btcNetwork);
|
|
81
|
+
}).catch(() => {
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
provider.on("accountsChanged", handleAccountsChanged);
|
|
85
|
+
provider.on("networkChanged", handleNetworkChanged);
|
|
86
|
+
this._removeAccountChangeListener = () => {
|
|
87
|
+
provider.removeListener("accountsChanged", handleAccountsChanged);
|
|
88
|
+
};
|
|
89
|
+
this._removeNetworkChangeListener = () => {
|
|
90
|
+
provider.removeListener("networkChanged", handleNetworkChanged);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
removeEventListeners() {
|
|
94
|
+
this._removeAccountChangeListener?.();
|
|
95
|
+
this._removeAccountChangeListener = null;
|
|
96
|
+
this._removeNetworkChangeListener?.();
|
|
97
|
+
this._removeNetworkChangeListener = null;
|
|
98
|
+
}
|
|
99
|
+
async disconnect() {
|
|
100
|
+
this.removeEventListeners();
|
|
101
|
+
this._publicKey = null;
|
|
102
|
+
this._address = null;
|
|
103
|
+
this.cleanup();
|
|
104
|
+
}
|
|
105
|
+
async getAccounts() {
|
|
106
|
+
this.ensureInstalled();
|
|
107
|
+
const provider = this.getProvider();
|
|
108
|
+
try {
|
|
109
|
+
const addresses = await provider.request({ method: "btc_requestAccounts" });
|
|
110
|
+
const publicKey = await provider.request({ method: "btc_getPublicKey" });
|
|
111
|
+
this._publicKey = publicKey;
|
|
112
|
+
return addresses.map((address) => ({
|
|
113
|
+
address,
|
|
114
|
+
publicKey,
|
|
115
|
+
type: this.inferAddressType(address)
|
|
116
|
+
}));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
this.handleError(error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Sign a message using BIP-322 simple format
|
|
123
|
+
* Note: Only bip322-simple is supported by imToken
|
|
124
|
+
*/
|
|
125
|
+
async signMessage(message) {
|
|
126
|
+
this.ensureInstalled();
|
|
127
|
+
const provider = this.getProvider();
|
|
128
|
+
try {
|
|
129
|
+
return await provider.request({
|
|
130
|
+
method: "btc_signMessage",
|
|
131
|
+
params: [message, "bip322-simple"]
|
|
132
|
+
});
|
|
133
|
+
} catch (error) {
|
|
134
|
+
this.handleError(error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async signPsbt(psbtHex, options) {
|
|
138
|
+
this.ensureInstalled();
|
|
139
|
+
const provider = this.getProvider();
|
|
140
|
+
try {
|
|
141
|
+
const autoFinalize = options?.autoFinalize ?? true;
|
|
142
|
+
return await provider.request({
|
|
143
|
+
method: "btc_signPsbt",
|
|
144
|
+
params: [psbtHex, autoFinalize]
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
this.handleError(error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async signPsbts(psbtHexs) {
|
|
151
|
+
this.ensureInstalled();
|
|
152
|
+
const provider = this.getProvider();
|
|
153
|
+
try {
|
|
154
|
+
return await provider.request({
|
|
155
|
+
method: "btc_signPsbts",
|
|
156
|
+
params: [psbtHexs]
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
this.handleError(error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Send Bitcoin transaction
|
|
164
|
+
* Builds a PSBT using bitcoinjs-lib, signs it with imToken, and broadcasts
|
|
165
|
+
*
|
|
166
|
+
* @param to - Recipient address
|
|
167
|
+
* @param satoshis - Amount to send in satoshis
|
|
168
|
+
* @param options - Send options (feeRate, etc.)
|
|
169
|
+
* @returns Transaction ID after broadcast
|
|
170
|
+
*/
|
|
171
|
+
async sendTransaction(to, satoshis, options) {
|
|
172
|
+
this.ensureInstalled();
|
|
173
|
+
const provider = this.getProvider();
|
|
174
|
+
if (!this._address || !this._publicKey) {
|
|
175
|
+
throw new Error("Not connected. Please call connect() first.");
|
|
176
|
+
}
|
|
177
|
+
const btcNetwork = getBitcoinJsNetwork(this._network);
|
|
178
|
+
const {
|
|
179
|
+
psbtHex,
|
|
180
|
+
selectedUtxos
|
|
181
|
+
} = await generatePsbtForSend(
|
|
182
|
+
to,
|
|
183
|
+
satoshis,
|
|
184
|
+
this._address,
|
|
185
|
+
this._publicKey,
|
|
186
|
+
this._network,
|
|
187
|
+
options
|
|
188
|
+
);
|
|
189
|
+
const signedPsbtHex = await provider.request({
|
|
190
|
+
method: "btc_signPsbt",
|
|
191
|
+
params: [psbtHex, false]
|
|
192
|
+
});
|
|
193
|
+
const signedPsbt = bitcoin.Psbt.fromHex(signedPsbtHex, { network: btcNetwork });
|
|
194
|
+
finalizeAllInputs(signedPsbt, selectedUtxos.length);
|
|
195
|
+
const tx = signedPsbt.extractTransaction();
|
|
196
|
+
const txHex = tx.toHex();
|
|
197
|
+
try {
|
|
198
|
+
const txid = await provider.request({
|
|
199
|
+
method: "btc_sendRawTransaction",
|
|
200
|
+
params: [txHex]
|
|
201
|
+
});
|
|
202
|
+
return txid;
|
|
203
|
+
} catch {
|
|
204
|
+
const btcService = new BtcService(this._network);
|
|
205
|
+
return await btcService.broadcastTransaction(txHex);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Broadcast a raw transaction
|
|
210
|
+
*/
|
|
211
|
+
async broadcastTransaction(txHex) {
|
|
212
|
+
this.ensureInstalled();
|
|
213
|
+
const provider = this.getProvider();
|
|
214
|
+
try {
|
|
215
|
+
return await provider.request({
|
|
216
|
+
method: "btc_sendRawTransaction",
|
|
217
|
+
params: [txHex]
|
|
218
|
+
});
|
|
219
|
+
} catch (error) {
|
|
220
|
+
this.handleError(error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async getNetwork() {
|
|
224
|
+
this.ensureInstalled();
|
|
225
|
+
const provider = this.getProvider();
|
|
226
|
+
try {
|
|
227
|
+
const network = await provider.request({ method: "btc_getNetwork" });
|
|
228
|
+
return this.mapNetwork(network);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
this.handleError(error);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async switchNetwork(network) {
|
|
234
|
+
this.ensureInstalled();
|
|
235
|
+
const provider = this.getProvider();
|
|
236
|
+
try {
|
|
237
|
+
const imTokenNetwork = this.mapToImTokenNetwork(network);
|
|
238
|
+
await provider.request({ method: "btc_switchNetwork", params: [imTokenNetwork] });
|
|
239
|
+
} catch (error) {
|
|
240
|
+
this.handleError(error);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get the public key (cached from connect)
|
|
245
|
+
*/
|
|
246
|
+
getPublicKey() {
|
|
247
|
+
return this._publicKey;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get balance for an address
|
|
251
|
+
*/
|
|
252
|
+
async getBalance(address) {
|
|
253
|
+
this.ensureInstalled();
|
|
254
|
+
const provider = this.getProvider();
|
|
255
|
+
try {
|
|
256
|
+
return await provider.request({
|
|
257
|
+
method: "btc_getBalance",
|
|
258
|
+
params: [address]
|
|
259
|
+
});
|
|
260
|
+
} catch (error) {
|
|
261
|
+
this.handleError(error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
mapNetwork(network) {
|
|
265
|
+
switch (network.toLowerCase()) {
|
|
266
|
+
case "mainnet":
|
|
267
|
+
return "mainnet";
|
|
268
|
+
case "signet":
|
|
269
|
+
return "signet";
|
|
270
|
+
case "testnet":
|
|
271
|
+
return "testnet";
|
|
272
|
+
default:
|
|
273
|
+
return "mainnet";
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
mapToImTokenNetwork(network) {
|
|
277
|
+
switch (network) {
|
|
278
|
+
case "mainnet":
|
|
279
|
+
return "mainnet";
|
|
280
|
+
case "signet":
|
|
281
|
+
case "testnet":
|
|
282
|
+
case "testnet4":
|
|
283
|
+
return "signet";
|
|
284
|
+
default:
|
|
285
|
+
return "mainnet";
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export { ImTokenConnector };
|
|
291
|
+
//# sourceMappingURL=out.js.map
|
|
292
|
+
//# sourceMappingURL=chunk-V66BXDTR.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/imtoken/ImTokenConnector.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n WalletAccount,\n BitcoinNetwork,\n SignPsbtOptions,\n} from 'otx-btc-wallet-core';\nimport * as bitcoin from 'bitcoinjs-lib';\nimport { BaseConnector } from '../base';\nimport { BtcService } from '../utils';\nimport {\n generatePsbtForSend,\n finalizeAllInputs,\n getBitcoinJsNetwork,\n type GeneratePsbtOptions,\n} from '../utils';\n\n/**\n * Options for sendTransaction method\n */\nexport interface ImTokenSendOptions extends GeneratePsbtOptions {\n // Additional imToken-specific options can be added here\n}\n\n/**\n * imToken Bitcoin provider interface\n * Provider is accessible via window.bitcoin\n * @see https://imtoken.gitbook.io/developers/products/webview/bitcoin\n */\ninterface ImTokenBitcoinProvider {\n request(params: { method: 'btc_requestAccounts' }): Promise<string[]>;\n request(params: { method: 'btc_getNetwork' }): Promise<string>;\n request(params: { method: 'btc_getPublicKey' }): Promise<string>;\n request(params: { method: 'btc_getBalance'; params: [string] }): Promise<number>;\n request(params: { method: 'btc_signPsbt'; params: [string, boolean?] }): Promise<string>;\n request(params: { method: 'btc_signPsbts'; params: [string[]] }): Promise<string[]>;\n request(params: {\n method: 'btc_signMessage';\n params: [string, 'bip322-simple' | 'bip322-full' | 'bip322-legacy'];\n }): Promise<string>;\n request(params: { method: 'btc_sendRawTransaction'; params: [string] }): Promise<string>;\n request(params: { method: 'btc_switchNetwork'; params: [string] }): Promise<null>;\n on(event: 'accountsChanged', callback: (...args: unknown[]) => void): void;\n on(event: 'networkChanged', callback: (...args: unknown[]) => void): void;\n removeListener(event: string, callback: (...args: unknown[]) => void): void;\n}\n\n// Extend window type\ndeclare global {\n interface Window {\n bitcoin?: ImTokenBitcoinProvider;\n }\n}\n\n// imToken wallet icon\nconst IMTOKEN_ICON =\n 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5MyIgaGVpZ2h0PSI5MyIgdmlld0JveD0iMCAwIDkzIDkzIiBmaWxsPSJub25lIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzIwMzVfMzk1KSI+CjxtYXNrIGlkPSJtYXNrMF8yMDM1XzM5NSIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSI5MyIgaGVpZ2h0PSI5MyI+CjxwYXRoIGQ9Ik05MyAwSDBWOTNIOTNWMFoiIGZpbGw9IndoaXRlIi8+CjwvbWFzaz4KPGcgbWFzaz0idXJsKCNtYXNrMF8yMDM1XzM5NSkiPgo8bWFzayBpZD0ibWFzazFfMjAzNV8zOTUiIHN0eWxlPSJtYXNrLXR5cGU6bHVtaW5hbmNlIiBtYXNrVW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4PSIwIiB5PSIwIiB3aWR0aD0iOTMiIGhlaWdodD0iOTMiPgo8cGF0aCBkPSJNOTIuODc2OCAwSDAuMDU4MTA1NVY5M0g5Mi44NzY4VjBaIiBmaWxsPSJ3aGl0ZSIvPgo8L21hc2s+CjxnIG1hc2s9InVybCgjbWFzazFfMjAzNV8zOTUpIj4KPHBhdGggZD0iTTcyLjEwNSAwSDIwLjk4MjJDOS40NjgyMiAwIDAuMTM0Mjc3IDkuMzUyMTkgMC4xMzQyNzcgMjAuODg4N1Y3Mi4xMTEzQzAuMTM0Mjc3IDgzLjY0NzkgOS40NjgyMiA5MyAyMC45ODIyIDkzSDcyLjEwNUM4My42MTkxIDkzIDkyLjk1MyA4My42NDc5IDkyLjk1MyA3Mi4xMTEzVjIwLjg4ODdDOTIuOTUzIDkuMzUyMTkgODMuNjE5MSAwIDcyLjEwNSAwWiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzIwMzVfMzk1KSIvPgo8cGF0aCBkPSJNNzUuNjc1MyAyOC43NTI0Qzc3LjYwNTYgNTQuOTQ3OSA2MC44IDY3LjMyOTEgNDUuNzM0MSA2OC42NDk0QzMxLjcyNzQgNjkuODc2OCAxOC41NDMgNjEuMjUzNCAxNy4zODYzIDQ4LjAwNDFDMTYuNDMyMiAzNy4wNTgxIDIzLjE4NDMgMzIuMzk3OSAyOC40ODkzIDMxLjkzMzVDMzMuOTQ1NCAzMS40NTQxIDM4LjUzMDcgMzUuMjI0MyAzOC45Mjg1IDM5Ljc4OTFDMzkuMzExNSA0NC4xNzc3IDM2LjU3ODQgNDYuMTc1NCAzNC42Nzc3IDQ2LjM0MTZDMzMuMTc0NCA0Ni40NzM2IDMxLjI4MzIgNDUuNTU5MyAzMS4xMTI1IDQzLjU5NTlDMzAuOTY2IDQxLjkwODggMzEuNjA1NCA0MS42NzkxIDMxLjQ0OTEgMzkuODg2OEMzMS4xNzEgMzYuNjk2MSAyOC4zOTQxIDM2LjMyNDUgMjYuODczOCAzNi40NTY1QzI1LjAzMzkgMzYuNjE4IDIxLjY5NTcgMzguNzY5NSAyMi4xNjQyIDQ0LjEyODhDMjIuNjM1MiA0OS41MzQ2IDI3LjgwODMgNTMuODA2MSAzNC41ODk3IDUzLjIxMkM0MS45MDc5IDUyLjU3MTMgNDcuMDAzIDQ2Ljg2MjQgNDcuMzg2MiAzOC44NTUxQzQ3LjM4MjYgMzguNDMxIDQ3LjQ3MTggMzguMDExMiA0Ny42NDczIDM3LjYyNTJMNDcuNjQ5NiAzNy42MTU2QzQ3LjcyODUgMzcuNDQ3NyA0Ny44MjA4IDM3LjI4NjYgNDcuOTI1NCAzNy4xMzM5QzQ4LjA4MTYgMzYuODk5MiA0OC4yODE4IDM2LjY0IDQ4LjU0MDMgMzYuMzU2M0M0OC41NDI4IDM2LjM0OSA0OC41NDI4IDM2LjM0OSA0OC41NDc3IDM2LjM0OUM0OC43MzU1IDM2LjEzNjMgNDguOTYyNSAzNS45MDY1IDQ5LjIxODcgMzUuNjU5NUM1Mi40MTU1IDMyLjYzNzUgNjMuOTI4MiAyNS41MTA1IDc0LjgxNjMgMjcuNzY3MkM3NS4wNDY2IDI3LjgxNjYgNzUuMjU0NSAyNy45MzkyIDc1LjQwOTMgMjguMTE2OUM3NS41NjQxIDI4LjI5NDUgNzUuNjU3MyAyOC41MTc0IDc1LjY3NTMgMjguNzUyNFoiIGZpbGw9IndoaXRlIi8+CjwvZz4KPC9nPgo8L2c+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMjAzNV8zOTUiIHgxPSI5Mi44OTc1IiB5MT0iMTUuNzE1NCIgeDI9IjMuMjE5OTciIHkyPSI1MC42NTA0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwQ0M1RkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDA3RkZGIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMjAzNV8zOTUiPgo8cmVjdCB3aWR0aD0iOTMiIGhlaWdodD0iOTMiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==';\n\n/**\n * imToken network type\n */\ntype ImTokenNetwork = 'mainnet' | 'signet';\n\n/**\n * imToken Wallet Connector\n *\n * @see https://imtoken.gitbook.io/developers/products/webview/bitcoin\n */\nexport class ImTokenConnector extends BaseConnector {\n readonly id = 'imtoken';\n readonly name = 'imToken';\n readonly icon = IMTOKEN_ICON;\n readonly downloadUrl = 'https://token.im/download';\n\n private _publicKey: string | null = null;\n private _address: string | null = null;\n private _network: BitcoinNetwork = 'mainnet';\n private _removeAccountChangeListener: (() => void) | null = null;\n private _removeNetworkChangeListener: (() => void) | null = null;\n\n protected getProvider(): ImTokenBitcoinProvider | undefined {\n if (typeof window === 'undefined') return undefined;\n return window.bitcoin;\n }\n\n async connect(network: BitcoinNetwork = 'mainnet'): Promise<WalletAccount> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n // Switch network first (may not be supported yet)\n await this.trySwitchNetwork(provider, network);\n\n // Request accounts\n const accounts = await provider.request({ method: 'btc_requestAccounts' });\n if (!accounts || accounts.length === 0) {\n throw new Error('No accounts found');\n }\n\n // Get public key\n this._publicKey = await provider.request({ method: 'btc_getPublicKey' });\n this._address = accounts[0] ?? '';\n this._network = network;\n\n // Setup event listeners\n this.setupEventListeners();\n\n return {\n address: this._address,\n publicKey: this._publicKey,\n type: this.inferAddressType(this._address),\n };\n } catch (error) {\n this.handleError(error);\n }\n }\n\n private async trySwitchNetwork(\n provider: ImTokenBitcoinProvider,\n network: BitcoinNetwork\n ): Promise<void> {\n try {\n const imTokenNetwork = this.mapToImTokenNetwork(network);\n await provider.request({ method: 'btc_switchNetwork', params: [imTokenNetwork] });\n } catch {\n // Network switching might not be supported yet, continue with current network\n console.warn('imToken network switching may not be supported');\n }\n }\n\n private setupEventListeners(): void {\n const provider = this.getProvider();\n if (!provider || typeof provider.on !== 'function') return;\n\n // Remove existing listeners\n this.removeEventListeners();\n\n // Account change listener\n const handleAccountsChanged = (...args: unknown[]) => {\n const accounts = args[0] as string[];\n if (!accounts || accounts.length === 0) {\n this.emitAccountsChanged([]);\n return;\n }\n\n provider\n .request({ method: 'btc_getPublicKey' })\n .then((publicKey) => {\n this._publicKey = publicKey;\n const walletAccounts: WalletAccount[] = accounts.map((address) => ({\n address,\n publicKey,\n type: this.inferAddressType(address),\n }));\n this.emitAccountsChanged(walletAccounts);\n })\n .catch(() => {\n this.emitAccountsChanged([]);\n });\n };\n\n // Network change listener\n const handleNetworkChanged = () => {\n // When network changes, we need to re-fetch network info\n provider\n .request({ method: 'btc_getNetwork' })\n .then((network) => {\n const btcNetwork = this.mapNetwork(network);\n this.emitNetworkChanged(btcNetwork);\n })\n .catch(() => {\n // Ignore errors\n });\n };\n\n provider.on('accountsChanged', handleAccountsChanged);\n provider.on('networkChanged', handleNetworkChanged);\n\n this._removeAccountChangeListener = () => {\n provider.removeListener('accountsChanged', handleAccountsChanged);\n };\n this._removeNetworkChangeListener = () => {\n provider.removeListener('networkChanged', handleNetworkChanged);\n };\n }\n\n private removeEventListeners(): void {\n this._removeAccountChangeListener?.();\n this._removeAccountChangeListener = null;\n\n this._removeNetworkChangeListener?.();\n this._removeNetworkChangeListener = null;\n }\n\n async disconnect(): Promise<void> {\n this.removeEventListeners();\n this._publicKey = null;\n this._address = null;\n this.cleanup();\n }\n\n async getAccounts(): Promise<WalletAccount[]> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n const addresses = await provider.request({ method: 'btc_requestAccounts' });\n const publicKey = await provider.request({ method: 'btc_getPublicKey' });\n this._publicKey = publicKey;\n\n return addresses.map((address) => ({\n address,\n publicKey,\n type: this.inferAddressType(address),\n }));\n } catch (error) {\n this.handleError(error);\n }\n }\n\n /**\n * Sign a message using BIP-322 simple format\n * Note: Only bip322-simple is supported by imToken\n */\n async signMessage(message: string): Promise<string> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n return await provider.request({\n method: 'btc_signMessage',\n params: [message, 'bip322-simple'],\n });\n } catch (error) {\n this.handleError(error);\n }\n }\n\n async signPsbt(psbtHex: string, options?: SignPsbtOptions): Promise<string> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n const autoFinalize = options?.autoFinalize ?? true;\n return await provider.request({\n method: 'btc_signPsbt',\n params: [psbtHex, autoFinalize],\n });\n } catch (error) {\n this.handleError(error);\n }\n }\n\n async signPsbts(psbtHexs: string[]): Promise<string[]> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n return await provider.request({\n method: 'btc_signPsbts',\n params: [psbtHexs],\n });\n } catch (error) {\n this.handleError(error);\n }\n }\n\n /**\n * Send Bitcoin transaction\n * Builds a PSBT using bitcoinjs-lib, signs it with imToken, and broadcasts\n *\n * @param to - Recipient address\n * @param satoshis - Amount to send in satoshis\n * @param options - Send options (feeRate, etc.)\n * @returns Transaction ID after broadcast\n */\n async sendTransaction(\n to: string,\n satoshis: number,\n options?: ImTokenSendOptions\n ): Promise<string> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n if (!this._address || !this._publicKey) {\n throw new Error('Not connected. Please call connect() first.');\n }\n\n const btcNetwork = getBitcoinJsNetwork(this._network);\n\n // 1. Generate PSBT using the utility\n const {\n psbtHex,\n selectedUtxos,\n } = await generatePsbtForSend(\n to,\n satoshis,\n this._address,\n this._publicKey,\n this._network,\n options\n );\n\n // 2. Sign PSBT with imToken (autoFinalize = false to handle finalization ourselves)\n const signedPsbtHex = await provider.request({\n method: 'btc_signPsbt',\n params: [psbtHex, false],\n });\n\n // 3. Parse signed PSBT and finalize\n const signedPsbt = bitcoin.Psbt.fromHex(signedPsbtHex, { network: btcNetwork });\n finalizeAllInputs(signedPsbt, selectedUtxos.length);\n\n // 4. Extract transaction\n const tx = signedPsbt.extractTransaction();\n const txHex = tx.toHex();\n\n // 5. Broadcast using imToken or fallback to BtcService\n try {\n const txid = await provider.request({\n method: 'btc_sendRawTransaction',\n params: [txHex],\n });\n return txid;\n } catch {\n // Fallback to BtcService broadcast\n const btcService = new BtcService(this._network);\n return await btcService.broadcastTransaction(txHex);\n }\n }\n\n /**\n * Broadcast a raw transaction\n */\n async broadcastTransaction(txHex: string): Promise<string> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n return await provider.request({\n method: 'btc_sendRawTransaction',\n params: [txHex],\n });\n } catch (error) {\n this.handleError(error);\n }\n }\n\n async getNetwork(): Promise<BitcoinNetwork> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n const network = await provider.request({ method: 'btc_getNetwork' });\n return this.mapNetwork(network);\n } catch (error) {\n this.handleError(error);\n }\n }\n\n async switchNetwork(network: BitcoinNetwork): Promise<void> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n const imTokenNetwork = this.mapToImTokenNetwork(network);\n await provider.request({ method: 'btc_switchNetwork', params: [imTokenNetwork] });\n } catch (error) {\n this.handleError(error);\n }\n }\n\n /**\n * Get the public key (cached from connect)\n */\n getPublicKey(): string | null {\n return this._publicKey;\n }\n\n /**\n * Get balance for an address\n */\n async getBalance(address: string): Promise<number> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n return await provider.request({\n method: 'btc_getBalance',\n params: [address],\n });\n } catch (error) {\n this.handleError(error);\n }\n }\n\n private mapNetwork(network: string): BitcoinNetwork {\n switch (network.toLowerCase()) {\n case 'mainnet':\n return 'mainnet';\n case 'signet':\n return 'signet';\n case 'testnet':\n return 'testnet';\n default:\n return 'mainnet';\n }\n }\n\n private mapToImTokenNetwork(network: BitcoinNetwork): ImTokenNetwork {\n switch (network) {\n case 'mainnet':\n return 'mainnet';\n case 'signet':\n case 'testnet':\n case 'testnet4':\n return 'signet';\n default:\n return 'mainnet';\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkFISNQZZ7_js = require('./chunk-FISNQZZ7.js');
|
|
4
|
+
var chunkMFXLQWOE_js = require('./chunk-MFXLQWOE.js');
|
|
5
|
+
var bitcoin = require('bitcoinjs-lib');
|
|
6
|
+
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var bitcoin__namespace = /*#__PURE__*/_interopNamespace(bitcoin);
|
|
26
|
+
|
|
27
|
+
var PHANTOM_ICON = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIwMCAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF83MDM1XzUxMjYpIj4KPG1hc2sgaWQ9Im1hc2swXzcwMzVfNTEyNiIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj4KPHBhdGggZD0iTTIwMCAwSDBWMjAwSDIwMFYwWiIgZmlsbD0id2hpdGUiLz4KPC9tYXNrPgo8ZyBtYXNrPSJ1cmwoI21hc2swXzcwMzVfNTEyNikiPgo8cGF0aCBkPSJNMTU3LjA2OCAwSDQyLjkzMkMxOS4yMjEzIDAgMCAxOS4yMjEzIDAgNDIuOTMyVjE1Ny4wNjhDMCAxODAuNzc5IDE5LjIyMTMgMjAwIDQyLjkzMiAyMDBIMTU3LjA2OEMxODAuNzc5IDIwMCAyMDAgMTgwLjc3OSAyMDAgMTU3LjA2OFY0Mi45MzJDMjAwIDE5LjIyMTMgMTgwLjc3OSAwIDE1Ny4wNjggMFoiIGZpbGw9IiNBQjlGRjIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04Ni4yMDMxIDEyOS45NjlDNzguMzUwMyAxNDIuMDAyIDY1LjE5MTMgMTU3LjIzIDQ3LjY4MTYgMTU3LjIzQzM5LjQwNDMgMTU3LjIzIDMxLjQ0NTMgMTUzLjgyMiAzMS40NDUzIDEzOS4wMkMzMS40NDUzIDEwMS4zMjQgODIuOTEzMyA0Mi45Njg4IDEzMC42NjcgNDIuOTY4OEMxNTcuODM0IDQyLjk2ODggMTY4LjY1OCA2MS44MTcgMTY4LjY1OCA4My4yMjFDMTY4LjY1OCAxMTAuNjk1IDE1MC44MyAxNDIuMTA4IDEzMy4xMDggMTQyLjEwOEMxMjcuNDg0IDE0Mi4xMDggMTI0LjcyNCAxMzkuMDIgMTI0LjcyNCAxMzQuMTIyQzEyNC43MjQgMTMyLjg0NCAxMjQuOTM3IDEzMS40NiAxMjUuMzYxIDEyOS45NjlDMTE5LjMxMiAxNDAuMjk4IDEwNy42MzkgMTQ5Ljg4MiA5Ni43MDkgMTQ5Ljg4MkM4OC43NSAxNDkuODgyIDg0LjcxNzUgMTQ0Ljg3NyA4NC43MTc1IDEzNy44NDlDODQuNzE3NSAxMzUuMjkzIDg1LjI0OCAxMzIuNjMxIDg2LjIwMzEgMTI5Ljk2OVpNMTUwLjcyNyA4Mi40NzgyQzE1MC43MjcgODguNzE1MiAxNDcuMDQ3IDkxLjgzMzcgMTQyLjkzMSA5MS44MzM3QzEzOC43NTIgOTEuODMzNyAxMzUuMTM1IDg4LjcxNTIgMTM1LjEzNSA4Mi40NzgyQzEzNS4xMzUgNzYuMjQxMyAxMzguNzUyIDczLjEyMjggMTQyLjkzMSA3My4xMjI4QzE0Ny4wNDcgNzMuMTIyOCAxNTAuNzI3IDc2LjI0MTMgMTUwLjcyNyA4Mi40NzgyWk0xMjcuMzM4IDgyLjQ3ODVDMTI3LjMzOCA4OC43MTU1IDEyMy42NTkgOTEuODM0IDExOS41NDIgOTEuODM0QzExNS4zNjQgOTEuODM0IDExMS43NDYgODguNzE1NSAxMTEuNzQ2IDgyLjQ3ODVDMTExLjc0NiA3Ni4yNDE1IDExNS4zNjQgNzMuMTIzMiAxMTkuNTQyIDczLjEyMzJDMTIzLjY1OSA3My4xMjMyIDEyNy4zMzggNzYuMjQxNSAxMjcuMzM4IDgyLjQ3ODVaIiBmaWxsPSIjRkZGREY4Ii8+CjwvZz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF83MDM1XzUxMjYiPgo8cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K";
|
|
28
|
+
var PhantomConnector = class extends chunkMFXLQWOE_js.BaseConnector {
|
|
29
|
+
constructor() {
|
|
30
|
+
super(...arguments);
|
|
31
|
+
this.id = "phantom";
|
|
32
|
+
this.name = "Phantom Wallet";
|
|
33
|
+
this.icon = PHANTOM_ICON;
|
|
34
|
+
this._accounts = [];
|
|
35
|
+
this._paymentAccount = null;
|
|
36
|
+
this._ordinalsAccount = null;
|
|
37
|
+
this._removeAccountChangeListener = null;
|
|
38
|
+
}
|
|
39
|
+
getProvider() {
|
|
40
|
+
if (typeof window === "undefined")
|
|
41
|
+
return void 0;
|
|
42
|
+
const provider = window.phantom?.bitcoin;
|
|
43
|
+
if (provider?.isPhantom) {
|
|
44
|
+
return provider;
|
|
45
|
+
}
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
async connect() {
|
|
49
|
+
this.ensureInstalled();
|
|
50
|
+
const provider = this.getProvider();
|
|
51
|
+
try {
|
|
52
|
+
const accounts = await provider.requestAccounts();
|
|
53
|
+
if (!accounts || accounts.length === 0) {
|
|
54
|
+
throw new Error("No accounts returned");
|
|
55
|
+
}
|
|
56
|
+
this._accounts = accounts;
|
|
57
|
+
this.updateAccountsFromBtcAccounts(accounts);
|
|
58
|
+
this.setupEventListeners();
|
|
59
|
+
if (!this._paymentAccount) {
|
|
60
|
+
throw new Error("No payment account found");
|
|
61
|
+
}
|
|
62
|
+
return this._paymentAccount;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.handleError(error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
updateAccountsFromBtcAccounts(accounts) {
|
|
68
|
+
const paymentAcc = accounts.find((a) => a.purpose === "payment");
|
|
69
|
+
const ordinalsAcc = accounts.find((a) => a.purpose === "ordinals");
|
|
70
|
+
if (paymentAcc) {
|
|
71
|
+
this._paymentAccount = {
|
|
72
|
+
address: paymentAcc.address,
|
|
73
|
+
publicKey: paymentAcc.publicKey,
|
|
74
|
+
type: this.mapAddressType(paymentAcc.addressType)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (ordinalsAcc) {
|
|
78
|
+
this._ordinalsAccount = {
|
|
79
|
+
address: ordinalsAcc.address,
|
|
80
|
+
publicKey: ordinalsAcc.publicKey,
|
|
81
|
+
type: this.mapAddressType(ordinalsAcc.addressType)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
mapAddressType(addressType) {
|
|
86
|
+
switch (addressType) {
|
|
87
|
+
case "p2tr":
|
|
88
|
+
return "taproot";
|
|
89
|
+
case "p2wpkh":
|
|
90
|
+
return "segwit";
|
|
91
|
+
case "p2sh":
|
|
92
|
+
return "nested-segwit";
|
|
93
|
+
case "p2pkh":
|
|
94
|
+
return "legacy";
|
|
95
|
+
default:
|
|
96
|
+
return "legacy";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
setupEventListeners() {
|
|
100
|
+
const provider = this.getProvider();
|
|
101
|
+
if (!provider)
|
|
102
|
+
return;
|
|
103
|
+
this.removeEventListeners();
|
|
104
|
+
const handleAccountsChanged = (accounts) => {
|
|
105
|
+
this._accounts = accounts;
|
|
106
|
+
this.updateAccountsFromBtcAccounts(accounts);
|
|
107
|
+
const walletAccounts = accounts.map((acc) => ({
|
|
108
|
+
address: acc.address,
|
|
109
|
+
publicKey: acc.publicKey,
|
|
110
|
+
type: this.mapAddressType(acc.addressType)
|
|
111
|
+
}));
|
|
112
|
+
this.emitAccountsChanged(walletAccounts);
|
|
113
|
+
};
|
|
114
|
+
provider.on("accountsChanged", handleAccountsChanged);
|
|
115
|
+
this._removeAccountChangeListener = () => {
|
|
116
|
+
provider.off("accountsChanged", handleAccountsChanged);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
removeEventListeners() {
|
|
120
|
+
this._removeAccountChangeListener?.();
|
|
121
|
+
this._removeAccountChangeListener = null;
|
|
122
|
+
}
|
|
123
|
+
async disconnect() {
|
|
124
|
+
this.removeEventListeners();
|
|
125
|
+
this._accounts = [];
|
|
126
|
+
this._paymentAccount = null;
|
|
127
|
+
this._ordinalsAccount = null;
|
|
128
|
+
this.cleanup();
|
|
129
|
+
}
|
|
130
|
+
async getAccounts() {
|
|
131
|
+
const accounts = [];
|
|
132
|
+
if (this._paymentAccount)
|
|
133
|
+
accounts.push(this._paymentAccount);
|
|
134
|
+
if (this._ordinalsAccount)
|
|
135
|
+
accounts.push(this._ordinalsAccount);
|
|
136
|
+
return accounts;
|
|
137
|
+
}
|
|
138
|
+
async signMessage(message) {
|
|
139
|
+
this.ensureInstalled();
|
|
140
|
+
const provider = this.getProvider();
|
|
141
|
+
if (!this._paymentAccount) {
|
|
142
|
+
throw new Error("Not connected");
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
146
|
+
const result = await provider.signMessage(this._paymentAccount.address, messageBytes);
|
|
147
|
+
return bytesToHex(result.signature);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
this.handleError(error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async signPsbt(psbtHex, options) {
|
|
153
|
+
this.ensureInstalled();
|
|
154
|
+
const provider = this.getProvider();
|
|
155
|
+
if (!this._paymentAccount) {
|
|
156
|
+
throw new Error("Not connected");
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const psbtBytes = hexToBytes(psbtHex);
|
|
160
|
+
const allAddresses = this._accounts.map((a) => a.address);
|
|
161
|
+
let inputsToSign;
|
|
162
|
+
if (options?.toSignInputs && options.toSignInputs.length > 0) {
|
|
163
|
+
inputsToSign = options.toSignInputs.map((input) => {
|
|
164
|
+
const addr = input.address && allAddresses.includes(input.address) ? input.address : this._paymentAccount.address;
|
|
165
|
+
const item = {
|
|
166
|
+
address: addr,
|
|
167
|
+
signingIndexes: [input.index]
|
|
168
|
+
};
|
|
169
|
+
if (input.sighashTypes && input.sighashTypes[0] !== void 0) {
|
|
170
|
+
item.sigHash = input.sighashTypes[0];
|
|
171
|
+
}
|
|
172
|
+
return item;
|
|
173
|
+
});
|
|
174
|
+
} else {
|
|
175
|
+
inputsToSign = [{
|
|
176
|
+
address: this._paymentAccount.address,
|
|
177
|
+
signingIndexes: [0]
|
|
178
|
+
}];
|
|
179
|
+
}
|
|
180
|
+
const signedPsbt = await provider.signPSBT(psbtBytes, { inputsToSign });
|
|
181
|
+
return bytesToHex(signedPsbt);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
this.handleError(error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Send Bitcoin transaction
|
|
188
|
+
* Builds a PSBT, signs it with Phantom, and broadcasts
|
|
189
|
+
*
|
|
190
|
+
* @param to - Recipient address
|
|
191
|
+
* @param satoshis - Amount to send in satoshis
|
|
192
|
+
* @param options - Send options (feeRate, etc.)
|
|
193
|
+
* @returns Transaction ID after broadcast
|
|
194
|
+
*/
|
|
195
|
+
async sendTransaction(to, satoshis, options) {
|
|
196
|
+
this.ensureInstalled();
|
|
197
|
+
const provider = this.getProvider();
|
|
198
|
+
if (!this._paymentAccount) {
|
|
199
|
+
throw new Error("Not connected. Please call connect() first.");
|
|
200
|
+
}
|
|
201
|
+
const network = "mainnet";
|
|
202
|
+
const btcNetwork = chunkFISNQZZ7_js.getBitcoinJsNetwork(network);
|
|
203
|
+
const {
|
|
204
|
+
psbt,
|
|
205
|
+
selectedUtxos,
|
|
206
|
+
inputsToSign
|
|
207
|
+
} = await chunkFISNQZZ7_js.generatePsbtForSend(
|
|
208
|
+
to,
|
|
209
|
+
satoshis,
|
|
210
|
+
this._paymentAccount.address,
|
|
211
|
+
this._paymentAccount.publicKey,
|
|
212
|
+
network,
|
|
213
|
+
options
|
|
214
|
+
);
|
|
215
|
+
const psbtBytes = psbt.toBuffer();
|
|
216
|
+
const phantomInputsToSign = [{
|
|
217
|
+
address: this._paymentAccount.address,
|
|
218
|
+
signingIndexes: inputsToSign.map((input) => input.index)
|
|
219
|
+
}];
|
|
220
|
+
const signedPsbtBytes = await provider.signPSBT(psbtBytes, {
|
|
221
|
+
inputsToSign: phantomInputsToSign
|
|
222
|
+
});
|
|
223
|
+
const signedPsbt = bitcoin__namespace.Psbt.fromBuffer(
|
|
224
|
+
new Uint8Array(signedPsbtBytes),
|
|
225
|
+
{ network: btcNetwork }
|
|
226
|
+
);
|
|
227
|
+
chunkFISNQZZ7_js.finalizeAllInputs(signedPsbt, selectedUtxos.length);
|
|
228
|
+
const tx = signedPsbt.extractTransaction();
|
|
229
|
+
const txHex = tx.toHex();
|
|
230
|
+
const btcService = new chunkFISNQZZ7_js.BtcService(network);
|
|
231
|
+
return await btcService.broadcastTransaction(txHex);
|
|
232
|
+
}
|
|
233
|
+
async getNetwork() {
|
|
234
|
+
return "mainnet";
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get the ordinals address (if available)
|
|
238
|
+
*/
|
|
239
|
+
getOrdinalsAddress() {
|
|
240
|
+
return this._ordinalsAccount;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get the payment address
|
|
244
|
+
*/
|
|
245
|
+
getPaymentAddress() {
|
|
246
|
+
return this._paymentAccount;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get all connected addresses
|
|
250
|
+
*/
|
|
251
|
+
getAllAddresses() {
|
|
252
|
+
return this._accounts.map((acc) => acc.address);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
function hexToBytes(hex) {
|
|
256
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
257
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
258
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
259
|
+
}
|
|
260
|
+
return bytes;
|
|
261
|
+
}
|
|
262
|
+
function bytesToHex(bytes) {
|
|
263
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
exports.PhantomConnector = PhantomConnector;
|
|
267
|
+
//# sourceMappingURL=out.js.map
|
|
268
|
+
//# sourceMappingURL=chunk-X77ZT4OI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/phantom/PhantomConnector.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n WalletAccount,\n BitcoinNetwork,\n SignPsbtOptions,\n} from 'otx-btc-wallet-core';\nimport * as bitcoin from 'bitcoinjs-lib';\nimport { BaseConnector } from '../base';\nimport { BtcService } from '../utils';\nimport {\n generatePsbtForSend,\n finalizeAllInputs,\n getBitcoinJsNetwork,\n type GeneratePsbtOptions,\n} from '../utils';\n\n/**\n * Options for sendTransaction method\n */\nexport interface PhantomSendOptions extends GeneratePsbtOptions {\n // Additional Phantom-specific options can be added here\n}\n\n/**\n * Phantom Bitcoin account type\n * @see https://docs.phantom.com/bitcoin/establishing-a-connection\n */\ninterface BtcAccount {\n address: string;\n addressType: 'p2tr' | 'p2wpkh' | 'p2sh' | 'p2pkh';\n publicKey: string;\n purpose: 'payment' | 'ordinals';\n}\n\n/**\n * Phantom Bitcoin provider interface\n * @see https://docs.phantom.com/bitcoin/provider-api-reference\n */\ninterface PhantomBitcoinProvider {\n isPhantom: boolean;\n requestAccounts(): Promise<BtcAccount[]>;\n signMessage(address: string, message: Uint8Array): Promise<{ signature: Uint8Array }>;\n signPSBT(\n psbt: Uint8Array,\n options: {\n inputsToSign: Array<{\n address: string;\n signingIndexes: number[];\n sigHash?: number;\n }>;\n }\n ): Promise<Uint8Array>;\n on(event: 'accountsChanged', callback: (accounts: BtcAccount[]) => void): void;\n off(event: 'accountsChanged', callback: (accounts: BtcAccount[]) => void): void;\n}\n\n// Extend window type\ndeclare global {\n interface Window {\n phantom?: {\n bitcoin?: PhantomBitcoinProvider;\n };\n }\n}\n\n// Phantom wallet icon\nconst PHANTOM_ICON =\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIwMCAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF83MDM1XzUxMjYpIj4KPG1hc2sgaWQ9Im1hc2swXzcwMzVfNTEyNiIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj4KPHBhdGggZD0iTTIwMCAwSDBWMjAwSDIwMFYwWiIgZmlsbD0id2hpdGUiLz4KPC9tYXNrPgo8ZyBtYXNrPSJ1cmwoI21hc2swXzcwMzVfNTEyNikiPgo8cGF0aCBkPSJNMTU3LjA2OCAwSDQyLjkzMkMxOS4yMjEzIDAgMCAxOS4yMjEzIDAgNDIuOTMyVjE1Ny4wNjhDMCAxODAuNzc5IDE5LjIyMTMgMjAwIDQyLjkzMiAyMDBIMTU3LjA2OEMxODAuNzc5IDIwMCAyMDAgMTgwLjc3OSAyMDAgMTU3LjA2OFY0Mi45MzJDMjAwIDE5LjIyMTMgMTgwLjc3OSAwIDE1Ny4wNjggMFoiIGZpbGw9IiNBQjlGRjIiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04Ni4yMDMxIDEyOS45NjlDNzguMzUwMyAxNDIuMDAyIDY1LjE5MTMgMTU3LjIzIDQ3LjY4MTYgMTU3LjIzQzM5LjQwNDMgMTU3LjIzIDMxLjQ0NTMgMTUzLjgyMiAzMS40NDUzIDEzOS4wMkMzMS40NDUzIDEwMS4zMjQgODIuOTEzMyA0Mi45Njg4IDEzMC42NjcgNDIuOTY4OEMxNTcuODM0IDQyLjk2ODggMTY4LjY1OCA2MS44MTcgMTY4LjY1OCA4My4yMjFDMTY4LjY1OCAxMTAuNjk1IDE1MC44MyAxNDIuMTA4IDEzMy4xMDggMTQyLjEwOEMxMjcuNDg0IDE0Mi4xMDggMTI0LjcyNCAxMzkuMDIgMTI0LjcyNCAxMzQuMTIyQzEyNC43MjQgMTMyLjg0NCAxMjQuOTM3IDEzMS40NiAxMjUuMzYxIDEyOS45NjlDMTE5LjMxMiAxNDAuMjk4IDEwNy42MzkgMTQ5Ljg4MiA5Ni43MDkgMTQ5Ljg4MkM4OC43NSAxNDkuODgyIDg0LjcxNzUgMTQ0Ljg3NyA4NC43MTc1IDEzNy44NDlDODQuNzE3NSAxMzUuMjkzIDg1LjI0OCAxMzIuNjMxIDg2LjIwMzEgMTI5Ljk2OVpNMTUwLjcyNyA4Mi40NzgyQzE1MC43MjcgODguNzE1MiAxNDcuMDQ3IDkxLjgzMzcgMTQyLjkzMSA5MS44MzM3QzEzOC43NTIgOTEuODMzNyAxMzUuMTM1IDg4LjcxNTIgMTM1LjEzNSA4Mi40NzgyQzEzNS4xMzUgNzYuMjQxMyAxMzguNzUyIDczLjEyMjggMTQyLjkzMSA3My4xMjI4QzE0Ny4wNDcgNzMuMTIyOCAxNTAuNzI3IDc2LjI0MTMgMTUwLjcyNyA4Mi40NzgyWk0xMjcuMzM4IDgyLjQ3ODVDMTI3LjMzOCA4OC43MTU1IDEyMy42NTkgOTEuODM0IDExOS41NDIgOTEuODM0QzExNS4zNjQgOTEuODM0IDExMS43NDYgODguNzE1NSAxMTEuNzQ2IDgyLjQ3ODVDMTExLjc0NiA3Ni4yNDE1IDExNS4zNjQgNzMuMTIzMiAxMTkuNTQyIDczLjEyMzJDMTIzLjY1OSA3My4xMjMyIDEyNy4zMzggNzYuMjQxNSAxMjcuMzM4IDgyLjQ3ODVaIiBmaWxsPSIjRkZGREY4Ii8+CjwvZz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF83MDM1XzUxMjYiPgo8cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K';\n\n/**\n * Phantom Wallet Connector (Bitcoin support)\n *\n * @see https://docs.phantom.com/bitcoin/detecting-the-provider\n */\nexport class PhantomConnector extends BaseConnector {\n readonly id = 'phantom';\n readonly name = 'Phantom Wallet';\n readonly icon = PHANTOM_ICON;\n\n private _accounts: BtcAccount[] = [];\n private _paymentAccount: WalletAccount | null = null;\n private _ordinalsAccount: WalletAccount | null = null;\n private _removeAccountChangeListener: (() => void) | null = null;\n\n protected getProvider(): PhantomBitcoinProvider | undefined {\n if (typeof window === 'undefined') return undefined;\n const provider = window.phantom?.bitcoin;\n if (provider?.isPhantom) {\n return provider;\n }\n return undefined;\n }\n\n async connect(): Promise<WalletAccount> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n try {\n const accounts = await provider.requestAccounts();\n\n if (!accounts || accounts.length === 0) {\n throw new Error('No accounts returned');\n }\n\n this._accounts = accounts;\n this.updateAccountsFromBtcAccounts(accounts);\n\n // Setup event listeners\n this.setupEventListeners();\n\n if (!this._paymentAccount) {\n throw new Error('No payment account found');\n }\n\n return this._paymentAccount;\n } catch (error) {\n this.handleError(error);\n }\n }\n\n private updateAccountsFromBtcAccounts(accounts: BtcAccount[]): void {\n const paymentAcc = accounts.find((a) => a.purpose === 'payment');\n const ordinalsAcc = accounts.find((a) => a.purpose === 'ordinals');\n\n if (paymentAcc) {\n this._paymentAccount = {\n address: paymentAcc.address,\n publicKey: paymentAcc.publicKey,\n type: this.mapAddressType(paymentAcc.addressType),\n };\n }\n\n if (ordinalsAcc) {\n this._ordinalsAccount = {\n address: ordinalsAcc.address,\n publicKey: ordinalsAcc.publicKey,\n type: this.mapAddressType(ordinalsAcc.addressType),\n };\n }\n }\n\n private mapAddressType(addressType: BtcAccount['addressType']): WalletAccount['type'] {\n switch (addressType) {\n case 'p2tr':\n return 'taproot';\n case 'p2wpkh':\n return 'segwit';\n case 'p2sh':\n return 'nested-segwit';\n case 'p2pkh':\n return 'legacy';\n default:\n return 'legacy';\n }\n }\n\n private setupEventListeners(): void {\n const provider = this.getProvider();\n if (!provider) return;\n\n // Remove existing listener\n this.removeEventListeners();\n\n // Account change listener\n const handleAccountsChanged = (accounts: BtcAccount[]) => {\n this._accounts = accounts;\n this.updateAccountsFromBtcAccounts(accounts);\n\n const walletAccounts: WalletAccount[] = accounts.map((acc) => ({\n address: acc.address,\n publicKey: acc.publicKey,\n type: this.mapAddressType(acc.addressType),\n }));\n\n this.emitAccountsChanged(walletAccounts);\n };\n\n provider.on('accountsChanged', handleAccountsChanged);\n\n this._removeAccountChangeListener = () => {\n provider.off('accountsChanged', handleAccountsChanged);\n };\n }\n\n private removeEventListeners(): void {\n this._removeAccountChangeListener?.();\n this._removeAccountChangeListener = null;\n }\n\n async disconnect(): Promise<void> {\n this.removeEventListeners();\n this._accounts = [];\n this._paymentAccount = null;\n this._ordinalsAccount = null;\n this.cleanup();\n }\n\n async getAccounts(): Promise<WalletAccount[]> {\n const accounts: WalletAccount[] = [];\n if (this._paymentAccount) accounts.push(this._paymentAccount);\n if (this._ordinalsAccount) accounts.push(this._ordinalsAccount);\n return accounts;\n }\n\n async signMessage(message: string): Promise<string> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n if (!this._paymentAccount) {\n throw new Error('Not connected');\n }\n\n try {\n // Convert message to Uint8Array\n const messageBytes = new TextEncoder().encode(message);\n const result = await provider.signMessage(this._paymentAccount.address, messageBytes);\n // Convert Uint8Array signature to hex string\n return bytesToHex(result.signature);\n } catch (error) {\n this.handleError(error);\n }\n }\n\n async signPsbt(psbtHex: string, options?: SignPsbtOptions): Promise<string> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n if (!this._paymentAccount) {\n throw new Error('Not connected');\n }\n\n try {\n // Convert hex to Uint8Array\n const psbtBytes = hexToBytes(psbtHex);\n\n // Build inputsToSign\n const allAddresses = this._accounts.map((a) => a.address);\n let inputsToSign: Array<{ address: string; signingIndexes: number[]; sigHash?: number }>;\n\n if (options?.toSignInputs && options.toSignInputs.length > 0) {\n inputsToSign = options.toSignInputs.map((input) => {\n const addr = input.address && allAddresses.includes(input.address)\n ? input.address\n : this._paymentAccount!.address;\n\n const item: { address: string; signingIndexes: number[]; sigHash?: number } = {\n address: addr,\n signingIndexes: [input.index],\n };\n\n if (input.sighashTypes && input.sighashTypes[0] !== undefined) {\n item.sigHash = input.sighashTypes[0];\n }\n\n return item;\n });\n } else {\n // Default: sign all inputs with payment address\n inputsToSign = [{\n address: this._paymentAccount.address,\n signingIndexes: [0],\n }];\n }\n\n const signedPsbt = await provider.signPSBT(psbtBytes, { inputsToSign });\n\n // Convert back to hex\n return bytesToHex(signedPsbt);\n } catch (error) {\n this.handleError(error);\n }\n }\n\n /**\n * Send Bitcoin transaction\n * Builds a PSBT, signs it with Phantom, and broadcasts\n *\n * @param to - Recipient address\n * @param satoshis - Amount to send in satoshis\n * @param options - Send options (feeRate, etc.)\n * @returns Transaction ID after broadcast\n */\n async sendTransaction(\n to: string,\n satoshis: number,\n options?: PhantomSendOptions\n ): Promise<string> {\n this.ensureInstalled();\n const provider = this.getProvider()!;\n\n if (!this._paymentAccount) {\n throw new Error('Not connected. Please call connect() first.');\n }\n\n const network: BitcoinNetwork = 'mainnet'; // Phantom only supports mainnet\n const btcNetwork = getBitcoinJsNetwork(network);\n\n // 1. Generate PSBT using the utility\n const {\n psbt,\n selectedUtxos,\n inputsToSign,\n } = await generatePsbtForSend(\n to,\n satoshis,\n this._paymentAccount.address,\n this._paymentAccount.publicKey,\n network,\n options\n );\n\n // 2. Convert PSBT to Uint8Array for Phantom\n const psbtBytes = psbt.toBuffer();\n\n // 3. Build inputsToSign for Phantom API\n const phantomInputsToSign = [{\n address: this._paymentAccount.address,\n signingIndexes: inputsToSign.map((input) => input.index),\n }];\n\n // 4. Sign PSBT with Phantom\n const signedPsbtBytes = await provider.signPSBT(psbtBytes, {\n inputsToSign: phantomInputsToSign,\n });\n\n // 5. Parse signed PSBT and finalize\n const signedPsbt = bitcoin.Psbt.fromBuffer(\n new Uint8Array(signedPsbtBytes),\n { network: btcNetwork }\n );\n finalizeAllInputs(signedPsbt, selectedUtxos.length);\n\n // 6. Extract transaction\n const tx = signedPsbt.extractTransaction();\n const txHex = tx.toHex();\n\n // 7. Broadcast using BtcService\n const btcService = new BtcService(network);\n return await btcService.broadcastTransaction(txHex);\n }\n\n async getNetwork(): Promise<BitcoinNetwork> {\n // Phantom Bitcoin only supports mainnet currently\n return 'mainnet';\n }\n\n /**\n * Get the ordinals address (if available)\n */\n getOrdinalsAddress(): WalletAccount | null {\n return this._ordinalsAccount;\n }\n\n /**\n * Get the payment address\n */\n getPaymentAddress(): WalletAccount | null {\n return this._paymentAccount;\n }\n\n /**\n * Get all connected addresses\n */\n getAllAddresses(): string[] {\n return this._accounts.map((acc) => acc.address);\n }\n}\n\n// Helper functions for hex/bytes conversion\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n"]}
|