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

@claritydao/midnight-sdk

v1.1.3

Published

Production-ready TypeScript SDK for governance operations on the Midnight blockchain.

Downloads

420

Readme

Agora DAO Midnight SDK

Production-ready TypeScript SDK for governance operations on the Midnight blockchain.

Overview

The Agora DAO Midnight SDK provides a type-safe, professional-grade interface for interacting with governance contracts on Midnight Network. It integrates with the Lace wallet extension and provides comprehensive APIs for DAO governance operations.

Features

  • Type-Safe: Full TypeScript support with comprehensive type definitions
  • Production-Ready: No hardcoded values, proper error handling, validation
  • Browser-Native: Works directly in modern browsers with Lace wallet
  • Observable State: Reactive patterns with RxJS for real-time updates
  • Zero-Knowledge: Privacy-preserving governance with ZK proofs
  • Well-Documented: Complete API reference and examples

Installation

pnpm add @claritydao/midnight-sdk

Quick Start

1. Deploy a New Governor Contract

import {
  GovernorAPI,
  initializeProviders,
  ConfigPresets,
} from "@claritydao/midnight-sdk";
import { randomBytes } from "@midnight-ntwrk/midnight-js-utils";
import pino from "pino";

const logger = pino({ level: "info" });

// Initialize providers (connects to Lace wallet)
const providers = await initializeProviders(logger);

// Create deployment configuration
const config = {
  governor: {
    governance: ConfigPresets.testing(), // or ConfigPresets.production()
    executionDelay: BigInt(60),
    gracePeriod: BigInt(300),
    maxActiveProposals: BigInt(100),
    proposalLifetime: BigInt(86400),
    emergencyVetoEnabled: true,
    emergencyVetoThreshold: BigInt(75000),
  },
  token: {
    name: "My DAO Token",
    symbol: "MDAO",
    decimals: BigInt(18),
  },
  adminKey: randomBytes(32), // IMPORTANT: Use secure random bytes!
};

// Deploy contract
const api = await GovernorAPI.deploy(providers, config, logger);
console.log("Contract deployed at:", api.deployedContractAddress);

2. Join an Existing Contract

import { GovernorAPI, initializeProviders } from "@claritydao/midnight-sdk";
import pino from "pino";

const logger = pino({ level: "info" });
const providers = await initializeProviders(logger);

// Join existing contract
const contractAddress = "existing-contract-address";
const api = await GovernorAPI.join(providers, contractAddress, logger);

3. Create a Proposal

import { VoteChoice } from "@claritydao/midnight-sdk";
import type { ProposalAction } from "@claritydao/midnight-sdk";

// Define proposal actions (what happens if proposal passes)
const actions: ProposalAction[] = [
  {
    typ: 0, // Transfer type
    token: tokenAddress,
    amount: BigInt(10000),
    to: recipientAddress,
    configKey: new Uint8Array(32),
    configValue: new Uint8Array(32),
  },
];

// Create proposal (requires sufficient stake)
const { txData, proposalId } = await api.createProposal(
  "Fund Community Initiative",
  "Transfer 10,000 tokens to community wallet for Q1 initiatives",
  creatorAddress,
  actions
);

console.log("Proposal created with ID:", proposalId);

4. Cast a Vote

import { VoteChoice } from "@claritydao/midnight-sdk";

// Vote on proposal (type-safe!)
await api.castVote(
  proposalId,
  voterAddress,
  VoteChoice.For // VoteChoice.Against or VoteChoice.Abstain
);

console.log("Vote cast successfully");

Configuration Presets

The SDK provides two configuration presets:

Testing Configuration

For development and testnet:

import { ConfigPresets } from "@claritydao/midnight-sdk";

const governanceConfig = ConfigPresets.testing();
// Returns:
// {
//   votingPeriod: BigInt(300),        // 5 minutes
//   quorumThreshold: BigInt(100),     // 100 tokens
//   proposalThreshold: BigInt(50),    // 50 tokens to create proposals
//   proposalLifetime: BigInt(3600),   // 1 hour
//   executionDelay: BigInt(60),       // 1 minute
//   gracePeriod: BigInt(300),         // 5 minutes
//   minStakeAmount: BigInt(10),       // 10 tokens minimum
//   stakingPeriod: BigInt(60),        // 1 minute lock
// }

Production Configuration

For mainnet:

import { ConfigPresets } from "@claritydao/midnight-sdk";

