Announcement: a full-featured open source ICRC-1 wallet

We are open-sourcing a new ICRC-1 wallet.

Features:

  • Manage arbitrarily many subaccounts and give them alias names
  • See balances and transaction history on a per-subaccount basis
  • Transfer between own subaccounts
  • Send directly to subaccounts of other principals
  • Display ICRC-1 encoded receive addresses for own subaccounts
  • Handle multiple ICRC-1 tokens (assets)
  • Add custom assets by their ledger canister id and index canister id
  • Manage contacts in a contact book
  • Contacts can be for specific assets and specific subaccounts
  • Login with II and NFID

You can try out the wallet right in the browser at: https://e4hv6-7yaaa-aaaao-a2ida-cai.icp0.io/
It is a web wallet and works well on desktop. It is currently not yet optimised for mobile.

Disclaimer: this is untested alpha software. Please don’t use it for real funds.

We would welcome any feedback, comments, feature requests and external contributions.

The source code can be found here: GitHub - research-ag/wallet
and is provided under GPL 3.

Here is a screenshot:

34 Likes

good job!
good job!
good job!

3 Likes

I deployed the latest icp ledger as a custom token at nxd7c-niaaa-aaaae-qaawa-cai and I logged in
and got a principal “co4du-t3fxy-jx63v-fepzd-5w5e2-skebp-riwa6-65yjw-6rz4o-w3mwz-xae” and I can query the balance with dfx:

dfx canister call nxd7c-niaaa-aaaae-qaawa-cai icrc1_balance_of ‘(record { owner = principal “co4du-t3fxy-jx63v-fepzd-5w5e2-skebp-riwa6-65yjw-6rz4o-w3mwz-xae” })’

which produces

(400_000_000 : nat)

However your app at https://e4hv6-7yaaa-aaaao-a2ida-cai.icp0.io/ shows a zero balance for the principal that it shows and which I copied out using the nice little icon provided for that purpose.

I can help debug if you like.

Right now

dfx canister --network ic call nxd7c-niaaa-aaaae-qaawa-cai icrc1_balance_of '(record { owner = principal "co4du-t3fxy-jx63v-fepzd-5w5e2-skebp-riwa6-65yjw-6rz4o-w3mwz-xae" })'

produces 0 for me. Did you move the funds out already?

Did you click the refresh button in the wallet that is next to the copy-to-clipboard button next to the principal?

How can I get tokens on that ledger to try myself?

1 Like

Same here now. Fixed it. Works great! Sorry for the noise.

1 Like

Sorry to bother you, but after successfully setting up a manual token on my internet identity login I am trying with NFID and I am getting:

Server returned an error: Code: 502 (TypeError: Failed to fetch) Body: TypeError: Failed to fetch Import token on your own responsability

1 Like

Why not provide this functionality in the NNS

2 Likes

Thanks for the report. I can reproduce it and will look into it.

I don’t know what the feature roadmap of the NNS wallet/dapp is.

It is risky for a production wallet to let users add their own tokens because they could get scammed. A scammer could create a ledger canister and try to “impersonate” an existing token with that fake ledger. The scammer could try to convince people to add that ledger under a token symbol that is already in use.

Anyway, the feature could be added hidden behind an “expert” checkbox with some warnings.

1 Like

Bug has been fixed. Please try again now and thanks for the report!

Thanx! Works great.

Do you have a timeline for Metamask login? That would be a great feature to expand the community.

No, would love to add that though. I can bump it up in priority. Are there any sample implementations around where we can take code from to speed it up?

1 Like

It looks pretty straightforward, you just use the sign mechanism:

Too bad NFID doesn’t do it.

I don’t see how that flow for login into web2 servers carries over to our situation. What we need is a Metamask Snap to sign individual transactions. AstroX/ME wallet has done a snap. Maybe it can be ported over.

1 Like

That is certainly the right (secure) way to do it. I’ll take a look at that Snap.

@timo @jplevyak

Consider taking a look at this

2 Likes

Wallet looks great!

I’m working on some sign in with Ethereum (SIWE) support libraries as well as template apps for Rust and Motoko to simplify the Metamask login flow for IC apps. Nothing is published yet but hopefully will be before christmas. It would be cool to see if we could add Metamask support to the ICRC-1 wallet using these libraries!

Curious to know why you would like a Metamask Snap to sign messages?

Below is a short video of the current progress. Almost all functionality of Rust lib is in place, in-canister verification of SIWE messages etc. Before publish it needs cleanup, documentation and some reviewing.

1 Like

Can you explain what is happening here? SIWE in this example means signing into what exactly? And what is the relation between the principal in your video and the Eth address?

Sure! Longish answer below.

In the demo, you “sign in” to the canister based backend, creating an authenticated session around the ETH address.

The flow:

  1. Connect ETH wallet
  2. Sign a message defined by the app, using the wallet
  3. Generate an identity (Ed25519KeyIdentity) based on the hash of the signature
  4. Request a SIWE message from the canister
  5. Sign the SIWE message
  6. Send signature to canister using identity from step 3 as caller
  7. Canister verifies signature against saved message from step 4 and recovers ETH address that signed the message
  8. Canister links signer address with caller and creates a session, returns session info
  9. Etc. Client calls other canister methods. Canister methods use an auth guard to verify session is valid and if needed get the ETH address of caller.

The identity is stored locally in the browser. Subsequent calls would require user start at step 4 if session has expired.

You can run the whole flow, but you don’t need to.

Stop at step 3. Generate an Identity by signing a message and then start interacting with the canister using that Identity.

If the message you sign is identical each time you use the app, the generated identity will be identical. But that’s it. No secure link between ETH address and Identity is made at the canister level. Also, since generated identity doesn’t expire, anyone that manages to get a hold of your identity can impersonate you in the app indefinitely. Not super secure, but most likely still a really good idea for an app that just want to store some user metadata or other non critical info.

Running the whole flow takes us almost over the finish line. After step 8 you have established a secure session with knowledge of the callers ETH address.

What remains uncertain is whether the calling identity establishing the session for an ETH address is indeed the same identity generated using a signature from that ETH address. Verification is possible, but that would require the client sending the signature from step 2 to the canister, a process that poses security risks.

Once vetKeys are launched, we can generate an identity seed on IC and transfer it securely to the client. Then the one-to-one link between ETH address and identity can be verified, meaning: In this app, ETH address X is always represented by IC identity X.

While we cannot ensure that the identity initiating a session is directly created from a specific ETH address, we can confirm that the identity in use has control over the mentioned ETH address. This means that, even though a one-to-one link between the ETH address and the identity isn’t fully verifiable now, the current system effectively ensures that the user controlling a given ETH address is the one interacting with the application.

In the ICRC-1 wallet I guess you don’t care too much about the ETH address? Could you elaborate what you mean by Metamask signing individual transactions?

Ok, that’s similar to what I thought it would be. But how can we use this to sign into a wallet? There is no backend canister nor any other server. There is only the ICRC-1 ledger and the web app. So unless the ledger support SIWE I don’t see how this can be used.

A Metamask Snap can have the ability to sign IC native calls and IC native delegations. So your Ethereum key, which is just an ECDSA key, can become an IC key as well.

By Metamask signing individual transactions I mean that the Metamask Snap signs native IC calls directly with the Ethereum key. For each IC call that you make, i.e. in the wallet case for each transfer that you make, the Metamask Snap opens and the user has to confirm.

Sessions with a Metamask Snap are also possible. Then the Snap signs a delegations and the delegatee is a session key generated by the wallet app for the session. But the delegator is always the same, it is the ECDSA key that Metamask holds.

1 Like