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 🙏

© 2026 – Pkg Stats / Ryan Hefner

dotcv-solana

v0.0.8

Published

Smart Contract Library

Readme

DotCv SDK

A TypeScript client library for interacting with DotCv's Solana program. This SDK enables domain registration, subdomain management, record creation and editing, domain resolution, payment routing, and webhook setup for monitoring transactions.

Features

  • Initialize connections to Solana devnet or mainnet.
  • Set up and manage parent domains on the Solana blockchain.
  • Create domain registrars for subdomain management.
  • Register subdomains as an admin or user.
  • Create, edit, and resolve records (e.g., Twitter handles, wallets).
  • Transfer subdomain ownership.
  • Set up payment routers for fund distribution.
  • Distribute SOL or SPL tokens via payment routers.
  • Configure webhooks for real-time transaction monitoring using Helius.

Installation

Install the package via npm or yarn:

npm install dotcv-solana --save

OR

yarn add dotcv-solana

This package depends on external libraries like @solana/web3.js, @coral-xyz/anchor, @bonfida/spl-name-service, base58, and helius-sdk. Ensure they are installed or handled via peer dependencies.

Usage

Domain Setup

Before using the SDK for subdomain management or other features, you need to set up a parent domain on the Solana blockchain. Below is a step-by-step guide for initializing and registering a parent domain.

Step 1: Create a Keypair

Generate a new keypair for the parent domain and save the private key to a file for later use. This keypair will act as the authority for admin operations.

import { Keypair } from '@solana/web3.js';
import fs from 'fs';
import base58 from 'bs58';

const createKeypair = async () => {
  const parentKeypair = Keypair.generate();
  const privateKeyBase58 = base58.encode(parentKeypair.secretKey);
  fs.writeFileSync('parentKeypair.txt', privateKeyBase58);
  console.log(
    'Keypair created and saved to parentKeypair.txt:',
    parentKeypair.publicKey.toBase58()
  );
  return parentKeypair;
};

