@otoplo/wallet-common 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/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/index.d.ts +2278 -0
- package/dist/index.js +2005 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
- package/src/index.ts +5 -0
- package/src/persistence/datastore/db.ts +47 -0
- package/src/persistence/datastore/index.ts +2 -0
- package/src/persistence/datastore/kv.ts +129 -0
- package/src/persistence/index.ts +2 -0
- package/src/persistence/wallet-db.ts +251 -0
- package/src/services/asset.ts +220 -0
- package/src/services/cache.ts +54 -0
- package/src/services/discovery.ts +110 -0
- package/src/services/index.ts +8 -0
- package/src/services/key.ts +55 -0
- package/src/services/rostrum.ts +214 -0
- package/src/services/session.ts +225 -0
- package/src/services/transaction.ts +388 -0
- package/src/services/tx-transformer.ts +244 -0
- package/src/services/wallet.ts +650 -0
- package/src/state/hooks.ts +45 -0
- package/src/state/index.ts +3 -0
- package/src/state/slices/auth.ts +28 -0
- package/src/state/slices/dapp.ts +32 -0
- package/src/state/slices/index.ts +6 -0
- package/src/state/slices/loader.ts +31 -0
- package/src/state/slices/notifications.ts +44 -0
- package/src/state/slices/status.ts +70 -0
- package/src/state/slices/wallet.ts +112 -0
- package/src/state/store.ts +24 -0
- package/src/types/dapp.types.ts +21 -0
- package/src/types/db.types.ts +142 -0
- package/src/types/index.ts +5 -0
- package/src/types/notification.types.ts +13 -0
- package/src/types/rostrum.types.ts +161 -0
- package/src/types/wallet.types.ts +62 -0
- package/src/utils/asset.ts +103 -0
- package/src/utils/common.ts +159 -0
- package/src/utils/enums.ts +22 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/keypath.ts +15 -0
- package/src/utils/price.ts +40 -0
- package/src/utils/seed.ts +57 -0
- package/src/utils/vault.ts +39 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2005 @@
|
|
|
1
|
+
import { AddressType as E, Networks as k, Address as b, CommonUtils as W, BufferUtils as y, HDPrivateKey as Z, GroupToken as O, GroupIdType as I, TransactionBuilder as Q, Transaction as B, UnitUtils as ee, Script as A, Opcode as v, Hash as U, ScriptOpcode as J, BNExtended as z, Output as xe, TxSigner as te, SighashType as se, ScriptFactory as Ne, OutputSighashType as ae, BufferWriter as Ee } from "libnexa-ts";
|
|
2
|
+
import { ElectrumClient as Me, TransportScheme as L, ConnectionStatus as Be } from "@otoplo/electrum-client";
|
|
3
|
+
import { mnemonicToSeedSync as _e, generateMnemonic as Ce, validateMnemonic as Pe } from "@scure/bip39";
|
|
4
|
+
import Oe from "jszip";
|
|
5
|
+
import { DAppProvider as Ue } from "wallet-comms-sdk";
|
|
6
|
+
import { gcm as de } from "@noble/ciphers/aes.js";
|
|
7
|
+
import { pbkdf2Async as De } from "@noble/hashes/pbkdf2.js";
|
|
8
|
+
import { sha256 as Ve } from "@noble/hashes/sha2.js";
|
|
9
|
+
import { wordlist as ue } from "@scure/bip39/wordlists/english.js";
|
|
10
|
+
import { createSlice as M, createAsyncThunk as Re, createSelector as j } from "@reduxjs/toolkit";
|
|
11
|
+
import { useSelector as He } from "react-redux";
|
|
12
|
+
const g = -1, ne = 9223372036854775807n, Ke = 274710, Nt = "0014461ad25081cb0119d034385ff154c8d3ad6bdd76";
|
|
13
|
+
function T() {
|
|
14
|
+
return k.defaultNetwork == k.testnet;
|
|
15
|
+
}
|
|
16
|
+
function Et(a) {
|
|
17
|
+
return T() ? a == "508c843a4b98fb25f57cf9ebafb245a5c16468f06519cdd467059a91e7b79d52" : a == "edc7144fe1ba4edd0edf35d7eea90f6cb1dba42314aa85da8207e97c5339c801";
|
|
18
|
+
}
|
|
19
|
+
function F(a, e = E.PayToScriptTemplate) {
|
|
20
|
+
return b.isValid(a, k.defaultNetwork, e);
|
|
21
|
+
}
|
|
22
|
+
function Mt() {
|
|
23
|
+
return `https://${T() ? "testnet-" : ""}explorer.nexa.org`;
|
|
24
|
+
}
|
|
25
|
+
function $() {
|
|
26
|
+
return Math.floor(Date.now() / 1e3);
|
|
27
|
+
}
|
|
28
|
+
function Bt(a, e) {
|
|
29
|
+
const t = (e - a) * 2, s = /* @__PURE__ */ new Date();
|
|
30
|
+
return s.setMinutes(s.getMinutes() + t), s.toLocaleDateString();
|
|
31
|
+
}
|
|
32
|
+
function C(a) {
|
|
33
|
+
return !a || a.length === 0;
|
|
34
|
+
}
|
|
35
|
+
function _t(a, e = 0) {
|
|
36
|
+
if (!a || a.length <= e)
|
|
37
|
+
return a || "";
|
|
38
|
+
const t = "...", s = Math.floor((e - t.length) / 2), n = a.slice(0, s), i = a.slice(a.length - s);
|
|
39
|
+
return n + t + i;
|
|
40
|
+
}
|
|
41
|
+
function Ct(a) {
|
|
42
|
+
return a.length === 0 ? "" : a.charAt(0).toUpperCase() + a.substring(1);
|
|
43
|
+
}
|
|
44
|
+
function R(a) {
|
|
45
|
+
return W.isHexa(a) ? y.hexToBuffer(a) : b.fromString(a).data;
|
|
46
|
+
}
|
|
47
|
+
function S(a) {
|
|
48
|
+
return W.isHexa(a) ? a : y.bufferToHex(R(a));
|
|
49
|
+
}
|
|
50
|
+
function ie(a) {
|
|
51
|
+
return W.isHexa(a) ? new b(R(a), k.defaultNetwork, E.GroupIdAddress).toString() : a;
|
|
52
|
+
}
|
|
53
|
+
function D(a) {
|
|
54
|
+
return a == null || a === "" ? "" : BigInt(a) <= 0n ? "0" : a.toString();
|
|
55
|
+
}
|
|
56
|
+
function re(a) {
|
|
57
|
+
let e = BigInt(0), t = BigInt(0);
|
|
58
|
+
return a.forEach((s) => {
|
|
59
|
+
e += BigInt(s.confirmed), t += BigInt(s.unconfirmed);
|
|
60
|
+
}), { confirmed: e.toString(), unconfirmed: t.toString() };
|
|
61
|
+
}
|
|
62
|
+
function G(a) {
|
|
63
|
+
const e = {};
|
|
64
|
+
return a.forEach((t) => {
|
|
65
|
+
for (const s in t)
|
|
66
|
+
e[s] ? (e[s].confirmed = (BigInt(e[s].confirmed) + BigInt(t[s].confirmed)).toString(), e[s].unconfirmed = (BigInt(e[s].unconfirmed) + BigInt(t[s].unconfirmed)).toString()) : e[s] = { confirmed: BigInt(t[s].confirmed).toString(), unconfirmed: BigInt(t[s].unconfirmed).toString() };
|
|
67
|
+
}), e;
|
|
68
|
+
}
|
|
69
|
+
const le = {
|
|
70
|
+
// Images
|
|
71
|
+
//'.svg': { media: 'image', mime: 'image/svg+xml' },
|
|
72
|
+
".gif": { media: "image", mime: "image/gif" },
|
|
73
|
+
".png": { media: "image", mime: "image/png" },
|
|
74
|
+
".apng": { media: "image", mime: "image/apng" },
|
|
75
|
+
".jpg": { media: "image", mime: "image/jpeg" },
|
|
76
|
+
".jpeg": { media: "image", mime: "image/jpeg" },
|
|
77
|
+
".avif": { media: "image", mime: "image/avif" },
|
|
78
|
+
".webp": { media: "image", mime: "image/webp" },
|
|
79
|
+
".bmp": { media: "image", mime: "image/bmp" },
|
|
80
|
+
// Videos
|
|
81
|
+
".mp4": { media: "video", mime: "video/mp4" },
|
|
82
|
+
".mpeg": { media: "video", mime: "video/mpeg" },
|
|
83
|
+
".mpg": { media: "video", mime: "video/mpeg" },
|
|
84
|
+
".webm": { media: "video", mime: "video/webm" },
|
|
85
|
+
".mov": { media: "video", mime: "video/quicktime" },
|
|
86
|
+
// Audio
|
|
87
|
+
".mp3": { media: "audio", mime: "audio/mpeg" },
|
|
88
|
+
".ogg": { media: "audio", mime: "audio/ogg" },
|
|
89
|
+
".wav": { media: "audio", mime: "audio/wav" },
|
|
90
|
+
".m4a": { media: "audio", mime: "audio/mp4" }
|
|
91
|
+
};
|
|
92
|
+
function Pt(a) {
|
|
93
|
+
const e = a.lastIndexOf(".");
|
|
94
|
+
if (e === -1) return "unknown";
|
|
95
|
+
const t = a.slice(e).toLowerCase();
|
|
96
|
+
return le[t]?.media ?? "unknown";
|
|
97
|
+
}
|
|
98
|
+
function Ot(a) {
|
|
99
|
+
const e = a.lastIndexOf(".");
|
|
100
|
+
if (e === -1) return "application/octet-stream";
|
|
101
|
+
const t = a.slice(e).toLowerCase();
|
|
102
|
+
return le[t]?.mime ?? "application/octet-stream";
|
|
103
|
+
}
|
|
104
|
+
class Ut {
|
|
105
|
+
getPrefix() {
|
|
106
|
+
return T() ? "testnet-" : "";
|
|
107
|
+
}
|
|
108
|
+
async getEncryptedSeed() {
|
|
109
|
+
return this.getValue("seed");
|
|
110
|
+
}
|
|
111
|
+
async saveEncryptedSeed(e) {
|
|
112
|
+
return this.setValue("seed", e);
|
|
113
|
+
}
|
|
114
|
+
async getVersionCode() {
|
|
115
|
+
return this.getValue("version-code");
|
|
116
|
+
}
|
|
117
|
+
async setVersionCode(e) {
|
|
118
|
+
return this.setValue("version-code", e);
|
|
119
|
+
}
|
|
120
|
+
async getReleaseNumber() {
|
|
121
|
+
return this.getValue("release-number");
|
|
122
|
+
}
|
|
123
|
+
async setReleaseNumber(e) {
|
|
124
|
+
return this.setValue("release-number", e);
|
|
125
|
+
}
|
|
126
|
+
async getRostrumParams() {
|
|
127
|
+
const e = await this.getValue(this.getPrefix() + "rostrum-params");
|
|
128
|
+
if (e)
|
|
129
|
+
return JSON.parse(e);
|
|
130
|
+
}
|
|
131
|
+
async saveRostrumParams(e) {
|
|
132
|
+
return this.setValue(this.getPrefix() + "rostrum-params", JSON.stringify(e));
|
|
133
|
+
}
|
|
134
|
+
async removeRostrumParams() {
|
|
135
|
+
return this.removeValue(this.getPrefix() + "rostrum-params");
|
|
136
|
+
}
|
|
137
|
+
async setHideZeroTokenConfig(e) {
|
|
138
|
+
return this.setValue("zero-tokens", JSON.stringify(e));
|
|
139
|
+
}
|
|
140
|
+
async getHideZeroTokenConfig() {
|
|
141
|
+
return await this.getValue("zero-tokens") === "true";
|
|
142
|
+
}
|
|
143
|
+
async setHideZeroVaultsConfig(e) {
|
|
144
|
+
return this.setValue("zero-vaults", JSON.stringify(e));
|
|
145
|
+
}
|
|
146
|
+
async getHideZeroVaultsConfig() {
|
|
147
|
+
return await this.getValue("zero-vaults") === "true";
|
|
148
|
+
}
|
|
149
|
+
async getSelectedCurrency() {
|
|
150
|
+
return await this.getValue("selectedCurrency") || "usd";
|
|
151
|
+
}
|
|
152
|
+
async setSelectedCurrency(e) {
|
|
153
|
+
return this.setValue("selectedCurrency", e);
|
|
154
|
+
}
|
|
155
|
+
async getUseBiometric() {
|
|
156
|
+
return await this.getValue("biometrics") === "true";
|
|
157
|
+
}
|
|
158
|
+
async setUseBiometric(e) {
|
|
159
|
+
return this.setValue("biometrics", JSON.stringify(e));
|
|
160
|
+
}
|
|
161
|
+
async getRequireAuth() {
|
|
162
|
+
const e = await this.getValue("auth");
|
|
163
|
+
return C(e) || e === "true";
|
|
164
|
+
}
|
|
165
|
+
async setRequireAuth(e) {
|
|
166
|
+
await this.setValue("auth", JSON.stringify(e));
|
|
167
|
+
}
|
|
168
|
+
async getReleaseNotesTime() {
|
|
169
|
+
const e = await this.getValue("show-notes");
|
|
170
|
+
return e ? Number(e) : void 0;
|
|
171
|
+
}
|
|
172
|
+
async setReleaseNotesTime(e) {
|
|
173
|
+
C(e) ? await this.removeValue("show-notes") : await this.setValue("show-notes", e);
|
|
174
|
+
}
|
|
175
|
+
async getIsTestnet() {
|
|
176
|
+
return await this.getValue("testnet") === "1";
|
|
177
|
+
}
|
|
178
|
+
async setIsTestnet(e) {
|
|
179
|
+
await this.setValue("testnet", e ? "1" : "0");
|
|
180
|
+
}
|
|
181
|
+
async getAutoLockSeconds() {
|
|
182
|
+
const e = await this.getValue("auto-lock");
|
|
183
|
+
return e ? parseInt(e) : 60;
|
|
184
|
+
}
|
|
185
|
+
async setAutoLockSeconds(e) {
|
|
186
|
+
await this.setValue("auto-lock", `${e}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
var f = /* @__PURE__ */ ((a) => (a[a.MAIN = 0] = "MAIN", a[a.VAULT = 1] = "VAULT", a[a.DAPP = 2] = "DAPP", a))(f || {}), m = /* @__PURE__ */ ((a) => (a[a.RECEIVE = 0] = "RECEIVE", a[a.CHANGE = 1] = "CHANGE", a))(m || {}), _ = /* @__PURE__ */ ((a) => (a.SignMessage = "signMessage", a.AddToken = "addToken", a.SignTransaction = "signTransaction", a.SendTransaction = "sendTransaction", a))(_ || {}), V = /* @__PURE__ */ ((a) => (a.TOKEN = "token", a.NFT = "nft", a))(V || {});
|
|
190
|
+
class Dt {
|
|
191
|
+
store;
|
|
192
|
+
updateCallback;
|
|
193
|
+
constructor(e) {
|
|
194
|
+
this.store = e;
|
|
195
|
+
}
|
|
196
|
+
onUpdate(e) {
|
|
197
|
+
this.updateCallback = e;
|
|
198
|
+
}
|
|
199
|
+
notify(e) {
|
|
200
|
+
this.updateCallback?.(e);
|
|
201
|
+
}
|
|
202
|
+
async initSchema() {
|
|
203
|
+
return this.store.initSchema();
|
|
204
|
+
}
|
|
205
|
+
async clearData() {
|
|
206
|
+
return this.store.clearData();
|
|
207
|
+
}
|
|
208
|
+
async addLocalTransaction(e) {
|
|
209
|
+
try {
|
|
210
|
+
await this.store.upsertTransaction(e), this.notify({ type: "tx_refresh" });
|
|
211
|
+
} catch (t) {
|
|
212
|
+
console.error(t);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async addAssetTransaction(e) {
|
|
216
|
+
return this.store.upsertAssetTransaction(e);
|
|
217
|
+
}
|
|
218
|
+
async getLocalTransactions(e, t) {
|
|
219
|
+
return this.store.getTransactions(e, t);
|
|
220
|
+
}
|
|
221
|
+
async getPageLocalTransactions(e, t, s, n) {
|
|
222
|
+
return this.store.getPageTransactions(e, t, s, n);
|
|
223
|
+
}
|
|
224
|
+
async countLocalTransactions(e, t) {
|
|
225
|
+
return this.store.countTransactions(e, t);
|
|
226
|
+
}
|
|
227
|
+
async clearLocalTransactions(e) {
|
|
228
|
+
await this.store.clearTransactions(e), this.notify({ type: "tx_refresh" });
|
|
229
|
+
}
|
|
230
|
+
async saveToken(e, t) {
|
|
231
|
+
const s = {
|
|
232
|
+
accountId: e,
|
|
233
|
+
tokenIdHex: t.tokenIdHex,
|
|
234
|
+
type: V.TOKEN,
|
|
235
|
+
addedTime: $()
|
|
236
|
+
};
|
|
237
|
+
await this.store.upsertAsset(s), await this.store.upsertToken(t), this.notify({ type: "token_added", accountId: e, token: s });
|
|
238
|
+
}
|
|
239
|
+
async getLocalTokens(e) {
|
|
240
|
+
return this.store.getTokens(e);
|
|
241
|
+
}
|
|
242
|
+
async getToken(e) {
|
|
243
|
+
return this.store.getToken(e);
|
|
244
|
+
}
|
|
245
|
+
async deleteToken(e, t) {
|
|
246
|
+
await this.removeAsset(e, t), await this.store.countAssetsById(t) == 0 && await this.store.deleteToken(t), this.notify({ type: "token_removed", accountId: e, tokenId: t });
|
|
247
|
+
}
|
|
248
|
+
async saveNft(e, t) {
|
|
249
|
+
await this.store.upsertNft(t), await this.saveAsset(e, !0);
|
|
250
|
+
}
|
|
251
|
+
async getLocalNfts(e, t, s) {
|
|
252
|
+
return this.store.getNfts(e, t, s);
|
|
253
|
+
}
|
|
254
|
+
async getLocalNft(e) {
|
|
255
|
+
return this.store.getNft(e);
|
|
256
|
+
}
|
|
257
|
+
async deleteNft(e, t, s = !1) {
|
|
258
|
+
await this.removeAsset(e, t), await this.store.countAssetsById(t) == 0 && (await this.store.deleteNft(t), this.notify({ type: "nft_deleted", id: t })), s && this.notify({ type: "nft_refresh" });
|
|
259
|
+
}
|
|
260
|
+
async countLocalNfts(e) {
|
|
261
|
+
return this.store.countNfts(e);
|
|
262
|
+
}
|
|
263
|
+
async isNftExist(e) {
|
|
264
|
+
return this.store.isNftExist(e);
|
|
265
|
+
}
|
|
266
|
+
async saveAsset(e, t = !1) {
|
|
267
|
+
await this.store.upsertAsset(e), t && this.notify({ type: "nft_refresh" });
|
|
268
|
+
}
|
|
269
|
+
async removeAsset(e, t) {
|
|
270
|
+
return this.store.deleteAsset(e, t);
|
|
271
|
+
}
|
|
272
|
+
async isAssetExistForAccount(e, t) {
|
|
273
|
+
return this.store.isAssetExistForAccount(e, t);
|
|
274
|
+
}
|
|
275
|
+
async saveAccount(e) {
|
|
276
|
+
const t = {
|
|
277
|
+
id: e.id,
|
|
278
|
+
address: e.address,
|
|
279
|
+
name: e.name,
|
|
280
|
+
height: e.height,
|
|
281
|
+
hidden: e.hidden,
|
|
282
|
+
statusHash: e.statusHash,
|
|
283
|
+
balance: JSON.stringify(e.balance),
|
|
284
|
+
tokensBalance: JSON.stringify(e.tokensBalance)
|
|
285
|
+
};
|
|
286
|
+
return this.store.upsertAccount(t);
|
|
287
|
+
}
|
|
288
|
+
async getAccounts() {
|
|
289
|
+
return (await this.store.getAccounts()).map((t) => ({
|
|
290
|
+
...t,
|
|
291
|
+
type: f.DAPP,
|
|
292
|
+
balance: JSON.parse(t.balance),
|
|
293
|
+
tokensBalance: JSON.parse(t.tokensBalance)
|
|
294
|
+
}));
|
|
295
|
+
}
|
|
296
|
+
async countAccounts() {
|
|
297
|
+
return this.store.countAccounts();
|
|
298
|
+
}
|
|
299
|
+
async updateAccountName(e, t) {
|
|
300
|
+
return this.store.updateAccountName(e, t);
|
|
301
|
+
}
|
|
302
|
+
async saveSession(e) {
|
|
303
|
+
return this.store.upsertSession(e);
|
|
304
|
+
}
|
|
305
|
+
async getAccountSessions(e) {
|
|
306
|
+
return this.store.getSessionsByAccount(e);
|
|
307
|
+
}
|
|
308
|
+
async removeSession(e) {
|
|
309
|
+
return this.store.deleteSession(e);
|
|
310
|
+
}
|
|
311
|
+
async saveAddress(e) {
|
|
312
|
+
const t = {
|
|
313
|
+
address: e.address,
|
|
314
|
+
idx: e.idx,
|
|
315
|
+
space: e.space,
|
|
316
|
+
height: e.height,
|
|
317
|
+
statusHash: e.statusHash,
|
|
318
|
+
used: e.used,
|
|
319
|
+
balance: JSON.stringify(e.balance),
|
|
320
|
+
tokensBalance: JSON.stringify(e.tokensBalance)
|
|
321
|
+
};
|
|
322
|
+
return this.store.upsertAddress(t);
|
|
323
|
+
}
|
|
324
|
+
async getReceiveAddresses() {
|
|
325
|
+
return (await this.store.getReceiveAddresses()).map((t) => ({
|
|
326
|
+
...t,
|
|
327
|
+
type: f.MAIN,
|
|
328
|
+
balance: JSON.parse(t.balance),
|
|
329
|
+
tokensBalance: JSON.parse(t.tokensBalance)
|
|
330
|
+
}));
|
|
331
|
+
}
|
|
332
|
+
async getChangeAddresses() {
|
|
333
|
+
return (await this.store.getChangeAddresses()).map((t) => ({
|
|
334
|
+
...t,
|
|
335
|
+
type: f.MAIN,
|
|
336
|
+
balance: JSON.parse(t.balance),
|
|
337
|
+
tokensBalance: JSON.parse(t.tokensBalance)
|
|
338
|
+
}));
|
|
339
|
+
}
|
|
340
|
+
async countAddresses() {
|
|
341
|
+
return this.store.countAddresses();
|
|
342
|
+
}
|
|
343
|
+
async getVaults() {
|
|
344
|
+
return (await this.store.getVaults()).map((t) => ({
|
|
345
|
+
...t,
|
|
346
|
+
type: f.VAULT,
|
|
347
|
+
balance: JSON.parse(t.balance),
|
|
348
|
+
tokensBalance: JSON.parse(t.tokensBalance)
|
|
349
|
+
}));
|
|
350
|
+
}
|
|
351
|
+
async saveVault(e) {
|
|
352
|
+
const t = {
|
|
353
|
+
address: e.address,
|
|
354
|
+
idx: e.idx,
|
|
355
|
+
block: e.block,
|
|
356
|
+
height: e.height,
|
|
357
|
+
statusHash: e.statusHash,
|
|
358
|
+
balance: JSON.stringify(e.balance),
|
|
359
|
+
tokensBalance: JSON.stringify(e.tokensBalance)
|
|
360
|
+
};
|
|
361
|
+
return this.store.upsertVault(t);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
class Vt {
|
|
365
|
+
walletDb;
|
|
366
|
+
tokens = /* @__PURE__ */ new Map();
|
|
367
|
+
nfts = /* @__PURE__ */ new Map();
|
|
368
|
+
constructor(e) {
|
|
369
|
+
this.walletDb = e;
|
|
370
|
+
}
|
|
371
|
+
clear() {
|
|
372
|
+
this.tokens.clear(), this.nfts.clear();
|
|
373
|
+
}
|
|
374
|
+
async getTokenById(e) {
|
|
375
|
+
if (e = S(e), this.tokens.has(e))
|
|
376
|
+
return this.tokens.get(e);
|
|
377
|
+
const t = await this.walletDb.getToken(e);
|
|
378
|
+
return t && this.tokens.set(e, t), t;
|
|
379
|
+
}
|
|
380
|
+
async getNftById(e) {
|
|
381
|
+
if (e = S(e), this.nfts.has(e))
|
|
382
|
+
return this.nfts.get(e);
|
|
383
|
+
const t = await this.walletDb.getLocalNft(e);
|
|
384
|
+
return t && this.nfts.set(e, t), t;
|
|
385
|
+
}
|
|
386
|
+
removeToken(e) {
|
|
387
|
+
this.tokens.delete(S(e));
|
|
388
|
+
}
|
|
389
|
+
removeNft(e) {
|
|
390
|
+
this.nfts.delete(S(e));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
class he {
|
|
394
|
+
kvStore;
|
|
395
|
+
client;
|
|
396
|
+
constructor(e) {
|
|
397
|
+
this.kvStore = e;
|
|
398
|
+
}
|
|
399
|
+
getFeatures() {
|
|
400
|
+
return this.execute("server.features");
|
|
401
|
+
}
|
|
402
|
+
getBlockTip() {
|
|
403
|
+
return this.execute("blockchain.headers.tip");
|
|
404
|
+
}
|
|
405
|
+
getBalance(e) {
|
|
406
|
+
return this.execute("blockchain.address.get_balance", e, "exclude_tokens");
|
|
407
|
+
}
|
|
408
|
+
getTransactionsHistory(e, t = 0) {
|
|
409
|
+
return this.execute("blockchain.address.get_history", e, { from_height: t });
|
|
410
|
+
}
|
|
411
|
+
getFirstUse(e) {
|
|
412
|
+
return this.execute("blockchain.address.get_first_use", e);
|
|
413
|
+
}
|
|
414
|
+
async isAddressUsed(e) {
|
|
415
|
+
try {
|
|
416
|
+
const t = await this.getFirstUse(e);
|
|
417
|
+
return !!(t.tx_hash && t.tx_hash !== "");
|
|
418
|
+
} catch (t) {
|
|
419
|
+
if (t instanceof Error && t.message.includes("not found"))
|
|
420
|
+
return !1;
|
|
421
|
+
throw t;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
getTransaction(e, t = !0) {
|
|
425
|
+
return this.execute("blockchain.transaction.get", e, t);
|
|
426
|
+
}
|
|
427
|
+
getUtxo(e) {
|
|
428
|
+
return this.execute("blockchain.utxo.get", e);
|
|
429
|
+
}
|
|
430
|
+
getNexaUtxos(e) {
|
|
431
|
+
return this.execute("blockchain.address.listunspent", e, "exclude_tokens");
|
|
432
|
+
}
|
|
433
|
+
async getTokenUtxos(e, t) {
|
|
434
|
+
return (await this.execute("token.address.listunspent", e, null, t)).unspent;
|
|
435
|
+
}
|
|
436
|
+
async getTokensBalance(e) {
|
|
437
|
+
const t = await this.execute("token.address.get_balance", e), s = {};
|
|
438
|
+
for (const n in t.confirmed)
|
|
439
|
+
t.confirmed[n] != 0 && (s[n] = { confirmed: BigInt(t.confirmed[n]).toString(), unconfirmed: "0" });
|
|
440
|
+
for (const n in t.unconfirmed)
|
|
441
|
+
t.unconfirmed[n] != 0 && (s[n] ? s[n].unconfirmed = BigInt(t.unconfirmed[n]).toString() : s[n] = { confirmed: "0", unconfirmed: BigInt(t.unconfirmed[n]).toString() });
|
|
442
|
+
return s;
|
|
443
|
+
}
|
|
444
|
+
getTokenGenesis(e) {
|
|
445
|
+
return this.execute("token.genesis.info", e);
|
|
446
|
+
}
|
|
447
|
+
broadcast(e) {
|
|
448
|
+
return this.execute("blockchain.transaction.broadcast", e);
|
|
449
|
+
}
|
|
450
|
+
subscribeHeaders(e) {
|
|
451
|
+
return this.client.subscribe((t) => {
|
|
452
|
+
const s = Array.isArray(t) ? t[0] : t, n = typeof s?.height == "number" ? s.height : 0;
|
|
453
|
+
e(n);
|
|
454
|
+
}, "blockchain.headers.subscribe");
|
|
455
|
+
}
|
|
456
|
+
subscribeAddress(e, t) {
|
|
457
|
+
return this.client.subscribe(t, "blockchain.address.subscribe", e);
|
|
458
|
+
}
|
|
459
|
+
async getLatency() {
|
|
460
|
+
try {
|
|
461
|
+
const e = Date.now();
|
|
462
|
+
return await this.getBlockTip() ? Date.now() - e : 0;
|
|
463
|
+
} catch {
|
|
464
|
+
return 0;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async connect(e) {
|
|
468
|
+
try {
|
|
469
|
+
e || (e = await this.getCurrentInstance()), this.client = new Me("com.otoplo.wallet", "1.4.3", e.host, e.port, e.scheme, 45 * 1e3, 10 * 1e3), await this.client.connect();
|
|
470
|
+
} catch (t) {
|
|
471
|
+
throw t instanceof Error ? console.info(t.message) : console.error(t), t;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
async disconnect(e) {
|
|
475
|
+
try {
|
|
476
|
+
return await this.client?.disconnect(e) ?? !1;
|
|
477
|
+
} catch (t) {
|
|
478
|
+
return console.error(t), !1;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async execute(e, ...t) {
|
|
482
|
+
await this.waitForConnection();
|
|
483
|
+
const s = await this.client.request(e, ...t);
|
|
484
|
+
if (s instanceof Error)
|
|
485
|
+
throw s;
|
|
486
|
+
return s;
|
|
487
|
+
}
|
|
488
|
+
waitForConnection(e = 5e3) {
|
|
489
|
+
const t = Date.now();
|
|
490
|
+
return new Promise((s, n) => {
|
|
491
|
+
const i = () => {
|
|
492
|
+
if (this.client?.connectionStatus == Be.CONNECTED)
|
|
493
|
+
return s();
|
|
494
|
+
if (Date.now() - t > e)
|
|
495
|
+
return n(new Error("Rostrum Connection timeout"));
|
|
496
|
+
setTimeout(i, 250);
|
|
497
|
+
};
|
|
498
|
+
i();
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
async getCurrentInstance() {
|
|
502
|
+
const e = await this.kvStore.getRostrumParams();
|
|
503
|
+
return e || he.getPredefinedInstances()[0];
|
|
504
|
+
}
|
|
505
|
+
static getPredefinedInstances() {
|
|
506
|
+
return T() ? [
|
|
507
|
+
{
|
|
508
|
+
scheme: L.WSS,
|
|
509
|
+
host: "testnet-electrum.nexa.org",
|
|
510
|
+
port: 30004,
|
|
511
|
+
label: "NexaOrg"
|
|
512
|
+
}
|
|
513
|
+
] : [
|
|
514
|
+
{
|
|
515
|
+
scheme: L.WSS,
|
|
516
|
+
host: "rostrum.otoplo.com",
|
|
517
|
+
port: 443,
|
|
518
|
+
label: "Otoplo"
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
scheme: L.WSS,
|
|
522
|
+
host: "electrum.nexa.org",
|
|
523
|
+
port: 20004,
|
|
524
|
+
label: "NexaOrg"
|
|
525
|
+
}
|
|
526
|
+
];
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function x(a, e, t) {
|
|
530
|
+
return `${a}'/${e}/${t}`;
|
|
531
|
+
}
|
|
532
|
+
function Le(a) {
|
|
533
|
+
const e = a.split("/");
|
|
534
|
+
return {
|
|
535
|
+
account: parseInt(e[0].replace("'", "")),
|
|
536
|
+
type: parseInt(e[1]),
|
|
537
|
+
index: parseInt(e[2])
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
class Rt {
|
|
541
|
+
seed;
|
|
542
|
+
masterKey;
|
|
543
|
+
accountKeys = /* @__PURE__ */ new Map();
|
|
544
|
+
walletKeys = /* @__PURE__ */ new Map();
|
|
545
|
+
init(e) {
|
|
546
|
+
this.seed = typeof e == "string" ? _e(e) : e, this.masterKey = Z.fromSeed(this.seed).deriveChild(44, !0).deriveChild(29223, !0);
|
|
547
|
+
}
|
|
548
|
+
reset() {
|
|
549
|
+
if (!this.seed)
|
|
550
|
+
throw new Error("KeysManager not initialized");
|
|
551
|
+
this.masterKey = Z.fromSeed(this.seed).deriveChild(44, !0).deriveChild(29223, !0), this.accountKeys.clear(), this.walletKeys.clear();
|
|
552
|
+
}
|
|
553
|
+
getAccountKey(e) {
|
|
554
|
+
let t = this.accountKeys.get(e);
|
|
555
|
+
return t || (t = this.masterKey.deriveChild(e, !0), this.accountKeys.set(e, t)), t;
|
|
556
|
+
}
|
|
557
|
+
getKey(e) {
|
|
558
|
+
const t = typeof e == "string" ? e : x(e.account, e.type, e.index);
|
|
559
|
+
let s = this.walletKeys.get(t);
|
|
560
|
+
if (!s) {
|
|
561
|
+
const { account: n, type: i, index: o } = typeof e == "string" ? Le(e) : e;
|
|
562
|
+
s = this.getAccountKey(n).deriveChild(i, !1).deriveChild(o, !1), this.walletKeys.set(t, s);
|
|
563
|
+
}
|
|
564
|
+
return s;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function fe() {
|
|
568
|
+
return {
|
|
569
|
+
name: "NiftyArt",
|
|
570
|
+
ticker: "NIFTY",
|
|
571
|
+
iconUrl: "https://niftyart.cash/td/niftyicon.svg",
|
|
572
|
+
parentGroup: "",
|
|
573
|
+
token: T() ? "nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx" : "nexa:tr9v70v4s9s6jfwz32ts60zqmmkp50lqv7t0ux620d50xa7dhyqqqcg6kdm6f",
|
|
574
|
+
tokenIdHex: T() ? "0e38fbf897c1d10bcce33883d40acb28fa7a37cc0733598526aecbdff4b50000" : "cacf3d958161a925c28a970d3c40deec1a3fe06796fe1b4a7b68f377cdb90000",
|
|
575
|
+
decimals: 0
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function Ht(a) {
|
|
579
|
+
return Y(qe() + a, "raw");
|
|
580
|
+
}
|
|
581
|
+
function q(a) {
|
|
582
|
+
try {
|
|
583
|
+
const e = R(a);
|
|
584
|
+
return O.isSubgroup(e) ? y.bufferToHex(e.subarray(0, 32)) === fe().tokenIdHex : !1;
|
|
585
|
+
} catch {
|
|
586
|
+
return !1;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function Fe(a) {
|
|
590
|
+
return a = X(a), Y(a, "json");
|
|
591
|
+
}
|
|
592
|
+
function Ge(a) {
|
|
593
|
+
return a = X(a), Y(a, "raw");
|
|
594
|
+
}
|
|
595
|
+
function oe(a, e) {
|
|
596
|
+
return a && typeof a == "string" ? a.startsWith("http") ? a.replace("http://", "https://") : a.startsWith("ipfs://") ? a : `${new URL(e).origin}${a}` : "";
|
|
597
|
+
}
|
|
598
|
+
function Kt(a) {
|
|
599
|
+
return a ? X(a) : null;
|
|
600
|
+
}
|
|
601
|
+
function X(a, e = "https://ipfs.nebula.markets/") {
|
|
602
|
+
return a?.startsWith("ipfs://") ? e + a.substring(7) : a;
|
|
603
|
+
}
|
|
604
|
+
function qe() {
|
|
605
|
+
return `https://${T() ? "testnet." : ""}niftyart.cash/_public/`;
|
|
606
|
+
}
|
|
607
|
+
async function Y(a, e) {
|
|
608
|
+
try {
|
|
609
|
+
const t = await fetch(a);
|
|
610
|
+
if (!t.ok)
|
|
611
|
+
throw new Error(`Failed to perform GET. status: ${t.status}`);
|
|
612
|
+
if (e === "raw") {
|
|
613
|
+
const s = await t.arrayBuffer();
|
|
614
|
+
return new Uint8Array(s);
|
|
615
|
+
} else
|
|
616
|
+
return await t.json();
|
|
617
|
+
} catch (t) {
|
|
618
|
+
throw new Error(`Unexpected Error: ${t}`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
class Lt {
|
|
622
|
+
walletDb;
|
|
623
|
+
rostrumService;
|
|
624
|
+
walletCache;
|
|
625
|
+
constructor(e, t, s) {
|
|
626
|
+
this.walletDb = e, this.rostrumService = t, this.walletCache = s;
|
|
627
|
+
}
|
|
628
|
+
async getTokenInfo(e, t = !0) {
|
|
629
|
+
try {
|
|
630
|
+
if (t) {
|
|
631
|
+
const c = await this.walletCache.getTokenById(e);
|
|
632
|
+
if (c)
|
|
633
|
+
return c;
|
|
634
|
+
}
|
|
635
|
+
const s = await this.rostrumService.getTokenGenesis(e), n = y.hexToBuffer(s.token_id_hex);
|
|
636
|
+
let i = "", o = "";
|
|
637
|
+
if (s.op_return_id == I.NRC2 || s.op_return_id == I.NRC3 || q(e))
|
|
638
|
+
return;
|
|
639
|
+
if (O.isSubgroup(n) && (i = new b(O.getParentGroupId(n), k.defaultNetwork, E.GroupIdAddress).toString()), s.document_url)
|
|
640
|
+
try {
|
|
641
|
+
if (s.op_return_id == I.NRC1) {
|
|
642
|
+
const c = await Ge(s.document_url), u = (await Oe.loadAsync(c)).file("info.json");
|
|
643
|
+
if (u) {
|
|
644
|
+
const l = await u.async("string"), h = JSON.parse(l);
|
|
645
|
+
o = oe(h[0]?.icon, s.document_url);
|
|
646
|
+
}
|
|
647
|
+
} else {
|
|
648
|
+
const c = await Fe(s.document_url);
|
|
649
|
+
o = oe(c[0]?.icon, s.document_url);
|
|
650
|
+
}
|
|
651
|
+
} catch (c) {
|
|
652
|
+
console.error("Failed to load metadata", c);
|
|
653
|
+
}
|
|
654
|
+
return {
|
|
655
|
+
token: s.group,
|
|
656
|
+
tokenIdHex: s.token_id_hex,
|
|
657
|
+
decimals: s.decimal_places ?? 0,
|
|
658
|
+
parentGroup: i,
|
|
659
|
+
name: s.name ?? "",
|
|
660
|
+
ticker: s.ticker ?? "",
|
|
661
|
+
iconUrl: o
|
|
662
|
+
};
|
|
663
|
+
} catch (s) {
|
|
664
|
+
console.error(s);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async isNftToken(e) {
|
|
669
|
+
try {
|
|
670
|
+
if (q(e) || await this.walletCache.getNftById(e) || (await this.rostrumService.getTokenGenesis(e)).op_return_id == I.NRC3)
|
|
671
|
+
return !0;
|
|
672
|
+
} catch (t) {
|
|
673
|
+
console.error(t);
|
|
674
|
+
}
|
|
675
|
+
return !1;
|
|
676
|
+
}
|
|
677
|
+
async getAssetInfo(e) {
|
|
678
|
+
let t = await this.getTokenInfo(e);
|
|
679
|
+
return t || await this.isNftToken(e) && (t = {
|
|
680
|
+
token: ie(e),
|
|
681
|
+
tokenIdHex: S(e),
|
|
682
|
+
decimals: 0
|
|
683
|
+
}), t;
|
|
684
|
+
}
|
|
685
|
+
async handleNftReceive(e, t, s) {
|
|
686
|
+
if (await this.walletDb.isNftExist(t)) {
|
|
687
|
+
const o = {
|
|
688
|
+
accountId: e,
|
|
689
|
+
tokenIdHex: t,
|
|
690
|
+
type: V.NFT,
|
|
691
|
+
addedTime: s
|
|
692
|
+
};
|
|
693
|
+
await this.walletDb.saveAsset(o, !0);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
const i = await this.rostrumService.getTokenGenesis(t);
|
|
697
|
+
if (i.op_return_id == I.NRC3) {
|
|
698
|
+
const o = R(t);
|
|
699
|
+
if (O.isSubgroup(o)) {
|
|
700
|
+
const r = new b(o.subarray(0, 32), k.defaultNetwork, E.GroupIdAddress).toString(), c = await this.rostrumService.getTokenGenesis(r);
|
|
701
|
+
c?.op_return_id == I.NRC2 && await this.saveNft(e, t, i.document_url ?? "", r, s, c?.name ?? "");
|
|
702
|
+
}
|
|
703
|
+
} else q(t) && await this.saveNft(e, t, "nifty", fe().token, s, "");
|
|
704
|
+
}
|
|
705
|
+
async saveNft(e, t, s, n, i, o) {
|
|
706
|
+
if (s)
|
|
707
|
+
try {
|
|
708
|
+
const r = {
|
|
709
|
+
accountId: e,
|
|
710
|
+
tokenIdHex: t,
|
|
711
|
+
type: V.NFT,
|
|
712
|
+
addedTime: i
|
|
713
|
+
}, c = {
|
|
714
|
+
parentGroup: n,
|
|
715
|
+
token: ie(t),
|
|
716
|
+
tokenIdHex: t,
|
|
717
|
+
source: s,
|
|
718
|
+
collection: o
|
|
719
|
+
};
|
|
720
|
+
await this.walletDb.saveNft(r, c);
|
|
721
|
+
} catch (r) {
|
|
722
|
+
console.error("failed to save NFT", r);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
async fetchAndSaveTokens(e, t) {
|
|
726
|
+
for (const s in t) {
|
|
727
|
+
const n = await this.getTokenInfo(s);
|
|
728
|
+
n && await this.walletDb.saveToken(e, n);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
async syncNfts(e, t) {
|
|
732
|
+
const s = Object.keys(t), n = await this.walletDb.getLocalNfts(e, 1, 1e3), i = async (r) => {
|
|
733
|
+
await this.walletDb.deleteNft(e, r, !0), this.walletCache.removeNft(r);
|
|
734
|
+
}, o = n?.filter((r) => !s.includes(r.tokenIdHex)) ?? [];
|
|
735
|
+
for (const r of o)
|
|
736
|
+
await i(r.tokenIdHex);
|
|
737
|
+
for (const [r, c] of Object.entries(t)) {
|
|
738
|
+
const d = S(r), u = c && BigInt(c.confirmed) + BigInt(c.unconfirmed) > 0n, l = n?.some((p) => p.tokenIdHex == d);
|
|
739
|
+
!u && !l || u && l || !await this.isNftToken(d) || (!u && l ? await i(d) : await this.handleNftReceive(e, d, $()));
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
class Ft {
|
|
744
|
+
rostrumService;
|
|
745
|
+
keyManager;
|
|
746
|
+
walletDb;
|
|
747
|
+
MAX_INPUTS_OUTPUTS = 250;
|
|
748
|
+
constructor(e, t, s) {
|
|
749
|
+
this.rostrumService = e, this.keyManager = t, this.walletDb = s;
|
|
750
|
+
}
|
|
751
|
+
async broadcastTransaction(e) {
|
|
752
|
+
return this.rostrumService.broadcast(e);
|
|
753
|
+
}
|
|
754
|
+
async fetchTransactionsHistory(e, t) {
|
|
755
|
+
let s = t;
|
|
756
|
+
const n = t > 0 ? t + 1 : 0, i = await this.rostrumService.getTransactionsHistory(e, n);
|
|
757
|
+
for (const o of i)
|
|
758
|
+
s = Math.max(s, o.height);
|
|
759
|
+
return { txs: i, lastHeight: s };
|
|
760
|
+
}
|
|
761
|
+
async fetchVaultTransactions(e) {
|
|
762
|
+
const t = await this.rostrumService.getTransactionsHistory(e, Ke), s = [];
|
|
763
|
+
for (const n of t) {
|
|
764
|
+
const i = this.classifyTransaction(n.tx_hash, [e]);
|
|
765
|
+
s.push(i);
|
|
766
|
+
}
|
|
767
|
+
return Promise.all(s);
|
|
768
|
+
}
|
|
769
|
+
async classifyAndSaveTransaction(e, t, s) {
|
|
770
|
+
const n = await this.classifyTransaction(t, s);
|
|
771
|
+
n.accountId = e;
|
|
772
|
+
const i = {
|
|
773
|
+
...n,
|
|
774
|
+
othersOutputs: JSON.stringify(n.othersOutputs),
|
|
775
|
+
myOutputs: JSON.stringify(n.myOutputs),
|
|
776
|
+
myInputs: JSON.stringify(n.myInputs)
|
|
777
|
+
}, o = /* @__PURE__ */ new Set();
|
|
778
|
+
n.myInputs.forEach((r) => r.assetId && o.add(r.assetId)), n.myOutputs.forEach((r) => r.assetId && o.add(r.assetId));
|
|
779
|
+
for (const r of o)
|
|
780
|
+
await this.walletDb.addAssetTransaction({
|
|
781
|
+
accountId: e,
|
|
782
|
+
assetId: r,
|
|
783
|
+
txIdem: n.txIdem,
|
|
784
|
+
time: n.time
|
|
785
|
+
});
|
|
786
|
+
await this.walletDb.addLocalTransaction(i);
|
|
787
|
+
}
|
|
788
|
+
async classifyTransaction(e, t) {
|
|
789
|
+
const s = await this.rostrumService.getTransaction(e), n = [], i = [], o = [], r = [];
|
|
790
|
+
for (const l of s.vin) {
|
|
791
|
+
const h = {
|
|
792
|
+
address: l.addresses[0],
|
|
793
|
+
nexaAmount: l.value_satoshi.toString(),
|
|
794
|
+
assetId: l.token_id_hex ?? "",
|
|
795
|
+
assetAmount: D(l.groupQuantity)
|
|
796
|
+
};
|
|
797
|
+
t.includes(h.address) ? n.push(h) : i.push(h);
|
|
798
|
+
}
|
|
799
|
+
for (const l of s.vout) {
|
|
800
|
+
if (C(l.scriptPubKey.addresses)) continue;
|
|
801
|
+
const h = {
|
|
802
|
+
address: l.scriptPubKey.addresses[0],
|
|
803
|
+
nexaAmount: l.value_satoshi.toString(),
|
|
804
|
+
assetId: l.scriptPubKey.token_id_hex ?? "",
|
|
805
|
+
assetAmount: D(l.scriptPubKey.groupQuantity)
|
|
806
|
+
};
|
|
807
|
+
t.includes(h.address) ? o.push(h) : r.push(h);
|
|
808
|
+
}
|
|
809
|
+
let c;
|
|
810
|
+
n.length === 0 ? c = "receive" : o.length === 0 ? c = "send" : i.length === 0 && r.length === 0 ? c = "self" : i.length === 0 && r.length > 0 ? c = "send" : i.length > 0 && r.length === 0 ? c = "receive" : c = "swap";
|
|
811
|
+
const d = s.height > 0;
|
|
812
|
+
return {
|
|
813
|
+
accountId: 0,
|
|
814
|
+
// Will be set in classifyAndSaveTransaction
|
|
815
|
+
txId: s.txid,
|
|
816
|
+
txIdem: s.txidem,
|
|
817
|
+
time: d ? s.time : $(),
|
|
818
|
+
height: d ? s.height : 0,
|
|
819
|
+
type: c,
|
|
820
|
+
fee: s.fee_satoshi.toString(),
|
|
821
|
+
othersOutputs: c == "send" || c == "swap" ? r : [],
|
|
822
|
+
myOutputs: o,
|
|
823
|
+
myInputs: n
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
async buildAndSignTransferTransaction(e, t, s, n, i, o, r, c) {
|
|
827
|
+
const d = {
|
|
828
|
+
feeFromAmount: i
|
|
829
|
+
}, u = this.prepareTransaction(t, n, o, c);
|
|
830
|
+
r && u.feePerByte(r);
|
|
831
|
+
let l = /* @__PURE__ */ new Map();
|
|
832
|
+
o && (l = await this.populateTokenInputsAndChange(u, e, s, o, BigInt(n)));
|
|
833
|
+
const h = await this.populateNexaInputsAndChange(u, e, s, d);
|
|
834
|
+
return l.forEach((p, P) => h.set(P, p)), await this.finalizeTransaction(u, Array.from(h.values()));
|
|
835
|
+
}
|
|
836
|
+
async buildAndSignConsolidateTransaction(e, t, s) {
|
|
837
|
+
const n = {
|
|
838
|
+
isConsolidate: !0,
|
|
839
|
+
templateData: s
|
|
840
|
+
}, i = new Q(), o = await this.populateNexaInputsAndChange(i, e, t, n);
|
|
841
|
+
return this.finalizeTransaction(i, Array.from(o.values()));
|
|
842
|
+
}
|
|
843
|
+
prepareTransaction(e, t, s, n) {
|
|
844
|
+
if (!F(e) && !F(e, E.PayToPublicKeyHash))
|
|
845
|
+
throw new Error("Invalid Address.");
|
|
846
|
+
if (s && BigInt(t) < 1n || !s && parseInt(t) < B.DUST_AMOUNT)
|
|
847
|
+
throw new Error("The amount is too low.");
|
|
848
|
+
if (s && BigInt(t) > ne || !s && parseInt(t) > B.MAX_MONEY)
|
|
849
|
+
throw new Error("The amount is too high.");
|
|
850
|
+
const i = new Q();
|
|
851
|
+
if (n && i.addData(n), s) {
|
|
852
|
+
if (!F(s, E.GroupIdAddress))
|
|
853
|
+
throw new Error("Invalid Token ID");
|
|
854
|
+
if (b.getOutputType(e) === 0)
|
|
855
|
+
throw new Error("Token must be sent to script template address");
|
|
856
|
+
i.to(e, B.DUST_AMOUNT, s, BigInt(t));
|
|
857
|
+
} else
|
|
858
|
+
i.to(e, t);
|
|
859
|
+
return i;
|
|
860
|
+
}
|
|
861
|
+
async populateNexaInputsAndChange(e, t, s, n) {
|
|
862
|
+
const i = t.filter((d) => BigInt(d.balance.confirmed) + BigInt(d.balance.unconfirmed) > 0n);
|
|
863
|
+
if (C(i))
|
|
864
|
+
throw new Error("Not enough Nexa balance.");
|
|
865
|
+
const o = /* @__PURE__ */ new Map(), r = n.isConsolidate ? 0 : Number(e.transaction.outputs.find((d) => d.value > 0n).value);
|
|
866
|
+
for (const d of i) {
|
|
867
|
+
const u = await this.rostrumService.getNexaUtxos(d.address);
|
|
868
|
+
for (const l of u) {
|
|
869
|
+
const h = {
|
|
870
|
+
outpoint: l.outpoint_hash,
|
|
871
|
+
address: d.address,
|
|
872
|
+
satoshis: l.value,
|
|
873
|
+
templateData: n.templateData
|
|
874
|
+
};
|
|
875
|
+
if (e.from(h), !o.has(d.address)) {
|
|
876
|
+
const p = this.keyManager.getKey(d.keyPath);
|
|
877
|
+
o.set(d.address, p.privateKey);
|
|
878
|
+
}
|
|
879
|
+
if (n.isConsolidate) {
|
|
880
|
+
if (e.change(s), e.transaction.inputs.length > this.MAX_INPUTS_OUTPUTS)
|
|
881
|
+
return o;
|
|
882
|
+
} else {
|
|
883
|
+
const p = e.transaction;
|
|
884
|
+
if (p.inputs.length > this.MAX_INPUTS_OUTPUTS)
|
|
885
|
+
throw new Error("Too many inputs. Consider consolidate transactions or reduce the send amount.");
|
|
886
|
+
const P = p.getUnspentValue();
|
|
887
|
+
if (P < 0n)
|
|
888
|
+
continue;
|
|
889
|
+
if (P == 0n && n.feeFromAmount) {
|
|
890
|
+
const H = p.estimateRequiredFee();
|
|
891
|
+
return p.updateOutputAmount(0, r - H), o;
|
|
892
|
+
}
|
|
893
|
+
if (e.change(s), n.feeFromAmount) {
|
|
894
|
+
const H = p.getChangeOutput();
|
|
895
|
+
let K = p.estimateRequiredFee();
|
|
896
|
+
p.updateOutputAmount(0, r - K), !H && p.getChangeOutput() && (K = p.estimateRequiredFee(), p.updateOutputAmount(0, r - K));
|
|
897
|
+
}
|
|
898
|
+
if (p.getUnspentValue() < p.estimateRequiredFee())
|
|
899
|
+
continue;
|
|
900
|
+
return o;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (n.isConsolidate) {
|
|
905
|
+
if (o.size > 0)
|
|
906
|
+
return o;
|
|
907
|
+
throw new Error("Not enough Nexa balance.");
|
|
908
|
+
}
|
|
909
|
+
const c = {
|
|
910
|
+
errorMsg: "Not enough Nexa balance.",
|
|
911
|
+
amount: ee.formatNEXA(e.transaction.outputs[0].value),
|
|
912
|
+
fee: ee.formatNEXA(e.transaction.estimateRequiredFee())
|
|
913
|
+
};
|
|
914
|
+
throw new Error(JSON.stringify(c));
|
|
915
|
+
}
|
|
916
|
+
async populateTokenInputsAndChange(e, t, s, n, i) {
|
|
917
|
+
const o = S(n), r = t.filter((u) => Object.keys(u.tokensBalance).includes(o));
|
|
918
|
+
if (C(r))
|
|
919
|
+
throw new Error("Not enough token balance.");
|
|
920
|
+
const c = /* @__PURE__ */ new Map();
|
|
921
|
+
let d = 0n;
|
|
922
|
+
for (const u of r) {
|
|
923
|
+
const l = await this.rostrumService.getTokenUtxos(u.address, n);
|
|
924
|
+
for (const h of l)
|
|
925
|
+
if (!(BigInt(h.token_amount) < 0n)) {
|
|
926
|
+
if (e.from({
|
|
927
|
+
outpoint: h.outpoint_hash,
|
|
928
|
+
address: u.address,
|
|
929
|
+
satoshis: h.value,
|
|
930
|
+
groupId: h.group,
|
|
931
|
+
groupAmount: BigInt(h.token_amount)
|
|
932
|
+
}), d = d + BigInt(h.token_amount), !c.has(u.address)) {
|
|
933
|
+
const p = this.keyManager.getKey(u.keyPath);
|
|
934
|
+
c.set(u.address, p.privateKey);
|
|
935
|
+
}
|
|
936
|
+
if (d > ne)
|
|
937
|
+
throw new Error("Token inputs exceeded max amount. Consider sending in small chunks");
|
|
938
|
+
if (e.transaction.inputs.length > this.MAX_INPUTS_OUTPUTS)
|
|
939
|
+
throw new Error("Too many inputs. Consider consolidating transactions or reduce the send amount.");
|
|
940
|
+
if (d == i)
|
|
941
|
+
return c;
|
|
942
|
+
if (d > i)
|
|
943
|
+
return e.to(s, B.DUST_AMOUNT, n, d - i), c;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
throw new Error("Not enough token balance");
|
|
947
|
+
}
|
|
948
|
+
async finalizeTransaction(e, t) {
|
|
949
|
+
const s = await this.rostrumService.getBlockTip();
|
|
950
|
+
return e.lockUntilBlockHeight(s.height).sign(t).build();
|
|
951
|
+
}
|
|
952
|
+
printTransactionJson(e) {
|
|
953
|
+
if (!e)
|
|
954
|
+
return "";
|
|
955
|
+
const t = {
|
|
956
|
+
...e.toObject(),
|
|
957
|
+
hex: e.toString()
|
|
958
|
+
};
|
|
959
|
+
return JSON.stringify(t, null, 2);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
function Gt(a = 12) {
|
|
963
|
+
return Ce(ue, a === 24 ? 256 : 128);
|
|
964
|
+
}
|
|
965
|
+
function Je(a) {
|
|
966
|
+
return Pe(a, ue);
|
|
967
|
+
}
|
|
968
|
+
async function pe(a, e) {
|
|
969
|
+
return De(Ve, a, e, { c: 6e5, dkLen: 32 });
|
|
970
|
+
}
|
|
971
|
+
async function qt(a, e) {
|
|
972
|
+
const t = y.getRandomBuffer(16), s = y.getRandomBuffer(12), n = y.utf8ToBuffer(a), i = await pe(e, t), o = de(i, s).encrypt(n), r = y.concat([t, s, o]);
|
|
973
|
+
return y.bufferToBase64(r);
|
|
974
|
+
}
|
|
975
|
+
async function ze(a, e) {
|
|
976
|
+
const t = y.base64ToBuffer(a), s = t.subarray(0, 16), n = t.subarray(16, 28), i = t.subarray(28), o = await pe(e, s), r = de(o, n).decrypt(i);
|
|
977
|
+
return y.bufferToUtf8(r);
|
|
978
|
+
}
|
|
979
|
+
async function Jt(a, e) {
|
|
980
|
+
try {
|
|
981
|
+
if (a) {
|
|
982
|
+
const t = await ze(a, e);
|
|
983
|
+
if (t && Je(t))
|
|
984
|
+
return t;
|
|
985
|
+
}
|
|
986
|
+
return !1;
|
|
987
|
+
} catch {
|
|
988
|
+
return !1;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const ge = {
|
|
992
|
+
usd: "$",
|
|
993
|
+
eur: "€",
|
|
994
|
+
gbp: "£",
|
|
995
|
+
cny: "¥",
|
|
996
|
+
jpy: "¥",
|
|
997
|
+
aud: "A$",
|
|
998
|
+
cad: "C$",
|
|
999
|
+
chf: "Fr"
|
|
1000
|
+
}, ye = Object.keys(ge);
|
|
1001
|
+
async function We() {
|
|
1002
|
+
const a = ye.join(","), e = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=nexacoin&include_24hr_change=true&vs_currencies=${a}`);
|
|
1003
|
+
if (!e.ok)
|
|
1004
|
+
throw new Error("Failed to fetch price");
|
|
1005
|
+
return (await e.json()).nexacoin || {};
|
|
1006
|
+
}
|
|
1007
|
+
function zt(a) {
|
|
1008
|
+
return ge[a] || a;
|
|
1009
|
+
}
|
|
1010
|
+
function me() {
|
|
1011
|
+
const a = {};
|
|
1012
|
+
return ye.forEach((e) => {
|
|
1013
|
+
a[e] = { value: 0, change: 0 };
|
|
1014
|
+
}), a;
|
|
1015
|
+
}
|
|
1016
|
+
function je() {
|
|
1017
|
+
return A.empty().add(v.OP_FROMALTSTACK).add(v.OP_DROP).add(v.OP_FROMALTSTACK).add(v.OP_CHECKLOCKTIMEVERIFY).add(v.OP_DROP).add(v.OP_FROMALTSTACK).add(v.OP_CHECKSIGVERIFY);
|
|
1018
|
+
}
|
|
1019
|
+
function $e() {
|
|
1020
|
+
const a = je();
|
|
1021
|
+
return U.sha256ripemd160(a.toBuffer());
|
|
1022
|
+
}
|
|
1023
|
+
function Xe(a) {
|
|
1024
|
+
return A.empty().add(a.toBuffer());
|
|
1025
|
+
}
|
|
1026
|
+
function Ye(a) {
|
|
1027
|
+
const e = Xe(a);
|
|
1028
|
+
return U.sha256ripemd160(e.toBuffer());
|
|
1029
|
+
}
|
|
1030
|
+
function Ze(a) {
|
|
1031
|
+
return a.map((e) => e <= 16 ? J.smallInt(e) : z.fromNumber(e).toScriptNumBuffer());
|
|
1032
|
+
}
|
|
1033
|
+
function Qe(a, e) {
|
|
1034
|
+
if (e.length !== 2)
|
|
1035
|
+
return;
|
|
1036
|
+
const t = $e(), s = Ye(a), n = Ze(e);
|
|
1037
|
+
return b.fromScriptTemplate(t, s, n).toString();
|
|
1038
|
+
}
|
|
1039
|
+
class Wt {
|
|
1040
|
+
walletDb;
|
|
1041
|
+
keyManager;
|
|
1042
|
+
providers;
|
|
1043
|
+
handlers;
|
|
1044
|
+
removeOnClose;
|
|
1045
|
+
updateCallback;
|
|
1046
|
+
constructor(e, t) {
|
|
1047
|
+
this.walletDb = e, this.keyManager = t, this.providers = /* @__PURE__ */ new Map(), this.handlers = /* @__PURE__ */ new Map(), this.removeOnClose = !0;
|
|
1048
|
+
}
|
|
1049
|
+
onUpdate(e) {
|
|
1050
|
+
this.updateCallback = e;
|
|
1051
|
+
}
|
|
1052
|
+
notify(e) {
|
|
1053
|
+
this.updateCallback?.(e);
|
|
1054
|
+
}
|
|
1055
|
+
getHandler(e) {
|
|
1056
|
+
return this.handlers.get(e);
|
|
1057
|
+
}
|
|
1058
|
+
add(e, t, s) {
|
|
1059
|
+
this.providers.has(e.id) || this.providers.set(e.id, /* @__PURE__ */ new Map());
|
|
1060
|
+
const n = s.details.sessionId, i = this.providers.get(e.id);
|
|
1061
|
+
this.registerHandlers(e, t, n), i.set(n, t), this.notify({ type: "session_added", accountId: e.id, sessionInfo: s });
|
|
1062
|
+
}
|
|
1063
|
+
remove(e, t) {
|
|
1064
|
+
const s = this.providers.get(e);
|
|
1065
|
+
if (s) {
|
|
1066
|
+
const n = s.get(t);
|
|
1067
|
+
n && (n.disconnect(), s.delete(t)), s.size === 0 && this.providers.delete(e);
|
|
1068
|
+
}
|
|
1069
|
+
this.notify({ type: "session_removed", accountId: e, sessionId: t });
|
|
1070
|
+
}
|
|
1071
|
+
async reload(e) {
|
|
1072
|
+
for (const t of e.values()) {
|
|
1073
|
+
if (t.id == g)
|
|
1074
|
+
continue;
|
|
1075
|
+
const s = await this.walletDb.getAccountSessions(t.id), n = this.providers.get(t.id), i = s.map(async (r) => {
|
|
1076
|
+
if (n?.has(r.sessionId))
|
|
1077
|
+
return;
|
|
1078
|
+
let c;
|
|
1079
|
+
try {
|
|
1080
|
+
c = new Ue(r.uri);
|
|
1081
|
+
const d = c.getSessionInfo();
|
|
1082
|
+
await c.connect(3e3);
|
|
1083
|
+
const u = await c.getAppInfo(2e3);
|
|
1084
|
+
await c.joinSession(t.address, 2e3);
|
|
1085
|
+
const l = await c.fetchPendingMessages();
|
|
1086
|
+
for (const h of l)
|
|
1087
|
+
this.notify({
|
|
1088
|
+
type: "new_notification",
|
|
1089
|
+
notification: {
|
|
1090
|
+
id: crypto.randomUUID(),
|
|
1091
|
+
createdAt: h.createdAt,
|
|
1092
|
+
type: "web3",
|
|
1093
|
+
title: "Request pending approval",
|
|
1094
|
+
message: `A connected dApp (${u.name}) has requested an action from your Account: ${t.name}. Review the request details before approving or rejecting.`,
|
|
1095
|
+
action: {
|
|
1096
|
+
type: "DAPP_REQUEST",
|
|
1097
|
+
account: t.id,
|
|
1098
|
+
sessionId: d.sessionId,
|
|
1099
|
+
payload: h.message
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
return { account: t, provider: c, metadata: { details: d, appInfo: u } };
|
|
1104
|
+
} catch (d) {
|
|
1105
|
+
console.error(`Failed to reload session ${r.sessionId}`, d), c?.disconnect(), await this.walletDb.removeSession(r.sessionId);
|
|
1106
|
+
}
|
|
1107
|
+
}), o = await Promise.allSettled(i);
|
|
1108
|
+
for (const r of o)
|
|
1109
|
+
r.status === "fulfilled" && r.value && this.add(r.value.account, r.value.provider, r.value.metadata);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
clear() {
|
|
1113
|
+
try {
|
|
1114
|
+
this.removeOnClose = !1;
|
|
1115
|
+
for (const [e, t] of this.providers) {
|
|
1116
|
+
for (const [, s] of t)
|
|
1117
|
+
s.disconnect();
|
|
1118
|
+
t.clear(), this.notify({ type: "sessions_cleared", accountId: e });
|
|
1119
|
+
}
|
|
1120
|
+
this.providers.clear();
|
|
1121
|
+
} finally {
|
|
1122
|
+
this.removeOnClose = !0;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
async revoke(e, t) {
|
|
1126
|
+
const s = this.providers.get(e);
|
|
1127
|
+
if (s) {
|
|
1128
|
+
const n = s.get(t);
|
|
1129
|
+
n && await n.revokeSession();
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
async replayMessage(e, t, s) {
|
|
1133
|
+
const n = this.providers.get(e);
|
|
1134
|
+
if (n) {
|
|
1135
|
+
const i = n.get(t);
|
|
1136
|
+
i && await i.replayMessage(s);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
registerHandlers(e, t, s) {
|
|
1140
|
+
t.onSessionDelete(() => this.walletDb.removeSession(s)), t.onClose(() => {
|
|
1141
|
+
this.removeOnClose && this.remove(e.id, s);
|
|
1142
|
+
});
|
|
1143
|
+
const n = (i, o) => new Promise((r, c) => {
|
|
1144
|
+
this.handlers.set(i, { resolve: r, reject: c }), this.notify({ type: "new_request", request: { type: i, accountId: e.id, sessionId: s, request: o } });
|
|
1145
|
+
});
|
|
1146
|
+
t.onSignMessage((i) => n(_.SignMessage, i)), t.onAddToken((i) => n(_.AddToken, i)), t.onSendTransaction((i) => n(_.SendTransaction, i)), t.onSignTransaction((i) => n(_.SignTransaction, i)), t.onGetAccount(() => {
|
|
1147
|
+
const i = { account: f.DAPP, type: m.RECEIVE, index: e.id };
|
|
1148
|
+
return {
|
|
1149
|
+
name: e.name,
|
|
1150
|
+
address: e.address,
|
|
1151
|
+
pubkey: this.keyManager.getKey(i).publicKey.toString(),
|
|
1152
|
+
blockchain: "nexa",
|
|
1153
|
+
network: k.defaultNetwork.name,
|
|
1154
|
+
capabilities: {
|
|
1155
|
+
addToken: !0,
|
|
1156
|
+
sendTransaction: !0,
|
|
1157
|
+
signMessage: !0,
|
|
1158
|
+
signTransaction: !0
|
|
1159
|
+
}
|
|
1160
|
+
};
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
class jt {
|
|
1165
|
+
rostrumService;
|
|
1166
|
+
keyManager;
|
|
1167
|
+
assetService;
|
|
1168
|
+
constructor(e, t, s) {
|
|
1169
|
+
this.rostrumService = e, this.keyManager = t, this.assetService = s;
|
|
1170
|
+
}
|
|
1171
|
+
async transformRawTransaction(e, t) {
|
|
1172
|
+
const s = this.createContext(e, t);
|
|
1173
|
+
return await this.processInputs(s), this.processOutputs(s), this.signInputs(s), await this.processAssetMovements(s), s.txDetails;
|
|
1174
|
+
}
|
|
1175
|
+
createContext(e, t) {
|
|
1176
|
+
try {
|
|
1177
|
+
return {
|
|
1178
|
+
address: t.address,
|
|
1179
|
+
privateKey: this.keyManager.getKey(t.keyPath).privateKey,
|
|
1180
|
+
txDetails: {
|
|
1181
|
+
tx: new B(e),
|
|
1182
|
+
send: [],
|
|
1183
|
+
receive: []
|
|
1184
|
+
},
|
|
1185
|
+
myInputs: /* @__PURE__ */ new Map(),
|
|
1186
|
+
myOutputs: /* @__PURE__ */ new Map(),
|
|
1187
|
+
involvedAssets: /* @__PURE__ */ new Set(),
|
|
1188
|
+
inputsToSign: /* @__PURE__ */ new Set(),
|
|
1189
|
+
coveredOutputs: /* @__PURE__ */ new Set(),
|
|
1190
|
+
allMyOutputsCovered: !1
|
|
1191
|
+
};
|
|
1192
|
+
} catch {
|
|
1193
|
+
throw new Error("Invalid transaction format.");
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
async processInputs(e) {
|
|
1197
|
+
const t = [];
|
|
1198
|
+
for (let s = 0; s < e.txDetails.tx.inputs.length; s++) {
|
|
1199
|
+
const n = this.processInput(e, s);
|
|
1200
|
+
t.push(n);
|
|
1201
|
+
}
|
|
1202
|
+
await Promise.all(t);
|
|
1203
|
+
}
|
|
1204
|
+
async processInput(e, t) {
|
|
1205
|
+
const s = e.txDetails.tx.inputs[t], n = await this.rostrumService.getUtxo(y.bufferToHex(s.outpoint));
|
|
1206
|
+
if (n.status == "spent")
|
|
1207
|
+
throw new Error("Input UTXO is already spent.");
|
|
1208
|
+
if (s.output = new xe(n.amount, n.scriptpubkey), s.output.address == e.address) {
|
|
1209
|
+
const i = n.token_id_hex || "NEXA";
|
|
1210
|
+
e.myInputs.set(t, {
|
|
1211
|
+
address: e.address,
|
|
1212
|
+
nexaAmount: n.amount.toString(),
|
|
1213
|
+
assetId: i,
|
|
1214
|
+
assetAmount: D(n.group_quantity) || n.amount.toString()
|
|
1215
|
+
}), e.involvedAssets.add(i), s.scriptSig.isEmpty() && e.inputsToSign.add(t);
|
|
1216
|
+
}
|
|
1217
|
+
s.scriptSig.findPlaceholder() > 0 && e.inputsToSign.add(t);
|
|
1218
|
+
}
|
|
1219
|
+
processOutputs(e) {
|
|
1220
|
+
for (let t = 0; t < e.txDetails.tx.outputs.length; t++) {
|
|
1221
|
+
const s = e.txDetails.tx.outputs[t].toObject();
|
|
1222
|
+
if (s.address !== e.address)
|
|
1223
|
+
continue;
|
|
1224
|
+
const n = s.groupId ? S(s.groupId) : "NEXA", i = {
|
|
1225
|
+
address: s.address,
|
|
1226
|
+
nexaAmount: s.value.toString(),
|
|
1227
|
+
assetId: n,
|
|
1228
|
+
assetAmount: D(s.groupAmount) || s.value.toString()
|
|
1229
|
+
};
|
|
1230
|
+
e.involvedAssets.add(n), e.myOutputs.set(t, i);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
signInputs(e) {
|
|
1234
|
+
if (e.inputsToSign.size == 0)
|
|
1235
|
+
throw new Error("No inputs to sign.");
|
|
1236
|
+
for (const t of e.inputsToSign) {
|
|
1237
|
+
const s = e.txDetails.tx.inputs[t], n = this.validateAndGetSubscript(s);
|
|
1238
|
+
if (s.scriptSig.isEmpty()) {
|
|
1239
|
+
e.allMyOutputsCovered = !0;
|
|
1240
|
+
const i = te.sign(e.txDetails.tx, t, se.ALL, n, e.privateKey).toTxFormat(), o = A.empty().add(e.privateKey.publicKey.toBuffer());
|
|
1241
|
+
s.scriptSig = Ne.buildScriptTemplateIn(void 0, o, A.empty().add(i));
|
|
1242
|
+
} else {
|
|
1243
|
+
const i = s.scriptSig.findPlaceholder(), r = s.scriptSig.chunks[i].buf.subarray(64), c = se.fromBuffer(r);
|
|
1244
|
+
this.processSignatureCoverage(e, c);
|
|
1245
|
+
const d = te.sign(e.txDetails.tx, t, c, n, e.privateKey).toTxFormat(r);
|
|
1246
|
+
s.scriptSig.replaceChunk(i, A.empty().add(d));
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
if (!e.allMyOutputsCovered)
|
|
1250
|
+
throw new Error("Some of interested outputs are not covered by signatures.");
|
|
1251
|
+
}
|
|
1252
|
+
validateAndGetSubscript(e) {
|
|
1253
|
+
if (e.output.scriptPubKey.isPublicKeyTemplateOut()) {
|
|
1254
|
+
if (e.scriptSig.isEmpty() || e.scriptSig.isPublicKeyTemplateIn())
|
|
1255
|
+
return A.empty().add(v.OP_FROMALTSTACK).add(v.OP_CHECKSIGVERIFY);
|
|
1256
|
+
throw new Error("Invalid input script type.");
|
|
1257
|
+
}
|
|
1258
|
+
if (e.output.scriptPubKey.isScriptTemplateOut()) {
|
|
1259
|
+
if (!e.scriptSig.isScriptTemplateIn())
|
|
1260
|
+
throw new Error("Unsupported input script type.");
|
|
1261
|
+
const t = e.output.scriptPubKey.getTemplateHash(), s = e.scriptSig.chunks[0].buf;
|
|
1262
|
+
if (y.equals(t, U.sha256ripemd160(s)) || y.equals(t, U.sha256sha256(s)))
|
|
1263
|
+
return A.fromBuffer(s);
|
|
1264
|
+
throw new Error("Invalid input script template.");
|
|
1265
|
+
}
|
|
1266
|
+
throw new Error("Unsupported prevout script type.");
|
|
1267
|
+
}
|
|
1268
|
+
processSignatureCoverage(e, t) {
|
|
1269
|
+
if (!e.allMyOutputsCovered)
|
|
1270
|
+
if (t.outType == ae.TWO)
|
|
1271
|
+
e.coveredOutputs.add(t.outData[0]), e.coveredOutputs.add(t.outData[1]), this.checkSignatureCoverage(e);
|
|
1272
|
+
else if (t.outType == ae.FIRSTN) {
|
|
1273
|
+
const s = t.outData[0];
|
|
1274
|
+
for (let n = 0; n < s; n++)
|
|
1275
|
+
e.coveredOutputs.add(n);
|
|
1276
|
+
this.checkSignatureCoverage(e);
|
|
1277
|
+
} else
|
|
1278
|
+
e.allMyOutputsCovered = !0;
|
|
1279
|
+
}
|
|
1280
|
+
checkSignatureCoverage(e) {
|
|
1281
|
+
if (!e.allMyOutputsCovered) {
|
|
1282
|
+
for (const t of e.myOutputs.keys())
|
|
1283
|
+
if (!e.coveredOutputs.has(t))
|
|
1284
|
+
return;
|
|
1285
|
+
e.allMyOutputsCovered = !0;
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
async processAssetMovements(e) {
|
|
1289
|
+
const t = Array.from(e.myInputs.values()), s = Array.from(e.myOutputs.values()), n = [];
|
|
1290
|
+
for (const i of e.involvedAssets) {
|
|
1291
|
+
const o = this.processAssetMovement(e, i, t, s);
|
|
1292
|
+
n.push(o);
|
|
1293
|
+
}
|
|
1294
|
+
await Promise.all(n);
|
|
1295
|
+
}
|
|
1296
|
+
async processAssetMovement(e, t, s, n) {
|
|
1297
|
+
const i = s.reduce((r, c) => c.assetId === t ? r + BigInt(c.assetAmount) : r, 0n), o = n.reduce((r, c) => c.assetId === t ? r + BigInt(c.assetAmount) : r, 0n);
|
|
1298
|
+
if (i > 0n || o > 0n) {
|
|
1299
|
+
const r = { amount: o - i };
|
|
1300
|
+
t !== "NEXA" && (r.asset = await this.assetService.getAssetInfo(t)), r.amount > 0n ? e.txDetails.receive.push(r) : r.amount < 0n && e.txDetails.send.push(r);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
const et = 274710, tt = "0014461ad25081cb0119d034385ff154c8d3ad6bdd76";
|
|
1305
|
+
class st {
|
|
1306
|
+
rostrumService;
|
|
1307
|
+
keyManager;
|
|
1308
|
+
constructor(e, t) {
|
|
1309
|
+
this.rostrumService = e, this.keyManager = t;
|
|
1310
|
+
}
|
|
1311
|
+
async discoverWalletIndex(e, t) {
|
|
1312
|
+
let s = 0, n = !1, i = 0;
|
|
1313
|
+
do {
|
|
1314
|
+
n = !0;
|
|
1315
|
+
for (let o = i; o < i + 20; o++) {
|
|
1316
|
+
const r = { account: e, type: t, index: o }, c = this.keyManager.getKey(r).privateKey.toAddress().toString();
|
|
1317
|
+
await this.rostrumService.isAddressUsed(c) && (s = o, n = !1);
|
|
1318
|
+
}
|
|
1319
|
+
i += 20;
|
|
1320
|
+
} while (!n);
|
|
1321
|
+
return s;
|
|
1322
|
+
}
|
|
1323
|
+
async discoverVaults(e) {
|
|
1324
|
+
const t = [];
|
|
1325
|
+
for (const i of e) {
|
|
1326
|
+
const o = this.checkVaultsForAddress(i);
|
|
1327
|
+
t.push(o);
|
|
1328
|
+
}
|
|
1329
|
+
const s = await Promise.all(t), n = /* @__PURE__ */ new Map();
|
|
1330
|
+
return s.forEach((i) => {
|
|
1331
|
+
i.forEach((o) => {
|
|
1332
|
+
const r = this.parseVaultDetails(o);
|
|
1333
|
+
r && n.set(r.address, r);
|
|
1334
|
+
});
|
|
1335
|
+
}), n;
|
|
1336
|
+
}
|
|
1337
|
+
async checkVaultsForAddress(e) {
|
|
1338
|
+
const t = /* @__PURE__ */ new Set(), s = await this.rostrumService.getTransactionsHistory(e, et);
|
|
1339
|
+
for (const n of s) {
|
|
1340
|
+
const o = (await this.rostrumService.getTransaction(n.tx_hash)).vout.filter((r) => r.scriptPubKey.hex.startsWith(tt));
|
|
1341
|
+
for (const r of o)
|
|
1342
|
+
t.add(r.scriptPubKey.hex);
|
|
1343
|
+
}
|
|
1344
|
+
return t;
|
|
1345
|
+
}
|
|
1346
|
+
parseVaultDetails(e) {
|
|
1347
|
+
const t = A.fromHex(e), s = new Ee().writeVarLengthBuf(t.toBuffer()).toBuffer(), n = new b(s).toString(), i = t.getVisibleArgs(), o = z.fromScriptNumBuffer(i.chunks[0].buf).toNumber(), r = J.isSmallIntOp(i.chunks[1].opcodenum) ? J.decodeOP_N(i.chunks[1].opcodenum) : z.fromScriptNumBuffer(i.chunks[1].buf).toNumber(), c = [o, r], d = this.keyManager.getKey({ account: f.VAULT, type: m.RECEIVE, index: r }), u = Qe(d.publicKey, c);
|
|
1348
|
+
return n != u ? void 0 : {
|
|
1349
|
+
address: n,
|
|
1350
|
+
block: o,
|
|
1351
|
+
idx: r,
|
|
1352
|
+
statusHash: "",
|
|
1353
|
+
balance: { confirmed: "0", unconfirmed: "0" },
|
|
1354
|
+
tokensBalance: {},
|
|
1355
|
+
height: 0,
|
|
1356
|
+
type: f.VAULT
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
class N {
|
|
1361
|
+
constructor(e, t, s, n, i, o, r) {
|
|
1362
|
+
this.keyManager = e, this.kvStore = t, this.walletDb = s, this.rostrumService = n, this.assetService = i, this.transactionService = o, this.sessionManager = r, this.discoveryService = new st(this.rostrumService, this.keyManager), this.accounts = /* @__PURE__ */ new Map(), this.accountsAddressToId = /* @__PURE__ */ new Map(), this.receiveAddresses = [], this.changeAddresses = [], this.vaults = /* @__PURE__ */ new Map(), this.pendingUpdates = /* @__PURE__ */ new Map(), this.addressResolvers = /* @__PURE__ */ new Map();
|
|
1363
|
+
}
|
|
1364
|
+
static GAP_LIMIT = 20;
|
|
1365
|
+
static DEBOUNCE_MS = 1e3;
|
|
1366
|
+
addressResolvers;
|
|
1367
|
+
accounts;
|
|
1368
|
+
accountsAddressToId;
|
|
1369
|
+
receiveAddresses;
|
|
1370
|
+
changeAddresses;
|
|
1371
|
+
vaults;
|
|
1372
|
+
pendingUpdates;
|
|
1373
|
+
updateTimer;
|
|
1374
|
+
updateCallback;
|
|
1375
|
+
discoveryService;
|
|
1376
|
+
onUpdate(e) {
|
|
1377
|
+
this.updateCallback = e;
|
|
1378
|
+
}
|
|
1379
|
+
notify(e) {
|
|
1380
|
+
this.updateCallback?.(e);
|
|
1381
|
+
}
|
|
1382
|
+
getAllAddresses() {
|
|
1383
|
+
return [...this.receiveAddresses, ...this.changeAddresses, ...this.accounts.values(), ...this.vaults.values()];
|
|
1384
|
+
}
|
|
1385
|
+
getMainAddresses() {
|
|
1386
|
+
return [...this.receiveAddresses, ...this.changeAddresses];
|
|
1387
|
+
}
|
|
1388
|
+
getReceiveAddress() {
|
|
1389
|
+
return this.receiveAddresses.find((e) => !e.used).address;
|
|
1390
|
+
}
|
|
1391
|
+
getChangeAddress(e = g) {
|
|
1392
|
+
return e != g ? this.accounts.get(e).address : this.changeAddresses.find((t) => !t.used).address;
|
|
1393
|
+
}
|
|
1394
|
+
getUsedAddressKeys(e = g) {
|
|
1395
|
+
if (e != g) {
|
|
1396
|
+
const t = this.accounts.get(e);
|
|
1397
|
+
return [{
|
|
1398
|
+
address: t.address,
|
|
1399
|
+
keyPath: x(f.DAPP, 0, t.id),
|
|
1400
|
+
balance: t.balance,
|
|
1401
|
+
tokensBalance: t.tokensBalance
|
|
1402
|
+
}];
|
|
1403
|
+
}
|
|
1404
|
+
return this.getMainAddresses().filter((t) => t.used).map((t) => ({
|
|
1405
|
+
address: t.address,
|
|
1406
|
+
keyPath: x(f.MAIN, t.space, t.idx),
|
|
1407
|
+
balance: t.balance,
|
|
1408
|
+
tokensBalance: t.tokensBalance
|
|
1409
|
+
}));
|
|
1410
|
+
}
|
|
1411
|
+
async reloadRostrum(e) {
|
|
1412
|
+
await this.kvStore.saveRostrumParams(e), await this.rostrumService.disconnect(!0), await this.initRostrum(), await this.subscribeAddresses(this.getAllAddresses());
|
|
1413
|
+
}
|
|
1414
|
+
async initRostrum() {
|
|
1415
|
+
await this.rostrumService.connect(), await this.rostrumService.subscribeHeaders((e) => this.notify({ type: "new_tip", tip: e }));
|
|
1416
|
+
}
|
|
1417
|
+
async initialize() {
|
|
1418
|
+
await this.initRostrum(), await this.walletDb.countAddresses() ? await this.loadWallet() : await this.discoverWallet();
|
|
1419
|
+
}
|
|
1420
|
+
async discoverWallet() {
|
|
1421
|
+
this.notify({ type: "discover_wallet", loading: !0 });
|
|
1422
|
+
const e = this.discoveryService.discoverWalletIndex(f.MAIN, m.RECEIVE), t = this.discoveryService.discoverWalletIndex(f.MAIN, m.CHANGE), s = this.discoveryService.discoverWalletIndex(f.DAPP, m.RECEIVE), [n, i, o] = await Promise.all([e, t, s]);
|
|
1423
|
+
this.receiveAddresses = this.deriveAddresses(m.RECEIVE, 0, n + N.GAP_LIMIT), this.changeAddresses = this.deriveAddresses(m.CHANGE, 0, i + N.GAP_LIMIT), this.accounts = this.deriveAccounts(0, o + 1), this.accounts.forEach((r) => this.accountsAddressToId.set(r.address, r.id)), await this.saveAddresses(this.receiveAddresses), await this.saveAddresses(this.changeAddresses), await this.saveAccounts(this.accounts), this.initState(), await this.initialSync(), this.notify({ type: "discover_wallet", loading: !1 }), this.notify({ type: "load_wallet", loading: !1 }), await this.rescanVaults();
|
|
1424
|
+
}
|
|
1425
|
+
async loadWallet() {
|
|
1426
|
+
this.receiveAddresses = await this.walletDb.getReceiveAddresses(), this.changeAddresses = await this.walletDb.getChangeAddresses();
|
|
1427
|
+
const e = await this.walletDb.getAccounts();
|
|
1428
|
+
this.accounts = new Map(e.map((s) => [s.id, s])), this.accountsAddressToId = new Map(e.map((s) => [s.address, s.id]));
|
|
1429
|
+
const t = await this.walletDb.getVaults();
|
|
1430
|
+
this.vaults = new Map(t.map((s) => [s.address, s])), this.initState(), this.notify({ type: "load_wallet", loading: !1 }), await this.reconnectSync();
|
|
1431
|
+
}
|
|
1432
|
+
async initialSync() {
|
|
1433
|
+
await this.subscribeAddresses(this.getAllAddresses(), !0);
|
|
1434
|
+
const e = [];
|
|
1435
|
+
e.push(this.assetService.fetchAndSaveTokens(g, G(this.getMainAddresses().map((t) => t.tokensBalance))));
|
|
1436
|
+
for (const t of this.accounts.values())
|
|
1437
|
+
e.push(this.assetService.fetchAndSaveTokens(t.id, t.tokensBalance));
|
|
1438
|
+
await Promise.all(e);
|
|
1439
|
+
}
|
|
1440
|
+
async reconnectSync() {
|
|
1441
|
+
await Promise.all([
|
|
1442
|
+
this.sessionManager.reload(this.accounts),
|
|
1443
|
+
this.subscribeAddresses(this.getAllAddresses())
|
|
1444
|
+
]);
|
|
1445
|
+
}
|
|
1446
|
+
initState() {
|
|
1447
|
+
const e = this.getMainAddresses();
|
|
1448
|
+
this.notify({
|
|
1449
|
+
type: "new_account",
|
|
1450
|
+
account: {
|
|
1451
|
+
id: g,
|
|
1452
|
+
name: "Main Wallet",
|
|
1453
|
+
address: this.getReceiveAddress(),
|
|
1454
|
+
balance: re(e.map((t) => t.balance)),
|
|
1455
|
+
tokensBalance: G(e.map((t) => t.tokensBalance)),
|
|
1456
|
+
tokens: [],
|
|
1457
|
+
sessions: {}
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
for (const t of this.accounts.values())
|
|
1461
|
+
this.notify({
|
|
1462
|
+
type: "new_account",
|
|
1463
|
+
account: {
|
|
1464
|
+
id: t.id,
|
|
1465
|
+
name: t.name,
|
|
1466
|
+
address: t.address,
|
|
1467
|
+
balance: t.balance,
|
|
1468
|
+
tokensBalance: t.tokensBalance,
|
|
1469
|
+
tokens: [],
|
|
1470
|
+
sessions: {}
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
for (const t of this.vaults.values())
|
|
1474
|
+
this.notify({
|
|
1475
|
+
type: "new_vault",
|
|
1476
|
+
vault: {
|
|
1477
|
+
address: t.address,
|
|
1478
|
+
balance: t.balance,
|
|
1479
|
+
block: t.block,
|
|
1480
|
+
index: t.idx
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
deriveAddresses(e, t, s) {
|
|
1485
|
+
const n = [];
|
|
1486
|
+
for (let i = t; i < t + s; i++) {
|
|
1487
|
+
const o = x(f.MAIN, e, i), c = {
|
|
1488
|
+
address: this.keyManager.getKey(o).privateKey.toAddress().toString(),
|
|
1489
|
+
space: e,
|
|
1490
|
+
idx: i,
|
|
1491
|
+
used: !1,
|
|
1492
|
+
height: 0,
|
|
1493
|
+
statusHash: "",
|
|
1494
|
+
balance: { confirmed: "0", unconfirmed: "0" },
|
|
1495
|
+
tokensBalance: {},
|
|
1496
|
+
type: f.MAIN
|
|
1497
|
+
};
|
|
1498
|
+
n.push(c);
|
|
1499
|
+
}
|
|
1500
|
+
return n;
|
|
1501
|
+
}
|
|
1502
|
+
deriveAccounts(e, t) {
|
|
1503
|
+
const s = /* @__PURE__ */ new Map();
|
|
1504
|
+
for (let n = e; n < e + t; n++) {
|
|
1505
|
+
const i = x(f.DAPP, 0, n), o = this.keyManager.getKey(i).privateKey.toAddress().toString(), r = {
|
|
1506
|
+
id: n,
|
|
1507
|
+
name: `Account ${n + 1}`,
|
|
1508
|
+
address: o,
|
|
1509
|
+
height: 0,
|
|
1510
|
+
hidden: 0,
|
|
1511
|
+
statusHash: "",
|
|
1512
|
+
balance: { confirmed: "0", unconfirmed: "0" },
|
|
1513
|
+
tokensBalance: {},
|
|
1514
|
+
type: f.DAPP
|
|
1515
|
+
};
|
|
1516
|
+
s.set(r.id, r);
|
|
1517
|
+
}
|
|
1518
|
+
return s;
|
|
1519
|
+
}
|
|
1520
|
+
async saveAddresses(e) {
|
|
1521
|
+
for (const t of e)
|
|
1522
|
+
await this.walletDb.saveAddress(t);
|
|
1523
|
+
}
|
|
1524
|
+
async saveAccounts(e) {
|
|
1525
|
+
for (const t of e.values())
|
|
1526
|
+
await this.walletDb.saveAccount(t);
|
|
1527
|
+
}
|
|
1528
|
+
async subscribeAddresses(e, t = !1) {
|
|
1529
|
+
const s = e.map(async (o) => {
|
|
1530
|
+
const r = await this.rostrumService.subscribeAddress(o.address, this.onSubscribeEvent);
|
|
1531
|
+
return { addr: o, result: r };
|
|
1532
|
+
}), n = [], i = await Promise.all(s);
|
|
1533
|
+
for (const { addr: o, result: r } of i)
|
|
1534
|
+
if (r && typeof r == "string" && o.statusHash != r) {
|
|
1535
|
+
if (t) {
|
|
1536
|
+
const c = new Promise((d) => {
|
|
1537
|
+
this.addressResolvers.set(o.address, d);
|
|
1538
|
+
});
|
|
1539
|
+
n.push(c);
|
|
1540
|
+
}
|
|
1541
|
+
this.registerUpdate({ address: o, result: r });
|
|
1542
|
+
}
|
|
1543
|
+
await Promise.all(n);
|
|
1544
|
+
}
|
|
1545
|
+
onSubscribeEvent = (e) => {
|
|
1546
|
+
if (!Array.isArray(e) || e.length < 2)
|
|
1547
|
+
return;
|
|
1548
|
+
const [t, s] = e, i = this.getAllAddresses().find((o) => o.address === t);
|
|
1549
|
+
i && i.statusHash !== s && this.registerUpdate({ address: i, result: s });
|
|
1550
|
+
};
|
|
1551
|
+
registerUpdate(e) {
|
|
1552
|
+
this.pendingUpdates.set(e.address.address, e), clearTimeout(this.updateTimer), this.updateTimer = setTimeout(() => {
|
|
1553
|
+
this.processPendingUpdates();
|
|
1554
|
+
}, N.DEBOUNCE_MS);
|
|
1555
|
+
}
|
|
1556
|
+
async processPendingUpdates() {
|
|
1557
|
+
if (this.pendingUpdates.size !== 0)
|
|
1558
|
+
try {
|
|
1559
|
+
this.notify({ type: "sync_wallet", loading: !0 }), console.log(`Processing ${this.pendingUpdates.size} pending updates...`);
|
|
1560
|
+
const e = Array.from(this.pendingUpdates);
|
|
1561
|
+
this.pendingUpdates.clear();
|
|
1562
|
+
const t = [], s = [], n = [];
|
|
1563
|
+
for (const [, r] of e)
|
|
1564
|
+
if (this.isAccountAddress(r.address)) {
|
|
1565
|
+
const c = this.fetchAndUpdateAccount(r.address, r.result);
|
|
1566
|
+
t.push(c);
|
|
1567
|
+
} else if (this.isVaultAddress(r.address)) {
|
|
1568
|
+
const c = this.fetchAndUpdateVault(r.address, r.result);
|
|
1569
|
+
s.push(c);
|
|
1570
|
+
} else {
|
|
1571
|
+
const c = this.fetchAndUpdateAddress(r.address, r.result);
|
|
1572
|
+
n.push(c);
|
|
1573
|
+
}
|
|
1574
|
+
const i = await Promise.all(t);
|
|
1575
|
+
await Promise.all(s);
|
|
1576
|
+
const o = await Promise.all(n);
|
|
1577
|
+
if (this.addressResolvers.size > 0)
|
|
1578
|
+
for (const [r] of e) {
|
|
1579
|
+
const c = this.addressResolvers.get(r);
|
|
1580
|
+
c && (c(), this.addressResolvers.delete(r));
|
|
1581
|
+
}
|
|
1582
|
+
await Promise.all([this.postProcessWalletUpdate(o), this.postProcessAccountUpdate(i)]);
|
|
1583
|
+
} catch (e) {
|
|
1584
|
+
console.error("Error processing pending updates:", e);
|
|
1585
|
+
} finally {
|
|
1586
|
+
this.notify({ type: "sync_wallet", loading: !1 });
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
async postProcessWalletUpdate(e) {
|
|
1590
|
+
if (e.length === 0)
|
|
1591
|
+
return;
|
|
1592
|
+
const t = /* @__PURE__ */ new Map();
|
|
1593
|
+
for (const { txs: r } of e)
|
|
1594
|
+
for (const c of r)
|
|
1595
|
+
t.set(c.tx_hash, c);
|
|
1596
|
+
await this.checkGapLimit(m.RECEIVE), await this.checkGapLimit(m.CHANGE);
|
|
1597
|
+
const s = this.getMainAddresses(), n = re(s.map((r) => r.balance)), i = G(s.map((r) => r.tokensBalance));
|
|
1598
|
+
this.notify({ type: "account_balance_updated", accountId: g, balance: n, tokensBalance: i }), this.notify({ type: "main_address_updated", address: this.getReceiveAddress() });
|
|
1599
|
+
const o = this.assetService.syncNfts(g, i);
|
|
1600
|
+
for (const r of t.values())
|
|
1601
|
+
await this.transactionService.classifyAndSaveTransaction(g, r.tx_hash, s.map((c) => c.address));
|
|
1602
|
+
await o;
|
|
1603
|
+
}
|
|
1604
|
+
async postProcessAccountUpdate(e) {
|
|
1605
|
+
if (e.length !== 0)
|
|
1606
|
+
for (const { address: t, txs: s } of e) {
|
|
1607
|
+
const n = t;
|
|
1608
|
+
this.notify({ type: "account_balance_updated", accountId: n.id, balance: n.balance, tokensBalance: n.tokensBalance });
|
|
1609
|
+
const i = this.assetService.syncNfts(n.id, n.tokensBalance);
|
|
1610
|
+
for (const o of s)
|
|
1611
|
+
await this.transactionService.classifyAndSaveTransaction(n.id, o.tx_hash, [n.address]);
|
|
1612
|
+
await i;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
async fetchAndUpdateAddress(e, t) {
|
|
1616
|
+
e.statusHash = t, e.balance = await this.rostrumService.getBalance(e.address), e.tokensBalance = await this.rostrumService.getTokensBalance(e.address);
|
|
1617
|
+
const s = await this.transactionService.fetchTransactionsHistory(e.address, e.height);
|
|
1618
|
+
return e.height = s.lastHeight, e.used || (e.used = s.txs.length > 0), await this.walletDb.saveAddress(e), { address: e, txs: s.txs };
|
|
1619
|
+
}
|
|
1620
|
+
async fetchAndUpdateAccount(e, t) {
|
|
1621
|
+
e.statusHash = t, e.balance = await this.rostrumService.getBalance(e.address), e.tokensBalance = await this.rostrumService.getTokensBalance(e.address);
|
|
1622
|
+
const s = await this.transactionService.fetchTransactionsHistory(e.address, e.height);
|
|
1623
|
+
return e.height = s.lastHeight, await this.walletDb.saveAccount(e), { address: e, txs: s.txs };
|
|
1624
|
+
}
|
|
1625
|
+
async fetchAndUpdateVault(e, t) {
|
|
1626
|
+
e.statusHash = t, e.balance = await this.rostrumService.getBalance(e.address);
|
|
1627
|
+
const s = await this.transactionService.fetchTransactionsHistory(e.address, e.height);
|
|
1628
|
+
return e.height = s.lastHeight, await this.walletDb.saveVault(e), this.notify({ type: "vault_balance_updated", address: e.address, balance: e.balance }), { address: e, txs: s.txs };
|
|
1629
|
+
}
|
|
1630
|
+
async checkGapLimit(e) {
|
|
1631
|
+
const t = e === m.RECEIVE ? this.receiveAddresses : this.changeAddresses, s = e === m.RECEIVE ? "Receive" : "Change", n = t.filter((i) => !i.used).length;
|
|
1632
|
+
if (n < N.GAP_LIMIT) {
|
|
1633
|
+
const i = N.GAP_LIMIT - n, o = t[t.length - 1].idx;
|
|
1634
|
+
console.log(`Deriving ${i} more ${s} addresses...`);
|
|
1635
|
+
const r = this.deriveAddresses(e, o + 1, i);
|
|
1636
|
+
e === m.RECEIVE ? this.receiveAddresses.push(...r) : this.changeAddresses.push(...r), await this.saveAddresses(r), await this.subscribeAddresses(r);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
async addNewAccount(e, t) {
|
|
1640
|
+
const s = x(f.DAPP, 0, e), n = this.keyManager.getKey(s).privateKey.toAddress().toString(), i = {
|
|
1641
|
+
id: e,
|
|
1642
|
+
name: t,
|
|
1643
|
+
address: n,
|
|
1644
|
+
height: 0,
|
|
1645
|
+
hidden: 0,
|
|
1646
|
+
statusHash: "",
|
|
1647
|
+
balance: { confirmed: "0", unconfirmed: "0" },
|
|
1648
|
+
tokensBalance: {},
|
|
1649
|
+
type: f.DAPP
|
|
1650
|
+
};
|
|
1651
|
+
this.accounts.set(e, i), this.accountsAddressToId.set(n, e), await this.walletDb.saveAccount(i), await this.subscribeAddresses([i]), this.notify({
|
|
1652
|
+
type: "new_account",
|
|
1653
|
+
account: {
|
|
1654
|
+
id: i.id,
|
|
1655
|
+
name: i.name,
|
|
1656
|
+
address: i.address,
|
|
1657
|
+
balance: i.balance,
|
|
1658
|
+
tokensBalance: i.tokensBalance,
|
|
1659
|
+
tokens: [],
|
|
1660
|
+
sessions: {}
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
async updateAccountName(e, t) {
|
|
1665
|
+
if (!t)
|
|
1666
|
+
return;
|
|
1667
|
+
const s = this.accounts.get(e);
|
|
1668
|
+
s.name = t, await this.walletDb.updateAccountName(e, t);
|
|
1669
|
+
}
|
|
1670
|
+
rescanAccount(e) {
|
|
1671
|
+
if (e == g) {
|
|
1672
|
+
const t = this.getMainAddresses();
|
|
1673
|
+
for (const s of t)
|
|
1674
|
+
s.height = 0, this.registerUpdate({ address: s, result: s.statusHash });
|
|
1675
|
+
} else {
|
|
1676
|
+
const t = this.accounts.get(e);
|
|
1677
|
+
t.height = 0, this.registerUpdate({ address: t, result: t.statusHash });
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
getVaultNextIndex() {
|
|
1681
|
+
return this.vaults.size == 0 ? 0 : Math.max(...Array.from(this.vaults.values(), (e) => e.idx)) + 1;
|
|
1682
|
+
}
|
|
1683
|
+
async addNewVault(e, t, s) {
|
|
1684
|
+
const n = {
|
|
1685
|
+
address: e,
|
|
1686
|
+
block: t,
|
|
1687
|
+
idx: s,
|
|
1688
|
+
height: 0,
|
|
1689
|
+
statusHash: "",
|
|
1690
|
+
balance: { confirmed: "0", unconfirmed: "0" },
|
|
1691
|
+
tokensBalance: {},
|
|
1692
|
+
type: f.VAULT
|
|
1693
|
+
};
|
|
1694
|
+
await this.addVault(n);
|
|
1695
|
+
}
|
|
1696
|
+
async addVault(e) {
|
|
1697
|
+
this.vaults.set(e.address, e), await this.walletDb.saveVault(e), await this.subscribeAddresses([e]), this.notify({
|
|
1698
|
+
type: "new_vault",
|
|
1699
|
+
vault: {
|
|
1700
|
+
address: e.address,
|
|
1701
|
+
balance: e.balance,
|
|
1702
|
+
block: e.block,
|
|
1703
|
+
index: e.idx
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
async rescanVaults() {
|
|
1708
|
+
let e = !1;
|
|
1709
|
+
const t = await this.discoveryService.discoverVaults(this.getMainAddresses().map((n) => n.address)), s = [];
|
|
1710
|
+
for (const [n, i] of t)
|
|
1711
|
+
this.vaults.has(n) || (e = !0, s.push(this.addVault(i)));
|
|
1712
|
+
return await Promise.all(s), e;
|
|
1713
|
+
}
|
|
1714
|
+
isVaultAddress(e) {
|
|
1715
|
+
return e.type == f.VAULT;
|
|
1716
|
+
}
|
|
1717
|
+
isAccountAddress(e) {
|
|
1718
|
+
return e.type == f.DAPP;
|
|
1719
|
+
}
|
|
1720
|
+
isMainAddress(e) {
|
|
1721
|
+
return e.type == f.MAIN;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
const at = {
|
|
1725
|
+
isWalletExist: !1,
|
|
1726
|
+
isAuthorized: !1
|
|
1727
|
+
}, we = M({
|
|
1728
|
+
name: "auth",
|
|
1729
|
+
initialState: at,
|
|
1730
|
+
reducers: {
|
|
1731
|
+
setWalletExist: (a, e) => {
|
|
1732
|
+
a.isWalletExist = e.payload;
|
|
1733
|
+
},
|
|
1734
|
+
setAuthorized: (a, e) => {
|
|
1735
|
+
a.isAuthorized = e.payload;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}), $t = we.actions, nt = we.reducer, it = {
|
|
1739
|
+
modalType: null,
|
|
1740
|
+
currentRequest: null
|
|
1741
|
+
}, ve = M({
|
|
1742
|
+
name: "dappModal",
|
|
1743
|
+
initialState: it,
|
|
1744
|
+
reducers: {
|
|
1745
|
+
showRequest(a, e) {
|
|
1746
|
+
a.modalType = e.payload.type, a.currentRequest = e.payload;
|
|
1747
|
+
},
|
|
1748
|
+
clear(a) {
|
|
1749
|
+
a.modalType = null, a.currentRequest = null;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}), Xt = ve.actions, rt = ve.reducer, ot = {
|
|
1753
|
+
counter: 0,
|
|
1754
|
+
loading: !1
|
|
1755
|
+
}, Ae = M({
|
|
1756
|
+
name: "loader",
|
|
1757
|
+
initialState: ot,
|
|
1758
|
+
reducers: {
|
|
1759
|
+
setLoader: (a, e) => {
|
|
1760
|
+
const t = e.payload.loading ? a.counter + 1 : Math.max(0, a.counter - 1);
|
|
1761
|
+
a.counter = t, a.loading = t > 0, a.text = e.payload.text;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}), Yt = Ae.actions, ct = Ae.reducer, dt = {
|
|
1765
|
+
wallet: {},
|
|
1766
|
+
web3: {}
|
|
1767
|
+
}, Se = M({
|
|
1768
|
+
name: "notifications",
|
|
1769
|
+
initialState: dt,
|
|
1770
|
+
reducers: {
|
|
1771
|
+
addNotification: (a, e) => {
|
|
1772
|
+
e.payload.type == "wallet" ? a.wallet[e.payload.id] = e.payload : e.payload.type == "web3" && (a.web3[e.payload.id] = e.payload);
|
|
1773
|
+
},
|
|
1774
|
+
removeNotification: (a, e) => {
|
|
1775
|
+
delete a.wallet[e.payload], delete a.web3[e.payload];
|
|
1776
|
+
},
|
|
1777
|
+
clearAll: (a, e) => {
|
|
1778
|
+
e.payload === "wallet" ? a.wallet = {} : e.payload === "web3" ? a.web3 = {} : e.payload === "all" && (a.wallet = {}, a.web3 = {});
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}), Zt = Se.actions, ut = Se.reducer, lt = {
|
|
1782
|
+
status: "Offline",
|
|
1783
|
+
height: 0,
|
|
1784
|
+
price: me(),
|
|
1785
|
+
hasNetwork: !0,
|
|
1786
|
+
isSuspended: !1
|
|
1787
|
+
}, ce = Re("status/fetchPrice", async () => {
|
|
1788
|
+
const a = me();
|
|
1789
|
+
try {
|
|
1790
|
+
const e = await We();
|
|
1791
|
+
Object.keys(e).forEach((t) => {
|
|
1792
|
+
a[t] = {
|
|
1793
|
+
value: e[t],
|
|
1794
|
+
change: e[`${t}_24h_change`]
|
|
1795
|
+
};
|
|
1796
|
+
});
|
|
1797
|
+
} catch {
|
|
1798
|
+
}
|
|
1799
|
+
return a;
|
|
1800
|
+
}), be = M({
|
|
1801
|
+
name: "status",
|
|
1802
|
+
initialState: lt,
|
|
1803
|
+
reducers: {
|
|
1804
|
+
setHeight: (a, e) => {
|
|
1805
|
+
e.payload > 0 && (a.status = "Online"), a.height = e.payload;
|
|
1806
|
+
},
|
|
1807
|
+
setOffline: (a) => {
|
|
1808
|
+
a.status = "Offline";
|
|
1809
|
+
},
|
|
1810
|
+
setHasNetwork: (a, e) => {
|
|
1811
|
+
a.hasNetwork = e.payload;
|
|
1812
|
+
},
|
|
1813
|
+
setIsSuspended: (a, e) => {
|
|
1814
|
+
a.isSuspended = e.payload;
|
|
1815
|
+
}
|
|
1816
|
+
},
|
|
1817
|
+
extraReducers: (a) => {
|
|
1818
|
+
a.addCase(ce.fulfilled, (e, t) => {
|
|
1819
|
+
e.price = t.payload;
|
|
1820
|
+
}).addCase(ce.rejected, (e, t) => {
|
|
1821
|
+
console.error(t.error.message);
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
}), Qt = be.actions, ht = be.reducer, ft = {
|
|
1825
|
+
isAuthorized: !1,
|
|
1826
|
+
selectedAccount: g,
|
|
1827
|
+
accounts: {
|
|
1828
|
+
"-1": {
|
|
1829
|
+
id: g,
|
|
1830
|
+
name: "Main Wallet",
|
|
1831
|
+
address: "",
|
|
1832
|
+
balance: { confirmed: "0", unconfirmed: "0" },
|
|
1833
|
+
tokensBalance: {},
|
|
1834
|
+
tokens: [],
|
|
1835
|
+
sessions: {}
|
|
1836
|
+
}
|
|
1837
|
+
},
|
|
1838
|
+
vaults: {},
|
|
1839
|
+
sync: !1,
|
|
1840
|
+
initLoad: !0,
|
|
1841
|
+
txUpdateTrigger: 0,
|
|
1842
|
+
nftsUpdateTrigger: 0
|
|
1843
|
+
}, ke = M({
|
|
1844
|
+
name: "wallet",
|
|
1845
|
+
initialState: ft,
|
|
1846
|
+
reducers: {
|
|
1847
|
+
setInitLoad: (a, e) => {
|
|
1848
|
+
a.initLoad = e.payload;
|
|
1849
|
+
},
|
|
1850
|
+
setSelectedAccount: (a, e) => {
|
|
1851
|
+
a.selectedAccount = e.payload;
|
|
1852
|
+
},
|
|
1853
|
+
setSync: (a, e) => {
|
|
1854
|
+
a.sync = e.payload;
|
|
1855
|
+
},
|
|
1856
|
+
setVault: (a, e) => {
|
|
1857
|
+
a.vaults[e.payload.address] = e.payload;
|
|
1858
|
+
},
|
|
1859
|
+
setVaultBalance: (a, e) => {
|
|
1860
|
+
a.vaults[e.payload.address].balance = e.payload.balance;
|
|
1861
|
+
},
|
|
1862
|
+
setAccount: (a, e) => {
|
|
1863
|
+
a.accounts[e.payload.id] = e.payload;
|
|
1864
|
+
},
|
|
1865
|
+
setMainAddress: (a, e) => {
|
|
1866
|
+
a.accounts[g].address = e.payload;
|
|
1867
|
+
},
|
|
1868
|
+
setAccountBalance: (a, e) => {
|
|
1869
|
+
a.accounts[e.payload.id].balance = e.payload.balance, a.accounts[e.payload.id].tokensBalance = e.payload.tokensBalance;
|
|
1870
|
+
},
|
|
1871
|
+
setAccountName: (a, e) => {
|
|
1872
|
+
a.accounts[e.payload.id].name = e.payload.name;
|
|
1873
|
+
},
|
|
1874
|
+
setTokens: (a, e) => {
|
|
1875
|
+
a.accounts[e.payload.id].tokens = e.payload.assets;
|
|
1876
|
+
},
|
|
1877
|
+
addToken: (a, e) => {
|
|
1878
|
+
a.accounts[e.payload.id].tokens.unshift(e.payload.asset);
|
|
1879
|
+
},
|
|
1880
|
+
removeToken: (a, e) => {
|
|
1881
|
+
a.accounts[e.payload.id].tokens = a.accounts[e.payload.id].tokens.filter((t) => t.tokenIdHex !== e.payload.tokenId);
|
|
1882
|
+
},
|
|
1883
|
+
refreshTxs: (a) => {
|
|
1884
|
+
a.txUpdateTrigger++;
|
|
1885
|
+
},
|
|
1886
|
+
refreshNfts: (a) => {
|
|
1887
|
+
a.nftsUpdateTrigger++;
|
|
1888
|
+
},
|
|
1889
|
+
addSession(a, e) {
|
|
1890
|
+
const { accountId: t, sessionInfo: s } = e.payload;
|
|
1891
|
+
a.accounts[t].sessions[s.details.sessionId] = s;
|
|
1892
|
+
},
|
|
1893
|
+
removeSession(a, e) {
|
|
1894
|
+
const { accountId: t, sessionId: s } = e.payload;
|
|
1895
|
+
a.accounts[t].sessions[s] && delete a.accounts[t].sessions[s];
|
|
1896
|
+
},
|
|
1897
|
+
clearSessions(a, e) {
|
|
1898
|
+
a.accounts[e.payload.accountId].sessions = {};
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}), es = ke.actions, pt = ke.reducer, ts = {
|
|
1902
|
+
loader: ct,
|
|
1903
|
+
dapp: rt,
|
|
1904
|
+
status: ht,
|
|
1905
|
+
wallet: pt,
|
|
1906
|
+
auth: nt,
|
|
1907
|
+
notifications: ut
|
|
1908
|
+
}, w = He.withTypes(), ss = () => w((a) => a.auth), as = () => w((a) => a.status.height), ns = (a) => w((e) => e.wallet.accounts[a]), is = () => w((a) => a.wallet.selectedAccount), rs = () => w((a) => Math.max(...Object.keys(a.wallet.accounts).map(Number))), os = (a) => w((e) => e.wallet.accounts[a].address), cs = (a) => w((e) => e.wallet.accounts[a].balance), ds = (a, e) => w((t) => t.wallet.accounts[a].tokensBalance[e]), us = (a, e) => w((t) => t.wallet.accounts[a].sessions[e]), ls = () => w((a) => a.wallet.vaults), Te = j(
|
|
1909
|
+
[(a) => a.notifications.wallet],
|
|
1910
|
+
(a) => Object.values(a).sort((e, t) => t.createdAt - e.createdAt)
|
|
1911
|
+
), Ie = j(
|
|
1912
|
+
[(a) => a.notifications.web3],
|
|
1913
|
+
(a) => Object.values(a).sort((e, t) => t.createdAt - e.createdAt)
|
|
1914
|
+
), gt = j(
|
|
1915
|
+
[Te, Ie],
|
|
1916
|
+
(a, e) => [...a, ...e].sort((t, s) => s.createdAt - t.createdAt)
|
|
1917
|
+
), hs = () => w(Te), fs = () => w(Ie), ps = () => w(gt);
|
|
1918
|
+
export {
|
|
1919
|
+
f as AccountType,
|
|
1920
|
+
Lt as AssetService,
|
|
1921
|
+
V as AssetType,
|
|
1922
|
+
Ut as KVStore,
|
|
1923
|
+
Rt as KeyManager,
|
|
1924
|
+
m as KeySpace,
|
|
1925
|
+
g as MAIN_WALLET_ID,
|
|
1926
|
+
ne as MAX_INT64,
|
|
1927
|
+
he as RostrumService,
|
|
1928
|
+
Wt as SessionManager,
|
|
1929
|
+
_ as SessionRequestType,
|
|
1930
|
+
Ft as TransactionService,
|
|
1931
|
+
jt as TransactionTransformer,
|
|
1932
|
+
Ke as VAULT_FIRST_BLOCK,
|
|
1933
|
+
Nt as VAULT_SCRIPT_PREFIX,
|
|
1934
|
+
Vt as WalletCache,
|
|
1935
|
+
Dt as WalletDB,
|
|
1936
|
+
N as WalletManager,
|
|
1937
|
+
$t as authActions,
|
|
1938
|
+
nt as authReducer,
|
|
1939
|
+
Ct as capitalizeFirstLetter,
|
|
1940
|
+
ye as currencies,
|
|
1941
|
+
$ as currentTimestamp,
|
|
1942
|
+
Xt as dappModalActions,
|
|
1943
|
+
rt as dappModalReducer,
|
|
1944
|
+
qt as encryptMnemonic,
|
|
1945
|
+
Bt as estimateDateByFutureBlock,
|
|
1946
|
+
Ge as fetchAssetBlob,
|
|
1947
|
+
Fe as fetchAssetDoc,
|
|
1948
|
+
Ht as fetchNiftyNFT,
|
|
1949
|
+
ce as fetchPrice,
|
|
1950
|
+
Gt as generateNewMnemonic,
|
|
1951
|
+
Qe as generateVaultAddress,
|
|
1952
|
+
Xe as generateVaultConstraint,
|
|
1953
|
+
Ze as generateVaultVisibleArgs,
|
|
1954
|
+
R as getAddressBuffer,
|
|
1955
|
+
zt as getCurrencySymbol,
|
|
1956
|
+
Mt as getExplorerUrl,
|
|
1957
|
+
Pt as getFileMediaType,
|
|
1958
|
+
Ot as getFileMimeType,
|
|
1959
|
+
We as getNexaPrices,
|
|
1960
|
+
fe as getNiftyToken,
|
|
1961
|
+
Ye as getVaultConstraintHash,
|
|
1962
|
+
je as getVaultTemplate,
|
|
1963
|
+
$e as getVaultTemplateHash,
|
|
1964
|
+
me as initializePrices,
|
|
1965
|
+
Et as isGenesisHashValid,
|
|
1966
|
+
Je as isMnemonicValid,
|
|
1967
|
+
q as isNiftySubgroup,
|
|
1968
|
+
C as isNullOrEmpty,
|
|
1969
|
+
T as isTestnet,
|
|
1970
|
+
F as isValidNexaAddress,
|
|
1971
|
+
x as keyPathToString,
|
|
1972
|
+
Yt as loaderActions,
|
|
1973
|
+
ct as loaderReducer,
|
|
1974
|
+
Zt as notificationsActions,
|
|
1975
|
+
ut as notificationsReducer,
|
|
1976
|
+
Kt as parseTokenDataUrl,
|
|
1977
|
+
ts as sharedReducers,
|
|
1978
|
+
Qt as statusActions,
|
|
1979
|
+
ht as statusReducer,
|
|
1980
|
+
Le as stringToKeyPath,
|
|
1981
|
+
re as sumBalance,
|
|
1982
|
+
G as sumTokensBalance,
|
|
1983
|
+
D as tokenAmountToAssetAmount,
|
|
1984
|
+
ie as tokenHexToAddr,
|
|
1985
|
+
S as tokenIdToHex,
|
|
1986
|
+
oe as transformTokenIconUrl,
|
|
1987
|
+
_t as truncateStringMiddle,
|
|
1988
|
+
ns as useAccount,
|
|
1989
|
+
ps as useAllNotifications,
|
|
1990
|
+
ss as useAuth,
|
|
1991
|
+
as as useBlockHeight,
|
|
1992
|
+
us as useDAppSession,
|
|
1993
|
+
rs as useLastAccountId,
|
|
1994
|
+
os as useMainReceiveAddress,
|
|
1995
|
+
is as useSelectedAccount,
|
|
1996
|
+
ds as useTokenBalance,
|
|
1997
|
+
ls as useVaults,
|
|
1998
|
+
cs as useWalletBalance,
|
|
1999
|
+
hs as useWalletNotifications,
|
|
2000
|
+
fs as useWeb3Notifications,
|
|
2001
|
+
Jt as validateAndDecryptMnemonic,
|
|
2002
|
+
es as walletActions,
|
|
2003
|
+
pt as walletReducer
|
|
2004
|
+
};
|
|
2005
|
+
//# sourceMappingURL=index.js.map
|