const governanceConfig = ConfigPresets.production();
// Returns:
// {
//   votingPeriod: BigInt(604800),     // 7 days
//   quorumThreshold: BigInt(100000),  // 100K tokens
//   proposalThreshold: BigInt(50000), // 50K tokens to create proposals
//   proposalLifetime: BigInt(1209600), // 14 days
//   executionDelay: BigInt(86400),    // 24 hours
//   gracePeriod: BigInt(259200),      // 3 days
//   minStakeAmount: BigInt(1000),     // 1K tokens minimum
//   stakingPeriod: BigInt(604800),    // 7 days lock
// }

API Reference

Types

DeploymentConfig

Complete deployment configuration:

interface DeploymentConfig {
  governor: GovernorConfig;
  token: TokenConfig;
  adminKey: Uint8Array; // 32 bytes - use randomBytes(32)
}

GovernorConfig

Governor contract parameters:

interface GovernorConfig {
  governance: GovernanceConfig;
  executionDelay: bigint;
  gracePeriod: bigint;
  maxActiveProposals: bigint;
  proposalLifetime: bigint;
  emergencyVetoEnabled: boolean;
  emergencyVetoThreshold: bigint;
}

GovernanceConfig

Core governance parameters:

interface GovernanceConfig {
  votingPeriod: bigint; // How long voting lasts
  quorumThreshold: bigint; // Minimum votes to pass
  proposalThreshold: bigint; // Stake required to create proposals
  proposalLifetime: bigint; // How long proposals remain valid
  executionDelay: bigint; // Time-lock before execution
  gracePeriod: bigint; // Window to execute after delay
  minStakeAmount: bigint; // Minimum stake to vote
  stakingPeriod: bigint; // Stake lock duration
}

TokenConfig

Token parameters:

interface TokenConfig {
  name: string; // Max 64 characters
  symbol: string; // Max 16 characters
  decimals: bigint; // Max 18
}

ProposalAction

Actions to execute when proposal passes:

interface ProposalAction {
  typ: number; // Action type (0 = transfer)
  token: Uint8Array; // Token address (32 bytes)
  amount: bigint; // Amount to transfer
  to: Uint8Array; // Recipient address (32 bytes)
  configKey: Uint8Array; // Config key (32 bytes)
  configValue: Uint8Array; // Config value (32 bytes)
}

VoteChoice

Type-safe vote choices:

enum VoteChoice {
  For = 0, // Vote in favor
  Against = 1, // Vote against
  Abstain = 2, // Abstain from voting
}

GovernorAPI

Static Methods

deploy()
static async deploy(
  providers: GovernorProviders,
  config: DeploymentConfig,
  logger?: Logger
): Promise<GovernorAPI>

Deploys a new governor contract with the specified configuration.

Parameters:

  • providers: Initialized providers (from initializeProviders)
  • config: Deployment configuration
  • logger (optional): Pino logger instance

Returns: GovernorAPI instance

Throws:

  • InvalidConfigurationError: Invalid configuration parameters
  • DeploymentError: Contract deployment failed
  • WalletNotConnectedError: Wallet not connected

Example:

const api = await GovernorAPI.deploy(providers, config, logger);
join()
static async join(
  providers: GovernorProviders,
  contractAddress: ContractAddress,
  logger?: Logger
): Promise<GovernorAPI>

Joins an existing governor contract.

Parameters:

  • providers: Initialized providers
  • contractAddress: Address of deployed contract
  • logger (optional): Pino logger instance

Returns: GovernorAPI instance

Throws:

  • ContractNotFoundError: Contract not found at address
  • WalletNotConnectedError: Wallet not connected

Instance Methods

createProposal()
async createProposal(
  title: string,
  description: string,
  creator: Uint8Array,
  actions: ProposalAction[]
): Promise<{ txData: FinalizedTxData; proposalId: bigint }>

Creates a new governance proposal.

Parameters:

  • title: Proposal title (max 256 chars)
  • description: Proposal description (max 10,000 chars)
  • creator: Creator address (32 bytes, must have sufficient stake)
  • actions: Array of actions to execute if proposal passes

Returns: Object with transaction data and proposal ID

Throws:

  • InvalidParameterError: Invalid parameters
  • InsufficientStakeError: Creator has insufficient stake
  • TransactionError: Transaction failed

Example:

