Zondax ledger-icp hardware wallet (Internet Computer ledger app)

I tried to sign custom calls to my canisters with ledger-icp and got this error

Error: A ledger error happened during signature:
Code: 27012
Message: "Data is invalid : Unexpected data type"

As far as I understand, it only works with predefined transactions hardcoded inside the ledger app. This means trying to sign your own requests will result in error and IC developers can’t use it at all for their own canisters.

As far as I can tell, these are the allowed transactions (NNS ICP & Neurons) ledger-icp/parser_txdef.h at 13b3d07a62cf3767f46b71c5e3788bda84ea122a · Zondax/ledger-icp · GitHub

Can someone confirm if that’s correct ? Perhaps there is some flag or a hidden feature?
If only ICP & Neuron transactions are allowed, then why?

In this file, nns-dapp/Service.ts at 55a89a9b5d5d4241277822197efc62f3d3e8d6ce · dfinity/nns-dapp · GitHub
requests are sent in an unusual way. It also uses send_pb - protobuf and not candid.
There is a demo in the repo, it also uses protobuf.

Perhaps the ledger app accepts only protobuf, which means only rust canisters can communicate with it?

I actually faced the same issue 27012 while rewriting the ledger for nns-dapp in svelte yesterday.
My colleague @lmuntaner ultimately found out that the function that was transforming the request was not yet moved to my rewrite.

Everything is now solved and my understanding of the issue is that the request has to be formatted differently if it uses the standard identity or the ledger identity - i.e. when it goes through the ledger

Here the differences:

Notice how both returns differ a bit.

Hope this helps, at least it solved my issue :smile:

2 Likes

So are you now able to sign arbitrary canister requests with a ledger device?

No idea.

I re-implemented this week in Svelte the nns-dapp feature for the accounts (“attach new hardware wallet”, “transfer icp from a hardware wallet” and “display principal on hardware wallet”). That’s why I faced above error.

I set this repo where we can check it out GitHub - infu/signtest: Testing Internet Computer hardware ledger signing
added it to the issue Unexpected data type · Issue #157 · Zondax/ledger-icp · GitHub
I see the different transformRequests now, will try that out

as far as I understand it has to look like that (a slightly modified dfx boilerplate) (Doesn’t work)


import { createActor } from "../../declarations/signtest";
import { LedgerIdentity } from "@dfinity/identity-ledgerhq";

document.querySelector("form").addEventListener("submit", async (e) => {
  let identity = await LedgerIdentity.create();

  const signtest = createActor(process.env.SIGNTEST_CANISTER_ID, {
    agentOptions: { identity },
  });

  const count = await signtest.add(3);
...

In nns-dapp we use a custom implementation of the ledger identity. It’s on my low-prio todo to clear why and if we stick to it. Anyway it might also explains why it did not work out with your sample repo and on the contrary it worked out for me this week. Spontaneous guessing though.

Yeah, I am checking it out

It doesn’t look much different and uses the different transformRequest

I took NNS dapp Identity and tried again, but I get the same error.
Here is the second repository GitHub - infu/signtest2
It uses nns-dapp/identity.ts at 977a114ed197511c78044d885981494935f22085 · dfinity/nns-dapp · GitHub
instead of @dfinity/identity-ledgerhq

This error comes from the device.
First repo error:

ledger.js:68 Uncaught (in promise) Error: A ledger error happened during signature:
Code: 27012
Message: "Data is invalid : Unexpected data type"

    at LedgerIdentity.sign (ledger.js:68:1)
    at async LedgerIdentity.transformRequest (ledger.js:79:1)
    at async HttpAgent.call (index.js:172:1)
    at async caller (actor.js:174:43)
    at async HTMLFormElement.<anonymous> (index.js:17:1)

Second repo error

nns_identity.ts:168 Uncaught (in promise) Error: A ledger error happened during signature:
Code: 27012
Message: "Data is invalid : Unexpected data type"

    at nns_identity.ts:168:1
    at async LedgerIdentity._executeWithApp (nns_identity.ts:205:1)
    at async LedgerIdentity.sign (nns_identity.ts:162:1)
    at async LedgerIdentity.transformRequest (nns_identity.ts:186:1)
    at async HttpAgent.call (index.js:172:1)
    at async caller (actor.js:174:43)
    at async HTMLFormElement.<anonymous> (index.js:17:1)
(anonymous) @ nns_identity.ts:168

Having a look at the sample repo you shared in your issue on zondax ledger-icp. If I get it right you get the error when you do query the actor const count = await signtest.add(3); right?

If so I think we have the same limitation - i.e. when we do query with the ledger identity we have to go through queries that use protobuf at the moment (also still on my todo list). For example, in nns-js, when we query the list of neurons through the dedicated function, we follow different path if identiy or ledger identity.

I know it does not solve your question directly but I guess it helps a bit.

1 Like

That confirms that the hardware Ledger app only works with protobuf functions, which can only be exposed by canisters in Rust, but not Motoko. I wonder if Zondax team are aware that they need to add Candid support?

2 Likes

Don’t know, good question. I’ll keep you posted - will update the thread if I ever get more info.

2 Likes

looking forward to this, thank you!

1 Like