@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomo-inc/chains-service",
3
- "version": "0.0.8",
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.7"
26
+ "@tomo-inc/wallet-utils": "0.0.8"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/supertest": "^2.0.12",
@@ -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,
@@ -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 { cache } from "@tomo-inc/wallet-utils";
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 Drc20Transaction = {
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
- export async function getTxDetail(txId: string): Promise<Drc20Transaction> {
60
+
61
+ export async function getTxDetail(txId: string): Promise<Transaction> {
65
62
  if (!txId) {
66
- return {} as Drc20Transaction;
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 Drc20TransactionResponse = {
353
- transactions: Drc20Transaction[];
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<Drc20TransactionResponse> {
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 res = await Promise.all(
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
- const filterTickList = [...new Set(res.map((item) => item.tick).filter((i) => i))];
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
  }
@@ -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.amount,
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
- // Reverse the hash to get the correct byte order (bitcoin uses little-endian)
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: Number(output.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: Number(output.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, DogecoinService };
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";