@tomo-inc/chains-service 0.0.8 → 0.0.9
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/dist/index.cjs +1258 -1035
- package/dist/index.d.cts +263 -90
- package/dist/index.d.ts +263 -90
- package/dist/index.js +1229 -1016
- package/package.json +2 -2
- package/src/api/network-data.ts +14 -0
- package/src/dogecoin/rpc.ts +10 -191
- package/src/dogecoin/service.ts +2 -16
- package/src/dogecoin/utils-doge.ts +19 -6
- package/src/index.ts +8 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomo-inc/chains-service",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"author": "tomo.inc",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"bitcoinjs-lib": "^7.0.0",
|
|
24
24
|
"crypto-js": "4.2.0",
|
|
25
25
|
"viem": "2.21.54",
|
|
26
|
-
"@tomo-inc/wallet-utils": "0.0.
|
|
26
|
+
"@tomo-inc/wallet-utils": "0.0.8"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/supertest": "^2.0.12",
|
package/src/api/network-data.ts
CHANGED
|
@@ -43,6 +43,20 @@ export const loadNetworks = (WALLET_DOMAIN: string) => {
|
|
|
43
43
|
supportGift: false,
|
|
44
44
|
supportHistory: true,
|
|
45
45
|
},
|
|
46
|
+
{
|
|
47
|
+
chainId: 36888,
|
|
48
|
+
chainIndex: 3688800,
|
|
49
|
+
name: "ABCORE",
|
|
50
|
+
chainName: "AB Core",
|
|
51
|
+
rpcUrls: ["https://rpc1.core.ab.org"],
|
|
52
|
+
blockExplorerUrl: "https://explorer.core.ab.org",
|
|
53
|
+
platformType: "EVM",
|
|
54
|
+
isTestnet: false,
|
|
55
|
+
icon: "/assets/ab.svg",
|
|
56
|
+
supportSwap: true,
|
|
57
|
+
supportGift: false,
|
|
58
|
+
supportHistory: true,
|
|
59
|
+
},
|
|
46
60
|
{
|
|
47
61
|
chainId: 6281971,
|
|
48
62
|
chainIndex: 628197100,
|
package/src/dogecoin/rpc.ts
CHANGED
|
@@ -3,8 +3,7 @@ import axios from "axios";
|
|
|
3
3
|
import { TRANSACTION_PAGE_SIZE, BLOCK_CONFIRMATIONS, FEE_RATE_KB } from "./config";
|
|
4
4
|
import { RPC_URL, MYDOGE_BASE_URL, RPC_TIMEOUT, TX_SIZE } from "./config";
|
|
5
5
|
import { TransactionParser, toSatoshi, toBitcoin } from "./utils";
|
|
6
|
-
import {
|
|
7
|
-
import { DogeSpendableUtxos, DunesTransactionParams } from "./type";
|
|
6
|
+
import { DogeSpendableUtxos } from "./type";
|
|
8
7
|
|
|
9
8
|
export const mydoge = axios.create({
|
|
10
9
|
baseURL: MYDOGE_BASE_URL,
|
|
@@ -29,7 +28,7 @@ export async function getBalance(address?: string) {
|
|
|
29
28
|
return res.data || {};
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
export type
|
|
31
|
+
export type Transaction = {
|
|
33
32
|
blockHash: string;
|
|
34
33
|
blockHeight: number;
|
|
35
34
|
blockTime: number;
|
|
@@ -57,13 +56,11 @@ export type Drc20Transaction = {
|
|
|
57
56
|
hex: string;
|
|
58
57
|
isAddress: boolean;
|
|
59
58
|
}[];
|
|
60
|
-
tick?: string;
|
|
61
|
-
tickAmount?: number;
|
|
62
|
-
drc20Detail?: Drc20Detail;
|
|
63
59
|
};
|
|
64
|
-
|
|
60
|
+
|
|
61
|
+
export async function getTxDetail(txId: string): Promise<Transaction> {
|
|
65
62
|
if (!txId) {
|
|
66
|
-
return {} as
|
|
63
|
+
return {} as Transaction;
|
|
67
64
|
}
|
|
68
65
|
const path = `/tx/${txId}`;
|
|
69
66
|
const api = `${MYDOGE_BASE_URL}/wallet/info?route=` + encodeURIComponent(path);
|
|
@@ -73,59 +70,6 @@ export async function getTxDetail(txId: string): Promise<Drc20Transaction> {
|
|
|
73
70
|
return res.data || {};
|
|
74
71
|
}
|
|
75
72
|
|
|
76
|
-
export async function getAccountDrc20List(address: string, config?: any) {
|
|
77
|
-
const api = `/drc20/${address}`;
|
|
78
|
-
const res: any = await mydoge.get(api);
|
|
79
|
-
return res.data || {};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const Drc20DetailsCacheKey = "Drc20Details";
|
|
83
|
-
const Drc20DetailsCache = cache.get(Drc20DetailsCacheKey) || {};
|
|
84
|
-
function isCacheAvailable(time: number) {
|
|
85
|
-
const cacheTime = 20 * 60 * 1000; // 30 minutes
|
|
86
|
-
return Date.now() - time <= cacheTime;
|
|
87
|
-
}
|
|
88
|
-
export type Drc20Detail = {
|
|
89
|
-
changePercent: string;
|
|
90
|
-
currentSupply: number;
|
|
91
|
-
floorPrice: number;
|
|
92
|
-
holders: number;
|
|
93
|
-
lastPrice: number;
|
|
94
|
-
maxSupply: number;
|
|
95
|
-
pic: string;
|
|
96
|
-
sales: number;
|
|
97
|
-
tick: string;
|
|
98
|
-
twentyFourHourVolume: string;
|
|
99
|
-
volume: string;
|
|
100
|
-
};
|
|
101
|
-
export async function getDrc20Detail(ticker: string): Promise<Drc20Detail> {
|
|
102
|
-
const drc20Detail = Drc20DetailsCache?.[ticker];
|
|
103
|
-
if (drc20Detail?.cacheTime && isCacheAvailable(drc20Detail?.cacheTime)) {
|
|
104
|
-
return drc20Detail;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const api = `/drc20/data/${ticker}`;
|
|
108
|
-
const res: any = await mydoge.get(api);
|
|
109
|
-
const detail = res.data || {};
|
|
110
|
-
if (detail.pic) {
|
|
111
|
-
Drc20DetailsCache[ticker] = { ...detail, cacheTime: Date.now() };
|
|
112
|
-
cache.set(Drc20DetailsCacheKey, Drc20DetailsCache, false);
|
|
113
|
-
}
|
|
114
|
-
return detail;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export async function getDRC20Inscriptions(address: string, ticker: string) {
|
|
118
|
-
const api = `${MYDOGE_BASE_URL}/inscriptions/${address}?filter=drc20&ticker=${encodeURIComponent(ticker)}`;
|
|
119
|
-
const query = (await mydoge.get(api)).data;
|
|
120
|
-
return query;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export async function getDRC20Balance(address: string, ticker: string) {
|
|
124
|
-
const api = `${MYDOGE_BASE_URL}/drc20/${address}?ticker=${ticker}`;
|
|
125
|
-
const res = (await mydoge.get(api))?.data || {};
|
|
126
|
-
return res;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
73
|
async function getUtxos(
|
|
130
74
|
address: string,
|
|
131
75
|
cursor: number,
|
|
@@ -212,100 +156,6 @@ export async function sendTransaction({ signed, senderAddress }: any) {
|
|
|
212
156
|
return jsonrpcRes;
|
|
213
157
|
}
|
|
214
158
|
|
|
215
|
-
//nft
|
|
216
|
-
export async function getNFTs(address?: string): Promise<{ list: any[]; total: number }> {
|
|
217
|
-
if (!address) {
|
|
218
|
-
throw new Error("address is required");
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const res: any = await mydoge.get(`/inscriptions/${address}`);
|
|
222
|
-
const { list = [], total = 0 } = res?.data || {};
|
|
223
|
-
return { list, total };
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export async function createNFTTransaction(params: {
|
|
227
|
-
senderAddress: string;
|
|
228
|
-
recipientAddress: string;
|
|
229
|
-
location: string;
|
|
230
|
-
inscriptionId: string;
|
|
231
|
-
}) {
|
|
232
|
-
const { senderAddress, recipientAddress, location, inscriptionId } = params;
|
|
233
|
-
try {
|
|
234
|
-
const res: any = await mydoge.post("/tx/prepare/inscription", {
|
|
235
|
-
sender: senderAddress,
|
|
236
|
-
recipient: recipientAddress,
|
|
237
|
-
location: location,
|
|
238
|
-
inscriptionId: inscriptionId,
|
|
239
|
-
});
|
|
240
|
-
const { rawTx, fee, amount } = res?.data || {};
|
|
241
|
-
|
|
242
|
-
return {
|
|
243
|
-
rawTx,
|
|
244
|
-
fee,
|
|
245
|
-
amount,
|
|
246
|
-
};
|
|
247
|
-
} catch (err) {
|
|
248
|
-
console.error("createNFTTransaction", err);
|
|
249
|
-
return {};
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
//dunes
|
|
254
|
-
export async function getDunesBalance(address: string, ticker?: string) {
|
|
255
|
-
const api = `/dunes/${address}${ticker ? `?ticker=${ticker}` : ""}`;
|
|
256
|
-
const res = (await mydoge.get(api))?.data || {};
|
|
257
|
-
return res;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const DuneDetailsCacheKey = "DuneDetails";
|
|
261
|
-
const DuneDetailsCache = cache.get(DuneDetailsCacheKey) || {};
|
|
262
|
-
export async function getDuneDetail(ticker: string): Promise<Drc20Detail> {
|
|
263
|
-
const duneDetail = DuneDetailsCache?.[ticker];
|
|
264
|
-
if (duneDetail?.cacheTime && isCacheAvailable(duneDetail?.cacheTime)) {
|
|
265
|
-
return duneDetail;
|
|
266
|
-
}
|
|
267
|
-
try {
|
|
268
|
-
const api = `/dunes/data/${ticker}`;
|
|
269
|
-
const res: any = await mydoge.get(api);
|
|
270
|
-
const detail = res.data || {};
|
|
271
|
-
if (detail.pic) {
|
|
272
|
-
DuneDetailsCache[ticker] = { ...detail, cacheTime: Date.now() };
|
|
273
|
-
cache.set(DuneDetailsCacheKey, DuneDetailsCache, false);
|
|
274
|
-
}
|
|
275
|
-
return detail;
|
|
276
|
-
} catch (err) {
|
|
277
|
-
throw new Error("dune detail not found");
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export async function createDunesTransaction(params: DunesTransactionParams) {
|
|
282
|
-
try {
|
|
283
|
-
const { senderAddress = "", recipientAddress = "", amount = 0, ticker = "" } = params;
|
|
284
|
-
const { balances = [] } = await getDunesBalance(senderAddress, ticker);
|
|
285
|
-
const { duneId } = balances[0] || {};
|
|
286
|
-
if (!duneId) {
|
|
287
|
-
throw new Error("duneId not found");
|
|
288
|
-
}
|
|
289
|
-
const response = await mydoge.post("/tx/prepare/dune", {
|
|
290
|
-
sender: senderAddress,
|
|
291
|
-
recipient: recipientAddress,
|
|
292
|
-
amount,
|
|
293
|
-
duneId,
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
const { rawTx, fee, inputs } = response.data;
|
|
297
|
-
|
|
298
|
-
return {
|
|
299
|
-
rawTx,
|
|
300
|
-
fee,
|
|
301
|
-
inputs, // usingUtxos
|
|
302
|
-
};
|
|
303
|
-
} catch (err) {
|
|
304
|
-
console.error("createDunesTransaction", err);
|
|
305
|
-
return {};
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
159
|
export async function estimateSmartFee({ senderAddress }: { senderAddress: string }) {
|
|
310
160
|
const smartfeeReq = {
|
|
311
161
|
jsonrpc: "2.0",
|
|
@@ -349,8 +199,8 @@ export async function onCreateTransaction({ data, sendResponse }: { data: any; s
|
|
|
349
199
|
}
|
|
350
200
|
}
|
|
351
201
|
|
|
352
|
-
export type
|
|
353
|
-
transactions:
|
|
202
|
+
export type TransactionResponse = {
|
|
203
|
+
transactions: Transaction[];
|
|
354
204
|
txIds: string[];
|
|
355
205
|
totalPages: number;
|
|
356
206
|
page: number;
|
|
@@ -362,7 +212,7 @@ export async function getTransactions(
|
|
|
362
212
|
pageSize: number;
|
|
363
213
|
pageNumber: number;
|
|
364
214
|
},
|
|
365
|
-
): Promise<
|
|
215
|
+
): Promise<TransactionResponse> {
|
|
366
216
|
const { pageSize = 10, pageNumber = 1 } = config || {};
|
|
367
217
|
const size = Math.min(pageSize, TRANSACTION_PAGE_SIZE);
|
|
368
218
|
// Get txids
|
|
@@ -383,7 +233,7 @@ export async function getTransactions(
|
|
|
383
233
|
totalPages = response.totalPages;
|
|
384
234
|
page = response.page;
|
|
385
235
|
|
|
386
|
-
const
|
|
236
|
+
const transactions = await Promise.all(
|
|
387
237
|
txIds?.map(async (txId: string) => {
|
|
388
238
|
const detail = await getTxDetail(txId);
|
|
389
239
|
const tx = new TransactionParser(detail.hex);
|
|
@@ -396,38 +246,7 @@ export async function getTransactions(
|
|
|
396
246
|
}) || [],
|
|
397
247
|
);
|
|
398
248
|
|
|
399
|
-
|
|
400
|
-
const tickList = [];
|
|
401
|
-
for (const item of filterTickList) {
|
|
402
|
-
try {
|
|
403
|
-
const detail = await getDrc20Detail(item);
|
|
404
|
-
tickList.push(detail);
|
|
405
|
-
} catch (err) {
|
|
406
|
-
console.error(`Failed to fetch detail for ${item}`, err);
|
|
407
|
-
tickList.push({ tick: item });
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
// const tickList = await throttledMap(
|
|
411
|
-
// filterTickList,
|
|
412
|
-
// async (item) => {
|
|
413
|
-
// try {
|
|
414
|
-
// return await getDrc20Detail(item);
|
|
415
|
-
// } catch (err) {
|
|
416
|
-
// console.error(`Failed to fetch detail for ${item}`, err);
|
|
417
|
-
// return { tick: item };
|
|
418
|
-
// }
|
|
419
|
-
// },
|
|
420
|
-
// 1,
|
|
421
|
-
// );
|
|
422
|
-
const tickMap = tickList.reduce((acc, cur) => {
|
|
423
|
-
acc[cur.tick] = cur;
|
|
424
|
-
return acc;
|
|
425
|
-
}, {} as any);
|
|
426
|
-
res.forEach((item) => {
|
|
427
|
-
item.drc20Detail = tickMap[item.tick];
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
return { transactions: res || [], txIds, totalPages, page };
|
|
249
|
+
return { transactions, txIds, totalPages, page };
|
|
431
250
|
} catch (err) {
|
|
432
251
|
throw new Error("getTransactions failed");
|
|
433
252
|
}
|
package/src/dogecoin/service.ts
CHANGED
|
@@ -19,10 +19,12 @@ import { createPsbt, addUsedUtxos } from "./utils";
|
|
|
19
19
|
export class DogecoinService extends BaseService {
|
|
20
20
|
private static instance: DogecoinService;
|
|
21
21
|
public chainType: ChainTypes | "";
|
|
22
|
+
public rpcService: typeof API;
|
|
22
23
|
|
|
23
24
|
public constructor(chainType: ChainTypes, accountInfo: IAccountInfo, tomoAppInfo: TomoAppInfo) {
|
|
24
25
|
super(tomoAppInfo, accountInfo);
|
|
25
26
|
this.chainType = chainType;
|
|
27
|
+
this.rpcService = API;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
//singleton
|
|
@@ -106,22 +108,6 @@ export class DogecoinService extends BaseService {
|
|
|
106
108
|
});
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
public async requestDecryptedMessage({ message }: { message: string }): Promise<{ decryptedMessage: string }> {
|
|
110
|
-
try {
|
|
111
|
-
if (!this.accountInfo?.decryptedMessage) {
|
|
112
|
-
throw new Error("decryptedMessage is not supported");
|
|
113
|
-
}
|
|
114
|
-
const unsignRes = await this.accountInfo?.decryptedMessage(message);
|
|
115
|
-
const { text = "" } = JSON.parse(unsignRes);
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
decryptedMessage: text,
|
|
119
|
-
};
|
|
120
|
-
} catch (err) {
|
|
121
|
-
throw new Error("requestDecryptedMessage err:" + JSON.stringify(err));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
111
|
public async _queryGasInfo(txData: TransactionParams): Promise<QueryGasResponse> {
|
|
126
112
|
const { chainId, amount = 0, decimals = 8 } = txData;
|
|
127
113
|
const gasLimitParam: any = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { address as AddressLib, Psbt, Transaction } from "bitcoinjs-lib";
|
|
2
2
|
import * as base from "./base";
|
|
3
3
|
import { network } from "./config";
|
|
4
|
+
import * as APIs from "./rpc";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* UTXO Transaction type definition
|
|
@@ -67,7 +68,7 @@ export class DogecoinUtils {
|
|
|
67
68
|
// Add outputs
|
|
68
69
|
for (const output of tx.outputs) {
|
|
69
70
|
psbt.addOutput({
|
|
70
|
-
value: output
|
|
71
|
+
value: BigInt(output?.amount || 0),
|
|
71
72
|
address: output.address,
|
|
72
73
|
});
|
|
73
74
|
}
|
|
@@ -129,8 +130,7 @@ export class DogecoinUtils {
|
|
|
129
130
|
// Each input references a previous transaction's output
|
|
130
131
|
for (let i = 0; i < transaction.ins.length; i++) {
|
|
131
132
|
const input = transaction.ins[i];
|
|
132
|
-
|
|
133
|
-
const hash = Buffer.from(input.hash).reverse().toString("hex");
|
|
133
|
+
const hash = Buffer.from(input.hash).toString("hex");
|
|
134
134
|
const index = input.index;
|
|
135
135
|
|
|
136
136
|
// Add input without nonWitnessUtxo initially
|
|
@@ -139,9 +139,22 @@ export class DogecoinUtils {
|
|
|
139
139
|
const inputOptions: any = {
|
|
140
140
|
hash,
|
|
141
141
|
index,
|
|
142
|
-
nonWitnessUtxo: input.script,
|
|
143
142
|
};
|
|
144
143
|
|
|
144
|
+
try {
|
|
145
|
+
const prevTxDetail = await APIs.getTxDetail(hash);
|
|
146
|
+
if (prevTxDetail?.hex) {
|
|
147
|
+
inputOptions.nonWitnessUtxo = Buffer.from(prevTxDetail.hex, "hex");
|
|
148
|
+
} else {
|
|
149
|
+
throw new Error(`Previous transaction ${hash} has no hex data`);
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Failed to fetch previous transaction ${hash} for input #${i}. ` +
|
|
154
|
+
`This is required for PSBT creation. Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
145
158
|
// If the transaction is unsigned and we have the script, we can add it
|
|
146
159
|
// Otherwise, nonWitnessUtxo will need to be fetched from RPC
|
|
147
160
|
psbt.addInput(inputOptions);
|
|
@@ -154,13 +167,13 @@ export class DogecoinUtils {
|
|
|
154
167
|
const address = AddressLib.fromOutputScript(output.script, network);
|
|
155
168
|
psbt.addOutput({
|
|
156
169
|
address,
|
|
157
|
-
value:
|
|
170
|
+
value: BigInt(output?.value || 0),
|
|
158
171
|
});
|
|
159
172
|
} catch (error) {
|
|
160
173
|
// If address extraction fails (e.g., OP_RETURN), add output with script and value
|
|
161
174
|
psbt.addOutput({
|
|
162
175
|
script: output.script,
|
|
163
|
-
value:
|
|
176
|
+
value: BigInt(output?.value || 0),
|
|
164
177
|
});
|
|
165
178
|
}
|
|
166
179
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { ChainTypes } from "@tomo-inc/wallet-utils";
|
|
2
|
+
import { DogecoinService } from "./dogecoin/service";
|
|
1
3
|
import { EvmService } from "./evm/service";
|
|
2
4
|
import { SolanaService } from "./solana/service";
|
|
3
|
-
import { DogecoinService } from "./dogecoin/service";
|
|
4
|
-
import { ChainTypes } from "@tomo-inc/wallet-utils";
|
|
5
5
|
|
|
6
|
-
export { EvmService, SolanaService
|
|
6
|
+
export { DogecoinService, EvmService, SolanaService };
|
|
7
7
|
|
|
8
8
|
export const ChainTypeServices = {
|
|
9
9
|
[ChainTypes.EVM]: EvmService,
|
|
@@ -18,8 +18,8 @@ export { TomoWallet } from "./wallet";
|
|
|
18
18
|
|
|
19
19
|
export type {
|
|
20
20
|
ChainAddress,
|
|
21
|
-
IAccountInfo,
|
|
22
21
|
IAccount,
|
|
22
|
+
IAccountInfo,
|
|
23
23
|
TomoAppInfo,
|
|
24
24
|
TransactionItem,
|
|
25
25
|
TransactionsParams,
|
|
@@ -27,3 +27,7 @@ export type {
|
|
|
27
27
|
} from "./types";
|
|
28
28
|
|
|
29
29
|
export { AccountType, TxTypes } from "./types";
|
|
30
|
+
|
|
31
|
+
export * as DogecoinAPI from "./dogecoin/rpc";
|
|
32
|
+
export * from "./dogecoin/utils";
|
|
33
|
+
export * from "./dogecoin/utils-doge";
|