@tomo-inc/chains-service 0.0.6 → 0.0.8

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.js CHANGED
@@ -1,10 +1,11 @@
1
- import { ChainTypes, SupportedChainTypes, TomoApiDomains, cache, getExplorerUrl } from '@tomo-inc/wallet-utils';
1
+ import { ChainTypes, SupportedChainTypes, cache, TomoApiDomains, getExplorerUrl } from '@tomo-inc/wallet-utils';
2
2
  import Bignumber, { BigNumber } from 'bignumber.js';
3
- import { toHex, parseUnits, createPublicClient, http, isHex, fromHex, parseTransaction, formatUnits, encodeFunctionData, erc20Abi } from 'viem';
3
+ import { toHex, parseUnits, isAddressEqual, createPublicClient, http, hexToBigInt, numberToHex, isHex, fromHex, parseTransaction, formatUnits, encodeFunctionData, erc20Abi } from 'viem';
4
4
  import axios from 'axios';
5
5
  import CryptoJS from 'crypto-js';
6
6
  import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, createTransferInstruction, TOKEN_PROGRAM_ID } from '@solana/spl-token';
7
- import { PublicKey, Connection, VersionedTransaction, sendAndConfirmRawTransaction, Transaction, SystemProgram, LAMPORTS_PER_SOL } from '@solana/web3.js';
7
+ import { PublicKey, Connection, VersionedTransaction, sendAndConfirmRawTransaction, Transaction as Transaction$1, SystemProgram, LAMPORTS_PER_SOL } from '@solana/web3.js';
8
+ import { Psbt, Transaction, address } from 'bitcoinjs-lib';
8
9
 
9
10
  var __create = Object.create;
10
11
  var __defProp = Object.defineProperty;
@@ -2796,18 +2797,18 @@ var Networks = class {
2796
2797
  return networks;
2797
2798
  }
