@tomo-inc/chains-service 0.0.8 → 0.0.9

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