@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.cjs CHANGED
@@ -7,6 +7,7 @@ var axios = require('axios');
7
7
  var CryptoJS = require('crypto-js');
8
8
  var splToken = require('@solana/spl-token');
9
9
  var web3_js = require('@solana/web3.js');
10
+ var bitcoinjsLib = require('bitcoinjs-lib');
10
11
 
11
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
13
 
@@ -2804,18 +2805,18 @@ var Networks = class {
2804
2805
  return networks;
2805
2806
  }
2806
2807
  async getNetworkByChainId(chainId) {
2807
- const network = await this.networkAPIs.getNetworkByChainId(chainId);
2808
- if (!network) {
2808
+ const network2 = await this.networkAPIs.getNetworkByChainId(chainId);
2809
+ if (!network2) {
2809
2810
  throw new Error("Network not found");
2810
2811
  }
2811
- return network;
2812
+ return network2;
2812
2813
  }
2813
2814
  async getNetworkByChainIndex(chainIndex) {
2814
- const network = await this.networkAPIs.getNetworkByChainIndex(chainIndex);
2815
- if (!network) {
2815
+ const network2 = await this.networkAPIs.getNetworkByChainIndex(chainIndex);
2816
+ if (!network2) {
2816
2817
  throw new Error("Network not found");
2817
2818
  }
2818
- return network;
2819
+ return network2;
2819
2820
  }
2820
2821
  async getCurrentNetwork() {
2821
2822
  const chainType = this.chainType;
@@ -2838,8 +2839,8 @@ var Tokens = class {
2838
2839
  constructor(tokenService) {
2839
2840
  this.tokenAPIs = tokenService;
2840
2841
  }
2841
- async searchTokens({ chainIndex, query }) {
2842
- const { data: tokens = [] } = await this.tokenAPIs.queryRemoteTokens({ keyword: query || "", chainIndex });
2842
+ async searchTokens({ chainIndex, keyword }) {
2843
+ const { data: tokens = [] } = await this.tokenAPIs.queryRemoteTokens({ keyword, chainIndex });
2843
2844
  return tokens;
2844
2845
  }
2845
2846
  // public async addToken(tokenInfo: Omit<TokenInfo, "id">): Promise<TokenInfo> {
@@ -2847,10 +2848,8 @@ var Tokens = class {
2847
2848
  // return tokenInfoData;
2848
2849
  // }
2849
2850
  async getTokenInfo({ address, chainIndex }) {
2850
- const {
2851
- data: { data: tokenInfo }
2852
- } = await this.tokenAPIs.getTokenInfo({ address, chainIndex });
2853
- return tokenInfo;
2851
+ const res = await this.tokenAPIs.getTokenInfo({ address, chainIndex });
2852
+ return res?.data?.data;
2854
2853
  }
2855
2854
  async getTokenRiskInfo(_params) {
2856
2855
  const { data: tokenRiskInfo } = await this.tokenAPIs.getTokenRisk(_params);
@@ -2980,7 +2979,7 @@ var BasePublicService = class {
2980
2979
  this.apiBase = publicApiBase;
2981
2980
  this.tomoAppInfo = tomoAppInfo;
2982
2981
  [this.tokenApi, this.txApi, this.walletApi].map(
2983
- (api, i) => api.interceptors.request.use((params) => {
2982
+ (api2, i) => api2.interceptors.request.use((params) => {
2984
2983
  const { tokenBaseUrl, txBaseUrl, walletBaseUrl } = publicApiBase;
2985
2984
  params.baseURL = [tokenBaseUrl, txBaseUrl, walletBaseUrl][i];
2986
2985
  return signRequest(params, tomoAppInfo.tomoClientId, tomoAppInfo);
@@ -3021,10 +3020,15 @@ var TokenAPIs = class _TokenAPIs extends BasePublicService {
3021
3020
  }
3022
3021
  }
3023
3022
  async getTokenRisk(params) {
3024
- if (typeof params.chainIndex !== "number" || !params.tokenAddress) {
3023
+ if (typeof params.chainIndex !== "number" || !params.address) {
3025
3024
  throw new Error("chainName or tokenAddress is required");
3026
3025
  }
3027
- const res = await this.tokenApi.post("/v1/market/risk/details", [params]);
3026
+ const res = await this.tokenApi.post("/v1/market/risk/details", [
3027
+ {
3028
+ chainIndex: params.chainIndex,
3029
+ tokenAddress: params.address
3030
+ }
3031
+ ]);
3028
3032
  return {
3029
3033
  success: res?.data?.code === "0",
3030
3034
  message: res?.data?.msg,
@@ -3112,10 +3116,10 @@ var TokenAPIs = class _TokenAPIs extends BasePublicService {
3112
3116
  }
3113
3117
  }
3114
3118
  async getTokenDetail(params) {
3115
- const { chainIndex, tokenAddress } = params;
3119
+ const { chainIndex, address } = params;
3116
3120
  const res = await this.tokenApi.get(`/v1/market/token/detail`, {
3117
3121
  params: {
3118
- tokenAddress,
3122
+ tokenAddress: address,
3119
3123
  chainIndex
3120
3124
  }
3121
3125
  });
@@ -3267,12 +3271,26 @@ var loadNetworks = (WALLET_DOMAIN) => {
3267
3271
  supportHistory: false
3268
3272
  },
3269
3273
  {
3270
- chainId: 221122420,
3271
- chainIndex: 22112242e3,
3272
- name: "DOGEOS_DEVNET",
3273
- chainName: "DogeOS Devnet",
3274
+ chainId: 26888,
3275
+ chainIndex: 2688800,
3276
+ name: "ABCORE_TESTNET",
3277
+ chainName: "AB Core Testnet",
3278
+ rpcUrls: ["https://rpc.core.testnet.ab.org"],
3279
+ blockExplorerUrl: "https://explorer.core.testnet.ab.org",
3280
+ platformType: "EVM",
3281
+ isTestnet: true,
3282
+ icon: "/assets/ab.svg",
3283
+ supportSwap: true,
3284
+ supportGift: false,
3285
+ supportHistory: true
3286
+ },
3287
+ {
3288
+ chainId: 6281971,
3289
+ chainIndex: 628197100,
3290
+ name: "DOGEOS_TESTNET",
3291
+ chainName: "DogeOS Testnet",
3274
3292
  rpcUrls: [`${WALLET_DOMAIN}/rpc/v1/doge_test`],
3275
- blockExplorerUrl: "https://blockscout.devnet.doge.xyz",
3293
+ blockExplorerUrl: "https://rpc.testnet.dogeos.com",
3276
3294
  platformType: "EVM",
3277
3295
  isTestnet: true,
3278
3296
  icon: "/assets/dogeos.svg",
@@ -3894,7 +3912,7 @@ var BaseService = class {
3894
3912
  }
3895
3913
  const baseUrlConfig = CONFIG[tomoAppInfo.tomoStage];
3896
3914
  const { tokenAPIs, transactionAPIs, networkAPIs } = tomoPublicApiService(baseUrlConfig, tomoAppInfo);
3897
- this.accountInfo = accountInfo;
3915
+ this.accountInfo = accountInfo || null;
3898
3916
  this.networks = new Networks(networkAPIs);
3899
3917
  this.tokens = new Tokens(tokenAPIs);
3900
3918
  this.transactions = new Transactions(transactionAPIs);
@@ -3904,8 +3922,8 @@ var BaseService = class {
3904
3922
  this.approveParams = params;
3905
3923
  }
3906
3924
  };
3907
- function getRPCClient(network) {
3908
- const { chainId, name, rpcUrls, nativeCurrencyDecimals, nativeCurrencyName, nativeCurrencySymbol } = network;
3925
+ function getRPCClient(network2) {
3926
+ const { chainId, name, rpcUrls, nativeCurrencyDecimals, nativeCurrencyName, nativeCurrencySymbol } = network2;
3909
3927
  const myCustomChain = {
3910
3928
  id: Number(chainId) || chainId,
3911
3929
  name,
@@ -3960,8 +3978,8 @@ var TxTypes = /* @__PURE__ */ ((TxTypes2) => {
3960
3978
  })(TxTypes || {});
3961
3979
 
3962
3980
  // src/evm/utils.ts
3963
- var isEvmChain = (network) => {
3964
- return network && network?.platformType === "EVM";
3981
+ var isEvmChain = (network2) => {
3982
+ return network2 && network2?.platformType === "EVM";
3965
3983
  };
3966
3984
  var getAllTypeChainIds = ({ chainId, chainType }) => {
3967
3985
  if (viem.isHex(chainId)) {
@@ -4053,7 +4071,7 @@ var EvmService = class _EvmService extends BaseService {
4053
4071
  }
4054
4072
  async personal_sign([message, address]) {
4055
4073
  const accounts = await this.accountInfo.getCurrent();
4056
- if (accounts[0].address !== address) {
4074
+ if (!viem.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.signMessage(message);
@@ -4061,7 +4079,7 @@ var EvmService = class _EvmService extends BaseService {
4061
4079
  }
4062
4080
  async eth_signTypedData_v4([address, typedData]) {
4063
4081
  const accounts = await this.accountInfo.getCurrent();
4064
- if (accounts[0].address !== address) {
4082
+ if (!viem.isAddressEqual(accounts[0].address, address)) {
4065
4083
  throw new Error("address is not the current account");
4066
4084
  }
4067
4085
  const signature = await this.accountInfo.signTypedData(typedData);
@@ -4069,7 +4087,7 @@ var EvmService = class _EvmService extends BaseService {
4069
4087
  }
4070
4088
  async eth_getBalance([address, type]) {
4071
4089
  const accounts = await this.accountInfo.getCurrent();
4072
- if (accounts[0].address !== address) {
4090
+ if (!viem.isAddressEqual(accounts[0].address, address)) {
4073
4091
  throw new Error("address is not the current account");
4074
4092
  }
4075
4093
  if (type !== "latest") {
@@ -4113,8 +4131,8 @@ var EvmService = class _EvmService extends BaseService {
4113
4131
  });
4114
4132
  if (!success) {
4115
4133
  console.error("queryGasInfo evm", txData, queryGasParams, message, gasInfo);
4116
- const BaseConfig2 = walletUtils.SupportedChainTypes[walletUtils.ChainTypes.EVM];
4117
- const { gasFee } = BaseConfig2;
4134
+ const BaseConfig3 = walletUtils.SupportedChainTypes[walletUtils.ChainTypes.EVM];
4135
+ const { gasFee } = BaseConfig3;
4118
4136
  return {
4119
4137
  success: true,
4120
4138
  gasFee: gasFee.toString()
@@ -4146,51 +4164,6 @@ var EvmService = class _EvmService extends BaseService {
4146
4164
  }
4147
4165
  };
4148
4166
  }
4149
- async eth_estimateGas(txs) {
4150
- const { from, to, value = "0x1" } = txs[0] || {};
4151
- const accounts = await this.accountInfo.getCurrent();
4152
- if (accounts[0].address !== from) {
4153
- throw new Error("address is not the current account");
4154
- }
4155
- if (!to) {
4156
- throw new Error("to is not set");
4157
- }
4158
- const chainId = txs[0]?.chainId || await this.eth_chainId();
4159
- const queryGasParams = {
4160
- chainIndex: Number(chainId),
4161
- callData: "0x",
4162
- gasLimitParam: {
4163
- from,
4164
- to,
4165
- value
4166
- },
4167
- addressList: [from]
4168
- };
4169
- const chainType = this.chainType;
4170
- const {
4171
- data: gasInfo,
4172
- success,
4173
- message
4174
- } = await this.transactions.queryGasInfo({
4175
- chainType,
4176
- params: queryGasParams
4177
- });
4178
- if (!success) {
4179
- console.error("queryGasInfo evm", txs, message, gasInfo);
4180
- const { gasFee } = walletUtils.SupportedChainTypes[chainType];
4181
- return gasFee.toString();
4182
- }
4183
- const res = getAllTypeChainIds({ chainId, chainType });
4184
- const { nativeCurrencyDecimals } = await this.networks.getNetworkByChainId(res.chainId);
4185
- const { baseFee = 1, gasLimit = 0 } = gasInfo;
4186
- const baseFeeBN = new Bignumber.BigNumber(baseFee);
4187
- const calcFee = (feeLevel) => {
4188
- const fee = baseFeeBN.plus(gasInfo[feeLevel] || 0);
4189
- const gasFee = fee.times(gasLimit);
4190
- return viem.toHex(BigInt(gasFee.toNumber()), nativeCurrencyDecimals);
4191
- };
4192
- return calcFee("priorityFeeMedium");
4193
- }
4194
4167
  async createPublicClient({ chainId, rpcUrl }) {
4195
4168
  if (rpcUrl) {
4196
4169
  return viem.createPublicClient({
@@ -4212,6 +4185,26 @@ var EvmService = class _EvmService extends BaseService {
4212
4185
  transport: viem.http(rpcUrl)
4213
4186
  });
4214
4187
  }
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
+ }
4207
+ }
4215
4208
  // Get nonce for current account
4216
4209
  async eth_getTransactionCount([address, type]) {
4217
4210
  const accounts = await this.accountInfo.getCurrent();
@@ -4240,30 +4233,35 @@ var EvmService = class _EvmService extends BaseService {
4240
4233
  nonce: "number",
4241
4234
  maxPriorityFeePerGas: "bigint",
4242
4235
  maxFeePerGas: "bigint",
4243
- gasLimit: "bigint",
4236
+ gasPrice: "bigint",
4244
4237
  gas: "bigint"
4245
4238
  };
4246
- let txData = txParams?.[0];
4247
- const gas = txData?.gas || txData?.gasLimit;
4248
- txData = { ...txData, gas, gasLimit: gas };
4239
+ const txData = txParams?.[0];
4240
+ txData.gas = txData?.gas || txData?.gasLimit;
4249
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
+ }
4250
4247
  const accounts = await this.accountInfo.getCurrent();
4251
4248
  const address = accounts[0].address;
4252
- if (txData?.from && txData?.from !== address) {
4249
+ if (txData?.from && !viem.isAddressEqual(txData?.from, address)) {
4253
4250
  console.error("eth_signTransaction error data: from is not the current account", txData);
4254
4251
  throw new Error(`eth_signTransaction error data: from is not the current account`);
4255
4252
  }
4256
4253
  delete txData?.from;
4254
+ delete txData?.gasLimit;
4257
4255
  const preparedTx = {
4258
- type: "eip1559",
4259
- gas: txData?.gasLimit,
4260
- account: address
4256
+ gas: txData?.gas
4261
4257
  };
4262
4258
  for (const key in preparedTxPropsType) {
4263
- if (!txData?.[key] || !viem.isHex(txData?.[key])) {
4264
- throw new Error(`${key}: no data or wrong type`);
4259
+ if (txData?.[key] && viem.isHex(txData?.[key])) {
4260
+ preparedTx[key] = preparedTxPropsType[key] === "number" ? viem.fromHex(txData[key], "number") : txData[key];
4265
4261
  }
4266
- preparedTx[key] = preparedTxPropsType[key] === "number" ? viem.fromHex(txData[key], "number") : txData[key];
4262
+ }
4263
+ if (txData?.type !== "legacy") {
4264
+ delete preparedTx.gasPrice;
4267
4265
  }
4268
4266
  try {
4269
4267
  const serializedTransaction = await this.accountInfo.signTransaction(
@@ -4386,6 +4384,34 @@ async function createTokenLegacyTransaction2({
4386
4384
  }
4387
4385
 
4388
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
+ }
4389
4415
  function toBase58(data) {
4390
4416
  throw new Error("toBase58 requires bs58 package. Install it: pnpm add bs58");
4391
4417
  }
@@ -4422,11 +4448,11 @@ var SolanaService = class _SolanaService extends BaseService {
4422
4448
  return await this.accountInfo.getCurrent();
4423
4449
  }
4424
4450
  async getAccount() {
4425
- const { currentAddress, publicKey } = await this.accountInfo.getIds();
4451
+ const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
4426
4452
  return { publicKey: publicKey || currentAddress, address: currentAddress };
4427
4453
  }
4428
4454
  async signMessage(params) {
4429
- const { currentAddress, publicKey } = await this.accountInfo.getIds();
4455
+ const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
4430
4456
  const { message } = params;
4431
4457
  const signature = await this.accountInfo.signMessage(message);
4432
4458
  return {
@@ -4436,7 +4462,7 @@ var SolanaService = class _SolanaService extends BaseService {
4436
4462
  };
4437
4463
  }
4438
4464
  async signIn(params) {
4439
- const { currentAddress, publicKey } = await this.accountInfo.getIds();
4465
+ const { address: currentAddress, publicKey } = await this.accountInfo.getCurrent();
4440
4466
  const { signature = "" } = await this.signMessage(params);
4441
4467
  return {
4442
4468
  address: currentAddress,
@@ -4627,6 +4653,486 @@ var SolanaService = class _SolanaService extends BaseService {
4627
4653
  }
4628
4654
  }
4629
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);
4693
+ }
4694
+ for (const output of tx.outputs) {
4695
+ psbt.addOutput({
4696
+ value: output.amount,
4697
+ address: output.address
4698
+ });
4699
+ }
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
+ 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));
4760
+ } 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
+ );
4765
+ }
4766
+ }
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");
4774
+ }
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");
4785
+ }
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
+ try {
4825
+ const utxoDetail = await getTxDetail(utxo.txid);
4826
+ return { utxo, utxoDetail };
4827
+ } catch (error) {
4828
+ return { utxo, utxoDetail: null };
4829
+ }
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
4845
+ });
4846
+ usingUtxos[txid] = 1;
4847
+ addedAmount += Number(outputValue);
4848
+ }
4849
+ }
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
4857
+ }
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
+ }
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 {};
4896
+ }
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);
4909
+ }
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
+ }))
4918
+ );
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;
4931
+ }
4932
+ var DuneDetailsCacheKey = "DuneDetails";
4933
+ walletUtils.cache.get(DuneDetailsCacheKey) || {};
4934
+ var sendDogeTx = async (rawTx) => {
4935
+ 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);
4941
+ }
4942
+ };
4943
+
4944
+ // src/dogecoin/service.ts
4945
+ var DogecoinService = class _DogecoinService extends BaseService {
4946
+ static instance;
4947
+ chainType;
4948
+ constructor(chainType, accountInfo, tomoAppInfo) {
4949
+ super(tomoAppInfo, accountInfo);
4950
+ 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
4987
+ };
4988
+ }
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");
4994
+ }
4995
+ const { balance = 0 } = await getBalance(address);
4996
+ return {
4997
+ address,
4998
+ balance
4999
+ };
5000
+ }
5001
+ async signMessage({ message, type }) {
5002
+ if (type) {
5003
+ throw new Error("bip322simple not support.");
5004
+ }
5005
+ const signature = await this.accountInfo.signMessage(message);
5006
+ return {
5007
+ signedMessage: signature
5008
+ };
5009
+ }
5010
+ async requestSignedMessage({
5011
+ message,
5012
+ type
5013
+ }) {
5014
+ return await this.signMessage({
5015
+ message,
5016
+ type
5017
+ });
5018
+ }
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));
5031
+ }
5032
+ }
5033
+ async _queryGasInfo(txData) {
5034
+ const { chainId, amount = 0, decimals = 8 } = txData;
5035
+ const gasLimitParam = {
5036
+ from: txData.from,
5037
+ to: txData.to,
5038
+ value: viem.toHex(viem.parseUnits(amount.toString(), decimals))
5039
+ };
5040
+ const queryGasParams = {
5041
+ chainIndex: Number(chainId || BaseConfig2.chainId),
5042
+ callData: "0x",
5043
+ gasLimitParam,
5044
+ addressList: [txData.from]
5045
+ };
5046
+ const {
5047
+ data: gasInfo,
5048
+ success,
5049
+ message
5050
+ } = await this.transactions.queryGasInfo({
5051
+ chainType: this.chainType,
5052
+ params: queryGasParams
5053
+ });
5054
+ if (!success) {
5055
+ console.error("queryGasInfo doge", success, txData, queryGasParams, gasInfo);
5056
+ const { gasFee } = BaseConfig2;
5057
+ return {
5058
+ success: true,
5059
+ gasFee: gasFee.toString()
5060
+ };
5061
+ }
5062
+ const { baseFee = 1, gasLimit = 1 } = gasInfo;
5063
+ 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")
5071
+ };
5072
+ return {
5073
+ success: true,
5074
+ fees,
5075
+ gasFee: fees.high
5076
+ };
5077
+ }
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;
5086
+ }
5087
+ const signedRawTx = DogecoinUtils.extractPsbtTransaction(signedPsbt, 1e5);
5088
+ if (signOnly) {
5089
+ return {
5090
+ signedRawTx
5091
+ };
5092
+ }
5093
+ const { txid = "" } = await sendDogeTx(signedRawTx);
5094
+ return {
5095
+ txId: txid,
5096
+ signedRawTx
5097
+ };
5098
+ }
5099
+ async requestTransaction(txData) {
5100
+ const spendableUtxos = txData?.spendableUtxos;
5101
+ if (txData.amountMismatch) {
5102
+ throw new Error("balance_insufficient");
5103
+ }
5104
+ if (!spendableUtxos || spendableUtxos?.length === 0) {
5105
+ txData.spendableUtxos = await getSpendableUtxos(txData.from);
5106
+ }
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.");
5112
+ }
5113
+ 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));
5119
+ }
5120
+ }
5121
+ async getTransactionStatus({ txId }) {
5122
+ const transaction = await getTxDetail(txId);
5123
+ if (!transaction?.txid) {
5124
+ return null;
5125
+ }
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
+ }
5135
+ };
4630
5136
  var TomoWallet = class _TomoWallet extends BaseService {
4631
5137
  static instance;
4632
5138
  walletId;
@@ -4646,8 +5152,8 @@ var TomoWallet = class _TomoWallet extends BaseService {
4646
5152
  }
4647
5153
  async getChainInfo(chainType, chainId) {
4648
5154
  this.networks.setChainType(chainType);
4649
- const network = await this.networks.getNetworkByChainId(chainId);
4650
- return network;
5155
+ const network2 = await this.networks.getNetworkByChainId(chainId);
5156
+ return network2;
4651
5157
  }
4652
5158
  //evm chains
4653
5159
  async isChainSupported(chainType, chainId) {
@@ -4714,12 +5220,13 @@ var TomoWallet = class _TomoWallet extends BaseService {
4714
5220
  // src/index.ts
4715
5221
  var ChainTypeServices = {
4716
5222
  [walletUtils.ChainTypes.EVM]: EvmService,
4717
- [walletUtils.ChainTypes.SOL]: SolanaService
4718
- // [ChainTypes.DOGE]: DogecoinService,
5223
+ [walletUtils.ChainTypes.SOL]: SolanaService,
5224
+ [walletUtils.ChainTypes.DOGE]: DogecoinService
4719
5225
  };
4720
5226
 
4721
5227
  exports.AccountType = AccountType;
4722
5228
  exports.ChainTypeServices = ChainTypeServices;
5229
+ exports.DogecoinService = DogecoinService;
4723
5230
  exports.EvmService = EvmService;
4724
5231
  exports.SolanaService = SolanaService;
4725
5232
  exports.TomoWallet = TomoWallet;