bnb-chain / javascript-sdk

Javascript SDK to communicate with BNB Beacon Chain.
Apache License 2.0
381 stars 240 forks source link

setPrivateKey called fails with messageless error when account is new #217

Closed CherryDT closed 4 years ago

CherryDT commented 5 years ago
const privateKey = BncClient.crypto.generatePrivateKey()
const client = new BncClient(API_URL)
await client.setPrivateKey(privateKey)

This code will throw an error Error with no message.

Two issues with this:

umanggoyal89 commented 5 years ago

I am also facing this same error when trying to send BNB using Transfer function and setPrivateKey. It just returns Error from request.js with code undefined. Were you able to find any solution to overcome this issue?

CherryDT commented 5 years ago

Well regarding the issue that you can't set the private key in the first place, what I did was just check the balance first (with address parameter - which works without private key), and if it was zero, I'd not attempt the transfer in the first place. Otherwise I'd attempt to set the private key at that point only, not initially.

Regarding the swallowed error, you could of course monkey-patch request.js as follows:

  const { default: HttpRequest } = require('@binance-chain/javascript-sdk/lib/utils/request')

  HttpRequest.prototype.request = function (method, path, params, opts) {
    const options = {
      method,
      url: path,
      ...opts
    }

    if (params) {
      if (method === "get") {
        options.params = params
      } else {
        options.data = params
      }
    }

    return this.httpClient
      .request(options)
      .then(response => {
        return { result: response.data, status: response.status }
      }).catch(err => {
        let error = err
        try {
          const msgObj = err.response && err.response.data
          if (!msgObj.message) throw new Error() // Just to jump into the "catch" that rethrows the original error
          error = new Error(msgObj.message)
          error.code = msgObj.code
        } catch (err) {
          throw error
        }
        throw error
      })
  }
umanggoyal89 commented 5 years ago

Thanks for you reply, although it seems it started to work after a few hours. Although i have another question but not sure if it's the correct place to ask, after getting transaction hash, when i query transaction details using the RPC endpoint tx it give me output but i am not sure how i can extract the parameters like To address, From address, Amount, Asset, Fee etc. I read in the documentation that it is encoded in amino but i did not see any option to decode this, do you have any idea about this?

here is how the output looks like for a transaction i fetched:

"result": { "hash": "B586F2A5EF641C11989A0ED2D42BDA30617FC4A558E9C442061904326E1DCA34", "height": "42373356", "index": 0, "tx_result": { "log": "Msg 0: ", "tags": [ { "key": "c2VuZGVy", "value": "dGJuYjE5d216a2x2c3ZzZmphZWRoazY0aHlmOGZmM2hqMHByZ3hjY2VqaA==" }, { "key": "cmVjaXBpZW50", "value": "dGJuYjFzNDY4aDN3dHBxcTdreGt2NjRhM2dubnhjNmF5d2ptZDY2ZGpkZg==" }, { "key": "YWN0aW9u", "value": "c2VuZA==" } ] }

CherryDT commented 5 years ago

The reason it started working is because you received money.

You will always get this error when using setPrivateKey with an address that was never used (= received money) before!

For the transaction, add ?format=json, as explained here https://docs.binance.org/api-reference/dex-api/paths.html#apiv1txhash

Getting the fee is a bit harder: You need to first get the block number from the TX endpoint, then you need to use transactions-in-block, which returns the transactions in a different format that includes the fee, and look for the transaction there by its ID. Note that there is a few seconds delay - I noticed then when I successfully submit a transaction, only 1-2 seconds later I get the TX back from the tx endpoint (before, you'll get a 404), and only 1-2 more seconds afterwards I get a result from transactions-in-block (before, you'll get a 400), so you may need to do a retry logic there in case you are too early.

Alternative: The official block explorer seems to use an internal endpoint https://explorer.binance.org/api/v1/tx?txHash=<hash> which also directly returns the fee, but that's undocumented so I don't know if it will stay and what sort of rate limit there is. It may be OK to call this a few times a day but I wouldn't rely on it, it's not meant for public use.

umanggoyal89 commented 5 years ago

@CherryDT Thank you so much for your quick response, you saved my day! I am actually trying to create a deposit and withdraw functionality for my Dapp and i am able to withdraw and getting the properties from the transaction now using your above response. It's just that now i need to know when someone has deposited any asset into any of the addresses i have. I think for that purpose i need to get the block height and then run a [transactions-in-block](https://docs.binance.org/api-reference/dex-api/paths.html#apiv1transactions-in-blockblockheight) so that i can get all the transactions and then loop through them to check each of the transactions if it has my addresses in the Output data. Unless there is another way for subscribing to multiple addresses at once like ETH has filters for looking into the blocks only with certain addresses... I really appreciate all your help sir!

CherryDT commented 5 years ago

Hm I think you could save the previous balance and known height and then only check for transactions once the balance changes (you can easily check the balance of an address).

I also needed deposit and withdraw but for me the use case is a bit different, I needed it to work like a payment gateway, so what I do is I create a new temporary private key when a user initiates payment and tell the user to deposit to that key's address. While the payment is pending, I keep checking that address' balance, and once I received enough money, the payment counts as successful and I transfer the money to the main account and no longer use that temporary private key. (Of course I keep it for future use in case someone accidentally sends another payment to that address, but I no longer monitor the balance.) - And then of course I have a procedure in place how to handle partial payments that are not finished up within a certain time, overpayments, etc., but that's beyond this now.

umanggoyal89 commented 5 years ago

Thanks, that is the same process i am following, generating a temporary address for my user to deposit. For transferring it to main account and checking the transaction, i stored a block number which i had checked and then i am calling the Node-info endpoint to get the latest block height, i check the difference between latest block and the last stored block and then i call transactions-in-block to get all the transaction one by one from all the blocks between the last stored block and the latest block, after that i loop through all the found transactions and check if any of the transactions have To Address matching my temporary address and if yes then i call transfer function again to move funds onto main account. I couldn't see any other way to achieve this, even the function to get the latest block height i have to use setInterval which checked every 3 minutes for latest blocks. WebSockets are pretty handy for these sort of operations but it seems websockets are of no use for this scenario. Anyway thanks for all your help! All the best for your project.