@lerna-labs/ekklesia-helpers
v1.1.0
Published
Shared helper functions for the Ekklesia platform
Readme
@lerna-labs/ekklesia-helpers
Shared helper functions for the Ekklesia platform — deduplicated from the proposals and voting modules into a single, typed TypeScript library.
Installation
npm install @lerna-labs/ekklesia-helpersUsage
Import from the root or from a specific subpath:
// Root import
import { validateAddress, verifySignature } from "@lerna-labs/ekklesia-helpers";
// Subpath imports (better tree shaking)
import { validateAddress } from "@lerna-labs/ekklesia-helpers/validation";
import { verifySignature } from "@lerna-labs/ekklesia-helpers/crypto";
import { verifyToken } from "@lerna-labs/ekklesia-helpers/auth";
import { fetchName, fetchIdentity, verifyDeposit } from "@lerna-labs/ekklesia-helpers/cardano";
import { connectToDatabase, loadRoutes } from "@lerna-labs/ekklesia-helpers/server";
import { canonicalize, canonicalBytes } from "@lerna-labs/ekklesia-helpers/json";Example: Validate a Cardano address
import { validateAddress } from "@lerna-labs/ekklesia-helpers/validation";
const result = validateAddress("stake1uxmeqz...", "stake");
if (typeof result === "string") {
console.log("Valid stake address:", result);
}Example: Verify a COSE signature
import { verifySignature } from "@lerna-labs/ekklesia-helpers/crypto";
const isValid = await verifySignature(payload, address, signatureObject);Example: Resolve a Cardano identity
import { fetchName, fetchIdentity } from "@lerna-labs/ekklesia-helpers/cardano";
// Simple name lookup
await fetchName("pool1qqqqqdk4zh..."); // "ATADA"
await fetchName("drep1y2200we9c9..."); // "YUTA"
await fetchName("stake1uxekfkqgs4..."); // "426"
// Rich identity with metadata
await fetchIdentity("pool1qqqqqdk4zh...");
// { displayName: "ATADA", fullName: "ATADA Stakepool in Austria", description: "...", homepage: "https://stakepool.at", type: "pool" }API
Validation (@lerna-labs/ekklesia-helpers/validation)
| Export | Description |
| ----------------- | ------------------------------------------------------------------------ |
| validateAddress | Validates Cardano addresses (bech32, hex, DRep CIP-105/CIP-129, calidus) |
| getAddressType | Returns address type, key hash, and hash type from a bech32 address |
| pubKeyToBech32 | Converts a hex public key to a bech32 address (DRep or calidus) |
| extractParts | Extracts header byte and body from a hex-encoded address |
| sanitizeInput | Sanitizes user input (strips HTML tags/entities, preserves URLs) |
Crypto (@lerna-labs/ekklesia-helpers/crypto)
| Export | Description |
| -------------------------- | -------------------------------------------------------------------- |
| verifySignature | Verifies Ed25519 and COSE Sign1 signatures against a Cardano address |
| isPartyToScript | Checks if an address is a signatory of a native script |
| getScriptCriteria | Extracts required signature criteria from a native script |
| validateScriptSignatures | Validates multiple signatures against a native script's requirements |
Auth (@lerna-labs/ekklesia-helpers/auth)
| Export | Description |
| ------------- | -------------------------------------------------------- |
| verifyToken | Verifies JWT tokens from cookies or Authorization header |
Cardano (@lerna-labs/ekklesia-helpers/cardano)
| Export | Description |
| ------------------- | ------------------------------------------------------------------ |
| getScript | Fetches a native script by script hash |
| fetchCalidusKey | Fetches the calidus (pool cold) key for a stake pool |
| fetchDrepName | Fetches the on-chain registered name for a DRep |
| validateDrep | Validates a DRep ID and returns its registration status |
| fetchHandle | Resolves an ADA Handle for an address (Handle.me + provider) |
| fetchTxInfo | Fetches detailed transaction info |
| fetchPoolTicker | Fetches a stake pool's ticker symbol |
| fetchPoolMetadata | Fetches full pool metadata (ticker, name, description, homepage) |
| fetchName | Resolves a human-readable name for any bech32 identifier |
| fetchIdentity | Returns rich identity metadata (displayName, type, description...) |
| verifyDeposit | Verifies transaction deposits and treasury donations on-chain |
Server (@lerna-labs/ekklesia-helpers/server)
| Export | Description |
| --------------------------- | --------------------------------------------------------- |
| initializeConsole | Overrides console methods with colored log-level prefixes |
| resetConsole | Restores original console behavior |
| connectToDatabase | Connects to MongoDB with auto-reconnect |
| disconnectFromDatabase | Gracefully disconnects from MongoDB |
| checkDatabaseConnection | Returns current database connection status |
| isDatabaseConnected | Boolean check for active database connection |
| checkDatabaseConnectionMW | Express middleware that returns 503 if database is down |
| loadEnvironmentVariables | Loads .env.{NODE_ENV} files with fallback to .env |
| loadRoutes | Recursively loads Express route files from a directory |
Canonical JSON (@lerna-labs/ekklesia-helpers/json)
| Export | Description |
| ---------------- | ------------------------------------------------------------------ |
| canonicalize | Serializes a value to canonical JSON (RFC 8785 / JCS), sorted keys |
| canonicalBytes | Returns the UTF-8 bytes of canonicalize(value) (a Uint8Array) |
canonicalize produces a deterministic, whitespace-free string that is
byte-for-byte stable regardless of key insertion order — it is the canonical
byte target for signing and hashing (e.g. blake2b voteHash / merkleRoot).
The output follows RFC 8785 so that
implementations in other languages interoperate. Object keys are sorted by
UTF-16 code unit, array order is preserved, and non-finite numbers
(NaN/Infinity) are rejected with a TypeError.
import { canonicalize, canonicalBytes } from "@lerna-labs/ekklesia-helpers/json";
canonicalize({ b: 1, a: 2 }); // '{"a":2,"b":1}'
canonicalBytes({ b: 1, a: 2 }); // Uint8Array of the UTF-8 bytes, ready to hashEnvironment Variables
The Cardano helpers require at least one provider configured:
| Variable | Description |
| ----------------------- | -------------------------------------------------------------- |
| API_URL | Koios API base URL |
| API_TOKEN | Koios API bearer token |
| BLOCKFROST_URL | Blockfrost API base URL (no trailing slash) |
| BLOCKFROST_PROJECT_ID | Blockfrost project ID |
| NETWORK_NAME | Network name (mainnet or preprod) |
| PRIMARY_PROVIDER | Preferred provider (koios or blockfrost, default: koios) |
If both providers are configured, the secondary is used as automatic fallback.
The auth helper (verifyToken) requires:
| Variable | Description |
| ------------ | ---------------------- |
| JWT_SECRET | Secret for JWT signing |
Development
npm install # Install dependencies
npm run build # Compile TypeScript
npm run lint # ESLint + Prettier check
npm run lint:fix # Auto-fix lint issues
npm run test # Run tests
npm run test:coverage # Run tests with coverage
npm run docs # Generate API documentationLive Tests
Live integration tests run against real mainnet APIs. Copy .local.env.example to .local.env and fill in your credentials (at least one provider required):
cp .local.env.example .local.env
# Edit .local.env with your API credentials
source .local.env && LIVE_TEST=true npx vitest run --reporter=verbose src/cardano/cardanoApi.live.test.tsContributing
- Create a feature branch from
main - Ensure
npm run lint && npm run test:coverage && npm run buildall pass - Open a pull request