const { proposalId } = await api.createProposal(
  "Increase Treasury Allocation",
  "Detailed description of the proposal...",
  creatorAddress,
  [
    {
      typ: 0,
      token: tokenAddress,
      amount: BigInt(10000),
      to: recipientAddress,
      configKey: new Uint8Array(32),
      configValue: new Uint8Array(32),
    },
  ]
);
castVote()
async castVote(
  proposalId: bigint,
  voter: Uint8Array,
  choice: VoteChoice
): Promise<FinalizedTxData>

Casts a vote on an active proposal.

Parameters:

  • proposalId: Proposal ID to vote on
  • voter: Voter address (32 bytes, must have voting power)
  • choice: Vote choice (VoteChoice.For, VoteChoice.Against, or VoteChoice.Abstain)

Returns: Transaction data

Throws:

  • InvalidParameterError: Invalid parameters
  • InsufficientStakeError: Voter has insufficient voting power
  • ProposalNotFoundError: Proposal not found or not active
  • TransactionError: Transaction failed

Example:

await api.castVote(proposalId, voterAddress, VoteChoice.For);
registerStake()
async registerStake(
  userId: Uint8Array,
  amount: bigint
): Promise<FinalizedTxData>

Registers a stake for governance participation.

Parameters:

  • userId: User address (32 bytes)
  • amount: Amount to stake

Returns: Transaction data

finalizeProposal()
async finalizeProposal(proposalId: bigint): Promise<FinalizedTxData>

Finalizes a proposal after voting period ends.

queueProposal()
async queueProposal(proposalId: bigint): Promise<FinalizedTxData>

Queues a finalized proposal for execution.

executeProposal()
async executeProposal(
  proposalId: bigint,
  effectId: bigint
): Promise<FinalizedTxData>

Executes a queued proposal after execution delay.

getProposalDetails()
async getProposalDetails(proposalId: bigint): Promise<ProposalDetails>

Gets detailed information about a proposal.

getProposalVoteCount()
async getProposalVoteCount(proposalId: bigint): Promise<VoteCount>

Gets vote counts for a proposal.

getStakeInfo()
async getStakeInfo(userId: Uint8Array): Promise<StakeInfo>

Gets staking information for a user.

initializeProviders()

async function initializeProviders(logger: Logger): Promise<GovernorProviders>;

Initializes all required providers for governance operations.

What it does:

  1. Connects to Lace wallet extension
  2. Requests user authorization
  3. Retrieves wallet state and service URIs
  4. Configures all providers (IndexedDB, HTTP, Indexer, Wallet, Proof server)

Parameters:

  • logger: Pino logger instance

Returns: Configured providers object

Throws:

  • Error if Lace wallet not found
  • Error if wallet connection rejected
  • Error if incompatible wallet version

Example:

const providers = await initializeProviders(logger);

GovernorManager

High-level API with observable state management.

Constructor

constructor(
  deploymentConfig: DeploymentConfig,
  logger: Logger
)

Parameters:

  • deploymentConfig: Deployment configuration
  • logger: Pino logger instance

Example:

const manager = new GovernorManager(config, logger);

Methods

resolve()
resolve(contractAddress?: ContractAddress): Observable<GovernorDeployment>

Deploys a new contract or joins an existing one, with observable state.

Returns: Observable that emits deployment state changes

Deployment States:

{ status: 'in-progress' }
{ status: 'deployed', api: DeployedGovernorAPI }
{ status: 'failed', error: Error }

Error Handling

Custom Error Classes

The SDK provides specific error classes for different failure scenarios:

import {
  GovernorSDKError,
  WalletNotConnectedError,
  InsufficientStakeError,
  InvalidParameterError,
  ContractNotFoundError,
  ProposalNotFoundError,
  TransactionError,
  DeploymentError,
  InvalidConfigurationError,
  NetworkNotConfiguredError,
} from "@claritydao/midnight-sdk";

Error Handling Pattern

try {
  await api.createProposal(title, description, creator, actions);
} catch (error) {
  if (error instanceof InsufficientStakeError) {
    console.error("Need more stake:", error.message);
  } else if (error instanceof InvalidParameterError) {
    console.error("Invalid input:", error.message);
  } else if (error instanceof TransactionError) {
    console.error("Transaction failed:", error.message);
  } else {
    console.error("Unexpected error:", error);
  }
}

Validation

The SDK includes comprehensive input validation:

import { validators } from "@claritydao/midnight-sdk";

// Validate proposal parameters
validators.validateProposal(title, description, creator);

// Validate vote choice
validators.validateVoteChoice(choice);

