@tomo-inc/chains-service 0.0.8 → 0.0.10

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