@tomo-inc/chains-service 0.0.7 → 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/CHANGELOG.md +4 -0
- package/dist/index.cjs +837 -107
- package/dist/index.d.cts +293 -46
- package/dist/index.d.ts +293 -46
- package/dist/index.js +834 -115
- package/package.json +2 -2
- package/src/api/network-data.ts +33 -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/rpc.ts +10 -191
- package/src/dogecoin/service.ts +44 -264
- package/src/dogecoin/type.ts +12 -0
- package/src/dogecoin/utils-doge.ts +88 -2
- package/src/dogecoin/utils.ts +3 -380
- package/src/evm/service.ts +57 -73
- package/src/index.ts +10 -5
- 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/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { ChainTypes, SupportedChainTypes, TomoApiDomains, cache, getExplorerUrl } from '@tomo-inc/wallet-utils';
|
|
2
|
-
import Bignumber, { BigNumber } from 'bignumber.js';
|
|
3
|
-
import { toHex, parseUnits, createPublicClient, http, isHex, fromHex, parseTransaction, formatUnits, encodeFunctionData, erc20Abi } from 'viem';
|
|
4
2
|
import axios from 'axios';
|
|
5
3
|
import CryptoJS from 'crypto-js';
|
|
4
|
+
import Bignumber, { BigNumber } from 'bignumber.js';
|
|
5
|
+
import { Psbt, Transaction, address } from 'bitcoinjs-lib';
|
|
6
|
+
import { toHex as toHex$1, parseUnits, isAddressEqual, createPublicClient, http, hexToBigInt, numberToHex, isHex, fromHex as fromHex$1, parseTransaction, formatUnits, encodeFunctionData, erc20Abi } from 'viem';
|
|
6
7
|
import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createTransferInstruction, TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
7
|
-
import { PublicKey, Connection, VersionedTransaction, sendAndConfirmRawTransaction, Transaction, SystemProgram, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
|
8
|
+
import { PublicKey, Connection, VersionedTransaction, sendAndConfirmRawTransaction, Transaction as Transaction$1, SystemProgram, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
|
8
9
|
|
|
9
10
|
var __create = Object.create;
|
|
10
11
|
var __defProp = Object.defineProperty;
|
|
@@ -15,6 +16,10 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
15
16
|
var __commonJS = (cb, mod) => function __require() {
|
|
16
17
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
18
|
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
22
|
+
};
|
|
18
23
|
var __copyProps = (to, from, except, desc) => {
|
|
19
24
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
25
|
for (let key of __getOwnPropNames(from))
|
|
@@ -2796,18 +2801,18 @@ var Networks = class {
|
|
|
2796
2801
|
return networks;
|
|
2797
2802
|
}
|
|
2798
2803
|
async getNetworkByChainId(chainId) {
|
|
2799
|
-
const
|
|
2800
|
-
if (!
|
|
2804
|
+
const network2 = await this.networkAPIs.getNetworkByChainId(chainId);
|
|
2805
|
+
if (!network2) {
|
|
2801
2806
|
throw new Error("Network not found");
|
|
2802
2807
|
}
|
|
2803
|
-
return
|
|
2808
|
+
return network2;
|
|
2804
2809
|
}
|
|
2805
2810
|
async getNetworkByChainIndex(chainIndex) {
|
|
2806
|
-
const
|
|
2807
|
-
if (!
|
|
2811
|
+
const network2 = await this.networkAPIs.getNetworkByChainIndex(chainIndex);
|
|
2812
|
+
if (!network2) {
|
|
2808
2813
|
throw new Error("Network not found");
|
|
2809
2814
|
}
|
|
2810
|
-
return
|
|
2815
|
+
return network2;
|
|
2811
2816
|
}
|
|
2812
2817
|
async getCurrentNetwork() {
|
|
2813
2818
|
const chainType = this.chainType;
|
|
@@ -2830,8 +2835,8 @@ var Tokens = class {
|
|
|
2830
2835
|
constructor(tokenService) {
|
|
2831
2836
|
this.tokenAPIs = tokenService;
|
|
2832
2837
|
}
|
|
2833
|
-
async searchTokens({ chainIndex,
|
|
2834
|
-
const { data: tokens = [] } = await this.tokenAPIs.queryRemoteTokens({ keyword
|
|
2838
|
+
async searchTokens({ chainIndex, keyword }) {
|
|
2839
|
+
const { data: tokens = [] } = await this.tokenAPIs.queryRemoteTokens({ keyword, chainIndex });
|
|
2835
2840
|
return tokens;
|
|
2836
2841
|
}
|
|
2837
2842
|
// public async addToken(tokenInfo: Omit<TokenInfo, "id">): Promise<TokenInfo> {
|
|
@@ -2839,10 +2844,8 @@ var Tokens = class {
|
|
|
2839
2844
|
// return tokenInfoData;
|
|
2840
2845
|
// }
|
|
2841
2846
|
async getTokenInfo({ address, chainIndex }) {
|
|
2842
|
-
const {
|
|
2843
|
-
|
|
2844
|
-
} = await this.tokenAPIs.getTokenInfo({ address, chainIndex });
|
|
2845
|
-
return tokenInfo;
|
|
2847
|
+
const res = await this.tokenAPIs.getTokenInfo({ address, chainIndex });
|
|
2848
|
+
return res?.data?.data;
|
|
2846
2849
|
}
|
|
2847
2850
|
async getTokenRiskInfo(_params) {
|
|
2848
2851
|
const { data: tokenRiskInfo } = await this.tokenAPIs.getTokenRisk(_params);
|
|
@@ -2972,7 +2975,7 @@ var BasePublicService = class {
|
|
|
2972
2975
|
this.apiBase = publicApiBase;
|
|
2973
2976
|
this.tomoAppInfo = tomoAppInfo;
|
|
2974
2977
|
[this.tokenApi, this.txApi, this.walletApi].map(
|
|
2975
|
-
(
|
|
2978
|
+
(api2, i) => api2.interceptors.request.use((params) => {
|
|
2976
2979
|
const { tokenBaseUrl, txBaseUrl, walletBaseUrl } = publicApiBase;
|
|
2977
2980
|
params.baseURL = [tokenBaseUrl, txBaseUrl, walletBaseUrl][i];
|
|
2978
2981
|
return signRequest(params, tomoAppInfo.tomoClientId, tomoAppInfo);
|
|
@@ -3013,10 +3016,15 @@ var TokenAPIs = class _TokenAPIs extends BasePublicService {
|
|
|
3013
3016
|
}
|
|
3014
3017
|
}
|
|
3015
3018
|
async getTokenRisk(params) {
|
|
3016
|
-
if (typeof params.chainIndex !== "number" || !params.
|
|
3019
|
+
if (typeof params.chainIndex !== "number" || !params.address) {
|
|
3017
3020
|
throw new Error("chainName or tokenAddress is required");
|
|
3018
3021
|
}
|
|
3019
|
-
const res = await this.tokenApi.post("/v1/market/risk/details", [
|
|
3022
|
+
const res = await this.tokenApi.post("/v1/market/risk/details", [
|
|
3023
|
+
{
|
|
3024
|
+
chainIndex: params.chainIndex,
|
|
3025
|
+
tokenAddress: params.address
|
|
3026
|
+
}
|
|
3027
|
+
]);
|
|
3020
3028
|
return {
|
|
3021
3029
|
success: res?.data?.code === "0",
|
|
3022
3030
|
message: res?.data?.msg,
|
|
@@ -3104,10 +3112,10 @@ var TokenAPIs = class _TokenAPIs extends BasePublicService {
|
|
|
3104
3112
|
}
|
|
3105
3113
|
}
|
|
3106
3114
|
async getTokenDetail(params) {
|
|
3107
|
-
const { chainIndex,
|
|
3115
|
+
const { chainIndex, address } = params;
|
|
3108
3116
|
const res = await this.tokenApi.get(`/v1/market/token/detail`, {
|
|
3109
3117
|
params: {
|
|
3110
|
-
tokenAddress,
|
|
3118
|
+
tokenAddress: address,
|
|
3111
3119
|
chainIndex
|
|
3112
3120
|
}
|
|
3113
3121
|
});
|
|
@@ -3259,12 +3267,40 @@ var loadNetworks = (WALLET_DOMAIN) => {
|
|
|
3259
3267
|
supportHistory: false
|
|
3260
3268
|
},
|
|
3261
3269
|
{
|
|
3262
|
-
chainId:
|
|
3263
|
-
chainIndex:
|
|
3264
|
-
name: "
|
|
3265
|
-
chainName: "
|
|
3270
|
+
chainId: 26888,
|
|
3271
|
+
chainIndex: 2688800,
|
|
3272
|
+
name: "ABCORE_TESTNET",
|
|
3273
|
+
chainName: "AB Core Testnet",
|
|
3274
|
+
rpcUrls: ["https://rpc.core.testnet.ab.org"],
|
|
3275
|
+
blockExplorerUrl: "https://explorer.core.testnet.ab.org",
|
|
3276
|
+
platformType: "EVM",
|
|
3277
|
+
isTestnet: true,
|
|
3278
|
+
icon: "/assets/ab.svg",
|
|
3279
|
+
supportSwap: true,
|
|
3280
|
+
supportGift: false,
|
|
3281
|
+
supportHistory: true
|
|
3282
|
+
},
|
|
3283
|
+
{
|
|
3284
|
+
chainId: 36888,
|
|
3285
|
+
chainIndex: 3688800,
|
|
3286
|
+
name: "ABCORE",
|
|
3287
|
+
chainName: "AB Core",
|
|
3288
|
+
rpcUrls: ["https://rpc1.core.ab.org"],
|
|
3289
|
+
blockExplorerUrl: "https://explorer.core.ab.org",
|
|
3290
|
+
platformType: "EVM",
|
|
3291
|
+
isTestnet: false,
|
|
3292
|
+
icon: "/assets/ab.svg",
|
|
3293
|
+
supportSwap: true,
|
|
3294
|
+
supportGift: false,
|
|
3295
|
+
supportHistory: true
|
|
3296
|
+
},
|
|
3297
|
+
{
|
|
3298
|
+
chainId: 6281971,
|
|
3299
|
+
chainIndex: 628197100,
|
|
3300
|
+
name: "DOGEOS_TESTNET",
|
|
3301
|
+
chainName: "DogeOS Testnet",
|
|
3266
3302
|
rpcUrls: [`${WALLET_DOMAIN}/rpc/v1/doge_test`],
|
|
3267
|
-
blockExplorerUrl: "https://
|
|
3303
|
+
blockExplorerUrl: "https://rpc.testnet.dogeos.com",
|
|
3268
3304
|
platformType: "EVM",
|
|
3269
3305
|
isTestnet: true,
|
|
3270
3306
|
icon: "/assets/dogeos.svg",
|
|
@@ -3886,7 +3922,7 @@ var BaseService = class {
|
|
|
3886
3922
|
}
|
|
3887
3923
|
const baseUrlConfig = CONFIG[tomoAppInfo.tomoStage];
|
|
3888
3924
|
const { tokenAPIs, transactionAPIs, networkAPIs } = tomoPublicApiService(baseUrlConfig, tomoAppInfo);
|
|
3889
|
-
this.accountInfo = accountInfo;
|
|
3925
|
+
this.accountInfo = accountInfo || null;
|
|
3890
3926
|
this.networks = new Networks(networkAPIs);
|
|
3891
3927
|
this.tokens = new Tokens(tokenAPIs);
|
|
3892
3928
|
this.transactions = new Transactions(transactionAPIs);
|
|
@@ -3896,8 +3932,718 @@ var BaseService = class {
|
|
|
3896
3932
|
this.approveParams = params;
|
|
3897
3933
|
}
|
|
3898
3934
|
};
|
|
3899
|
-
|
|
3900
|
-
|
|
3935
|
+
|
|
3936
|
+
// src/dogecoin/base.ts
|
|
3937
|
+
function fromHex(hex) {
|
|
3938
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
3939
|
+
const paddedHex = cleanHex.length % 2 === 0 ? cleanHex : "0" + cleanHex;
|
|
3940
|
+
const bytes = new Uint8Array(paddedHex.length / 2);
|
|
3941
|
+
for (let i = 0; i < paddedHex.length; i += 2) {
|
|
3942
|
+
bytes[i / 2] = parseInt(paddedHex.substr(i, 2), 16);
|
|
3943
|
+
}
|
|
3944
|
+
return bytes;
|
|
3945
|
+
}
|
|
3946
|
+
function toHex(data) {
|
|
3947
|
+
if (typeof data === "string") {
|
|
3948
|
+
return data.startsWith("0x") ? data.slice(2) : data;
|
|
3949
|
+
}
|
|
3950
|
+
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
3951
|
+
return buffer.toString("hex");
|
|
3952
|
+
}
|
|
3953
|
+
function toBase64(data) {
|
|
3954
|
+
if (typeof data === "string") {
|
|
3955
|
+
const buffer2 = Buffer.from(data, "hex");
|
|
3956
|
+
return buffer2.toString("base64");
|
|
3957
|
+
}
|
|
3958
|
+
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
3959
|
+
return buffer.toString("base64");
|
|
3960
|
+
}
|
|
3961
|
+
function fromBase64(base64) {
|
|
3962
|
+
const buffer = Buffer.from(base64, "base64");
|
|
3963
|
+
return new Uint8Array(buffer);
|
|
3964
|
+
}
|
|
3965
|
+
function toBase58(data) {
|
|
3966
|
+
throw new Error("toBase58 requires bs58 package. Install it: pnpm add bs58");
|
|
3967
|
+
}
|
|
3968
|
+
var BaseConfig = SupportedChainTypes[ChainTypes.DOGE];
|
|
3969
|
+
var BLOCK_CONFIRMATIONS = 1;
|
|
3970
|
+
var FEE_RATE_KB = 0.5;
|
|
3971
|
+
var DECIMALS = 1e8;
|
|
3972
|
+
var TRANSACTION_PAGE_SIZE = 10;
|
|
3973
|
+
var MYDOGE_BASE_URL = "https://api.mydoge.com";
|
|
3974
|
+
var RPC_URL = "https://api.bitcore.io/api/DOGE/mainnet";
|
|
3975
|
+
var RPC_TIMEOUT = 20 * 1e3;
|
|
3976
|
+
var TX_OVERHEAD = 10;
|
|
3977
|
+
var P2SH_INPUT_SIZE = 148;
|
|
3978
|
+
var TX_OUTPUT_SIZE = 34;
|
|
3979
|
+
var DEFAULT_OUTPUT_COUNT = 2;
|
|
3980
|
+
var TX_SIZE = 1 * P2SH_INPUT_SIZE + DEFAULT_OUTPUT_COUNT * TX_OUTPUT_SIZE + TX_OVERHEAD;
|
|
3981
|
+
var network = {
|
|
3982
|
+
messagePrefix: "Dogecoin Signed Message:\n",
|
|
3983
|
+
bech32: "dc",
|
|
3984
|
+
bip44: 3,
|
|
3985
|
+
bip32: {
|
|
3986
|
+
public: 49990397,
|
|
3987
|
+
private: 49988504
|
|
3988
|
+
},
|
|
3989
|
+
pubKeyHash: 30,
|
|
3990
|
+
scriptHash: 22,
|
|
3991
|
+
wif: 158
|
|
3992
|
+
};
|
|
3993
|
+
|
|
3994
|
+
// src/dogecoin/rpc.ts
|
|
3995
|
+
var rpc_exports = {};
|
|
3996
|
+
__export(rpc_exports, {
|
|
3997
|
+
estimateSmartFee: () => estimateSmartFee,
|
|
3998
|
+
getBalance: () => getBalance,
|
|
3999
|
+
getDogeFeeByBlock: () => getDogeFeeByBlock,
|
|
4000
|
+
getInscriptionsUtxo: () => getInscriptionsUtxo,
|
|
4001
|
+
getInscriptionsUtxos: () => getInscriptionsUtxos,
|
|
4002
|
+
getSpendableUtxos: () => getSpendableUtxos,
|
|
4003
|
+
getTransactions: () => getTransactions,
|
|
4004
|
+
getTxDetail: () => getTxDetail,
|
|
4005
|
+
getUnSpentUtxos: () => getUnSpentUtxos,
|
|
4006
|
+
mydoge: () => mydoge,
|
|
4007
|
+
onCreateTransaction: () => onCreateTransaction,
|
|
4008
|
+
sendDogeTx: () => sendDogeTx,
|
|
4009
|
+
sendTransaction: () => sendTransaction
|
|
4010
|
+
});
|
|
4011
|
+
var KOINU_PER_DOGE = new BigNumber(DECIMALS);
|
|
4012
|
+
function toSatoshi(bitcion) {
|
|
4013
|
+
try {
|
|
4014
|
+
const amount = new BigNumber(bitcion);
|
|
4015
|
+
if (amount.isNaN() || amount.isNegative()) {
|
|
4016
|
+
throw new Error("Invalid amount");
|
|
4017
|
+
}
|
|
4018
|
+
return amount.times(KOINU_PER_DOGE).integerValue(BigNumber.ROUND_DOWN).toNumber();
|
|
4019
|
+
} catch (error) {
|
|
4020
|
+
throw new Error(`toSatoshi failed: ${error.message}`);
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
function toBitcoin(satoshis) {
|
|
4024
|
+
try {
|
|
4025
|
+
const amount = new BigNumber(satoshis);
|
|
4026
|
+
if (amount.isNaN() || amount.isNegative()) {
|
|
4027
|
+
throw new Error("Invalid Koinu amount");
|
|
4028
|
+
}
|
|
4029
|
+
return amount.dividedBy(KOINU_PER_DOGE).toNumber();
|
|
4030
|
+
} catch (error) {
|
|
4031
|
+
throw new Error(`toBitcoin failed: ${error.message}`);
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
function addUsedUtxos(newUsedUtxos) {
|
|
4035
|
+
const usedUtxos = cache.get("UsedUtxos") || {};
|
|
4036
|
+
for (const txid in newUsedUtxos) {
|
|
4037
|
+
usedUtxos[txid] = 1;
|
|
4038
|
+
}
|
|
4039
|
+
cache.set("UsedUtxos", usedUtxos, false);
|
|
4040
|
+
}
|
|
4041
|
+
function getUsedUtxos() {
|
|
4042
|
+
return cache.get("UsedUtxos") || {};
|
|
4043
|
+
}
|
|
4044
|
+
async function createPsbt({
|
|
4045
|
+
from,
|
|
4046
|
+
to,
|
|
4047
|
+
amount,
|
|
4048
|
+
fee,
|
|
4049
|
+
spendableUtxos
|
|
4050
|
+
}) {
|
|
4051
|
+
const utxos = spendableUtxos;
|
|
4052
|
+
if (!utxos || utxos.length === 0) {
|
|
4053
|
+
throw new Error("no spendable utxos");
|
|
4054
|
+
}
|
|
4055
|
+
const sendAmount = toSatoshi(amount);
|
|
4056
|
+
const sendCost = toSatoshi(fee);
|
|
4057
|
+
const totalNeeded = sendAmount + sendCost;
|
|
4058
|
+
const sortedUtxos = utxos.sort((a, b) => b.outputValue - a.outputValue);
|
|
4059
|
+
const selectedUtxos = [];
|
|
4060
|
+
let accumulatedAmount = 0;
|
|
4061
|
+
for (const utxo of sortedUtxos) {
|
|
4062
|
+
if (accumulatedAmount >= totalNeeded) break;
|
|
4063
|
+
selectedUtxos.push(utxo);
|
|
4064
|
+
accumulatedAmount += Number(utxo.outputValue);
|
|
4065
|
+
}
|
|
4066
|
+
if (accumulatedAmount < totalNeeded) {
|
|
4067
|
+
throw new Error("not enough funds to cover amount and fee");
|
|
4068
|
+
}
|
|
4069
|
+
const utxoDetailPromises = selectedUtxos.map(async (utxo) => {
|
|
4070
|
+
try {
|
|
4071
|
+
const utxoDetail = await getTxDetail(utxo.txid);
|
|
4072
|
+
return { utxo, utxoDetail };
|
|
4073
|
+
} catch (error) {
|
|
4074
|
+
return { utxo, utxoDetail: null };
|
|
4075
|
+
}
|
|
4076
|
+
});
|
|
4077
|
+
const utxoResults = await Promise.all(utxoDetailPromises);
|
|
4078
|
+
const inputs = [];
|
|
4079
|
+
const usingUtxos = {};
|
|
4080
|
+
let addedAmount = 0;
|
|
4081
|
+
for (const { utxo, utxoDetail } of utxoResults) {
|
|
4082
|
+
if (addedAmount >= totalNeeded) break;
|
|
4083
|
+
const { txid, vout, outputValue, address = from } = utxo;
|
|
4084
|
+
if (utxoDetail?.hex && !usingUtxos[txid]) {
|
|
4085
|
+
inputs.push({
|
|
4086
|
+
txId: txid,
|
|
4087
|
+
vOut: vout,
|
|
4088
|
+
amount: Number(outputValue),
|
|
4089
|
+
nonWitnessUtxo: utxoDetail.hex,
|
|
4090
|
+
address
|
|
4091
|
+
});
|
|
4092
|
+
usingUtxos[txid] = 1;
|
|
4093
|
+
addedAmount += Number(outputValue);
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
if (addedAmount < totalNeeded) {
|
|
4097
|
+
throw new Error("not enough funds to cover amount and fee");
|
|
4098
|
+
}
|
|
4099
|
+
const outputs = [
|
|
4100
|
+
{
|
|
4101
|
+
address: to,
|
|
4102
|
+
amount: sendAmount
|
|
4103
|
+
}
|
|
4104
|
+
];
|
|
4105
|
+
const changeAmount = addedAmount - sendAmount - sendCost;
|
|
4106
|
+
if (changeAmount > 0) {
|
|
4107
|
+
outputs.push({
|
|
4108
|
+
address: from,
|
|
4109
|
+
amount: changeAmount
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
4112
|
+
const params = {
|
|
4113
|
+
address: from,
|
|
4114
|
+
inputs,
|
|
4115
|
+
outputs
|
|
4116
|
+
};
|
|
4117
|
+
const psbtBase64 = DogecoinUtils.buildPsbtToBase64(params);
|
|
4118
|
+
return { psbtBase64, usingUtxos };
|
|
4119
|
+
}
|
|
4120
|
+
function decodePsbt(psbt, type = "hex") {
|
|
4121
|
+
try {
|
|
4122
|
+
if (type === "base64") {
|
|
4123
|
+
const psbtHex = toHex(fromBase64(psbt));
|
|
4124
|
+
return Psbt.fromHex(psbtHex, {
|
|
4125
|
+
network
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
return Psbt.fromHex(psbt, {
|
|
4129
|
+
network
|
|
4130
|
+
});
|
|
4131
|
+
} catch (error) {
|
|
4132
|
+
console.error("Failed to decode PSBT:", error);
|
|
4133
|
+
throw error;
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
var TransactionParser = class {
|
|
4137
|
+
rawTx;
|
|
4138
|
+
constructor(rawTx) {
|
|
4139
|
+
this.rawTx = rawTx;
|
|
4140
|
+
}
|
|
4141
|
+
hexToText(hex) {
|
|
4142
|
+
let str = "";
|
|
4143
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
4144
|
+
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
|
4145
|
+
}
|
|
4146
|
+
return str;
|
|
4147
|
+
}
|
|
4148
|
+
extractOPReturnData() {
|
|
4149
|
+
const ordIndex = this.rawTx.indexOf("6f7264");
|
|
4150
|
+
if (ordIndex === -1) return null;
|
|
4151
|
+
const dataHex = this.rawTx.substring(ordIndex);
|
|
4152
|
+
const dataText = this.hexToText(dataHex);
|
|
4153
|
+
const jsonMatch = dataText.match(/\{.*?\}/);
|
|
4154
|
+
return jsonMatch ? JSON.parse(jsonMatch[0]) : null;
|
|
4155
|
+
}
|
|
4156
|
+
parseScript() {
|
|
4157
|
+
return {
|
|
4158
|
+
version: this.rawTx.substring(0, 8),
|
|
4159
|
+
inputCount: parseInt(this.rawTx.substring(8, 10)),
|
|
4160
|
+
inputs: this.parseInputs(),
|
|
4161
|
+
opReturnData: this.extractOPReturnData()
|
|
4162
|
+
};
|
|
4163
|
+
}
|
|
4164
|
+
parseInputs() {
|
|
4165
|
+
const inputs = [];
|
|
4166
|
+
const position = 10;
|
|
4167
|
+
const firstInput = {
|
|
4168
|
+
txid: this.rawTx.substring(position, position + 64),
|
|
4169
|
+
vout: this.rawTx.substring(position + 64, position + 72),
|
|
4170
|
+
scriptData: this.extractOPReturnData()
|
|
4171
|
+
};
|
|
4172
|
+
inputs.push(firstInput);
|
|
4173
|
+
return inputs;
|
|
4174
|
+
}
|
|
4175
|
+
};
|
|
4176
|
+
|
|
4177
|
+
// src/dogecoin/rpc.ts
|
|
4178
|
+
var mydoge = axios.create({
|
|
4179
|
+
baseURL: MYDOGE_BASE_URL
|
|
4180
|
+
});
|
|
4181
|
+
var api = axios.create({
|
|
4182
|
+
baseURL: RPC_URL,
|
|
4183
|
+
timeout: RPC_TIMEOUT
|
|
4184
|
+
});
|
|
4185
|
+
async function getBalance(address) {
|
|
4186
|
+
if (!address) {
|
|
4187
|
+
return {
|
|
4188
|
+
address: "",
|
|
4189
|
+
balance: 0
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
const path = `/address/${address}?page=1&pageSize=10`;
|
|
4193
|
+
const api2 = `${MYDOGE_BASE_URL}/wallet/info?route=` + encodeURIComponent(path);
|
|
4194
|
+
const res = await mydoge.get(api2);
|
|
4195
|
+
return res.data || {};
|
|
4196
|
+
}
|
|
4197
|
+
async function getTxDetail(txId) {
|
|
4198
|
+
if (!txId) {
|
|
4199
|
+
return {};
|
|
4200
|
+
}
|
|
4201
|
+
const path = `/tx/${txId}`;
|
|
4202
|
+
const api2 = `${MYDOGE_BASE_URL}/wallet/info?route=` + encodeURIComponent(path);
|
|
4203
|
+
const res = await mydoge.get(api2);
|
|
4204
|
+
return res.data || {};
|
|
4205
|
+
}
|
|
4206
|
+
async function getUtxos(address, cursor, result, filter, tx = null) {
|
|
4207
|
+
const query = (await mydoge.get(`${MYDOGE_BASE_URL}/utxos/${address}?filter=${filter}${cursor ? `&cursor=${cursor}` : ""}`)).data;
|
|
4208
|
+
let { utxos } = query;
|
|
4209
|
+
if (tx) {
|
|
4210
|
+
utxos = utxos.filter((utxo) => utxo.txid === tx?.txid && utxo.vout === tx?.vout);
|
|
4211
|
+
}
|
|
4212
|
+
result.push(
|
|
4213
|
+
...utxos.map((i) => ({
|
|
4214
|
+
txid: i.txid,
|
|
4215
|
+
vout: i.vout,
|
|
4216
|
+
outputValue: i.satoshis,
|
|
4217
|
+
script: i.script_pubkey,
|
|
4218
|
+
...filter === "inscriptions" && { inscriptions: i.inscriptions }
|
|
4219
|
+
}))
|
|
4220
|
+
);
|
|
4221
|
+
if (result.length && tx) {
|
|
4222
|
+
return;
|
|
4223
|
+
}
|
|
4224
|
+
result = result.sort((a, b) => toBitcoin(b.outputValue) - toBitcoin(a.outputValue));
|
|
4225
|
+
if (query.next_cursor) {
|
|
4226
|
+
return getUtxos(address, query.next_cursor, result, filter, tx);
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
async function getInscriptionsUtxos(address) {
|
|
4230
|
+
const inscriptions = [];
|
|
4231
|
+
await getUtxos(address, 0, inscriptions, "inscriptions");
|
|
4232
|
+
return inscriptions;
|
|
4233
|
+
}
|
|
4234
|
+
async function getSpendableUtxos(address) {
|
|
4235
|
+
const utxos = [];
|
|
4236
|
+
await getUtxos(address, 0, utxos, "spendable");
|
|
4237
|
+
return utxos;
|
|
4238
|
+
}
|
|
4239
|
+
async function getUnSpentUtxos(address) {
|
|
4240
|
+
try {
|
|
4241
|
+
const result = await api.get(`/address/${address}/?unspent=true&limit=${0}`);
|
|
4242
|
+
const unSpentUtxo = result?.data;
|
|
4243
|
+
if (unSpentUtxo?.length) return unSpentUtxo.filter((utxo) => !utxo.spentTxid);
|
|
4244
|
+
return [];
|
|
4245
|
+
} catch (e) {
|
|
4246
|
+
return [];
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
async function getInscriptionsUtxo(address, tx) {
|
|
4250
|
+
const inscriptions = [];
|
|
4251
|
+
await getUtxos(address, 0, inscriptions, "inscriptions", tx);
|
|
4252
|
+
return inscriptions[0];
|
|
4253
|
+
}
|
|
4254
|
+
async function sendTransaction({ signed, senderAddress }) {
|
|
4255
|
+
const jsonrpcReq = {
|
|
4256
|
+
jsonrpc: "2.0",
|
|
4257
|
+
id: `${senderAddress}_send_${Date.now()}`,
|
|
4258
|
+
method: "sendrawtransaction",
|
|
4259
|
+
params: [signed]
|
|
4260
|
+
};
|
|
4261
|
+
const jsonrpcRes = (await mydoge.post("/wallet/rpc", jsonrpcReq)).data;
|
|
4262
|
+
return jsonrpcRes;
|
|
4263
|
+
}
|
|
4264
|
+
async function estimateSmartFee({ senderAddress }) {
|
|
4265
|
+
const smartfeeReq = {
|
|
4266
|
+
jsonrpc: "2.0",
|
|
4267
|
+
id: `${senderAddress}_estimatesmartfee_${Date.now()}`,
|
|
4268
|
+
method: "estimatesmartfee",
|
|
4269
|
+
params: [BLOCK_CONFIRMATIONS]
|
|
4270
|
+
// confirm within x blocks
|
|
4271
|
+
};
|
|
4272
|
+
const feeData = (await mydoge.post("/wallet/rpc", smartfeeReq)).data;
|
|
4273
|
+
const feeRate = feeData?.result?.feerate || FEE_RATE_KB;
|
|
4274
|
+
const feePerKB = toSatoshi(feeRate * 2);
|
|
4275
|
+
return { feePerKB };
|
|
4276
|
+
}
|
|
4277
|
+
async function onCreateTransaction({ data, sendResponse }) {
|
|
4278
|
+
const amountSatoshi = toSatoshi(data.dogeAmount);
|
|
4279
|
+
const amount = toBitcoin(amountSatoshi);
|
|
4280
|
+
try {
|
|
4281
|
+
const response = await mydoge.post("/v3/tx/prepare", {
|
|
4282
|
+
sender: data.senderAddress,
|
|
4283
|
+
recipient: data.recipientAddress,
|
|
4284
|
+
amount
|
|
4285
|
+
});
|
|
4286
|
+
const { rawTx, fee, amount: resultAmount } = response.data;
|
|
4287
|
+
let amountMismatch = false;
|
|
4288
|
+
if (resultAmount < amount - fee) {
|
|
4289
|
+
amountMismatch = true;
|
|
4290
|
+
}
|
|
4291
|
+
sendResponse?.({
|
|
4292
|
+
rawTx,
|
|
4293
|
+
fee,
|
|
4294
|
+
amount: resultAmount,
|
|
4295
|
+
amountMismatch
|
|
4296
|
+
});
|
|
4297
|
+
} catch (err) {
|
|
4298
|
+
sendResponse?.(false);
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
async function getTransactions(address, config) {
|
|
4302
|
+
const { pageSize = 10, pageNumber = 1 } = config || {};
|
|
4303
|
+
const size = Math.min(pageSize, TRANSACTION_PAGE_SIZE);
|
|
4304
|
+
let txIds = [];
|
|
4305
|
+
let totalPages;
|
|
4306
|
+
let page;
|
|
4307
|
+
try {
|
|
4308
|
+
const response = (await mydoge.get("/wallet/info", {
|
|
4309
|
+
params: {
|
|
4310
|
+
route: `/address/${address}?page=${pageNumber}&pageSize=${size}`
|
|
4311
|
+
}
|
|
4312
|
+
})).data;
|
|
4313
|
+
txIds = response.txids;
|
|
4314
|
+
totalPages = response.totalPages;
|
|
4315
|
+
page = response.page;
|
|
4316
|
+
const transactions = await Promise.all(
|
|
4317
|
+
txIds?.map(async (txId) => {
|
|
4318
|
+
const detail = await getTxDetail(txId);
|
|
4319
|
+
const tx = new TransactionParser(detail.hex);
|
|
4320
|
+
const parsedData = tx.parseScript();
|
|
4321
|
+
return {
|
|
4322
|
+
...detail,
|
|
4323
|
+
tick: parsedData?.opReturnData?.tick,
|
|
4324
|
+
tickAmount: parsedData?.opReturnData?.amt
|
|
4325
|
+
};
|
|
4326
|
+
}) || []
|
|
4327
|
+
);
|
|
4328
|
+
return { transactions, txIds, totalPages, page };
|
|
4329
|
+
} catch (err) {
|
|
4330
|
+
throw new Error("getTransactions failed");
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
4333
|
+
var getDogeFeeByBlock = async (address, n = 22) => {
|
|
4334
|
+
const res = await api.get(`/fee/${n}`);
|
|
4335
|
+
return (res.data?.feerate || 0.01) * TX_SIZE;
|
|
4336
|
+
};
|
|
4337
|
+
var sendDogeTx = async (rawTx) => {
|
|
4338
|
+
try {
|
|
4339
|
+
const res = await api.post("/tx/send", { rawTx });
|
|
4340
|
+
return res.data;
|
|
4341
|
+
} catch (e) {
|
|
4342
|
+
if (typeof e?.response?.data === "string") return Promise.reject(e?.response?.data);
|
|
4343
|
+
else return Promise.reject(e.message);
|
|
4344
|
+
}
|
|
4345
|
+
};
|
|
4346
|
+
|
|
4347
|
+
// src/dogecoin/utils-doge.ts
|
|
4348
|
+
var DogecoinAddress = class {
|
|
4349
|
+
static network = network;
|
|
4350
|
+
static generatePath(addressIndex) {
|
|
4351
|
+
return `m/44'/3'/0'/0/${addressIndex || 0}`;
|
|
4352
|
+
}
|
|
4353
|
+
static verifyAddress(address$1) {
|
|
4354
|
+
try {
|
|
4355
|
+
const decoded = address.fromBase58Check(address$1);
|
|
4356
|
+
return decoded.version === network.pubKeyHash || decoded.version === network.scriptHash;
|
|
4357
|
+
} catch (error) {
|
|
4358
|
+
return false;
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
};
|
|
4362
|
+
var DogecoinUtils = class {
|
|
4363
|
+
/**
|
|
4364
|
+
* Build the PSBT from the transaction
|
|
4365
|
+
* @param tx utxoTx
|
|
4366
|
+
* @param _maximumFeeRate number (optional, currently unused)
|
|
4367
|
+
* @returns base64
|
|
4368
|
+
*/
|
|
4369
|
+
static buildPsbtToBase64(tx, _maximumFeeRate) {
|
|
4370
|
+
const psbt = new Psbt({ network });
|
|
4371
|
+
for (const input of tx.inputs) {
|
|
4372
|
+
const inputOptions = {
|
|
4373
|
+
hash: input.txId,
|
|
4374
|
+
index: input.vOut
|
|
4375
|
+
};
|
|
4376
|
+
if (input.nonWitnessUtxo) {
|
|
4377
|
+
inputOptions.nonWitnessUtxo = Buffer.from(input.nonWitnessUtxo, "hex");
|
|
4378
|
+
}
|
|
4379
|
+
psbt.addInput(inputOptions);
|
|
4380
|
+
}
|
|
4381
|
+
for (const output of tx.outputs) {
|
|
4382
|
+
psbt.addOutput({
|
|
4383
|
+
value: BigInt(output?.amount || 0),
|
|
4384
|
+
address: output.address
|
|
4385
|
+
});
|
|
4386
|
+
}
|
|
4387
|
+
const psbtHex = psbt.toHex();
|
|
4388
|
+
return toBase64(fromHex(psbtHex));
|
|
4389
|
+
}
|
|
4390
|
+
/**
|
|
4391
|
+
* Extract the transaction from the signed PSBT
|
|
4392
|
+
* @param signedPsbt base64
|
|
4393
|
+
* @param _maximumFeeRate number (optional, currently unused)
|
|
4394
|
+
* @returns transaction hex
|
|
4395
|
+
*/
|
|
4396
|
+
static extractPsbtTransaction(signedPsbt, _maximumFeeRate) {
|
|
4397
|
+
const signedPsbtHex = toHex(fromBase64(signedPsbt));
|
|
4398
|
+
const psbt = Psbt.fromHex(signedPsbtHex, {
|
|
4399
|
+
network
|
|
4400
|
+
});
|
|
4401
|
+
const maximumFeeRate = _maximumFeeRate || 1e5;
|
|
4402
|
+
psbt.setMaximumFeeRate(maximumFeeRate);
|
|
4403
|
+
psbt.finalizeAllInputs();
|
|
4404
|
+
const transaction = psbt.extractTransaction();
|
|
4405
|
+
return transaction.toHex();
|
|
4406
|
+
}
|
|
4407
|
+
/**
|
|
4408
|
+
* Convert raw transaction hex to PSBT base64
|
|
4409
|
+
* This method converts an unsigned raw transaction to PSBT format.
|
|
4410
|
+
* Note: The rawTx should be an unsigned transaction, and the nonWitnessUtxo
|
|
4411
|
+
* for each input will need to be fetched separately from the blockchain.
|
|
4412
|
+
* @param rawTx hex string of the raw transaction
|
|
4413
|
+
* @returns base64 string of the PSBT
|
|
4414
|
+
*/
|
|
4415
|
+
static async rawTxToPsbtBase64(rawTx) {
|
|
4416
|
+
try {
|
|
4417
|
+
const cleanHex = rawTx.startsWith("0x") ? rawTx.slice(2) : rawTx;
|
|
4418
|
+
const transaction = Transaction.fromHex(cleanHex);
|
|
4419
|
+
const psbt = new Psbt({ network });
|
|
4420
|
+
for (let i = 0; i < transaction.ins.length; i++) {
|
|
4421
|
+
const input = transaction.ins[i];
|
|
4422
|
+
const hash = Buffer.from(input.hash).toString("hex");
|
|
4423
|
+
const index = input.index;
|
|
4424
|
+
const inputOptions = {
|
|
4425
|
+
hash,
|
|
4426
|
+
index
|
|
4427
|
+
};
|
|
4428
|
+
try {
|
|
4429
|
+
const prevTxDetail = await getTxDetail(hash);
|
|
4430
|
+
if (prevTxDetail?.hex) {
|
|
4431
|
+
inputOptions.nonWitnessUtxo = Buffer.from(prevTxDetail.hex, "hex");
|
|
4432
|
+
} else {
|
|
4433
|
+
throw new Error(`Previous transaction ${hash} has no hex data`);
|
|
4434
|
+
}
|
|
4435
|
+
} catch (error) {
|
|
4436
|
+
throw new Error(
|
|
4437
|
+
`Failed to fetch previous transaction ${hash} for input #${i}. This is required for PSBT creation. Error: ${error instanceof Error ? error.message : String(error)}`
|
|
4438
|
+
);
|
|
4439
|
+
}
|
|
4440
|
+
psbt.addInput(inputOptions);
|
|
4441
|
+
}
|
|
4442
|
+
for (const output of transaction.outs) {
|
|
4443
|
+
try {
|
|
4444
|
+
const address$1 = address.fromOutputScript(output.script, network);
|
|
4445
|
+
psbt.addOutput({
|
|
4446
|
+
address: address$1,
|
|
4447
|
+
value: BigInt(output?.value || 0)
|
|
4448
|
+
});
|
|
4449
|
+
} catch (error) {
|
|
4450
|
+
psbt.addOutput({
|
|
4451
|
+
script: output.script,
|
|
4452
|
+
value: BigInt(output?.value || 0)
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4455
|
+
}
|
|
4456
|
+
const psbtHex = psbt.toHex();
|
|
4457
|
+
return toBase64(fromHex(psbtHex));
|
|
4458
|
+
} catch (error) {
|
|
4459
|
+
console.warn("rawTxToPsbtBase64 failed:", error);
|
|
4460
|
+
throw new Error(
|
|
4461
|
+
`Failed to convert raw transaction to PSBT: ${error instanceof Error ? error.message : String(error)}`
|
|
4462
|
+
);
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
};
|
|
4466
|
+
var DogecoinService = class _DogecoinService extends BaseService {
|
|
4467
|
+
static instance;
|
|
4468
|
+
chainType;
|
|
4469
|
+
rpcService;
|
|
4470
|
+
constructor(chainType, accountInfo, tomoAppInfo) {
|
|
4471
|
+
super(tomoAppInfo, accountInfo);
|
|
4472
|
+
this.chainType = chainType;
|
|
4473
|
+
this.rpcService = rpc_exports;
|
|
4474
|
+
}
|
|
4475
|
+
//singleton
|
|
4476
|
+
static getInstance(chainType, accountInfo, tomoAppInfo) {
|
|
4477
|
+
if (!_DogecoinService.instance) {
|
|
4478
|
+
_DogecoinService.instance = new _DogecoinService(chainType, accountInfo, tomoAppInfo);
|
|
4479
|
+
}
|
|
4480
|
+
return _DogecoinService.instance;
|
|
4481
|
+
}
|
|
4482
|
+
async requestAccounts() {
|
|
4483
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4484
|
+
const { publicKey, address } = addresses?.[0] || {};
|
|
4485
|
+
if (!address) {
|
|
4486
|
+
throw new Error("address is not set");
|
|
4487
|
+
}
|
|
4488
|
+
const { balance = 0 } = await getBalance(address);
|
|
4489
|
+
return {
|
|
4490
|
+
address,
|
|
4491
|
+
balance,
|
|
4492
|
+
approved: true,
|
|
4493
|
+
publicKey
|
|
4494
|
+
};
|
|
4495
|
+
}
|
|
4496
|
+
async getAccounts() {
|
|
4497
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4498
|
+
return accounts.map((account) => account.address);
|
|
4499
|
+
}
|
|
4500
|
+
async getConnectionStatus() {
|
|
4501
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4502
|
+
const { address } = addresses?.[0] || {};
|
|
4503
|
+
if (!address) {
|
|
4504
|
+
throw new Error("address is not set");
|
|
4505
|
+
}
|
|
4506
|
+
return {
|
|
4507
|
+
connected: true,
|
|
4508
|
+
address,
|
|
4509
|
+
selectedWalletAddress: address
|
|
4510
|
+
};
|
|
4511
|
+
}
|
|
4512
|
+
async getBalance() {
|
|
4513
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4514
|
+
const { address } = addresses?.[0] || {};
|
|
4515
|
+
if (!address) {
|
|
4516
|
+
throw new Error("address is not set");
|
|
4517
|
+
}
|
|
4518
|
+
const { balance = 0 } = await getBalance(address);
|
|
4519
|
+
return {
|
|
4520
|
+
address,
|
|
4521
|
+
balance
|
|
4522
|
+
};
|
|
4523
|
+
}
|
|
4524
|
+
async signMessage({ message, type }) {
|
|
4525
|
+
if (type) {
|
|
4526
|
+
throw new Error("bip322simple not support.");
|
|
4527
|
+
}
|
|
4528
|
+
const signature = await this.accountInfo.signMessage(message);
|
|
4529
|
+
return {
|
|
4530
|
+
signedMessage: signature
|
|
4531
|
+
};
|
|
4532
|
+
}
|
|
4533
|
+
async requestSignedMessage({
|
|
4534
|
+
message,
|
|
4535
|
+
type
|
|
4536
|
+
}) {
|
|
4537
|
+
return await this.signMessage({
|
|
4538
|
+
message,
|
|
4539
|
+
type
|
|
4540
|
+
});
|
|
4541
|
+
}
|
|
4542
|
+
async _queryGasInfo(txData) {
|
|
4543
|
+
const { chainId, amount = 0, decimals = 8 } = txData;
|
|
4544
|
+
const gasLimitParam = {
|
|
4545
|
+
from: txData.from,
|
|
4546
|
+
to: txData.to,
|
|
4547
|
+
value: toHex$1(parseUnits(amount.toString(), decimals))
|
|
4548
|
+
};
|
|
4549
|
+
const queryGasParams = {
|
|
4550
|
+
chainIndex: Number(chainId || BaseConfig.chainId),
|
|
4551
|
+
callData: "0x",
|
|
4552
|
+
gasLimitParam,
|
|
4553
|
+
addressList: [txData.from]
|
|
4554
|
+
};
|
|
4555
|
+
const {
|
|
4556
|
+
data: gasInfo,
|
|
4557
|
+
success,
|
|
4558
|
+
message
|
|
4559
|
+
} = await this.transactions.queryGasInfo({
|
|
4560
|
+
chainType: this.chainType,
|
|
4561
|
+
params: queryGasParams
|
|
4562
|
+
});
|
|
4563
|
+
if (!success) {
|
|
4564
|
+
console.error("queryGasInfo doge", success, txData, queryGasParams, gasInfo);
|
|
4565
|
+
const { gasFee } = BaseConfig;
|
|
4566
|
+
return {
|
|
4567
|
+
success: true,
|
|
4568
|
+
gasFee: gasFee.toString()
|
|
4569
|
+
};
|
|
4570
|
+
}
|
|
4571
|
+
const { baseFee = 1, gasLimit = 1 } = gasInfo;
|
|
4572
|
+
const calcFee = (feeLevel) => {
|
|
4573
|
+
const gasFee = new BigNumber(gasInfo[feeLevel] || baseFee).times(gasLimit);
|
|
4574
|
+
return toBitcoin(gasFee.toNumber());
|
|
4575
|
+
};
|
|
4576
|
+
const fees = {
|
|
4577
|
+
low: calcFee("priorityFeeLow"),
|
|
4578
|
+
medium: calcFee("priorityFeeMedium"),
|
|
4579
|
+
high: calcFee("priorityFeeHigh")
|
|
4580
|
+
};
|
|
4581
|
+
return {
|
|
4582
|
+
success: true,
|
|
4583
|
+
fees,
|
|
4584
|
+
gasFee: fees.high
|
|
4585
|
+
};
|
|
4586
|
+
}
|
|
4587
|
+
async requestPsbt({
|
|
4588
|
+
rawTx,
|
|
4589
|
+
signOnly
|
|
4590
|
+
}) {
|
|
4591
|
+
const psbtBase64 = await DogecoinUtils.rawTxToPsbtBase64(rawTx);
|
|
4592
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
4593
|
+
if (!signedPsbt) {
|
|
4594
|
+
return null;
|
|
4595
|
+
}
|
|
4596
|
+
const signedRawTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
|
|
4597
|
+
if (signOnly) {
|
|
4598
|
+
return {
|
|
4599
|
+
signedRawTx
|
|
4600
|
+
};
|
|
4601
|
+
}
|
|
4602
|
+
const { txid = "" } = await sendDogeTx(signedRawTx);
|
|
4603
|
+
return {
|
|
4604
|
+
txId: txid,
|
|
4605
|
+
signedRawTx
|
|
4606
|
+
};
|
|
4607
|
+
}
|
|
4608
|
+
async requestTransaction(txData) {
|
|
4609
|
+
const spendableUtxos = txData?.spendableUtxos;
|
|
4610
|
+
if (txData.amountMismatch) {
|
|
4611
|
+
throw new Error("balance_insufficient");
|
|
4612
|
+
}
|
|
4613
|
+
if (!spendableUtxos || spendableUtxos?.length === 0) {
|
|
4614
|
+
txData.spendableUtxos = await getSpendableUtxos(txData.from);
|
|
4615
|
+
}
|
|
4616
|
+
const { psbtBase64, usingUtxos } = await createPsbt(txData);
|
|
4617
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
4618
|
+
const signedTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
|
|
4619
|
+
if (signedTx === "") {
|
|
4620
|
+
throw new Error("Error: sign transaction err.");
|
|
4621
|
+
}
|
|
4622
|
+
try {
|
|
4623
|
+
const { txid } = await sendDogeTx(signedTx);
|
|
4624
|
+
addUsedUtxos(usingUtxos);
|
|
4625
|
+
return { txId: txid };
|
|
4626
|
+
} catch (err) {
|
|
4627
|
+
throw new Error("Error: send transaction err." + JSON.stringify(err));
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
async getTransactionStatus({ txId }) {
|
|
4631
|
+
const transaction = await getTxDetail(txId);
|
|
4632
|
+
if (!transaction?.txid) {
|
|
4633
|
+
return null;
|
|
4634
|
+
}
|
|
4635
|
+
return {
|
|
4636
|
+
txId: transaction.txid,
|
|
4637
|
+
confirmations: transaction.confirmations,
|
|
4638
|
+
status: transaction.confirmations > 0 ? "confirmed" : "pending",
|
|
4639
|
+
dogeAmount: transaction.vout[0]?.value,
|
|
4640
|
+
blockTime: transaction.blockTime,
|
|
4641
|
+
address: transaction.vout[0]?.addresses[0]
|
|
4642
|
+
};
|
|
4643
|
+
}
|
|
4644
|
+
};
|
|
4645
|
+
function getRPCClient(network2) {
|
|
4646
|
+
const { chainId, name, rpcUrls, nativeCurrencyDecimals, nativeCurrencyName, nativeCurrencySymbol } = network2;
|
|
3901
4647
|
const myCustomChain = {
|
|
3902
4648
|
id: Number(chainId) || chainId,
|
|
3903
4649
|
name,
|
|
@@ -3952,20 +4698,20 @@ var TxTypes = /* @__PURE__ */ ((TxTypes2) => {
|
|
|
3952
4698
|
})(TxTypes || {});
|
|
3953
4699
|
|
|
3954
4700
|
// src/evm/utils.ts
|
|
3955
|
-
var isEvmChain = (
|
|
3956
|
-
return
|
|
4701
|
+
var isEvmChain = (network2) => {
|
|
4702
|
+
return network2 && network2?.platformType === "EVM";
|
|
3957
4703
|
};
|
|
3958
4704
|
var getAllTypeChainIds = ({ chainId, chainType }) => {
|
|
3959
4705
|
if (isHex(chainId)) {
|
|
3960
4706
|
const chainIdHex2 = chainId;
|
|
3961
|
-
chainId = fromHex(chainId, "number").toString();
|
|
4707
|
+
chainId = fromHex$1(chainId, "number").toString();
|
|
3962
4708
|
return {
|
|
3963
4709
|
chainId,
|
|
3964
4710
|
chainIdHex: chainIdHex2,
|
|
3965
4711
|
chainUid: `${chainType}:${chainId}`
|
|
3966
4712
|
};
|
|
3967
4713
|
}
|
|
3968
|
-
const chainIdHex = toHex(Number(chainId));
|
|
4714
|
+
const chainIdHex = toHex$1(Number(chainId));
|
|
3969
4715
|
return {
|
|
3970
4716
|
chainId,
|
|
3971
4717
|
chainIdHex,
|
|
@@ -4045,7 +4791,7 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4045
4791
|
}
|
|
4046
4792
|
async personal_sign([message, address]) {
|
|
4047
4793
|
const accounts = await this.accountInfo.getCurrent();
|
|
4048
|
-
if (accounts[0].address
|
|
4794
|
+
if (!isAddressEqual(accounts[0].address, address)) {
|
|
4049
4795
|
throw new Error("address is not the current account");
|
|
4050
4796
|
}
|
|
4051
4797
|
const signature = await this.accountInfo.signMessage(message);
|
|
@@ -4053,7 +4799,7 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4053
4799
|
}
|
|
4054
4800
|
async eth_signTypedData_v4([address, typedData]) {
|
|
4055
4801
|
const accounts = await this.accountInfo.getCurrent();
|
|
4056
|
-
if (accounts[0].address
|
|
4802
|
+
if (!isAddressEqual(accounts[0].address, address)) {
|
|
4057
4803
|
throw new Error("address is not the current account");
|
|
4058
4804
|
}
|
|
4059
4805
|
const signature = await this.accountInfo.signTypedData(typedData);
|
|
@@ -4061,7 +4807,7 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4061
4807
|
}
|
|
4062
4808
|
async eth_getBalance([address, type]) {
|
|
4063
4809
|
const accounts = await this.accountInfo.getCurrent();
|
|
4064
|
-
if (accounts[0].address
|
|
4810
|
+
if (!isAddressEqual(accounts[0].address, address)) {
|
|
4065
4811
|
throw new Error("address is not the current account");
|
|
4066
4812
|
}
|
|
4067
4813
|
if (type !== "latest") {
|
|
@@ -4079,7 +4825,7 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4079
4825
|
}
|
|
4080
4826
|
async _queryGasInfo(txData) {
|
|
4081
4827
|
const { tokenAddress, chainId, amount = 0, decimals = 9 } = txData;
|
|
4082
|
-
const value = tokenAddress ? "0x1" : toHex(parseUnits(amount.toString(), decimals));
|
|
4828
|
+
const value = tokenAddress ? "0x1" : toHex$1(parseUnits(amount.toString(), decimals));
|
|
4083
4829
|
let callData = "0x";
|
|
4084
4830
|
if (tokenAddress) {
|
|
4085
4831
|
callData = createErc20TxData(txData, { decimals, address: tokenAddress })?.data;
|
|
@@ -4105,8 +4851,8 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4105
4851
|
});
|
|
4106
4852
|
if (!success) {
|
|
4107
4853
|
console.error("queryGasInfo evm", txData, queryGasParams, message, gasInfo);
|
|
4108
|
-
const
|
|
4109
|
-
const { gasFee } =
|
|
4854
|
+
const BaseConfig3 = SupportedChainTypes[ChainTypes.EVM];
|
|
4855
|
+
const { gasFee } = BaseConfig3;
|
|
4110
4856
|
return {
|
|
4111
4857
|
success: true,
|
|
4112
4858
|
gasFee: gasFee.toString()
|
|
@@ -4138,51 +4884,6 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4138
4884
|
}
|
|
4139
4885
|
};
|
|
4140
4886
|
}
|
|
4141
|
-
async eth_estimateGas(txs) {
|
|
4142
|
-
const { from, to, value = "0x1" } = txs[0] || {};
|
|
4143
|
-
const accounts = await this.accountInfo.getCurrent();
|
|
4144
|
-
if (accounts[0].address !== from) {
|
|
4145
|
-
throw new Error("address is not the current account");
|
|
4146
|
-
}
|
|
4147
|
-
if (!to) {
|
|
4148
|
-
throw new Error("to is not set");
|
|
4149
|
-
}
|
|
4150
|
-
const chainId = txs[0]?.chainId || await this.eth_chainId();
|
|
4151
|
-
const queryGasParams = {
|
|
4152
|
-
chainIndex: Number(chainId),
|
|
4153
|
-
callData: "0x",
|
|
4154
|
-
gasLimitParam: {
|
|
4155
|
-
from,
|
|
4156
|
-
to,
|
|
4157
|
-
value
|
|
4158
|
-
},
|
|
4159
|
-
addressList: [from]
|
|
4160
|
-
};
|
|
4161
|
-
const chainType = this.chainType;
|
|
4162
|
-
const {
|
|
4163
|
-
data: gasInfo,
|
|
4164
|
-
success,
|
|
4165
|
-
message
|
|
4166
|
-
} = await this.transactions.queryGasInfo({
|
|
4167
|
-
chainType,
|
|
4168
|
-
params: queryGasParams
|
|
4169
|
-
});
|
|
4170
|
-
if (!success) {
|
|
4171
|
-
console.error("queryGasInfo evm", txs, message, gasInfo);
|
|
4172
|
-
const { gasFee } = SupportedChainTypes[chainType];
|
|
4173
|
-
return gasFee.toString();
|
|
4174
|
-
}
|
|
4175
|
-
const res = getAllTypeChainIds({ chainId, chainType });
|
|
4176
|
-
const { nativeCurrencyDecimals } = await this.networks.getNetworkByChainId(res.chainId);
|
|
4177
|
-
const { baseFee = 1, gasLimit = 0 } = gasInfo;
|
|
4178
|
-
const baseFeeBN = new BigNumber(baseFee);
|
|
4179
|
-
const calcFee = (feeLevel) => {
|
|
4180
|
-
const fee = baseFeeBN.plus(gasInfo[feeLevel] || 0);
|
|
4181
|
-
const gasFee = fee.times(gasLimit);
|
|
4182
|
-
return toHex(BigInt(gasFee.toNumber()), nativeCurrencyDecimals);
|
|
4183
|
-
};
|
|
4184
|
-
return calcFee("priorityFeeMedium");
|
|
4185
|
-
}
|
|
4186
4887
|
async createPublicClient({ chainId, rpcUrl }) {
|
|
4187
4888
|
if (rpcUrl) {
|
|
4188
4889
|
return createPublicClient({
|
|
@@ -4204,6 +4905,26 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4204
4905
|
transport: http(rpcUrl)
|
|
4205
4906
|
});
|
|
4206
4907
|
}
|
|
4908
|
+
async eth_estimateGas(txs) {
|
|
4909
|
+
const { from, to, value = "0x1", chainId } = txs[0] || {};
|
|
4910
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4911
|
+
if (accounts[0].address !== from) {
|
|
4912
|
+
throw new Error("address is not the current account");
|
|
4913
|
+
}
|
|
4914
|
+
if (!to) {
|
|
4915
|
+
throw new Error("to is not set");
|
|
4916
|
+
}
|
|
4917
|
+
try {
|
|
4918
|
+
const rpcClient = await this.createPublicClient({ chainId: chainId?.toString() });
|
|
4919
|
+
const gas = await rpcClient.estimateGas({ account: from, to, value: hexToBigInt(value) });
|
|
4920
|
+
return gas;
|
|
4921
|
+
} catch (error) {
|
|
4922
|
+
console.warn("Failed to estimate gas:", error);
|
|
4923
|
+
const BaseConfig3 = SupportedChainTypes[ChainTypes.EVM];
|
|
4924
|
+
const { gasFee } = BaseConfig3;
|
|
4925
|
+
return numberToHex(parseUnits(gasFee.toString(), 18));
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4207
4928
|
// Get nonce for current account
|
|
4208
4929
|
async eth_getTransactionCount([address, type]) {
|
|
4209
4930
|
const accounts = await this.accountInfo.getCurrent();
|
|
@@ -4232,30 +4953,35 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4232
4953
|
nonce: "number",
|
|
4233
4954
|
maxPriorityFeePerGas: "bigint",
|
|
4234
4955
|
maxFeePerGas: "bigint",
|
|
4235
|
-
|
|
4956
|
+
gasPrice: "bigint",
|
|
4236
4957
|
gas: "bigint"
|
|
4237
4958
|
};
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
txData = { ...txData, gas, gasLimit: gas };
|
|
4959
|
+
const txData = txParams?.[0];
|
|
4960
|
+
txData.gas = txData?.gas || txData?.gasLimit;
|
|
4241
4961
|
txData.data = txData.data || "0x";
|
|
4962
|
+
txData.value = txData.value || "0x";
|
|
4963
|
+
txData.nonce = txData.nonce || "0x0";
|
|
4964
|
+
if (!txData?.gas || !txData?.to || !txData?.chainId) {
|
|
4965
|
+
throw new Error("gas or to or chainId is not set");
|
|
4966
|
+
}
|
|
4242
4967
|
const accounts = await this.accountInfo.getCurrent();
|
|
4243
4968
|
const address = accounts[0].address;
|
|
4244
|
-
if (txData?.from && txData?.from
|
|
4969
|
+
if (txData?.from && !isAddressEqual(txData?.from, address)) {
|
|
4245
4970
|
console.error("eth_signTransaction error data: from is not the current account", txData);
|
|
4246
4971
|
throw new Error(`eth_signTransaction error data: from is not the current account`);
|
|
4247
4972
|
}
|
|
4248
4973
|
delete txData?.from;
|
|
4974
|
+
delete txData?.gasLimit;
|
|
4249
4975
|
const preparedTx = {
|
|
4250
|
-
|
|
4251
|
-
gas: txData?.gasLimit,
|
|
4252
|
-
account: address
|
|
4976
|
+
gas: txData?.gas
|
|
4253
4977
|
};
|
|
4254
4978
|
for (const key in preparedTxPropsType) {
|
|
4255
|
-
if (
|
|
4256
|
-
|
|
4979
|
+
if (txData?.[key] && isHex(txData?.[key])) {
|
|
4980
|
+
preparedTx[key] = preparedTxPropsType[key] === "number" ? fromHex$1(txData[key], "number") : txData[key];
|
|
4257
4981
|
}
|
|
4258
|
-
|
|
4982
|
+
}
|
|
4983
|
+
if (txData?.type !== "legacy") {
|
|
4984
|
+
delete preparedTx.gasPrice;
|
|
4259
4985
|
}
|
|
4260
4986
|
try {
|
|
4261
4987
|
const serializedTransaction = await this.accountInfo.signTransaction(
|
|
@@ -4301,7 +5027,7 @@ var EvmService = class _EvmService extends BaseService {
|
|
|
4301
5027
|
}
|
|
4302
5028
|
}
|
|
4303
5029
|
};
|
|
4304
|
-
var
|
|
5030
|
+
var BaseConfig2 = SupportedChainTypes[ChainTypes.SOL];
|
|
4305
5031
|
var TOKEN_METADATA_PROGRAM_ID = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
|
|
4306
5032
|
var MSG_PREFIX = "\xFFsolana offchain";
|
|
4307
5033
|
|
|
@@ -4312,7 +5038,7 @@ var txToHex = (tx) => {
|
|
|
4312
5038
|
if (tx instanceof VersionedTransaction) {
|
|
4313
5039
|
return Buffer.from(tx.serialize()).toString("hex");
|
|
4314
5040
|
}
|
|
4315
|
-
if (tx instanceof Transaction) {
|
|
5041
|
+
if (tx instanceof Transaction$1) {
|
|
4316
5042
|
return Buffer.from(
|
|
4317
5043
|
tx.serialize({
|
|
4318
5044
|
requireAllSignatures: false,
|
|
@@ -4326,7 +5052,7 @@ async function createLegacyTx({ from = "", to = "", amount = 0 }, connection) {
|
|
|
4326
5052
|
const fromPubkey = new PublicKey(from);
|
|
4327
5053
|
const toPubkey = new PublicKey(to);
|
|
4328
5054
|
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
4329
|
-
const transaction = new Transaction().add(
|
|
5055
|
+
const transaction = new Transaction$1().add(
|
|
4330
5056
|
SystemProgram.transfer({
|
|
4331
5057
|
fromPubkey,
|
|
4332
5058
|
//payer
|
|
@@ -4356,7 +5082,7 @@ async function createTokenLegacyTransaction2({
|
|
|
4356
5082
|
const fromTokenPubKey = await getAssociatedTokenAddress(tokenPublicKey, fromPubkey);
|
|
4357
5083
|
const toTokenPubKey = await getAssociatedTokenAddress(tokenPublicKey, toPubkey);
|
|
4358
5084
|
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
4359
|
-
const transaction = new Transaction();
|
|
5085
|
+
const transaction = new Transaction$1();
|
|
4360
5086
|
transaction.feePayer = fromPubkey;
|
|
4361
5087
|
transaction.recentBlockhash = blockhash;
|
|
4362
5088
|
try {
|
|
@@ -4376,13 +5102,6 @@ async function createTokenLegacyTransaction2({
|
|
|
4376
5102
|
throw error;
|
|
4377
5103
|
}
|
|
4378
5104
|
}
|
|
4379
|
-
|
|
4380
|
-
// src/dogecoin/base.ts
|
|
4381
|
-
function toBase58(data) {
|
|
4382
|
-
throw new Error("toBase58 requires bs58 package. Install it: pnpm add bs58");
|
|
4383
|
-
}
|
|
4384
|
-
|
|
4385
|
-
// src/solana/service.ts
|
|
4386
5105
|
var SolanaService = class _SolanaService extends BaseService {
|
|
4387
5106
|
static instance;
|
|
4388
5107
|
chainType;
|
|
@@ -4414,11 +5133,11 @@ var SolanaService = class _SolanaService extends BaseService {
|
|
|
4414
5133
|
return await this.accountInfo.getCurrent();
|
|
4415
5134
|
}
|
|
4416
5135
|
async getAccount() {
|
|
4417
|
-
const { currentAddress, publicKey } = await this.accountInfo.
|
|
5136
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
4418
5137
|
return { publicKey: publicKey || currentAddress, address: currentAddress };
|
|
4419
5138
|
}
|
|
4420
5139
|
async signMessage(params) {
|
|
4421
|
-
const { currentAddress, publicKey } = await this.accountInfo.
|
|
5140
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
4422
5141
|
const { message } = params;
|
|
4423
5142
|
const signature = await this.accountInfo.signMessage(message);
|
|
4424
5143
|
return {
|
|
@@ -4428,7 +5147,7 @@ var SolanaService = class _SolanaService extends BaseService {
|
|
|
4428
5147
|
};
|
|
4429
5148
|
}
|
|
4430
5149
|
async signIn(params) {
|
|
4431
|
-
const { currentAddress, publicKey } = await this.accountInfo.
|
|
5150
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
4432
5151
|
const { signature = "" } = await this.signMessage(params);
|
|
4433
5152
|
return {
|
|
4434
5153
|
address: currentAddress,
|
|
@@ -4445,7 +5164,7 @@ var SolanaService = class _SolanaService extends BaseService {
|
|
|
4445
5164
|
}
|
|
4446
5165
|
async _queryGasInfo(txData) {
|
|
4447
5166
|
const { tokenAddress, chainId, amount = 0, decimals = 9 } = txData;
|
|
4448
|
-
const value = tokenAddress ? "0x1" : toHex(parseUnits(amount.toString(), decimals));
|
|
5167
|
+
const value = tokenAddress ? "0x1" : toHex$1(parseUnits(amount.toString(), decimals));
|
|
4449
5168
|
let callData = "0x";
|
|
4450
5169
|
if (tokenAddress) {
|
|
4451
5170
|
const transaction = await createTokenLegacyTransaction2(txData, this.connection);
|
|
@@ -4475,7 +5194,7 @@ var SolanaService = class _SolanaService extends BaseService {
|
|
|
4475
5194
|
});
|
|
4476
5195
|
if (!success) {
|
|
4477
5196
|
console.error("queryGasInfo solana", txData, message, gasInfo);
|
|
4478
|
-
const { gasFee } =
|
|
5197
|
+
const { gasFee } = BaseConfig2;
|
|
4479
5198
|
return {
|
|
4480
5199
|
success: true,
|
|
4481
5200
|
gasFee: gasFee.toString()
|
|
@@ -4638,8 +5357,8 @@ var TomoWallet = class _TomoWallet extends BaseService {
|
|
|
4638
5357
|
}
|
|
4639
5358
|
async getChainInfo(chainType, chainId) {
|
|
4640
5359
|
this.networks.setChainType(chainType);
|
|
4641
|
-
const
|
|
4642
|
-
return
|
|
5360
|
+
const network2 = await this.networks.getNetworkByChainId(chainId);
|
|
5361
|
+
return network2;
|
|
4643
5362
|
}
|
|
4644
5363
|
//evm chains
|
|
4645
5364
|
async isChainSupported(chainType, chainId) {
|
|
@@ -4706,8 +5425,8 @@ var TomoWallet = class _TomoWallet extends BaseService {
|
|
|
4706
5425
|
// src/index.ts
|
|
4707
5426
|
var ChainTypeServices = {
|
|
4708
5427
|
[ChainTypes.EVM]: EvmService,
|
|
4709
|
-
[ChainTypes.SOL]: SolanaService
|
|
4710
|
-
|
|
5428
|
+
[ChainTypes.SOL]: SolanaService,
|
|
5429
|
+
[ChainTypes.DOGE]: DogecoinService
|
|
4711
5430
|
};
|
|
4712
5431
|
|
|
4713
|
-
export { AccountType, ChainTypeServices, EvmService, SolanaService, TomoWallet, TxTypes };
|
|
5432
|
+
export { AccountType, ChainTypeServices, rpc_exports as DogecoinAPI, DogecoinAddress, DogecoinService, DogecoinUtils, EvmService, SolanaService, TomoWallet, TransactionParser, TxTypes, addUsedUtxos, createPsbt, decodePsbt, getUsedUtxos, toBitcoin, toSatoshi };
|