This tutorial has been tested on Linux and Mac OS. Some steps may be slightly different if you use another OS.

## Prerequisites

- Node.js version 20 or higher.
- URLs and API keys for the MPC nodes in a running instance of the Builder Vault TSM. You can follow one of the tutorials [Local Deployment](https://builder-vault-tsm.docs.blockdaemon.com/docs/getting-started-local-deployment), [Hosted Sandbox](https://builder-vault-tsm.docs.blockdaemon.com/docs/getting-started-hosted-sandbox), or [AWS Marketplace](https://builder-vault-tsm.docs.blockdaemon.com/docs/getting-started-aws) to get access to a Builder Vault TSM.
- Check our public repo [here](https://gitlab.com/Blockdaemon/nodejs-tsm-sdkv2).

## Step 1: Create a Node.js Project and Fetch the TSM SDK

In the following steps, we will create a small Node.js project that uses the Builder Vault Node.js SDK:

- Create a new folder for your project on your local machine
- Open a terminal window and go to the project folder.
- Initiate a new Node.js project with default values by using the following command:

```bash
    npm init -y
    ```

- Set the public repository with the following command:

```bash
    npm config set @sepior:registry=https://gitlab.com/api/v4/projects/56306653/packages/npm/
    ```

- Install the TSM SDK by using the following command:

```bash
    npm install @sepior/tsmsdkv2
    ```

> 📘
>
> ### TSM Version
>
> When you connect to an MPC node with the SDK, it is important that the version of the SDK matches the version of the MPC node. You can see the actual version of an MPC node in the demo TSM using this Linux command:
>
> `curl https://node1.tsm.sepior.net/version`
>
> If the returned version is for example `70.2.4`, you can fetch the corresponding version of the SDK with this:
>
> `npm install @sepior/tsmsdkv2@70.2.4`
>
> For the full list of SDK versions, please see [here](https://builder-vault-tsm.docs.blockdaemon.com/changelog).

## Step 2: Run the TSM SDK

In the following steps, we connect to three MPC nodes in the Builder Vault, each with its instance of the SDK. The SDKs are called concurrently to obtain a partial signature from each MPC node. These partial signatures are then combined to form the final signature.

- Create a file `example.js` in the project folder with the following code:

```javascript
    const { TSMClient, Configuration, SessionConfig, curves } = require("@sepior/tsmsdkv2");
    const crypto = require('crypto');

const main = async () => {

// Create clients for each of the nodes

const configs = [
        {
          url: "http://localhost:8500",
          apiKey: "apikey0",
        },
        {
          url: "http://localhost:8501",
          apiKey: "apikey1",
        },
        {
          url: "http://localhost:8502",
          apiKey: "apikey2",
        },
      ];

const clients = [];

for (const rawConfig of configs) {
        const config = await new Configuration(rawConfig.url);
        await config.withAPIKeyAuthentication(rawConfig.apiKey);
        const client = await TSMClient.withConfiguration(config);

clients.push(client);
      }

// Generate an ECDSA master key

const threshold = 1; // The security threshold for this key
      const keygenPlayers = [0, 1, 2]; // The key should be secret shared among all three MPC nodes
      const keygenSessionConfig = await SessionConfig.newSessionConfig(
        await SessionConfig.GenerateSessionID(),
        new Uint32Array(keygenPlayers),
        {}
      );

const keyIds = ["", "", ""];

console.log(`Generating key using players ${keygenPlayers}`);

const generateKeyPromises = [];

for (const [i, client] of clients.entries()) {
        const func = async () => {
          const ecdsaApi = client.ECDSA();
          keyIds[i] = await ecdsaApi.generateKey(
            keygenSessionConfig,
            threshold,
            curves.SECP256K1,
            ""
          );
        };
        generateKeyPromises.push(func());
      }

await Promise.all(generateKeyPromises);

// Validate key IDs

for (let i = 1; i < keyIds.length; i++) {
        if (keyIds[0] !== keyIds[i]) {
          console.log("Key ids do not match");
          return;
        }
      }

const keyId = keyIds[0];

console.log(`Generated key with id: ${keyId}`);

// Get the public key

const derivationPath = new Uint32Array([]); // We don't use key derivation in this example

const publickeys = [];

for (const client of clients) {
        const ecdsaApi = client.ECDSA();
        publickeys.push(await ecdsaApi.publicKey(keyId, derivationPath));
      }

// Validate public keys

for (let i = 1; i < publickeys.length; i++) {
        if (!Buffer.from(publickeys[0]).equals(publickeys[i])) {
          console.log("Public keys does not match");
          return;
        }
      }

const publicKey = publickeys[0];

console.log(`Public key: ${Buffer.from(publicKey).toString("hex")}`);

// We can now sign with the created key

const message = "This is a message to be signed";
      const messageHash = crypto.createHash("sha256").update(message).digest();

const signPlayers = [0, 1]; // We want to sign with the first two MPC nodes
      const sessionId = await SessionConfig.GenerateSessionID();
      const signSessionConfig = await SessionConfig.newSessionConfig(
        sessionId,
        new Uint32Array(signPlayers),
        {}
      );

console.log(`Creating signature using players ${signPlayers}`);

const partialSignatures = [];

const partialSignaturePromises = [];

for (const playerIdx of signPlayers) {
        const func = async () => {
          const ecdsaApi = clients[playerIdx].ECDSA();

const partialSignResult = await ecdsaApi.sign(
            signSessionConfig,
            keyId,
            new Uint32Array([]),
            messageHash
          );

partialSignatures.push(partialSignResult);
        };

partialSignaturePromises.push(func());
      }

await Promise.all(partialSignaturePromises);

const ecdsaApi = clients[0].ECDSA();

const signature = await ecdsaApi.finalizeSignature(
        messageHash,
        partialSignatures
      );

// Verify the signature relative to the signed message and the public key

try {
        const result = await ecdsaApi.verifySignature(
          publicKey,
          messageHash,
          signature.signature
        );
        console.log(result);
        console.log(
          `Signature: ${Buffer.from(signature.signature).toString("hex")}`
        );
      } catch (e) {
        console.log(e);
      }
    }

main();
    ```

- Remember to replace the MPC node URLs and API keys above with the URLs and API keys for the actual Builder Vault TSM instance you use.
- Run the following commands to execute the SDK:

```bash
    node example.js
    ```

The code above should result in output like this:

```
    Generating key using players 0,1,2
    Generated key with id: RwmUOhDwMQ0SOqrDNTY7XYSEUpFD
    ...
    ```

This means you have successfully generated a new ECDSA key in the Builder Vault and used it for signing.

If you get error messages, go to our [troubleshooting guide](https://builder-vault-tsm.docs.blockdaemon.com/docs/troubleshooting) or contact our support team.