// Run once to generate keypair
createKeypair();
  • Purpose: Creates a new Solana keypair and saves the private key in parentKeypair.txt.
  • Important: Run this step only once and secure the parentKeypair.txt file. Comment out or remove the call after execution to avoid generating new keypairs unnecessarily.
  • Airdrop: For devnet testing, airdrop SOL to the public key of the generated keypair to cover transaction fees (e.g., use solana airdrop 1 <PUBLIC_KEY> --url https://api.devnet.solana.com).

Step 2: Fetch the Keypair

Load the saved keypair from the file for use in subsequent operations.

import { Keypair } from '@solana/web3.js';
import fs from 'fs';
import base58 from 'bs58';

const fetchKeypair = async () => {
  const privateKeyBase58FromFile = fs.readFileSync('parentKeypair.txt', 'utf8');
  const privateKeyFromFile = base58.decode(privateKeyBase58FromFile);
  const keypair = Keypair.fromSecretKey(privateKeyFromFile);
  return keypair;
};

// Fetch keypair
fetchKeypair().then(() => console.log('Keypair fetched'));
  • Purpose: Retrieves the private key from parentKeypair.txt and creates a Keypair object.
  • Important: Ensure parentKeypair.txt exists and contains the correct base58-encoded private key.

Step 3: Create the Parent Domain

Register the parent domain on the Solana blockchain. This step involves creating a name registry and registering the domain using the @bonfida/spl-name-service library.

import {
  Connection,
  Keypair,
  SystemProgram,
  Transaction,
} from '@solana/web3.js';
import {
  devnet,
  getHashedNameSync,
  NameRegistryState,
  createAssociatedTokenAccountInstruction,
  getAssociatedTokenAddress,
  createSyncNativeInstruction,
  NATIVE_MINT,
  signAndSendInstructions,
} from '@bonfida/spl-name-service';
import { sendAndConfirmTransaction } from '@solana/web3.js';
import base58 from 'bs58';

const connection = new Connection(
  'https://api.devnet.solana.com', // Replace with your RPC URL
  'confirmed'
);

const createDomain = async (domain: string, parentKeypair: Keypair) => {
  const space = 1 * 1000; // Space for name registry (in bytes)

  // Create associated token account (ATA) for Wrapped SOL (WSOL)
  const parentAta = await getAssociatedTokenAddress(
    NATIVE_MINT,
    parentKeypair.publicKey
  );
  const wrapTransaction = new Transaction().add(
    createAssociatedTokenAccountInstruction(
      parentKeypair.publicKey,
      parentAta,
      parentKeypair.publicKey,
      NATIVE_MINT
    ),
    SystemProgram.transfer({
      fromPubkey: parentKeypair.publicKey,
      toPubkey: parentAta,
      lamports: 0.45 * LAMPORTS_PER_SOL, // Amount to wrap
    }),
    createSyncNativeInstruction(parentAta)
  );
  const wrapTxSig = await sendAndConfirmTransaction(
    connection,
    wrapTransaction,
    [parentKeypair]
  );
  console.log('SOL wrapped:', wrapTxSig);

  // Register domain
  const createDomainIx = await devnet.bindings.registerDomainNameV2(
    connection,
    domain,
    space,
    parentKeypair.publicKey,
    parentAta,
    NATIVE_MINT
  );

  // Create name registry
  const hashedName = getHashedNameSync(domain);
  const nameAccount = devnet.utils.getNameAccountKeySync(hashedName);
  const createNameTx = new Transaction().add(
    SystemProgram.transfer({
      fromPubkey: parentKeypair.publicKey,
      toPubkey: nameAccount,
      lamports: await connection.getMinimumBalanceForRentExemption(space),
    }),
    await devnet.bindings.createNameRegistry(
      connection,
      domain,
      space,
      parentKeypair.publicKey,
      parentKeypair.publicKey
    )
  );
  createNameTx.feePayer = parentKeypair.publicKey;
  const createNameTxSig = await sendAndConfirmTransaction(
    connection,
    createNameTx,
    [parentKeypair]
  );
  console.log('Registry Registered:', createNameTxSig);

  // Finalize domain registration
  const registerTxSig = await signAndSendInstructions(
    connection,
    [parentKeypair],
    parentKeypair,
    createDomainIx
  );
  console.log('Domain Registered:', registerTxSig);

  // Verify domain
  const domainKeys = devnet.utils.getDomainKeySync(domain);
  const reverseKey = devnet.utils.getReverseKeySync(domain);
  const { registry } = await NameRegistryState.retrieve(
    connection,
    nameAccount
  );
  console.log('Registry Owner:', registry.owner.toBase58());
};

// Run once to create domain
const domain = 'yourparentdomain'; // e.g., 'manuchem'
fetchKeypair().then(async (keypair) => {
  await createDomain(domain, keypair);
  console.log('Domain created');
});
  • Purpose: Registers the parent domain (e.g., manuchem.sol) on the Solana blockchain.
  • Steps:
    1. Create an associated token account (ATA) for Wrapped SOL (WSOL) to pay for registration.
    2. Create a name registry for the domain.
    3. Register the domain using @bonfida/spl-name-service.
    4. Verify the domain by retrieving its keys and registry owner.
  • Important:
    • Replace the RPC URL with a reliable endpoint (e.g., QuickNode or Helius).
    • Ensure the wallet has sufficient SOL for fees (e.g., 0.45 SOL for wrapping).
    • Run this step only once and comment it out afterward to avoid duplicate registrations.
    • The domain variable should not include .sol (e.g., use manuchem instead of manuchem.sol).

Initializing the SDK

After setting up the parent domain, initialize the DotCvWeb3 instance with the keypair and domain.

import { DotCvWeb3, Record, ApiEndpoints } from 'dotcv-solana';
import { Keypair } from '@solana/web3.js';
import fs from 'fs';
import base58 from 'bs58';

const apiEndpoints: ApiEndpoints = {
  mainnet: 'https://api.mainnet-beta.solana.com',
  devnet: 'https://api.devnet.solana.com',
};

const fetchKeypair = async () => {
  const privateKeyBase58FromFile = fs.readFileSync('parentKeypair.txt', 'utf8');
  return Keypair.fromSecretKey(base58.decode(privateKeyBase58FromFile));
};

fetchKeypair().then((keypair) => {
  const web3 = new DotCvWeb3(
    base58.encode(keypair.secretKey), // Private key (base58)
    'devnet', // Network: 'devnet' or 'mainnet-beta'
    apiEndpoints,
    'yourparentdomain.sol' // Parent domain (e.g., 'manuchem.sol')
  );
  console.log('SDK initialized');
});
  • privateKey: Base58-encoded private key from parentKeypair.txt.
  • network: 'devnet' for testing or 'mainnet-beta' for production.
  • rpcEndpoints: Object with mainnet and devnet RPC URLs.
  • domain: Parent domain with .sol suffix (e.g., manuchem.sol).

Domain Management

Create a Domain Registrar

A registrar is required to manage subdomain registrations under the parent domain.

const registrar = await web3.createRegistrar();
console.log('Registrar created:', registrar);

Returns an object with registrar details (e.g., authority, fee account, price schedule).

Register a Subdomain (Admin)

Admins can register subdomains directly.

const { txHash, domainOwner } = await web3.adminRegisterSubDomain(
  'mysubdomain'
);
console.log('Subdomain registered:', txHash);
  • subDomain: Subdomain name (without parent, e.g., 'mysubdomain' instead of 'mysubdomain.yourparentdomain.sol').
  • Returns: Transaction hash and domain owner public key.

Register a Subdomain (User)

Users can register subdomains via a serialized transaction (to be signed on the frontend).

const { serializedTransaction, message } = await web3.registerSubDomain(
  'mysubdomain',
  'BUYER_PUBLIC_KEY_BASE58'
);
console.log('Serialized transaction for signing:', serializedTransaction);
  • subDomain: Subdomain name.
  • buyer: Buyer's public key (base58).
  • Returns: Serialized transaction (base58) for user signing and a message.

Transfer Subdomain Ownership

Transfer ownership of a subdomain to a new public key.

const { serializedTransaction, message } = await web3.transferSubDomain(
  'mysubdomain',
  'NEW_OWNER_PUBLIC_KEY_BASE58'
);
console.log('Transfer transaction:', serializedTransaction);
  • subDomain: Subdomain name.
  • newOwner: New owner's public key (base58).
  • Returns: Serialized transaction for signing.

Resolve a Domain

Retrieve information linked to a subdomain (e.g., associated SOL wallet).

const domainInfo = await web3.resolveDomain('mysubdomain');
console.log('Resolved SOL wallet:', domainInfo.solWallet);
  • subDomain: Subdomain name.
  • Returns: Object with resolved data (e.g., { solWallet: 'PUBLIC_KEY' }).

Record Management

Records allow linking data (e.g., Twitter handles, emails) to subdomains using the Record enum from @bonfida/spl-name-service.

Supported records include: Record.Twitter, Record.Discord, Record.Github, Record.SOL, etc.

Create a Record

const { serializedTransaction, message } = await web3.createRecord(
  'mysubdomain',
  '@twitterhandle',
  'DOMAIN_OWNER_PUBLIC_KEY_BASE58',
  Record.Twitter
);
console.log('Record creation transaction:', serializedTransaction);
  • subDomain: Subdomain name.
  • content: Record content (e.g., Twitter username).
  • domainOwner: Domain owner's public key (base58).
  • record: Type of record (e.g., Record.Twitter).
  • Returns: Serialized transaction for signing.

Edit a Record

const { serializedTransaction, message } = await web3.editRecord(
  'mysubdomain',
  '@newtwitterhandle',
  'DOMAIN_OWNER_PUBLIC_KEY_BASE58',
  Record.Twitter
);
console.log('Record edit transaction:', serializedTransaction);

Parameters are the same as createRecord.

Payment Router

Initialize a Payment Router

Set up a payment router for a subdomain to distribute incoming funds based on rules.

const distributionRules = [
  { wallet: 'WALLET1_PUBLIC_KEY_BASE58', percentage: 60 },
  { wallet: 'WALLET2_PUBLIC_KEY_BASE58', percentage: 40 },
];

const { serializedTx, paymentRouterPDA, bump } =
  await web3.initializePaymentRouter('mysubdomain', distributionRules);
console.log('Payment router initialized:', paymentRouterPDA);
  • subDomain: Subdomain name.
  • distributionRules: Array of rules (wallets and percentages; must sum to 100).
  • Returns: Serialized transaction, PDA address, and bump.

Distribute Funds

Distribute SOL or SPL tokens from the payment router.

const { distributeTxSignature, paymentRouterPDA, isSol } =
  await web3.distribute(
    'mysubdomain',
    1000000000, // Amount (lamports for SOL)
    true // true for SOL, false for SPL tokens
    // Optional: 'MINT_ADDRESS_BASE58' for SPL tokens
  );
console.log('Funds distributed:', distributeTxSignature);

Interface Types

ApiEndpoints

interface ApiEndpoints {
  mainnet: string; // Mainnet RPC URL
  devnet: string; // Devnet RPC URL
}

DistributionRuleInput

interface DistributionRuleInput {
  wallet: string; // Wallet public key (base58)
  percentage: number; // Percentage (0-100)
}

WebhookConfig (Internal, for Reference)

interface WebhookConfig {
  webhookUrl: string;
  accountAddresses: string[];
  transactionTypes: string[];
  webhookType: string;
}

Error Handling

Methods throw descriptive errors. Always wrap calls in try-catch:

try {
  await web3.adminRegisterSubDomain('mysubdomain');
} catch (error) {
  console.error('Error:', error.message);
  if (error.message.includes('unauthorized')) {
    // Handle specific errors
  }
}

Common errors include invalid keys, insufficient funds, or network issues. For domain setup, ensure the wallet has SOL