bnb-chain / javascript-sdk

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

Using multiSend with ledger #174

Closed cbarraford closed 5 years ago

cbarraford commented 5 years ago

The api docs doesn't give a good example of how to use multisend with ledger.

I gather you need to init the client with useLedgerSigningDelegate, but when i try to run multiSend after that i get the following error

Failed to sign with Ledger device: U2F TIMEOUT

Im not getting a prompt on my ledger to sign the request, so I wonder if theres a step i'm mising. Thanks in advance! And thanks for this great js package!

notatestuser commented 5 years ago

Hi. Did you create the Ledger transport first? Here is an example

cbarraford commented 5 years ago

@notatestuser i believe so, here is my code for that part...

    // use the u2f transport
    const timeout = 10000
    const transport = await ledger.transports.u2f.create(timeout)
    const app = new ledger.app(transport)
notatestuser commented 5 years ago

Would you mind posting the full snippet of code?

I would take it through standard debugging steps:

cbarraford commented 5 years ago

@notatestuser I did notice that ledger.transports.u2f would have a value of false by default, so I imported @ledgerhq/hw-transport-u2f manually and set u2f to the import.

here is a more complete snippet...

import { ledger, crypto } from '@binance-chain/javascript-sdk'
import Transport from '@ledgerhq/hw-transport-u2f'

ledger.transports.u2f = Transport

const ledgerConnect = async () => {

    // use the u2f transport
    const timeout = 50000
    const transport = await ledger.transports.u2f.create(timeout)
    const app = new ledger.app(transport)

    // do some things! (firmware app must be open now)

    // get version
    try {
      const version = await app.getVersion()
      console.log("version", version)
    } catch ({ message, statusCode }) {
      console.error("version error", message, statusCode)
    }

    // we can provide the hd path (app checks first two parts are same as below)
    const hdPath = [44, 714, 0, 0, 0]

    // get public key
    let pk
    try {
      pk = (await app.getPublicKey(hdPath)).pk
      // get address from pubkey
      const address = crypto.getAddressFromPublicKey(pk)
      console.log("address", address)
    } catch ({ message, statusCode }) {
      console.error("pk error", message, statusCode)
    }

    //txMsg can be a string, as it is passed to Buffer.from(txMsg) - default encoding is `utf8`
    const txMsg = `{"account_number":1,"chain_id":"bnbchain","data":null,"memo":"memo","msgs":["msg"],"sequence":1,"source":1}`

    // sign a tx
    try {
      const signature = (await app.sign(txMsg, hdPath)).signature
      console.log("signature", signature)
    } catch ({ message, statusCode }) {
      console.error("sign error", message, statusCode)
    }
}

This code above gives the following error

Ledger signSecp256k1 error (chunk 1): Data is invalid Ledger device: UNKNOWN_ERROR (0x6984) UNKNOWN_ERROR Error

To answer your questions...

1) I am able to get version number ok.

device_locked: false
error_message: "No errors"
major: 1
minor: 1
patch: 4
return_code: 36864
test_mode: false

2) I increased those timeout with no effect. Never prompted to sign on my ledger, and I get the error pretty fast (within a second or two).

3) Haven't tried a regular transfer yet, but i doubt the multisend is too large considering its just a coin going to two addresses.

notatestuser commented 5 years ago

Hmm, not quite sure what's happening here. Would you mind trying with a different tx - perhaps one that isn't using a string in place of a message object? Something like:

{"account_number":"12","chain_id":"bnbchain","data":null,"memo":"smiley!☺","msgs":[{"id":"BA36F0FAD74D8F41045463E4774F328F4AF779E5-4","ordertype":2,"price":1612345678,"quantity":123456,"sender":"bnc1hgm0p7khfk85zpz5v0j8wnej3a90w7098fpxyh","side":1,"symbol":"NNB-338_BNB","timeinforce":3}],"sequence":"3","source":"1"}
cbarraford commented 5 years ago

will do. But I am stepping back from this development for a few weeks. Get back you in a month-ish. Thanks for the tip!

cbarraford commented 5 years ago

@notatestuser tried using your msg string, got the same error (as seen below). Will continue tinkering...

TransportStatusError (status code 27012): Ledger device: UNKNOWN_ERROR (0x6984)

abelliumnt commented 5 years ago

@cbarraford Did you have made it with other transaction types? Or your problems only happen on multisend transaction?

cbarraford commented 5 years ago

@HaoyangLiu i can't sign anything (not just multisend). Its seeming like a transport issue at this time and not relating to the code in this repo...

abelliumnt commented 5 years ago

Before sign the transaction, you need to call ShowAddressSECP256K1. Thus ledger can decide use which private key to sign.

cbarraford commented 5 years ago

@HaoyangLiu thanks, do you have example code somewhere? The example code wiki in this repo doesn't say anything about needing ShowAddressSECP256K1

cbarraford commented 5 years ago

heres my source code. https://gitlab.com/canyacoin/binancechain/binancetools/blob/ledger-support/src/components/pages/Wallet/Ledger.js#L18-L86

abelliumnt commented 5 years ago

Maybe you should call this before signing transaction: https://github.com/binance-chain/javascript-sdk/blob/4e32be6d263b869622e5384133a3a6db1b3ff9de/src/ledger/ledger-app.js#L507 Maybe the example wiki just missed this.

cbarraford commented 5 years ago

@HaoyangLiu thanks, that solved the problem. I'll open a PR/etc to fix the wiki example. Thanks again.