@tomo-inc/chains-service 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1309 -1055
- package/dist/index.d.cts +283 -90
- package/dist/index.d.ts +283 -90
- package/dist/index.js +1280 -1036
- package/package.json +2 -2
- package/src/api/network-data.ts +14 -0
- package/src/api/network.ts +2 -3
- package/src/api/token.ts +6 -6
- package/src/base/service.ts +1 -1
- package/src/base/token.ts +1 -1
- package/src/dogecoin/rpc.ts +10 -191
- package/src/dogecoin/service.ts +74 -40
- package/src/dogecoin/utils-doge.ts +19 -6
- package/src/evm/service.ts +5 -5
- package/src/index.ts +8 -4
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ChainTypes, SupportedChainTypes,
|
|
2
|
-
import Bignumber, { BigNumber } from 'bignumber.js';
|
|
3
|
-
import { toHex, parseUnits, isAddressEqual, createPublicClient, http, hexToBigInt, numberToHex, isHex, fromHex, parseTransaction, formatUnits, encodeFunctionData, erc20Abi } from 'viem';
|
|
1
|
+
import { ChainTypes, SupportedChainTypes, TomoApiDomains, cache, getExplorerUrl } from '@tomo-inc/wallet-utils';
|
|
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
8
|
import { PublicKey, Connection, VersionedTransaction, sendAndConfirmRawTransaction, Transaction as Transaction$1, SystemProgram, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
|
8
|
-
import { Psbt, Transaction, address } from 'bitcoinjs-lib';
|
|
9
9
|
|
|
10
10
|
var __create = Object.create;
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
@@ -16,6 +16,10 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
16
16
|
var __commonJS = (cb, mod) => function __require() {
|
|
17
17
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
18
|
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
22
|
+
};
|
|
19
23
|
var __copyProps = (to, from, except, desc) => {
|
|
20
24
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
21
25
|
for (let key of __getOwnPropNames(from))
|
|
@@ -2897,24 +2901,6 @@ var Transactions = class {
|
|
|
2897
2901
|
return res?.data || {};
|
|
2898
2902
|
}
|
|
2899
2903
|
};
|
|
2900
|
-
function getConfig(tomoStage) {
|
|
2901
|
-
const domain = TomoApiDomains[tomoStage];
|
|
2902
|
-
if (!domain) {
|
|
2903
|
-
throw new Error("Invalid tomo stage");
|
|
2904
|
-
}
|
|
2905
|
-
return {
|
|
2906
|
-
rpcBaseUrl: `${domain}`,
|
|
2907
|
-
walletBaseUrl: `${domain}/wallet`,
|
|
2908
|
-
txBaseUrl: `${domain}/quote`,
|
|
2909
|
-
tokenBaseUrl: `${domain}/token`,
|
|
2910
|
-
userBaseUrl: `${domain}/user/api`
|
|
2911
|
-
};
|
|
2912
|
-
}
|
|
2913
|
-
var CONFIG = {
|
|
2914
|
-
prod: getConfig("prod"),
|
|
2915
|
-
pre: getConfig("pre"),
|
|
2916
|
-
dev: getConfig("dev")
|
|
2917
|
-
};
|
|
2918
2904
|
function generateSignature(config) {
|
|
2919
2905
|
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
2920
2906
|
const method = config.method.toUpperCase();
|
|
@@ -3276,6 +3262,20 @@ var loadNetworks = (WALLET_DOMAIN) => {
|
|
|
3276
3262
|
supportGift: false,
|
|
3277
3263
|
supportHistory: true
|
|
3278
3264
|
},
|
|
3265
|
+
{
|
|
3266
|
+
chainId: 36888,
|
|
3267
|
+
chainIndex: 3688800,
|
|
3268
|
+
name: "ABCORE",
|
|
3269
|
+
chainName: "AB Core",
|
|
3270
|
+
rpcUrls: ["https://rpc1.core.ab.org"],
|
|
3271
|
+
blockExplorerUrl: "https://explorer.core.ab.org",
|
|
3272
|
+
platformType: "EVM",
|
|
3273
|
+
isTestnet: false,
|
|
3274
|
+
icon: "/assets/ab.svg",
|
|
3275
|
+
supportSwap: true,
|
|
3276
|
+
supportGift: false,
|
|
3277
|
+
supportHistory: true
|
|
3278
|
+
},
|
|
3279
3279
|
{
|
|
3280
3280
|
chainId: 6281971,
|
|
3281
3281
|
chainIndex: 628197100,
|
|
@@ -3889,6 +3889,24 @@ function tomoPublicApiService(publicApiBase, tomoAppInfo) {
|
|
|
3889
3889
|
walletAPIs: WalletAPIs.getInstance(publicApiBase, tomoAppInfo)
|
|
3890
3890
|
};
|
|
3891
3891
|
}
|
|
3892
|
+
function getConfig(tomoStage) {
|
|
3893
|
+
const domain = TomoApiDomains[tomoStage];
|
|
3894
|
+
if (!domain) {
|
|
3895
|
+
throw new Error("Invalid tomo stage");
|
|
3896
|
+
}
|
|
3897
|
+
return {
|
|
3898
|
+
rpcBaseUrl: `${domain}`,
|
|
3899
|
+
walletBaseUrl: `${domain}/wallet`,
|
|
3900
|
+
txBaseUrl: `${domain}/quote`,
|
|
3901
|
+
tokenBaseUrl: `${domain}/token`,
|
|
3902
|
+
userBaseUrl: `${domain}/user/api`
|
|
3903
|
+
};
|
|
3904
|
+
}
|
|
3905
|
+
var CONFIG = {
|
|
3906
|
+
prod: getConfig("prod"),
|
|
3907
|
+
pre: getConfig("pre"),
|
|
3908
|
+
dev: getConfig("dev")
|
|
3909
|
+
};
|
|
3892
3910
|
|
|
3893
3911
|
// src/base/service.ts
|
|
3894
3912
|
var BaseService = class {
|
|
@@ -3914,571 +3932,934 @@ var BaseService = class {
|
|
|
3914
3932
|
this.approveParams = params;
|
|
3915
3933
|
}
|
|
3916
3934
|
};
|
|
3917
|
-
function getRPCClient(network2) {
|
|
3918
|
-
const { chainId, name, rpcUrls, nativeCurrencyDecimals, nativeCurrencyName, nativeCurrencySymbol } = network2;
|
|
3919
|
-
const myCustomChain = {
|
|
3920
|
-
id: Number(chainId) || chainId,
|
|
3921
|
-
name,
|
|
3922
|
-
nativeCurrency: {
|
|
3923
|
-
name: nativeCurrencyName,
|
|
3924
|
-
symbol: nativeCurrencySymbol,
|
|
3925
|
-
decimals: nativeCurrencyDecimals
|
|
3926
|
-
},
|
|
3927
|
-
rpcUrls: {
|
|
3928
|
-
default: {
|
|
3929
|
-
http: rpcUrls,
|
|
3930
|
-
webSocket: []
|
|
3931
|
-
},
|
|
3932
|
-
public: {
|
|
3933
|
-
http: rpcUrls,
|
|
3934
|
-
webSocket: []
|
|
3935
|
-
}
|
|
3936
|
-
},
|
|
3937
|
-
blockExplorers: {
|
|
3938
|
-
default: {
|
|
3939
|
-
name: "Explorer",
|
|
3940
|
-
url: rpcUrls[0]
|
|
3941
|
-
}
|
|
3942
|
-
}
|
|
3943
|
-
};
|
|
3944
|
-
const rpcClient = createPublicClient({
|
|
3945
|
-
chain: myCustomChain,
|
|
3946
|
-
pollingInterval: 1e4,
|
|
3947
|
-
cacheTime: 1e4,
|
|
3948
|
-
transport: http()
|
|
3949
|
-
});
|
|
3950
|
-
return { rpcClient };
|
|
3951
|
-
}
|
|
3952
|
-
|
|
3953
|
-
// src/types/account.ts
|
|
3954
|
-
var AccountType = /* @__PURE__ */ ((AccountType2) => {
|
|
3955
|
-
AccountType2["EOA"] = "EOA";
|
|
3956
|
-
AccountType2["SOCIAL"] = "SOCIAL";
|
|
3957
|
-
return AccountType2;
|
|
3958
|
-
})(AccountType || {});
|
|
3959
|
-
|
|
3960
|
-
// src/types/wallet.ts
|
|
3961
|
-
var TxTypes = /* @__PURE__ */ ((TxTypes2) => {
|
|
3962
|
-
TxTypes2[TxTypes2["swap"] = 1] = "swap";
|
|
3963
|
-
TxTypes2[TxTypes2["bridge"] = 2] = "bridge";
|
|
3964
|
-
TxTypes2[TxTypes2["receive"] = 31] = "receive";
|
|
3965
|
-
TxTypes2[TxTypes2["send"] = 32] = "send";
|
|
3966
|
-
TxTypes2[TxTypes2["approve"] = 4] = "approve";
|
|
3967
|
-
TxTypes2[TxTypes2["contractInteraction"] = 5] = "contractInteraction";
|
|
3968
|
-
TxTypes2[TxTypes2["redPocket"] = 6] = "redPocket";
|
|
3969
|
-
return TxTypes2;
|
|
3970
|
-
})(TxTypes || {});
|
|
3971
3935
|
|
|
3972
|
-
// src/
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
chainId = fromHex(chainId, "number").toString();
|
|
3980
|
-
return {
|
|
3981
|
-
chainId,
|
|
3982
|
-
chainIdHex: chainIdHex2,
|
|
3983
|
-
chainUid: `${chainType}:${chainId}`
|
|
3984
|
-
};
|
|
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);
|
|
3985
3943
|
}
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
}
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
return
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
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");
|
|
4005
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
|
+
};
|
|
4006
3993
|
|
|
4007
|
-
// src/
|
|
4008
|
-
var
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
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}`);
|
|
4014
4021
|
}
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
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");
|
|
4018
4028
|
}
|
|
4019
|
-
return
|
|
4029
|
+
return amount.dividedBy(KOINU_PER_DOGE).toNumber();
|
|
4030
|
+
} catch (error) {
|
|
4031
|
+
throw new Error(`toBitcoin failed: ${error.message}`);
|
|
4020
4032
|
}
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4033
|
+
}
|
|
4034
|
+
function addUsedUtxos(newUsedUtxos) {
|
|
4035
|
+
const usedUtxos = cache.get("UsedUtxos") || {};
|
|
4036
|
+
for (const txid in newUsedUtxos) {
|
|
4037
|
+
usedUtxos[txid] = 1;
|
|
4024
4038
|
}
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
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");
|
|
4028
4054
|
}
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
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);
|
|
4037
4065
|
}
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
const currentNetwork = await this.networks.getCurrentNetwork();
|
|
4041
|
-
return currentNetwork;
|
|
4066
|
+
if (accumulatedAmount < totalNeeded) {
|
|
4067
|
+
throw new Error("not enough funds to cover amount and fee");
|
|
4042
4068
|
}
|
|
4043
|
-
async
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
return await this.networks.setCurrentNetwork(chainId);
|
|
4050
|
-
}
|
|
4051
|
-
//evm chains
|
|
4052
|
-
async isChainSupported(chainId) {
|
|
4053
|
-
const chainType = this.chainType;
|
|
4054
|
-
const res = getAllTypeChainIds({ chainId, chainType });
|
|
4055
|
-
if (!res.chainId) {
|
|
4056
|
-
return false;
|
|
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 };
|
|
4057
4075
|
}
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
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);
|
|
4061
4094
|
}
|
|
4062
|
-
return isEvmChain(chainInfo);
|
|
4063
4095
|
}
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
if (!isAddressEqual(accounts[0].address, address)) {
|
|
4067
|
-
throw new Error("address is not the current account");
|
|
4068
|
-
}
|
|
4069
|
-
const signature = await this.accountInfo.signMessage(message);
|
|
4070
|
-
return signature;
|
|
4096
|
+
if (addedAmount < totalNeeded) {
|
|
4097
|
+
throw new Error("not enough funds to cover amount and fee");
|
|
4071
4098
|
}
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4099
|
+
const outputs = [
|
|
4100
|
+
{
|
|
4101
|
+
address: to,
|
|
4102
|
+
amount: sendAmount
|
|
4076
4103
|
}
|
|
4077
|
-
|
|
4078
|
-
|
|
4104
|
+
];
|
|
4105
|
+
const changeAmount = addedAmount - sendAmount - sendCost;
|
|
4106
|
+
if (changeAmount > 0) {
|
|
4107
|
+
outputs.push({
|
|
4108
|
+
address: from,
|
|
4109
|
+
amount: changeAmount
|
|
4110
|
+
});
|
|
4079
4111
|
}
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
const
|
|
4092
|
-
return
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
return "0";
|
|
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
|
+
});
|
|
4096
4127
|
}
|
|
4128
|
+
return Psbt.fromHex(psbt, {
|
|
4129
|
+
network
|
|
4130
|
+
});
|
|
4131
|
+
} catch (error) {
|
|
4132
|
+
console.error("Failed to decode PSBT:", error);
|
|
4133
|
+
throw error;
|
|
4097
4134
|
}
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
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));
|
|
4104
4145
|
}
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
const
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
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()
|
|
4115
4162
|
};
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
if (!success) {
|
|
4125
|
-
console.error("queryGasInfo evm", txData, queryGasParams, message, gasInfo);
|
|
4126
|
-
const BaseConfig3 = SupportedChainTypes[ChainTypes.EVM];
|
|
4127
|
-
const { gasFee } = BaseConfig3;
|
|
4128
|
-
return {
|
|
4129
|
-
success: true,
|
|
4130
|
-
gasFee: gasFee.toString()
|
|
4131
|
-
};
|
|
4132
|
-
}
|
|
4133
|
-
const nativeChainId = SupportedChainTypes[this.chainType].chainId;
|
|
4134
|
-
const { nativeCurrency } = await this.networks.getNetworkByChainId(nativeChainId);
|
|
4135
|
-
const { baseFee = 1, gasLimit = 0 } = gasInfo;
|
|
4136
|
-
const baseFeeBN = new BigNumber(baseFee);
|
|
4137
|
-
const calcFee = (feeLevel) => {
|
|
4138
|
-
const fee = baseFeeBN.plus(gasInfo[feeLevel] || 0);
|
|
4139
|
-
const gasFee = fee.times(gasLimit);
|
|
4140
|
-
return formatUnits(BigInt(gasFee.toNumber()), nativeCurrency?.decimals || 9);
|
|
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()
|
|
4141
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) {
|
|
4142
4187
|
return {
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
low: calcFee("priorityFeeLow"),
|
|
4146
|
-
medium: calcFee("priorityFeeMedium"),
|
|
4147
|
-
high: calcFee("priorityFeeHigh")
|
|
4148
|
-
},
|
|
4149
|
-
gasFee: calcFee("priorityFeeMedium"),
|
|
4150
|
-
baseFee,
|
|
4151
|
-
gasLimit,
|
|
4152
|
-
priorityFee: {
|
|
4153
|
-
low: gasInfo.priorityFeeLow,
|
|
4154
|
-
medium: gasInfo.priorityFeeMedium,
|
|
4155
|
-
high: gasInfo.priorityFeeHigh
|
|
4156
|
-
}
|
|
4188
|
+
address: "",
|
|
4189
|
+
balance: 0
|
|
4157
4190
|
};
|
|
4158
4191
|
}
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
const chainInfo = await this.networks.getNetworkByChainId(chainId);
|
|
4168
|
-
rpcUrl = chainInfo.rpcUrls[0];
|
|
4169
|
-
} else {
|
|
4170
|
-
const chainInfo = await this.getCurrentChain();
|
|
4171
|
-
rpcUrl = chainInfo.rpcUrls[0];
|
|
4172
|
-
}
|
|
4173
|
-
if (!rpcUrl) {
|
|
4174
|
-
throw new Error("rpcUrl is not set");
|
|
4175
|
-
}
|
|
4176
|
-
return createPublicClient({
|
|
4177
|
-
transport: http(rpcUrl)
|
|
4178
|
-
});
|
|
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 {};
|
|
4179
4200
|
}
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
const rpcClient = await this.createPublicClient({ chainId: chainId?.toString() });
|
|
4191
|
-
const gas = await rpcClient.estimateGas({ account: from, to, value: hexToBigInt(value) });
|
|
4192
|
-
return gas;
|
|
4193
|
-
} catch (error) {
|
|
4194
|
-
console.warn("Failed to estimate gas:", error);
|
|
4195
|
-
const BaseConfig3 = SupportedChainTypes[ChainTypes.EVM];
|
|
4196
|
-
const { gasFee } = BaseConfig3;
|
|
4197
|
-
return numberToHex(parseUnits(gasFee.toString(), 18));
|
|
4198
|
-
}
|
|
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);
|
|
4199
4211
|
}
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
const nonce = await rpcClient.getTransactionCount({ address });
|
|
4212
|
-
return nonce;
|
|
4213
|
-
} catch (error) {
|
|
4214
|
-
console.error("Failed to get nonce:", error);
|
|
4215
|
-
throw new Error(`Failed to get nonce: ${error}`);
|
|
4216
|
-
}
|
|
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;
|
|
4217
4223
|
}
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
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;
|
|
4238
4290
|
}
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
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;
|
|
4244
4359
|
}
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
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");
|
|
4253
4378
|
}
|
|
4379
|
+
psbt.addInput(inputOptions);
|
|
4254
4380
|
}
|
|
4255
|
-
|
|
4256
|
-
|
|
4381
|
+
for (const output of tx.outputs) {
|
|
4382
|
+
psbt.addOutput({
|
|
4383
|
+
value: BigInt(output?.amount || 0),
|
|
4384
|
+
address: output.address
|
|
4385
|
+
});
|
|
4257
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) {
|
|
4258
4416
|
try {
|
|
4259
|
-
const
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
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);
|
|
4267
4441
|
}
|
|
4268
|
-
const
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
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
|
+
);
|
|
4272
4463
|
}
|
|
4273
4464
|
}
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
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);
|
|
4284
4479
|
}
|
|
4480
|
+
return _DogecoinService.instance;
|
|
4285
4481
|
}
|
|
4286
|
-
async
|
|
4287
|
-
|
|
4288
|
-
|
|
4482
|
+
async requestAccounts() {
|
|
4483
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4484
|
+
return addresses.map((account) => account.address);
|
|
4485
|
+
}
|
|
4486
|
+
async getAccounts() {
|
|
4487
|
+
return await this.requestAccounts();
|
|
4488
|
+
}
|
|
4489
|
+
async getConnectionStatus() {
|
|
4490
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4491
|
+
const { address } = addresses?.[0] || {};
|
|
4492
|
+
if (!address) {
|
|
4493
|
+
throw new Error("address is not set");
|
|
4289
4494
|
}
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4495
|
+
return {
|
|
4496
|
+
connected: true,
|
|
4497
|
+
address,
|
|
4498
|
+
selectedWalletAddress: address
|
|
4499
|
+
};
|
|
4500
|
+
}
|
|
4501
|
+
async getBalance() {
|
|
4502
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4503
|
+
const { address } = addresses?.[0] || {};
|
|
4504
|
+
if (!address) {
|
|
4505
|
+
throw new Error("address is not set");
|
|
4299
4506
|
}
|
|
4507
|
+
const { balance = 0 } = await getBalance(address);
|
|
4508
|
+
return {
|
|
4509
|
+
confirmed: balance,
|
|
4510
|
+
unconfirmed: 0,
|
|
4511
|
+
total: balance
|
|
4512
|
+
};
|
|
4300
4513
|
}
|
|
4301
|
-
}
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
__toESM(require_bn());
|
|
4308
|
-
new PublicKey(TOKEN_METADATA_PROGRAM_ID);
|
|
4309
|
-
var txToHex = (tx) => {
|
|
4310
|
-
if (tx instanceof VersionedTransaction) {
|
|
4311
|
-
return Buffer.from(tx.serialize()).toString("hex");
|
|
4514
|
+
async signMessage({ message, type }) {
|
|
4515
|
+
if (type) {
|
|
4516
|
+
throw new Error("bip322simple not support.");
|
|
4517
|
+
}
|
|
4518
|
+
const signature = await this.accountInfo.signMessage(message);
|
|
4519
|
+
return signature;
|
|
4312
4520
|
}
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4521
|
+
async requestSignedMessage({
|
|
4522
|
+
message,
|
|
4523
|
+
type
|
|
4524
|
+
}) {
|
|
4525
|
+
const signedMessage = await this.signMessage({
|
|
4526
|
+
message,
|
|
4527
|
+
type
|
|
4528
|
+
});
|
|
4529
|
+
return {
|
|
4530
|
+
signedMessage
|
|
4531
|
+
};
|
|
4320
4532
|
}
|
|
4321
|
-
|
|
4322
|
-
};
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
}
|
|
4350
|
-
|
|
4351
|
-
const
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
const
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4533
|
+
async _queryGasInfo(txData) {
|
|
4534
|
+
const { chainId, amount = 0, decimals = 8 } = txData;
|
|
4535
|
+
const gasLimitParam = {
|
|
4536
|
+
from: txData.from,
|
|
4537
|
+
to: txData.to,
|
|
4538
|
+
value: toHex$1(parseUnits(amount.toString(), decimals))
|
|
4539
|
+
};
|
|
4540
|
+
const queryGasParams = {
|
|
4541
|
+
chainIndex: Number(chainId || BaseConfig.chainId),
|
|
4542
|
+
callData: "0x",
|
|
4543
|
+
gasLimitParam,
|
|
4544
|
+
addressList: [txData.from]
|
|
4545
|
+
};
|
|
4546
|
+
const {
|
|
4547
|
+
data: gasInfo,
|
|
4548
|
+
success,
|
|
4549
|
+
message
|
|
4550
|
+
} = await this.transactions.queryGasInfo({
|
|
4551
|
+
chainType: this.chainType,
|
|
4552
|
+
params: queryGasParams
|
|
4553
|
+
});
|
|
4554
|
+
if (!success) {
|
|
4555
|
+
console.error("queryGasInfo doge", success, txData, queryGasParams, gasInfo);
|
|
4556
|
+
const { gasFee } = BaseConfig;
|
|
4557
|
+
return {
|
|
4558
|
+
success: true,
|
|
4559
|
+
gasFee: gasFee.toString()
|
|
4560
|
+
};
|
|
4561
|
+
}
|
|
4562
|
+
const { baseFee = 1, gasLimit = 1 } = gasInfo;
|
|
4563
|
+
const calcFee = (feeLevel) => {
|
|
4564
|
+
const gasFee = new BigNumber(gasInfo[feeLevel] || baseFee).times(gasLimit);
|
|
4565
|
+
return toBitcoin(gasFee.toNumber());
|
|
4566
|
+
};
|
|
4567
|
+
const fees = {
|
|
4568
|
+
low: calcFee("priorityFeeLow"),
|
|
4569
|
+
medium: calcFee("priorityFeeMedium"),
|
|
4570
|
+
high: calcFee("priorityFeeHigh")
|
|
4571
|
+
};
|
|
4572
|
+
return {
|
|
4573
|
+
success: true,
|
|
4574
|
+
fees,
|
|
4575
|
+
gasFee: fees.high
|
|
4576
|
+
};
|
|
4577
|
+
}
|
|
4578
|
+
async requestPsbt({
|
|
4579
|
+
rawTx,
|
|
4580
|
+
signOnly
|
|
4581
|
+
}) {
|
|
4582
|
+
const psbtBase64 = await DogecoinUtils.rawTxToPsbtBase64(rawTx);
|
|
4583
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
4584
|
+
if (!signedPsbt) {
|
|
4585
|
+
return null;
|
|
4586
|
+
}
|
|
4587
|
+
const signedRawTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
|
|
4588
|
+
if (signOnly) {
|
|
4589
|
+
return {
|
|
4590
|
+
signedRawTx
|
|
4591
|
+
};
|
|
4592
|
+
}
|
|
4593
|
+
const { txid = "" } = await sendDogeTx(signedRawTx);
|
|
4594
|
+
return {
|
|
4595
|
+
txId: txid,
|
|
4596
|
+
signedRawTx
|
|
4597
|
+
};
|
|
4598
|
+
}
|
|
4599
|
+
async signPsbt({ psbtHex, options }) {
|
|
4600
|
+
const psbtBase64 = toBase64(fromHex(psbtHex));
|
|
4601
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
4602
|
+
if (!signedPsbt) {
|
|
4603
|
+
throw new Error("error psbtHex:" + psbtHex);
|
|
4604
|
+
}
|
|
4605
|
+
const signedRawTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
|
|
4606
|
+
return signedRawTx;
|
|
4607
|
+
}
|
|
4608
|
+
async signPsbts({ psbtHexs, options }) {
|
|
4609
|
+
throw new Error("not implemented");
|
|
4610
|
+
}
|
|
4611
|
+
async requestTransaction(txData) {
|
|
4612
|
+
const spendableUtxos = txData?.spendableUtxos;
|
|
4613
|
+
if (txData.amountMismatch) {
|
|
4614
|
+
throw new Error("balance_insufficient");
|
|
4615
|
+
}
|
|
4616
|
+
if (!spendableUtxos || spendableUtxos?.length === 0) {
|
|
4617
|
+
txData.spendableUtxos = await getSpendableUtxos(txData.from);
|
|
4618
|
+
}
|
|
4619
|
+
const { psbtBase64, usingUtxos } = await createPsbt(txData);
|
|
4620
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
4621
|
+
const signedTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
|
|
4622
|
+
if (signedTx === "") {
|
|
4623
|
+
throw new Error("Error: sign transaction err.");
|
|
4624
|
+
}
|
|
4360
4625
|
try {
|
|
4361
|
-
const
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
throw
|
|
4626
|
+
const { txid } = await sendDogeTx(signedTx);
|
|
4627
|
+
addUsedUtxos(usingUtxos);
|
|
4628
|
+
return { txId: txid };
|
|
4629
|
+
} catch (err) {
|
|
4630
|
+
throw new Error("Error: send transaction err." + JSON.stringify(err));
|
|
4366
4631
|
}
|
|
4367
|
-
const tokenAmount = new Bignumber(amount).multipliedBy(new Bignumber(10).pow(decimals)).toNumber();
|
|
4368
|
-
transaction.add(
|
|
4369
|
-
createTransferInstruction(fromTokenPubKey, toTokenPubKey, fromPubkey, tokenAmount, [], TOKEN_PROGRAM_ID)
|
|
4370
|
-
);
|
|
4371
|
-
return transaction;
|
|
4372
|
-
} catch (error) {
|
|
4373
|
-
console.error("Error creating transaction:", error);
|
|
4374
|
-
throw error;
|
|
4375
4632
|
}
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4633
|
+
async send({
|
|
4634
|
+
toAddress,
|
|
4635
|
+
amount,
|
|
4636
|
+
fee = 0,
|
|
4637
|
+
options
|
|
4638
|
+
}) {
|
|
4639
|
+
if (fee <= 0) {
|
|
4640
|
+
throw new Error("fee is required");
|
|
4641
|
+
}
|
|
4642
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4643
|
+
const { address } = addresses?.[0] || {};
|
|
4644
|
+
const txData = {
|
|
4645
|
+
to: toAddress,
|
|
4646
|
+
amount: toBitcoin(amount),
|
|
4647
|
+
fee: toBitcoin(fee || 0),
|
|
4648
|
+
from: address
|
|
4649
|
+
};
|
|
4650
|
+
const res = await this.requestTransaction(txData);
|
|
4651
|
+
return res?.txId || "";
|
|
4652
|
+
}
|
|
4653
|
+
async sendDogecoin({
|
|
4654
|
+
toAddress,
|
|
4655
|
+
amount,
|
|
4656
|
+
options,
|
|
4657
|
+
fee
|
|
4658
|
+
}) {
|
|
4659
|
+
return await this.send({ toAddress, amount, options, fee });
|
|
4385
4660
|
}
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4661
|
+
async getTransactionStatus({ txId }) {
|
|
4662
|
+
const transaction = await getTxDetail(txId);
|
|
4663
|
+
if (!transaction?.txid) {
|
|
4664
|
+
return null;
|
|
4665
|
+
}
|
|
4666
|
+
return {
|
|
4667
|
+
txId: transaction.txid,
|
|
4668
|
+
confirmations: transaction.confirmations,
|
|
4669
|
+
status: transaction.confirmations > 0 ? "confirmed" : "pending",
|
|
4670
|
+
dogeAmount: transaction.vout[0]?.value,
|
|
4671
|
+
blockTime: transaction.blockTime,
|
|
4672
|
+
address: transaction.vout[0]?.addresses[0]
|
|
4673
|
+
};
|
|
4391
4674
|
}
|
|
4392
|
-
|
|
4393
|
-
|
|
4675
|
+
};
|
|
4676
|
+
function getRPCClient(network2) {
|
|
4677
|
+
const { chainId, name, rpcUrls, nativeCurrencyDecimals, nativeCurrencyName, nativeCurrencySymbol } = network2;
|
|
4678
|
+
const myCustomChain = {
|
|
4679
|
+
id: Number(chainId) || chainId,
|
|
4680
|
+
name,
|
|
4681
|
+
nativeCurrency: {
|
|
4682
|
+
name: nativeCurrencyName,
|
|
4683
|
+
symbol: nativeCurrencySymbol,
|
|
4684
|
+
decimals: nativeCurrencyDecimals
|
|
4685
|
+
},
|
|
4686
|
+
rpcUrls: {
|
|
4687
|
+
default: {
|
|
4688
|
+
http: rpcUrls,
|
|
4689
|
+
webSocket: []
|
|
4690
|
+
},
|
|
4691
|
+
public: {
|
|
4692
|
+
http: rpcUrls,
|
|
4693
|
+
webSocket: []
|
|
4694
|
+
}
|
|
4695
|
+
},
|
|
4696
|
+
blockExplorers: {
|
|
4697
|
+
default: {
|
|
4698
|
+
name: "Explorer",
|
|
4699
|
+
url: rpcUrls[0]
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
4702
|
+
};
|
|
4703
|
+
const rpcClient = createPublicClient({
|
|
4704
|
+
chain: myCustomChain,
|
|
4705
|
+
pollingInterval: 1e4,
|
|
4706
|
+
cacheTime: 1e4,
|
|
4707
|
+
transport: http()
|
|
4708
|
+
});
|
|
4709
|
+
return { rpcClient };
|
|
4394
4710
|
}
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4711
|
+
|
|
4712
|
+
// src/types/account.ts
|
|
4713
|
+
var AccountType = /* @__PURE__ */ ((AccountType2) => {
|
|
4714
|
+
AccountType2["EOA"] = "EOA";
|
|
4715
|
+
AccountType2["SOCIAL"] = "SOCIAL";
|
|
4716
|
+
return AccountType2;
|
|
4717
|
+
})(AccountType || {});
|
|
4718
|
+
|
|
4719
|
+
// src/types/wallet.ts
|
|
4720
|
+
var TxTypes = /* @__PURE__ */ ((TxTypes2) => {
|
|
4721
|
+
TxTypes2[TxTypes2["swap"] = 1] = "swap";
|
|
4722
|
+
TxTypes2[TxTypes2["bridge"] = 2] = "bridge";
|
|
4723
|
+
TxTypes2[TxTypes2["receive"] = 31] = "receive";
|
|
4724
|
+
TxTypes2[TxTypes2["send"] = 32] = "send";
|
|
4725
|
+
TxTypes2[TxTypes2["approve"] = 4] = "approve";
|
|
4726
|
+
TxTypes2[TxTypes2["contractInteraction"] = 5] = "contractInteraction";
|
|
4727
|
+
TxTypes2[TxTypes2["redPocket"] = 6] = "redPocket";
|
|
4728
|
+
return TxTypes2;
|
|
4729
|
+
})(TxTypes || {});
|
|
4730
|
+
|
|
4731
|
+
// src/evm/utils.ts
|
|
4732
|
+
var isEvmChain = (network2) => {
|
|
4733
|
+
return network2 && network2?.platformType === "EVM";
|
|
4734
|
+
};
|
|
4735
|
+
var getAllTypeChainIds = ({ chainId, chainType }) => {
|
|
4736
|
+
if (isHex(chainId)) {
|
|
4737
|
+
const chainIdHex2 = chainId;
|
|
4738
|
+
chainId = fromHex$1(chainId, "number").toString();
|
|
4739
|
+
return {
|
|
4740
|
+
chainId,
|
|
4741
|
+
chainIdHex: chainIdHex2,
|
|
4742
|
+
chainUid: `${chainType}:${chainId}`
|
|
4743
|
+
};
|
|
4399
4744
|
}
|
|
4400
|
-
const
|
|
4401
|
-
return
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
}
|
|
4407
|
-
function
|
|
4408
|
-
|
|
4745
|
+
const chainIdHex = toHex$1(Number(chainId));
|
|
4746
|
+
return {
|
|
4747
|
+
chainId,
|
|
4748
|
+
chainIdHex,
|
|
4749
|
+
chainUid: `${chainType}:${chainId}`
|
|
4750
|
+
};
|
|
4751
|
+
};
|
|
4752
|
+
function createErc20TxData(params, token) {
|
|
4753
|
+
const { decimals, address: tokenAddress } = token;
|
|
4754
|
+
const value = parseUnits(params?.amount.toString(), decimals);
|
|
4755
|
+
const callData = encodeFunctionData({
|
|
4756
|
+
abi: erc20Abi,
|
|
4757
|
+
functionName: "transfer",
|
|
4758
|
+
args: [params.to, value]
|
|
4759
|
+
});
|
|
4760
|
+
return {
|
|
4761
|
+
...params,
|
|
4762
|
+
...{ amount: 0, data: callData, to: tokenAddress }
|
|
4763
|
+
};
|
|
4409
4764
|
}
|
|
4410
4765
|
|
|
4411
|
-
// src/
|
|
4412
|
-
var
|
|
4766
|
+
// src/evm/service.ts
|
|
4767
|
+
var EvmService = class _EvmService extends BaseService {
|
|
4413
4768
|
static instance;
|
|
4414
4769
|
chainType;
|
|
4415
|
-
rpcUrl;
|
|
4416
|
-
connection;
|
|
4417
4770
|
constructor(chainType, accountInfo, tomoAppInfo) {
|
|
4418
4771
|
super(tomoAppInfo, accountInfo);
|
|
4419
4772
|
this.chainType = chainType;
|
|
4420
|
-
const config = {
|
|
4421
|
-
commitment: "confirmed",
|
|
4422
|
-
disableRetryOnRateLimit: false,
|
|
4423
|
-
confirmTransactionInitialTimeout: 12e4,
|
|
4424
|
-
fetch: (url, options) => {
|
|
4425
|
-
return fetch(url, { ...options });
|
|
4426
|
-
}
|
|
4427
|
-
};
|
|
4428
|
-
const domain = TomoApiDomains[tomoAppInfo.tomoStage];
|
|
4429
|
-
const rpcUrl = `${domain}/rpc/v1/solana`;
|
|
4430
|
-
this.rpcUrl = rpcUrl;
|
|
4431
|
-
this.connection = new Connection(rpcUrl, config);
|
|
4432
4773
|
}
|
|
4433
4774
|
static getInstance(chainType, accountInfo, tomoAppInfo) {
|
|
4434
|
-
if (!
|
|
4435
|
-
|
|
4775
|
+
if (!_EvmService.instance) {
|
|
4776
|
+
_EvmService.instance = new _EvmService(chainType, accountInfo, tomoAppInfo);
|
|
4436
4777
|
}
|
|
4437
|
-
return
|
|
4778
|
+
return _EvmService.instance;
|
|
4438
4779
|
}
|
|
4439
|
-
async
|
|
4440
|
-
|
|
4780
|
+
async eth_requestAccounts() {
|
|
4781
|
+
const res = await this.eth_accounts();
|
|
4782
|
+
return res;
|
|
4441
4783
|
}
|
|
4442
|
-
async
|
|
4443
|
-
const
|
|
4444
|
-
return
|
|
4784
|
+
async eth_accounts() {
|
|
4785
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4786
|
+
return accounts.map((account) => account.address);
|
|
4445
4787
|
}
|
|
4446
|
-
async
|
|
4447
|
-
const
|
|
4448
|
-
const
|
|
4788
|
+
async eth_chainId() {
|
|
4789
|
+
const chainType = this.chainType;
|
|
4790
|
+
const currentNetwork = await this.getCurrentChain();
|
|
4791
|
+
const { chainIdHex } = getAllTypeChainIds({
|
|
4792
|
+
chainId: currentNetwork?.chainId,
|
|
4793
|
+
chainType
|
|
4794
|
+
});
|
|
4795
|
+
return chainIdHex;
|
|
4796
|
+
}
|
|
4797
|
+
async getCurrentChain() {
|
|
4798
|
+
this.networks.setChainType(this.chainType);
|
|
4799
|
+
const currentNetwork = await this.networks.getCurrentNetwork();
|
|
4800
|
+
return currentNetwork;
|
|
4801
|
+
}
|
|
4802
|
+
async wallet_switchEthereumChain(networks) {
|
|
4803
|
+
const { chainId } = networks[0];
|
|
4804
|
+
const isSupported = await this.isChainSupported(chainId);
|
|
4805
|
+
if (!isSupported) {
|
|
4806
|
+
throw new Error(`Chain ${chainId} is not supported`);
|
|
4807
|
+
}
|
|
4808
|
+
return await this.networks.setCurrentNetwork(chainId);
|
|
4809
|
+
}
|
|
4810
|
+
//evm chains
|
|
4811
|
+
async isChainSupported(chainId) {
|
|
4812
|
+
const chainType = this.chainType;
|
|
4813
|
+
const res = getAllTypeChainIds({ chainId, chainType });
|
|
4814
|
+
if (!res.chainId) {
|
|
4815
|
+
return false;
|
|
4816
|
+
}
|
|
4817
|
+
const chainInfo = await this.networks.getNetworkByChainId(res.chainId);
|
|
4818
|
+
if (!chainInfo) {
|
|
4819
|
+
return false;
|
|
4820
|
+
}
|
|
4821
|
+
return isEvmChain(chainInfo);
|
|
4822
|
+
}
|
|
4823
|
+
async personal_sign([message, address]) {
|
|
4824
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4825
|
+
if (!isAddressEqual(accounts[0].address, address)) {
|
|
4826
|
+
throw new Error("address is not the current account");
|
|
4827
|
+
}
|
|
4449
4828
|
const signature = await this.accountInfo.signMessage(message);
|
|
4450
|
-
return
|
|
4451
|
-
message: MSG_PREFIX + message,
|
|
4452
|
-
signature,
|
|
4453
|
-
publicKey: publicKey || currentAddress
|
|
4454
|
-
};
|
|
4829
|
+
return signature;
|
|
4455
4830
|
}
|
|
4456
|
-
async
|
|
4457
|
-
const
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
signedMessage: MSG_PREFIX + params.message
|
|
4464
|
-
};
|
|
4831
|
+
async eth_signTypedData_v4([address, typedData]) {
|
|
4832
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4833
|
+
if (!isAddressEqual(accounts[0].address, address)) {
|
|
4834
|
+
throw new Error("address is not the current account");
|
|
4835
|
+
}
|
|
4836
|
+
const signature = await this.accountInfo.signTypedData(typedData);
|
|
4837
|
+
return signature;
|
|
4465
4838
|
}
|
|
4466
|
-
async
|
|
4467
|
-
|
|
4468
|
-
|
|
4839
|
+
async eth_getBalance([address, type]) {
|
|
4840
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4841
|
+
if (!isAddressEqual(accounts[0].address, address)) {
|
|
4842
|
+
throw new Error("address is not the current account");
|
|
4843
|
+
}
|
|
4844
|
+
if (type !== "latest") {
|
|
4845
|
+
throw new Error("type is not supported");
|
|
4846
|
+
}
|
|
4847
|
+
const chainInfo = await this.getCurrentChain();
|
|
4848
|
+
const { rpcClient } = getRPCClient(chainInfo);
|
|
4849
|
+
try {
|
|
4850
|
+
const balance = await rpcClient.getBalance({ address });
|
|
4851
|
+
return balance?.toString() || "0";
|
|
4852
|
+
} catch (error) {
|
|
4853
|
+
console.error(error);
|
|
4854
|
+
return "0";
|
|
4469
4855
|
}
|
|
4470
|
-
return this[method](params);
|
|
4471
4856
|
}
|
|
4472
4857
|
async _queryGasInfo(txData) {
|
|
4473
4858
|
const { tokenAddress, chainId, amount = 0, decimals = 9 } = txData;
|
|
4474
|
-
const value = tokenAddress ? "0x1" : toHex(parseUnits(amount.toString(), decimals));
|
|
4859
|
+
const value = tokenAddress ? "0x1" : toHex$1(parseUnits(amount.toString(), decimals));
|
|
4475
4860
|
let callData = "0x";
|
|
4476
4861
|
if (tokenAddress) {
|
|
4477
|
-
|
|
4478
|
-
callData = "0x" + txToHex(transaction);
|
|
4479
|
-
} else {
|
|
4480
|
-
const transaction = await createLegacyTx(txData, this.connection);
|
|
4481
|
-
callData = "0x" + txToHex(transaction);
|
|
4862
|
+
callData = createErc20TxData(txData, { decimals, address: tokenAddress })?.data;
|
|
4482
4863
|
}
|
|
4483
4864
|
const gasLimitParam = {
|
|
4484
4865
|
from: txData.from,
|
|
@@ -4500,8 +4881,9 @@ var SolanaService = class _SolanaService extends BaseService {
|
|
|
4500
4881
|
params: queryGasParams
|
|
4501
4882
|
});
|
|
4502
4883
|
if (!success) {
|
|
4503
|
-
console.error("queryGasInfo
|
|
4504
|
-
const
|
|
4884
|
+
console.error("queryGasInfo evm", txData, queryGasParams, message, gasInfo);
|
|
4885
|
+
const BaseConfig3 = SupportedChainTypes[ChainTypes.EVM];
|
|
4886
|
+
const { gasFee } = BaseConfig3;
|
|
4505
4887
|
return {
|
|
4506
4888
|
success: true,
|
|
4507
4889
|
gasFee: gasFee.toString()
|
|
@@ -4510,528 +4892,326 @@ var SolanaService = class _SolanaService extends BaseService {
|
|
|
4510
4892
|
const nativeChainId = SupportedChainTypes[this.chainType].chainId;
|
|
4511
4893
|
const { nativeCurrency } = await this.networks.getNetworkByChainId(nativeChainId);
|
|
4512
4894
|
const { baseFee = 1, gasLimit = 0 } = gasInfo;
|
|
4513
|
-
const baseFeeBN = new BigNumber(baseFee)
|
|
4514
|
-
const gasLimitBN = new BigNumber(gasLimit);
|
|
4895
|
+
const baseFeeBN = new BigNumber(baseFee);
|
|
4515
4896
|
const calcFee = (feeLevel) => {
|
|
4516
|
-
const
|
|
4517
|
-
const gasFee =
|
|
4897
|
+
const fee = baseFeeBN.plus(gasInfo[feeLevel] || 0);
|
|
4898
|
+
const gasFee = fee.times(gasLimit);
|
|
4518
4899
|
return formatUnits(BigInt(gasFee.toNumber()), nativeCurrency?.decimals || 9);
|
|
4519
4900
|
};
|
|
4520
4901
|
return {
|
|
4521
|
-
success
|
|
4902
|
+
success,
|
|
4522
4903
|
fees: {
|
|
4523
4904
|
low: calcFee("priorityFeeLow"),
|
|
4524
4905
|
medium: calcFee("priorityFeeMedium"),
|
|
4525
4906
|
high: calcFee("priorityFeeHigh")
|
|
4526
4907
|
},
|
|
4527
|
-
gasFee: calcFee("
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
VersionedTransaction.deserialize(hexBuffer);
|
|
4535
|
-
rawTx = hexBuffer;
|
|
4536
|
-
} catch (error) {
|
|
4537
|
-
const base58Buffer = Buffer.from(toBase58());
|
|
4538
|
-
VersionedTransaction.deserialize(base58Buffer);
|
|
4539
|
-
rawTx = base58Buffer;
|
|
4540
|
-
}
|
|
4541
|
-
const transaction = VersionedTransaction.deserialize(rawTx);
|
|
4542
|
-
if (!transaction.signatures || transaction.signatures.length === 0) {
|
|
4543
|
-
throw new Error("lack of sign");
|
|
4544
|
-
}
|
|
4545
|
-
const confirmationStrategy = {
|
|
4546
|
-
commitment: "confirmed",
|
|
4547
|
-
preflightCommitment: "processed",
|
|
4548
|
-
skipPreflight: false,
|
|
4549
|
-
maxRetries: 5
|
|
4550
|
-
};
|
|
4551
|
-
try {
|
|
4552
|
-
const simulation = await this.connection.simulateTransaction(transaction);
|
|
4553
|
-
if (simulation.value.err) {
|
|
4554
|
-
const logs = simulation.value.logs || [];
|
|
4555
|
-
const message = logs[logs.length - 1] || simulation.value.err || "Unknown error occurred";
|
|
4556
|
-
console.error("send err logs:", logs, message);
|
|
4557
|
-
throw new Error(`Transaction failed: ${message}`);
|
|
4558
|
-
}
|
|
4559
|
-
const signature = await sendAndConfirmRawTransaction(this.connection, rawTx, confirmationStrategy);
|
|
4560
|
-
return signature;
|
|
4561
|
-
} catch (error) {
|
|
4562
|
-
if (error.message.includes("TransactionExpiredTimeoutError")) {
|
|
4563
|
-
const status = await this.connection.getSignatureStatus(transaction.signatures[0].toString());
|
|
4564
|
-
if (status.value?.confirmationStatus === "confirmed") {
|
|
4565
|
-
return transaction.signatures[0].toString();
|
|
4566
|
-
}
|
|
4567
|
-
}
|
|
4568
|
-
throw error;
|
|
4569
|
-
}
|
|
4570
|
-
}
|
|
4571
|
-
async signTransaction({ rawTransaction }) {
|
|
4572
|
-
const params = {
|
|
4573
|
-
rawTransaction,
|
|
4574
|
-
walletId: -1,
|
|
4575
|
-
rpcUrl: this.rpcUrl
|
|
4576
|
-
};
|
|
4577
|
-
const signature = await this.accountInfo.signTransaction(JSON.stringify(params));
|
|
4578
|
-
return signature;
|
|
4579
|
-
}
|
|
4580
|
-
async signAndSendTransaction({ rawTransaction }) {
|
|
4581
|
-
const signedTx = await this.signTransaction({ rawTransaction });
|
|
4582
|
-
if (!signedTx) {
|
|
4583
|
-
return "";
|
|
4584
|
-
}
|
|
4585
|
-
const signature = await this.sendSignedTx(signedTx);
|
|
4586
|
-
return signature;
|
|
4587
|
-
}
|
|
4588
|
-
async signAllTransactions({ rawTransactions }) {
|
|
4589
|
-
throw new Error("no support");
|
|
4590
|
-
}
|
|
4591
|
-
async signAndSendAllTransactions({ rawTransactions }) {
|
|
4592
|
-
throw new Error("no support");
|
|
4593
|
-
}
|
|
4594
|
-
async queryRent(params) {
|
|
4595
|
-
const MIN_ACCOUNT_ACTIVATION_SOL = "0.0009";
|
|
4596
|
-
const SPL_TOKEN_ACCOUNT_CREATION_FEE = "0.001";
|
|
4597
|
-
try {
|
|
4598
|
-
const { toAddress, tokenAddress } = params;
|
|
4599
|
-
let minTransferAmount = "0";
|
|
4600
|
-
let createSplTokenFee = null;
|
|
4601
|
-
if (tokenAddress) {
|
|
4602
|
-
const tokenAccountExists = await this.checkTokenAccount(toAddress, tokenAddress);
|
|
4603
|
-
if (!tokenAccountExists) {
|
|
4604
|
-
createSplTokenFee = SPL_TOKEN_ACCOUNT_CREATION_FEE;
|
|
4605
|
-
}
|
|
4606
|
-
} else {
|
|
4607
|
-
const accountExists = await this.checkAccountExists(toAddress);
|
|
4608
|
-
if (!accountExists) {
|
|
4609
|
-
minTransferAmount = MIN_ACCOUNT_ACTIVATION_SOL;
|
|
4610
|
-
}
|
|
4908
|
+
gasFee: calcFee("priorityFeeMedium"),
|
|
4909
|
+
baseFee,
|
|
4910
|
+
gasLimit,
|
|
4911
|
+
priorityFee: {
|
|
4912
|
+
low: gasInfo.priorityFeeLow,
|
|
4913
|
+
medium: gasInfo.priorityFeeMedium,
|
|
4914
|
+
high: gasInfo.priorityFeeHigh
|
|
4611
4915
|
}
|
|
4612
|
-
|
|
4613
|
-
minTransferAmount,
|
|
4614
|
-
createSplTokenFee
|
|
4615
|
-
};
|
|
4616
|
-
return rentInfo;
|
|
4617
|
-
} catch (error) {
|
|
4618
|
-
console.error("queryRent error:", error);
|
|
4619
|
-
const defaultRentInfo = {
|
|
4620
|
-
minTransferAmount: "0",
|
|
4621
|
-
createSplTokenFee: null
|
|
4622
|
-
};
|
|
4623
|
-
return defaultRentInfo;
|
|
4624
|
-
}
|
|
4916
|
+
};
|
|
4625
4917
|
}
|
|
4626
|
-
async
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4918
|
+
async createPublicClient({ chainId, rpcUrl }) {
|
|
4919
|
+
if (rpcUrl) {
|
|
4920
|
+
return createPublicClient({
|
|
4921
|
+
transport: http(rpcUrl)
|
|
4922
|
+
});
|
|
4923
|
+
}
|
|
4924
|
+
if (chainId) {
|
|
4925
|
+
this.networks.setChainType(this.chainType);
|
|
4926
|
+
const chainInfo = await this.networks.getNetworkByChainId(chainId);
|
|
4927
|
+
rpcUrl = chainInfo.rpcUrls[0];
|
|
4928
|
+
} else {
|
|
4929
|
+
const chainInfo = await this.getCurrentChain();
|
|
4930
|
+
rpcUrl = chainInfo.rpcUrls[0];
|
|
4931
|
+
}
|
|
4932
|
+
if (!rpcUrl) {
|
|
4933
|
+
throw new Error("rpcUrl is not set");
|
|
4633
4934
|
}
|
|
4935
|
+
return createPublicClient({
|
|
4936
|
+
transport: http(rpcUrl)
|
|
4937
|
+
});
|
|
4634
4938
|
}
|
|
4635
|
-
async
|
|
4939
|
+
async eth_estimateGas(txs) {
|
|
4940
|
+
const { from, to, value = "0x1", chainId } = txs[0] || {};
|
|
4941
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4942
|
+
if (accounts[0].address !== from) {
|
|
4943
|
+
throw new Error("address is not the current account");
|
|
4944
|
+
}
|
|
4945
|
+
if (!to) {
|
|
4946
|
+
throw new Error("to is not set");
|
|
4947
|
+
}
|
|
4636
4948
|
try {
|
|
4637
|
-
const
|
|
4638
|
-
const
|
|
4639
|
-
|
|
4640
|
-
const accountInfo = await this.connection.getAccountInfo(associatedTokenAddress);
|
|
4641
|
-
return accountInfo !== null;
|
|
4949
|
+
const rpcClient = await this.createPublicClient({ chainId: chainId?.toString() });
|
|
4950
|
+
const gas = await rpcClient.estimateGas({ account: from, to, value: hexToBigInt(value) });
|
|
4951
|
+
return gas;
|
|
4642
4952
|
} catch (error) {
|
|
4643
|
-
console.warn("Failed to
|
|
4644
|
-
|
|
4953
|
+
console.warn("Failed to estimate gas:", error);
|
|
4954
|
+
const BaseConfig3 = SupportedChainTypes[ChainTypes.EVM];
|
|
4955
|
+
const { gasFee } = BaseConfig3;
|
|
4956
|
+
return numberToHex(parseUnits(gasFee.toString(), 18));
|
|
4645
4957
|
}
|
|
4646
4958
|
}
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
var RPC_TIMEOUT = 20 * 1e3;
|
|
4653
|
-
var network = {
|
|
4654
|
-
messagePrefix: "Dogecoin Signed Message:\n",
|
|
4655
|
-
bech32: "dc",
|
|
4656
|
-
bip44: 3,
|
|
4657
|
-
bip32: {
|
|
4658
|
-
public: 49990397,
|
|
4659
|
-
private: 49988504
|
|
4660
|
-
},
|
|
4661
|
-
pubKeyHash: 30,
|
|
4662
|
-
scriptHash: 22,
|
|
4663
|
-
wif: 158
|
|
4664
|
-
};
|
|
4665
|
-
|
|
4666
|
-
// src/dogecoin/utils-doge.ts
|
|
4667
|
-
var DogecoinUtils = class {
|
|
4668
|
-
/**
|
|
4669
|
-
* Build the PSBT from the transaction
|
|
4670
|
-
* @param tx utxoTx
|
|
4671
|
-
* @param _maximumFeeRate number (optional, currently unused)
|
|
4672
|
-
* @returns base64
|
|
4673
|
-
*/
|
|
4674
|
-
static buildPsbtToBase64(tx, _maximumFeeRate) {
|
|
4675
|
-
const psbt = new Psbt({ network });
|
|
4676
|
-
for (const input of tx.inputs) {
|
|
4677
|
-
const inputOptions = {
|
|
4678
|
-
hash: input.txId,
|
|
4679
|
-
index: input.vOut
|
|
4680
|
-
};
|
|
4681
|
-
if (input.nonWitnessUtxo) {
|
|
4682
|
-
inputOptions.nonWitnessUtxo = Buffer.from(input.nonWitnessUtxo, "hex");
|
|
4683
|
-
}
|
|
4684
|
-
psbt.addInput(inputOptions);
|
|
4959
|
+
// Get nonce for current account
|
|
4960
|
+
async eth_getTransactionCount([address, type]) {
|
|
4961
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4962
|
+
if (accounts[0].address !== address) {
|
|
4963
|
+
throw new Error("address is not the current account");
|
|
4685
4964
|
}
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
value: output.amount,
|
|
4689
|
-
address: output.address
|
|
4690
|
-
});
|
|
4965
|
+
if (type !== "latest" && type !== "pending") {
|
|
4966
|
+
throw new Error("type is not supported");
|
|
4691
4967
|
}
|
|
4692
|
-
const psbtHex = psbt.toHex();
|
|
4693
|
-
return toBase64(fromHex3(psbtHex));
|
|
4694
|
-
}
|
|
4695
|
-
/**
|
|
4696
|
-
* Extract the transaction from the signed PSBT
|
|
4697
|
-
* @param signedPsbt base64
|
|
4698
|
-
* @param _maximumFeeRate number (optional, currently unused)
|
|
4699
|
-
* @returns transaction hex
|
|
4700
|
-
*/
|
|
4701
|
-
static extractPsbtTransaction(signedPsbt, _maximumFeeRate) {
|
|
4702
|
-
const signedPsbtHex = toHex3(fromBase64(signedPsbt));
|
|
4703
|
-
const psbt = Psbt.fromHex(signedPsbtHex, {
|
|
4704
|
-
network
|
|
4705
|
-
});
|
|
4706
|
-
const maximumFeeRate = _maximumFeeRate || 1e5;
|
|
4707
|
-
psbt.setMaximumFeeRate(maximumFeeRate);
|
|
4708
|
-
psbt.finalizeAllInputs();
|
|
4709
|
-
const transaction = psbt.extractTransaction();
|
|
4710
|
-
return transaction.toHex();
|
|
4711
|
-
}
|
|
4712
|
-
/**
|
|
4713
|
-
* Convert raw transaction hex to PSBT base64
|
|
4714
|
-
* This method converts an unsigned raw transaction to PSBT format.
|
|
4715
|
-
* Note: The rawTx should be an unsigned transaction, and the nonWitnessUtxo
|
|
4716
|
-
* for each input will need to be fetched separately from the blockchain.
|
|
4717
|
-
* @param rawTx hex string of the raw transaction
|
|
4718
|
-
* @returns base64 string of the PSBT
|
|
4719
|
-
*/
|
|
4720
|
-
static async rawTxToPsbtBase64(rawTx) {
|
|
4721
4968
|
try {
|
|
4722
|
-
const
|
|
4723
|
-
const
|
|
4724
|
-
|
|
4725
|
-
for (let i = 0; i < transaction.ins.length; i++) {
|
|
4726
|
-
const input = transaction.ins[i];
|
|
4727
|
-
const hash = Buffer.from(input.hash).reverse().toString("hex");
|
|
4728
|
-
const index = input.index;
|
|
4729
|
-
const inputOptions = {
|
|
4730
|
-
hash,
|
|
4731
|
-
index,
|
|
4732
|
-
nonWitnessUtxo: input.script
|
|
4733
|
-
};
|
|
4734
|
-
psbt.addInput(inputOptions);
|
|
4735
|
-
}
|
|
4736
|
-
for (const output of transaction.outs) {
|
|
4737
|
-
try {
|
|
4738
|
-
const address$1 = address.fromOutputScript(output.script, network);
|
|
4739
|
-
psbt.addOutput({
|
|
4740
|
-
address: address$1,
|
|
4741
|
-
value: Number(output.value)
|
|
4742
|
-
});
|
|
4743
|
-
} catch (error) {
|
|
4744
|
-
psbt.addOutput({
|
|
4745
|
-
script: output.script,
|
|
4746
|
-
value: Number(output.value)
|
|
4747
|
-
});
|
|
4748
|
-
}
|
|
4749
|
-
}
|
|
4750
|
-
const psbtHex = psbt.toHex();
|
|
4751
|
-
return toBase64(fromHex3(psbtHex));
|
|
4969
|
+
const rpcClient = await this.createPublicClient({});
|
|
4970
|
+
const nonce = await rpcClient.getTransactionCount({ address });
|
|
4971
|
+
return nonce;
|
|
4752
4972
|
} catch (error) {
|
|
4753
|
-
console.
|
|
4754
|
-
throw new Error(
|
|
4755
|
-
`Failed to convert raw transaction to PSBT: ${error instanceof Error ? error.message : String(error)}`
|
|
4756
|
-
);
|
|
4973
|
+
console.error("Failed to get nonce:", error);
|
|
4974
|
+
throw new Error(`Failed to get nonce: ${error}`);
|
|
4757
4975
|
}
|
|
4758
4976
|
}
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4977
|
+
//https://docs.metamask.io/snaps/reference/keyring-api/chain-methods/#eth_signtransaction
|
|
4978
|
+
async eth_signTransaction(txParams) {
|
|
4979
|
+
const preparedTxPropsType = {
|
|
4980
|
+
chainId: "number",
|
|
4981
|
+
to: "string",
|
|
4982
|
+
value: "bigint",
|
|
4983
|
+
data: "string",
|
|
4984
|
+
nonce: "number",
|
|
4985
|
+
maxPriorityFeePerGas: "bigint",
|
|
4986
|
+
maxFeePerGas: "bigint",
|
|
4987
|
+
gasPrice: "bigint",
|
|
4988
|
+
gas: "bigint"
|
|
4989
|
+
};
|
|
4990
|
+
const txData = txParams?.[0];
|
|
4991
|
+
txData.gas = txData?.gas || txData?.gasLimit;
|
|
4992
|
+
txData.data = txData.data || "0x";
|
|
4993
|
+
txData.value = txData.value || "0x";
|
|
4994
|
+
txData.nonce = txData.nonce || "0x0";
|
|
4995
|
+
if (!txData?.gas || !txData?.to || !txData?.chainId) {
|
|
4996
|
+
throw new Error("gas or to or chainId is not set");
|
|
4766
4997
|
}
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4998
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4999
|
+
const address = accounts[0].address;
|
|
5000
|
+
if (txData?.from && !isAddressEqual(txData?.from, address)) {
|
|
5001
|
+
console.error("eth_signTransaction error data: from is not the current account", txData);
|
|
5002
|
+
throw new Error(`eth_signTransaction error data: from is not the current account`);
|
|
5003
|
+
}
|
|
5004
|
+
delete txData?.from;
|
|
5005
|
+
delete txData?.gasLimit;
|
|
5006
|
+
const preparedTx = {
|
|
5007
|
+
gas: txData?.gas
|
|
5008
|
+
};
|
|
5009
|
+
for (const key in preparedTxPropsType) {
|
|
5010
|
+
if (txData?.[key] && isHex(txData?.[key])) {
|
|
5011
|
+
preparedTx[key] = preparedTxPropsType[key] === "number" ? fromHex$1(txData[key], "number") : txData[key];
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
5014
|
+
if (txData?.type !== "legacy") {
|
|
5015
|
+
delete preparedTx.gasPrice;
|
|
4777
5016
|
}
|
|
4778
|
-
return amount.dividedBy(KOINU_PER_DOGE).toNumber();
|
|
4779
|
-
} catch (error) {
|
|
4780
|
-
throw new Error(`toBitcoin failed: ${error.message}`);
|
|
4781
|
-
}
|
|
4782
|
-
}
|
|
4783
|
-
function addUsedUtxos(newUsedUtxos) {
|
|
4784
|
-
const usedUtxos = cache.get("UsedUtxos") || {};
|
|
4785
|
-
for (const txid in newUsedUtxos) {
|
|
4786
|
-
usedUtxos[txid] = 1;
|
|
4787
|
-
}
|
|
4788
|
-
cache.set("UsedUtxos", usedUtxos, false);
|
|
4789
|
-
}
|
|
4790
|
-
async function createPsbt({
|
|
4791
|
-
from,
|
|
4792
|
-
to,
|
|
4793
|
-
amount,
|
|
4794
|
-
fee,
|
|
4795
|
-
spendableUtxos
|
|
4796
|
-
}) {
|
|
4797
|
-
const utxos = spendableUtxos;
|
|
4798
|
-
if (!utxos || utxos.length === 0) {
|
|
4799
|
-
throw new Error("no spendable utxos");
|
|
4800
|
-
}
|
|
4801
|
-
const sendAmount = toSatoshi(amount);
|
|
4802
|
-
const sendCost = toSatoshi(fee);
|
|
4803
|
-
const totalNeeded = sendAmount + sendCost;
|
|
4804
|
-
const sortedUtxos = utxos.sort((a, b) => b.outputValue - a.outputValue);
|
|
4805
|
-
const selectedUtxos = [];
|
|
4806
|
-
let accumulatedAmount = 0;
|
|
4807
|
-
for (const utxo of sortedUtxos) {
|
|
4808
|
-
if (accumulatedAmount >= totalNeeded) break;
|
|
4809
|
-
selectedUtxos.push(utxo);
|
|
4810
|
-
accumulatedAmount += Number(utxo.outputValue);
|
|
4811
|
-
}
|
|
4812
|
-
if (accumulatedAmount < totalNeeded) {
|
|
4813
|
-
throw new Error("not enough funds to cover amount and fee");
|
|
4814
|
-
}
|
|
4815
|
-
const utxoDetailPromises = selectedUtxos.map(async (utxo) => {
|
|
4816
5017
|
try {
|
|
4817
|
-
const
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
5018
|
+
const serializedTransaction = await this.accountInfo.signTransaction(
|
|
5019
|
+
JSON.stringify({
|
|
5020
|
+
data: preparedTx,
|
|
5021
|
+
types: preparedTxPropsType
|
|
5022
|
+
})
|
|
5023
|
+
);
|
|
5024
|
+
if (serializedTransaction === "") {
|
|
5025
|
+
throw new Error(`eth_signTransaction error data: ${JSON.stringify(txParams)}`);
|
|
5026
|
+
}
|
|
5027
|
+
const data = parseTransaction(serializedTransaction);
|
|
5028
|
+
return data;
|
|
5029
|
+
} catch (err) {
|
|
5030
|
+
throw new Error("eth_signTransaction error" + err);
|
|
4821
5031
|
}
|
|
4822
|
-
}
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
if (addedAmount >= totalNeeded) break;
|
|
4829
|
-
const { txid, vout, outputValue, address = from } = utxo;
|
|
4830
|
-
if (utxoDetail?.hex && !usingUtxos[txid]) {
|
|
4831
|
-
inputs.push({
|
|
4832
|
-
txId: txid,
|
|
4833
|
-
vOut: vout,
|
|
4834
|
-
amount: Number(outputValue),
|
|
4835
|
-
nonWitnessUtxo: utxoDetail.hex,
|
|
4836
|
-
address
|
|
5032
|
+
}
|
|
5033
|
+
async eth_sendRawTransaction([serializedTransaction], rpcUrl) {
|
|
5034
|
+
try {
|
|
5035
|
+
const rpcClient = await this.createPublicClient({ rpcUrl });
|
|
5036
|
+
const hash = await rpcClient.sendRawTransaction({
|
|
5037
|
+
serializedTransaction
|
|
4837
5038
|
});
|
|
4838
|
-
|
|
4839
|
-
|
|
5039
|
+
return hash;
|
|
5040
|
+
} catch (error) {
|
|
5041
|
+
console.error("Failed to send transaction via RPC:", error, rpcUrl);
|
|
5042
|
+
throw error;
|
|
4840
5043
|
}
|
|
4841
5044
|
}
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
{
|
|
4847
|
-
|
|
4848
|
-
|
|
5045
|
+
async getTransaction(hash) {
|
|
5046
|
+
if (!hash || !isHex(hash)) {
|
|
5047
|
+
throw new Error("txId is not valid");
|
|
5048
|
+
}
|
|
5049
|
+
try {
|
|
5050
|
+
const rpcClient = await this.createPublicClient({});
|
|
5051
|
+
const tx = await rpcClient.getTransaction({
|
|
5052
|
+
hash
|
|
5053
|
+
});
|
|
5054
|
+
return tx;
|
|
5055
|
+
} catch (error) {
|
|
5056
|
+
console.error("Failed to send transaction via RPC:", error, hash);
|
|
5057
|
+
throw error;
|
|
4849
5058
|
}
|
|
4850
|
-
];
|
|
4851
|
-
const changeAmount = addedAmount - sendAmount - sendCost;
|
|
4852
|
-
if (changeAmount > 0) {
|
|
4853
|
-
outputs.push({
|
|
4854
|
-
address: from,
|
|
4855
|
-
amount: changeAmount
|
|
4856
|
-
});
|
|
4857
|
-
}
|
|
4858
|
-
const params = {
|
|
4859
|
-
address: from,
|
|
4860
|
-
inputs,
|
|
4861
|
-
outputs
|
|
4862
|
-
};
|
|
4863
|
-
const psbtBase64 = DogecoinUtils.buildPsbtToBase64(params);
|
|
4864
|
-
return { psbtBase64, usingUtxos };
|
|
4865
|
-
}
|
|
4866
|
-
var mydoge = axios.create({
|
|
4867
|
-
baseURL: MYDOGE_BASE_URL
|
|
4868
|
-
});
|
|
4869
|
-
var api = axios.create({
|
|
4870
|
-
baseURL: RPC_URL,
|
|
4871
|
-
timeout: RPC_TIMEOUT
|
|
4872
|
-
});
|
|
4873
|
-
async function getBalance(address) {
|
|
4874
|
-
if (!address) {
|
|
4875
|
-
return {
|
|
4876
|
-
address: "",
|
|
4877
|
-
balance: 0
|
|
4878
|
-
};
|
|
4879
5059
|
}
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
5060
|
+
};
|
|
5061
|
+
var BaseConfig2 = SupportedChainTypes[ChainTypes.SOL];
|
|
5062
|
+
var TOKEN_METADATA_PROGRAM_ID = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
|
|
5063
|
+
var MSG_PREFIX = "\xFFsolana offchain";
|
|
5064
|
+
|
|
5065
|
+
// src/solana/utils.ts
|
|
5066
|
+
__toESM(require_bn());
|
|
5067
|
+
new PublicKey(TOKEN_METADATA_PROGRAM_ID);
|
|
5068
|
+
var txToHex = (tx) => {
|
|
5069
|
+
if (tx instanceof VersionedTransaction) {
|
|
5070
|
+
return Buffer.from(tx.serialize()).toString("hex");
|
|
4888
5071
|
}
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
async function getUtxos(address, cursor, result, filter, tx = null) {
|
|
4897
|
-
const query = (await mydoge.get(`${MYDOGE_BASE_URL}/utxos/${address}?filter=${filter}${cursor ? `&cursor=${cursor}` : ""}`)).data;
|
|
4898
|
-
let { utxos } = query;
|
|
4899
|
-
if (tx) {
|
|
4900
|
-
utxos = utxos.filter((utxo) => utxo.txid === tx?.txid && utxo.vout === tx?.vout);
|
|
5072
|
+
if (tx instanceof Transaction$1) {
|
|
5073
|
+
return Buffer.from(
|
|
5074
|
+
tx.serialize({
|
|
5075
|
+
requireAllSignatures: false,
|
|
5076
|
+
verifySignatures: false
|
|
5077
|
+
})
|
|
5078
|
+
).toString("hex");
|
|
4901
5079
|
}
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
5080
|
+
return tx;
|
|
5081
|
+
};
|
|
5082
|
+
async function createLegacyTx({ from = "", to = "", amount = 0 }, connection) {
|
|
5083
|
+
const fromPubkey = new PublicKey(from);
|
|
5084
|
+
const toPubkey = new PublicKey(to);
|
|
5085
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
5086
|
+
const transaction = new Transaction$1().add(
|
|
5087
|
+
SystemProgram.transfer({
|
|
5088
|
+
fromPubkey,
|
|
5089
|
+
//payer
|
|
5090
|
+
toPubkey,
|
|
5091
|
+
//toAccount
|
|
5092
|
+
lamports: amount * LAMPORTS_PER_SOL
|
|
5093
|
+
})
|
|
4910
5094
|
);
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
if (query.next_cursor) {
|
|
4916
|
-
return getUtxos(address, query.next_cursor, result, filter, tx);
|
|
4917
|
-
}
|
|
4918
|
-
}
|
|
4919
|
-
async function getSpendableUtxos(address) {
|
|
4920
|
-
const utxos = [];
|
|
4921
|
-
await getUtxos(address, 0, utxos, "spendable");
|
|
4922
|
-
return utxos;
|
|
5095
|
+
transaction.feePayer = fromPubkey;
|
|
5096
|
+
transaction.recentBlockhash = blockhash;
|
|
5097
|
+
transaction.lastValidBlockHeight = lastValidBlockHeight;
|
|
5098
|
+
return transaction;
|
|
4923
5099
|
}
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
5100
|
+
async function createTokenLegacyTransaction2({
|
|
5101
|
+
from = "",
|
|
5102
|
+
to = "",
|
|
5103
|
+
amount = 0,
|
|
5104
|
+
tokenAddress = "",
|
|
5105
|
+
priorityFee = 0,
|
|
5106
|
+
// microLamports (1 SOL = 1e6 microLamports)
|
|
5107
|
+
decimals = 6
|
|
5108
|
+
}, connection) {
|
|
4927
5109
|
try {
|
|
4928
|
-
const
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
5110
|
+
const fromPubkey = new PublicKey(from);
|
|
5111
|
+
const toPubkey = new PublicKey(to);
|
|
5112
|
+
const tokenPublicKey = new PublicKey(tokenAddress);
|
|
5113
|
+
const fromTokenPubKey = await getAssociatedTokenAddress(tokenPublicKey, fromPubkey);
|
|
5114
|
+
const toTokenPubKey = await getAssociatedTokenAddress(tokenPublicKey, toPubkey);
|
|
5115
|
+
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
5116
|
+
const transaction = new Transaction$1();
|
|
5117
|
+
transaction.feePayer = fromPubkey;
|
|
5118
|
+
transaction.recentBlockhash = blockhash;
|
|
5119
|
+
try {
|
|
5120
|
+
const instruction = createAssociatedTokenAccountInstruction(fromPubkey, toTokenPubKey, toPubkey, tokenPublicKey);
|
|
5121
|
+
transaction.add(instruction);
|
|
5122
|
+
} catch (error) {
|
|
5123
|
+
console.error(error);
|
|
5124
|
+
throw error;
|
|
5125
|
+
}
|
|
5126
|
+
const tokenAmount = new Bignumber(amount).multipliedBy(new Bignumber(10).pow(decimals)).toNumber();
|
|
5127
|
+
transaction.add(
|
|
5128
|
+
createTransferInstruction(fromTokenPubKey, toTokenPubKey, fromPubkey, tokenAmount, [], TOKEN_PROGRAM_ID)
|
|
5129
|
+
);
|
|
5130
|
+
return transaction;
|
|
5131
|
+
} catch (error) {
|
|
5132
|
+
console.error("Error creating transaction:", error);
|
|
5133
|
+
throw error;
|
|
4933
5134
|
}
|
|
4934
|
-
}
|
|
4935
|
-
|
|
4936
|
-
// src/dogecoin/service.ts
|
|
4937
|
-
var DogecoinService = class _DogecoinService extends BaseService {
|
|
5135
|
+
}
|
|
5136
|
+
var SolanaService = class _SolanaService extends BaseService {
|
|
4938
5137
|
static instance;
|
|
4939
5138
|
chainType;
|
|
5139
|
+
rpcUrl;
|
|
5140
|
+
connection;
|
|
4940
5141
|
constructor(chainType, accountInfo, tomoAppInfo) {
|
|
4941
5142
|
super(tomoAppInfo, accountInfo);
|
|
4942
5143
|
this.chainType = chainType;
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
}
|
|
4951
|
-
async requestAccounts() {
|
|
4952
|
-
const addresses = await this.accountInfo.getCurrent();
|
|
4953
|
-
const { publicKey, address } = addresses?.[0] || {};
|
|
4954
|
-
if (!address) {
|
|
4955
|
-
throw new Error("address is not set");
|
|
4956
|
-
}
|
|
4957
|
-
const { balance = 0 } = await getBalance(address);
|
|
4958
|
-
return {
|
|
4959
|
-
address,
|
|
4960
|
-
balance,
|
|
4961
|
-
approved: true,
|
|
4962
|
-
publicKey
|
|
4963
|
-
};
|
|
4964
|
-
}
|
|
4965
|
-
async getAccounts() {
|
|
4966
|
-
const accounts = await this.accountInfo.getCurrent();
|
|
4967
|
-
return accounts.map((account) => account.address);
|
|
4968
|
-
}
|
|
4969
|
-
async getConnectionStatus() {
|
|
4970
|
-
const addresses = await this.accountInfo.getCurrent();
|
|
4971
|
-
const { address } = addresses?.[0] || {};
|
|
4972
|
-
if (!address) {
|
|
4973
|
-
throw new Error("address is not set");
|
|
4974
|
-
}
|
|
4975
|
-
return {
|
|
4976
|
-
connected: true,
|
|
4977
|
-
address,
|
|
4978
|
-
selectedWalletAddress: address
|
|
5144
|
+
const config = {
|
|
5145
|
+
commitment: "confirmed",
|
|
5146
|
+
disableRetryOnRateLimit: false,
|
|
5147
|
+
confirmTransactionInitialTimeout: 12e4,
|
|
5148
|
+
fetch: (url, options) => {
|
|
5149
|
+
return fetch(url, { ...options });
|
|
5150
|
+
}
|
|
4979
5151
|
};
|
|
5152
|
+
const domain = TomoApiDomains[tomoAppInfo.tomoStage];
|
|
5153
|
+
const rpcUrl = `${domain}/rpc/v1/solana`;
|
|
5154
|
+
this.rpcUrl = rpcUrl;
|
|
5155
|
+
this.connection = new Connection(rpcUrl, config);
|
|
4980
5156
|
}
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
if (!address) {
|
|
4985
|
-
throw new Error("address is not set");
|
|
5157
|
+
static getInstance(chainType, accountInfo, tomoAppInfo) {
|
|
5158
|
+
if (!_SolanaService.instance) {
|
|
5159
|
+
_SolanaService.instance = new _SolanaService(chainType, accountInfo, tomoAppInfo);
|
|
4986
5160
|
}
|
|
4987
|
-
|
|
4988
|
-
return {
|
|
4989
|
-
address,
|
|
4990
|
-
balance
|
|
4991
|
-
};
|
|
5161
|
+
return _SolanaService.instance;
|
|
4992
5162
|
}
|
|
4993
|
-
async
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
5163
|
+
async getCurrentWalletAccount() {
|
|
5164
|
+
return await this.accountInfo.getCurrent();
|
|
5165
|
+
}
|
|
5166
|
+
async getAccount() {
|
|
5167
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
5168
|
+
return { publicKey: publicKey || currentAddress, address: currentAddress };
|
|
5169
|
+
}
|
|
5170
|
+
async signMessage(params) {
|
|
5171
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
5172
|
+
const { message } = params;
|
|
4997
5173
|
const signature = await this.accountInfo.signMessage(message);
|
|
4998
5174
|
return {
|
|
4999
|
-
|
|
5175
|
+
message: MSG_PREFIX + message,
|
|
5176
|
+
signature,
|
|
5177
|
+
publicKey: publicKey || currentAddress
|
|
5000
5178
|
};
|
|
5001
5179
|
}
|
|
5002
|
-
async
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5180
|
+
async signIn(params) {
|
|
5181
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
5182
|
+
const { signature = "" } = await this.signMessage(params);
|
|
5183
|
+
return {
|
|
5184
|
+
address: currentAddress,
|
|
5185
|
+
publicKey: publicKey || currentAddress,
|
|
5186
|
+
signature,
|
|
5187
|
+
signedMessage: MSG_PREFIX + params.message
|
|
5188
|
+
};
|
|
5010
5189
|
}
|
|
5011
|
-
async
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
throw new Error("decryptedMessage is not supported");
|
|
5015
|
-
}
|
|
5016
|
-
const unsignRes = await this.accountInfo?.decryptedMessage(message);
|
|
5017
|
-
const { text = "" } = JSON.parse(unsignRes);
|
|
5018
|
-
return {
|
|
5019
|
-
decryptedMessage: text
|
|
5020
|
-
};
|
|
5021
|
-
} catch (err) {
|
|
5022
|
-
throw new Error("requestDecryptedMessage err:" + JSON.stringify(err));
|
|
5190
|
+
async request({ method, params }) {
|
|
5191
|
+
if (!this[method]) {
|
|
5192
|
+
throw new Error(`${method} in request is not supported`);
|
|
5023
5193
|
}
|
|
5194
|
+
return this[method](params);
|
|
5024
5195
|
}
|
|
5025
5196
|
async _queryGasInfo(txData) {
|
|
5026
|
-
const { chainId, amount = 0, decimals =
|
|
5197
|
+
const { tokenAddress, chainId, amount = 0, decimals = 9 } = txData;
|
|
5198
|
+
const value = tokenAddress ? "0x1" : toHex$1(parseUnits(amount.toString(), decimals));
|
|
5199
|
+
let callData = "0x";
|
|
5200
|
+
if (tokenAddress) {
|
|
5201
|
+
const transaction = await createTokenLegacyTransaction2(txData, this.connection);
|
|
5202
|
+
callData = "0x" + txToHex(transaction);
|
|
5203
|
+
} else {
|
|
5204
|
+
const transaction = await createLegacyTx(txData, this.connection);
|
|
5205
|
+
callData = "0x" + txToHex(transaction);
|
|
5206
|
+
}
|
|
5027
5207
|
const gasLimitParam = {
|
|
5028
5208
|
from: txData.from,
|
|
5029
5209
|
to: txData.to,
|
|
5030
|
-
value
|
|
5210
|
+
value
|
|
5031
5211
|
};
|
|
5032
5212
|
const queryGasParams = {
|
|
5033
|
-
chainIndex: Number(chainId
|
|
5034
|
-
callData
|
|
5213
|
+
chainIndex: Number(chainId),
|
|
5214
|
+
callData,
|
|
5035
5215
|
gasLimitParam,
|
|
5036
5216
|
addressList: [txData.from]
|
|
5037
5217
|
};
|
|
@@ -5044,85 +5224,149 @@ var DogecoinService = class _DogecoinService extends BaseService {
|
|
|
5044
5224
|
params: queryGasParams
|
|
5045
5225
|
});
|
|
5046
5226
|
if (!success) {
|
|
5047
|
-
console.error("queryGasInfo
|
|
5227
|
+
console.error("queryGasInfo solana", txData, message, gasInfo);
|
|
5048
5228
|
const { gasFee } = BaseConfig2;
|
|
5049
5229
|
return {
|
|
5050
5230
|
success: true,
|
|
5051
5231
|
gasFee: gasFee.toString()
|
|
5052
5232
|
};
|
|
5053
5233
|
}
|
|
5054
|
-
const
|
|
5234
|
+
const nativeChainId = SupportedChainTypes[this.chainType].chainId;
|
|
5235
|
+
const { nativeCurrency } = await this.networks.getNetworkByChainId(nativeChainId);
|
|
5236
|
+
const { baseFee = 1, gasLimit = 0 } = gasInfo;
|
|
5237
|
+
const baseFeeBN = new BigNumber(baseFee).times(2);
|
|
5238
|
+
const gasLimitBN = new BigNumber(gasLimit);
|
|
5055
5239
|
const calcFee = (feeLevel) => {
|
|
5056
|
-
const
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
const fees = {
|
|
5060
|
-
low: calcFee("priorityFeeLow"),
|
|
5061
|
-
medium: calcFee("priorityFeeMedium"),
|
|
5062
|
-
high: calcFee("priorityFeeHigh")
|
|
5240
|
+
const priorityFee = gasLimitBN.times(gasInfo[feeLevel] || 0).dividedBy(1e6).integerValue(BigNumber.ROUND_DOWN);
|
|
5241
|
+
const gasFee = baseFeeBN.plus(priorityFee);
|
|
5242
|
+
return formatUnits(BigInt(gasFee.toNumber()), nativeCurrency?.decimals || 9);
|
|
5063
5243
|
};
|
|
5064
5244
|
return {
|
|
5065
5245
|
success: true,
|
|
5066
|
-
fees
|
|
5067
|
-
|
|
5246
|
+
fees: {
|
|
5247
|
+
low: calcFee("priorityFeeLow"),
|
|
5248
|
+
medium: calcFee("priorityFeeMedium"),
|
|
5249
|
+
high: calcFee("priorityFeeHigh")
|
|
5250
|
+
},
|
|
5251
|
+
gasFee: calcFee("priorityFeeHigh")
|
|
5068
5252
|
};
|
|
5069
5253
|
}
|
|
5070
|
-
async
|
|
5071
|
-
rawTx
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5254
|
+
async sendSignedTx(signedTx) {
|
|
5255
|
+
let rawTx;
|
|
5256
|
+
try {
|
|
5257
|
+
const hexBuffer = Buffer.from(signedTx, "hex");
|
|
5258
|
+
VersionedTransaction.deserialize(hexBuffer);
|
|
5259
|
+
rawTx = hexBuffer;
|
|
5260
|
+
} catch (error) {
|
|
5261
|
+
const base58Buffer = Buffer.from(toBase58());
|
|
5262
|
+
VersionedTransaction.deserialize(base58Buffer);
|
|
5263
|
+
rawTx = base58Buffer;
|
|
5078
5264
|
}
|
|
5079
|
-
const
|
|
5080
|
-
if (
|
|
5081
|
-
|
|
5082
|
-
signedRawTx
|
|
5083
|
-
};
|
|
5265
|
+
const transaction = VersionedTransaction.deserialize(rawTx);
|
|
5266
|
+
if (!transaction.signatures || transaction.signatures.length === 0) {
|
|
5267
|
+
throw new Error("lack of sign");
|
|
5084
5268
|
}
|
|
5085
|
-
const
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5269
|
+
const confirmationStrategy = {
|
|
5270
|
+
commitment: "confirmed",
|
|
5271
|
+
preflightCommitment: "processed",
|
|
5272
|
+
skipPreflight: false,
|
|
5273
|
+
maxRetries: 5
|
|
5089
5274
|
};
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5275
|
+
try {
|
|
5276
|
+
const simulation = await this.connection.simulateTransaction(transaction);
|
|
5277
|
+
if (simulation.value.err) {
|
|
5278
|
+
const logs = simulation.value.logs || [];
|
|
5279
|
+
const message = logs[logs.length - 1] || simulation.value.err || "Unknown error occurred";
|
|
5280
|
+
console.error("send err logs:", logs, message);
|
|
5281
|
+
throw new Error(`Transaction failed: ${message}`);
|
|
5282
|
+
}
|
|
5283
|
+
const signature = await sendAndConfirmRawTransaction(this.connection, rawTx, confirmationStrategy);
|
|
5284
|
+
return signature;
|
|
5285
|
+
} catch (error) {
|
|
5286
|
+
if (error.message.includes("TransactionExpiredTimeoutError")) {
|
|
5287
|
+
const status = await this.connection.getSignatureStatus(transaction.signatures[0].toString());
|
|
5288
|
+
if (status.value?.confirmationStatus === "confirmed") {
|
|
5289
|
+
return transaction.signatures[0].toString();
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
throw error;
|
|
5095
5293
|
}
|
|
5096
|
-
|
|
5097
|
-
|
|
5294
|
+
}
|
|
5295
|
+
async signTransaction({ rawTransaction }) {
|
|
5296
|
+
const params = {
|
|
5297
|
+
rawTransaction,
|
|
5298
|
+
walletId: -1,
|
|
5299
|
+
rpcUrl: this.rpcUrl
|
|
5300
|
+
};
|
|
5301
|
+
const signature = await this.accountInfo.signTransaction(JSON.stringify(params));
|
|
5302
|
+
return signature;
|
|
5303
|
+
}
|
|
5304
|
+
async signAndSendTransaction({ rawTransaction }) {
|
|
5305
|
+
const signedTx = await this.signTransaction({ rawTransaction });
|
|
5306
|
+
if (!signedTx) {
|
|
5307
|
+
return "";
|
|
5098
5308
|
}
|
|
5099
|
-
const
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5309
|
+
const signature = await this.sendSignedTx(signedTx);
|
|
5310
|
+
return signature;
|
|
5311
|
+
}
|
|
5312
|
+
async signAllTransactions({ rawTransactions }) {
|
|
5313
|
+
throw new Error("no support");
|
|
5314
|
+
}
|
|
5315
|
+
async signAndSendAllTransactions({ rawTransactions }) {
|
|
5316
|
+
throw new Error("no support");
|
|
5317
|
+
}
|
|
5318
|
+
async queryRent(params) {
|
|
5319
|
+
const MIN_ACCOUNT_ACTIVATION_SOL = "0.0009";
|
|
5320
|
+
const SPL_TOKEN_ACCOUNT_CREATION_FEE = "0.001";
|
|
5321
|
+
try {
|
|
5322
|
+
const { toAddress, tokenAddress } = params;
|
|
5323
|
+
let minTransferAmount = "0";
|
|
5324
|
+
let createSplTokenFee = null;
|
|
5325
|
+
if (tokenAddress) {
|
|
5326
|
+
const tokenAccountExists = await this.checkTokenAccount(toAddress, tokenAddress);
|
|
5327
|
+
if (!tokenAccountExists) {
|
|
5328
|
+
createSplTokenFee = SPL_TOKEN_ACCOUNT_CREATION_FEE;
|
|
5329
|
+
}
|
|
5330
|
+
} else {
|
|
5331
|
+
const accountExists = await this.checkAccountExists(toAddress);
|
|
5332
|
+
if (!accountExists) {
|
|
5333
|
+
minTransferAmount = MIN_ACCOUNT_ACTIVATION_SOL;
|
|
5334
|
+
}
|
|
5335
|
+
}
|
|
5336
|
+
const rentInfo = {
|
|
5337
|
+
minTransferAmount,
|
|
5338
|
+
createSplTokenFee
|
|
5339
|
+
};
|
|
5340
|
+
return rentInfo;
|
|
5341
|
+
} catch (error) {
|
|
5342
|
+
console.error("queryRent error:", error);
|
|
5343
|
+
const defaultRentInfo = {
|
|
5344
|
+
minTransferAmount: "0",
|
|
5345
|
+
createSplTokenFee: null
|
|
5346
|
+
};
|
|
5347
|
+
return defaultRentInfo;
|
|
5104
5348
|
}
|
|
5349
|
+
}
|
|
5350
|
+
async checkAccountExists(address) {
|
|
5105
5351
|
try {
|
|
5106
|
-
const
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5352
|
+
const accountInfo = await this.connection.getAccountInfo(new PublicKey(address));
|
|
5353
|
+
return accountInfo !== null;
|
|
5354
|
+
} catch (error) {
|
|
5355
|
+
console.warn("Failed to check account exists:", error);
|
|
5356
|
+
return false;
|
|
5111
5357
|
}
|
|
5112
5358
|
}
|
|
5113
|
-
async
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5359
|
+
async checkTokenAccount(ownerAddress, tokenMint) {
|
|
5360
|
+
try {
|
|
5361
|
+
const owner = new PublicKey(ownerAddress);
|
|
5362
|
+
const mint = new PublicKey(tokenMint);
|
|
5363
|
+
const associatedTokenAddress = await getAssociatedTokenAddress(mint, owner);
|
|
5364
|
+
const accountInfo = await this.connection.getAccountInfo(associatedTokenAddress);
|
|
5365
|
+
return accountInfo !== null;
|
|
5366
|
+
} catch (error) {
|
|
5367
|
+
console.warn("Failed to check token account:", error);
|
|
5368
|
+
return false;
|
|
5117
5369
|
}
|
|
5118
|
-
return {
|
|
5119
|
-
txId: transaction.txid,
|
|
5120
|
-
confirmations: transaction.confirmations,
|
|
5121
|
-
status: transaction.confirmations > 0 ? "confirmed" : "pending",
|
|
5122
|
-
dogeAmount: transaction.vout[0]?.value,
|
|
5123
|
-
blockTime: transaction.blockTime,
|
|
5124
|
-
address: transaction.vout[0]?.addresses[0]
|
|
5125
|
-
};
|
|
5126
5370
|
}
|
|
5127
5371
|
};
|
|
5128
5372
|
var TomoWallet = class _TomoWallet extends BaseService {
|
|
@@ -5216,4 +5460,4 @@ var ChainTypeServices = {
|
|
|
5216
5460
|
[ChainTypes.DOGE]: DogecoinService
|
|
5217
5461
|
};
|
|
5218
5462
|
|
|
5219
|
-
export { AccountType, ChainTypeServices, DogecoinService, EvmService, SolanaService, TomoWallet, TxTypes };
|
|
5463
|
+
export { AccountType, ChainTypeServices, rpc_exports as DogecoinAPI, DogecoinAddress, DogecoinService, DogecoinUtils, EvmService, SolanaService, TomoWallet, TransactionParser, TxTypes, addUsedUtxos, createPsbt, decodePsbt, getUsedUtxos, toBitcoin, toSatoshi };
|