// Validate deployment config
validators.validateDeploymentConfig(config);

// Validate governance config
validators.validateGovernanceConfig(governanceConfig);

Browser Integration

Critical: Frontend Setup Requirements

Before integrating this SDK into a browser application, you MUST configure your build tool correctly to handle WASM modules. Failure to do so will result in the error ocrt.maxField is not a function.

Required: Vite Configuration

IMPORTANT: Use Vite, not Next.js. Next.js 15's Turbopack does not support the webpack configurations required for this WASM pattern.

1. Install Required Vite Plugins

pnpm add -D vite vite-plugin-wasm vite-plugin-top-level-await @vitejs/plugin-react

2. Create vite.config.ts

This exact configuration is required. It's based on Midnight Network's official example-bboard implementation:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";

export default defineConfig({
  cacheDir: "./.vite",
  build: {
    target: "esnext",
    minify: false,
    rollupOptions: {
      output: {
        manualChunks: {
          // CRITICAL: Separate chunk for WASM modules
          wasm: ["@midnight-ntwrk/onchain-runtime"],
        },
      },
    },
    commonjsOptions: {
      // CRITICAL: Transform CommonJS to ESM
      transformMixedEsModules: true,
      extensions: [".js", ".cjs"],
      ignoreDynamicRequires: true,
    },
  },
  plugins: [
    react(),
    wasm(),
    topLevelAwait({
      promiseExportName: "__tla",
      promiseImportName: (i) => `__tla_${i}`,
    }),
    // CRITICAL: Custom resolver for WASM module handling
    {
      name: "wasm-module-resolver",
      resolveId(source, importer) {
        if (
          source === "@midnight-ntwrk/onchain-runtime" &&
          importer &&
          importer.includes("@midnight-ntwrk/compact-runtime")
        ) {
          return {
            id: source,
            external: false,
            moduleSideEffects: true,
          };
        }
        return null;
      },
    },
  ],
  optimizeDeps: {
    esbuildOptions: {
      target: "esnext",
      supported: { "top-level-await": true },
      platform: "browser",
      format: "esm",
      loader: { ".wasm": "binary" },
    },
    // CRITICAL: Pre-bundle for CJS→ESM conversion
    include: ["@midnight-ntwrk/compact-runtime"],
    // CRITICAL: Exclude WASM from optimization
    exclude: [
      "@midnight-ntwrk/onchain-runtime",
      "@midnight-ntwrk/onchain-runtime/midnight_onchain_runtime_wasm_bg.wasm",
      "@midnight-ntwrk/onchain-runtime/midnight_onchain_runtime_wasm.js",
    ],
  },
  resolve: {
    extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".wasm"],
    mainFields: ["browser", "module", "main"],
  },
});

Why This Configuration is Required

The SDK depends on @midnight-ntwrk/compact-runtime, which:

  1. Uses CommonJS (require())
  2. Imports @midnight-ntwrk/onchain-runtime (WASM module with top-level await)
  3. Executes ocrt.maxField() at the top level

This creates a CJS/ESM/WASM incompatibility that requires the custom resolver plugin and specific optimization settings.

Why Next.js Doesn't Work

Next.js 15 uses Turbopack by default, which doesn't support the webpack configurations needed:

// These webpack configs don't work with Turbopack:
config.experiments = {
  asyncWebAssembly: true,
  topLevelAwait: true,
};

Even with the --webpack flag, the CJS/ESM/WASM interaction is too complex for Next.js to handle correctly.

Recommendation: Use Vite for browser dApps (officially supported by Midnight Network).

React Example

import {
  GovernorManager,
  VoteChoice,
  ConfigPresets,
} from "@claritydao/midnight-sdk";
import { randomBytes } from "@midnight-ntwrk/midnight-js-utils";
import { useEffect, useState } from "react";
import pino from "pino";

const logger = pino({ level: "info" });

const config = {
  governor: {
    governance: ConfigPresets.testing(),
    executionDelay: BigInt(60),
    gracePeriod: BigInt(300),
    maxActiveProposals: BigInt(100),
    proposalLifetime: BigInt(3600),
    emergencyVetoEnabled: true,
    emergencyVetoThreshold: BigInt(75000),
  },
  token: {
    name: "Test DAO",
    symbol: "TDAO",
    decimals: BigInt(18),
  },
  adminKey: randomBytes(32), // CRITICAL: Use secure random bytes!
};

