XRPLF / xrpl.js

A JavaScript/TypeScript API for interacting with the XRP Ledger in Node.js and the browser
https://xrpl.org/
1.2k stars 510 forks source link

Same Sequence Problem #1792

Closed junha-ahn closed 2 years ago

junha-ahn commented 2 years ago

rippled node version 1.7.3

Hi, I have some problem with "XRP sending"

I think It's not a bug or something, It might be my mistake. so plz tell me some tips to do

async function multiSignPayment(paymentObject) {
  const { from, to, amount, tag } = paymentObject
  const defaultFees = 30
  const recommendedFees = (await rippleAPI.getFee()) * 1e6
  const multiSignPayment = {
    TransactionType: 'Payment',
    Account: from,
    Destination: to,
    Amount: amount,
    Fee: Math.min(Math.max(recommendedFees, defaultFees), 420000).toString() // 0.00003 XRP max: 0.42 XRP
  }

  // Force the TX to fail after 25 blocks instead of 3 (slow connections / IO /...)
  const instructions = {
    maxLedgerVersionOffset: 25
  }

  if (tag) multiSignPayment.DestinationTag = Number(tag)

  const prepared = await rippleAPI.prepareTransaction(
    multiSignPayment,
    instructions
  )

  // * Get Account & Secrets
  const signers = await Secrets.getSigners()
  const signedTransactions = signers.map(({ address, account, secret }) => {
    const addr = address || account
    return rippleAPI.sign(prepared.txJSON, secret, { signAs: addr })
      .signedTransaction
  })
  const combinedTx = rippleAPI.combine(signedTransactions)

  const result = await rippleAPI.submit(combinedTx.signedTransaction)
  const ledger = await rippleAPI.getLedger()

  console.log(result) // my log point

  return { result, ledger }
}
2021-11-10T00:53:51.829Z {"resultCode":"telINSUF_FEE_P","Fee":"532","LastLedgerSequence":67598148,"Sequence":xxx,"hash":"...."}
2021-11-10T00:53:54.979Z {"resultCode":"telINSUF_FEE_P","Fee":"1039","LastLedgerSequence":67598149,"Sequence":xxx,"hash":"..."}
2021-11-10T00:54:18.262Z {"resultCode":"telINSUF_FEE_P","Fee":"30421","LastLedgerSequence":67598156,"Sequence":xxx,"hash":"..."}
2021-11-10T00:54:21.410Z {"resultCode":"telINSUF_FEE_P","Fee":"16042","LastLedgerSequence":67598157,"Sequence":xxx,"hash":"..."}
2021-11-10T00:56:14.362Z {"resultCode":"telINSUF_FEE_P","Fee":"420000","LastLedgerSequence":67598184,"Sequence":xxx,"hash":"..."}
2021-11-10T01:01:17.263Z {"resultCode":"telINSUF_FEE_P","Fee":"420000","LastLedgerSequence":67598260,"Sequence":xxx,"hash":"..."}
2021-11-10T01:02:56.918Z {"resultCode":"telINSUF_FEE_P","Fee":"30","LastLedgerSequence":67598284,"Sequence":xxx,"hash":"..."}
2021-11-10T01:03:00.066Z {"resultCode":"telINSUF_FEE_P","Fee":"30","LastLedgerSequence":67598285,"Sequence":xxx,"hash":"..."}

If you look at my log, The transactions have the same Sequence (xxx)

And only the last transaction was successful.

Time is not even close. 00:53 ~ 1:03 (during 10 minutes)

I think the transaction fee is too low so all Transaction just in Queue

so My first question is "How to calculate exact FEE, and fast FEE" so my transaction will Success very fast


and I think I can specify a specific sequence. (sequence filed In the instruction obj)

And that's not a good idea, because if the "A transaction" fails, the sequence of other transactions executed after "A transaction" must be corrected.

So what can I do I want to know how can I fix this problem


Summary

  1. How can I calculate a fast transaction fee
  2. Is there an average transaction fee site? (like eth gas station), so I can change my max transaction fee
  3. How can I avoid the same sequence (with SDK)

Thank you for your help.

