@tomo-inc/chains-service 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1309 -1055
- package/dist/index.d.cts +283 -90
- package/dist/index.d.ts +283 -90
- package/dist/index.js +1280 -1036
- package/package.json +2 -2
- package/src/api/network-data.ts +14 -0
- package/src/api/network.ts +2 -3
- package/src/api/token.ts +6 -6
- package/src/base/service.ts +1 -1
- package/src/base/token.ts +1 -1
- package/src/dogecoin/rpc.ts +10 -191
- package/src/dogecoin/service.ts +74 -40
- package/src/dogecoin/utils-doge.ts +19 -6
- package/src/evm/service.ts +5 -5
- package/src/index.ts +8 -4
package/dist/index.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))
|
|
@@ -2905,24 +2909,6 @@ var Transactions = class {
|
|
|
2905
2909
|
return res?.data || {};
|
|
2906
2910
|
}
|
|
2907
2911
|
};
|
|
2908
|
-
function getConfig(tomoStage) {
|
|
2909
|
-
const domain = walletUtils.TomoApiDomains[tomoStage];
|
|
2910
|
-
if (!domain) {
|
|
2911
|
-
throw new Error("Invalid tomo stage");
|
|
2912
|
-
}
|
|
2913
|
-
return {
|
|
2914
|
-
rpcBaseUrl: `${domain}`,
|
|
2915
|
-
walletBaseUrl: `${domain}/wallet`,
|
|
2916
|
-
txBaseUrl: `${domain}/quote`,
|
|
2917
|
-
tokenBaseUrl: `${domain}/token`,
|
|
2918
|
-
userBaseUrl: `${domain}/user/api`
|
|
2919
|
-
};
|
|
2920
|
-
}
|
|
2921
|
-
var CONFIG = {
|
|
2922
|
-
prod: getConfig("prod"),
|
|
2923
|
-
pre: getConfig("pre"),
|
|
2924
|
-
dev: getConfig("dev")
|
|
2925
|
-
};
|
|
2926
2912
|
function generateSignature(config) {
|
|
2927
2913
|
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
2928
2914
|
const method = config.method.toUpperCase();
|
|
@@ -3284,6 +3270,20 @@ var loadNetworks = (WALLET_DOMAIN) => {
|
|
|
3284
3270
|
supportGift: false,
|
|
3285
3271
|
supportHistory: true
|
|
3286
3272
|
},
|
|
3273
|
+
{
|
|
3274
|
+
chainId: 36888,
|
|
3275
|
+
chainIndex: 3688800,
|
|
3276
|
+
name: "ABCORE",
|
|
3277
|
+
chainName: "AB Core",
|
|
3278
|
+
rpcUrls: ["https://rpc1.core.ab.org"],
|
|
3279
|
+
blockExplorerUrl: "https://explorer.core.ab.org",
|
|
3280
|
+
platformType: "EVM",
|
|
3281
|
+
isTestnet: false,
|
|
3282
|
+
icon: "/assets/ab.svg",
|
|
3283
|
+
supportSwap: true,
|
|
3284
|
+
supportGift: false,
|
|
3285
|
+
supportHistory: true
|
|
3286
|
+
},
|
|
3287
3287
|
{
|
|
3288
3288
|
chainId: 6281971,
|
|
3289
3289
|
chainIndex: 628197100,
|
|
@@ -3897,6 +3897,24 @@ function tomoPublicApiService(publicApiBase, tomoAppInfo) {
|
|
|
3897
3897
|
walletAPIs: WalletAPIs.getInstance(publicApiBase, tomoAppInfo)
|
|
3898
3898
|
};
|
|
3899
3899
|
}
|
|
3900
|
+
function getConfig(tomoStage) {
|
|
3901
|
+
const domain = walletUtils.TomoApiDomains[tomoStage];
|
|
3902
|
+
if (!domain) {
|
|
3903
|
+
throw new Error("Invalid tomo stage");
|
|
3904
|
+
}
|
|
3905
|
+
return {
|
|
3906
|
+
rpcBaseUrl: `${domain}`,
|
|
3907
|
+
walletBaseUrl: `${domain}/wallet`,
|
|
3908
|
+
txBaseUrl: `${domain}/quote`,
|
|
3909
|
+
tokenBaseUrl: `${domain}/token`,
|
|
3910
|
+
userBaseUrl: `${domain}/user/api`
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
var CONFIG = {
|
|
3914
|
+
prod: getConfig("prod"),
|
|
3915
|
+
pre: getConfig("pre"),
|
|
3916
|
+
dev: getConfig("dev")
|
|
3917
|
+
};
|
|
3900
3918
|
|
|
3901
3919
|
// src/base/service.ts
|
|
3902
3920
|
var BaseService = class {
|
|
@@ -3922,580 +3940,614 @@ 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;
|
|
4050
|
-
}
|
|
4051
|
-
async wallet_switchEthereumChain(networks) {
|
|
4052
|
-
const { chainId } = networks[0];
|
|
4053
|
-
const isSupported = await this.isChainSupported(chainId);
|
|
4054
|
-
if (!isSupported) {
|
|
4055
|
-
throw new Error(`Chain ${chainId} is not supported`);
|
|
4056
|
-
}
|
|
4057
|
-
return await this.networks.setCurrentNetwork(chainId);
|
|
4074
|
+
if (accumulatedAmount < totalNeeded) {
|
|
4075
|
+
throw new Error("not enough funds to cover amount and fee");
|
|
4058
4076
|
}
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
return
|
|
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 };
|
|
4065
4083
|
}
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
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);
|
|
4069
4102
|
}
|
|
4070
|
-
return isEvmChain(chainInfo);
|
|
4071
4103
|
}
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
if (!viem.isAddressEqual(accounts[0].address, address)) {
|
|
4075
|
-
throw new Error("address is not the current account");
|
|
4076
|
-
}
|
|
4077
|
-
const signature = await this.accountInfo.signMessage(message);
|
|
4078
|
-
return signature;
|
|
4104
|
+
if (addedAmount < totalNeeded) {
|
|
4105
|
+
throw new Error("not enough funds to cover amount and fee");
|
|
4079
4106
|
}
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4107
|
+
const outputs = [
|
|
4108
|
+
{
|
|
4109
|
+
address: to,
|
|
4110
|
+
amount: sendAmount
|
|
4084
4111
|
}
|
|
4085
|
-
|
|
4086
|
-
|
|
4112
|
+
];
|
|
4113
|
+
const changeAmount = addedAmount - sendAmount - sendCost;
|
|
4114
|
+
if (changeAmount > 0) {
|
|
4115
|
+
outputs.push({
|
|
4116
|
+
address: from,
|
|
4117
|
+
amount: changeAmount
|
|
4118
|
+
});
|
|
4087
4119
|
}
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
const
|
|
4100
|
-
return
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
return "0";
|
|
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
|
+
});
|
|
4104
4135
|
}
|
|
4136
|
+
return bitcoinjsLib.Psbt.fromHex(psbt, {
|
|
4137
|
+
network
|
|
4138
|
+
});
|
|
4139
|
+
} catch (error) {
|
|
4140
|
+
console.error("Failed to decode PSBT:", error);
|
|
4141
|
+
throw error;
|
|
4105
4142
|
}
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
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));
|
|
4112
4153
|
}
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
const
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4154
|
+
return str;
|
|
4155
|
+
}
|
|
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
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
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");
|
|
4207
4339
|
}
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
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) {
|
|
4217
4362
|
try {
|
|
4218
|
-
const
|
|
4219
|
-
|
|
4220
|
-
return nonce;
|
|
4363
|
+
const decoded = bitcoinjsLib.address.fromBase58Check(address);
|
|
4364
|
+
return decoded.version === network.pubKeyHash || decoded.version === network.scriptHash;
|
|
4221
4365
|
} catch (error) {
|
|
4222
|
-
|
|
4223
|
-
throw new Error(`Failed to get nonce: ${error}`);
|
|
4366
|
+
return false;
|
|
4224
4367
|
}
|
|
4225
4368
|
}
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
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");
|
|
4246
|
-
}
|
|
4247
|
-
const accounts = await this.accountInfo.getCurrent();
|
|
4248
|
-
const address = accounts[0].address;
|
|
4249
|
-
if (txData?.from && !viem.isAddressEqual(txData?.from, address)) {
|
|
4250
|
-
console.error("eth_signTransaction error data: from is not the current account", txData);
|
|
4251
|
-
throw new Error(`eth_signTransaction error data: from is not the current account`);
|
|
4252
|
-
}
|
|
4253
|
-
delete txData?.from;
|
|
4254
|
-
delete txData?.gasLimit;
|
|
4255
|
-
const preparedTx = {
|
|
4256
|
-
gas: txData?.gas
|
|
4257
|
-
};
|
|
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];
|
|
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");
|
|
4261
4386
|
}
|
|
4387
|
+
psbt.addInput(inputOptions);
|
|
4262
4388
|
}
|
|
4263
|
-
|
|
4264
|
-
|
|
4389
|
+
for (const output of tx.outputs) {
|
|
4390
|
+
psbt.addOutput({
|
|
4391
|
+
value: BigInt(output?.amount || 0),
|
|
4392
|
+
address: output.address
|
|
4393
|
+
});
|
|
4265
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) {
|
|
4266
4424
|
try {
|
|
4267
|
-
const
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
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);
|
|
4275
4449
|
}
|
|
4276
|
-
const
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
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));
|
|
4466
|
+
} catch (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
|
+
);
|
|
4280
4471
|
}
|
|
4281
4472
|
}
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
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);
|
|
4292
4487
|
}
|
|
4488
|
+
return _DogecoinService.instance;
|
|
4293
4489
|
}
|
|
4294
|
-
async
|
|
4295
|
-
|
|
4296
|
-
|
|
4490
|
+
async requestAccounts() {
|
|
4491
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4492
|
+
return addresses.map((account) => account.address);
|
|
4493
|
+
}
|
|
4494
|
+
async getAccounts() {
|
|
4495
|
+
return await this.requestAccounts();
|
|
4496
|
+
}
|
|
4497
|
+
async getConnectionStatus() {
|
|
4498
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4499
|
+
const { address } = addresses?.[0] || {};
|
|
4500
|
+
if (!address) {
|
|
4501
|
+
throw new Error("address is not set");
|
|
4297
4502
|
}
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4503
|
+
return {
|
|
4504
|
+
connected: true,
|
|
4505
|
+
address,
|
|
4506
|
+
selectedWalletAddress: address
|
|
4507
|
+
};
|
|
4508
|
+
}
|
|
4509
|
+
async getBalance() {
|
|
4510
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4511
|
+
const { address } = addresses?.[0] || {};
|
|
4512
|
+
if (!address) {
|
|
4513
|
+
throw new Error("address is not set");
|
|
4307
4514
|
}
|
|
4515
|
+
const { balance = 0 } = await getBalance(address);
|
|
4516
|
+
return {
|
|
4517
|
+
confirmed: balance,
|
|
4518
|
+
unconfirmed: 0,
|
|
4519
|
+
total: balance
|
|
4520
|
+
};
|
|
4308
4521
|
}
|
|
4309
|
-
}
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
__toESM(require_bn());
|
|
4316
|
-
new web3_js.PublicKey(TOKEN_METADATA_PROGRAM_ID);
|
|
4317
|
-
var txToHex = (tx) => {
|
|
4318
|
-
if (tx instanceof web3_js.VersionedTransaction) {
|
|
4319
|
-
return Buffer.from(tx.serialize()).toString("hex");
|
|
4522
|
+
async signMessage({ message, type }) {
|
|
4523
|
+
if (type) {
|
|
4524
|
+
throw new Error("bip322simple not support.");
|
|
4525
|
+
}
|
|
4526
|
+
const signature = await this.accountInfo.signMessage(message);
|
|
4527
|
+
return signature;
|
|
4320
4528
|
}
|
|
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);
|
|
4529
|
+
async requestSignedMessage({
|
|
4530
|
+
message,
|
|
4531
|
+
type
|
|
4532
|
+
}) {
|
|
4533
|
+
const signedMessage = await this.signMessage({
|
|
4534
|
+
message,
|
|
4535
|
+
type
|
|
4536
|
+
});
|
|
4467
4537
|
return {
|
|
4468
|
-
|
|
4469
|
-
publicKey: publicKey || currentAddress,
|
|
4470
|
-
signature,
|
|
4471
|
-
signedMessage: MSG_PREFIX + params.message
|
|
4538
|
+
signedMessage
|
|
4472
4539
|
};
|
|
4473
4540
|
}
|
|
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);
|
|
4479
|
-
}
|
|
4480
4541
|
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
|
-
}
|
|
4542
|
+
const { chainId, amount = 0, decimals = 8 } = txData;
|
|
4491
4543
|
const gasLimitParam = {
|
|
4492
4544
|
from: txData.from,
|
|
4493
4545
|
to: txData.to,
|
|
4494
|
-
value
|
|
4546
|
+
value: viem.toHex(viem.parseUnits(amount.toString(), decimals))
|
|
4495
4547
|
};
|
|
4496
4548
|
const queryGasParams = {
|
|
4497
|
-
chainIndex: Number(chainId),
|
|
4498
|
-
callData,
|
|
4549
|
+
chainIndex: Number(chainId || BaseConfig.chainId),
|
|
4550
|
+
callData: "0x",
|
|
4499
4551
|
gasLimitParam,
|
|
4500
4552
|
addressList: [txData.from]
|
|
4501
4553
|
};
|
|
@@ -4508,538 +4560,666 @@ var SolanaService = class _SolanaService extends BaseService {
|
|
|
4508
4560
|
params: queryGasParams
|
|
4509
4561
|
});
|
|
4510
4562
|
if (!success) {
|
|
4511
|
-
console.error("queryGasInfo
|
|
4563
|
+
console.error("queryGasInfo doge", success, txData, queryGasParams, gasInfo);
|
|
4512
4564
|
const { gasFee } = BaseConfig;
|
|
4513
4565
|
return {
|
|
4514
4566
|
success: true,
|
|
4515
4567
|
gasFee: gasFee.toString()
|
|
4516
4568
|
};
|
|
4517
4569
|
}
|
|
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);
|
|
4570
|
+
const { baseFee = 1, gasLimit = 1 } = gasInfo;
|
|
4523
4571
|
const calcFee = (feeLevel) => {
|
|
4524
|
-
const
|
|
4525
|
-
|
|
4526
|
-
|
|
4572
|
+
const gasFee = new Bignumber.BigNumber(gasInfo[feeLevel] || baseFee).times(gasLimit);
|
|
4573
|
+
return toBitcoin(gasFee.toNumber());
|
|
4574
|
+
};
|
|
4575
|
+
const fees = {
|
|
4576
|
+
low: calcFee("priorityFeeLow"),
|
|
4577
|
+
medium: calcFee("priorityFeeMedium"),
|
|
4578
|
+
high: calcFee("priorityFeeHigh")
|
|
4527
4579
|
};
|
|
4528
4580
|
return {
|
|
4529
4581
|
success: true,
|
|
4530
|
-
fees
|
|
4531
|
-
|
|
4532
|
-
medium: calcFee("priorityFeeMedium"),
|
|
4533
|
-
high: calcFee("priorityFeeHigh")
|
|
4534
|
-
},
|
|
4535
|
-
gasFee: calcFee("priorityFeeHigh")
|
|
4582
|
+
fees,
|
|
4583
|
+
gasFee: fees.high
|
|
4536
4584
|
};
|
|
4537
4585
|
}
|
|
4538
|
-
async
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
web3_js.VersionedTransaction.deserialize(base58Buffer);
|
|
4547
|
-
rawTx = base58Buffer;
|
|
4586
|
+
async requestPsbt({
|
|
4587
|
+
rawTx,
|
|
4588
|
+
signOnly
|
|
4589
|
+
}) {
|
|
4590
|
+
const psbtBase64 = await DogecoinUtils.rawTxToPsbtBase64(rawTx);
|
|
4591
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
4592
|
+
if (!signedPsbt) {
|
|
4593
|
+
return null;
|
|
4548
4594
|
}
|
|
4549
|
-
const
|
|
4550
|
-
if (
|
|
4551
|
-
|
|
4595
|
+
const signedRawTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
|
|
4596
|
+
if (signOnly) {
|
|
4597
|
+
return {
|
|
4598
|
+
signedRawTx
|
|
4599
|
+
};
|
|
4552
4600
|
}
|
|
4553
|
-
const
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
maxRetries: 5
|
|
4601
|
+
const { txid = "" } = await sendDogeTx(signedRawTx);
|
|
4602
|
+
return {
|
|
4603
|
+
txId: txid,
|
|
4604
|
+
signedRawTx
|
|
4558
4605
|
};
|
|
4559
|
-
|
|
4560
|
-
|
|
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;
|
|
4606
|
+
}
|
|
4607
|
+
async signPsbt({ psbtHex, options }) {
|
|
4608
|
+
const psbtBase64 = toBase64(fromHex(psbtHex));
|
|
4609
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
4610
|
+
if (!signedPsbt) {
|
|
4611
|
+
throw new Error("error psbtHex:" + psbtHex);
|
|
4577
4612
|
}
|
|
4613
|
+
const signedRawTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
|
|
4614
|
+
return signedRawTx;
|
|
4578
4615
|
}
|
|
4579
|
-
async
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4616
|
+
async signPsbts({ psbtHexs, options }) {
|
|
4617
|
+
throw new Error("not implemented");
|
|
4618
|
+
}
|
|
4619
|
+
async requestTransaction(txData) {
|
|
4620
|
+
const spendableUtxos = txData?.spendableUtxos;
|
|
4621
|
+
if (txData.amountMismatch) {
|
|
4622
|
+
throw new Error("balance_insufficient");
|
|
4623
|
+
}
|
|
4624
|
+
if (!spendableUtxos || spendableUtxos?.length === 0) {
|
|
4625
|
+
txData.spendableUtxos = await getSpendableUtxos(txData.from);
|
|
4626
|
+
}
|
|
4627
|
+
const { psbtBase64, usingUtxos } = await createPsbt(txData);
|
|
4628
|
+
const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
|
|
4629
|
+
const signedTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
|
|
4630
|
+
if (signedTx === "") {
|
|
4631
|
+
throw new Error("Error: sign transaction err.");
|
|
4632
|
+
}
|
|
4633
|
+
try {
|
|
4634
|
+
const { txid } = await sendDogeTx(signedTx);
|
|
4635
|
+
addUsedUtxos(usingUtxos);
|
|
4636
|
+
return { txId: txid };
|
|
4637
|
+
} catch (err) {
|
|
4638
|
+
throw new Error("Error: send transaction err." + JSON.stringify(err));
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
async send({
|
|
4642
|
+
toAddress,
|
|
4643
|
+
amount,
|
|
4644
|
+
fee = 0,
|
|
4645
|
+
options
|
|
4646
|
+
}) {
|
|
4647
|
+
if (fee <= 0) {
|
|
4648
|
+
throw new Error("fee is required");
|
|
4649
|
+
}
|
|
4650
|
+
const addresses = await this.accountInfo.getCurrent();
|
|
4651
|
+
const { address } = addresses?.[0] || {};
|
|
4652
|
+
const txData = {
|
|
4653
|
+
to: toAddress,
|
|
4654
|
+
amount: toBitcoin(amount),
|
|
4655
|
+
fee: toBitcoin(fee || 0),
|
|
4656
|
+
from: address
|
|
4584
4657
|
};
|
|
4585
|
-
const
|
|
4586
|
-
return
|
|
4658
|
+
const res = await this.requestTransaction(txData);
|
|
4659
|
+
return res?.txId || "";
|
|
4660
|
+
}
|
|
4661
|
+
async sendDogecoin({
|
|
4662
|
+
toAddress,
|
|
4663
|
+
amount,
|
|
4664
|
+
options,
|
|
4665
|
+
fee
|
|
4666
|
+
}) {
|
|
4667
|
+
return await this.send({ toAddress, amount, options, fee });
|
|
4587
4668
|
}
|
|
4588
|
-
async
|
|
4589
|
-
const
|
|
4590
|
-
if (!
|
|
4591
|
-
return
|
|
4669
|
+
async getTransactionStatus({ txId }) {
|
|
4670
|
+
const transaction = await getTxDetail(txId);
|
|
4671
|
+
if (!transaction?.txid) {
|
|
4672
|
+
return null;
|
|
4592
4673
|
}
|
|
4593
|
-
|
|
4594
|
-
|
|
4674
|
+
return {
|
|
4675
|
+
txId: transaction.txid,
|
|
4676
|
+
confirmations: transaction.confirmations,
|
|
4677
|
+
status: transaction.confirmations > 0 ? "confirmed" : "pending",
|
|
4678
|
+
dogeAmount: transaction.vout[0]?.value,
|
|
4679
|
+
blockTime: transaction.blockTime,
|
|
4680
|
+
address: transaction.vout[0]?.addresses[0]
|
|
4681
|
+
};
|
|
4595
4682
|
}
|
|
4596
|
-
|
|
4597
|
-
|
|
4683
|
+
};
|
|
4684
|
+
function getRPCClient(network2) {
|
|
4685
|
+
const { chainId, name, rpcUrls, nativeCurrencyDecimals, nativeCurrencyName, nativeCurrencySymbol } = network2;
|
|
4686
|
+
const myCustomChain = {
|
|
4687
|
+
id: Number(chainId) || chainId,
|
|
4688
|
+
name,
|
|
4689
|
+
nativeCurrency: {
|
|
4690
|
+
name: nativeCurrencyName,
|
|
4691
|
+
symbol: nativeCurrencySymbol,
|
|
4692
|
+
decimals: nativeCurrencyDecimals
|
|
4693
|
+
},
|
|
4694
|
+
rpcUrls: {
|
|
4695
|
+
default: {
|
|
4696
|
+
http: rpcUrls,
|
|
4697
|
+
webSocket: []
|
|
4698
|
+
},
|
|
4699
|
+
public: {
|
|
4700
|
+
http: rpcUrls,
|
|
4701
|
+
webSocket: []
|
|
4702
|
+
}
|
|
4703
|
+
},
|
|
4704
|
+
blockExplorers: {
|
|
4705
|
+
default: {
|
|
4706
|
+
name: "Explorer",
|
|
4707
|
+
url: rpcUrls[0]
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
};
|
|
4711
|
+
const rpcClient = viem.createPublicClient({
|
|
4712
|
+
chain: myCustomChain,
|
|
4713
|
+
pollingInterval: 1e4,
|
|
4714
|
+
cacheTime: 1e4,
|
|
4715
|
+
transport: viem.http()
|
|
4716
|
+
});
|
|
4717
|
+
return { rpcClient };
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4720
|
+
// src/types/account.ts
|
|
4721
|
+
var AccountType = /* @__PURE__ */ ((AccountType2) => {
|
|
4722
|
+
AccountType2["EOA"] = "EOA";
|
|
4723
|
+
AccountType2["SOCIAL"] = "SOCIAL";
|
|
4724
|
+
return AccountType2;
|
|
4725
|
+
})(AccountType || {});
|
|
4726
|
+
|
|
4727
|
+
// src/types/wallet.ts
|
|
4728
|
+
var TxTypes = /* @__PURE__ */ ((TxTypes2) => {
|
|
4729
|
+
TxTypes2[TxTypes2["swap"] = 1] = "swap";
|
|
4730
|
+
TxTypes2[TxTypes2["bridge"] = 2] = "bridge";
|
|
4731
|
+
TxTypes2[TxTypes2["receive"] = 31] = "receive";
|
|
4732
|
+
TxTypes2[TxTypes2["send"] = 32] = "send";
|
|
4733
|
+
TxTypes2[TxTypes2["approve"] = 4] = "approve";
|
|
4734
|
+
TxTypes2[TxTypes2["contractInteraction"] = 5] = "contractInteraction";
|
|
4735
|
+
TxTypes2[TxTypes2["redPocket"] = 6] = "redPocket";
|
|
4736
|
+
return TxTypes2;
|
|
4737
|
+
})(TxTypes || {});
|
|
4738
|
+
|
|
4739
|
+
// src/evm/utils.ts
|
|
4740
|
+
var isEvmChain = (network2) => {
|
|
4741
|
+
return network2 && network2?.platformType === "EVM";
|
|
4742
|
+
};
|
|
4743
|
+
var getAllTypeChainIds = ({ chainId, chainType }) => {
|
|
4744
|
+
if (viem.isHex(chainId)) {
|
|
4745
|
+
const chainIdHex2 = chainId;
|
|
4746
|
+
chainId = viem.fromHex(chainId, "number").toString();
|
|
4747
|
+
return {
|
|
4748
|
+
chainId,
|
|
4749
|
+
chainIdHex: chainIdHex2,
|
|
4750
|
+
chainUid: `${chainType}:${chainId}`
|
|
4751
|
+
};
|
|
4598
4752
|
}
|
|
4599
|
-
|
|
4600
|
-
|
|
4753
|
+
const chainIdHex = viem.toHex(Number(chainId));
|
|
4754
|
+
return {
|
|
4755
|
+
chainId,
|
|
4756
|
+
chainIdHex,
|
|
4757
|
+
chainUid: `${chainType}:${chainId}`
|
|
4758
|
+
};
|
|
4759
|
+
};
|
|
4760
|
+
function createErc20TxData(params, token) {
|
|
4761
|
+
const { decimals, address: tokenAddress } = token;
|
|
4762
|
+
const value = viem.parseUnits(params?.amount.toString(), decimals);
|
|
4763
|
+
const callData = viem.encodeFunctionData({
|
|
4764
|
+
abi: viem.erc20Abi,
|
|
4765
|
+
functionName: "transfer",
|
|
4766
|
+
args: [params.to, value]
|
|
4767
|
+
});
|
|
4768
|
+
return {
|
|
4769
|
+
...params,
|
|
4770
|
+
...{ amount: 0, data: callData, to: tokenAddress }
|
|
4771
|
+
};
|
|
4772
|
+
}
|
|
4773
|
+
|
|
4774
|
+
// src/evm/service.ts
|
|
4775
|
+
var EvmService = class _EvmService extends BaseService {
|
|
4776
|
+
static instance;
|
|
4777
|
+
chainType;
|
|
4778
|
+
constructor(chainType, accountInfo, tomoAppInfo) {
|
|
4779
|
+
super(tomoAppInfo, accountInfo);
|
|
4780
|
+
this.chainType = chainType;
|
|
4601
4781
|
}
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4782
|
+
static getInstance(chainType, accountInfo, tomoAppInfo) {
|
|
4783
|
+
if (!_EvmService.instance) {
|
|
4784
|
+
_EvmService.instance = new _EvmService(chainType, accountInfo, tomoAppInfo);
|
|
4785
|
+
}
|
|
4786
|
+
return _EvmService.instance;
|
|
4787
|
+
}
|
|
4788
|
+
async eth_requestAccounts() {
|
|
4789
|
+
const res = await this.eth_accounts();
|
|
4790
|
+
return res;
|
|
4791
|
+
}
|
|
4792
|
+
async eth_accounts() {
|
|
4793
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4794
|
+
return accounts.map((account) => account.address);
|
|
4795
|
+
}
|
|
4796
|
+
async eth_chainId() {
|
|
4797
|
+
const chainType = this.chainType;
|
|
4798
|
+
const currentNetwork = await this.getCurrentChain();
|
|
4799
|
+
const { chainIdHex } = getAllTypeChainIds({
|
|
4800
|
+
chainId: currentNetwork?.chainId,
|
|
4801
|
+
chainType
|
|
4802
|
+
});
|
|
4803
|
+
return chainIdHex;
|
|
4804
|
+
}
|
|
4805
|
+
async getCurrentChain() {
|
|
4806
|
+
this.networks.setChainType(this.chainType);
|
|
4807
|
+
const currentNetwork = await this.networks.getCurrentNetwork();
|
|
4808
|
+
return currentNetwork;
|
|
4809
|
+
}
|
|
4810
|
+
async wallet_switchEthereumChain(networks) {
|
|
4811
|
+
const { chainId } = networks[0];
|
|
4812
|
+
const isSupported = await this.isChainSupported(chainId);
|
|
4813
|
+
if (!isSupported) {
|
|
4814
|
+
throw new Error(`Chain ${chainId} is not supported`);
|
|
4815
|
+
}
|
|
4816
|
+
return await this.networks.setCurrentNetwork(chainId);
|
|
4817
|
+
}
|
|
4818
|
+
//evm chains
|
|
4819
|
+
async isChainSupported(chainId) {
|
|
4820
|
+
const chainType = this.chainType;
|
|
4821
|
+
const res = getAllTypeChainIds({ chainId, chainType });
|
|
4822
|
+
if (!res.chainId) {
|
|
4823
|
+
return false;
|
|
4824
|
+
}
|
|
4825
|
+
const chainInfo = await this.networks.getNetworkByChainId(res.chainId);
|
|
4826
|
+
if (!chainInfo) {
|
|
4827
|
+
return false;
|
|
4828
|
+
}
|
|
4829
|
+
return isEvmChain(chainInfo);
|
|
4830
|
+
}
|
|
4831
|
+
async personal_sign([message, address]) {
|
|
4832
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4833
|
+
if (!viem.isAddressEqual(accounts[0].address, address)) {
|
|
4834
|
+
throw new Error("address is not the current account");
|
|
4835
|
+
}
|
|
4836
|
+
const signature = await this.accountInfo.signMessage(message);
|
|
4837
|
+
return signature;
|
|
4838
|
+
}
|
|
4839
|
+
async eth_signTypedData_v4([address, typedData]) {
|
|
4840
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4841
|
+
if (!viem.isAddressEqual(accounts[0].address, address)) {
|
|
4842
|
+
throw new Error("address is not the current account");
|
|
4843
|
+
}
|
|
4844
|
+
const signature = await this.accountInfo.signTypedData(typedData);
|
|
4845
|
+
return signature;
|
|
4846
|
+
}
|
|
4847
|
+
async eth_getBalance([address, type]) {
|
|
4848
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4849
|
+
if (!viem.isAddressEqual(accounts[0].address, address)) {
|
|
4850
|
+
throw new Error("address is not the current account");
|
|
4851
|
+
}
|
|
4852
|
+
if (type !== "latest") {
|
|
4853
|
+
throw new Error("type is not supported");
|
|
4854
|
+
}
|
|
4855
|
+
const chainInfo = await this.getCurrentChain();
|
|
4856
|
+
const { rpcClient } = getRPCClient(chainInfo);
|
|
4605
4857
|
try {
|
|
4606
|
-
const
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4858
|
+
const balance = await rpcClient.getBalance({ address });
|
|
4859
|
+
return balance?.toString() || "0";
|
|
4860
|
+
} catch (error) {
|
|
4861
|
+
console.error(error);
|
|
4862
|
+
return "0";
|
|
4863
|
+
}
|
|
4864
|
+
}
|
|
4865
|
+
async _queryGasInfo(txData) {
|
|
4866
|
+
const { tokenAddress, chainId, amount = 0, decimals = 9 } = txData;
|
|
4867
|
+
const value = tokenAddress ? "0x1" : viem.toHex(viem.parseUnits(amount.toString(), decimals));
|
|
4868
|
+
let callData = "0x";
|
|
4869
|
+
if (tokenAddress) {
|
|
4870
|
+
callData = createErc20TxData(txData, { decimals, address: tokenAddress })?.data;
|
|
4871
|
+
}
|
|
4872
|
+
const gasLimitParam = {
|
|
4873
|
+
from: txData.from,
|
|
4874
|
+
to: txData.to,
|
|
4875
|
+
value
|
|
4876
|
+
};
|
|
4877
|
+
const queryGasParams = {
|
|
4878
|
+
chainIndex: Number(chainId),
|
|
4879
|
+
callData,
|
|
4880
|
+
gasLimitParam,
|
|
4881
|
+
addressList: [txData.from]
|
|
4882
|
+
};
|
|
4883
|
+
const {
|
|
4884
|
+
data: gasInfo,
|
|
4885
|
+
success,
|
|
4886
|
+
message
|
|
4887
|
+
} = await this.transactions.queryGasInfo({
|
|
4888
|
+
chainType: this.chainType,
|
|
4889
|
+
params: queryGasParams
|
|
4890
|
+
});
|
|
4891
|
+
if (!success) {
|
|
4892
|
+
console.error("queryGasInfo evm", txData, queryGasParams, message, gasInfo);
|
|
4893
|
+
const BaseConfig3 = walletUtils.SupportedChainTypes[walletUtils.ChainTypes.EVM];
|
|
4894
|
+
const { gasFee } = BaseConfig3;
|
|
4895
|
+
return {
|
|
4896
|
+
success: true,
|
|
4897
|
+
gasFee: gasFee.toString()
|
|
4898
|
+
};
|
|
4899
|
+
}
|
|
4900
|
+
const nativeChainId = walletUtils.SupportedChainTypes[this.chainType].chainId;
|
|
4901
|
+
const { nativeCurrency } = await this.networks.getNetworkByChainId(nativeChainId);
|
|
4902
|
+
const { baseFee = 1, gasLimit = 0 } = gasInfo;
|
|
4903
|
+
const baseFeeBN = new Bignumber.BigNumber(baseFee);
|
|
4904
|
+
const calcFee = (feeLevel) => {
|
|
4905
|
+
const fee = baseFeeBN.plus(gasInfo[feeLevel] || 0);
|
|
4906
|
+
const gasFee = fee.times(gasLimit);
|
|
4907
|
+
return viem.formatUnits(BigInt(gasFee.toNumber()), nativeCurrency?.decimals || 9);
|
|
4908
|
+
};
|
|
4909
|
+
return {
|
|
4910
|
+
success,
|
|
4911
|
+
fees: {
|
|
4912
|
+
low: calcFee("priorityFeeLow"),
|
|
4913
|
+
medium: calcFee("priorityFeeMedium"),
|
|
4914
|
+
high: calcFee("priorityFeeHigh")
|
|
4915
|
+
},
|
|
4916
|
+
gasFee: calcFee("priorityFeeMedium"),
|
|
4917
|
+
baseFee,
|
|
4918
|
+
gasLimit,
|
|
4919
|
+
priorityFee: {
|
|
4920
|
+
low: gasInfo.priorityFeeLow,
|
|
4921
|
+
medium: gasInfo.priorityFeeMedium,
|
|
4922
|
+
high: gasInfo.priorityFeeHigh
|
|
4619
4923
|
}
|
|
4620
|
-
|
|
4621
|
-
minTransferAmount,
|
|
4622
|
-
createSplTokenFee
|
|
4623
|
-
};
|
|
4624
|
-
return rentInfo;
|
|
4625
|
-
} catch (error) {
|
|
4626
|
-
console.error("queryRent error:", error);
|
|
4627
|
-
const defaultRentInfo = {
|
|
4628
|
-
minTransferAmount: "0",
|
|
4629
|
-
createSplTokenFee: null
|
|
4630
|
-
};
|
|
4631
|
-
return defaultRentInfo;
|
|
4632
|
-
}
|
|
4924
|
+
};
|
|
4633
4925
|
}
|
|
4634
|
-
async
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4926
|
+
async createPublicClient({ chainId, rpcUrl }) {
|
|
4927
|
+
if (rpcUrl) {
|
|
4928
|
+
return viem.createPublicClient({
|
|
4929
|
+
transport: viem.http(rpcUrl)
|
|
4930
|
+
});
|
|
4931
|
+
}
|
|
4932
|
+
if (chainId) {
|
|
4933
|
+
this.networks.setChainType(this.chainType);
|
|
4934
|
+
const chainInfo = await this.networks.getNetworkByChainId(chainId);
|
|
4935
|
+
rpcUrl = chainInfo.rpcUrls[0];
|
|
4936
|
+
} else {
|
|
4937
|
+
const chainInfo = await this.getCurrentChain();
|
|
4938
|
+
rpcUrl = chainInfo.rpcUrls[0];
|
|
4939
|
+
}
|
|
4940
|
+
if (!rpcUrl) {
|
|
4941
|
+
throw new Error("rpcUrl is not set");
|
|
4641
4942
|
}
|
|
4943
|
+
return viem.createPublicClient({
|
|
4944
|
+
transport: viem.http(rpcUrl)
|
|
4945
|
+
});
|
|
4642
4946
|
}
|
|
4643
|
-
async
|
|
4947
|
+
async eth_estimateGas(txs) {
|
|
4948
|
+
const { from, to, value = "0x1", chainId } = txs[0] || {};
|
|
4949
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4950
|
+
if (accounts[0].address !== from) {
|
|
4951
|
+
throw new Error("address is not the current account");
|
|
4952
|
+
}
|
|
4953
|
+
if (!to) {
|
|
4954
|
+
throw new Error("to is not set");
|
|
4955
|
+
}
|
|
4644
4956
|
try {
|
|
4645
|
-
const
|
|
4646
|
-
const
|
|
4647
|
-
|
|
4648
|
-
const accountInfo = await this.connection.getAccountInfo(associatedTokenAddress);
|
|
4649
|
-
return accountInfo !== null;
|
|
4957
|
+
const rpcClient = await this.createPublicClient({ chainId: chainId?.toString() });
|
|
4958
|
+
const gas = await rpcClient.estimateGas({ account: from, to, value: viem.hexToBigInt(value) });
|
|
4959
|
+
return gas;
|
|
4650
4960
|
} catch (error) {
|
|
4651
|
-
console.warn("Failed to
|
|
4652
|
-
|
|
4961
|
+
console.warn("Failed to estimate gas:", error);
|
|
4962
|
+
const BaseConfig3 = walletUtils.SupportedChainTypes[walletUtils.ChainTypes.EVM];
|
|
4963
|
+
const { gasFee } = BaseConfig3;
|
|
4964
|
+
return viem.numberToHex(viem.parseUnits(gasFee.toString(), 18));
|
|
4653
4965
|
}
|
|
4654
4966
|
}
|
|
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);
|
|
4967
|
+
// Get nonce for current account
|
|
4968
|
+
async eth_getTransactionCount([address, type]) {
|
|
4969
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
4970
|
+
if (accounts[0].address !== address) {
|
|
4971
|
+
throw new Error("address is not the current account");
|
|
4693
4972
|
}
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
value: output.amount,
|
|
4697
|
-
address: output.address
|
|
4698
|
-
});
|
|
4973
|
+
if (type !== "latest" && type !== "pending") {
|
|
4974
|
+
throw new Error("type is not supported");
|
|
4699
4975
|
}
|
|
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
4976
|
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));
|
|
4977
|
+
const rpcClient = await this.createPublicClient({});
|
|
4978
|
+
const nonce = await rpcClient.getTransactionCount({ address });
|
|
4979
|
+
return nonce;
|
|
4760
4980
|
} catch (error) {
|
|
4761
|
-
console.
|
|
4762
|
-
throw new Error(
|
|
4763
|
-
`Failed to convert raw transaction to PSBT: ${error instanceof Error ? error.message : String(error)}`
|
|
4764
|
-
);
|
|
4981
|
+
console.error("Failed to get nonce:", error);
|
|
4982
|
+
throw new Error(`Failed to get nonce: ${error}`);
|
|
4765
4983
|
}
|
|
4766
4984
|
}
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4985
|
+
//https://docs.metamask.io/snaps/reference/keyring-api/chain-methods/#eth_signtransaction
|
|
4986
|
+
async eth_signTransaction(txParams) {
|
|
4987
|
+
const preparedTxPropsType = {
|
|
4988
|
+
chainId: "number",
|
|
4989
|
+
to: "string",
|
|
4990
|
+
value: "bigint",
|
|
4991
|
+
data: "string",
|
|
4992
|
+
nonce: "number",
|
|
4993
|
+
maxPriorityFeePerGas: "bigint",
|
|
4994
|
+
maxFeePerGas: "bigint",
|
|
4995
|
+
gasPrice: "bigint",
|
|
4996
|
+
gas: "bigint"
|
|
4997
|
+
};
|
|
4998
|
+
const txData = txParams?.[0];
|
|
4999
|
+
txData.gas = txData?.gas || txData?.gasLimit;
|
|
5000
|
+
txData.data = txData.data || "0x";
|
|
5001
|
+
txData.value = txData.value || "0x";
|
|
5002
|
+
txData.nonce = txData.nonce || "0x0";
|
|
5003
|
+
if (!txData?.gas || !txData?.to || !txData?.chainId) {
|
|
5004
|
+
throw new Error("gas or to or chainId is not set");
|
|
4774
5005
|
}
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
5006
|
+
const accounts = await this.accountInfo.getCurrent();
|
|
5007
|
+
const address = accounts[0].address;
|
|
5008
|
+
if (txData?.from && !viem.isAddressEqual(txData?.from, address)) {
|
|
5009
|
+
console.error("eth_signTransaction error data: from is not the current account", txData);
|
|
5010
|
+
throw new Error(`eth_signTransaction error data: from is not the current account`);
|
|
5011
|
+
}
|
|
5012
|
+
delete txData?.from;
|
|
5013
|
+
delete txData?.gasLimit;
|
|
5014
|
+
const preparedTx = {
|
|
5015
|
+
gas: txData?.gas
|
|
5016
|
+
};
|
|
5017
|
+
for (const key in preparedTxPropsType) {
|
|
5018
|
+
if (txData?.[key] && viem.isHex(txData?.[key])) {
|
|
5019
|
+
preparedTx[key] = preparedTxPropsType[key] === "number" ? viem.fromHex(txData[key], "number") : txData[key];
|
|
5020
|
+
}
|
|
5021
|
+
}
|
|
5022
|
+
if (txData?.type !== "legacy") {
|
|
5023
|
+
delete preparedTx.gasPrice;
|
|
4785
5024
|
}
|
|
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
5025
|
try {
|
|
4825
|
-
const
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
5026
|
+
const serializedTransaction = await this.accountInfo.signTransaction(
|
|
5027
|
+
JSON.stringify({
|
|
5028
|
+
data: preparedTx,
|
|
5029
|
+
types: preparedTxPropsType
|
|
5030
|
+
})
|
|
5031
|
+
);
|
|
5032
|
+
if (serializedTransaction === "") {
|
|
5033
|
+
throw new Error(`eth_signTransaction error data: ${JSON.stringify(txParams)}`);
|
|
5034
|
+
}
|
|
5035
|
+
const data = viem.parseTransaction(serializedTransaction);
|
|
5036
|
+
return data;
|
|
5037
|
+
} catch (err) {
|
|
5038
|
+
throw new Error("eth_signTransaction error" + err);
|
|
4829
5039
|
}
|
|
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
|
|
5040
|
+
}
|
|
5041
|
+
async eth_sendRawTransaction([serializedTransaction], rpcUrl) {
|
|
5042
|
+
try {
|
|
5043
|
+
const rpcClient = await this.createPublicClient({ rpcUrl });
|
|
5044
|
+
const hash = await rpcClient.sendRawTransaction({
|
|
5045
|
+
serializedTransaction
|
|
4845
5046
|
});
|
|
4846
|
-
|
|
4847
|
-
|
|
5047
|
+
return hash;
|
|
5048
|
+
} catch (error) {
|
|
5049
|
+
console.error("Failed to send transaction via RPC:", error, rpcUrl);
|
|
5050
|
+
throw error;
|
|
4848
5051
|
}
|
|
4849
5052
|
}
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
{
|
|
4855
|
-
|
|
4856
|
-
|
|
5053
|
+
async getTransaction(hash) {
|
|
5054
|
+
if (!hash || !viem.isHex(hash)) {
|
|
5055
|
+
throw new Error("txId is not valid");
|
|
5056
|
+
}
|
|
5057
|
+
try {
|
|
5058
|
+
const rpcClient = await this.createPublicClient({});
|
|
5059
|
+
const tx = await rpcClient.getTransaction({
|
|
5060
|
+
hash
|
|
5061
|
+
});
|
|
5062
|
+
return tx;
|
|
5063
|
+
} catch (error) {
|
|
5064
|
+
console.error("Failed to send transaction via RPC:", error, hash);
|
|
5065
|
+
throw error;
|
|
4857
5066
|
}
|
|
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
5067
|
}
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
5068
|
+
};
|
|
5069
|
+
var BaseConfig2 = walletUtils.SupportedChainTypes[walletUtils.ChainTypes.SOL];
|
|
5070
|
+
var TOKEN_METADATA_PROGRAM_ID = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
|
|
5071
|
+
var MSG_PREFIX = "\xFFsolana offchain";
|
|
5072
|
+
|
|
5073
|
+
// src/solana/utils.ts
|
|
5074
|
+
__toESM(require_bn());
|
|
5075
|
+
new web3_js.PublicKey(TOKEN_METADATA_PROGRAM_ID);
|
|
5076
|
+
var txToHex = (tx) => {
|
|
5077
|
+
if (tx instanceof web3_js.VersionedTransaction) {
|
|
5078
|
+
return Buffer.from(tx.serialize()).toString("hex");
|
|
4896
5079
|
}
|
|
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);
|
|
5080
|
+
if (tx instanceof web3_js.Transaction) {
|
|
5081
|
+
return Buffer.from(
|
|
5082
|
+
tx.serialize({
|
|
5083
|
+
requireAllSignatures: false,
|
|
5084
|
+
verifySignatures: false
|
|
5085
|
+
})
|
|
5086
|
+
).toString("hex");
|
|
4909
5087
|
}
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
5088
|
+
return tx;
|
|
5089
|
+
};
|
|
5090
|
+
async function createLegacyTx({ from = "", to = "", amount = 0 }, connection) {
|
|
5091
|
+
const fromPubkey = new web3_js.PublicKey(from);
|
|
5092
|
+
const toPubkey = new web3_js.PublicKey(to);
|
|
5093
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
|
|
5094
|
+
const transaction = new web3_js.Transaction().add(
|
|
5095
|
+
web3_js.SystemProgram.transfer({
|
|
5096
|
+
fromPubkey,
|
|
5097
|
+
//payer
|
|
5098
|
+
toPubkey,
|
|
5099
|
+
//toAccount
|
|
5100
|
+
lamports: amount * web3_js.LAMPORTS_PER_SOL
|
|
5101
|
+
})
|
|
4918
5102
|
);
|
|
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;
|
|
5103
|
+
transaction.feePayer = fromPubkey;
|
|
5104
|
+
transaction.recentBlockhash = blockhash;
|
|
5105
|
+
transaction.lastValidBlockHeight = lastValidBlockHeight;
|
|
5106
|
+
return transaction;
|
|
4931
5107
|
}
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
5108
|
+
async function createTokenLegacyTransaction2({
|
|
5109
|
+
from = "",
|
|
5110
|
+
to = "",
|
|
5111
|
+
amount = 0,
|
|
5112
|
+
tokenAddress = "",
|
|
5113
|
+
priorityFee = 0,
|
|
5114
|
+
// microLamports (1 SOL = 1e6 microLamports)
|
|
5115
|
+
decimals = 6
|
|
5116
|
+
}, connection) {
|
|
4935
5117
|
try {
|
|
4936
|
-
const
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
5118
|
+
const fromPubkey = new web3_js.PublicKey(from);
|
|
5119
|
+
const toPubkey = new web3_js.PublicKey(to);
|
|
5120
|
+
const tokenPublicKey = new web3_js.PublicKey(tokenAddress);
|
|
5121
|
+
const fromTokenPubKey = await splToken.getAssociatedTokenAddress(tokenPublicKey, fromPubkey);
|
|
5122
|
+
const toTokenPubKey = await splToken.getAssociatedTokenAddress(tokenPublicKey, toPubkey);
|
|
5123
|
+
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
5124
|
+
const transaction = new web3_js.Transaction();
|
|
5125
|
+
transaction.feePayer = fromPubkey;
|
|
5126
|
+
transaction.recentBlockhash = blockhash;
|
|
5127
|
+
try {
|
|
5128
|
+
const instruction = splToken.createAssociatedTokenAccountInstruction(fromPubkey, toTokenPubKey, toPubkey, tokenPublicKey);
|
|
5129
|
+
transaction.add(instruction);
|
|
5130
|
+
} catch (error) {
|
|
5131
|
+
console.error(error);
|
|
5132
|
+
throw error;
|
|
5133
|
+
}
|
|
5134
|
+
const tokenAmount = new Bignumber__default.default(amount).multipliedBy(new Bignumber__default.default(10).pow(decimals)).toNumber();
|
|
5135
|
+
transaction.add(
|
|
5136
|
+
splToken.createTransferInstruction(fromTokenPubKey, toTokenPubKey, fromPubkey, tokenAmount, [], splToken.TOKEN_PROGRAM_ID)
|
|
5137
|
+
);
|
|
5138
|
+
return transaction;
|
|
5139
|
+
} catch (error) {
|
|
5140
|
+
console.error("Error creating transaction:", error);
|
|
5141
|
+
throw error;
|
|
4941
5142
|
}
|
|
4942
|
-
}
|
|
4943
|
-
|
|
4944
|
-
// src/dogecoin/service.ts
|
|
4945
|
-
var DogecoinService = class _DogecoinService extends BaseService {
|
|
5143
|
+
}
|
|
5144
|
+
var SolanaService = class _SolanaService extends BaseService {
|
|
4946
5145
|
static instance;
|
|
4947
5146
|
chainType;
|
|
5147
|
+
rpcUrl;
|
|
5148
|
+
connection;
|
|
4948
5149
|
constructor(chainType, accountInfo, tomoAppInfo) {
|
|
4949
5150
|
super(tomoAppInfo, accountInfo);
|
|
4950
5151
|
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
|
|
5152
|
+
const config = {
|
|
5153
|
+
commitment: "confirmed",
|
|
5154
|
+
disableRetryOnRateLimit: false,
|
|
5155
|
+
confirmTransactionInitialTimeout: 12e4,
|
|
5156
|
+
fetch: (url, options) => {
|
|
5157
|
+
return fetch(url, { ...options });
|
|
5158
|
+
}
|
|
4987
5159
|
};
|
|
5160
|
+
const domain = walletUtils.TomoApiDomains[tomoAppInfo.tomoStage];
|
|
5161
|
+
const rpcUrl = `${domain}/rpc/v1/solana`;
|
|
5162
|
+
this.rpcUrl = rpcUrl;
|
|
5163
|
+
this.connection = new web3_js.Connection(rpcUrl, config);
|
|
4988
5164
|
}
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
if (!address) {
|
|
4993
|
-
throw new Error("address is not set");
|
|
5165
|
+
static getInstance(chainType, accountInfo, tomoAppInfo) {
|
|
5166
|
+
if (!_SolanaService.instance) {
|
|
5167
|
+
_SolanaService.instance = new _SolanaService(chainType, accountInfo, tomoAppInfo);
|
|
4994
5168
|
}
|
|
4995
|
-
|
|
4996
|
-
return {
|
|
4997
|
-
address,
|
|
4998
|
-
balance
|
|
4999
|
-
};
|
|
5169
|
+
return _SolanaService.instance;
|
|
5000
5170
|
}
|
|
5001
|
-
async
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5171
|
+
async getCurrentWalletAccount() {
|
|
5172
|
+
return await this.accountInfo.getCurrent();
|
|
5173
|
+
}
|
|
5174
|
+
async getAccount() {
|
|
5175
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
5176
|
+
return { publicKey: publicKey || currentAddress, address: currentAddress };
|
|
5177
|
+
}
|
|
5178
|
+
async signMessage(params) {
|
|
5179
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
5180
|
+
const { message } = params;
|
|
5005
5181
|
const signature = await this.accountInfo.signMessage(message);
|
|
5006
5182
|
return {
|
|
5007
|
-
|
|
5183
|
+
message: MSG_PREFIX + message,
|
|
5184
|
+
signature,
|
|
5185
|
+
publicKey: publicKey || currentAddress
|
|
5008
5186
|
};
|
|
5009
5187
|
}
|
|
5010
|
-
async
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5188
|
+
async signIn(params) {
|
|
5189
|
+
const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
|
|
5190
|
+
const { signature = "" } = await this.signMessage(params);
|
|
5191
|
+
return {
|
|
5192
|
+
address: currentAddress,
|
|
5193
|
+
publicKey: publicKey || currentAddress,
|
|
5194
|
+
signature,
|
|
5195
|
+
signedMessage: MSG_PREFIX + params.message
|
|
5196
|
+
};
|
|
5018
5197
|
}
|
|
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));
|
|
5198
|
+
async request({ method, params }) {
|
|
5199
|
+
if (!this[method]) {
|
|
5200
|
+
throw new Error(`${method} in request is not supported`);
|
|
5031
5201
|
}
|
|
5202
|
+
return this[method](params);
|
|
5032
5203
|
}
|
|
5033
5204
|
async _queryGasInfo(txData) {
|
|
5034
|
-
const { chainId, amount = 0, decimals =
|
|
5205
|
+
const { tokenAddress, chainId, amount = 0, decimals = 9 } = txData;
|
|
5206
|
+
const value = tokenAddress ? "0x1" : viem.toHex(viem.parseUnits(amount.toString(), decimals));
|
|
5207
|
+
let callData = "0x";
|
|
5208
|
+
if (tokenAddress) {
|
|
5209
|
+
const transaction = await createTokenLegacyTransaction2(txData, this.connection);
|
|
5210
|
+
callData = "0x" + txToHex(transaction);
|
|
5211
|
+
} else {
|
|
5212
|
+
const transaction = await createLegacyTx(txData, this.connection);
|
|
5213
|
+
callData = "0x" + txToHex(transaction);
|
|
5214
|
+
}
|
|
5035
5215
|
const gasLimitParam = {
|
|
5036
5216
|
from: txData.from,
|
|
5037
5217
|
to: txData.to,
|
|
5038
|
-
value
|
|
5218
|
+
value
|
|
5039
5219
|
};
|
|
5040
5220
|
const queryGasParams = {
|
|
5041
|
-
chainIndex: Number(chainId
|
|
5042
|
-
callData
|
|
5221
|
+
chainIndex: Number(chainId),
|
|
5222
|
+
callData,
|
|
5043
5223
|
gasLimitParam,
|
|
5044
5224
|
addressList: [txData.from]
|
|
5045
5225
|
};
|
|
@@ -5052,85 +5232,149 @@ var DogecoinService = class _DogecoinService extends BaseService {
|
|
|
5052
5232
|
params: queryGasParams
|
|
5053
5233
|
});
|
|
5054
5234
|
if (!success) {
|
|
5055
|
-
console.error("queryGasInfo
|
|
5235
|
+
console.error("queryGasInfo solana", txData, message, gasInfo);
|
|
5056
5236
|
const { gasFee } = BaseConfig2;
|
|
5057
5237
|
return {
|
|
5058
5238
|
success: true,
|
|
5059
5239
|
gasFee: gasFee.toString()
|
|
5060
5240
|
};
|
|
5061
5241
|
}
|
|
5062
|
-
const
|
|
5242
|
+
const nativeChainId = walletUtils.SupportedChainTypes[this.chainType].chainId;
|
|
5243
|
+
const { nativeCurrency } = await this.networks.getNetworkByChainId(nativeChainId);
|
|
5244
|
+
const { baseFee = 1, gasLimit = 0 } = gasInfo;
|
|
5245
|
+
const baseFeeBN = new Bignumber.BigNumber(baseFee).times(2);
|
|
5246
|
+
const gasLimitBN = new Bignumber.BigNumber(gasLimit);
|
|
5063
5247
|
const calcFee = (feeLevel) => {
|
|
5064
|
-
const
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
const fees = {
|
|
5068
|
-
low: calcFee("priorityFeeLow"),
|
|
5069
|
-
medium: calcFee("priorityFeeMedium"),
|
|
5070
|
-
high: calcFee("priorityFeeHigh")
|
|
5248
|
+
const priorityFee = gasLimitBN.times(gasInfo[feeLevel] || 0).dividedBy(1e6).integerValue(Bignumber.BigNumber.ROUND_DOWN);
|
|
5249
|
+
const gasFee = baseFeeBN.plus(priorityFee);
|
|
5250
|
+
return viem.formatUnits(BigInt(gasFee.toNumber()), nativeCurrency?.decimals || 9);
|
|
5071
5251
|
};
|
|
5072
5252
|
return {
|
|
5073
5253
|
success: true,
|
|
5074
|
-
fees
|
|
5075
|
-
|
|
5254
|
+
fees: {
|
|
5255
|
+
low: calcFee("priorityFeeLow"),
|
|
5256
|
+
medium: calcFee("priorityFeeMedium"),
|
|
5257
|
+
high: calcFee("priorityFeeHigh")
|
|
5258
|
+
},
|
|
5259
|
+
gasFee: calcFee("priorityFeeHigh")
|
|
5076
5260
|
};
|
|
5077
5261
|
}
|
|
5078
|
-
async
|
|
5079
|
-
rawTx
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5262
|
+
async sendSignedTx(signedTx) {
|
|
5263
|
+
let rawTx;
|
|
5264
|
+
try {
|
|
5265
|
+
const hexBuffer = Buffer.from(signedTx, "hex");
|
|
5266
|
+
web3_js.VersionedTransaction.deserialize(hexBuffer);
|
|
5267
|
+
rawTx = hexBuffer;
|
|
5268
|
+
} catch (error) {
|
|
5269
|
+
const base58Buffer = Buffer.from(toBase58());
|
|
5270
|
+
web3_js.VersionedTransaction.deserialize(base58Buffer);
|
|
5271
|
+
rawTx = base58Buffer;
|
|
5086
5272
|
}
|
|
5087
|
-
const
|
|
5088
|
-
if (
|
|
5089
|
-
|
|
5090
|
-
signedRawTx
|
|
5091
|
-
};
|
|
5273
|
+
const transaction = web3_js.VersionedTransaction.deserialize(rawTx);
|
|
5274
|
+
if (!transaction.signatures || transaction.signatures.length === 0) {
|
|
5275
|
+
throw new Error("lack of sign");
|
|
5092
5276
|
}
|
|
5093
|
-
const
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5277
|
+
const confirmationStrategy = {
|
|
5278
|
+
commitment: "confirmed",
|
|
5279
|
+
preflightCommitment: "processed",
|
|
5280
|
+
skipPreflight: false,
|
|
5281
|
+
maxRetries: 5
|
|
5097
5282
|
};
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5283
|
+
try {
|
|
5284
|
+
const simulation = await this.connection.simulateTransaction(transaction);
|
|
5285
|
+
if (simulation.value.err) {
|
|
5286
|
+
const logs = simulation.value.logs || [];
|
|
5287
|
+
const message = logs[logs.length - 1] || simulation.value.err || "Unknown error occurred";
|
|
5288
|
+
console.error("send err logs:", logs, message);
|
|
5289
|
+
throw new Error(`Transaction failed: ${message}`);
|
|
5290
|
+
}
|
|
5291
|
+
const signature = await web3_js.sendAndConfirmRawTransaction(this.connection, rawTx, confirmationStrategy);
|
|
5292
|
+
return signature;
|
|
5293
|
+
} catch (error) {
|
|
5294
|
+
if (error.message.includes("TransactionExpiredTimeoutError")) {
|
|
5295
|
+
const status = await this.connection.getSignatureStatus(transaction.signatures[0].toString());
|
|
5296
|
+
if (status.value?.confirmationStatus === "confirmed") {
|
|
5297
|
+
return transaction.signatures[0].toString();
|
|
5298
|
+
}
|
|
5299
|
+
}
|
|
5300
|
+
throw error;
|
|
5103
5301
|
}
|
|
5104
|
-
|
|
5105
|
-
|
|
5302
|
+
}
|
|
5303
|
+
async signTransaction({ rawTransaction }) {
|
|
5304
|
+
const params = {
|
|
5305
|
+
rawTransaction,
|
|
5306
|
+
walletId: -1,
|
|
5307
|
+
rpcUrl: this.rpcUrl
|
|
5308
|
+
};
|
|
5309
|
+
const signature = await this.accountInfo.signTransaction(JSON.stringify(params));
|
|
5310
|
+
return signature;
|
|
5311
|
+
}
|
|
5312
|
+
async signAndSendTransaction({ rawTransaction }) {
|
|
5313
|
+
const signedTx = await this.signTransaction({ rawTransaction });
|
|
5314
|
+
if (!signedTx) {
|
|
5315
|
+
return "";
|
|
5106
5316
|
}
|
|
5107
|
-
const
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5317
|
+
const signature = await this.sendSignedTx(signedTx);
|
|
5318
|
+
return signature;
|
|
5319
|
+
}
|
|
5320
|
+
async signAllTransactions({ rawTransactions }) {
|
|
5321
|
+
throw new Error("no support");
|
|
5322
|
+
}
|
|
5323
|
+
async signAndSendAllTransactions({ rawTransactions }) {
|
|
5324
|
+
throw new Error("no support");
|
|
5325
|
+
}
|
|
5326
|
+
async queryRent(params) {
|
|
5327
|
+
const MIN_ACCOUNT_ACTIVATION_SOL = "0.0009";
|
|
5328
|
+
const SPL_TOKEN_ACCOUNT_CREATION_FEE = "0.001";
|
|
5329
|
+
try {
|
|
5330
|
+
const { toAddress, tokenAddress } = params;
|
|
5331
|
+
let minTransferAmount = "0";
|
|
5332
|
+
let createSplTokenFee = null;
|
|
5333
|
+
if (tokenAddress) {
|
|
5334
|
+
const tokenAccountExists = await this.checkTokenAccount(toAddress, tokenAddress);
|
|
5335
|
+
if (!tokenAccountExists) {
|
|
5336
|
+
createSplTokenFee = SPL_TOKEN_ACCOUNT_CREATION_FEE;
|
|
5337
|
+
}
|
|
5338
|
+
} else {
|
|
5339
|
+
const accountExists = await this.checkAccountExists(toAddress);
|
|
5340
|
+
if (!accountExists) {
|
|
5341
|
+
minTransferAmount = MIN_ACCOUNT_ACTIVATION_SOL;
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5344
|
+
const rentInfo = {
|
|
5345
|
+
minTransferAmount,
|
|
5346
|
+
createSplTokenFee
|
|
5347
|
+
};
|
|
5348
|
+
return rentInfo;
|
|
5349
|
+
} catch (error) {
|
|
5350
|
+
console.error("queryRent error:", error);
|
|
5351
|
+
const defaultRentInfo = {
|
|
5352
|
+
minTransferAmount: "0",
|
|
5353
|
+
createSplTokenFee: null
|
|
5354
|
+
};
|
|
5355
|
+
return defaultRentInfo;
|
|
5112
5356
|
}
|
|
5357
|
+
}
|
|
5358
|
+
async checkAccountExists(address) {
|
|
5113
5359
|
try {
|
|
5114
|
-
const
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5360
|
+
const accountInfo = await this.connection.getAccountInfo(new web3_js.PublicKey(address));
|
|
5361
|
+
return accountInfo !== null;
|
|
5362
|
+
} catch (error) {
|
|
5363
|
+
console.warn("Failed to check account exists:", error);
|
|
5364
|
+
return false;
|
|
5119
5365
|
}
|
|
5120
5366
|
}
|
|
5121
|
-
async
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5367
|
+
async checkTokenAccount(ownerAddress, tokenMint) {
|
|
5368
|
+
try {
|
|
5369
|
+
const owner = new web3_js.PublicKey(ownerAddress);
|
|
5370
|
+
const mint = new web3_js.PublicKey(tokenMint);
|
|
5371
|
+
const associatedTokenAddress = await splToken.getAssociatedTokenAddress(mint, owner);
|
|
5372
|
+
const accountInfo = await this.connection.getAccountInfo(associatedTokenAddress);
|
|
5373
|
+
return accountInfo !== null;
|
|
5374
|
+
} catch (error) {
|
|
5375
|
+
console.warn("Failed to check token account:", error);
|
|
5376
|
+
return false;
|
|
5125
5377
|
}
|
|
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
5378
|
}
|
|
5135
5379
|
};
|
|
5136
5380
|
var TomoWallet = class _TomoWallet extends BaseService {
|
|
@@ -5226,8 +5470,18 @@ var ChainTypeServices = {
|
|
|
5226
5470
|
|
|
5227
5471
|
exports.AccountType = AccountType;
|
|
5228
5472
|
exports.ChainTypeServices = ChainTypeServices;
|
|
5473
|
+
exports.DogecoinAPI = rpc_exports;
|
|
5474
|
+
exports.DogecoinAddress = DogecoinAddress;
|
|
5229
5475
|
exports.DogecoinService = DogecoinService;
|
|
5476
|
+
exports.DogecoinUtils = DogecoinUtils;
|
|
5230
5477
|
exports.EvmService = EvmService;
|
|
5231
5478
|
exports.SolanaService = SolanaService;
|
|
5232
5479
|
exports.TomoWallet = TomoWallet;
|
|
5480
|
+
exports.TransactionParser = TransactionParser;
|
|
5233
5481
|
exports.TxTypes = TxTypes;
|
|
5482
|
+
exports.addUsedUtxos = addUsedUtxos;
|
|
5483
|
+
exports.createPsbt = createPsbt;
|
|
5484
|
+
exports.decodePsbt = decodePsbt;
|
|
5485
|
+
exports.getUsedUtxos = getUsedUtxos;
|
|
5486
|
+
exports.toBitcoin = toBitcoin;
|
|
5487
|
+
exports.toSatoshi = toSatoshi;
|