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