npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@turnkey/wallet-stamper

v0.0.3

Published

Wallet stamper for @turnkey/http

Downloads

81

Readme

Turnkey Wallet Stamper

The @turnkey/wallet-stamper package provides a mechanism to stamp requests using a wallet public key and signature. This package supports both Solana and Ethereum wallets, allowing for seamless integration with various blockchain applications.

Installation

Before you start using the Turnkey Wallet Stamper, make sure to install the necessary packages in your project. This guide assumes you have a Node.js environment ready for development.

Install the required packages using NPM, Yarn, or PNPM:

npm install @turnkey/wallet-stamper @turnkey/http

Usage

Prerequisites

Add Wallet Public Key as Authenticator

To use the wallet stamper, you must add the user's wallet public key as an API key authenticator. This can be achieved either by creating a sub-organization activity or by generating a new API key and passing in the wallet's public key.

The API key stamper is necessary because we need to use the parent organization's API key to add the authenticator initially. Once this is done, the wallet can stamp activity requests independently without needing the parent organization's API private and public keys.

Below are the steps to add a Solana public key as a wallet authenticator:

const apiKeyStamper = new ApiKeyStamper({
  apiPublicKey: process.env.API_PUBLIC_KEY ?? "",
  apiPrivateKey: process.env.API_PRIVATE_KEY ?? "",
});

const client = new TurnkeyClient({ baseUrl: BASE_URL }, apiKeyStamper);

const activityPoller = createActivityPoller({
  client,
  requestFn: client.createApiKeys,
});

// See "Example: Signing with a Solana Wallet" below for how to implement the SolanaWallet interface
const mockWallet = new MockSolanaWallet();

// This is the public key of the wallet that will be added as an API key and used to stamp future requests
const publicKey = mockWallet.recoverPublicKey();

// The userId of the user that we will add the wallet public key as an authenticator
const userId = "f4a5e6b4-3b9c-4f69-b7f6-9c2f456a4d23";

// We set the curve type to 'API_KEY_CURVE_ED25519' for solana wallets
// If using an EVM wallet, set the curve type to 'API_KEY_CURVE_SECP256K1'
const curveType = "API_KEY_CURVE_ED25519";

const result = activityPoller({
  type: "ACTIVITY_TYPE_CREATE_API_KEYS_V2",
  timestampMs: new Date().getTime().toString(),
  organizationId: "acd0bc97-2af5-475b-bc34-0fa7ca3bdc75",
  parameters: {
    apiKeys: [
      {
        apiKeyName: "solana-wallet",
        publicKey,
        curveType,
      },
    ],
    userId,
  },
});

Using the Wallet Stamper

To use the @turnkey/wallet-stamper package, follow these steps:

  1. Choose Your Wallet Type: Decide whether you will use an EVM-based wallet (e.g., Ethereum) or a Solana-based wallet.

  2. Implement the Wallet Interface: Depending on your chosen blockchain, implement the wallet interface. This involves creating methods to sign messages and recover public keys.

  3. Instantiate the WalletStamper: Create an instance of the WalletStamper using the wallet interface.

  4. Instantiate the TurnkeyClient: Create an instance of the TurnkeyClient with the WalletStamper instance.

  5. Stamp Requests: Now when making request using the TurnkeyClient, the wallet stamper will automatically stamp the request with the user's wallet public key and signature.

Example: Signing with a Solana Wallet

In this example, we are using a local Solana wallet. For information on using an injected Solana wallet such as Solflare, please refer to the with-wallet-stamper example.

import { Keypair } from "@solana/web3.js";
import { decodeUTF8 } from "tweetnacl-util";
import nacl from "tweetnacl";
import { TurnkeyClient } from "@turnkey/http";
import { WalletStamper, SolanaWalletInterface } from "@turnkey/wallet-stamper";

class SolanaWallet implements SolanaWalletInterface {
  keypair = Keypair.fromSecretKey(SOLANA_PRIVATE_KEY);
  type = "solana" as const;

  async signMessage(message: string): Promise<string> {
    const messageBytes = decodeUTF8(message);
    const signature = nacl.sign.detached(messageBytes, this.keypair.secretKey);
    return Buffer.from(signature).toString("hex");
  }

  recoverPublicKey(): string {
    // Convert the base24 encoded Solana wallet public key (the one displayed in the wallet)
    // into the ed25519 decoded public key
    const ed25519PublicKey = Buffer.from(
      this.keypair.publicKey.toBuffer()
    ).toString("hex");
    return ed25519PublicKey;
  }
}

// Instantiate the WalletStamper with the SolanaWallet
const walletStamper = new WalletStamper(new SolanaWallet());

// Instantiate the TurnkeyClient with the WalletStamper
const client = new TurnkeyClient({ baseUrl: BASE_URL }, walletStamper);

// Call getWhoami to get the sub org's organizationId and userId passing in the parent org id
// whoami { organizationId: string; organizationName: string; userId: string; username: string; }
const whoami = await client.getWhoami({
  organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID,
});

// Now that we have the sub organization id, we can make requests using that sub org id

// Get the wallets for this sub organization
const wallets = await client.getWallets({
  organizationId: whoami.organizationId,
});

Example: Signing with an Ethereum Wallet

The primary difference between signing with an Ethereum Wallet and a Solana Wallet is the process of obtaining the public key. For Solana, the public key can be directly derived from the wallet. However, for Ethereum, the secp256k1 public key cannot be directly retrieved. Instead, you must first obtain a signature from the user and then recover the public key from that signature. This additional step is why the Ethereum Wallet (EVM Wallet) interface includes a different method for recovering the public key.

import {
  createWalletClient,
  custom,
  recoverPublicKey,
  hashMessage,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

import { WalletStamper, EvmWalletInterface } from "@turnkey/wallet-stamper";

export class EthereumWallet implements EvmWalletInterface {
  account = privateKeyToAccount(ETHEREUM_PRIVATE_KEY);
  type = "evm" as const;

  async signMessage(message: string): Promise<string> {
    // Create a new wallet client with a JSON-RPC account from the injected provider
    const walletClient = createWalletClient({
      chain: mainnet,
      transport: custom(window.ethereum!),
    });
    // Prompt the user to sign the message with their wallet
    const signature = await walletClient.signMessage({
      account: this.account,
      message,
    });
    return signature;
  }

  async recoverPublicKey(message: string, signature: string): Promise<string> {
    const secp256k1PublicKey = recoverPublicKey({
      hash: hashMessage(message),
      signature: signature as Hex,
    });
    return secp256k1PublicKey;
  }
}

// Instantiate the WalletStamper with the EthereumWallet
const walletStamper = new WalletStamper(new EthereumWallet());

// Instantiate the TurnkeyClient with the WalletStamper
const client = new TurnkeyClient({ baseUrl: BASE_URL }, walletStamper);

// Call getWhoami to get the sub org's organizationId and userId passing in the parent org id
// whoami { organizationId: string; organizationName: string; userId: string; username: string; }
const whoami = await client.getWhoami({
  organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID,
});

// Now that we have the sub organization id, we can make requests using that sub org id

// Get the wallets for this sub organization
const wallets = await client.getWallets({
  organizationId: whoami.organizationId,
});

Contributing

Contributions are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request.

License

The @turnkey/wallet-stamper package is licensed under the MIT License.