const manager = new GovernorManager(config, logger);

function GovernanceApp() {
  const [deployment, setDeployment] = useState(null);

  useEffect(() => {
    const subscription = manager.resolve().subscribe(setDeployment);
    return () => subscription.unsubscribe();
  }, []);

  if (!deployment || deployment.status === "in-progress") {
    return <div>Connecting to wallet and deploying contract...</div>;
  }

  if (deployment.status === "failed") {
    return <div>Error: {deployment.error.message}</div>;
  }

  const api = deployment.api;

  const handleCreateProposal = async () => {
    try {
      const actions = [
        {
          typ: 0,
          token: tokenAddress,
          amount: BigInt(1000),
          to: recipientAddress,
          configKey: new Uint8Array(32),
          configValue: new Uint8Array(32),
        },
      ];

      const { proposalId } = await api.createProposal(
        "My Proposal",
        "Description here",
        creatorAddress,
        actions
      );

      alert(`Proposal created: ${proposalId}`);
    } catch (error) {
      alert(`Error: ${error.message}`);
    }
  };

  const handleVote = async (proposalId) => {
    try {
      await api.castVote(proposalId, voterAddress, VoteChoice.For);
      alert("Vote cast successfully");
    } catch (error) {
      alert(`Error: ${error.message}`);
    }
  };

  return (
    <div>
      <h1>Governor Contract</h1>
      <p>Address: {api.deployedContractAddress}</p>

      <button onClick={handleCreateProposal}>Create Proposal</button>
      <button onClick={() => handleVote(1n)}>Vote For Proposal #1</button>
    </div>
  );
}

Security Best Practices

1. Always Use Secure Random for Admin Keys

import { randomBytes } from "@midnight-ntwrk/midnight-js-utils";

// ✅ Good: Cryptographically secure random
const adminKey = randomBytes(32);

// ❌ Bad: Predictable key
const adminKey = new Uint8Array(32); // All zeros - INSECURE!

2. Use Configuration Presets

// ✅ Good: Use tested presets
const config = ConfigPresets.production();

// ❌ Bad: Hardcoded magic numbers
const config = {
  votingPeriod: BigInt(300), // What does 300 mean?
  // ...
};

3. Validate User Inputs

import { validators } from "@claritydao/midnight-sdk";

// ✅ Good: Validate before submission
validators.validateProposal(title, description, creator);
await api.createProposal(title, description, creator, actions);

// ❌ Bad: No validation
await api.createProposal(title, description, creator, actions);

4. Handle Errors Gracefully

// ✅ Good: Specific error handling
try {
  await api.castVote(proposalId, voter, choice);
} catch (error) {
  if (error instanceof InsufficientStakeError) {
    showMessage("You need more stake to vote");
  } else {
    showMessage("An error occurred");
  }
}

// ❌ Bad: Silent failures
try {
  await api.castVote(proposalId, voter, choice);
} catch (error) {
  // Silent failure
}

Troubleshooting

Critical: WASM Initialization Error

Problem: TypeError: ocrt.maxField is not a function

Root Cause: CJS/ESM/WASM module incompatibility. The SDK depends on @midnight-ntwrk/compact-runtime (CommonJS) which requires @midnight-ntwrk/onchain-runtime (WASM with top-level await).

Solutions:

  1. Use Vite, not Next.js

    # Install Vite and required plugins
    pnpm add -D vite vite-plugin-wasm vite-plugin-top-level-await @vitejs/plugin-react
  2. Configure vite.config.ts correctly - See "Browser Integration" section above for complete configuration

  3. Key configuration requirements:

    • Custom wasm-module-resolver plugin
    • transformMixedEsModules: true in commonjsOptions
    • Manual chunks for WASM modules
    • Proper include/exclude in optimizeDeps

Error Location:

File: node_modules/@midnight-ntwrk/compact-runtime/dist/runtime.js:90
Code: exports.MAX_FIELD = ocrt.maxField();

Why Next.js Doesn't Work:

  • Next.js 15 uses Turbopack which doesn't support required webpack configurations
  • Even with --webpack flag, the CJS/ESM/WASM interaction is too complex

Reference Implementations:

  • Working example: Midnight Network's example-bboard application
  • Configuration tested and verified in production

Admin Key Security Error

Problem: "Insecure admin key detected" or predictable deployment behavior

Root Cause: Using an all-zeros Uint8Array for admin key

Bad Example:

const adminKey = new Uint8Array(32); // All zeros - INSECURE!

