Using @dfinity/agent in node.js

I’m also running into this issue as well

We’ve been investigating on and off for a while and haven’t been able to find a simple way to reproduce identities from dfx in agent-js. I think it’s going to require re-implementing the full logic of the Rust library that dfx uses as a new npm package.

It’s something we want to do, but it’s below a few other more critical features we want to get through first

It might be happening on tweetnacl, better using try catch and loop the keypair function

Update - @ericswanson helped me unblock this:

seed.txt

early cinnamon crucial teach mobile just toast real rebel around card priority spike aerobic result account marble hero action intact inside elbow wrestle oval

$ keysmith principal yields
bh66w-ffyze-maien-ejjje-wzhqi-crrjo-rcgs7-twjrh-kf2km-hbtia-eae

seed.js

const Identity = require("@dfinity/identity");
const hdkey = require("hdkey");
const fs = require("fs");

const { Secp256k1KeyIdentity } = Identity;

const bip39 = require("bip39");

const phrase = fs.readFileSync("seed.txt").toString().trim();

export const identityFromSeed = async (phrase) => {
  const seed = await bip39.mnemonicToSeed(phrase);
  const root = hdkey.fromMasterSeed(seed);
  const addrnode = root.derive("m/44'/223'/0'/0/0");

  return Secp256k1KeyIdentity.fromSecretKey(addrnode.privateKey);
};

identityFromSeed(phrase).then((identity) => {
  console.log(identity.getPrincipal().toString());
});

$ node seed.js yields
bh66w-ffyze-maien-ejjje-wzhqi-crrjo-rcgs7-twjrh-kf2km-hbtia-eae

We still have to hunt down the implementation used by rust-openssl/pkey.rs at master · sfackler/rust-openssl · GitHub, but this should open up the possibility to use a single identity across JS and dfx, from a shared seed phrase!

Of course, this comes with some security caveats -

  • Do not ever commit a seed phrase to your souce code.
  • THIS PATTERN SHOULD NEVER BE USED IN A FRONTEND APPLICATION
  • Use a different identity for each project to mitigate risk
  • Dfinity does not officially endorse doing this
7 Likes

This looks promising :partying_face:, would that also work for the pem file? since our dfx identity is created using a pem file, or is there a way to get the seed phrase from a pem file?

1 Like

also wondering this^

What am I missing here? I used this pattern in my node.js script, and the identity actually works fine. The problem is that when I try to include my actor from /src/declaration/project_name/project_name.js or /src/declaration/project_name/index.js I get the dreaded: SyntaxError: Unexpected token ‘export’ or Cannot use import statement outside a module. I can get around this by copying the export const idlFacotry…line from the file into my script and getting rid of the export. But this is really annoying to do every time. Perhaps I’ve missed some updates to the js world in the last couple of years, but I’ve tried a number of things and I either get that something isn’t compatible with commonjs or visa versa. Does the IDL get dumped somewhere that I can reference with a require(‘x’) statement?

If you are using NodeJS LTS or more recent versions you can use script module files .mjs.

What I do is adding a postbuild script in package.json that automatically copy the .did.js files to .did.mjs files. Then in myscript.mjs script I can import the idlFactory as following (modules script support import)

import {idlFactory} from '..dfx/local/canisters/manager/manager.did.mjs';

Got a couple of scripts there GitHub - papyrs/ic: Backend canisters and providers of Papyrs

1 Like

Just to confirm… I want to use my default dfx identity to authenticate with my canister using the @dfinity/agent library in node…

Ideally using the PEM file in ~/.config/dfx/identity/default/identity.pem. I’m guessing from this thread that this is not possible right now? (I need to create a new identity / principal using keysmith and use that instead. Does that sound right?)

1 Like

I think you can use Key Smith to produce a back up phrase for your default identity. Just run the help for key Smith and you’ll see a number of options.

1 Like

If I do this I have to convert all of my other require statements to import statements correct? I feel like I tried this but one of my existing packages demanded that I use require or something like that. I’ll give this a try and report back.

Yes probably, a bit a pity but at least a one time change. Let me know if it works out :crossed_fingers:

Thanks to everyone above for the great commentary, really was nice to follow as I started troubleshooting not being able to load in my existing dfx principal via agent-js (I received the same different dfx and node principals as everyone else).

Since it did take me a bit of time to get through it all, hopefully these steps make it easier for those of you running into a similar issue (as of the time of this post).

Steps

  1. Navigate to my .dfx identities → ~/.config/.dfx/identity
  2. Create a new identity → mkdir local-testing; cd local-testing
  3. Download quill since keysmith is now deprecated.
  4. Test that quill is installed correctly → quill
  5. Look up how to generate a key → quill generate --help
  6. Generate a key and seed file → quill generate --pem-file identity.pem --seed-file seed.txt
  7. Now implement Kyle’s code snippet below (copied from comment this post is replying to)
  1. If you run this an have NodeJS version 17+, you will receive the following error coming from the hdkey library import → Error: error:0308010C:digital envelope routines::unsupported. The possible solutions for getting around this are here, the easiest of which is downgrading to node 16. (@kpeacock/sdk team can you provide a node 17+ solution? :slightly_smiling_face: )
  2. Now running the above node script and dfx identity get-principal should return the same identity! Yay!
4 Likes

So there’s no way to just pass in a path to a PEM file and get an identity that can be used in @dfinity/agent? It used to be the case that I could just pass in JSON object and all of this worked, that was last Summer. What happened?

a JSON object has always worked and still does work. The complicated flow is specifically for deriving an identical principal across dfx, quill, and agent-js

Do you know where the documentation is on what the JSON object should look like? I keep getting errors with it

Here is working code:

5 Likes

It does work! Thank you!

1 Like

For all those who are interested on this thread, I have created a dfinity module in BlueprintJS called blueprint-dfinity. The goal of this module is to make it easier to write NodeJS clients for the Internet Computer.

Some of the key features of this BlueprintJS module are:

  • Declarative approach for defining actors
  • Ability to bind actors and their source canisters to properties
  • Codifies bootstrapping code

The current version of trunk uses the original property definition approach to defining actors, and binding them to object properties. The newest version of the blueprint-dfinity, which is to be released with v5, uses decorators to define the actors and bindings to properties on ES6 classes.

blueprint/packages/blueprint-dfinity at v5 · onehilltech/blueprint · GitHub

Please have a look and provide feedback.

Disclaimer. I am the creator of BlueprintJS.

4 Likes