2798
2799
  async getNetworkByChainId(chainId) {
2799
- const network = await this.networkAPIs.getNetworkByChainId(chainId);
2800
- if (!network) {
2800
+ const network2 = await this.networkAPIs.getNetworkByChainId(chainId);
2801
+ if (!network2) {
2801
2802
  throw new Error("Network not found");
2802
2803
  }
2803
- return network;
2804
+ return network2;
2804
2805
  }
2805
2806
  async getNetworkByChainIndex(chainIndex) {
2806
- const network = await this.networkAPIs.getNetworkByChainIndex(chainIndex);
2807
- if (!network) {
2807
+ const network2 = await this.networkAPIs.getNetworkByChainIndex(chainIndex);
2808
+ if (!network2) {
2808
2809
  throw new Error("Network not found");
2809
2810
  }
2810
- return network;
2811
+ return network2;
2811
2812
  }
2812
2813
  async getCurrentNetwork() {
2813
2814
  const chainType = this.chainType;
@@ -2830,8 +2831,8 @@ var Tokens = class {
2830
2831
  constructor(tokenService) {
2831
2832
  this.tokenAPIs = tokenService;
2832
2833
  }
2833
- async searchTokens({ chainIndex, query }) {
2834
- const { data: tokens = [] } = await this.tokenAPIs.queryRemoteTokens({ keyword: query || "", chainIndex });
2834
+ async searchTokens({ chainIndex, keyword }) {
2835
+ const { data: tokens = [] } = await this.tokenAPIs.queryRemoteTokens({ keyword, chainIndex });
2835
2836
  return tokens;
2836
2837
  }
2837
2838
  // public async addToken(tokenInfo: Omit<TokenInfo, "id">): Promise<TokenInfo> {
@@ -2839,10 +2840,8 @@ var Tokens = class {
2839
2840
  // return tokenInfoData;
2840
2841
  // }
2841
2842
  async getTokenInfo({ address, chainIndex }) {
2842
- const {
2843
- data: { data: tokenInfo }
2844
- } = await this.tokenAPIs.getTokenInfo({ address, chainIndex });
2845
- return tokenInfo;
2843
+ const res = await this.tokenAPIs.getTokenInfo({ address, chainIndex });
2844
+ return res?.data?.data;
2846
2845
  }
2847
2846
  async getTokenRiskInfo(_params) {
2848
2847
  const { data: tokenRiskInfo } = await this.tokenAPIs.getTokenRisk(_params);
@@ -2972,7 +2971,7 @@ var BasePublicService = class {
2972
2971
  this.apiBase = publicApiBase;
2973
2972
  this.tomoAppInfo = tomoAppInfo;
2974
2973
  [this.tokenApi, this.txApi, this.walletApi].map(
2975
- (api, i) => api.interceptors.request.use((params) => {
2974
+ (api2, i) => api2.interceptors.request.use((params) => {
2976
2975
  const { tokenBaseUrl, txBaseUrl, walletBaseUrl } = publicApiBase;
2977
2976
  params.baseURL = [tokenBaseUrl, txBaseUrl, walletBaseUrl][i];
2978
2977
  return signRequest(params, tomoAppInfo.tomoClientId, tomoAppInfo);
@@ -3013,10 +3012,15 @@ var TokenAPIs = class _TokenAPIs extends BasePublicService {
3013
3012
  }
3014
3013
  }
3015
3014
  async getTokenRisk(params) {
3016
- if (typeof params.chainIndex !== "number" || !params.tokenAddress) {
3015
+ if (typeof params.chainIndex !== "number" || !params.address) {
3017
3016
  throw new Error("chainName or tokenAddress is required");
3018
3017
  }
3019
- const res = await this.tokenApi.post("/v1/market/risk/details", [params]);
3018
+ const res = await this.tokenApi.post("/v1/market/risk/details", [
3019
+ {
3020
+ chainIndex: params.chainIndex,
3021
+ tokenAddress: params.address
3022
+ }
3023
+ ]);
3020
3024
  return {
3021
3025
  success: res?.data?.code === "0",
3022
3026
  message: res?.data?.msg,
@@ -3104,10 +3108,10 @@ var TokenAPIs = class _TokenAPIs extends BasePublicService {
3104
3108
  }
3105
3109
  }
3106
3110
  async getTokenDetail(params) {
3107
- const { chainIndex, tokenAddress } = params;
3111
+ const { chainIndex, address } = params;
3108
3112
  const res = await this.tokenApi.get(`/v1/market/token/detail`, {
3109
3113
  params: {
3110
- tokenAddress,
3114
+ tokenAddress: address,
3111
3115
  chainIndex
3112
3116
  }
3113
3117
  });
@@ -3259,12 +3263,26 @@ var loadNetworks = (WALLET_DOMAIN) => {
3259
3263
  supportHistory: false
3260
3264
  },
3261
3265
  {
3262
- chainId: 221122420,
3263
- chainIndex: 22112242e3,
3264
- name: "DOGEOS_DEVNET",
3265
- chainName: "DogeOS Devnet",
3266
+ chainId: 26888,
3267
+ chainIndex: 2688800,
3268
+ name: "ABCORE_TESTNET",
3269
+ chainName: "AB Core Testnet",
3270
+ rpcUrls: ["https://rpc.core.testnet.ab.org"],
3271
+ blockExplorerUrl: "https://explorer.core.testnet.ab.org",
3272
+ platformType: "EVM",
3273
+ isTestnet: true,
3274
+ icon: "/assets/ab.svg",
3275
+ supportSwap: true,
3276
+ supportGift: false,
3277
+ supportHistory: true
3278
+ },
3279
+ {
3280
+ chainId: 6281971,
3281
+ chainIndex: 628197100,
3282
+ name: "DOGEOS_TESTNET",
3283
+ chainName: "DogeOS Testnet",
3266
3284
  rpcUrls: [`${WALLET_DOMAIN}/rpc/v1/doge_test`],
3267
- blockExplorerUrl: "https://blockscout.devnet.doge.xyz",
3285
+ blockExplorerUrl: "https://rpc.testnet.dogeos.com",
3268
3286
  platformType: "EVM",
3269
3287
  isTestnet: true,
3270
3288
  icon: "/assets/dogeos.svg",
@@ -3886,7 +3904,7 @@ var BaseService = class {
3886
3904
  }
3887
3905
  const baseUrlConfig = CONFIG[tomoAppInfo.tomoStage];
3888
3906
  const { tokenAPIs, transactionAPIs, networkAPIs } = tomoPublicApiService(baseUrlConfig, tomoAppInfo);
3889
- this.accountInfo = accountInfo;
3907
+ this.accountInfo = accountInfo || null;
3890
3908
  this.networks = new Networks(networkAPIs);
3891
3909
  this.tokens = new Tokens(tokenAPIs);
3892
3910
  this.transactions = new Transactions(transactionAPIs);
@@ -3896,8 +3914,8 @@ var BaseService = class {
3896
3914
  this.approveParams = params;
3897
3915
  }
3898
3916
  };
3899
- function getRPCClient(network) {
3900
- const { chainId, name, rpcUrls, nativeCurrencyDecimals, nativeCurrencyName, nativeCurrencySymbol } = network;
3917
+ function getRPCClient(network2) {
3918
+ const { chainId, name, rpcUrls, nativeCurrencyDecimals, nativeCurrencyName, nativeCurrencySymbol } = network2;
3901
3919
  const myCustomChain = {
3902
3920
  id: Number(chainId) || chainId,
3903
3921
  name,
@@ -3952,8 +3970,8 @@ var TxTypes = /* @__PURE__ */ ((TxTypes2) => {
3952
3970
  })(TxTypes || {});
3953
3971
 
3954
3972
  // src/evm/utils.ts
3955
- var isEvmChain = (network) => {
3956
- return network && network?.platformType === "EVM";
3973
+ var isEvmChain = (network2) => {
3974
+ return network2 && network2?.platformType === "EVM";
3957
3975
  };
3958
3976
  var getAllTypeChainIds = ({ chainId, chainType }) => {
3959
3977
  if (isHex(chainId)) {
@@ -4045,7 +4063,7 @@ var EvmService = class _EvmService extends BaseService {
4045
4063
  }
4046
4064
  async personal_sign([message, address]) {
4047
4065
  const accounts = await this.accountInfo.getCurrent();
4048
- if (accounts[0].address !== address) {
4066
+ if (!isAddressEqual(accounts[0].address, address)) {
4049
4067
  throw new Error("address is not the current account");
4050
4068
  }
4051
4069
  const signature = await this.accountInfo.signMessage(message);
@@ -4053,7 +4071,7 @@ var EvmService = class _EvmService extends BaseService {
4053
4071
  }
4054
4072
  async eth_signTypedData_v4([address, typedData]) {
4055
4073
  const accounts = await this.accountInfo.getCurrent();
4056
- if (accounts[0].address !== address) {
4074
+ if (!isAddressEqual(accounts[0].address, address)) {
4057
4075
  throw new Error("address is not the current account");
4058
4076
  }
4059
4077
  const signature = await this.accountInfo.signTypedData(typedData);
@@ -4061,7 +4079,7 @@ var EvmService = class _EvmService extends BaseService {
4061
4079
  }
4062
4080
  async eth_getBalance([address, type]) {
4063
4081
  const accounts = await this.accountInfo.getCurrent();
4064
- if (accounts[0].address !== address) {
4082
+ if (!isAddressEqual(accounts[0].address, address)) {
4065
4083
  throw new Error("address is not the current account");
4066
4084
  }
4067
4085
  if (type !== "latest") {
@@ -4105,8 +4123,8 @@ var EvmService = class _EvmService extends BaseService {
4105
4123
  });
4106
4124
  if (!success) {
4107
4125
  console.error("queryGasInfo evm", txData, queryGasParams, message, gasInfo);
4108
- const BaseConfig2 = SupportedChainTypes[ChainTypes.EVM];
4109
- const { gasFee } = BaseConfig2;
4126
+ const BaseConfig3 = SupportedChainTypes[ChainTypes.EVM];
4127
+ const { gasFee } = BaseConfig3;
4110
4128
  return {
4111
4129
  success: true,
4112
4130
  gasFee: gasFee.toString()
@@ -4138,51 +4156,6 @@ var EvmService = class _EvmService extends BaseService {
4138
4156
  }
4139
4157
  };
4140
4158
  }
4141
- async eth_estimateGas(txs) {
4142
- const { from, to, value = "0x1" } = txs[0] || {};
4143
- const accounts = await this.accountInfo.getCurrent();
4144
- if (accounts[0].address !== from) {
4145
- throw new Error("address is not the current account");
4146
- }
4147
- if (!to) {
4148
- throw new Error("to is not set");
4149
- }
4150
- const chainId = txs[0]?.chainId || await this.eth_chainId();
4151
- const queryGasParams = {
4152
- chainIndex: Number(chainId),
4153
- callData: "0x",
4154
- gasLimitParam: {
4155
- from,
4156
- to,
4157
- value
4158
- },
4159
- addressList: [from]
4160
- };
4161
- const chainType = this.chainType;
4162
- const {
4163
- data: gasInfo,
4164
- success,
4165
- message
4166
- } = await this.transactions.queryGasInfo({
4167
- chainType,
4168
- params: queryGasParams
4169
- });
4170
- if (!success) {
4171
- console.error("queryGasInfo evm", txs, message, gasInfo);
4172
- const { gasFee } = SupportedChainTypes[chainType];
4173
- return gasFee.toString();
4174
- }
4175
- const res = getAllTypeChainIds({ chainId, chainType });
4176
- const { nativeCurrencyDecimals } = await this.networks.getNetworkByChainId(res.chainId);
4177
- const { baseFee = 1, gasLimit = 0 } = gasInfo;
4178
- const baseFeeBN = new BigNumber(baseFee);
4179
- const calcFee = (feeLevel) => {
4180
- const fee = baseFeeBN.plus(gasInfo[feeLevel] || 0);
4181
- const gasFee = fee.times(gasLimit);
4182
- return toHex(BigInt(gasFee.toNumber()), nativeCurrencyDecimals);
4183
- };
4184
- return calcFee("priorityFeeMedium");
4185
- }
4186
4159
  async createPublicClient({ chainId, rpcUrl }) {
4187
4160
  if (rpcUrl) {
4188
4161
  return createPublicClient({
@@ -4204,6 +4177,26 @@ var EvmService = class _EvmService extends BaseService {
4204
4177
  transport: http(rpcUrl)
4205
4178
  });
4206
4179
  }
4180
+ async eth_estimateGas(txs) {
4181
+ const { from, to, value = "0x1", chainId } = txs[0] || {};
4182
+ const accounts = await this.accountInfo.getCurrent();
4183
+ if (accounts[0].address !== from) {
4184
+ throw new Error("address is not the current account");
4185
+ }
4186
+ if (!to) {
4187
+ throw new Error("to is not set");
4188
+ }
4189
+ try {
4190
+ const rpcClient = await this.createPublicClient({ chainId: chainId?.toString() });
4191
+ const gas = await rpcClient.estimateGas({ account: from, to, value: hexToBigInt(value) });
4192
+ return gas;
4193
+ } catch (error) {
4194
+ console.warn("Failed to estimate gas:", error);
4195
+ const BaseConfig3 = SupportedChainTypes[ChainTypes.EVM];
4196
+ const { gasFee } = BaseConfig3;
4197
+ return numberToHex(parseUnits(gasFee.toString(), 18));
4198
+ }
4199
+ }
4207
4200
  // Get nonce for current account
4208
4201
  async eth_getTransactionCount([address, type]) {
4209
4202
  const accounts = await this.accountInfo.getCurrent();
@@ -4232,30 +4225,35 @@ var EvmService = class _EvmService extends BaseService {
4232
4225
  nonce: "number",
4233
4226
  maxPriorityFeePerGas: "bigint",
4234
4227
  maxFeePerGas: "bigint",
4235
- gasLimit: "bigint",
4228
+ gasPrice: "bigint",
4236
4229
  gas: "bigint"
4237
4230
  };
4238
- let txData = txParams?.[0];
4239
- const gas = txData?.gas || txData?.gasLimit;
4240
- txData = { ...txData, gas, gasLimit: gas };
4231
+ const txData = txParams?.[0];
4232
+ txData.gas = txData?.gas || txData?.gasLimit;
4241
4233
  txData.data = txData.data || "0x";
4234
+ txData.value = txData.value || "0x";
4235
+ txData.nonce = txData.nonce || "0x0";
4236
+ if (!txData?.gas || !txData?.to || !txData?.chainId) {
4237
+ throw new Error("gas or to or chainId is not set");
4238
+ }
4242
4239
  const accounts = await this.accountInfo.getCurrent();
4243
4240
  const address = accounts[0].address;
4244
- if (txData?.from && txData?.from !== address) {
4241
+ if (txData?.from && !isAddressEqual(txData?.from, address)) {
4245
4242
  console.error("eth_signTransaction error data: from is not the current account", txData);
4246
4243
  throw new Error(`eth_signTransaction error data: from is not the current account`);
4247
4244
  }
4248
4245
  delete txData?.from;
4246
+ delete txData?.gasLimit;
4249
4247
  const preparedTx = {
4250
- type: "eip1559",
4251
- gas: txData?.gasLimit,
4252
- account: address
4248
+ gas: txData?.gas
4253
4249
  };
4254
4250
  for (const key in preparedTxPropsType) {
4255
- if (!txData?.[key] || !isHex(txData?.[key])) {
4256
- throw new Error(`${key}: no data or wrong type`);
4251
+ if (txData?.[key] && isHex(txData?.[key])) {
4252
+ preparedTx[key] = preparedTxPropsType[key] === "number" ? fromHex(txData[key], "number") : txData[key];
4257
4253
  }
4258
- preparedTx[key] = preparedTxPropsType[key] === "number" ? fromHex(txData[key], "number") : txData[key];
4254
+ }
4255
+ if (txData?.type !== "legacy") {
4256
+ delete preparedTx.gasPrice;
4259
4257
  }
4260
4258
  try {
4261
4259
  const serializedTransaction = await this.accountInfo.signTransaction(
@@ -4312,7 +4310,7 @@ var txToHex = (tx) => {
4312
4310
  if (tx instanceof VersionedTransaction) {
4313
4311
  return Buffer.from(tx.serialize()).toString("hex");
4314
4312
  }
4315
- if (tx instanceof Transaction) {
4313
+ if (tx instanceof Transaction$1) {
4316
4314
  return Buffer.from(
4317
4315
  tx.serialize({
4318
4316
  requireAllSignatures: false,
@@ -4326,7 +4324,7 @@ async function createLegacyTx({ from = "", to = "", amount = 0 }, connection) {
4326
4324
  const fromPubkey = new PublicKey(from);
4327
4325
  const toPubkey = new PublicKey(to);
4328
4326
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
4329
- const transaction = new Transaction().add(
4327
+ const transaction = new Transaction$1().add(
4330
4328
  SystemProgram.transfer({
4331
4329
  fromPubkey,
4332
4330
  //payer
@@ -4356,7 +4354,7 @@ async function createTokenLegacyTransaction2({
4356
4354
  const fromTokenPubKey = await getAssociatedTokenAddress(tokenPublicKey, fromPubkey);
4357
4355
  const toTokenPubKey = await getAssociatedTokenAddress(tokenPublicKey, toPubkey);
4358
4356
  const { blockhash } = await connection.getLatestBlockhash("confirmed");
4359
- const transaction = new Transaction();
4357
+ const transaction = new Transaction$1();
4360
4358
  transaction.feePayer = fromPubkey;
4361
4359
  transaction.recentBlockhash = blockhash;
4362
4360
  try {
@@ -4378,6 +4376,34 @@ async function createTokenLegacyTransaction2({
4378
4376
  }
4379
4377
 
4380
4378
  // src/dogecoin/base.ts
4379
+ function fromHex3(hex) {
4380
+ const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
4381
+ const paddedHex = cleanHex.length % 2 === 0 ? cleanHex : "0" + cleanHex;
4382
+ const bytes = new Uint8Array(paddedHex.length / 2);
4383
+ for (let i = 0; i < paddedHex.length; i += 2) {
4384
+ bytes[i / 2] = parseInt(paddedHex.substr(i, 2), 16);
4385
+ }
4386
+ return bytes;
4387
+ }
4388
+ function toHex3(data) {
4389
+ if (typeof data === "string") {
4390
+ return data.startsWith("0x") ? data.slice(2) : data;
4391
+ }
4392
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
4393
+ return buffer.toString("hex");
4394
+ }
4395
+ function toBase64(data) {
4396
+ if (typeof data === "string") {
4397
+ const buffer2 = Buffer.from(data, "hex");
4398
+ return buffer2.toString("base64");
4399
+ }
4400
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
4401
+ return buffer.toString("base64");
4402
+ }
4403
+ function fromBase64(base64) {
4404
+ const buffer = Buffer.from(base64, "base64");
4405
+ return new Uint8Array(buffer);
4406
+ }
4381
4407
  function toBase58(data) {
4382
4408
  throw new Error("toBase58 requires bs58 package. Install it: pnpm add bs58");
4383
4409
  }
@@ -4414,11 +4440,11 @@ var SolanaService = class _SolanaService extends BaseService {
4414
4440
  return await this.accountInfo.getCurrent();
4415
4441
  }
4416
4442
  async getAccount() {
4417
- const { currentAddress, publicKey } = await this.accountInfo.getIds();
4443
+ const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
4418
4444
  return { publicKey: publicKey || currentAddress, address: currentAddress };
4419
4445
  }
4420
4446
  async signMessage(params) {
4421
- const { currentAddress, publicKey } = await this.accountInfo.getIds();
4447
+ const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
4422
4448
  const { message } = params;
4423
4449
  const signature = await this.accountInfo.signMessage(message);
4424
4450
  return {
@@ -4428,7 +4454,7 @@ var SolanaService = class _SolanaService extends BaseService {
4428
4454
  };
4429
4455
  }
4430
4456
  async signIn(params) {
4431
- const { currentAddress, publicKey } = await this.accountInfo.getIds();
4457
+ const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
4432
4458
  const { signature = "" } = await this.signMessage(params);
4433
4459
  return {
4434
4460
  address: currentAddress,
@@ -4619,6 +4645,486 @@ var SolanaService = class _SolanaService extends BaseService {
4619
4645
  }
4620
4646
  }
4621
4647
  };
4648
+ var BaseConfig2 = SupportedChainTypes[ChainTypes.DOGE];
4649
+ var DECIMALS = 1e8;
4650
+ var MYDOGE_BASE_URL = "https://api.mydoge.com";
4651
+ var RPC_URL = "https://api.bitcore.io/api/DOGE/mainnet";
4652
+ var RPC_TIMEOUT = 20 * 1e3;
4653
+ var network = {
4654
+ messagePrefix: "Dogecoin Signed Message:\n",
4655
+ bech32: "dc",
4656
+ bip44: 3,
4657
+ bip32: {
4658
+ public: 49990397,
4659
+ private: 49988504
4660
+ },
4661
+ pubKeyHash: 30,
4662
+ scriptHash: 22,
4663
+ wif: 158
4664
+ };
4665
+
4666
+ // src/dogecoin/utils-doge.ts
4667
+ var DogecoinUtils = class {
4668
+ /**
4669
+ * Build the PSBT from the transaction
4670
+ * @param tx utxoTx
4671
+ * @param _maximumFeeRate number (optional, currently unused)
4672
+ * @returns base64
4673
+ */
4674
+ static buildPsbtToBase64(tx, _maximumFeeRate) {
4675
+ const psbt = new Psbt({ network });
4676
+ for (const input of tx.inputs) {
4677
+ const inputOptions = {
4678
+ hash: input.txId,
4679
+ index: input.vOut
4680
+ };
4681
+ if (input.nonWitnessUtxo) {
4682
+ inputOptions.nonWitnessUtxo = Buffer.from(input.nonWitnessUtxo, "hex");
4683
+ }
4684
+ psbt.addInput(inputOptions);
4685
+ }
4686
+ for (const output of tx.outputs) {
4687
+ psbt.addOutput({
4688
+ value: output.amount,
4689
+ address: output.address
4690
+ });
4691
+ }
4692
+ const psbtHex = psbt.toHex();
4693
+ return toBase64(fromHex3(psbtHex));
4694
+ }
4695
+ /**
4696
+ * Extract the transaction from the signed PSBT
4697
+ * @param signedPsbt base64
4698
+ * @param _maximumFeeRate number (optional, currently unused)
4699
+ * @returns transaction hex
4700
+ */
4701
+ static extractPsbtTransaction(signedPsbt, _maximumFeeRate) {
4702
+ const signedPsbtHex = toHex3(fromBase64(signedPsbt));
4703
+ const psbt = Psbt.fromHex(signedPsbtHex, {
4704
+ network
4705
+ });
4706
+ const maximumFeeRate = _maximumFeeRate || 1e5;
4707
+ psbt.setMaximumFeeRate(maximumFeeRate);
4708
+ psbt.finalizeAllInputs();
4709
+ const transaction = psbt.extractTransaction();
4710
+ return transaction.toHex();
4711
+ }
4712
+ /**
4713
+ * Convert raw transaction hex to PSBT base64
4714
+ * This method converts an unsigned raw transaction to PSBT format.
4715
+ * Note: The rawTx should be an unsigned transaction, and the nonWitnessUtxo
4716
+ * for each input will need to be fetched separately from the blockchain.
4717
+ * @param rawTx hex string of the raw transaction
4718
+ * @returns base64 string of the PSBT
4719
+ */
4720
+ static async rawTxToPsbtBase64(rawTx) {
4721
+ try {
4722
+ const cleanHex = rawTx.startsWith("0x") ? rawTx.slice(2) : rawTx;
4723
+ const transaction = Transaction.fromHex(cleanHex);
4724
+ const psbt = new Psbt({ network });
4725
+ for (let i = 0; i < transaction.ins.length; i++) {
4726
+ const input = transaction.ins[i];
4727
+ const hash = Buffer.from(input.hash).reverse().toString("hex");
4728
+ const index = input.index;
4729
+ const inputOptions = {
4730
+ hash,
4731
+ index,
4732
+ nonWitnessUtxo: input.script
4733
+ };
4734
+ psbt.addInput(inputOptions);
4735
+ }
4736
+ for (const output of transaction.outs) {
4737
+ try {
4738
+ const address$1 = address.fromOutputScript(output.script, network);
4739
+ psbt.addOutput({
4740
+ address: address$1,
4741
+ value: Number(output.value)
4742
+ });
4743
+ } catch (error) {
4744
+ psbt.addOutput({
4745
+ script: output.script,
4746
+ value: Number(output.value)
4747
+ });
4748
+ }
4749
+ }
4750
+ const psbtHex = psbt.toHex();
4751
+ return toBase64(fromHex3(psbtHex));
4752
+ } catch (error) {
4753
+ console.warn("rawTxToPsbtBase64 failed:", error);
4754
+ throw new Error(
4755
+ `Failed to convert raw transaction to PSBT: ${error instanceof Error ? error.message : String(error)}`
4756
+ );
4757
+ }
4758
+ }
4759
+ };
4760
+ var KOINU_PER_DOGE = new BigNumber(DECIMALS);
4761
+ function toSatoshi(bitcion) {
4762
+ try {
4763
+ const amount = new BigNumber(bitcion);
4764
+ if (amount.isNaN() || amount.isNegative()) {
4765
+ throw new Error("Invalid amount");
4766
+ }
4767
+ return amount.times(KOINU_PER_DOGE).integerValue(BigNumber.ROUND_DOWN).toNumber();
4768
+ } catch (error) {
4769
+ throw new Error(`toSatoshi failed: ${error.message}`);
4770
+ }
4771
+ }
4772
+ function toBitcoin(satoshis) {
4773
+ try {
4774
+ const amount = new BigNumber(satoshis);
4775
+ if (amount.isNaN() || amount.isNegative()) {
4776
+ throw new Error("Invalid Koinu amount");
4777
+ }
4778
+ return amount.dividedBy(KOINU_PER_DOGE).toNumber();
4779
+ } catch (error) {
4780
+ throw new Error(`toBitcoin failed: ${error.message}`);
4781
+ }
4782
+ }
4783
+ function addUsedUtxos(newUsedUtxos) {
4784
+ const usedUtxos = cache.get("UsedUtxos") || {};
4785
+ for (const txid in newUsedUtxos) {
4786
+ usedUtxos[txid] = 1;
4787
+ }
4788
+ cache.set("UsedUtxos", usedUtxos, false);
4789
+ }
4790
+ async function createPsbt({
4791
+ from,
4792
+ to,
4793
+ amount,
4794
+ fee,
4795
+ spendableUtxos
4796
+ }) {
4797
+ const utxos = spendableUtxos;
4798
+ if (!utxos || utxos.length === 0) {
4799
+ throw new Error("no spendable utxos");
4800
+ }
4801
+ const sendAmount = toSatoshi(amount);
4802
+ const sendCost = toSatoshi(fee);
4803
+ const totalNeeded = sendAmount + sendCost;
4804
+ const sortedUtxos = utxos.sort((a, b) => b.outputValue - a.outputValue);
4805
+ const selectedUtxos = [];
4806
+ let accumulatedAmount = 0;
4807
+ for (const utxo of sortedUtxos) {
4808
+ if (accumulatedAmount >= totalNeeded) break;
4809
+ selectedUtxos.push(utxo);
4810
+ accumulatedAmount += Number(utxo.outputValue);
4811
+ }
4812
+ if (accumulatedAmount < totalNeeded) {
4813
+ throw new Error("not enough funds to cover amount and fee");
4814
+ }
4815
+ const utxoDetailPromises = selectedUtxos.map(async (utxo) => {
4816
+ try {
4817
+ const utxoDetail = await getTxDetail(utxo.txid);
4818
+ return { utxo, utxoDetail };
4819
+ } catch (error) {
4820
+ return { utxo, utxoDetail: null };
4821
+ }
4822
+ });
4823
+ const utxoResults = await Promise.all(utxoDetailPromises);
4824
+ const inputs = [];
4825
+ const usingUtxos = {};
4826
+ let addedAmount = 0;
4827
+ for (const { utxo, utxoDetail } of utxoResults) {
4828
+ if (addedAmount >= totalNeeded) break;
4829
+ const { txid, vout, outputValue, address = from } = utxo;
4830
+ if (utxoDetail?.hex && !usingUtxos[txid]) {
4831
+ inputs.push({
4832
+ txId: txid,
4833
+ vOut: vout,
4834
+ amount: Number(outputValue),
4835
+ nonWitnessUtxo: utxoDetail.hex,
4836
+ address
4837
+ });
4838
+ usingUtxos[txid] = 1;
4839
+ addedAmount += Number(outputValue);
4840
+ }
4841
+ }
4842
+ if (addedAmount < totalNeeded) {
4843
+ throw new Error("not enough funds to cover amount and fee");
4844
+ }
4845
+ const outputs = [
4846
+ {
4847
+ address: to,
4848
+ amount: sendAmount
4849
+ }
4850
+ ];
4851
+ const changeAmount = addedAmount - sendAmount - sendCost;
4852
+ if (changeAmount > 0) {
4853
+ outputs.push({
4854
+ address: from,
4855
+ amount: changeAmount
4856
+ });
4857
+ }
4858
+ const params = {
4859
+ address: from,
4860
+ inputs,
4861
+ outputs
4862
+ };
4863
+ const psbtBase64 = DogecoinUtils.buildPsbtToBase64(params);
4864
+ return { psbtBase64, usingUtxos };
4865
+ }
4866
+ var mydoge = axios.create({
4867
+ baseURL: MYDOGE_BASE_URL
4868
+ });
4869
+ var api = axios.create({
4870
+ baseURL: RPC_URL,
4871
+ timeout: RPC_TIMEOUT
4872
+ });
4873
+ async function getBalance(address) {
4874
+ if (!address) {
4875
+ return {
4876
+ address: "",
4877
+ balance: 0
4878
+ };
4879
+ }
4880
+ const path = `/address/${address}?page=1&pageSize=10`;
4881
+ const api2 = `${MYDOGE_BASE_URL}/wallet/info?route=` + encodeURIComponent(path);
4882
+ const res = await mydoge.get(api2);
4883
+ return res.data || {};
4884
+ }
4885
+ async function getTxDetail(txId) {
4886
+ if (!txId) {
4887
+ return {};
4888
+ }
4889
+ const path = `/tx/${txId}`;
4890
+ const api2 = `${MYDOGE_BASE_URL}/wallet/info?route=` + encodeURIComponent(path);
4891
+ const res = await mydoge.get(api2);
4892
+ return res.data || {};
4893
+ }
4894
+ var Drc20DetailsCacheKey = "Drc20Details";
4895
+ cache.get(Drc20DetailsCacheKey) || {};
4896
+ async function getUtxos(address, cursor, result, filter, tx = null) {
4897
+ const query = (await mydoge.get(`${MYDOGE_BASE_URL}/utxos/${address}?filter=${filter}${cursor ? `&cursor=${cursor}` : ""}`)).data;
4898
+ let { utxos } = query;
4899
+ if (tx) {
4900
+ utxos = utxos.filter((utxo) => utxo.txid === tx?.txid && utxo.vout === tx?.vout);
4901
+ }
4902
+ result.push(
4903
+ ...utxos.map((i) => ({
4904
+ txid: i.txid,
4905
+ vout: i.vout,
4906
+ outputValue: i.satoshis,
4907
+ script: i.script_pubkey,
4908
+ ...filter === "inscriptions"
4909
+ }))
4910
+ );
4911
+ if (result.length && tx) {
4912
+ return;
4913
+ }
4914
+ result = result.sort((a, b) => toBitcoin(b.outputValue) - toBitcoin(a.outputValue));
4915
+ if (query.next_cursor) {
4916
+ return getUtxos(address, query.next_cursor, result, filter, tx);
4917
+ }
4918
+ }
4919
+ async function getSpendableUtxos(address) {
4920
+ const utxos = [];
4921
+ await getUtxos(address, 0, utxos, "spendable");
4922
+ return utxos;
4923
+ }
4924
+ var DuneDetailsCacheKey = "DuneDetails";
4925
+ cache.get(DuneDetailsCacheKey) || {};
4926
+ var sendDogeTx = async (rawTx) => {
4927
+ try {
4928
+ const res = await api.post("/tx/send", { rawTx });
4929
+ return res.data;
4930
+ } catch (e) {
4931
+ if (typeof e?.response?.data === "string") return Promise.reject(e?.response?.data);
4932
+ else return Promise.reject(e.message);
4933
+ }
4934
+ };
4935
+
4936
+ // src/dogecoin/service.ts
4937
+ var DogecoinService = class _DogecoinService extends BaseService {
4938
+ static instance;
4939
+ chainType;
4940
+ constructor(chainType, accountInfo, tomoAppInfo) {
4941
+ super(tomoAppInfo, accountInfo);
4942
+ this.chainType = chainType;
4943
+ }
4944
+ //singleton
4945
+ static getInstance(chainType, accountInfo, tomoAppInfo) {
4946
+ if (!_DogecoinService.instance) {
4947
+ _DogecoinService.instance = new _DogecoinService(chainType, accountInfo, tomoAppInfo);
4948
+ }
4949
+ return _DogecoinService.instance;
4950
+ }
4951
+ async requestAccounts() {
4952
+ const addresses = await this.accountInfo.getCurrent();
4953
+ const { publicKey, address } = addresses?.[0] || {};
4954
+ if (!address) {
4955
+ throw new Error("address is not set");
4956
+ }
4957
+ const { balance = 0 } = await getBalance(address);
4958
+ return {
4959
+ address,
4960
+ balance,
4961
+ approved: true,
4962
+ publicKey
4963
+ };
4964
+ }
4965
+ async getAccounts() {
4966
+ const accounts = await this.accountInfo.getCurrent();
4967
+ return accounts.map((account) => account.address);
4968
+ }
4969
+ async getConnectionStatus() {
4970
+ const addresses = await this.accountInfo.getCurrent();
4971
+ const { address } = addresses?.[0] || {};
4972
+ if (!address) {
4973
+ throw new Error("address is not set");
4974
+ }
4975
+ return {
4976
+ connected: true,
4977
+ address,
4978
+ selectedWalletAddress: address
4979
+ };
4980
+ }
4981
+ async getBalance() {
4982
+ const addresses = await this.accountInfo.getCurrent();
4983
+ const { address } = addresses?.[0] || {};
4984
+ if (!address) {
4985
+ throw new Error("address is not set");
4986
+ }
4987
+ const { balance = 0 } = await getBalance(address);
4988
+ return {
4989
+ address,
4990
+ balance
4991
+ };
4992
+ }
4993
+ async signMessage({ message, type }) {
4994
+ if (type) {
4995
+ throw new Error("bip322simple not support.");
4996
+ }
4997
+ const signature = await this.accountInfo.signMessage(message);
4998
+ return {
4999
+ signedMessage: signature
5000
+ };
5001
+ }
5002
+ async requestSignedMessage({
5003
+ message,
5004
+ type
5005
+ }) {
5006
+ return await this.signMessage({
5007
+ message,
5008
+ type
5009
+ });
5010
+ }
5011
+ async requestDecryptedMessage({ message }) {
5012
+ try {
5013
+ if (!this.accountInfo?.decryptedMessage) {
5014
+ throw new Error("decryptedMessage is not supported");
5015
+ }
5016
+ const unsignRes = await this.accountInfo?.decryptedMessage(message);
5017
+ const { text = "" } = JSON.parse(unsignRes);
5018
+ return {
5019
+ decryptedMessage: text
5020
+ };
5021
+ } catch (err) {
5022
+ throw new Error("requestDecryptedMessage err:" + JSON.stringify(err));
5023
+ }
5024
+ }
5025
+ async _queryGasInfo(txData) {
5026
+ const { chainId, amount = 0, decimals = 8 } = txData;
5027
+ const gasLimitParam = {
5028
+ from: txData.from,
5029
+ to: txData.to,
5030
+ value: toHex(parseUnits(amount.toString(), decimals))
5031
+ };
5032
+ const queryGasParams = {
5033
+ chainIndex: Number(chainId || BaseConfig2.chainId),
5034
+ callData: "0x",
5035
+ gasLimitParam,
5036
+ addressList: [txData.from]
5037
+ };
5038
+ const {
5039
+ data: gasInfo,
5040
+ success,
5041
+ message
5042
+ } = await this.transactions.queryGasInfo({
5043
+ chainType: this.chainType,
5044
+ params: queryGasParams
5045
+ });
5046
+ if (!success) {
5047
+ console.error("queryGasInfo doge", success, txData, queryGasParams, gasInfo);
5048
+ const { gasFee } = BaseConfig2;
5049
+ return {
5050
+ success: true,
5051
+ gasFee: gasFee.toString()
5052
+ };
5053
+ }
5054
+ const { baseFee = 1, gasLimit = 1 } = gasInfo;
5055
+ const calcFee = (feeLevel) => {
5056
+ const gasFee = new BigNumber(gasInfo[feeLevel] || baseFee).times(gasLimit);
5057
+ return toBitcoin(gasFee.toNumber());
5058
+ };
5059
+ const fees = {
5060
+ low: calcFee("priorityFeeLow"),
5061
+ medium: calcFee("priorityFeeMedium"),
5062
+ high: calcFee("priorityFeeHigh")
5063
+ };
5064
+ return {
5065
+ success: true,
5066
+ fees,
5067
+ gasFee: fees.high
5068
+ };
5069
+ }
5070
+ async requestPsbt({
5071
+ rawTx,
5072
+ signOnly
5073
+ }) {
5074
+ const psbtBase64 = await DogecoinUtils.rawTxToPsbtBase64(rawTx);
5075
+ const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
5076
+ if (!signedPsbt) {
5077
+ return null;
5078
+ }
5079
+ const signedRawTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
5080
+ if (signOnly) {
5081
+ return {
5082
+ signedRawTx
5083
+ };
5084
+ }
5085
+ const { txid = "" } = await sendDogeTx(signedRawTx);
5086
+ return {
5087
+ txId: txid,
5088
+ signedRawTx
5089
+ };
5090
+ }
5091
+ async requestTransaction(txData) {
5092
+ const spendableUtxos = txData?.spendableUtxos;
5093
+ if (txData.amountMismatch) {
5094
+ throw new Error("balance_insufficient");
5095
+ }
5096
+ if (!spendableUtxos || spendableUtxos?.length === 0) {
5097
+ txData.spendableUtxos = await getSpendableUtxos(txData.from);
5098
+ }
5099
+ const { psbtBase64, usingUtxos } = await createPsbt(txData);
5100
+ const signedPsbt = await this.accountInfo.signTransaction(JSON.stringify({ psbtBase64 }));
5101
+ const signedTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
5102
+ if (signedTx === "") {
5103
+ throw new Error("Error: sign transaction err.");
5104
+ }
5105
+ try {
5106
+ const { txid } = await sendDogeTx(signedTx);
5107
+ addUsedUtxos(usingUtxos);
5108
+ return { txId: txid };
5109
+ } catch (err) {
5110
+ throw new Error("Error: send transaction err." + JSON.stringify(err));
5111
+ }
5112
+ }
5113
+ async getTransactionStatus({ txId }) {
5114
+ const transaction = await getTxDetail(txId);
5115
+ if (!transaction?.txid) {
5116
+ return null;
5117
+ }
5118
+ return {
5119
+ txId: transaction.txid,
5120
+ confirmations: transaction.confirmations,
5121
+ status: transaction.confirmations > 0 ? "confirmed" : "pending",
5122
+ dogeAmount: transaction.vout[0]?.value,
5123
+ blockTime: transaction.blockTime,
5124
+ address: transaction.vout[0]?.addresses[0]
5125
+ };
5126
+ }
5127
+ };
4622
5128
  var TomoWallet = class _TomoWallet extends BaseService {
4623
5129
  static instance;
4624
5130
  walletId;
@@ -4638,8 +5144,8 @@ var TomoWallet = class _TomoWallet extends BaseService {
4638
5144
  }
4639
5145
  async getChainInfo(chainType, chainId) {
4640
5146
  this.networks.setChainType(chainType);
4641
- const network = await this.networks.getNetworkByChainId(chainId);
4642
- return network;
5147
+ const network2 = await this.networks.getNetworkByChainId(chainId);
5148
+ return network2;
4643
5149
  }
4644
5150
  //evm chains
4645
5151
  async isChainSupported(chainType, chainId) {
@@ -4706,8 +5212,8 @@ var TomoWallet = class _TomoWallet extends BaseService {
4706
5212
  // src/index.ts
4707
5213
  var ChainTypeServices = {
4708
5214
  [ChainTypes.EVM]: EvmService,
4709
- [ChainTypes.SOL]: SolanaService
4710
- // [ChainTypes.DOGE]: DogecoinService,
5215
+ [ChainTypes.SOL]: SolanaService,
5216
+ [ChainTypes.DOGE]: DogecoinService
4711
5217
  };
4712
5218
 
4713
- export { AccountType, ChainTypeServices, EvmService, SolanaService, TomoWallet, TxTypes };
5219
+ export { AccountType, ChainTypeServices, DogecoinService, EvmService, SolanaService, TomoWallet, TxTypes };