Correct Solution:

import { randomBytes } from "@midnight-ntwrk/midnight-js-utils";

const adminKey = randomBytes(32); // Cryptographically secure

Why It Matters:

  • Admin keys control contract upgrades and governance
  • Predictable keys can be exploited
  • All production deployments MUST use secure random keys

Build Configuration Issues

Problem: "require is not defined" in browser

Root Cause: Contract module uses CommonJS but wasn't transformed to ESM

Solution:

// In vite.config.ts
commonjsOptions: {
  transformMixedEsModules: true,
  extensions: ['.js', '.cjs'],
}

Problem: "Top-level await is not available"

Root Cause: WASM module with top-level await in wrong bundle chunk

Solution:

// In vite.config.ts
rollupOptions: {
  output: {
    manualChunks: {
      wasm: ['@midnight-ntwrk/onchain-runtime'],
    },
  },
}

Problem: Module optimization errors during build

Root Cause: Vite trying to optimize WASM and CJS modules together

Solution:

// In vite.config.ts
optimizeDeps: {
  include: ['@midnight-ntwrk/compact-runtime'],  // Pre-bundle for CJS→ESM
  exclude: [
    '@midnight-ntwrk/onchain-runtime',           // Don't optimize WASM
    '@claritydao/midnight-agora-contracts',              // Don't optimize contract
  ],
}

Wallet Connection Issues

Problem: "Could not find Midnight Lace wallet"

Solutions:

  • Install Lace wallet extension from lace.io
  • Refresh the browser page
  • Check browser console for errors
  • Ensure extension is enabled

Problem: "Incompatible version of Midnight Lace wallet"

Solutions:

  • Update Lace extension to latest version
  • SDK requires Lace API v1.x
  • Check compatibility in browser console

Transaction Failures

Problem: InsufficientStakeError

Solutions:

  • Register stake before creating proposals
  • Ensure stake amount ≥ proposalThreshold
  • Check staking period hasn't expired

Problem: TransactionError: "User rejected transaction"

Solutions:

  • User must approve transaction in Lace wallet
  • Check wallet has sufficient tDUST balance
  • Verify transaction parameters are correct

Build Errors

Problem: TypeScript compilation errors

Solutions:

# Update dependencies
pnpm install

# Rebuild SDK
pnpm run build

Problem: Module not found errors

Solutions:

  • Ensure all dependencies are installed
  • Check node_modules exists
  • Verify import paths are correct

Runtime Errors

Problem: "Contract not found at address"

Solutions:

  • Verify contract address is correct
  • Check network connection
  • Ensure contract is deployed on current network

Problem: "InvalidParameterError"

Solutions:

  • Check parameter types and formats
  • Validate inputs before submission
  • Review API documentation for correct usage

Cache Issues

Problem: Changes not reflected after rebuild

Solutions:

# Clear Vite cache
rm -rf node_modules/.vite

# Clear browser cache or hard refresh
# Chrome/Edge: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)
# Firefox: Ctrl+F5 (Windows) or Cmd+Shift+R (Mac)

Development

Building from Source

# Clone repository
git clone https://github.com/your-org/agora-dao-midnight-sdk
cd internalAgoraMidnightSDK

# Install dependencies
pnpm install

# Build contract first
cd contract
pnpm run build

# Build SDK
cd ../sdk
pnpm run build

Project Structure

sdk/
├── src/
│   ├── browser-providers.ts    # Lace wallet integration
│   ├── governor-api.ts          # Main API implementation
│   ├── manager.ts               # Observable state manager
│   ├── types.ts                 # Type definitions
│   ├── errors.ts                # Error classes
│   ├── validators.ts            # Input validation
│   ├── common-types.ts          # Shared types
│   ├── index.ts                 # Main exports
│   └── utils/                   # Utility functions
├── package.json
├── tsconfig.json
└── README.md

Prerequisites

Required Software

  1. Lace Wallet Extension

    • Install from lace.io
    • Create or import wallet
    • Switch to Midnight TestNet
  2. TestNet Tokens (tDUST)

  3. Modern Browser

    • Chrome/Edge 89+
    • Firefox 89+
    • Safari 15+

Browser APIs Required

  • IndexedDB (private state storage)
  • Web Crypto API (cryptographic operations)
  • Fetch API (HTTP requests)
  • WebSocket (real-time updates)

Resources

License

Apache-2.0

Support

For issues and questions: