@tomo-inc/chains-service 0.0.7 → 0.0.8
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 +4 -0
- package/dist/index.cjs +602 -95
- package/dist/index.d.cts +117 -43
- package/dist/index.d.ts +117 -43
- package/dist/index.js +608 -102
- package/package.json +2 -2
- package/src/api/network-data.ts +19 -5
- package/src/api/token.ts +11 -6
- package/src/base/service.ts +4 -4
- package/src/base/token.ts +8 -9
- package/src/dogecoin/service.ts +46 -252
- package/src/dogecoin/type.ts +12 -0
- package/src/dogecoin/utils-doge.ts +75 -2
- package/src/dogecoin/utils.ts +3 -380
- package/src/evm/service.ts +57 -73
- package/src/index.ts +5 -4
- package/src/solana/service.ts +6 -6
- package/src/types/account.ts +4 -3
- package/src/types/index.ts +3 -3
- package/src/utils/index.ts +7 -0
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.8",
|
|
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.7"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/supertest": "^2.0.12",
|
package/src/api/network-data.ts
CHANGED
|
@@ -30,12 +30,26 @@ export const loadNetworks = (WALLET_DOMAIN: string) => {
|
|
|
30
30
|
supportHistory: false,
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
|
-
chainId:
|
|
34
|
-
chainIndex:
|
|
35
|
-
name: "
|
|
36
|
-
chainName: "
|
|
33
|
+
chainId: 26888,
|
|
34
|
+
chainIndex: 2688800,
|
|
35
|
+
name: "ABCORE_TESTNET",
|
|
36
|
+
chainName: "AB Core Testnet",
|
|
37
|
+
rpcUrls: ["https://rpc.core.testnet.ab.org"],
|
|
38
|
+
blockExplorerUrl: "https://explorer.core.testnet.ab.org",
|
|
39
|
+
platformType: "EVM",
|
|
40
|
+
isTestnet: true,
|
|
41
|
+
icon: "/assets/ab.svg",
|
|
42
|
+
supportSwap: true,
|
|
43
|
+
supportGift: false,
|
|
44
|
+
supportHistory: true,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
chainId: 6281971,
|
|
48
|
+
chainIndex: 628197100,
|
|
49
|
+
name: "DOGEOS_TESTNET",
|
|
50
|
+
chainName: "DogeOS Testnet",
|
|
37
51
|
rpcUrls: [`${WALLET_DOMAIN}/rpc/v1/doge_test`],
|
|
38
|
-
blockExplorerUrl: "https://
|
|
52
|
+
blockExplorerUrl: "https://rpc.testnet.dogeos.com",
|
|
39
53
|
platformType: "EVM",
|
|
40
54
|
isTestnet: true,
|
|
41
55
|
icon: "/assets/dogeos.svg",
|
package/src/api/token.ts
CHANGED
|
@@ -43,11 +43,16 @@ export class TokenAPIs extends BasePublicService {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
async getTokenRisk(params:
|
|
47
|
-
if (typeof params.chainIndex !== "number" || !params.
|
|
46
|
+
async getTokenRisk(params: GetTokenInfoParams): Promise<RemoteResponse<any>> {
|
|
47
|
+
if (typeof params.chainIndex !== "number" || !params.address) {
|
|
48
48
|
throw new Error("chainName or tokenAddress is required");
|
|
49
49
|
}
|
|
50
|
-
const res = await this.tokenApi.post("/v1/market/risk/details", [
|
|
50
|
+
const res = await this.tokenApi.post("/v1/market/risk/details", [
|
|
51
|
+
{
|
|
52
|
+
chainIndex: params.chainIndex,
|
|
53
|
+
tokenAddress: params.address,
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
51
56
|
return {
|
|
52
57
|
success: res?.data?.code === "0",
|
|
53
58
|
message: res?.data?.msg,
|
|
@@ -142,13 +147,13 @@ export class TokenAPIs extends BasePublicService {
|
|
|
142
147
|
}
|
|
143
148
|
}
|
|
144
149
|
|
|
145
|
-
async getTokenDetail(params:
|
|
150
|
+
async getTokenDetail(params: GetTokenInfoParams): Promise<RemoteResponse<any>> {
|
|
146
151
|
//bad design, should: chainId + tokenAddress
|
|
147
|
-
const { chainIndex,
|
|
152
|
+
const { chainIndex, address } = params;
|
|
148
153
|
|
|
149
154
|
const res = await this.tokenApi.get(`/v1/market/token/detail`, {
|
|
150
155
|
params: {
|
|
151
|
-
tokenAddress,
|
|
156
|
+
tokenAddress: address,
|
|
152
157
|
chainIndex,
|
|
153
158
|
},
|
|
154
159
|
});
|
package/src/base/service.ts
CHANGED
|
@@ -4,23 +4,23 @@ import { Transactions } from "./transaction";
|
|
|
4
4
|
|
|
5
5
|
import { CONFIG } from "../config";
|
|
6
6
|
import { tomoPublicApiService } from "../api";
|
|
7
|
-
import { TomoAppInfo } from "../types";
|
|
7
|
+
import { IAccountInfo, TomoAppInfo } from "../types";
|
|
8
8
|
|
|
9
9
|
export class BaseService {
|
|
10
10
|
public isDappConnected: boolean;
|
|
11
11
|
public approveParams: any;
|
|
12
|
-
public accountInfo:
|
|
12
|
+
public accountInfo: IAccountInfo;
|
|
13
13
|
public networks: Networks;
|
|
14
14
|
public tokens: Tokens;
|
|
15
15
|
public transactions: Transactions;
|
|
16
16
|
|
|
17
|
-
public constructor(tomoAppInfo: TomoAppInfo, accountInfo?:
|
|
17
|
+
public constructor(tomoAppInfo: TomoAppInfo, accountInfo?: IAccountInfo) {
|
|
18
18
|
if (!tomoAppInfo.tomoStage || !CONFIG[tomoAppInfo.tomoStage]) {
|
|
19
19
|
throw new Error("Tomo stage is required");
|
|
20
20
|
}
|
|
21
21
|
const baseUrlConfig = CONFIG[tomoAppInfo.tomoStage];
|
|
22
22
|
const { tokenAPIs, transactionAPIs, networkAPIs } = tomoPublicApiService(baseUrlConfig, tomoAppInfo);
|
|
23
|
-
this.accountInfo = accountInfo;
|
|
23
|
+
this.accountInfo = (accountInfo || null) as IAccountInfo;
|
|
24
24
|
this.networks = new Networks(networkAPIs);
|
|
25
25
|
this.tokens = new Tokens(tokenAPIs);
|
|
26
26
|
this.transactions = new Transactions(transactionAPIs);
|
package/src/base/token.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//types
|
|
2
2
|
import { TokenInfo } from "../types";
|
|
3
3
|
import { TokenAPIs } from "../api/token";
|
|
4
|
+
import { GetTokenInfoParams } from "../api/types";
|
|
4
5
|
|
|
5
6
|
export class Tokens {
|
|
6
7
|
public tokenAPIs: TokenAPIs;
|
|
@@ -9,8 +10,8 @@ export class Tokens {
|
|
|
9
10
|
this.tokenAPIs = tokenService;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
public async searchTokens({ chainIndex,
|
|
13
|
-
const { data: tokens = [] }: any = await this.tokenAPIs.queryRemoteTokens({ keyword
|
|
13
|
+
public async searchTokens({ chainIndex, keyword }: { chainIndex?: number; keyword: string }): Promise<TokenInfo[]> {
|
|
14
|
+
const { data: tokens = [] }: any = await this.tokenAPIs.queryRemoteTokens({ keyword, chainIndex });
|
|
14
15
|
return tokens;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -19,19 +20,17 @@ export class Tokens {
|
|
|
19
20
|
// return tokenInfoData;
|
|
20
21
|
// }
|
|
21
22
|
|
|
22
|
-
public async getTokenInfo({ address, chainIndex }:
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
}: any = await this.tokenAPIs.getTokenInfo({ address, chainIndex });
|
|
26
|
-
return tokenInfo;
|
|
23
|
+
public async getTokenInfo({ address, chainIndex }: GetTokenInfoParams): Promise<TokenInfo> {
|
|
24
|
+
const res: any = await this.tokenAPIs.getTokenInfo({ address, chainIndex });
|
|
25
|
+
return res?.data?.data;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
public async getTokenRiskInfo(_params:
|
|
28
|
+
public async getTokenRiskInfo(_params: GetTokenInfoParams): Promise<any> {
|
|
30
29
|
const { data: tokenRiskInfo }: any = await this.tokenAPIs.getTokenRisk(_params);
|
|
31
30
|
return tokenRiskInfo;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
public async getTokenDetail(_params:
|
|
33
|
+
public async getTokenDetail(_params: GetTokenInfoParams): Promise<any> {
|
|
35
34
|
const { data }: any = await this.tokenAPIs.getTokenDetail(_params);
|
|
36
35
|
return data;
|
|
37
36
|
}
|
package/src/dogecoin/service.ts
CHANGED
|
@@ -6,37 +6,42 @@ import { BigNumber } from "bignumber.js";
|
|
|
6
6
|
import { DogecoinUtils } from "./utils-doge";
|
|
7
7
|
import { parseUnits, toHex } from "viem";
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import { TransactionParams
|
|
9
|
+
import { IAccountInfo, QueryGasParams, QueryGasResponse, TomoAppInfo } from "../types";
|
|
10
|
+
import { TransactionParams } from "./type";
|
|
11
11
|
|
|
12
12
|
import * as API from "./rpc";
|
|
13
13
|
import * as utils from "./utils";
|
|
14
|
-
import { DogeTxData
|
|
14
|
+
import { DogeTxData } from "./type";
|
|
15
15
|
|
|
16
16
|
import { BaseConfig } from "./config";
|
|
17
|
+
import { createPsbt, addUsedUtxos } from "./utils";
|
|
17
18
|
|
|
18
19
|
export class DogecoinService extends BaseService {
|
|
19
20
|
private static instance: DogecoinService;
|
|
20
21
|
public chainType: ChainTypes | "";
|
|
21
22
|
|
|
22
|
-
public constructor(chainType: ChainTypes, accountInfo:
|
|
23
|
+
public constructor(chainType: ChainTypes, accountInfo: IAccountInfo, tomoAppInfo: TomoAppInfo) {
|
|
23
24
|
super(tomoAppInfo, accountInfo);
|
|
24
25
|
this.chainType = chainType;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
//singleton
|
|
28
|
-
public static getInstance(chainType: ChainTypes, accountInfo:
|
|
29
|
+
public static getInstance(chainType: ChainTypes, accountInfo: IAccountInfo, tomoAppInfo: TomoAppInfo) {
|
|
29
30
|
if (!DogecoinService.instance) {
|
|
30
31
|
DogecoinService.instance = new DogecoinService(chainType, accountInfo, tomoAppInfo);
|
|
31
32
|
}
|
|
32
33
|
return DogecoinService.instance;
|
|
33
34
|
}
|
|
34
35
|
public async requestAccounts(): Promise<{ address: string; balance: number; approved: boolean; publicKey: string }> {
|
|
35
|
-
const
|
|
36
|
-
const {
|
|
36
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
37
|
+
const { publicKey, address } = addresses?.[0] || {};
|
|
38
|
+
if (!address) {
|
|
39
|
+
throw new Error("address is not set");
|
|
40
|
+
}
|
|
41
|
+
const { balance = 0 } = await API.getBalance(address);
|
|
37
42
|
|
|
38
43
|
return {
|
|
39
|
-
address
|
|
44
|
+
address,
|
|
40
45
|
balance,
|
|
41
46
|
approved: true,
|
|
42
47
|
publicKey,
|
|
@@ -44,21 +49,29 @@ export class DogecoinService extends BaseService {
|
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
public async getAccounts(): Promise<string[]> {
|
|
47
|
-
const
|
|
48
|
-
return
|
|
52
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
53
|
+
return accounts.map((account: { address: string }) => account.address);
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
public async getConnectionStatus(): Promise<{ connected: boolean; address: string; selectedWalletAddress: string }> {
|
|
52
|
-
const
|
|
57
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
58
|
+
const { address } = addresses?.[0] || {};
|
|
59
|
+
if (!address) {
|
|
60
|
+
throw new Error("address is not set");
|
|
61
|
+
}
|
|
53
62
|
return {
|
|
54
63
|
connected: true,
|
|
55
|
-
address
|
|
56
|
-
selectedWalletAddress:
|
|
64
|
+
address,
|
|
65
|
+
selectedWalletAddress: address,
|
|
57
66
|
};
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
public async getBalance(): Promise<{ address: string; balance: number }> {
|
|
61
|
-
const
|
|
70
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
71
|
+
const { address } = addresses?.[0] || {};
|
|
72
|
+
if (!address) {
|
|
73
|
+
throw new Error("address is not set");
|
|
74
|
+
}
|
|
62
75
|
|
|
63
76
|
const { balance = 0 } = await API.getBalance(address);
|
|
64
77
|
|
|
@@ -68,12 +81,12 @@ export class DogecoinService extends BaseService {
|
|
|
68
81
|
};
|
|
69
82
|
}
|
|
70
83
|
|
|
71
|
-
public async signMessage({
|
|
72
|
-
if (type
|
|
84
|
+
public async signMessage({ message, type }: { message: string; type?: string }): Promise<{ signedMessage: string }> {
|
|
85
|
+
if (type) {
|
|
73
86
|
throw new Error("bip322simple not support.");
|
|
74
87
|
}
|
|
75
88
|
|
|
76
|
-
const signature = await this.accountInfo.signMessage(
|
|
89
|
+
const signature = await this.accountInfo.signMessage(message);
|
|
77
90
|
|
|
78
91
|
return {
|
|
79
92
|
signedMessage: signature,
|
|
@@ -81,21 +94,24 @@ export class DogecoinService extends BaseService {
|
|
|
81
94
|
}
|
|
82
95
|
|
|
83
96
|
public async requestSignedMessage({
|
|
84
|
-
|
|
97
|
+
message,
|
|
85
98
|
type,
|
|
86
99
|
}: {
|
|
87
|
-
|
|
100
|
+
message: string;
|
|
88
101
|
type?: string;
|
|
89
102
|
}): Promise<{ signedMessage: string }> {
|
|
90
103
|
return await this.signMessage({
|
|
91
|
-
|
|
104
|
+
message,
|
|
92
105
|
type,
|
|
93
106
|
});
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
public async requestDecryptedMessage({ message }: { message: string }): Promise<{ decryptedMessage: string }> {
|
|
97
110
|
try {
|
|
98
|
-
|
|
111
|
+
if (!this.accountInfo?.decryptedMessage) {
|
|
112
|
+
throw new Error("decryptedMessage is not supported");
|
|
113
|
+
}
|
|
114
|
+
const unsignRes = await this.accountInfo?.decryptedMessage(message);
|
|
99
115
|
const { text = "" } = JSON.parse(unsignRes);
|
|
100
116
|
|
|
101
117
|
return {
|
|
@@ -114,7 +130,7 @@ export class DogecoinService extends BaseService {
|
|
|
114
130
|
value: toHex(parseUnits(amount.toString(), decimals)),
|
|
115
131
|
};
|
|
116
132
|
const queryGasParams: QueryGasParams = {
|
|
117
|
-
chainIndex: Number(chainId),
|
|
133
|
+
chainIndex: Number(chainId || BaseConfig.chainId),
|
|
118
134
|
callData: "0x",
|
|
119
135
|
gasLimitParam,
|
|
120
136
|
addressList: [txData.from],
|
|
@@ -164,19 +180,14 @@ export class DogecoinService extends BaseService {
|
|
|
164
180
|
rawTx: string;
|
|
165
181
|
signOnly?: boolean;
|
|
166
182
|
}): Promise<{ signedRawTx: string; txId?: string } | null> {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
data: {
|
|
170
|
-
walletId: -1,
|
|
171
|
-
rawTx,
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
const signedRawTx = await this.accountInfo.signTransaction(JSON.stringify(params));
|
|
183
|
+
const psbtBase64 = await DogecoinUtils.rawTxToPsbtBase64(rawTx);
|
|
184
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
175
185
|
|
|
176
|
-
if (!
|
|
186
|
+
if (!signedPsbt) {
|
|
177
187
|
return null;
|
|
178
188
|
}
|
|
179
189
|
|
|
190
|
+
const signedRawTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 100000);
|
|
180
191
|
if (signOnly) {
|
|
181
192
|
return {
|
|
182
193
|
signedRawTx,
|
|
@@ -200,18 +211,16 @@ export class DogecoinService extends BaseService {
|
|
|
200
211
|
txData.spendableUtxos = await API.getSpendableUtxos(txData.from);
|
|
201
212
|
}
|
|
202
213
|
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify(dogeTxParams));
|
|
207
|
-
signedTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 100000);
|
|
214
|
+
const { psbtBase64, usingUtxos } = await createPsbt(txData as any);
|
|
215
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
216
|
+
const signedTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 100000);
|
|
208
217
|
|
|
209
218
|
if (signedTx === "") {
|
|
210
219
|
throw new Error("Error: sign transaction err.");
|
|
211
220
|
}
|
|
212
221
|
try {
|
|
213
222
|
const { txid } = await API.sendDogeTx(signedTx);
|
|
214
|
-
|
|
223
|
+
addUsedUtxos(usingUtxos);
|
|
215
224
|
return { txId: txid };
|
|
216
225
|
} catch (err) {
|
|
217
226
|
throw new Error("Error: send transaction err." + JSON.stringify(err));
|
|
@@ -233,219 +242,4 @@ export class DogecoinService extends BaseService {
|
|
|
233
242
|
address: transaction.vout[0]?.addresses[0],
|
|
234
243
|
};
|
|
235
244
|
}
|
|
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
245
|
}
|
package/src/dogecoin/type.ts
CHANGED
|
@@ -27,3 +27,15 @@ export interface TransactionParams {
|
|
|
27
27
|
chainId?: number | string;
|
|
28
28
|
tokenAddress?: string;
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
export interface DogeTxData {
|
|
32
|
+
amountMismatch?: boolean;
|
|
33
|
+
amount: number;
|
|
34
|
+
fee: number;
|
|
35
|
+
to: string;
|
|
36
|
+
from: string;
|
|
37
|
+
senderAddress?: string;
|
|
38
|
+
sender?: string;
|
|
39
|
+
fromAddress?: string;
|
|
40
|
+
spendableUtxos?: DogeSpendableUtxos[];
|
|
41
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { address as AddressLib, Psbt } from "bitcoinjs-lib";
|
|
1
|
+
import { address as AddressLib, Psbt, Transaction } from "bitcoinjs-lib";
|
|
2
2
|
import * as base from "./base";
|
|
3
3
|
import { network } from "./config";
|
|
4
4
|
|
|
@@ -67,8 +67,8 @@ export class DogecoinUtils {
|
|
|
67
67
|
// Add outputs
|
|
68
68
|
for (const output of tx.outputs) {
|
|
69
69
|
psbt.addOutput({
|
|
70
|
+
value: output.amount,
|
|
70
71
|
address: output.address,
|
|
71
|
-
value: BigInt(output.amount),
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -94,6 +94,9 @@ export class DogecoinUtils {
|
|
|
94
94
|
network,
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
const maximumFeeRate = _maximumFeeRate || 100000;
|
|
98
|
+
psbt.setMaximumFeeRate(maximumFeeRate);
|
|
99
|
+
|
|
97
100
|
// Note: Fee rate check may be inaccurate when extracting transaction
|
|
98
101
|
// For strict fee rate checking, it should be calculated manually after extracting the transaction
|
|
99
102
|
|
|
@@ -102,4 +105,74 @@ export class DogecoinUtils {
|
|
|
102
105
|
const transaction = psbt.extractTransaction();
|
|
103
106
|
return transaction.toHex();
|
|
104
107
|
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Convert raw transaction hex to PSBT base64
|
|
111
|
+
* This method converts an unsigned raw transaction to PSBT format.
|
|
112
|
+
* Note: The rawTx should be an unsigned transaction, and the nonWitnessUtxo
|
|
113
|
+
* for each input will need to be fetched separately from the blockchain.
|
|
114
|
+
* @param rawTx hex string of the raw transaction
|
|
115
|
+
* @returns base64 string of the PSBT
|
|
116
|
+
*/
|
|
117
|
+
static async rawTxToPsbtBase64(rawTx: string): Promise<string> {
|
|
118
|
+
try {
|
|
119
|
+
// Clean the hex string (remove 0x prefix if present)
|
|
120
|
+
const cleanHex = rawTx.startsWith("0x") ? rawTx.slice(2) : rawTx;
|
|
121
|
+
|
|
122
|
+
// Parse the raw transaction
|
|
123
|
+
const transaction = Transaction.fromHex(cleanHex);
|
|
124
|
+
|
|
125
|
+
// Create a new PSBT
|
|
126
|
+
const psbt = new Psbt({ network });
|
|
127
|
+
|
|
128
|
+
// Add inputs from the transaction
|
|
129
|
+
// Each input references a previous transaction's output
|
|
130
|
+
for (let i = 0; i < transaction.ins.length; i++) {
|
|
131
|
+
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");
|
|
134
|
+
const index = input.index;
|
|
135
|
+
|
|
136
|
+
// Add input without nonWitnessUtxo initially
|
|
137
|
+
// The nonWitnessUtxo (full previous transaction) should be added separately
|
|
138
|
+
// if needed for signing. For now, we create the PSBT structure.
|
|
139
|
+
const inputOptions: any = {
|
|
140
|
+
hash,
|
|
141
|
+
index,
|
|
142
|
+
nonWitnessUtxo: input.script,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// If the transaction is unsigned and we have the script, we can add it
|
|
146
|
+
// Otherwise, nonWitnessUtxo will need to be fetched from RPC
|
|
147
|
+
psbt.addInput(inputOptions);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Add outputs from the transaction
|
|
151
|
+
for (const output of transaction.outs) {
|
|
152
|
+
// Extract address and value from the output script
|
|
153
|
+
try {
|
|
154
|
+
const address = AddressLib.fromOutputScript(output.script, network);
|
|
155
|
+
psbt.addOutput({
|
|
156
|
+
address,
|
|
157
|
+
value: Number(output.value),
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
// If address extraction fails (e.g., OP_RETURN), add output with script and value
|
|
161
|
+
psbt.addOutput({
|
|
162
|
+
script: output.script,
|
|
163
|
+
value: Number(output.value),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Convert PSBT to base64
|
|
169
|
+
const psbtHex = psbt.toHex();
|
|
170
|
+
return base.toBase64(base.fromHex(psbtHex));
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.warn("rawTxToPsbtBase64 failed:", error);
|
|
173
|
+
throw new Error(
|
|
174
|
+
`Failed to convert raw transaction to PSBT: ${error instanceof Error ? error.message : String(error)}`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
105
178
|
}
|