mvadari commented 2 years ago

The fee for multisigning is a little different: https://xrpl.org/multi-signing.html

The prepareTransaction method will actually calculate the correct fee for you, as long as you have signersCount (the number of total signers) in the instructions - so you don't need to do it separately yourself.

junha-ahn commented 2 years ago

"The prepareTransaction method will actually calculate the correct fee for you"

But I think the getFee() function can't calculate the correct fee now.

Look at the log above the submit() function return to me "telINSUF_FEE_P"

And There is a same sequence function so I think prepareTransaction() is same for getFee() function ....

Is there any other way to recommend?

mvadari commented 2 years ago

getFee() only returns the fee for a standard transaction, so it won't work for a multisigned transaction.

Not sure what you mean by a "same sequence function" - I don't quite follow what the problem you're having is there.

junha-ahn commented 2 years ago

thank you

"getFee() only returns the fee for a standard transaction, so it won't work for a multisigned transaction."

I have two signatures (in multi sign address)

so If the "standard fee"(getFee() result) is a "1 XRP", I have to set "3 XRP" for my multisigned transaction


And Sorry, My mistake.

Look at my log, there are 8 transactions (log) but the sequence is all same.

mvadari commented 2 years ago

I would just let prepareTransaction calculate the fee for you (which it will do if you leave the Fee field blank). It will automatically do the math for you (the normal fee is not 1 XRP, it's ~10 drops of XRP, which is about .00001 XRP).

The sequence is the same because your transaction failed. The sequence only increments with a successful transaction.

junha-ahn commented 2 years ago

Ah, I understand now!!!!

Thank you and I will change my logic

  1. I just use prepareTransaction
  2. And I will set The MaxFee field in instructions Because I am afraid that an unexpected amount of fee will be paid. (Is it work?)

(My last question) But Is it ok "sign and combine" after prepareTransaction()

Because I think, We don't know how many signatures do we need, It just automatically calculate after combine?

mvadari commented 2 years ago

Re 2: the default MaxFee is 2 XRP, so you shouldn't be able to pay more than that. You can set it to a different global value when you initialize the RippleAPI. But you can also set a different MaxFee for this specific transaction.

You'll need to know the number of signatures at the time of prepareTransaction, for the fee calculation. You'll definitely need to know the number of signatures at the time of each sign, if you want to re-calculate the fee by hand just before signing.

If you're referring to the signer quorum, the transaction will fail if you don't have enough signatures. You can also find out what the quorum is with an account_info request.

junha-ahn commented 2 years ago

Ok Thank you for sharing about default MaxFee

"You'll need to know the number of signatures at the time of prepareTransaction"

I know the number of signatures in my account

so, for calculate fee

what I need to do in prepareTransaction() argument, Do I need to fill the "Signers" filed?

Is there a example for multisign transaction with ripplejs?

mvadari commented 2 years ago

You can pass a signersCount into prepareTransaction

The way that multisigning works with the library is that you sign each transaction individually, and then you combine them into one transaction using combine. When preparing each individual transaction, put the total number of signers in the instructions.

junha-ahn commented 2 years ago

Thank you! All your help!!! What an amazing!


async function multiSignPayment(paymentObject) {
  const { from, to, amount, tag } = paymentObject

  const multiSignPayment = {
    TransactionType: 'Payment',
    Account: from,
    Destination: to,
    Amount: amount,
    // no Fee filed
  }

  const instructions = {
    maxLedgerVersionOffset: 25,
    signersCount: 2, // Added
  }

  if (tag) multiSignPayment.DestinationTag = Number(tag)

  const prepared = await rippleAPI.prepareTransaction(
    multiSignPayment,
    instructions
  )

 // The bottom is all the same.

  const signers = await Secrets.getSigners()
  const signedTransactions = signers.map(({ address, account, secret }) => {
    const addr = address || account
    return rippleAPI.sign(prepared.txJSON, secret, { signAs: addr })
      .signedTransaction
  })
  const combinedTx = rippleAPI.combine(signedTransactions)

 return await rippleAPI.submit(combinedTx.signedTransaction)
}