@tomo-inc/chains-service 0.0.2
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/CHANGELOG.md +3 -0
- package/README.md +15 -0
- package/package.json +38 -0
- package/project.json +59 -0
- package/src/api/__tests__/config.ts +21 -0
- package/src/api/__tests__/token.test.ts +120 -0
- package/src/api/__tests__/transaction.test.ts +86 -0
- package/src/api/__tests__/user.test.ts +105 -0
- package/src/api/__tests__/wallet.test.ts +73 -0
- package/src/api/base.ts +52 -0
- package/src/api/index.ts +24 -0
- package/src/api/network-data.ts +572 -0
- package/src/api/network.ts +81 -0
- package/src/api/token.ts +182 -0
- package/src/api/transaction.ts +59 -0
- package/src/api/types/common.ts +35 -0
- package/src/api/types/index.ts +13 -0
- package/src/api/types/type.ts +283 -0
- package/src/api/user.ts +83 -0
- package/src/api/utils/index.ts +34 -0
- package/src/api/utils/signature.ts +60 -0
- package/src/api/wallet.ts +57 -0
- package/src/base/network.ts +55 -0
- package/src/base/service.ts +33 -0
- package/src/base/token.ts +43 -0
- package/src/base/transaction.ts +58 -0
- package/src/config.ts +21 -0
- package/src/dogecoin/base.ts +39 -0
- package/src/dogecoin/config.ts +43 -0
- package/src/dogecoin/rpc.ts +449 -0
- package/src/dogecoin/service.ts +451 -0
- package/src/dogecoin/type.ts +29 -0
- package/src/dogecoin/utils-doge.ts +105 -0
- package/src/dogecoin/utils.ts +601 -0
- package/src/evm/rpc.ts +68 -0
- package/src/evm/service.ts +403 -0
- package/src/evm/utils.ts +92 -0
- package/src/index.ts +28 -0
- package/src/solana/config.ts +5 -0
- package/src/solana/service.ts +312 -0
- package/src/solana/types.ts +91 -0
- package/src/solana/utils.ts +635 -0
- package/src/types/account.ts +58 -0
- package/src/types/dapp.ts +7 -0
- package/src/types/gas.ts +53 -0
- package/src/types/index.ts +81 -0
- package/src/types/network.ts +66 -0
- package/src/types/tx.ts +181 -0
- package/src/types/wallet.ts +49 -0
- package/src/wallet.ts +96 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +18 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { ChainTypes } from "@tomo-inc/wallet-utils";
|
|
2
|
+
|
|
3
|
+
import { BaseService } from "../base/service";
|
|
4
|
+
|
|
5
|
+
import { BigNumber } from "bignumber.js";
|
|
6
|
+
import { DogecoinUtils } from "./utils-doge";
|
|
7
|
+
import { parseUnits, toHex } from "viem";
|
|
8
|
+
|
|
9
|
+
import { IaccountInfo, QueryGasParams, QueryGasResponse, TomoAppInfo } from "../types";
|
|
10
|
+
import { TransactionParams, DunesTransactionParams } from "./type";
|
|
11
|
+
|
|
12
|
+
import * as API from "./rpc";
|
|
13
|
+
import * as utils from "./utils";
|
|
14
|
+
import { DogeTxData, waitOnly } from "./utils";
|
|
15
|
+
|
|
16
|
+
import { BaseConfig } from "./config";
|
|
17
|
+
|
|
18
|
+
export class DogecoinService extends BaseService {
|
|
19
|
+
private static instance: DogecoinService;
|
|
20
|
+
public chainType: ChainTypes | "";
|
|
21
|
+
|
|
22
|
+
public constructor(chainType: ChainTypes, accountInfo: IaccountInfo, tomoAppInfo: TomoAppInfo) {
|
|
23
|
+
super(tomoAppInfo, accountInfo);
|
|
24
|
+
this.chainType = chainType;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//singleton
|
|
28
|
+
public static getInstance(chainType: ChainTypes, accountInfo: any, tomoAppInfo: TomoAppInfo) {
|
|
29
|
+
if (!DogecoinService.instance) {
|
|
30
|
+
DogecoinService.instance = new DogecoinService(chainType, accountInfo, tomoAppInfo);
|
|
31
|
+
}
|
|
32
|
+
return DogecoinService.instance;
|
|
33
|
+
}
|
|
34
|
+
public async requestAccounts(): Promise<{ address: string; balance: number; approved: boolean; publicKey: string }> {
|
|
35
|
+
const { currentAddress, publicKey = "" }: any = await this.accountInfo.getIds();
|
|
36
|
+
const { balance = 0 } = await API.getBalance(currentAddress);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
address: currentAddress,
|
|
40
|
+
balance,
|
|
41
|
+
approved: true,
|
|
42
|
+
publicKey,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public async getAccounts(): Promise<string[]> {
|
|
47
|
+
const { addresses = [] }: any = await this.accountInfo.getIds();
|
|
48
|
+
return addresses;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public async getConnectionStatus(): Promise<{ connected: boolean; address: string; selectedWalletAddress: string }> {
|
|
52
|
+
const { currentAddress }: any = await this.accountInfo.getIds();
|
|
53
|
+
return {
|
|
54
|
+
connected: true,
|
|
55
|
+
address: currentAddress,
|
|
56
|
+
selectedWalletAddress: currentAddress,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async getBalance(): Promise<{ address: string; balance: number }> {
|
|
61
|
+
const { currentAddress: address }: any = await this.accountInfo.getIds();
|
|
62
|
+
|
|
63
|
+
const { balance = 0 } = await API.getBalance(address);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
address,
|
|
67
|
+
balance,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public async signMessage({ text, type }: { text: string; type?: string }): Promise<{ signedMessage: string }> {
|
|
72
|
+
if (type !== "") {
|
|
73
|
+
throw new Error("bip322simple not support.");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const signature = await this.accountInfo.signMessage(text);
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
signedMessage: signature,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public async requestSignedMessage({
|
|
84
|
+
text,
|
|
85
|
+
type,
|
|
86
|
+
}: {
|
|
87
|
+
text: string;
|
|
88
|
+
type?: string;
|
|
89
|
+
}): Promise<{ signedMessage: string }> {
|
|
90
|
+
return await this.signMessage({
|
|
91
|
+
text,
|
|
92
|
+
type,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public async requestDecryptedMessage({ message }: { message: string }): Promise<{ decryptedMessage: string }> {
|
|
97
|
+
try {
|
|
98
|
+
const unsignRes = await this.accountInfo.decryptedMessage(message);
|
|
99
|
+
const { text = "" } = JSON.parse(unsignRes);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
decryptedMessage: text,
|
|
103
|
+
};
|
|
104
|
+
} catch (err) {
|
|
105
|
+
throw new Error("requestDecryptedMessage err:" + JSON.stringify(err));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public async _queryGasInfo(txData: TransactionParams): Promise<QueryGasResponse> {
|
|
110
|
+
const { chainId, amount = 0, decimals = 8 } = txData;
|
|
111
|
+
const gasLimitParam: any = {
|
|
112
|
+
from: txData.from,
|
|
113
|
+
to: txData.to,
|
|
114
|
+
value: toHex(parseUnits(amount.toString(), decimals)),
|
|
115
|
+
};
|
|
116
|
+
const queryGasParams: QueryGasParams = {
|
|
117
|
+
chainIndex: Number(chainId),
|
|
118
|
+
callData: "0x",
|
|
119
|
+
gasLimitParam,
|
|
120
|
+
addressList: [txData.from],
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const {
|
|
124
|
+
data: gasInfo,
|
|
125
|
+
success,
|
|
126
|
+
message,
|
|
127
|
+
}: any = await this.transactions.queryGasInfo({
|
|
128
|
+
chainType: this.chainType,
|
|
129
|
+
params: queryGasParams,
|
|
130
|
+
});
|
|
131
|
+
if (!success) {
|
|
132
|
+
console.error("queryGasInfo doge", success, txData, queryGasParams, gasInfo);
|
|
133
|
+
|
|
134
|
+
const { gasFee } = BaseConfig;
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
gasFee: gasFee.toString(),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const { baseFee = 1, gasLimit = 1 } = gasInfo;
|
|
142
|
+
const calcFee = (feeLevel: string) => {
|
|
143
|
+
const gasFee = new BigNumber(gasInfo[feeLevel] || baseFee).times(gasLimit);
|
|
144
|
+
return utils.toBitcoin(gasFee.toNumber());
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const fees = {
|
|
148
|
+
low: calcFee("priorityFeeLow"),
|
|
149
|
+
medium: calcFee("priorityFeeMedium"),
|
|
150
|
+
high: calcFee("priorityFeeHigh"),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
fees,
|
|
156
|
+
gasFee: fees.high,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public async requestPsbt({
|
|
161
|
+
rawTx,
|
|
162
|
+
signOnly,
|
|
163
|
+
}: {
|
|
164
|
+
rawTx: string;
|
|
165
|
+
signOnly?: boolean;
|
|
166
|
+
}): Promise<{ signedRawTx: string; txId?: string } | null> {
|
|
167
|
+
const params = {
|
|
168
|
+
type: "signRawTx",
|
|
169
|
+
data: {
|
|
170
|
+
walletId: -1,
|
|
171
|
+
rawTx,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
const signedRawTx = await this.accountInfo.signTransaction(JSON.stringify(params));
|
|
175
|
+
|
|
176
|
+
if (!signedRawTx) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (signOnly) {
|
|
181
|
+
return {
|
|
182
|
+
signedRawTx,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const { txid = "" } = await API.sendDogeTx(signedRawTx);
|
|
187
|
+
return {
|
|
188
|
+
txId: txid,
|
|
189
|
+
signedRawTx,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public async requestTransaction(txData: DogeTxData): Promise<{ txId: string } | null> {
|
|
194
|
+
//txData = createTx;
|
|
195
|
+
const spendableUtxos = txData?.spendableUtxos;
|
|
196
|
+
if (txData.amountMismatch) {
|
|
197
|
+
throw new Error("balance_insufficient");
|
|
198
|
+
}
|
|
199
|
+
if (!spendableUtxos || spendableUtxos?.length === 0) {
|
|
200
|
+
txData.spendableUtxos = await API.getSpendableUtxos(txData.from);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let signedTx = "";
|
|
204
|
+
const _UsingUtxos = {};
|
|
205
|
+
const dogeTxParams = await utils.createPsbt(txData as any);
|
|
206
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify(dogeTxParams));
|
|
207
|
+
signedTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 100000);
|
|
208
|
+
|
|
209
|
+
if (signedTx === "") {
|
|
210
|
+
throw new Error("Error: sign transaction err.");
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const { txid } = await API.sendDogeTx(signedTx);
|
|
214
|
+
utils.addUsedUtxos(_UsingUtxos);
|
|
215
|
+
return { txId: txid };
|
|
216
|
+
} catch (err) {
|
|
217
|
+
throw new Error("Error: send transaction err." + JSON.stringify(err));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
public async getTransactionStatus({ txId }: { txId: string }): Promise<any> {
|
|
222
|
+
const transaction = await API.getTxDetail(txId);
|
|
223
|
+
if (!transaction?.txid) {
|
|
224
|
+
return null as any;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
txId: transaction.txid,
|
|
229
|
+
confirmations: transaction.confirmations,
|
|
230
|
+
status: transaction.confirmations > 0 ? "confirmed" : "pending",
|
|
231
|
+
dogeAmount: transaction.vout[0]?.value,
|
|
232
|
+
blockTime: transaction.blockTime,
|
|
233
|
+
address: transaction.vout[0]?.addresses[0],
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public async getDRC20Balance(params: {
|
|
238
|
+
ticker: string;
|
|
239
|
+
}): Promise<{ availableBalance: number; transferableBalance: number; ticker: string; address: string }> {
|
|
240
|
+
const { currentAddress: address }: any = await this.accountInfo.getIds();
|
|
241
|
+
const { ticker } = params;
|
|
242
|
+
|
|
243
|
+
const { balances = [] } = await API.getDRC20Balance(address, ticker);
|
|
244
|
+
|
|
245
|
+
let availableBalance = 0;
|
|
246
|
+
let transferableBalance = 0;
|
|
247
|
+
if (balances.length) {
|
|
248
|
+
availableBalance = balances[0].availableBalance;
|
|
249
|
+
transferableBalance = balances[0].transferableBalance;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
availableBalance,
|
|
254
|
+
transferableBalance,
|
|
255
|
+
ticker,
|
|
256
|
+
address,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public async getTransferableDRC20(params: {
|
|
261
|
+
ticker: string;
|
|
262
|
+
}): Promise<{ inscriptions: any[]; ticker: string; address: string }> {
|
|
263
|
+
const { currentAddress: address }: any = await this.accountInfo.getIds();
|
|
264
|
+
const { ticker } = params;
|
|
265
|
+
|
|
266
|
+
const query = await API.getDRC20Inscriptions(address, ticker);
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
inscriptions: query.list || [],
|
|
270
|
+
ticker: ticker,
|
|
271
|
+
address: address,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public async requestAvailableDRC20Transaction({
|
|
276
|
+
ticker,
|
|
277
|
+
amount,
|
|
278
|
+
}: {
|
|
279
|
+
ticker: string;
|
|
280
|
+
amount: number;
|
|
281
|
+
}): Promise<{ txId: string; ticker: string; amount: number }> {
|
|
282
|
+
const inscription = `{"p":"drc-20","op":"transfer","tick":"${ticker}","amt":"${amount}"}`;
|
|
283
|
+
let params = null;
|
|
284
|
+
let usingUtxos = null;
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
const { currentAddress: senderAddress }: any = await this.accountInfo.getIds();
|
|
288
|
+
const utxos = await API.getSpendableUtxos(senderAddress);
|
|
289
|
+
const res = utils.createDrc20Data(utxos, senderAddress, inscription);
|
|
290
|
+
params = res.params;
|
|
291
|
+
usingUtxos = res.usingUtxos || {};
|
|
292
|
+
} catch (err) {
|
|
293
|
+
throw new Error("Error: sign transaction err." + JSON.stringify(err));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const signedTxs = await this.accountInfo.signTransaction(params);
|
|
297
|
+
const { commitTx = "", revealTxs = [] } = signedTxs;
|
|
298
|
+
const revealTx = revealTxs[0] || "";
|
|
299
|
+
if (commitTx === "" || revealTx === "") {
|
|
300
|
+
throw new Error("Error: sign transaction err.");
|
|
301
|
+
}
|
|
302
|
+
const { txid: commitTxId } = await API.sendDogeTx(commitTx);
|
|
303
|
+
await waitOnly(0.3 * 1000); //ms
|
|
304
|
+
const { txid: revealTxId } = await API.sendDogeTx(revealTx);
|
|
305
|
+
if (revealTxId && commitTxId) {
|
|
306
|
+
utils.addUsedUtxos(usingUtxos);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
txId: revealTxId,
|
|
311
|
+
ticker,
|
|
312
|
+
amount,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
public async getInscriptionTransactionDetail(params: { recipientAddress: string; location: string }): Promise<{
|
|
317
|
+
senderAddress: string;
|
|
318
|
+
inscription: any;
|
|
319
|
+
inputTxid: string;
|
|
320
|
+
fee: number;
|
|
321
|
+
amount: number;
|
|
322
|
+
rawTx: string;
|
|
323
|
+
} | null> {
|
|
324
|
+
const { currentAddress: senderAddress }: any = await this.accountInfo.getIds();
|
|
325
|
+
const { recipientAddress, location } = params;
|
|
326
|
+
|
|
327
|
+
const locations: string[] = location.split(":");
|
|
328
|
+
const inputTxid: string = locations[0] || "";
|
|
329
|
+
const vout = Number(locations[1] || 0);
|
|
330
|
+
const offset = Number(locations[2] || 0);
|
|
331
|
+
|
|
332
|
+
if (inputTxid?.length !== 64 || Number.isNaN(vout)) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const res = await API.getInscriptionsUtxo(senderAddress, {
|
|
337
|
+
txid: inputTxid,
|
|
338
|
+
vout,
|
|
339
|
+
});
|
|
340
|
+
const { inscriptions = [] } = res || {};
|
|
341
|
+
|
|
342
|
+
if (inscriptions.length === 0) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const inscription = inscriptions?.find((i: any) => i.offset === offset);
|
|
347
|
+
const { rawTx, fee, amount }: any = await API.createNFTTransaction({
|
|
348
|
+
senderAddress,
|
|
349
|
+
recipientAddress,
|
|
350
|
+
location,
|
|
351
|
+
inscriptionId: inscription.inscription_id,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
senderAddress,
|
|
356
|
+
inscription,
|
|
357
|
+
inputTxid,
|
|
358
|
+
fee,
|
|
359
|
+
amount,
|
|
360
|
+
rawTx,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
public async requestInscriptionTransaction(params: {
|
|
365
|
+
recipientAddress: string;
|
|
366
|
+
location: string;
|
|
367
|
+
}): Promise<{ txId: string }> {
|
|
368
|
+
const { currentAddress: senderAddress }: any = await this.accountInfo.getIds();
|
|
369
|
+
const { recipientAddress, location } = params;
|
|
370
|
+
|
|
371
|
+
const locations: string[] = location.split(":");
|
|
372
|
+
const inputTxid: string = locations[0] || "";
|
|
373
|
+
|
|
374
|
+
const detail = await this.getInscriptionTransactionDetail(params);
|
|
375
|
+
if (!detail) {
|
|
376
|
+
throw new Error("Error: location not found");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const { fee, amount }: any = detail || {};
|
|
380
|
+
|
|
381
|
+
const utxos = await API.getSpendableUtxos(senderAddress);
|
|
382
|
+
const { psbtBase64, usingUtxos } = await utils.createInscriptionPsbt({
|
|
383
|
+
inscriptionId: inputTxid,
|
|
384
|
+
senderAddress,
|
|
385
|
+
recipientAddress,
|
|
386
|
+
utxos,
|
|
387
|
+
fee,
|
|
388
|
+
amount,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const signedRawTx = await this.accountInfo.signTransaction({
|
|
392
|
+
type: 2,
|
|
393
|
+
psbt: psbtBase64,
|
|
394
|
+
});
|
|
395
|
+
const tx = DogecoinUtils.extractPsbtTransaction(signedRawTx, 100000);
|
|
396
|
+
|
|
397
|
+
const { txid } = await API.sendDogeTx(tx);
|
|
398
|
+
|
|
399
|
+
if (txid) {
|
|
400
|
+
utils.addUsedUtxos(usingUtxos);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
txId: txid,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public async getDunesBalance(params?: {
|
|
409
|
+
ticker: string;
|
|
410
|
+
}): Promise<{ balance: number; ticker: string; address: string }> {
|
|
411
|
+
const { ticker = "" } = params || {};
|
|
412
|
+
|
|
413
|
+
if (!ticker) {
|
|
414
|
+
throw new Error("Error: ticker is required");
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const { currentAddress: address }: any = await this.accountInfo.getIds();
|
|
418
|
+
const { balances = [] } = await API.getDunesBalance(address, ticker);
|
|
419
|
+
|
|
420
|
+
let balance = 0;
|
|
421
|
+
if (balances.length) {
|
|
422
|
+
balance = balances[0].overallBalance;
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
balance,
|
|
426
|
+
ticker,
|
|
427
|
+
address,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
public async requestDunesTransaction(params: DunesTransactionParams): Promise<{ txId: string }> {
|
|
432
|
+
const { currentAddress: senderAddress }: any = await this.accountInfo.getIds();
|
|
433
|
+
const { rawTx: rawTransaction, inputs }: any = await API.createDunesTransaction({ ...params, senderAddress });
|
|
434
|
+
|
|
435
|
+
//utxo
|
|
436
|
+
|
|
437
|
+
const signParams = {
|
|
438
|
+
rawTransaction,
|
|
439
|
+
};
|
|
440
|
+
const signedRawTx = await this.accountInfo.signTransaction(signParams);
|
|
441
|
+
|
|
442
|
+
if (!signedRawTx) {
|
|
443
|
+
throw new Error("Error: sign transaction err.");
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const { txid = "" } = await API.sendDogeTx(signedRawTx);
|
|
447
|
+
return {
|
|
448
|
+
txId: txid,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface DunesTransactionParams {
|
|
2
|
+
senderAddress?: string;
|
|
3
|
+
recipientAddress: string;
|
|
4
|
+
amount: number;
|
|
5
|
+
ticker: string;
|
|
6
|
+
duneId?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type DogeSpendableUtxos = {
|
|
10
|
+
txid: string;
|
|
11
|
+
vout: number;
|
|
12
|
+
outputValue: number;
|
|
13
|
+
script: string;
|
|
14
|
+
tx?: {
|
|
15
|
+
txid: string;
|
|
16
|
+
vout: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export interface TransactionParams {
|
|
21
|
+
accountId?: string;
|
|
22
|
+
from: string;
|
|
23
|
+
to: string;
|
|
24
|
+
fee?: string;
|
|
25
|
+
amount: number;
|
|
26
|
+
decimals?: number;
|
|
27
|
+
chainId?: number | string;
|
|
28
|
+
tokenAddress?: string;
|
|
29
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { address as AddressLib, Psbt } from "bitcoinjs-lib";
|
|
2
|
+
import * as base from "./base";
|
|
3
|
+
import { network } from "./config";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* UTXO Transaction type definition
|
|
7
|
+
* Replaces the utxoTx type from @okxweb3/coin-bitcoin
|
|
8
|
+
*/
|
|
9
|
+
export interface utxoTx {
|
|
10
|
+
address: string;
|
|
11
|
+
inputs: Array<{
|
|
12
|
+
txId: string;
|
|
13
|
+
vOut: number;
|
|
14
|
+
amount: number;
|
|
15
|
+
nonWitnessUtxo?: string; // hex string
|
|
16
|
+
address?: string;
|
|
17
|
+
}>;
|
|
18
|
+
outputs: Array<{
|
|
19
|
+
address: string;
|
|
20
|
+
amount: number;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class DogecoinAddress {
|
|
25
|
+
static network = network;
|
|
26
|
+
|
|
27
|
+
static generatePath(addressIndex: number): string {
|
|
28
|
+
return `m/44'/3'/0'/0/${addressIndex || 0}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static verifyAddress(address: string): boolean {
|
|
32
|
+
try {
|
|
33
|
+
// Use bitcoinjs-lib address decoding
|
|
34
|
+
const decoded = AddressLib.fromBase58Check(address);
|
|
35
|
+
return decoded.version === network.pubKeyHash || decoded.version === network.scriptHash;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class DogecoinUtils {
|
|
43
|
+
/**
|
|
44
|
+
* Build the PSBT from the transaction
|
|
45
|
+
* @param tx utxoTx
|
|
46
|
+
* @param _maximumFeeRate number (optional, currently unused)
|
|
47
|
+
* @returns base64
|
|
48
|
+
*/
|
|
49
|
+
static buildPsbtToBase64(tx: utxoTx, _maximumFeeRate?: number): string {
|
|
50
|
+
const psbt = new Psbt({ network: network });
|
|
51
|
+
|
|
52
|
+
// Add inputs
|
|
53
|
+
for (const input of tx.inputs) {
|
|
54
|
+
const inputOptions: any = {
|
|
55
|
+
hash: input.txId,
|
|
56
|
+
index: input.vOut,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// If nonWitnessUtxo (full transaction hex) exists, add it
|
|
60
|
+
if (input.nonWitnessUtxo) {
|
|
61
|
+
inputOptions.nonWitnessUtxo = Buffer.from(input.nonWitnessUtxo, "hex");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
psbt.addInput(inputOptions);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add outputs
|
|
68
|
+
for (const output of tx.outputs) {
|
|
69
|
+
psbt.addOutput({
|
|
70
|
+
address: output.address,
|
|
71
|
+
value: BigInt(output.amount),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Note: Fee rate check may be inaccurate at build stage since input values may not be fully determined
|
|
76
|
+
// For strict fee rate checking, it should be done after signing (extractPsbtTransaction)
|
|
77
|
+
|
|
78
|
+
// Convert to base64
|
|
79
|
+
const psbtHex = psbt.toHex();
|
|
80
|
+
return base.toBase64(base.fromHex(psbtHex));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Extract the transaction from the signed PSBT
|
|
85
|
+
* @param signedPsbt base64
|
|
86
|
+
* @param _maximumFeeRate number (optional, currently unused)
|
|
87
|
+
* @returns transaction hex
|
|
88
|
+
*/
|
|
89
|
+
static extractPsbtTransaction(signedPsbt: string, _maximumFeeRate?: number): string {
|
|
90
|
+
const signedPsbtHex = base.toHex(base.fromBase64(signedPsbt));
|
|
91
|
+
|
|
92
|
+
// Create PSBT from hex
|
|
93
|
+
const psbt = Psbt.fromHex(signedPsbtHex, {
|
|
94
|
+
network,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Note: Fee rate check may be inaccurate when extracting transaction
|
|
98
|
+
// For strict fee rate checking, it should be calculated manually after extracting the transaction
|
|
99
|
+
|
|
100
|
+
// Finalize all inputs and extract transaction
|
|
101
|
+
psbt.finalizeAllInputs();
|
|
102
|
+
const transaction = psbt.extractTransaction();
|
|
103
|
+
return transaction.toHex();
|
|
104
|
+
}
|
|
105
|
+
}
|