From 98d4c4e9d83a3d3b931842a549d0cb342d88fd6b Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 2 Jun 2026 14:06:28 +0300 Subject: [PATCH 01/65] fix(#1): add configurable security middleware Add security middleware with configurable settings via environment variables: - CORS: Cross-origin resource sharing with configurable allowed origins - Rate limiting: Configurable window and max requests (default: 100/15min) - Body size limits: Configurable max request body size (default: 1mb) - Optional API key authentication: When enabled, requires X-API-Key header All settings are optional with sensible defaults. Public endpoints (/health, /api-docs, /docs) bypass authentication when enabled. New files: - src/middleware/security.ts: Security middleware implementation Modified files: - src/server.ts: Apply security middleware after express.json() - .env.example: Document new security environment variables - package.json: Add cors and express-rate-limit dependencies Resolves: #1 --- .env.example | 16 ++++++ package.json | 3 ++ src/middleware/security.ts | 103 +++++++++++++++++++++++++++++++++++++ src/server.ts | 8 ++- 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/middleware/security.ts diff --git a/.env.example b/.env.example index ab20dcc4..1eb5bf88 100644 --- a/.env.example +++ b/.env.example @@ -28,3 +28,19 @@ IAGON_API_KEY= # ── Cardano Network ────────────────────────────────────────── # Valid values: mainnet, preprod (defaults to mainnet) CARDANO_NETWORK=mainnet + +# ── Security (Optional) ───────────────────────────────────── +# Max request body size (default: 1mb) +MAX_BODY_SIZE=1mb + +# Rate limiting: requests per window (default: 100 requests per 15 min) +RATE_LIMIT_WINDOW_MS=900000 +RATE_LIMIT_MAX_REQUESTS=100 + +# CORS: comma-separated list of allowed origins, or * for all (default: *) +CORS_ORIGINS=* + +# API Key authentication (default: disabled) +# When enabled, all requests (except /health, /api-docs, /docs) require X-API-Key header +API_KEY_ENABLED=false +API_KEY= diff --git a/package.json b/package.json index 7c9d85b4..b07b385f 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "description": "Cardano SDK for Fireblocks MPC custody", "devDependencies": { "@eslint/js": "^10.0.1", + "@types/cors": "^2.8.19", "@types/express": "^5.0.6", "@types/jest": "^30.0.0", "@types/node": "^22.14.1", @@ -56,8 +57,10 @@ "bech32": "^2.0.0", "blakejs": "^1.2.1", "cbor2": "^2.0.1", + "cors": "^2.8.6", "dotenv": "^16.5.0", "express": "^5.1.0", + "express-rate-limit": "^8.5.1", "jose": "^6.1.3", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.1", diff --git a/src/middleware/security.ts b/src/middleware/security.ts new file mode 100644 index 00000000..e0938905 --- /dev/null +++ b/src/middleware/security.ts @@ -0,0 +1,103 @@ +/** + * Security middleware for the Cardano Raw SDK API server. + * + * Provides configurable security features: + * - CORS (Cross-Origin Resource Sharing) + * - Rate limiting + * - Optional API key authentication + * - Request body size limits + * + * All settings are configurable via environment variables to allow + * clients to customize security based on their deployment requirements. + */ + +import type { Express, Request, Response, NextFunction } from "express"; +import rateLimit from "express-rate-limit"; +import cors from "cors"; + +export interface SecurityConfig { + maxBodySize: string; + rateLimitWindowMs: number; + rateLimitMaxRequests: number; + corsOrigins: string | string[]; + apiKeyEnabled: boolean; + apiKey?: string; +} + +/** + * Load security configuration from environment variables. + * All values have sensible defaults for development. + */ +const getConfig = (): SecurityConfig => ({ + maxBodySize: process.env.MAX_BODY_SIZE || "1mb", + rateLimitWindowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || "900000", 10), // 15 minutes + rateLimitMaxRequests: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || "100", 10), + corsOrigins: process.env.CORS_ORIGINS ? process.env.CORS_ORIGINS.split(",") : "*", + apiKeyEnabled: process.env.API_KEY_ENABLED === "true", + apiKey: process.env.API_KEY, +}); + +/** + * Apply security middleware to the Express application. + * + * Order of middleware: + * 1. CORS - Handle cross-origin requests + * 2. Rate limiting - Prevent abuse + * 3. API key auth (optional) - Require authentication header + * + * @param app - Express application instance + */ +export const applySecurityMiddleware = (app: Express): void => { + const config = getConfig(); + + // CORS configuration + app.use( + cors({ + origin: config.corsOrigins, + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allowedHeaders: ["Content-Type", "Authorization", "X-API-Key"], + credentials: true, + }) + ); + + // Rate limiting + app.use( + rateLimit({ + windowMs: config.rateLimitWindowMs, + max: config.rateLimitMaxRequests, + message: { success: false, error: "Too many requests, please try again later" }, + standardHeaders: true, + legacyHeaders: false, + // Skip rate limiting for health checks + skip: (req: Request) => req.path === "/health", + }) + ); + + // Optional API key authentication + if (config.apiKeyEnabled && config.apiKey) { + app.use((req: Request, res: Response, next: NextFunction) => { + // Skip auth for health check and documentation endpoints + const publicPaths = ["/health", "/api-docs", "/docs"]; + if (publicPaths.some((p) => req.path.startsWith(p))) { + return next(); + } + + const providedKey = req.headers["x-api-key"]; + if (providedKey !== config.apiKey) { + return res.status(401).json({ + success: false, + error: "Invalid or missing API key", + code: "UNAUTHORIZED", + }); + } + + next(); + }); + } +}; + +/** + * Get the configured max body size for use in express.json() middleware. + * @returns Max body size string (e.g., "1mb", "100kb") + */ +export const getMaxBodySize = (): string => process.env.MAX_BODY_SIZE || "1mb"; diff --git a/src/server.ts b/src/server.ts index a3ba5ffd..bf29c126 100644 --- a/src/server.ts +++ b/src/server.ts @@ -6,6 +6,7 @@ import express, { Request, Response } from "express"; import { config, Logger } from "./utils/index.js"; import { getSwaggerSpec, swaggerUi } from "./utils/swagger.js"; +import { applySecurityMiddleware, getMaxBodySize } from "./middleware/security.js"; import { SdkManager } from "./pool/sdkManager.js"; import { configureRouter } from "./api/router.js"; import { FireblocksCardanoRawSDK } from "./FireblocksCardanoRawSDK.js"; @@ -49,6 +50,7 @@ const startServer = () => { // Configure middlewares with raw body preservation for webhook endpoint app.use( express.json({ + limit: getMaxBodySize(), verify: (req, _res, buf, _encoding) => { // Preserve raw body for webhook signature verification const r = req as Request & { url?: string; rawBody?: Buffer }; @@ -58,7 +60,11 @@ const startServer = () => { }, }) ); - app.use(express.urlencoded({ extended: true })); + app.use(express.urlencoded({ extended: true, limit: getMaxBodySize() })); + + // Apply security middleware (CORS, rate limiting, optional API key auth) + applySecurityMiddleware(app); + app.use(errorHandler); // Initialize base config for Fireblocks From 621a695ff39f1d500fc1bbc51833aa7626b63c87 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 2 Jun 2026 14:08:18 +0300 Subject: [PATCH 02/65] fix(#2): add fee parameter validation with bounds Add bounds validation to prevent loss-of-funds from excessive fees: - MIN_PROTOCOL_FEE: 155,381 lovelace (Cardano protocol minFeeB) - MAX_REASONABLE_FEE: 10,000,000 lovelace (10 ADA) The 10 ADA upper bound is sufficient for even complex multi-asset transactions while protecting against accidental high fee values that could drain funds. Applied to all schemas accepting optional fee parameter: - delegateToDRepRequestSchema - registerAsDRepRequestSchema - castVoteRequestSchema Uses a reusable optionalFeeSchema for consistency. --- src/api/validation.ts | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/api/validation.ts b/src/api/validation.ts index 74bfc529..42c7264b 100644 --- a/src/api/validation.ts +++ b/src/api/validation.ts @@ -1,6 +1,27 @@ import { z } from "zod"; import { Request, Response, NextFunction } from "express"; +import { CardanoConstants } from "../constants.js"; + +/** + * Fee validation bounds. + * MIN_PROTOCOL_FEE: Minimum fee per Cardano protocol (minFeeB constant) + * MAX_REASONABLE_FEE: Upper bound to prevent accidental loss-of-funds (10 ADA) + */ +const MIN_PROTOCOL_FEE = CardanoConstants.MIN_FEE_B; // 155,381 lovelace +const MAX_REASONABLE_FEE = 10_000_000; // 10 ADA - covers even complex multi-asset tx + +/** + * Reusable schema for optional fee parameter with bounds validation. + * Prevents loss-of-funds from excessive fees while ensuring minimum protocol requirements. + */ +const optionalFeeSchema = z + .number() + .int("fee must be an integer") + .min(MIN_PROTOCOL_FEE, `fee must be at least ${MIN_PROTOCOL_FEE} lovelace (Cardano minimum)`) + .max(MAX_REASONABLE_FEE, `fee cannot exceed ${MAX_REASONABLE_FEE} lovelace (10 ADA)`) + .optional(); + /** * Generic validation middleware that validates request body against a Zod schema * @param schema - Zod schema to validate against @@ -314,7 +335,7 @@ export const delegateToDRepRequestSchema = z "drepId must be bech32 (drep1... or drep_script1...) or a 56-character hex string" ) .optional(), - fee: z.number().int().positive().optional(), + fee: optionalFeeSchema, }) .refine((data) => data.drepAction !== "custom-drep" || !!data.drepId, { message: "drepId is required when drepAction is custom-drep", @@ -339,7 +360,7 @@ export const registerAsDRepRequestSchema = z.object({ }) .optional(), depositAmount: z.number().int().positive().optional(), - fee: z.number().int().positive().optional(), + fee: optionalFeeSchema, }); export type RegisterAsDRepRequest = z.infer; @@ -367,7 +388,7 @@ export const castVoteRequestSchema = z.object({ }), vote: z.enum(["yes", "no", "abstain"], { message: 'vote must be "yes", "no", or "abstain"' }), anchor: anchorSchema.optional(), - fee: z.number().int().positive().optional(), + fee: optionalFeeSchema, }); export type CastVoteRequest = z.infer; From 04359398b997b29572bab9c1599a8407f92fb4fd Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 2 Jun 2026 14:13:08 +0300 Subject: [PATCH 03/65] fix(#5): make protocol parameters configurable Add support for overriding Cardano protocol parameters at SDK initialization to handle hard fork changes without requiring an SDK update. New ProtocolParams interface with configurable values: - minFeeA: Fee coefficient (lovelace per tx byte) - minFeeB: Fee constant (base lovelace fee) - coinsPerUtxoByte: For min-ada calculations - stakeKeyDeposit: Stake key registration deposit - drepDeposit: DRep registration deposit Usage: const sdk = await FireblocksCardanoRawSDK.createInstance({ ...config, protocolParams: { minFeeA: 44, minFeeB: 155381 } }); New files: - src/utils/protocolParams.ts: Protocol params singleton with get/set/reset Modified files: - src/types/config.ts: Add ProtocolParams interface - src/FireblocksCardanoRawSDK.ts: Accept protocolParams in createInstance - src/api/validation.ts: Use configurable minFeeB for fee validation - src/utils/index.ts: Export protocolParams functions - .env.example: Document protocol param environment variables --- .env.example | 11 ++++++ src/FireblocksCardanoRawSDK.ts | 20 ++++++++++ src/api/validation.ts | 15 ++++--- src/types/config.ts | 18 +++++++++ src/utils/index.ts | 1 + src/utils/protocolParams.ts | 72 ++++++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 src/utils/protocolParams.ts diff --git a/.env.example b/.env.example index 1eb5bf88..eb2aa521 100644 --- a/.env.example +++ b/.env.example @@ -44,3 +44,14 @@ CORS_ORIGINS=* # When enabled, all requests (except /health, /api-docs, /docs) require X-API-Key header API_KEY_ENABLED=false API_KEY= + +# ── Protocol Parameters (Optional) ────────────────────────── +# Override Cardano protocol parameters if they change at a hard fork. +# Only set these if you need to override the SDK defaults. +# These are passed to SDK via createInstance({ protocolParams: {...} }) +# +# MIN_FEE_A=44 # Fee coefficient: lovelace per tx byte +# MIN_FEE_B=155381 # Fee constant: base lovelace fee +# COINS_PER_UTXO_BYTE=4310 # For min-ada calculations +# STAKE_KEY_DEPOSIT=2000000 # Stake key registration deposit +# DREP_DEPOSIT=500000000 # DRep registration deposit diff --git a/src/FireblocksCardanoRawSDK.ts b/src/FireblocksCardanoRawSDK.ts index fc6ec2ee..08a60c9f 100644 --- a/src/FireblocksCardanoRawSDK.ts +++ b/src/FireblocksCardanoRawSDK.ts @@ -28,6 +28,7 @@ import { parseAdaStringToLovelace, getStakeAddressFromBaseAddress, utxoLocks, + setProtocolParams, } from "./utils/index.js"; import { @@ -168,6 +169,18 @@ export class FireblocksCardanoRawSDK { assetCacheTTL?: number; /** Disable SSL certificate verification (use only in development) */ disableSslVerification?: boolean; + /** + * Override Cardano protocol parameters. + * Use this to adapt to protocol changes without waiting for an SDK update. + * Only specified fields will be overridden; others use defaults. + */ + protocolParams?: { + minFeeA?: number; + minFeeB?: number; + coinsPerUtxoByte?: number; + stakeKeyDeposit?: number; + drepDeposit?: number; + }; }): Promise => { try { const logger = new Logger(`app:fireblocks-cardano-raw-sdk`); @@ -179,8 +192,15 @@ export class FireblocksCardanoRawSDK { iagonApiKey, assetCacheTTL, disableSslVerification = false, + protocolParams, } = params; + // Apply custom protocol parameters if provided + if (protocolParams) { + setProtocolParams(protocolParams); + logger.info("Custom protocol parameters applied", protocolParams); + } + if (network === Networks.PREVIEW) { throw new Error(`Unsupported network: ${network}`); } diff --git a/src/api/validation.ts b/src/api/validation.ts index 42c7264b..61152a19 100644 --- a/src/api/validation.ts +++ b/src/api/validation.ts @@ -1,25 +1,30 @@ import { z } from "zod"; import { Request, Response, NextFunction } from "express"; -import { CardanoConstants } from "../constants.js"; +import { getProtocolParams } from "../utils/protocolParams.js"; /** * Fee validation bounds. - * MIN_PROTOCOL_FEE: Minimum fee per Cardano protocol (minFeeB constant) + * MIN_PROTOCOL_FEE: Minimum fee per Cardano protocol (minFeeB constant, configurable) * MAX_REASONABLE_FEE: Upper bound to prevent accidental loss-of-funds (10 ADA) */ -const MIN_PROTOCOL_FEE = CardanoConstants.MIN_FEE_B; // 155,381 lovelace +const getMinProtocolFee = () => getProtocolParams().minFeeB; // Default: 155,381 lovelace const MAX_REASONABLE_FEE = 10_000_000; // 10 ADA - covers even complex multi-asset tx /** * Reusable schema for optional fee parameter with bounds validation. * Prevents loss-of-funds from excessive fees while ensuring minimum protocol requirements. + * Note: Uses a function to get min fee to support configurable protocol params. */ const optionalFeeSchema = z .number() .int("fee must be an integer") - .min(MIN_PROTOCOL_FEE, `fee must be at least ${MIN_PROTOCOL_FEE} lovelace (Cardano minimum)`) - .max(MAX_REASONABLE_FEE, `fee cannot exceed ${MAX_REASONABLE_FEE} lovelace (10 ADA)`) + .refine((fee) => fee >= getMinProtocolFee(), { + message: `fee must be at least the protocol minimum (minFeeB)`, + }) + .refine((fee) => fee <= MAX_REASONABLE_FEE, { + message: `fee cannot exceed ${MAX_REASONABLE_FEE} lovelace (10 ADA)`, + }) .optional(); /** diff --git a/src/types/config.ts b/src/types/config.ts index 3c223a4b..2dcb6399 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -6,6 +6,24 @@ export interface PoolConfig { retryAttempts: number; } +/** + * Cardano protocol parameters that may change at hard forks. + * These can be overridden via SDK configuration to adapt to protocol updates + * without waiting for an SDK release. + */ +export interface ProtocolParams { + /** Fee coefficient A: lovelace per transaction byte (default: 44) */ + minFeeA: number; + /** Fee constant B: base lovelace fee (default: 155381) */ + minFeeB: number; + /** Lovelace per UTxO byte for min-ada calculations (default: 4310) */ + coinsPerUtxoByte: number; + /** Stake key registration/deregistration deposit (default: 2_000_000) */ + stakeKeyDeposit: number; + /** DRep registration deposit (default: 500_000_000) */ + drepDeposit: number; +} + export interface SdkManagerMetrics { totalInstances: number; activeInstances: number; diff --git a/src/utils/index.ts b/src/utils/index.ts index 119301a9..f31b442b 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -8,4 +8,5 @@ export * from "./logger.js"; export * from "./sanitizer.js"; export * from "./staking.utils.js"; export * from "./utxoLock.js"; +export * from "./protocolParams.js"; // NOTE: swagger.js is NOT exported here - it's only used by server.ts diff --git a/src/utils/protocolParams.ts b/src/utils/protocolParams.ts new file mode 100644 index 00000000..b279d9df --- /dev/null +++ b/src/utils/protocolParams.ts @@ -0,0 +1,72 @@ +/** + * Protocol Parameters Management + * + * Provides configurable Cardano protocol parameters with sensible defaults. + * Parameters can be overridden at SDK initialization to adapt to protocol + * changes without requiring an SDK update. + * + * Usage: + * // At SDK init (optional) + * setProtocolParams({ minFeeA: 44, minFeeB: 155381 }); + * + * // Throughout codebase + * const { minFeeA, minFeeB } = getProtocolParams(); + */ + +import { ProtocolParams } from "../types/config.js"; +import { CardanoConstants, CardanoAmounts } from "../constants.js"; + +/** + * Default protocol parameters based on current Cardano mainnet values. + * These serve as fallbacks when no custom params are provided. + */ +const DEFAULT_PROTOCOL_PARAMS: ProtocolParams = { + minFeeA: CardanoConstants.MIN_FEE_A, // 44 + minFeeB: CardanoConstants.MIN_FEE_B, // 155381 + coinsPerUtxoByte: CardanoAmounts.COINS_PER_UTXO_BYTE, // 4310 + stakeKeyDeposit: CardanoAmounts.DEPOSIT_AMOUNT, // 2_000_000 + drepDeposit: CardanoAmounts.DREP_REGISTRATION_DEPOSIT, // 500_000_000 +}; + +/** + * Current protocol parameters (singleton). + * Starts with defaults, can be updated via setProtocolParams(). + */ +let currentParams: ProtocolParams = { ...DEFAULT_PROTOCOL_PARAMS }; + +/** + * Get current protocol parameters. + * Returns a copy to prevent accidental mutation. + */ +export const getProtocolParams = (): Readonly => { + return { ...currentParams }; +}; + +/** + * Update protocol parameters. + * Merges provided values with current params (partial update supported). + * + * @param params - Partial protocol parameters to update + */ +export const setProtocolParams = (params: Partial): void => { + currentParams = { + ...currentParams, + ...params, + }; +}; + +/** + * Reset protocol parameters to defaults. + * Useful for testing or when switching networks. + */ +export const resetProtocolParams = (): void => { + currentParams = { ...DEFAULT_PROTOCOL_PARAMS }; +}; + +/** + * Get the default protocol parameters (immutable). + * Useful for reference or resetting to known values. + */ +export const getDefaultProtocolParams = (): Readonly => { + return { ...DEFAULT_PROTOCOL_PARAMS }; +}; From a5843433705c3c12fdc0b68b2177b90f6e11f0b8 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 2 Jun 2026 14:24:57 +0300 Subject: [PATCH 04/65] fix(#6): propagate fetchUtxos API errors instead of swallowing them Previously, fetchUtxos silently returned empty arrays when: - Network errors occurred (catch block) - API returned success=false This masked real errors, making debugging difficult and causing downstream code to incorrectly assume the address had no UTXOs. Now throws SdkApiError with: - Descriptive error message including the address - Error type: UTXO_FETCH_ERROR - Service: iagon-api Empty UTXO results from a successful API call (address has no UTXOs) still return [] as this is a valid state. --- src/utils/cardano.ts | 66 +++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/src/utils/cardano.ts b/src/utils/cardano.ts index 15076ebc..79971318 100644 --- a/src/utils/cardano.ts +++ b/src/utils/cardano.ts @@ -28,6 +28,7 @@ import { fetchAndSelectUtxosForAdaParams, fetchAndSelectUtxosForMultiTokenParams, SupportedAssets, + SdkApiError, } from "../types/index.js"; import { Logger } from "./logger.js"; import { CardanoAmounts, CardanoConstants } from "../constants.js"; @@ -252,37 +253,52 @@ export const fetchUtxos = async ( iagonApiService: IagonApiService, address: string ): Promise => { - try { - logger.info(`Fetching UTXOs for address: ${address}`); - const response = await iagonApiService.getUtxosByAddress(address); + logger.info(`Fetching UTXOs for address: ${address}`); - logger.info(`API Response:`, JSON.stringify(response, null, 2)); + let response; + try { + response = await iagonApiService.getUtxosByAddress(address); + } catch (error: unknown) { + // Propagate network/API errors instead of swallowing them + const message = error instanceof Error ? error.message : String(error); + logger.error(`Error fetching UTXOs for ${address}: ${message}`); + throw new SdkApiError( + `Failed to fetch UTXOs for address ${address}: ${message}`, + 500, + "UTXO_FETCH_ERROR", + { address }, + "iagon-api" + ); + } - if (response.success) { - const utxos = response.data; + logger.info(`API Response:`, JSON.stringify(response, null, 2)); + + // Propagate API errors instead of returning empty array + if (!response.success) { + logger.error(`API returned success=false for address: ${address}`); + throw new SdkApiError( + `Failed to fetch UTXOs for address ${address}: API returned unsuccessful response`, + 500, + "UTXO_FETCH_ERROR", + { address }, + "iagon-api" + ); + } - if (!utxos || utxos.length === 0) { - logger.warn(`No UTXOs found for address: ${address}`); - return []; - } + const utxos = response.data; - logger.info(`Found ${utxos.length} UTXOs`); - if (utxos.length > 0) { - logger.info(`Sample UTXO assets:`, JSON.stringify(utxos[0].value.assets, null, 2)); - } - - return utxos; - } else { - logger.warn(`API returned success=false for address: ${address}`); - return []; - } - } catch (error: unknown) { - logger.error( - `Error fetching UTXOs for ${address}: ${error instanceof Error ? error.message : String(error)}`, - error instanceof Error ? error.stack : undefined - ); + // Empty result is valid (address has no UTXOs) - return empty array + if (!utxos || utxos.length === 0) { + logger.warn(`No UTXOs found for address: ${address}`); return []; } + + logger.info(`Found ${utxos.length} UTXOs`); + if (utxos.length > 0) { + logger.info(`Sample UTXO assets:`, JSON.stringify(utxos[0].value.assets, null, 2)); + } + + return utxos; }; export const calculateTokenAmount = ( From e47651eb514f20d83b3dd6ace97bce6da50b4dcc Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 2 Jun 2026 14:26:22 +0300 Subject: [PATCH 05/65] fix(#7): document process-local UTXO lock limitation Add documentation about the in-memory UTXO locking mechanism and its limitations for multi-replica deployments. Key points documented: - UTXO locks are process-local (in-memory) - Multi-replica deployments may experience double-spend attempts - Mitigation strategies: sticky sessions, external locking, single replica New section "Deployment Considerations" added before Security Considerations with code example showing automatic UTXO locking behavior. --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 7bcf6069..90da5eae 100644 --- a/README.md +++ b/README.md @@ -1420,6 +1420,33 @@ await sdk.shutdown(); // Clean up resources and clear cache - Verify all required environment variables are set - Check Docker logs: `docker-compose logs -f` +## Deployment Considerations + +### UTXO Locking Limitation + +The SDK uses **in-memory UTXO locking** to prevent double-spend within a single process. When a UTXO is selected for a transaction, it is temporarily locked (default: 2 minutes) to prevent concurrent transactions from selecting the same UTXO. + +**Important for multi-replica deployments:** + +This in-memory lock only works within a single Node.js process. If you run multiple replicas/instances of the API service (e.g., in Kubernetes, Docker Swarm, or behind a load balancer), concurrent requests to different replicas may select the same UTXO, causing one transaction to fail. + +**Mitigation strategies:** + +1. **Sticky Sessions**: Route requests for the same vault account to the same replica using session affinity (e.g., based on `vaultAccountId`) + +2. **External Locking**: Implement distributed locking using Redis, a database, or another external coordination service + +3. **Single Replica**: For low-traffic deployments, run a single replica to ensure in-memory locks are effective + +4. **Sequential Processing**: Queue transactions for the same vault account to process them sequentially + +```typescript +// Example: The SDK locks UTXOs automatically +const result1 = await sdk.transferAda({ ... }); // UTXO locked during tx +const result2 = await sdk.transferAda({ ... }); // Different UTXO selected +// Locks automatically release after transaction completes or TTL expires +``` + ## Security Considerations - ⚠️ Never commit `.env` files or secret keys to version control From 73f90eeccf76e1033d05aff539db015f7627315a Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 2 Jun 2026 14:31:09 +0300 Subject: [PATCH 06/65] fix(#8): add UTXO locking to staking and governance operations Previously, staking and governance operations did not use the UTXO locking mechanism, allowing concurrent operations on the same vault to potentially select the same UTXO (double-spend risk). Changes: - UtxoProvider.findAddressWithSuitableUtxo now: - Filters out already-locked UTXOs before selection - Locks the selected UTXO and returns a release function - All staking/governance operations now release UTXO locks in finally blocks - AddressWithUtxo interface includes a release() function - Added lockOne() helper to UtxoLockManager for single-UTXO locking Operations updated: - registerStakingCredential - delegateToPool - deregisterStakingCredential - withdrawRewards - delegateToDRep - registerAsDRep - castVote Locks auto-expire after UTXO_LOCK_TTL_MS (2 minutes) as a safety net. --- .../staking/helpers/utxo-provider.helper.ts | 17 ++++++- src/services/staking/staking.service.ts | 46 +++++++++++++++---- .../staking/types/staking.interfaces.ts | 5 ++ src/utils/utxoLock.ts | 8 ++++ 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/services/staking/helpers/utxo-provider.helper.ts b/src/services/staking/helpers/utxo-provider.helper.ts index f1977a95..72c1c4ff 100644 --- a/src/services/staking/helpers/utxo-provider.helper.ts +++ b/src/services/staking/helpers/utxo-provider.helper.ts @@ -8,6 +8,7 @@ import { findSuitableUtxo, UtxoForStaking, formatWithDecimals, + utxoLocks, } from "../../../utils/index.js"; import { SdkApiError } from "../../../types/index.js"; import { FireblocksService, IagonApiService } from "../../index.js"; @@ -39,10 +40,14 @@ export class UtxoProvider implements IUtxoProvider { const utxo = await this.findUtxoForAddress(addressObj.address, minAmount); if (utxo) { + // Lock the UTXO to prevent double-spend in concurrent operations + const release = utxoLocks.lockOne(utxo.txHash, utxo.indexInTx); + return { address: addressObj.address, addressIndex: addressObj.bip44AddressIndex ?? 0, utxo, + release, }; } @@ -81,7 +86,17 @@ export class UtxoProvider implements IUtxoProvider { return null; } - return findSuitableUtxo(utxosResponse.data, minAmount); + // Filter out already locked UTXOs to prevent double-spend + const availableUtxos = utxosResponse.data.filter( + (u) => !utxoLocks.isLocked(u.transaction_id, u.output_index) + ); + + if (availableUtxos.length === 0) { + this.logger.debug(`All UTXOs for ${address} are currently locked`); + return null; + } + + return findSuitableUtxo(availableUtxos, minAmount); } private createInsufficientFundsError(vaultAccountId: string, minAmount: number): SdkApiError { diff --git a/src/services/staking/staking.service.ts b/src/services/staking/staking.service.ts index 5cb97e60..b61a3c3b 100644 --- a/src/services/staking/staking.service.ts +++ b/src/services/staking/staking.service.ts @@ -148,6 +148,8 @@ export class StakingService { this.logger.info(`Registering staking credential for vault account ${vaultAccountId}`); + let addressWithUtxo: AddressWithUtxo | undefined; + try { const existingRegistration = await this.validator.checkRegistrationStatus(vaultAccountId); @@ -156,7 +158,7 @@ export class StakingService { } const minInputAmount = depositAmount + fee + CardanoConstants.MIN_UTXO_BASE_LOVELACE; - const addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( + addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( vaultAccountId, minInputAmount ); @@ -181,6 +183,8 @@ export class StakingService { }; } catch (error: unknown) { throw this.errorHandler.handleApiError(error, "registering staking credential"); + } finally { + addressWithUtxo?.release(); } } @@ -192,11 +196,13 @@ export class StakingService { this.logger.info(`Delegating to pool ${poolId} for vault account ${vaultAccountId}`); + let addressWithUtxo: AddressWithUtxo | undefined; + try { await this.validator.validateDelegationPrerequisites(vaultAccountId, poolId); const minAmount = CardanoConstants.MIN_UTXO_BASE_LOVELACE + fee; - const addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( + addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( vaultAccountId, minAmount ); @@ -230,6 +236,8 @@ export class StakingService { }; } catch (error: unknown) { throw this.errorHandler.handleApiError(error, "delegating to stake pool"); + } finally { + addressWithUtxo?.release(); } } @@ -243,6 +251,8 @@ export class StakingService { this.logger.info(`Deregistering staking credential for vault account ${vaultAccountId}`); + let addressWithUtxo: AddressWithUtxo | undefined; + try { const isRegistered = await this.validator.checkRegistrationStatus(vaultAccountId); @@ -256,7 +266,7 @@ export class StakingService { } const minInputAmount = CardanoConstants.MIN_UTXO_BASE_LOVELACE + fee; - const addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( + addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( vaultAccountId, minInputAmount ); @@ -270,6 +280,8 @@ export class StakingService { }; } catch (error: unknown) { throw this.errorHandler.handleApiError(error, "deregistering staking credential"); + } finally { + addressWithUtxo?.release(); } } @@ -279,6 +291,8 @@ export class StakingService { async withdrawRewards( options: WithdrawRewardsOptions ): Promise { + let addressWithUtxo: AddressWithUtxo | undefined; + try { const { vaultAccountId, limit, fee } = options; @@ -288,7 +302,7 @@ export class StakingService { this.logger.info(`Withdrawing rewards for vault account ${vaultAccountId}`); const minInputAmount = CardanoConstants.MIN_UTXO_BASE_LOVELACE + fee; - const addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( + addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( vaultAccountId, minInputAmount ); @@ -318,12 +332,12 @@ export class StakingService { // Build a helper so we can retry with a corrected amount without repeating code const buildAndSubmitWithdrawal = async (amount: number) => { - const netAmount = addressWithUtxo.utxo.nativeAmount - fee + amount; + const netAmount = addressWithUtxo!.utxo.nativeAmount - fee + amount; const w = { ...withdrawal, reward: amount }; const withdrawalsDict = serializeWithdrawals([w]); return this.buildSignAndSubmit({ vaultAccountId, - addressInfo: addressWithUtxo, + addressInfo: addressWithUtxo!, netAmount, fee, withdrawals: withdrawalsDict, @@ -374,6 +388,8 @@ export class StakingService { }; } catch (error: unknown) { throw this.errorHandler.handleApiError(error, "withdrawing staking rewards"); + } finally { + addressWithUtxo?.release(); } } @@ -381,6 +397,8 @@ export class StakingService { * Delegate voting power to a DRep (Conway era governance) */ async delegateToDRep(options: DRepDelegationOptions): Promise { + let addressWithUtxo: AddressWithUtxo | undefined; + try { const { vaultAccountId, @@ -395,7 +413,7 @@ export class StakingService { await this.validator.validateRegistrationStatus(vaultAccountId, true); const minInputAmount = fee * MIN_DREP_DELEGATION_AMOUNT_MULTIPLIER; - const addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( + addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( vaultAccountId, minInputAmount ); @@ -429,6 +447,8 @@ export class StakingService { }; } catch (error: unknown) { throw this.errorHandler.handleApiError(error, "delegating to DRep"); + } finally { + addressWithUtxo?.release(); } } @@ -436,6 +456,8 @@ export class StakingService { * Register the vault account as a DRep (Conway era governance) */ async registerAsDRep(options: RegisterAsDRepOptions): Promise { + let addressWithUtxo: AddressWithUtxo | undefined; + try { const { vaultAccountId, @@ -447,7 +469,7 @@ export class StakingService { this.logger.info(`Registering vault account ${vaultAccountId} as a DRep`); const minInputAmount = depositAmount + fee; - const addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( + addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( vaultAccountId, minInputAmount ); @@ -490,6 +512,8 @@ export class StakingService { }; } catch (error: unknown) { throw this.errorHandler.handleApiError(error, "registering as DRep"); + } finally { + addressWithUtxo?.release(); } } @@ -497,6 +521,8 @@ export class StakingService { * Cast a governance vote as a DRep (Conway era) */ async castVote(options: CastVoteOptions): Promise { + let addressWithUtxo: AddressWithUtxo | undefined; + try { const { vaultAccountId, @@ -513,7 +539,7 @@ export class StakingService { const voteInteger: 0 | 1 | 2 = vote === "no" ? 0 : vote === "yes" ? 1 : 2; const minInputAmount = fee; - const addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( + addressWithUtxo = await this.utxoProvider.findAddressWithSuitableUtxo( vaultAccountId, minInputAmount ); @@ -554,6 +580,8 @@ export class StakingService { }; } catch (error: unknown) { throw this.errorHandler.handleApiError(error, "casting governance vote"); + } finally { + addressWithUtxo?.release(); } } diff --git a/src/services/staking/types/staking.interfaces.ts b/src/services/staking/types/staking.interfaces.ts index 1fc809c1..1267ed17 100644 --- a/src/services/staking/types/staking.interfaces.ts +++ b/src/services/staking/types/staking.interfaces.ts @@ -65,6 +65,11 @@ export interface AddressInfo { export interface AddressWithUtxo extends AddressInfo { readonly utxo: UtxoForStaking; + /** + * Release the UTXO lock. Should be called in finally blocks after transaction completes. + * If not called, lock will auto-expire after UTXO_LOCK_TTL_MS. + */ + readonly release: () => void; } export interface SigningContext { diff --git a/src/utils/utxoLock.ts b/src/utils/utxoLock.ts index 850067e0..96301f6e 100644 --- a/src/utils/utxoLock.ts +++ b/src/utils/utxoLock.ts @@ -41,6 +41,14 @@ class UtxoLockManager { for (const k of keys) this.locks.delete(k); }; } + + /** + * Lock a single UTxO by txHash and index. + * Convenience method for staking operations that select one UTXO at a time. + */ + lockOne(txHash: string, index: number): () => void { + return this.lock([{ transaction_id: txHash, output_index: index }]); + } } export const utxoLocks = new UtxoLockManager(); From e708a8df096e03f05014ac2bfba20f2163a8b126 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 2 Jun 2026 14:42:41 +0300 Subject: [PATCH 07/65] docs: add security and protocol parameters configuration to README - Add security configuration section documenting CORS, rate limiting, and API key authentication setup with examples - Add protocol parameters configuration section explaining how to override Cardano protocol params at SDK initialization - Update environment variables table with all security-related vars (MAX_BODY_SIZE, RATE_LIMIT_*, CORS_ORIGINS, API_KEY_*) Related to fixes #1 (security middleware) and #5 (protocol params) --- README.md | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/README.md b/README.md index 90da5eae..aeaab1d1 100644 --- a/README.md +++ b/README.md @@ -800,6 +800,12 @@ cp .env.example .env | `FIREBLOCKS_BASE_PATH` | No | `https://api.fireblocks.io` | Fireblocks workspace URL - also controls webhook JWKS region | | `IAGON_API_KEY` | Yes | - | Iagon API key for blockchain data queries | | `CARDANO_NETWORK` | No | `mainnet` | `mainnet` or `preprod` | +| `MAX_BODY_SIZE` | No | `1mb` | Maximum request body size | +| `RATE_LIMIT_WINDOW_MS` | No | `900000` | Rate limit time window (ms) | +| `RATE_LIMIT_MAX_REQUESTS` | No | `100` | Max requests per rate limit window | +| `CORS_ORIGINS` | No | `*` | Allowed CORS origins (comma-separated) | +| `API_KEY_ENABLED` | No | `false` | Enable API key authentication | +| `API_KEY` | No | - | API key value (required if `API_KEY_ENABLED=true`) | **Fireblocks base path options:** @@ -850,6 +856,101 @@ export FIREBLOCKS_API_USER_SECRET_KEY=$(cat key.b64) **Priority:** If both `FIREBLOCKS_API_USER_SECRET_KEY` and `FIREBLOCKS_API_USER_SECRET_KEY_PATH` are set, the direct key content takes precedence. +### Security Configuration + +The API server includes configurable security middleware for CORS, rate limiting, and optional API key authentication. All settings are configured via environment variables. + +| Variable | Default | Description | +|----------|---------|-------------| +| `MAX_BODY_SIZE` | `1mb` | Maximum request body size (e.g., `1mb`, `500kb`) | +| `RATE_LIMIT_WINDOW_MS` | `900000` | Rate limiting time window in milliseconds (default: 15 minutes) | +| `RATE_LIMIT_MAX_REQUESTS` | `100` | Maximum requests allowed per window | +| `CORS_ORIGINS` | `*` | Allowed CORS origins. Use `*` for all, or comma-separated list (e.g., `https://app.example.com,https://admin.example.com`) | +| `API_KEY_ENABLED` | `false` | Enable API key authentication | +| `API_KEY` | - | Required API key value when `API_KEY_ENABLED=true` | + +#### CORS Configuration + +```bash +# Allow all origins (development) +CORS_ORIGINS=* + +# Allow specific origins (production) +CORS_ORIGINS=https://myapp.com,https://admin.myapp.com +``` + +#### Rate Limiting + +Rate limiting protects against abuse. The `/health` endpoint is exempt to support load balancer health checks. + +```bash +# 100 requests per 15 minutes (default) +RATE_LIMIT_WINDOW_MS=900000 +RATE_LIMIT_MAX_REQUESTS=100 + +# Stricter: 50 requests per 5 minutes +RATE_LIMIT_WINDOW_MS=300000 +RATE_LIMIT_MAX_REQUESTS=50 +``` + +#### API Key Authentication + +When enabled, all requests (except `/health`, `/api-docs`, `/docs`) require the `X-API-Key` header. + +```bash +# Enable API key authentication +API_KEY_ENABLED=true +API_KEY=your-secret-api-key-here +``` + +Client requests must include the header: + +```bash +curl -H "X-API-Key: your-secret-api-key-here" http://localhost:8000/api/balance/123 +``` + +### Protocol Parameters Configuration + +Cardano protocol parameters can be overridden at SDK initialization. This is useful when protocol parameters change at hard forks, allowing you to update without waiting for an SDK release. + +| Parameter | Default | Description | +|-----------|---------|-------------| +| `minFeeA` | `44` | Fee coefficient: lovelace per transaction byte | +| `minFeeB` | `155381` | Fee constant: base lovelace fee | +| `coinsPerUtxoByte` | `4310` | Lovelace per UTxO byte for min-ADA calculations | +| `stakeKeyDeposit` | `2000000` | Stake key registration deposit (2 ADA) | +| `drepDeposit` | `500000000` | DRep registration deposit (500 ADA) | + +#### SDK Usage + +```typescript +const sdk = await FireblocksCardanoRawSDK.createInstance({ + fireblocksConfig: { apiKey, secretKey, basePath }, + vaultAccountId: "123", + network: Networks.MAINNET, + iagonApiKey: "...", + // Override protocol parameters (optional) + protocolParams: { + minFeeA: 44, + minFeeB: 155381, + coinsPerUtxoByte: 4310, + stakeKeyDeposit: 2_000_000, + drepDeposit: 500_000_000, + } +}); +``` + +You only need to specify parameters that have changed - unspecified parameters keep their defaults. + +```typescript +// Only override fee parameters after a hard fork +protocolParams: { + minFeeA: 45, // New value + minFeeB: 160000, // New value + // coinsPerUtxoByte, stakeKeyDeposit, drepDeposit keep defaults +} +``` + ## API Documentation ### Swagger UI From 74d887d9cfaf3765630510faa8cba44711783e12 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 2 Jun 2026 14:44:43 +0300 Subject: [PATCH 08/65] ci: add automatic CI workflow on push/PR to main - Runs lint, typecheck, and build on every push to main and PR - Runs E2E staking info test (read-only) after build passes - Runs E2E ADA transfer test (3 ADA between vault 0 and 1) - PRs to main will be blocked if CI fails This addresses review issue #3 by adding automatic CI while keeping the manual workflow_dispatch tests for more complex scenarios. --- .github/workflows/ci.yml | 95 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..697875cc --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,95 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + lint-and-build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + + - name: Lint + run: npm run lint + + - name: Type check + run: npm run typecheck + + - name: Build + run: npm run build + + e2e-staking-info: + runs-on: ubuntu-latest + needs: lint-and-build + environment: tests + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Run staking info test (read-only) + env: + FIREBLOCKS_API_USER_KEY: ${{ secrets.FIREBLOCKS_API_KEY }} + FIREBLOCKS_API_USER_SECRET_KEY: ${{ secrets.FIREBLOCKS_API_SECRET }} + IAGON_API_KEY: ${{ secrets.IAGON_API_KEY }} + CARDANO_NETWORK: mainnet + VAULT_ACCOUNT_ID: '1' + STAKING_ACTION: info + FIREBLOCKS_BASE_PATH: ${{ secrets.FIREBLOCKS_BASE_PATH }} + run: npx tsx scripts/test-staking.ts + + e2e-ada-transfer: + runs-on: ubuntu-latest + needs: lint-and-build + environment: tests + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run build + + - name: Run ADA transfer test + env: + FIREBLOCKS_API_USER_KEY: ${{ secrets.FIREBLOCKS_API_KEY }} + FIREBLOCKS_API_USER_SECRET_KEY: ${{ secrets.FIREBLOCKS_API_SECRET }} + IAGON_API_KEY: ${{ secrets.IAGON_API_KEY }} + CARDANO_NETWORK: mainnet + TRANSFER_AMOUNT_ADA: '3' + SOURCE_VAULT: '0' + DEST_VAULT: '1' + FIREBLOCKS_BASE_PATH: ${{ secrets.FIREBLOCKS_BASE_PATH }} + run: npx tsx scripts/test-ada-transfer.ts From 193d5086cb1395dceb02498945cdd0ba7481860d Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 11:36:03 +0300 Subject: [PATCH 09/65] fix(M-01): verify webhook signature before parsing JSON Mount /api/webhook with express.raw() so the raw request body is preserved and signature verification runs before any JSON parsing. The webhook handler now verifies the Fireblocks signature against the raw buffer and only parses JSON after successful verification, ensuring untrusted payloads are never deserialized prior to authentication. --- src/api/controllers/controller.ts | 30 +++++++++++++++++------------- src/server.ts | 17 ++++------------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/api/controllers/controller.ts b/src/api/controllers/controller.ts index 11ae5a5c..b0d869f3 100644 --- a/src/api/controllers/controller.ts +++ b/src/api/controllers/controller.ts @@ -376,10 +376,9 @@ export class ApiController { public enrichWebhookPayload = async (req: Request, res: Response) => { try { - // Extract raw body (Buffer) for signature verification - // rawBody is attached by express.json verify callback - const rawBody = (req as Request & { rawBody?: Buffer }).rawBody; - if (!rawBody) { + // req.body is a Buffer because /api/webhook is mounted with express.raw() + const rawBody = req.body; + if (!Buffer.isBuffer(rawBody) || rawBody.length === 0) { return res.status(400).json({ success: false, error: @@ -387,10 +386,6 @@ export class ApiController { }); } - // Body is already parsed by express.json - const payload = req.body; - - // Extract headers for signature verification const headers: Record = { "fireblocks-webhook-signature": req.headers["fireblocks-webhook-signature"] as | string @@ -398,12 +393,11 @@ export class ApiController { "fireblocks-signature": req.headers["fireblocks-signature"] as string | undefined, }; - // Get webhook environment from Fireblocks basePath config const environment = getWebhookEnvironment( (config.FIREBLOCKS.basePath as BasePath) || BasePath.US ); - // Step 1: Verify signature using a default SDK instance + // Verify signature first, before parsing untrusted JSON const verifyResult = await this.sdkManager.withSdk("0", (sdk) => sdk.verifyWebhook(rawBody, headers, environment) ); @@ -415,10 +409,20 @@ export class ApiController { }); } - // Step 2: acquire the correct vault SDK for enrichment - const vaultAccountId = payload?.data?.destination?.id ?? "0"; + let payload: unknown; + try { + payload = JSON.parse(rawBody.toString("utf8")); + } catch { + return res.status(400).json({ + success: false, + error: "Invalid JSON payload", + }); + } + + const vaultAccountId = + (payload as { data?: { destination?: { id?: string } } })?.data?.destination?.id ?? "0"; const result = await this.sdkManager.withSdk(vaultAccountId, (sdk) => - sdk.enrichWebhookPayload(payload) + sdk.enrichWebhookPayload(payload as Parameters[0]) ); this.logger.info("Webhook verified and enriched successfully"); diff --git a/src/server.ts b/src/server.ts index bf29c126..15079b98 100644 --- a/src/server.ts +++ b/src/server.ts @@ -47,19 +47,10 @@ const startServer = () => { const app = express(); - // Configure middlewares with raw body preservation for webhook endpoint - app.use( - express.json({ - limit: getMaxBodySize(), - verify: (req, _res, buf, _encoding) => { - // Preserve raw body for webhook signature verification - const r = req as Request & { url?: string; rawBody?: Buffer }; - if (r.url?.split("?")[0] === "/api/webhook") { - r.rawBody = buf; - } - }, - }) - ); + // Webhook uses raw body parser; signature must be verified before parsing JSON + app.use("/api/webhook", express.raw({ type: "application/json", limit: getMaxBodySize() })); + + app.use(express.json({ limit: getMaxBodySize() })); app.use(express.urlencoded({ extended: true, limit: getMaxBodySize() })); // Apply security middleware (CORS, rate limiting, optional API key auth) From ba74331cd84c3ac01ab49975d2e07c372860a38b Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 11:38:59 +0300 Subject: [PATCH 10/65] fix(M-02): validate recipient address format and network resolveRecipientAddress now parses recipientAddress with Address.from_bech32 and rejects malformed bech32 strings, then checks that the embedded network id matches the SDK's configured network (mainnet=1, preprod=0). This prevents accidental cross-network sends and surfaces a clear validation error instead of a downstream parse failure. --- src/FireblocksCardanoRawSDK.ts | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/FireblocksCardanoRawSDK.ts b/src/FireblocksCardanoRawSDK.ts index 08a60c9f..41bf9d28 100644 --- a/src/FireblocksCardanoRawSDK.ts +++ b/src/FireblocksCardanoRawSDK.ts @@ -440,6 +440,40 @@ export class FireblocksCardanoRawSDK { return address; } + /** + * Validates a user-supplied Cardano bech32 address: parses it and ensures + * the embedded network id matches the SDK's configured network. + */ + private validateRecipientAddress(recipientAddress: string): void { + let parsed; + try { + parsed = Address.from_bech32(recipientAddress); + } catch { + throw new SdkApiError( + "Invalid recipientAddress: not a valid Cardano bech32 address", + 400, + "ValidationError", + { recipientAddress }, + "FireblocksCardanoRawSDK" + ); + } + try { + const expectedNetworkId = this.network === Networks.MAINNET ? 1 : 0; + const actualNetworkId = parsed.network_id(); + if (actualNetworkId !== expectedNetworkId) { + throw new SdkApiError( + `recipientAddress network mismatch: address is for network ${actualNetworkId}, SDK is configured for ${this.network} (network ${expectedNetworkId})`, + 400, + "ValidationError", + { recipientAddress, expectedNetworkId, actualNetworkId }, + "FireblocksCardanoRawSDK" + ); + } + } finally { + parsed.free(); + } + } + /** * Resolves recipient address from either a direct address or a vault account ID. * Validates that exactly one recipient option is provided. @@ -467,6 +501,9 @@ export class FireblocksCardanoRawSDK { "FireblocksCardanoRawSDK" ); } + if (recipientAddress) { + this.validateRecipientAddress(recipientAddress); + } if (recipientVaultAccountId) { const recipientAddressData = await this.fireblocksService.getVaultAccountAddress( recipientVaultAccountId, From 3b321537adbf873e1c0ecdd6235303828031d998 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 11:40:52 +0300 Subject: [PATCH 11/65] fix(M-04): encode lovelace and fee as BigInt in CBOR Convert netAmount and feeAmount to BigInt before passing them to cbor2 in buildPayload. JS Number only safely represents integers up to 2^53; on-chain lovelace amounts can in theory exceed that bound. Using BigInt forces cbor2 to emit CBOR unsigned integers (major type 0) without precision loss. --- src/utils/staking.utils.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/utils/staking.utils.ts b/src/utils/staking.utils.ts index 1f96e966..f9507567 100644 --- a/src/utils/staking.utils.ts +++ b/src/utils/staking.utils.ts @@ -305,9 +305,10 @@ export const buildPayload = ( const { toAddress, txInputs, ttl, certificates, withdrawals, votingProcedures, network } = options; - // Ensure lovelace amounts are CBOR unsigned integers, not floats. - const netAmount = Math.round(options.netAmount); - const feeAmount = Math.round(options.feeAmount); + // Encode lovelace amounts as BigInt so cbor2 emits CBOR unsigned ints + // (major type 0) without risking JS Number (53-bit) precision loss. + const netAmount = BigInt(Math.round(options.netAmount)); + const feeAmount = BigInt(Math.round(options.feeAmount)); // Build inputs array const inputsArr = txInputs.map((input) => { From f24bf228a56d886c631a38ad7273168a7cc0de1a Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 11:44:22 +0300 Subject: [PATCH 12/65] fix(M-06): exclude script-locked UTxOs from selection Add isSpendableUtxo helper that rejects UTxOs carrying datum_hash or script_hash, and apply it alongside the existing utxo-lock filter in fetchAndSelectUtxosForCnt / Ada / MultiToken and in the staking UTxO provider. These outputs require a Plutus or native-script witness and are unspendable with the Ed25519 keys held in Fireblocks vaults. --- .../staking/helpers/utxo-provider.helper.ts | 6 +++-- src/utils/cardano.ts | 22 ++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/services/staking/helpers/utxo-provider.helper.ts b/src/services/staking/helpers/utxo-provider.helper.ts index 72c1c4ff..b7495977 100644 --- a/src/services/staking/helpers/utxo-provider.helper.ts +++ b/src/services/staking/helpers/utxo-provider.helper.ts @@ -9,6 +9,7 @@ import { UtxoForStaking, formatWithDecimals, utxoLocks, + isSpendableUtxo, } from "../../../utils/index.js"; import { SdkApiError } from "../../../types/index.js"; import { FireblocksService, IagonApiService } from "../../index.js"; @@ -86,9 +87,10 @@ export class UtxoProvider implements IUtxoProvider { return null; } - // Filter out already locked UTXOs to prevent double-spend + // Filter out locked UTXOs (double-spend prevention) and script/datum + // UTXOs which cannot be spent with a simple Ed25519 witness. const availableUtxos = utxosResponse.data.filter( - (u) => !utxoLocks.isLocked(u.transaction_id, u.output_index) + (u) => !utxoLocks.isLocked(u.transaction_id, u.output_index) && isSpendableUtxo(u) ); if (availableUtxos.length === 0) { diff --git a/src/utils/cardano.ts b/src/utils/cardano.ts index 79971318..1bad7b8c 100644 --- a/src/utils/cardano.ts +++ b/src/utils/cardano.ts @@ -129,6 +129,16 @@ export const calculateTransactionFee = (tx: Transaction): number => { return parseInt(calculatedFee.to_str()); }; +/** + * A UTxO is spendable by this SDK only with a simple Ed25519 witness. + * UTxOs carrying a datum_hash (Plutus v1/v2 inputs) or a script_hash + * (script-locked outputs) require a Plutus/native-script witness and + * must be excluded from selection. + */ +export const isSpendableUtxo = (utxo: UtxoData): boolean => { + return !utxo.datum_hash && !utxo.script_hash; +}; + export const fetchAndSelectUtxosForCnt = async (params: fetchAndSelectUtxosForCntParams) => { const { iagonApiService, @@ -141,7 +151,9 @@ export const fetchAndSelectUtxosForCnt = async (params: fetchAndSelectUtxosForCn } = params; try { const rawUtxos = await fetchUtxos(iagonApiService, address); - const utxos = rawUtxos.filter((u) => !utxoLocks.isLocked(u.transaction_id, u.output_index)); + const utxos = rawUtxos.filter( + (u) => !utxoLocks.isLocked(u.transaction_id, u.output_index) && isSpendableUtxo(u) + ); const tokenUtxosWithAmounts = filterUtxos(utxos, tokenPolicyId, tokenName) .map((utxo) => ({ @@ -770,7 +782,9 @@ export const fetchAndSelectUtxosForAda = async ( const { iagonApiService, address, lovelaceAmount, transactionFee, lock = false } = params; const rawUtxos = await fetchUtxos(iagonApiService, address); - const utxos = rawUtxos.filter((u) => !utxoLocks.isLocked(u.transaction_id, u.output_index)); + const utxos = rawUtxos.filter( + (u) => !utxoLocks.isLocked(u.transaction_id, u.output_index) && isSpendableUtxo(u) + ); if (!utxos || utxos.length === 0) { throw new Error(`No UTxOs found for address: ${address}`); } @@ -949,7 +963,9 @@ export const fetchAndSelectUtxosForMultiToken = async ( const { iagonApiService, address, tokens, transactionFee, lovelaceAmount, lock = false } = params; const rawUtxos = await fetchUtxos(iagonApiService, address); - const utxos = rawUtxos.filter((u) => !utxoLocks.isLocked(u.transaction_id, u.output_index)); + const utxos = rawUtxos.filter( + (u) => !utxoLocks.isLocked(u.transaction_id, u.output_index) && isSpendableUtxo(u) + ); if (!utxos || utxos.length === 0) { throw new Error(`No UTxOs found for address: ${address}`); } From 608dd50c30b1b37f183d637a8cd0062a14ce9ea7 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 11:45:16 +0300 Subject: [PATCH 13/65] fix(M-07): bound governanceActionId.index to uint16 Conway CDDL defines gov_action_id.gov_action_index as uint .size 2. Cap the value at 65535 in the cast-vote request schema so out-of-range indices are rejected at the API boundary instead of producing an invalid CBOR transaction body. --- src/api/validation.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/api/validation.ts b/src/api/validation.ts index 61152a19..c8612bea 100644 --- a/src/api/validation.ts +++ b/src/api/validation.ts @@ -389,7 +389,11 @@ export const castVoteRequestSchema = z.object({ .string() .length(64, "governanceActionId.txHash must be a 64-character hex string") .regex(/^[0-9a-fA-F]{64}$/, "governanceActionId.txHash must be hex"), - index: z.number().int().nonnegative("governanceActionId.index must be a non-negative integer"), + index: z + .number() + .int() + .min(0, "governanceActionId.index must be a non-negative integer") + .max(65535, "governanceActionId.index must fit in uint16 (0-65535) per Conway CDDL"), }), vote: z.enum(["yes", "no", "abstain"], { message: 'vote must be "yes", "no", or "abstain"' }), anchor: anchorSchema.optional(), From 5f5510cfbe6d483aea0e53decf8fcfd0f38b5bbc Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 11:45:55 +0300 Subject: [PATCH 14/65] fix(M-08): validate DRep ID bech32 checksum and payload length Replace the bare regex check on drepId with a bech32-aware validator that decodes drep1.../drep_script1... strings, verifies the checksum, and confirms the payload is exactly 28 bytes (the credential length). Hex form keeps the existing 56-char check. --- src/api/validation.ts | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/api/validation.ts b/src/api/validation.ts index c8612bea..70239937 100644 --- a/src/api/validation.ts +++ b/src/api/validation.ts @@ -1,8 +1,28 @@ import { z } from "zod"; import { Request, Response, NextFunction } from "express"; +import { bech32 } from "bech32"; import { getProtocolParams } from "../utils/protocolParams.js"; +/** + * Validate a DRep ID. Accepts either: + * - a 56-char hex string (28-byte credential), or + * - a bech32 string with HRP "drep" or "drep_script" decoding to 28 bytes. + * Verifies the bech32 checksum and payload length. + */ +const isValidDrepId = (id: string): boolean => { + if (/^[0-9a-fA-F]{56}$/.test(id)) return true; + if (!id.startsWith("drep1") && !id.startsWith("drep_script1")) return false; + try { + const decoded = bech32.decode(id, 1000); + if (decoded.prefix !== "drep" && decoded.prefix !== "drep_script") return false; + const bytes = bech32.fromWords(decoded.words); + return bytes.length === 28; + } catch { + return false; + } +}; + /** * Fee validation bounds. * MIN_PROTOCOL_FEE: Minimum fee per Cardano protocol (minFeeB constant, configurable) @@ -335,9 +355,9 @@ export const delegateToDRepRequestSchema = z }), drepId: z .string() - .regex( - /^(drep1|drep_script1)[a-z0-9]+$|^[0-9a-fA-F]{56}$/, - "drepId must be bech32 (drep1... or drep_script1...) or a 56-character hex string" + .refine( + isValidDrepId, + "drepId must be a 56-character hex string or a valid bech32 string with HRP drep/drep_script decoding to 28 bytes" ) .optional(), fee: optionalFeeSchema, From 189567bb4a79a1cdd3df38cdf73efc703f8154a2 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:04:32 +0300 Subject: [PATCH 15/65] refactor(M-02): extract validateRecipientAddress to a free helper Move the recipient-address parse/network-id check out of the private SDK method and into assertRecipientAddress in utils/cardano.ts so it can be unit-tested directly. Behaviour is unchanged; the SDK now calls the helper through its existing resolveRecipientAddress chokepoint. --- src/FireblocksCardanoRawSDK.ts | 37 ++------------------------------- src/utils/cardano.ts | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/FireblocksCardanoRawSDK.ts b/src/FireblocksCardanoRawSDK.ts index 41bf9d28..15f5f2f9 100644 --- a/src/FireblocksCardanoRawSDK.ts +++ b/src/FireblocksCardanoRawSDK.ts @@ -29,6 +29,7 @@ import { getStakeAddressFromBaseAddress, utxoLocks, setProtocolParams, + assertRecipientAddress, } from "./utils/index.js"; import { @@ -440,40 +441,6 @@ export class FireblocksCardanoRawSDK { return address; } - /** - * Validates a user-supplied Cardano bech32 address: parses it and ensures - * the embedded network id matches the SDK's configured network. - */ - private validateRecipientAddress(recipientAddress: string): void { - let parsed; - try { - parsed = Address.from_bech32(recipientAddress); - } catch { - throw new SdkApiError( - "Invalid recipientAddress: not a valid Cardano bech32 address", - 400, - "ValidationError", - { recipientAddress }, - "FireblocksCardanoRawSDK" - ); - } - try { - const expectedNetworkId = this.network === Networks.MAINNET ? 1 : 0; - const actualNetworkId = parsed.network_id(); - if (actualNetworkId !== expectedNetworkId) { - throw new SdkApiError( - `recipientAddress network mismatch: address is for network ${actualNetworkId}, SDK is configured for ${this.network} (network ${expectedNetworkId})`, - 400, - "ValidationError", - { recipientAddress, expectedNetworkId, actualNetworkId }, - "FireblocksCardanoRawSDK" - ); - } - } finally { - parsed.free(); - } - } - /** * Resolves recipient address from either a direct address or a vault account ID. * Validates that exactly one recipient option is provided. @@ -502,7 +469,7 @@ export class FireblocksCardanoRawSDK { ); } if (recipientAddress) { - this.validateRecipientAddress(recipientAddress); + assertRecipientAddress(recipientAddress, this.network); } if (recipientVaultAccountId) { const recipientAddressData = await this.fireblocksService.getVaultAccountAddress( diff --git a/src/utils/cardano.ts b/src/utils/cardano.ts index 1bad7b8c..ca50e05c 100644 --- a/src/utils/cardano.ts +++ b/src/utils/cardano.ts @@ -29,6 +29,7 @@ import { fetchAndSelectUtxosForMultiTokenParams, SupportedAssets, SdkApiError, + Networks, } from "../types/index.js"; import { Logger } from "./logger.js"; import { CardanoAmounts, CardanoConstants } from "../constants.js"; @@ -139,6 +140,43 @@ export const isSpendableUtxo = (utxo: UtxoData): boolean => { return !utxo.datum_hash && !utxo.script_hash; }; +/** + * Parses a bech32 Cardano address and asserts it is on the expected network. + * Throws SdkApiError on parse failure or network mismatch. + */ +export const assertRecipientAddress = ( + recipientAddress: string, + network: Networks +): void => { + let parsed; + try { + parsed = Address.from_bech32(recipientAddress); + } catch { + throw new SdkApiError( + "Invalid recipientAddress: not a valid Cardano bech32 address", + 400, + "ValidationError", + { recipientAddress }, + "FireblocksCardanoRawSDK" + ); + } + try { + const expectedNetworkId = network === Networks.MAINNET ? 1 : 0; + const actualNetworkId = parsed.network_id(); + if (actualNetworkId !== expectedNetworkId) { + throw new SdkApiError( + `recipientAddress network mismatch: address is for network ${actualNetworkId}, SDK is configured for ${network} (network ${expectedNetworkId})`, + 400, + "ValidationError", + { recipientAddress, expectedNetworkId, actualNetworkId }, + "FireblocksCardanoRawSDK" + ); + } + } finally { + parsed.free(); + } +}; + export const fetchAndSelectUtxosForCnt = async (params: fetchAndSelectUtxosForCntParams) => { const { iagonApiService, From f45f90c90e168364d27742fcb4032608eb8461db Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:05:01 +0300 Subject: [PATCH 16/65] test(M-01..M-08): add unit tests for medium-priority fixes Cover the behaviour introduced by the M-01..M-08 commits: - M-01: webhook handler verifies signature against the raw Buffer before parsing JSON; rejects non-buffer/empty bodies, bad signatures and signature-valid-but-malformed JSON. - M-02: assertRecipientAddress accepts matching-network addresses and rejects malformed bech32 / cross-network addresses with descriptive ValidationError info. - M-04: buildPayload emits CBOR major-type-0 uint bytes for lovelace and fee, including the 8-byte form for values above 2^32 and proper rounding of fractional inputs. - M-06: isSpendableUtxo excludes UTxOs with datum_hash or script_hash. - M-07: governanceActionId.index rejects values above uint16 max. - M-08: DRep ID validation accepts hex and bech32, rejects bad checksums, wrong-length payloads and unknown HRPs. Switched the test script to node --experimental-vm-modules so Jest can load cbor2 (ESM-only) when running the new buildPayload tests. --- package.json | 2 +- src/__tests__/api/controller.test.ts | 149 +++++++++++++++++++++- src/__tests__/api/validation.test.ts | 109 ++++++++++++++++ src/__tests__/utils/cardano.test.ts | 113 ++++++++++++++++ src/__tests__/utils/staking.utils.test.ts | 101 +++++++++++++++ 5 files changed, 472 insertions(+), 2 deletions(-) create mode 100644 src/__tests__/utils/cardano.test.ts create mode 100644 src/__tests__/utils/staking.utils.test.ts diff --git a/package.json b/package.json index b07b385f..628e237b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "format:check": "prettier --check \"src/**/*.ts\"", "typecheck": "tsc --noEmit", "lint": "eslint src --ext .ts", - "test": "jest", + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", "docs": "typedoc src" }, "keywords": [ diff --git a/src/__tests__/api/controller.test.ts b/src/__tests__/api/controller.test.ts index c5fd73d1..29d1ec8a 100644 --- a/src/__tests__/api/controller.test.ts +++ b/src/__tests__/api/controller.test.ts @@ -1,7 +1,9 @@ -import { describe, it, expect, jest, beforeEach } from '@jest/globals'; +import { describe, it, expect, jest, beforeAll, afterAll } from '@jest/globals'; import { Request, Response } from 'express'; import { BasePath } from '@fireblocks/ts-sdk'; import { SdkApiError } from '../../types/errors.js'; +import { ApiController } from '../../api/controllers/controller.js'; +import { initConfig, resetConfig } from '../../utils/config.js'; // We'll test the exported getWebhookEnvironment function by importing the module // For private methods, we'll test them indirectly through public methods @@ -364,6 +366,151 @@ describe('Response Format Consistency', () => { }); }); +// M-01: webhook signature must be verified against the RAW request body BEFORE +// any JSON parsing of untrusted input. +describe('ApiController.enrichWebhookPayload - M-01 signature-before-parse', () => { + beforeAll(() => { + initConfig({ + FIREBLOCKS: { + apiKey: 'test-key', + secretKey: 'test-secret', + basePath: BasePath.US, + }, + }); + }); + + afterAll(() => { + resetConfig(); + }); + + + type SdkStub = { + verifyWebhook: jest.Mock<(...args: unknown[]) => Promise>; + enrichWebhookPayload: jest.Mock<(...args: unknown[]) => Promise>; + }; + + const buildController = (sdk: SdkStub) => { + const sdkManager = { + withSdk: jest.fn(async (_vault: string, fn: (s: SdkStub) => Promise) => fn(sdk)), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + return new ApiController(sdkManager); + }; + + const validPayload = { id: 'tx-1', data: { destination: { id: '7' } } }; + const validJson = JSON.stringify(validPayload); + + it('returns 400 when the body is not a Buffer', async () => { + const sdk: SdkStub = { + verifyWebhook: jest.fn(async () => true), + enrichWebhookPayload: jest.fn(async () => ({})), + }; + const controller = buildController(sdk); + const req = { body: validPayload, headers: {} } as unknown as Request; + const res = createMockResponse(); + + await controller.enrichWebhookPayload(req, res as Response); + + expect(res.status).toHaveBeenCalledWith(400); + expect(sdk.verifyWebhook).not.toHaveBeenCalled(); + expect(sdk.enrichWebhookPayload).not.toHaveBeenCalled(); + }); + + it('returns 400 when the body Buffer is empty', async () => { + const sdk: SdkStub = { + verifyWebhook: jest.fn(async () => true), + enrichWebhookPayload: jest.fn(async () => ({})), + }; + const controller = buildController(sdk); + const req = { body: Buffer.alloc(0), headers: {} } as unknown as Request; + const res = createMockResponse(); + + await controller.enrichWebhookPayload(req, res as Response); + + expect(res.status).toHaveBeenCalledWith(400); + expect(sdk.verifyWebhook).not.toHaveBeenCalled(); + }); + + it('passes the RAW Buffer (not a parsed object) to verifyWebhook', async () => { + const rawBody = Buffer.from(validJson, 'utf8'); + const sdk: SdkStub = { + verifyWebhook: jest.fn(async () => true), + enrichWebhookPayload: jest.fn(async () => ({ enriched: true })), + }; + const controller = buildController(sdk); + const req = { + body: rawBody, + headers: { 'fireblocks-signature': 'sig-abc' }, + } as unknown as Request; + const res = createMockResponse(); + + await controller.enrichWebhookPayload(req, res as Response); + + expect(sdk.verifyWebhook).toHaveBeenCalledTimes(1); + const firstArg = sdk.verifyWebhook.mock.calls[0][0]; + expect(Buffer.isBuffer(firstArg)).toBe(true); + expect((firstArg as Buffer).equals(rawBody)).toBe(true); + }); + + it('returns 401 when verifyWebhook returns false and does NOT parse JSON or enrich', async () => { + const sdk: SdkStub = { + verifyWebhook: jest.fn(async () => false), + enrichWebhookPayload: jest.fn(async () => ({})), + }; + const controller = buildController(sdk); + const req = { + body: Buffer.from(validJson, 'utf8'), + headers: {}, + } as unknown as Request; + const res = createMockResponse(); + + await controller.enrichWebhookPayload(req, res as Response); + + expect(sdk.verifyWebhook).toHaveBeenCalled(); + expect(sdk.enrichWebhookPayload).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(401); + }); + + it('returns 400 on signature-valid but malformed JSON, without enriching', async () => { + const sdk: SdkStub = { + verifyWebhook: jest.fn(async () => true), + enrichWebhookPayload: jest.fn(async () => ({})), + }; + const controller = buildController(sdk); + const req = { + body: Buffer.from('not json{', 'utf8'), + headers: {}, + } as unknown as Request; + const res = createMockResponse(); + + await controller.enrichWebhookPayload(req, res as Response); + + expect(sdk.verifyWebhook).toHaveBeenCalled(); + expect(sdk.enrichWebhookPayload).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(400); + }); + + it('parses JSON and enriches only after signature verification succeeds', async () => { + const sdk: SdkStub = { + verifyWebhook: jest.fn(async () => true), + enrichWebhookPayload: jest.fn(async () => ({ enriched: true })), + }; + const controller = buildController(sdk); + const req = { + body: Buffer.from(validJson, 'utf8'), + headers: {}, + } as unknown as Request; + const res = createMockResponse(); + + await controller.enrichWebhookPayload(req, res as Response); + + expect(sdk.verifyWebhook).toHaveBeenCalled(); + expect(sdk.enrichWebhookPayload).toHaveBeenCalledTimes(1); + expect(sdk.enrichWebhookPayload.mock.calls[0][0]).toEqual(validPayload); + expect(res.status).toHaveBeenCalledWith(200); + }); +}); + // Helper function to test BasePath mapping function mapBasePathToEnvironment(basePath: BasePath): 'US' | 'EU' | 'EU2' | 'SANDBOX' { const path = basePath as string; diff --git a/src/__tests__/api/validation.test.ts b/src/__tests__/api/validation.test.ts index 58db21ed..cad25667 100644 --- a/src/__tests__/api/validation.test.ts +++ b/src/__tests__/api/validation.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect } from '@jest/globals'; import { z } from 'zod'; +import { bech32 } from 'bech32'; import { vaultAccountIdParamsSchema, poolIdParamsSchema, @@ -12,8 +13,16 @@ import { withdrawRewardsRequestSchema, registerAsDRepRequestSchema, castVoteRequestSchema, + delegateToDRepRequestSchema, } from '../../api/validation.js'; +const encodeBech32 = (hrp: string, bytes: Buffer): string => + bech32.encode(hrp, bech32.toWords(bytes), 1000); + +const VALID_DREP_KEY_HEX = 'a'.repeat(56); // 28 bytes +const VALID_DREP_KEY_BECH32 = encodeBech32('drep', Buffer.alloc(28, 0x11)); +const VALID_DREP_SCRIPT_BECH32 = encodeBech32('drep_script', Buffer.alloc(28, 0x22)); + describe('vaultAccountIdParamsSchema', () => { it('should accept valid vault account ID', () => { const result = vaultAccountIdParamsSchema.parse({ vaultAccountId: '123' }); @@ -524,4 +533,104 @@ describe('castVoteRequestSchema', () => { }); expect(result.fee).toBe(1_000_000); }); + + // M-07: governanceActionId.index must be a uint16 (0..65535) per Conway CDDL + describe('M-07: governanceActionId.index uint16 bound', () => { + it('accepts the maximum allowed index (65535)', () => { + const result = castVoteRequestSchema.parse({ + vaultAccountId: '0', + governanceActionId: { txHash: 'a'.repeat(64), index: 65535 }, + vote: 'yes', + }); + expect(result.governanceActionId.index).toBe(65535); + }); + + it('rejects index above uint16 max (65536)', () => { + expect(() => + castVoteRequestSchema.parse({ + vaultAccountId: '0', + governanceActionId: { txHash: 'a'.repeat(64), index: 65536 }, + vote: 'yes', + }) + ).toThrow(z.ZodError); + }); + + it('rejects a very large index', () => { + expect(() => + castVoteRequestSchema.parse({ + vaultAccountId: '0', + governanceActionId: { txHash: 'a'.repeat(64), index: 2 ** 32 }, + vote: 'yes', + }) + ).toThrow(z.ZodError); + }); + }); +}); + +// M-08: DRep ID must be 56-char hex OR a valid bech32 (drep/drep_script) decoding to 28 bytes +describe('delegateToDRepRequestSchema - M-08 DRep ID validation', () => { + const baseValid = { + vaultAccountId: '0', + drepAction: 'custom-drep' as const, + }; + + it('accepts a 56-character hex DRep ID', () => { + const result = delegateToDRepRequestSchema.parse({ + ...baseValid, + drepId: VALID_DREP_KEY_HEX, + }); + expect(result.drepId).toBe(VALID_DREP_KEY_HEX); + }); + + it('accepts a valid drep1... bech32 DRep ID', () => { + const result = delegateToDRepRequestSchema.parse({ + ...baseValid, + drepId: VALID_DREP_KEY_BECH32, + }); + expect(result.drepId).toBe(VALID_DREP_KEY_BECH32); + }); + + it('accepts a valid drep_script1... bech32 DRep ID', () => { + const result = delegateToDRepRequestSchema.parse({ + ...baseValid, + drepId: VALID_DREP_SCRIPT_BECH32, + }); + expect(result.drepId).toBe(VALID_DREP_SCRIPT_BECH32); + }); + + it('rejects a bech32 string with an invalid checksum', () => { + // Corrupt the last char of a valid bech32 string to break the checksum + const corrupted = + VALID_DREP_KEY_BECH32.slice(0, -1) + + (VALID_DREP_KEY_BECH32.endsWith('q') ? 'p' : 'q'); + expect(() => + delegateToDRepRequestSchema.parse({ ...baseValid, drepId: corrupted }) + ).toThrow(z.ZodError); + }); + + it('rejects a bech32 string whose payload is not 28 bytes', () => { + const wrongLen = encodeBech32('drep', Buffer.alloc(20, 0x11)); + expect(() => + delegateToDRepRequestSchema.parse({ ...baseValid, drepId: wrongLen }) + ).toThrow(z.ZodError); + }); + + it('rejects a bech32 string with an unknown HRP', () => { + const wrongHrp = encodeBech32('stake', Buffer.alloc(28, 0x11)); + expect(() => + delegateToDRepRequestSchema.parse({ ...baseValid, drepId: wrongHrp }) + ).toThrow(z.ZodError); + }); + + it('rejects a hex string of the wrong length', () => { + expect(() => + delegateToDRepRequestSchema.parse({ ...baseValid, drepId: 'ab'.repeat(20) }) + ).toThrow(z.ZodError); + }); + + it('rejects free-form strings that match neither hex nor bech32', () => { + expect(() => + delegateToDRepRequestSchema.parse({ ...baseValid, drepId: 'not-a-drep-id' }) + ).toThrow(z.ZodError); + }); }); diff --git a/src/__tests__/utils/cardano.test.ts b/src/__tests__/utils/cardano.test.ts new file mode 100644 index 00000000..e32e124d --- /dev/null +++ b/src/__tests__/utils/cardano.test.ts @@ -0,0 +1,113 @@ +import { describe, it, expect } from '@jest/globals'; +import { bech32 } from 'bech32'; +import { isSpendableUtxo, assertRecipientAddress } from '../../utils/cardano.js'; +import { UtxoData, Networks, SdkApiError } from '../../types/index.js'; + +const makeBaseAddress = (mainnet: boolean): string => { + // Header byte 0x01 = base address, payment-key + stake-key. + // Network nibble: 1 = mainnet, 0 = preprod. + const header = Buffer.from([mainnet ? 0x01 : 0x00]); + const payment = Buffer.alloc(28, 0x11); + const stake = Buffer.alloc(28, 0x22); + const hrp = mainnet ? 'addr' : 'addr_test'; + return bech32.encode(hrp, bech32.toWords(Buffer.concat([header, payment, stake])), 1000); +}; + +const makeUtxo = (overrides: Partial = {}): UtxoData => ({ + transaction_id: 'a'.repeat(64), + output_index: 0, + address: 'addr1qx', + value: { lovelace: 1_500_000, assets: {} }, + datum_hash: null, + script_hash: null, + created_at: { slot_no: 0, header_hash: 'b'.repeat(64) }, + ...overrides, +}); + +// M-06: filter out UTxOs that cannot be spent with a simple Ed25519 witness. +describe('isSpendableUtxo (M-06)', () => { + it('accepts a plain UTxO (no datum, no script)', () => { + expect(isSpendableUtxo(makeUtxo())).toBe(true); + }); + + it('rejects a UTxO with a datum_hash', () => { + expect(isSpendableUtxo(makeUtxo({ datum_hash: 'c'.repeat(64) }))).toBe(false); + }); + + it('rejects a UTxO with a script_hash', () => { + expect(isSpendableUtxo(makeUtxo({ script_hash: 'd'.repeat(56) }))).toBe(false); + }); + + it('rejects a UTxO with both datum_hash and script_hash', () => { + expect( + isSpendableUtxo( + makeUtxo({ + datum_hash: 'c'.repeat(64), + script_hash: 'd'.repeat(56), + }) + ) + ).toBe(false); + }); + + it('treats empty strings as falsy (no datum/script)', () => { + // Defensive: although the API types use string | null, an empty string + // also indicates "absent" and must still be spendable. + expect( + isSpendableUtxo( + makeUtxo({ + datum_hash: '' as unknown as string | null, + script_hash: '' as unknown as string | null, + }) + ) + ).toBe(true); + }); +}); + +// M-02: recipientAddress must be a valid bech32 address on the SDK's network. +describe('assertRecipientAddress (M-02)', () => { + it('accepts a mainnet address when SDK is on mainnet', () => { + expect(() => assertRecipientAddress(makeBaseAddress(true), Networks.MAINNET)).not.toThrow(); + }); + + it('accepts a preprod address when SDK is on preprod', () => { + expect(() => assertRecipientAddress(makeBaseAddress(false), Networks.PREPROD)).not.toThrow(); + }); + + it('rejects a mainnet address when SDK is on preprod', () => { + expect(() => assertRecipientAddress(makeBaseAddress(true), Networks.PREPROD)).toThrow( + SdkApiError + ); + }); + + it('rejects a preprod address when SDK is on mainnet', () => { + expect(() => assertRecipientAddress(makeBaseAddress(false), Networks.MAINNET)).toThrow( + SdkApiError + ); + }); + + it('rejects malformed bech32 with ValidationError', () => { + try { + assertRecipientAddress('addr1notreallyvalid', Networks.MAINNET); + throw new Error('expected to throw'); + } catch (err) { + expect(err).toBeInstanceOf(SdkApiError); + expect((err as SdkApiError).statusCode).toBe(400); + expect((err as SdkApiError).errorType).toBe('ValidationError'); + } + }); + + it('rejects a non-cardano string with ValidationError', () => { + expect(() => assertRecipientAddress('hello world', Networks.MAINNET)).toThrow(SdkApiError); + }); + + it('returns descriptive error info for network mismatch', () => { + try { + assertRecipientAddress(makeBaseAddress(true), Networks.PREPROD); + throw new Error('expected to throw'); + } catch (err) { + const e = err as SdkApiError; + expect(e.message).toMatch(/network mismatch/i); + expect(e.errorInfo).toMatchObject({ expectedNetworkId: 0, actualNetworkId: 1 }); + } + }); +}); diff --git a/src/__tests__/utils/staking.utils.test.ts b/src/__tests__/utils/staking.utils.test.ts new file mode 100644 index 00000000..389c91b8 --- /dev/null +++ b/src/__tests__/utils/staking.utils.test.ts @@ -0,0 +1,101 @@ +import { describe, it, expect } from '@jest/globals'; +import { bech32 } from 'bech32'; +import { buildPayload } from '../../utils/staking.utils.js'; +import { Networks } from '../../types/index.js'; + +/** + * Helper: build a syntactically valid Shelley-era preprod base address + * (header 0x00 + 28-byte payment hash + 28-byte stake hash) so buildPayload + * has a parseable target for the output. + */ +const makePreprodAddress = (): string => { + const header = Buffer.from([0x00]); + const payment = Buffer.alloc(28, 0x11); + const stake = Buffer.alloc(28, 0x22); + const fullBytes = Buffer.concat([header, payment, stake]); + return bech32.encode('addr_test', bech32.toWords(fullBytes), 1000); +}; + +/** + * Look for the exact CBOR byte sequence for an unsigned integer value + * (major type 0). cbor2 emits the minimum-length form, and BigInt values + * are encoded with the same major-type-0 layout as Numbers, so a successful + * search proves the value was serialized as a CBOR unsigned int with full + * precision. + */ +const cborUintBytes = (value: bigint): number[] => { + if (value < 0n) throw new Error('expected unsigned'); + if (value < 24n) return [Number(value)]; + if (value < 256n) return [0x18, Number(value)]; + if (value < 65536n) return [0x19, Number(value >> 8n) & 0xff, Number(value) & 0xff]; + if (value < 0x1_0000_0000n) { + return [ + 0x1a, + Number((value >> 24n) & 0xffn), + Number((value >> 16n) & 0xffn), + Number((value >> 8n) & 0xffn), + Number(value & 0xffn), + ]; + } + // 8-byte form for >= 2^32 + const out = [0x1b]; + for (let i = 7n; i >= 0n; i--) { + out.push(Number((value >> (i * 8n)) & 0xffn)); + } + return out; +}; + +const bufferContainsSequence = (buf: Buffer, seq: number[]): boolean => { + outer: for (let i = 0; i <= buf.length - seq.length; i++) { + for (let j = 0; j < seq.length; j++) { + if (buf[i + j] !== seq[j]) continue outer; + } + return true; + } + return false; +}; + +// M-04: hand-built CBOR transaction body must encode lovelace and fee with +// full integer precision (no JS Number 53-bit truncation). +describe('buildPayload - M-04 BigInt lovelace/fee encoding', () => { + const baseOptions = { + toAddress: makePreprodAddress(), + txInputs: [{ txHash: Buffer.alloc(32, 0xaa), indexInTx: 0 }], + network: Networks.PREPROD, + }; + + it('encodes typical lovelace and fee as CBOR unsigned integers', () => { + const { serialized } = buildPayload({ + ...baseOptions, + netAmount: 2_000_000, + feeAmount: 200_000, + }); + + expect(bufferContainsSequence(serialized, cborUintBytes(2_000_000n))).toBe(true); + expect(bufferContainsSequence(serialized, cborUintBytes(200_000n))).toBe(true); + }); + + it('uses 8-byte CBOR uint encoding for lovelace > 2^32', () => { + const big = 5_000_000_000n; // 5 billion lovelace = 5,000 ADA, > 2^32 + const { serialized } = buildPayload({ + ...baseOptions, + netAmount: Number(big), + feeAmount: 200_000, + }); + + const expected = cborUintBytes(big); + expect(expected[0]).toBe(0x1b); // 8-byte uint header + expect(bufferContainsSequence(serialized, expected)).toBe(true); + }); + + it('rounds fractional inputs to integers before BigInt conversion', () => { + const { serialized } = buildPayload({ + ...baseOptions, + netAmount: 1_500_000.6, + feeAmount: 200_000.4, + }); + + expect(bufferContainsSequence(serialized, cborUintBytes(1_500_001n))).toBe(true); + expect(bufferContainsSequence(serialized, cborUintBytes(200_000n))).toBe(true); + }); +}); From d72ebe6c1afde882187327eb6182e10f66304fba Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:06:52 +0300 Subject: [PATCH 17/65] fix(M-09): drop internal info and service fields from error responses handleError previously echoed SdkApiError.errorInfo and .service back to the API consumer, leaking internal context (queried vault account ids, upstream service names, dependency metadata). Keep both fields in the server-side log only; the public JSON now exposes just success, error message, statusCode and error type. --- src/api/controllers/controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/controllers/controller.ts b/src/api/controllers/controller.ts index b0d869f3..1d44e168 100644 --- a/src/api/controllers/controller.ts +++ b/src/api/controllers/controller.ts @@ -787,13 +787,13 @@ export class ApiController { message: error.message, }); + // errorInfo and service are logged server-side only; keep the public + // response minimal to avoid leaking internal context. res.status(statusCode).json({ success: false, error: error.message, statusCode: error.statusCode, type: error.errorType, - info: error.errorInfo, - service: error.service, }); } else { const message = error instanceof Error ? error.message : "Unknown error"; From 6db4e5c67cc531a7bdc690043bcf50f77b2f1e1f Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:08:20 +0300 Subject: [PATCH 18/65] fix(M-10): throw when fee convergence loop fails instead of returning last estimate Previously convergeTransactionFee logged a warning and returned the last currentFee if the loop did not converge within TX_FEE_MAX_ITERATIONS. That value can be below the actual minimum fee for the resulting tx body, leaving callers to build and submit an underpaid transaction that the network rejects (or, worse, that silently differs from the declared fee). Throw an SdkApiError on non-convergence so the failure surfaces immediately and the caller can retry or escalate. --- src/utils/cardano.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/utils/cardano.ts b/src/utils/cardano.ts index ca50e05c..a7aef650 100644 --- a/src/utils/cardano.ts +++ b/src/utils/cardano.ts @@ -789,12 +789,16 @@ const convergeTransactionFee = ( currentFee = calculatedFee; } - logger.warn( - `[${label}] fee did not converge after ${CardanoConstants.TX_FEE_MAX_ITERATIONS} iterations` + // Non-convergence is unsafe: returning the last estimate would build a tx + // whose declared fee is below the network minimum, and the node will reject + // it. Fail loudly instead so the caller can retry or surface the error. + throw new SdkApiError( + `[${label}] fee did not converge after ${CardanoConstants.TX_FEE_MAX_ITERATIONS} iterations`, + 500, + "FeeConvergenceError", + { label, lastFee: currentFee, maxIterations: CardanoConstants.TX_FEE_MAX_ITERATIONS }, + "FireblocksCardanoRawSDK" ); - const finalOutputs = buildOutputsFn(currentFee); - const finalTxBody = buildTransaction({ txInputs, txOutputs: finalOutputs, fee: currentFee, ttl }); - return { outputs: finalOutputs, fee: currentFee, txBody: finalTxBody }; }; /** From c703a23d0ddbcb6b0c85472c4c97f1ff3d465ad2 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:11:28 +0300 Subject: [PATCH 19/65] fix(M-11): align DRep ID encode/decode/validate with CIP-129 encodeDRepId previously used HRP "drep_script" for script-based DReps, which is non-standard: per CIP-129 the HRP is always "drep" and the type is encoded in the leading header byte (0x22 key, 0x23 script). decodeDRepId required a 29-byte payload, while the request validator required 28 bytes, so legitimate CIP-129 inputs were rejected at the API edge while legitimate CIP-105 inputs failed inside the SDK. After this change: - encodeDRepId emits CIP-129 (HRP=drep, 29 bytes). - decodeDRepId accepts CIP-129 (29 bytes, header-tagged) AND CIP-105 legacy (28 bytes, HRP-tagged: drep / drep_script). - isValidDrepId mirrors decodeDRepId: hex, CIP-105 (28B), or CIP-129 (29B with HRP=drep only). 29-byte payloads with HRP=drep_script are rejected as malformed. --- src/__tests__/api/validation.test.ts | 35 +++++++++++++++++++++++++++ src/api/validation.ts | 11 ++++++--- src/utils/staking.utils.ts | 36 ++++++++++++++-------------- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/__tests__/api/validation.test.ts b/src/__tests__/api/validation.test.ts index cad25667..48b77808 100644 --- a/src/__tests__/api/validation.test.ts +++ b/src/__tests__/api/validation.test.ts @@ -22,6 +22,15 @@ const encodeBech32 = (hrp: string, bytes: Buffer): string => const VALID_DREP_KEY_HEX = 'a'.repeat(56); // 28 bytes const VALID_DREP_KEY_BECH32 = encodeBech32('drep', Buffer.alloc(28, 0x11)); const VALID_DREP_SCRIPT_BECH32 = encodeBech32('drep_script', Buffer.alloc(28, 0x22)); +// CIP-129: HRP="drep", 29-byte payload (header 0x22 key / 0x23 script + 28-byte hash) +const VALID_DREP_KEY_CIP129 = encodeBech32( + 'drep', + Buffer.concat([Buffer.from([0x22]), Buffer.alloc(28, 0x11)]) +); +const VALID_DREP_SCRIPT_CIP129 = encodeBech32( + 'drep', + Buffer.concat([Buffer.from([0x23]), Buffer.alloc(28, 0x22)]) +); describe('vaultAccountIdParamsSchema', () => { it('should accept valid vault account ID', () => { @@ -598,6 +607,32 @@ describe('delegateToDRepRequestSchema - M-08 DRep ID validation', () => { expect(result.drepId).toBe(VALID_DREP_SCRIPT_BECH32); }); + it('accepts a CIP-129 key-hash DRep ID (HRP=drep, 29-byte payload, header 0x22)', () => { + const result = delegateToDRepRequestSchema.parse({ + ...baseValid, + drepId: VALID_DREP_KEY_CIP129, + }); + expect(result.drepId).toBe(VALID_DREP_KEY_CIP129); + }); + + it('accepts a CIP-129 script-hash DRep ID (HRP=drep, 29-byte payload, header 0x23)', () => { + const result = delegateToDRepRequestSchema.parse({ + ...baseValid, + drepId: VALID_DREP_SCRIPT_CIP129, + }); + expect(result.drepId).toBe(VALID_DREP_SCRIPT_CIP129); + }); + + it('rejects a 29-byte payload with HRP drep_script (CIP-129 only uses HRP drep)', () => { + const invalid = encodeBech32( + 'drep_script', + Buffer.concat([Buffer.from([0x23]), Buffer.alloc(28, 0x22)]) + ); + expect(() => + delegateToDRepRequestSchema.parse({ ...baseValid, drepId: invalid }) + ).toThrow(z.ZodError); + }); + it('rejects a bech32 string with an invalid checksum', () => { // Corrupt the last char of a valid bech32 string to break the checksum const corrupted = diff --git a/src/api/validation.ts b/src/api/validation.ts index 70239937..457afd62 100644 --- a/src/api/validation.ts +++ b/src/api/validation.ts @@ -5,9 +5,10 @@ import { bech32 } from "bech32"; import { getProtocolParams } from "../utils/protocolParams.js"; /** - * Validate a DRep ID. Accepts either: + * Validate a DRep ID. Accepts: * - a 56-char hex string (28-byte credential), or - * - a bech32 string with HRP "drep" or "drep_script" decoding to 28 bytes. + * - CIP-105 bech32 (HRP "drep" or "drep_script", 28-byte payload), or + * - CIP-129 bech32 (HRP "drep", 29-byte payload = header + 28-byte hash). * Verifies the bech32 checksum and payload length. */ const isValidDrepId = (id: string): boolean => { @@ -17,7 +18,11 @@ const isValidDrepId = (id: string): boolean => { const decoded = bech32.decode(id, 1000); if (decoded.prefix !== "drep" && decoded.prefix !== "drep_script") return false; const bytes = bech32.fromWords(decoded.words); - return bytes.length === 28; + // CIP-105 = 28 bytes (raw hash); CIP-129 = 29 bytes (header byte + hash). + // CIP-129 only uses HRP "drep"; "drep_script" implies CIP-105. + if (bytes.length === 28) return true; + if (bytes.length === 29 && decoded.prefix === "drep") return true; + return false; } catch { return false; } diff --git a/src/utils/staking.utils.ts b/src/utils/staking.utils.ts index f9507567..3b01bd6c 100644 --- a/src/utils/staking.utils.ts +++ b/src/utils/staking.utils.ts @@ -442,26 +442,27 @@ const validateAnchorDataHash = (dataHash: string): void => { }; const decodeDRepId = (drepId: string): { keyHash: Buffer; isScript: boolean } => { - // Check if it's bech32 format (starts with drep1 or drep_script1) + // Bech32 forms accepted: + // - CIP-129: HRP "drep", 29-byte payload (header 0x22/0x23 + 28-byte hash) + // - CIP-105 legacy: HRP "drep" (key) or "drep_script" (script), 28-byte payload if (drepId.startsWith("drep1") || drepId.startsWith("drep_script1")) { const decoded = bech32.decode(drepId, 1000); const fullBytes = Buffer.from(bech32.fromWords(decoded.words)); - // Expected: 1 header byte + 28 credential bytes = 29 bytes total - if (fullBytes.length !== DREP_CREDENTIAL_BYTES + 1) { - throw new Error( - `Invalid DRep ID: bech32 payload must be ${DREP_CREDENTIAL_BYTES + 1} bytes, got ${fullBytes.length}` - ); + if (fullBytes.length === DREP_CREDENTIAL_BYTES + 1) { + // CIP-129: header 0x22 = key-hash, 0x23 = script-hash + const isScript = (fullBytes[0] & 0x01) === 1; + return { keyHash: Buffer.from(fullBytes.subarray(1)), isScript }; } - // First byte is the header indicating the type - const header = fullBytes[0]; - const keyHash = Buffer.from(fullBytes.subarray(1)); // Skip header byte to get 28-byte key hash - - // Header format: 0b00100010 (0x22) for key hash, 0b00100011 (0x23) for script hash - const isScript = (header & 0x01) === 1; + if (fullBytes.length === DREP_CREDENTIAL_BYTES) { + // CIP-105: HRP distinguishes key vs script + return { keyHash: fullBytes, isScript: decoded.prefix === "drep_script" }; + } - return { keyHash, isScript }; + throw new Error( + `Invalid DRep ID: bech32 payload must be ${DREP_CREDENTIAL_BYTES} or ${DREP_CREDENTIAL_BYTES + 1} bytes, got ${fullBytes.length}` + ); } // Hex format: must be exactly 56 hex characters (28 bytes) @@ -562,16 +563,15 @@ export const buildDRepRegistrationCertificate = ( }; /** - * Encode a stake credential as a bech32 DRep ID - * Key-based DRep: hrp="drep", header=0x22 - * Script-based DRep: hrp="drep_script", header=0x23 + * Encode a stake credential as a CIP-129 DRep ID. + * Per CIP-129 the HRP is always "drep"; the type is encoded in the leading + * header byte (0x22 = key-hash, 0x23 = script-hash). */ export const encodeDRepId = (credential: Buffer, isScript: boolean = false): string => { const header = Buffer.from([isScript ? 0x23 : 0x22]); const fullBytes = Buffer.concat([header, credential]); const words = bech32.toWords(fullBytes); - const hrp = isScript ? "drep_script" : "drep"; - return bech32.encode(hrp, words, 1000); + return bech32.encode("drep", words, 1000); }; /** From a73dfe5ecf5235d46f896641bcf894d1ed10de3c Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:31:07 +0300 Subject: [PATCH 20/65] fix(M-12): reject oversize transactions before signing Add CardanoConstants.MAX_TX_SIZE_BYTES (16384) and assertTxSizeWithinLimit in utils/cardano. Call it from FireblocksCardanoRawSDK.signTransaction and from StakingService just before forwarding to Fireblocks, so a transaction body that would exceed the protocol maxTxSize is rejected with a TxSizeLimitExceeded error instead of consuming a signing quota and being refused by the node. --- src/FireblocksCardanoRawSDK.ts | 2 ++ src/constants.ts | 6 ++++++ src/services/staking/staking.service.ts | 2 ++ src/utils/cardano.ts | 28 +++++++++++++++++++++++++ 4 files changed, 38 insertions(+) diff --git a/src/FireblocksCardanoRawSDK.ts b/src/FireblocksCardanoRawSDK.ts index 15f5f2f9..70f8db8b 100644 --- a/src/FireblocksCardanoRawSDK.ts +++ b/src/FireblocksCardanoRawSDK.ts @@ -30,6 +30,7 @@ import { utxoLocks, setProtocolParams, assertRecipientAddress, + assertTxSizeWithinLimit, } from "./utils/index.js"; import { @@ -887,6 +888,7 @@ export class FireblocksCardanoRawSDK { txBody: TransactionBody, assetId: SupportedAssets = SupportedAssets.ADA ): Promise { + assertTxSizeWithinLimit(txBody.to_bytes(), "signTransaction"); const txHashHex = this.calculateTransactionHash(txBody); const transactionPayload = this.createFireblocksTransactionPayload(assetId, txHashHex); diff --git a/src/constants.ts b/src/constants.ts index c518b1b9..9c09d632 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -38,6 +38,12 @@ export enum CardanoConstants { * independent protocol constants that must be updated separately if either changes. */ MIN_UTXO_BASE_LOVELACE = 1_000_000, + /** + * Maximum on-chain transaction size in bytes (Cardano protocol parameter + * maxTxSize). Conway mainnet is 16,384. We refuse to sign anything larger + * because the network would reject it. + */ + MAX_TX_SIZE_BYTES = 16_384, } export enum CardanoAmounts { diff --git a/src/services/staking/staking.service.ts b/src/services/staking/staking.service.ts index b61a3c3b..65e94ee5 100644 --- a/src/services/staking/staking.service.ts +++ b/src/services/staking/staking.service.ts @@ -21,6 +21,7 @@ import { embedSignaturesInTx, getSigningPayload, drepActionToDRepInfo, + assertTxSizeWithinLimit, } from "../../utils/index.js"; import { @@ -710,6 +711,7 @@ export class StakingService { }); // Sign transaction + assertTxSizeWithinLimit(serialized, `staking:${operation}`); const txHash = getSigningPayload(serialized); const witnesses = await this.transactionSigner.signTransaction({ txHash: txHash.toString("hex"), diff --git a/src/utils/cardano.ts b/src/utils/cardano.ts index a7aef650..c2e9389e 100644 --- a/src/utils/cardano.ts +++ b/src/utils/cardano.ts @@ -130,6 +130,34 @@ export const calculateTransactionFee = (tx: Transaction): number => { return parseInt(calculatedFee.to_str()); }; +/** + * Throws if a serialized transaction would exceed the network maxTxSize. + * Uses a conservative projection that accounts for one Ed25519 witness and + * the surrounding CBOR overhead, so the check is meaningful for the + * already-signed and not-yet-signed forms alike. + */ +export const assertTxSizeWithinLimit = ( + serializedTxBodyOrTx: Uint8Array, + context: string +): void => { + const bodyLen = serializedTxBodyOrTx.length; + const projected = bodyLen + CardanoConstants.TX_WITNESS_SIZE_BYTES + 16; + if (projected > CardanoConstants.MAX_TX_SIZE_BYTES) { + throw new SdkApiError( + `Transaction too large: estimated ${projected} bytes exceeds maxTxSize=${CardanoConstants.MAX_TX_SIZE_BYTES}`, + 400, + "TxSizeLimitExceeded", + { + context, + bodyBytes: bodyLen, + projectedSignedBytes: projected, + maxTxSize: CardanoConstants.MAX_TX_SIZE_BYTES, + }, + "FireblocksCardanoRawSDK" + ); + } +}; + /** * A UTxO is spendable by this SDK only with a simple Ed25519 witness. * UTxOs carrying a datum_hash (Plutus v1/v2 inputs) or a script_hash From 29a5299020ba764c6bd03aa924020a01be907198 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:32:42 +0300 Subject: [PATCH 21/65] fix(M-13): do not leak raw exception messages on unhandled errors When an exception was not an SdkApiError, handleError surfaced the raw error.message back to the API consumer, which can leak stack-derived details, file paths, internal identifiers, or upstream failure messages. Log the full message and stack server-side and return a generic Internal server error response with a stable type discriminator. --- src/api/controllers/controller.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/api/controllers/controller.ts b/src/api/controllers/controller.ts index 1d44e168..89ddb709 100644 --- a/src/api/controllers/controller.ts +++ b/src/api/controllers/controller.ts @@ -796,11 +796,17 @@ export class ApiController { type: error.errorType, }); } else { - const message = error instanceof Error ? error.message : "Unknown error"; + // Log the full error server-side, but never echo arbitrary internal + // exception messages to the API consumer. + const message = error instanceof Error ? error.message : String(error); this.logger.error(`${endpoint} - UnhandledError:`, message); + if (error instanceof Error && error.stack) { + this.logger.error(`${endpoint} - stack:`, error.stack); + } res.status(500).json({ success: false, - error: message, + error: "Internal server error", + type: "InternalServerError", }); } } From 56d60ea9a448603de81e8e928f50852e05a9abfe Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:37:43 +0300 Subject: [PATCH 22/65] fix(M-14): follow pagination when aggregating vault history fetchAllVaultHistory previously made one history call per address and treated whatever subset the API chose to return as the complete result. For accounts with more than one page of history, getAllTransactionHistory and getAllDetailedTxHistory silently dropped older transactions. Introduce fetchAllPagesForAddress which walks the upstream pagination (hasMore + short-page fallback) until the stream ends, with a hard cap of 1000 pages * 100 rows = 100k transactions per address. When the caller supplies an explicit limit, the original single-page behaviour is preserved so existing query shapes keep working. --- src/FireblocksCardanoRawSDK.ts | 61 ++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/FireblocksCardanoRawSDK.ts b/src/FireblocksCardanoRawSDK.ts index 70f8db8b..5f27d703 100644 --- a/src/FireblocksCardanoRawSDK.ts +++ b/src/FireblocksCardanoRawSDK.ts @@ -511,6 +511,58 @@ export class FireblocksCardanoRawSDK { throw new Error(`${context}Failed: ${errorMessage}`); } + /** + * Iterates the underlying paginated history endpoint for a single address + * and concatenates every page until pagination.hasMore is false (or the + * server reports no more rows). If the caller pinned an explicit limit + * the original single-page semantics are preserved so existing query + * shapes still work. + */ + private async fetchAllPagesForAddress( + fetchFn: (params: { + address: string; + limit?: number; + offset?: number; + fromSlot?: number; + }) => Promise<{ + success: boolean; + data?: T[]; + last_updated?: LastUpdated; + pagination?: TransactionPagination; + }>, + address: string, + options: { limit?: number; offset?: number; fromSlot?: number } + ): Promise<{ success: boolean; data?: T[]; last_updated?: LastUpdated }> { + if (options.limit !== undefined) { + return fetchFn({ address, ...options }); + } + + const PAGE_SIZE = 100; + const MAX_PAGES = 1000; // safety bound: 100k transactions per address + const aggregated: T[] = []; + let lastUpdated: LastUpdated | undefined; + let offset = options.offset ?? 0; + + for (let page = 0; page < MAX_PAGES; page++) { + const response = await fetchFn({ + address, + limit: PAGE_SIZE, + offset, + fromSlot: options.fromSlot, + }); + const items = response.data ?? []; + aggregated.push(...items); + lastUpdated = response.last_updated ?? lastUpdated; + + const hasMore = response.pagination?.hasMore; + if (hasMore === false) break; + if (items.length < PAGE_SIZE) break; // server returned a short page → end of stream + offset += items.length; + } + + return { success: true, data: aggregated, last_updated: lastUpdated }; + } + /** * Fetches transaction history across all vault addresses. * Shared by getAllTransactionHistory() and getAllDetailedTxHistory(). @@ -521,7 +573,12 @@ export class FireblocksCardanoRawSDK { limit?: number; offset?: number; fromSlot?: number; - }) => Promise<{ success: boolean; data?: T[]; last_updated?: LastUpdated }>, + }) => Promise<{ + success: boolean; + data?: T[]; + last_updated?: LastUpdated; + pagination?: TransactionPagination; + }>, options: { limit?: number; offset?: number; fromSlot?: number; groupByAddress?: boolean } ): Promise<{ success: boolean; @@ -554,7 +611,7 @@ export class FireblocksCardanoRawSDK { const validAddresses = addressesResponse.filter((addr) => addr.address); const allHistories = await Promise.all( - validAddresses.map((addr) => fetchFn({ address: addr.address!, ...options })) + validAddresses.map((addr) => this.fetchAllPagesForAddress(fetchFn, addr.address!, options)) ); const mostRecentUpdate = allHistories.reduce((latest, current) => { From d670ba5d4f89093e4a98cbe94602de1899e03b55 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:38:44 +0300 Subject: [PATCH 23/65] fix(M-15): reject delegation to retired or unknown pools StakingValidator.checkDelegationPrerequisites previously fetched getPoolInfo and discarded the response. Now it inspects the result and throws PoolNotFound (404) when the pool does not exist or PoolRetired (400) when the pool has at least one retirement certificate filed, preventing delegation transactions that the chain would either reject or that would be effectively wasted on a soon-to-be-retired pool. --- .../helpers/staking-validator.helper.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/services/staking/helpers/staking-validator.helper.ts b/src/services/staking/helpers/staking-validator.helper.ts index c100c465..35a37b4f 100644 --- a/src/services/staking/helpers/staking-validator.helper.ts +++ b/src/services/staking/helpers/staking-validator.helper.ts @@ -50,7 +50,25 @@ export class StakingValidator implements IStakingValidator { this.logger.info(`Already delegated to pool ${poolId}`); } - await this.iagonApiService.getPoolInfo(poolId); + const poolInfo = await this.iagonApiService.getPoolInfo(poolId); + if (!poolInfo?.data) { + throw new SdkApiError( + `Pool ${poolId} not found`, + 404, + "PoolNotFound", + { poolId }, + "staking-service" + ); + } + if (poolInfo.data.retirement && poolInfo.data.retirement.length > 0) { + throw new SdkApiError( + `Pool ${poolId} has filed a retirement certificate and cannot accept new delegations`, + 400, + "PoolRetired", + { poolId, retirement: poolInfo.data.retirement }, + "staking-service" + ); + } } async checkRegistrationStatus(vaultAccountId: string): Promise { From b9b328a482414f3c9f0cc7a4509384cedc41bf34 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:42:29 +0300 Subject: [PATCH 24/65] fix(M-17): make Iagon base URL configurable IagonApiService hardcoded the upstream base URL at the class field level via the iagonBaseUrl module constant, leaving no override path for staging or private Iagon deployments. The URL is now resolved at construction time from (in order): an explicit constructor arg, the IAGON_BASE_URL environment variable, then the built-in default. FireblocksCardanoRawSDK.createInstance forwards an optional iagonBaseUrl param so library consumers can override without touching the environment. Documented in README and .env.example. --- .env.example | 3 +++ README.md | 3 +++ src/FireblocksCardanoRawSDK.ts | 10 +++++++++- src/services/iagon.api.service.ts | 6 ++++-- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index eb2aa521..0ba128c2 100644 --- a/.env.example +++ b/.env.example @@ -24,6 +24,9 @@ FIREBLOCKS_BASE_PATH=https://api.fireblocks.io # ── Iagon API (Required) ───────────────────────────────────── IAGON_API_KEY= +# Optional - override the Iagon API base URL (defaults to https://api.fireblocks.partners.iagon.com). +# Set this for staging or private Iagon deployments. +# IAGON_BASE_URL=https://api.fireblocks.partners.iagon.com # ── Cardano Network ────────────────────────────────────────── # Valid values: mainnet, preprod (defaults to mainnet) diff --git a/README.md b/README.md index aeaab1d1..7a36de6f 100644 --- a/README.md +++ b/README.md @@ -799,6 +799,7 @@ cp .env.example .env | `FIREBLOCKS_API_USER_SECRET_KEY_PATH` | Yes | - | Absolute path to the Fireblocks RSA secret key file | | `FIREBLOCKS_BASE_PATH` | No | `https://api.fireblocks.io` | Fireblocks workspace URL - also controls webhook JWKS region | | `IAGON_API_KEY` | Yes | - | Iagon API key for blockchain data queries | +| `IAGON_BASE_URL` | No | `https://api.fireblocks.partners.iagon.com` | Override the Iagon API base URL (staging / private deployments) | | `CARDANO_NETWORK` | No | `mainnet` | `mainnet` or `preprod` | | `MAX_BODY_SIZE` | No | `1mb` | Maximum request body size | | `RATE_LIMIT_WINDOW_MS` | No | `900000` | Rate limit time window (ms) | @@ -929,6 +930,8 @@ const sdk = await FireblocksCardanoRawSDK.createInstance({ vaultAccountId: "123", network: Networks.MAINNET, iagonApiKey: "...", + // Override the Iagon API base URL (optional - falls back to IAGON_BASE_URL env, then default) + iagonBaseUrl: "https://staging.iagon.example.com", // Override protocol parameters (optional) protocolParams: { minFeeA: 44, diff --git a/src/FireblocksCardanoRawSDK.ts b/src/FireblocksCardanoRawSDK.ts index 5f27d703..066da77e 100644 --- a/src/FireblocksCardanoRawSDK.ts +++ b/src/FireblocksCardanoRawSDK.ts @@ -171,6 +171,12 @@ export class FireblocksCardanoRawSDK { assetCacheTTL?: number; /** Disable SSL certificate verification (use only in development) */ disableSslVerification?: boolean; + /** + * Override the Iagon API base URL. Falls back to the IAGON_BASE_URL + * environment variable and finally to the built-in default. Useful for + * staging/private deployments and tests. + */ + iagonBaseUrl?: string; /** * Override Cardano protocol parameters. * Use this to adapt to protocol changes without waiting for an SDK update. @@ -194,6 +200,7 @@ export class FireblocksCardanoRawSDK { iagonApiKey, assetCacheTTL, disableSslVerification = false, + iagonBaseUrl: iagonBaseUrlOverride, protocolParams, } = params; @@ -212,7 +219,8 @@ export class FireblocksCardanoRawSDK { iagonApiKey, network, assetCacheTTL, - disableSslVerification + disableSslVerification, + iagonBaseUrlOverride ); const stakingService = new StakingService(fireblocksService, iagonApiService, network); const assetId = network === Networks.MAINNET ? SupportedAssets.ADA : SupportedAssets.ADA_TEST; diff --git a/src/services/iagon.api.service.ts b/src/services/iagon.api.service.ts index cf1b23a4..321f0696 100644 --- a/src/services/iagon.api.service.ts +++ b/src/services/iagon.api.service.ts @@ -82,7 +82,7 @@ interface CachedAssetInfo { export class IagonApiService { private readonly logger = new Logger("services:iagon-api-service"); private network: Networks; - private readonly iagonBaseUrl = iagonBaseUrl; + private readonly iagonBaseUrl: string; private readonly iagonApiKey: string; private readonly errorHandler = new ErrorHandler("iagon-api", this.logger); private readonly axiosInstance; @@ -95,7 +95,8 @@ export class IagonApiService { apiKey: string, network: Networks = Networks.MAINNET, assetCacheTTL: number = 1000 * 60 * 60 * 24, // Default: 24 hours - disableSslVerification: boolean = false + disableSslVerification: boolean = false, + baseUrl?: string ) { // Validate API key is provided and not empty if (!apiKey || apiKey.trim() === "") { @@ -125,6 +126,7 @@ export class IagonApiService { this.iagonApiKey = apiKey; this.network = network; this.ASSET_CACHE_TTL = assetCacheTTL; + this.iagonBaseUrl = baseUrl ?? process.env.IAGON_BASE_URL ?? iagonBaseUrl; // Create axios instance with default headers this.axiosInstance = axios.create({ From 945e5f421544a648fb5515424858a09e3a50da73 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:45:22 +0300 Subject: [PATCH 25/65] fix(M-18): plug WASM handle leaks on the signing path signTransaction allocated PublicKey, Vkey, Ed25519Signature, Vkeywitness, Vkeywitnesses, TransactionWitnessSet and Transaction handles from the cardano-serialization-lib WASM module but did not free them on early returns or thrown exceptions. The returned Transaction was also never freed by any caller after submission. Each Fireblocks signing request therefore leaked a handful of WASM allocations. After this change: - signTransaction holds every intermediate handle in named locals and releases them in a try/catch around the success path, so a throw at any stage frees everything that was allocated up to that point. - All four call sites (CNT transfer, multi-token transfer, single-batch consolidation, batched consolidation) wrap submitTransaction in a try/finally that calls signedTransaction.free() once the CBOR has been serialized for the upstream POST. --- src/FireblocksCardanoRawSDK.ts | 126 ++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 40 deletions(-) diff --git a/src/FireblocksCardanoRawSDK.ts b/src/FireblocksCardanoRawSDK.ts index 066da77e..59db9db9 100644 --- a/src/FireblocksCardanoRawSDK.ts +++ b/src/FireblocksCardanoRawSDK.ts @@ -968,46 +968,72 @@ export class FireblocksCardanoRawSDK { const publicKeyBytes = Uint8Array.from(Buffer.from(signatureResponse.publicKey, "hex")); const signatureBytes = Uint8Array.from(Buffer.from(signatureResponse.signature.fullSig, "hex")); - const pubKey = PublicKey.from_bytes(publicKeyBytes); - const cardanoPubKey = Vkey.new(pubKey); - pubKey.free(); - const cardanoSig = Ed25519Signature.from_bytes(signatureBytes); - - const witness = Vkeywitness.new(cardanoPubKey, cardanoSig); - cardanoPubKey.free(); - cardanoSig.free(); - const witnesses = Vkeywitnesses.new(); - witnesses.add(witness); - witness.free(); - - const witnessSet = TransactionWitnessSet.new(); - witnessSet.set_vkeys(witnesses); - witnesses.free(); - - const signedTx = Transaction.new(txBody, witnessSet); - - // Verify the fee is sufficient using Cardano's min_fee calculation - const minRequiredFee = calculateTransactionFee(signedTx); - witnessSet.free(); - const allocatedFee = parseInt(txBody.fee().to_str()); - - if (minRequiredFee > allocatedFee) { - throw new SdkApiError( - `Transaction requires minimum ${minRequiredFee} lovelace but only ${allocatedFee} lovelace was allocated. This indicates a bug in fee calculation.`, - 500, - "FeeEstimationError", - { minRequiredFee, allocatedFee, difference: minRequiredFee - allocatedFee }, + // Track every WASM handle allocated below so we can free them on any + // exception path before returning. The successful path also frees the + // intermediates and only the returned Transaction stays live. + let pubKey: PublicKey | undefined; + let cardanoPubKey: Vkey | undefined; + let cardanoSig: Ed25519Signature | undefined; + let witness: Vkeywitness | undefined; + let witnesses: Vkeywitnesses | undefined; + let witnessSet: TransactionWitnessSet | undefined; + let signedTx: Transaction | undefined; + + try { + pubKey = PublicKey.from_bytes(publicKeyBytes); + cardanoPubKey = Vkey.new(pubKey); + cardanoSig = Ed25519Signature.from_bytes(signatureBytes); + + witness = Vkeywitness.new(cardanoPubKey, cardanoSig); + witnesses = Vkeywitnesses.new(); + witnesses.add(witness); + + witnessSet = TransactionWitnessSet.new(); + witnessSet.set_vkeys(witnesses); + + signedTx = Transaction.new(txBody, witnessSet); + + // Verify the fee is sufficient using Cardano's min_fee calculation + const minRequiredFee = calculateTransactionFee(signedTx); + const allocatedFee = parseInt(txBody.fee().to_str()); + + if (minRequiredFee > allocatedFee) { + throw new SdkApiError( + `Transaction requires minimum ${minRequiredFee} lovelace but only ${allocatedFee} lovelace was allocated. This indicates a bug in fee calculation.`, + 500, + "FeeEstimationError", + { minRequiredFee, allocatedFee, difference: minRequiredFee - allocatedFee }, "FireblocksCardanoRawSDK" ); } - const feeDifference = allocatedFee - minRequiredFee; - this.logger.info( - `Transaction fee verified: allocated ${allocatedFee} lovelace, ` + - `minimum required ${minRequiredFee} lovelace (margin: ${feeDifference} lovelace)` - ); + const feeDifference = allocatedFee - minRequiredFee; + this.logger.info( + `Transaction fee verified: allocated ${allocatedFee} lovelace, ` + + `minimum required ${minRequiredFee} lovelace (margin: ${feeDifference} lovelace)` + ); - return signedTx; + // Successful path: free intermediates, keep signedTx alive for the caller. + pubKey.free(); + cardanoPubKey.free(); + cardanoSig.free(); + witness.free(); + witnesses.free(); + witnessSet.free(); + const result = signedTx; + signedTx = undefined; // prevent the catch/finally from freeing it + return result; + } catch (err) { + // Free any handle that was allocated before the throw. + signedTx?.free(); + witnessSet?.free(); + witnesses?.free(); + witness?.free(); + cardanoSig?.free(); + cardanoPubKey?.free(); + pubKey?.free(); + throw err; + } } // ─── Transfer Architecture ────────────────────────────────────────────────── @@ -1252,8 +1278,13 @@ export class FireblocksCardanoRawSDK { // Sign transaction with Fireblocks const signedTransaction = await this.signTransaction(txBody); - // Submit transaction to blockchain - const txHash = await submitTransaction(this.iagonApiService, signedTransaction); + // Submit transaction to blockchain (free WASM handle once serialized). + let txHash: string; + try { + txHash = await submitTransaction(this.iagonApiService, signedTransaction); + } finally { + signedTransaction.free(); + } this.logger.info(`Transfer successful: ${txHash} (fee: ${feeFormatted.value} ADA)`); @@ -1748,7 +1779,12 @@ export class FireblocksCardanoRawSDK { ); const signedTransaction = await this.signTransaction(txBody); - const txHash = await submitTransaction(this.iagonApiService, signedTransaction); + let txHash: string; + try { + txHash = await submitTransaction(this.iagonApiService, signedTransaction); + } finally { + signedTransaction.free(); + } this.logger.info( `Multi-token transfer successful: ${txHash} (fee: ${feeFormatted.value} ADA)` @@ -1867,7 +1903,12 @@ export class FireblocksCardanoRawSDK { ); const signedTransaction = await this.signTransaction(txBody); - const txHash = await submitTransaction(this.iagonApiService, signedTransaction); + let txHash: string; + try { + txHash = await submitTransaction(this.iagonApiService, signedTransaction); + } finally { + signedTransaction.free(); + } this.logger.info(`UTxO consolidation successful: ${txHash}`); const { lovelace: outputLovelace, tokenPolicies } = this.extractOutputMetadata(outputs[0]); @@ -1930,7 +1971,12 @@ export class FireblocksCardanoRawSDK { ); const signedTransaction = await this.signTransaction(txBody); - const txHash = await submitTransaction(this.iagonApiService, signedTransaction); + let txHash: string; + try { + txHash = await submitTransaction(this.iagonApiService, signedTransaction); + } finally { + signedTransaction.free(); + } batches.push({ txHash, From 69e58cbb67b211912f443f089a9a319f9eab3dc6 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:48:02 +0300 Subject: [PATCH 26/65] fix(M-20): drop docker-compose references from README The repo only ships a Dockerfile - there is no docker-compose.yml - so README instructions telling users to run docker-compose up/logs/down/ restart all fail. Replace the docker-compose snippets with equivalent plain docker run / docker logs / docker stop / docker restart commands and remove docker-compose.yml from the project-tree listing. --- README.md | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7a36de6f..9aff35a2 100644 --- a/README.md +++ b/README.md @@ -379,7 +379,15 @@ Run the SDK as a REST API service using Docker or Node.js directly. 3. **Start the service**: ```bash - docker-compose up -d + # Build the image + docker build -t cardano-raw-sdk . + + # Run the container (mounts the local .env and Fireblocks secret key) + docker run -d --name cardano-raw-sdk \ + --env-file .env \ + -p 8000:8000 \ + -v "$PWD/secrets:/app/secrets:ro" \ + cardano-raw-sdk ``` 4. **Access the API**: @@ -1375,7 +1383,6 @@ cardano-raw-sdk/ │ └── server.ts # Express server setup ├── docs/ # Generated documentation ├── Dockerfile # Docker configuration -├── docker-compose.yml # Docker Compose setup ├── package.json ├── tsconfig.json └── README.md @@ -1389,20 +1396,24 @@ cardano-raw-sdk/ docker build -t cardano-raw-sdk:latest . ``` -#### Run with Docker Compose +#### Run the Container ```bash -# Start services -docker-compose up -d +# Start the service +docker run -d --name cardano-raw-sdk \ + --env-file .env \ + -p 8000:8000 \ + -v "$PWD/secrets:/app/secrets:ro" \ + cardano-raw-sdk:latest # View logs -docker-compose logs -f +docker logs -f cardano-raw-sdk -# Stop services -docker-compose down +# Stop the service +docker stop cardano-raw-sdk -# Restart services -docker-compose restart +# Restart the service +docker restart cardano-raw-sdk ``` #### Docker Compose Configuration @@ -1522,7 +1533,7 @@ await sdk.shutdown(); // Clean up resources and clear cache 3. **Docker container fails to start** - Check that the secret key path is correct and file is readable - Verify all required environment variables are set - - Check Docker logs: `docker-compose logs -f` + - Check Docker logs: `docker logs -f cardano-raw-sdk` ## Deployment Considerations From bf1abd578347d6583030b1797d2d2a6458bf8b27 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 12:50:20 +0300 Subject: [PATCH 27/65] fix(M-21): coalesce concurrent SDK creations per vault account SdkManager.getSdk read from sdkPool, and on a miss awaited sdkFactory(...) and then stored the result. Two requests for the same uncached vaultAccountId arriving on the same event-loop tick both pass the cache check, each call the factory, and the second factory result overwrites the first - the orphaned SDK keeps its underlying connections open with no future getSdk hit to reuse it. Add a pendingSdkCreations map keyed by vaultAccountId. On cache miss, the first caller registers its in-flight Promise and runs the factory; concurrent callers for the same key await that Promise instead of starting their own. The entry is removed in a finally block so a failed creation does not pin the key. useCount is incremented per caller that joins, which also fixes the previous off-by-one where the first caller's write reset useCount to 1. Adds src/__tests__/pool/sdkManager.test.ts covering: shared instance across 8 concurrent waiters, distinct allocations for distinct keys, and recovery after a rejected in-flight creation. --- src/__tests__/pool/sdkManager.test.ts | 104 ++++++++++++++++++++++++++ src/pool/sdkManager.ts | 39 +++++++--- 2 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 src/__tests__/pool/sdkManager.test.ts diff --git a/src/__tests__/pool/sdkManager.test.ts b/src/__tests__/pool/sdkManager.test.ts new file mode 100644 index 00000000..872fac14 --- /dev/null +++ b/src/__tests__/pool/sdkManager.test.ts @@ -0,0 +1,104 @@ +import { describe, it, expect, jest, afterEach } from '@jest/globals'; +import { SdkManager } from '../../pool/sdkManager.js'; +import { Networks } from '../../types/index.js'; + +// Minimal stand-in for FireblocksCardanoRawSDK in pool tests. The manager +// never calls SDK methods - it only stores/returns the instance. +const makeFakeSdk = (id: string) => ({ __id: id }); + +const makeManager = ( + factory: (vaultAccountId: string) => Promise, + maxPoolSize = 100 +): SdkManager => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const baseConfig: any = { apiKey: 'k', secretKey: 's', basePath: 'https://x' }; + const manager = new SdkManager( + baseConfig, + Networks.PREPROD, + { maxPoolSize }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (async (vaultAccountId: string) => factory(vaultAccountId)) as any + ); + return manager; +}; + +describe('SdkManager.getSdk - M-21 concurrent cache-miss', () => { + let manager: SdkManager | undefined; + + afterEach(() => { + // SdkManager schedules a setInterval; clear it so tests exit cleanly. + if (manager) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const interval = (manager as any).cleanupInterval as NodeJS.Timeout; + clearInterval(interval); + manager = undefined; + } + }); + + it('shares a single SDK across concurrent callers for the same vault', async () => { + let calls = 0; + // Factory deliberately yields with a small await so multiple callers can + // race past the cache check before the first one resolves. + const factory = jest.fn(async (vaultAccountId: string) => { + calls++; + await new Promise((r) => setTimeout(r, 10)); + return makeFakeSdk(`${vaultAccountId}:${calls}`); + }); + + manager = makeManager(factory); + + const concurrency = 8; + const results = await Promise.all( + Array.from({ length: concurrency }, () => manager!.getSdk('vault-A')) + ); + + // Factory called exactly once across all concurrent waiters. + expect(factory).toHaveBeenCalledTimes(1); + + // Every caller received the same instance. + const first = results[0]; + for (const r of results) { + expect(r).toBe(first); + } + }); + + it('still allocates separate SDKs for distinct vault accounts in parallel', async () => { + let calls = 0; + const factory = jest.fn(async (vaultAccountId: string) => { + calls++; + await new Promise((r) => setTimeout(r, 5)); + return makeFakeSdk(`${vaultAccountId}:${calls}`); + }); + + manager = makeManager(factory); + + const [a, b, c] = await Promise.all([ + manager.getSdk('vault-A'), + manager.getSdk('vault-B'), + manager.getSdk('vault-C'), + ]); + + expect(factory).toHaveBeenCalledTimes(3); + expect(a).not.toBe(b); + expect(b).not.toBe(c); + expect(a).not.toBe(c); + }); + + it('re-allocates after a previous in-flight creation rejected', async () => { + let attempt = 0; + const factory = jest.fn(async () => { + attempt++; + await new Promise((r) => setTimeout(r, 5)); + if (attempt === 1) throw new Error('boom'); + return makeFakeSdk(`vault-A:${attempt}`); + }); + + manager = makeManager(factory); + + await expect(manager.getSdk('vault-A')).rejects.toThrow('boom'); + // Second call must not be blocked by the stale in-flight entry. + const sdk = await manager.getSdk('vault-A'); + expect(sdk).toBeDefined(); + expect(factory).toHaveBeenCalledTimes(2); + }); +}); diff --git a/src/pool/sdkManager.ts b/src/pool/sdkManager.ts index 998f891e..a0709a80 100644 --- a/src/pool/sdkManager.ts +++ b/src/pool/sdkManager.ts @@ -62,6 +62,9 @@ interface SdkPoolItem { */ export class SdkManager { private sdkPool: Map = new Map(); + // Tracks in-flight SDK factory calls per vault key so concurrent cache + // misses share a single allocation instead of each producing their own. + private pendingSdkCreations: Map> = new Map(); private baseConfig: ConfigurationOptions; private poolConfig: PoolConfig; private cleanupInterval: NodeJS.Timeout; @@ -162,6 +165,19 @@ export class SdkManager { return poolItem.sdk; } + // Concurrent cache-miss: join an in-flight creation for the same key + // instead of racing the factory and producing duplicate SDK instances. + const pending = this.pendingSdkCreations.get(key); + if (pending) { + const sdk = await pending; + const item = this.sdkPool.get(key); + if (item) { + item.lastUsed = new Date(); + item.useCount++; + } + return sdk; + } + // Check pool capacity if (this.sdkPool.size >= this.poolConfig.maxPoolSize) { const removed = this.removeOldestIdleSdk(); @@ -172,17 +188,20 @@ export class SdkManager { } } - // Create new SDK + // Create new SDK, registering the in-flight Promise so concurrent callers + // can wait on the same allocation instead of starting their own. this.logger.info(`Creating new SDK for Vault #${vaultAccountId}`); - const sdk = await this.sdkFactory(vaultAccountId, this.baseConfig, this.network); - - this.sdkPool.set(key, { - sdk, - lastUsed: new Date(), - useCount: 1, - }); - - return sdk; + const creation = this.sdkFactory(vaultAccountId, this.baseConfig, this.network) + .then((sdk) => { + this.sdkPool.set(key, { sdk, lastUsed: new Date(), useCount: 1 }); + return sdk; + }); + this.pendingSdkCreations.set(key, creation); + try { + return await creation; + } finally { + this.pendingSdkCreations.delete(key); + } } /** From 1245e6f19464ddafe669e09c379a27a3736f42c5 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 15:20:02 +0300 Subject: [PATCH 28/65] ci: run unit tests on every PR and push to main Add a unit-tests job that runs npm test (302 tests across 9 suites) in parallel with lint-and-build. Both e2e jobs (staking-info, ada-transfer) now depend on it via needs: [lint-and-build, unit-tests] so a unit-test regression skips the mainnet e2e runs instead of burning real funds. --- .github/workflows/ci.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 697875cc..44d33711 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,9 +31,27 @@ jobs: - name: Build run: npm run build + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + + - name: Run unit tests + run: npm test + e2e-staking-info: runs-on: ubuntu-latest - needs: lint-and-build + needs: [lint-and-build, unit-tests] environment: tests steps: @@ -64,7 +82,7 @@ jobs: e2e-ada-transfer: runs-on: ubuntu-latest - needs: lint-and-build + needs: [lint-and-build, unit-tests] environment: tests steps: From 11145d02bba9654e5002cfa3dc87dc0e9ab8421e Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 16:31:06 +0300 Subject: [PATCH 29/65] fix(H-1): surface Iagon submit error instead of swallowing it submitTransfer now throws SdkApiError(TX_SUBMIT_REJECTED) carrying the upstream Iagon error string. submitTransaction propagates SdkApiError unchanged so the staking-service IncompleteWithdrawals retry detector finally sees the substrings it matches on. --- src/__tests__/utils/cardano.submit.test.ts | 84 ++++++++++++++++++++++ src/services/iagon.api.service.ts | 31 ++++++-- src/utils/cardano.ts | 15 ++-- 3 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 src/__tests__/utils/cardano.submit.test.ts diff --git a/src/__tests__/utils/cardano.submit.test.ts b/src/__tests__/utils/cardano.submit.test.ts new file mode 100644 index 00000000..532426eb --- /dev/null +++ b/src/__tests__/utils/cardano.submit.test.ts @@ -0,0 +1,84 @@ +import { describe, it, expect, jest } from '@jest/globals'; +import { Transaction } from '@emurgo/cardano-serialization-lib-nodejs'; +import { submitTransaction } from '../../utils/cardano.js'; +import { SdkApiError } from '../../types/index.js'; +import { IagonApiService } from '../../services/index.js'; + +// H-1: submitTransaction must surface the upstream Iagon error message and +// preserve SdkApiError so the staking-service retry-on-IncompleteWithdrawals +// substring check fires correctly. + +const fakeSignedTx = (): Transaction => { + // Tiny CBOR for testing: a Transaction object with empty body and + // witness set. submitTransaction only calls .to_bytes() on it. + return { + to_bytes: () => new Uint8Array([0x84, 0xa0, 0xa0, 0xf6, 0xf6]), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; +}; + +describe('submitTransaction - H-1 upstream error propagation', () => { + it('passes through an SdkApiError thrown by submitTransfer with its message intact', async () => { + const sdkErr = new SdkApiError( + 'Transaction submission rejected: IncompleteWithdrawals: must withdraw rewards in full', + 400, + 'TX_SUBMIT_REJECTED', + { iagonError: 'IncompleteWithdrawals: must withdraw rewards in full' }, + 'iagon-api' + ); + const mockIagon = { + submitTransfer: jest.fn(async () => { + throw sdkErr; + }), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as unknown as IagonApiService; + + await expect(submitTransaction(mockIagon, fakeSignedTx())).rejects.toBe(sdkErr); + }); + + it('preserves the "IncompleteWithdrawals" substring so the retry detector fires', async () => { + const mockIagon = { + submitTransfer: jest.fn(async () => { + throw new SdkApiError( + 'Transaction submission rejected: IncompleteWithdrawals: amount mismatch', + 400, + 'TX_SUBMIT_REJECTED' + ); + }), + } as unknown as IagonApiService; + + try { + await submitTransaction(mockIagon, fakeSignedTx()); + throw new Error('expected throw'); + } catch (err) { + expect(err).toBeInstanceOf(SdkApiError); + const msg = (err as Error).message.toLowerCase(); + // Same substrings the staking-service retry detector matches on. + expect(msg).toMatch(/incomplete|rewards in full|incompletewithdrawals/); + } + }); + + it('wraps non-SdkApiError exceptions in a generic Error (legacy behaviour)', async () => { + const mockIagon = { + submitTransfer: jest.fn(async () => { + throw new Error('network reset'); + }), + } as unknown as IagonApiService; + + await expect(submitTransaction(mockIagon, fakeSignedTx())).rejects.toThrow( + /Error submitting transaction: network reset/ + ); + }); + + it('returns the txHash on a successful submission', async () => { + const mockIagon = { + submitTransfer: jest.fn(async () => ({ + success: true as const, + data: { txHash: 'aa'.repeat(32) }, + })), + } as unknown as IagonApiService; + + const hash = await submitTransaction(mockIagon, fakeSignedTx()); + expect(hash).toBe('aa'.repeat(32)); + }); +}); diff --git a/src/services/iagon.api.service.ts b/src/services/iagon.api.service.ts index 321f0696..e47f4bc5 100644 --- a/src/services/iagon.api.service.ts +++ b/src/services/iagon.api.service.ts @@ -34,11 +34,13 @@ const balanceResponseSchema = z.object({ }), }); +// Iagon /v1/tx/submit returns either { success: true, data: { txHash } } +// on a successful submission or { success: false, error: "..." } on rejection. +// Both arrive with HTTP 200, so we parse permissively and surface the error +// from submitTransfer itself (see below). const transferResponseSchema = z.object({ success: z.boolean(), - data: z.object({ - txHash: z.string(), - }), + data: z.object({ txHash: z.string() }).optional(), error: z.string().optional(), }); import { @@ -391,10 +393,27 @@ export class IagonApiService { const response = await this.axiosInstance.post(url, txData); - if (response.status === 200) { - return this.validateResponse(response.data, transferResponseSchema, "submitTransfer"); + if (response.status !== 200) { + throw new SdkApiError(`Unexpected response status: ${response.status}`, response.status); } - throw new SdkApiError(`Unexpected response status: ${response.status}`, response.status); + + const parsed = this.validateResponse( + response.data, + transferResponseSchema, + "submitTransfer" + ); + + if (!parsed.success || !parsed.data?.txHash) { + throw new SdkApiError( + `Transaction submission rejected: ${parsed.error ?? "unknown error"}`, + 400, + "TX_SUBMIT_REJECTED", + { iagonError: parsed.error }, + "iagon-api" + ); + } + + return { success: true, data: parsed.data }; } catch (error: unknown) { throw this.errorHandler.handleApiError(error, `submitting transfer`); } diff --git a/src/utils/cardano.ts b/src/utils/cardano.ts index c2e9389e..43bf8122 100644 --- a/src/utils/cardano.ts +++ b/src/utils/cardano.ts @@ -664,16 +664,15 @@ export const submitTransaction = async ( logger.info(`CBOR hex (first 200 chars): ${txCbor.substring(0, 200)}`); logger.info(`CBOR hex (full): ${txCbor}`); - // Submit transaction using Iagon API + // submitTransfer throws on rejection with the upstream Iagon error + // message preserved, so we don't need to re-check response.success here. const response = await iagonApiService.submitTransfer(txCbor, false); - - if (response.success) { - logger.info(`Transaction successfully submitted. Transaction ID: ${response.data.txHash}`); - return response.data.txHash; - } - - throw new Error("Transaction submission failed"); + logger.info(`Transaction successfully submitted. Transaction ID: ${response.data.txHash}`); + return response.data.txHash; } catch (error) { + // Preserve SdkApiError (and its TX_SUBMIT_REJECTED type) so callers can + // pattern-match on it. Wrap only generic errors. + if (error instanceof SdkApiError) throw error; throw new Error( `Error submitting transaction: ${error instanceof Error ? error.message : error}`, { cause: error } From 772f6cf4beefff07afba41faf68ba23eea0ec510 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 16:36:41 +0300 Subject: [PATCH 30/65] fix(H-2): only report "not registered" on Iagon 404, not on every error checkRegistrationStatus caught every exception and returned false. An Iagon outage during deregistration was indistinguishable from a genuinely unregistered stake key, causing the SDK to skip the deregistration silently and leave the user's 2 ADA deposit locked. Now only an SdkApiError with statusCode 404 maps to false; everything else propagates to the caller. --- .../services/stakingValidator.test.ts | 73 +++++++++++++++++++ .../helpers/staking-validator.helper.ts | 13 +++- 2 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 src/__tests__/services/stakingValidator.test.ts diff --git a/src/__tests__/services/stakingValidator.test.ts b/src/__tests__/services/stakingValidator.test.ts new file mode 100644 index 00000000..76751df9 --- /dev/null +++ b/src/__tests__/services/stakingValidator.test.ts @@ -0,0 +1,73 @@ +import { describe, it, expect, jest } from '@jest/globals'; +import { StakingValidator } from '../../services/staking/helpers/staking-validator.helper.js'; +import { SdkApiError } from '../../types/index.js'; +import { Logger } from '../../utils/index.js'; + +// H-2: checkRegistrationStatus must distinguish "stake key does not exist" +// (404) from any other error. A bare catch turning every failure into +// "not registered" causes the SDK to silently skip a deregistration on an +// Iagon outage, leaving the user's 2 ADA deposit locked. + +const makeValidator = ( + getStakeAccountInfo: jest.Mock<(stakeAddress: string) => Promise> +): StakingValidator => { + const addressResolver = { + getStakeAddress: jest.fn(async (_id: string) => 'stake_test1abc'), + }; + const iagon = { + getStakeAccountInfo, + }; + const logger = new Logger('test:staking-validator'); + return new StakingValidator( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + iagon as any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + addressResolver as any, + logger + ); +}; + +describe('StakingValidator.checkRegistrationStatus - H-2', () => { + it('returns true when Iagon reports the stake account active', async () => { + const v = makeValidator( + jest.fn(async () => ({ data: { active: true } })) + ); + await expect(v.checkRegistrationStatus('vault-1')).resolves.toBe(true); + }); + + it('returns false on Iagon 404 (stake key not registered)', async () => { + const v = makeValidator( + jest.fn(async () => { + throw new SdkApiError('not found', 404, 'NotFound', undefined, 'iagon-api'); + }) + ); + await expect(v.checkRegistrationStatus('vault-1')).resolves.toBe(false); + }); + + it('propagates a 5xx Iagon failure instead of reporting "not registered"', async () => { + const v = makeValidator( + jest.fn(async () => { + throw new SdkApiError('upstream broken', 503, 'BadGateway', undefined, 'iagon-api'); + }) + ); + await expect(v.checkRegistrationStatus('vault-1')).rejects.toBeInstanceOf(SdkApiError); + }); + + it('propagates a network error (no statusCode) instead of reporting "not registered"', async () => { + const v = makeValidator( + jest.fn(async () => { + throw new SdkApiError('ECONNREFUSED', undefined, undefined, undefined, 'iagon-api'); + }) + ); + await expect(v.checkRegistrationStatus('vault-1')).rejects.toBeInstanceOf(SdkApiError); + }); + + it('propagates a non-SdkApiError throwable instead of reporting "not registered"', async () => { + const v = makeValidator( + jest.fn(async () => { + throw new TypeError('boom'); + }) + ); + await expect(v.checkRegistrationStatus('vault-1')).rejects.toBeInstanceOf(TypeError); + }); +}); diff --git a/src/services/staking/helpers/staking-validator.helper.ts b/src/services/staking/helpers/staking-validator.helper.ts index 35a37b4f..348231e3 100644 --- a/src/services/staking/helpers/staking-validator.helper.ts +++ b/src/services/staking/helpers/staking-validator.helper.ts @@ -76,9 +76,16 @@ export class StakingValidator implements IStakingValidator { const stakeAddress = await this.addressResolver.getStakeAddress(vaultAccountId); const accountInfo = await this.iagonApiService.getStakeAccountInfo(stakeAddress); return accountInfo.data.active; - } catch { - this.logger.info("Stake key not yet registered"); - return false; + } catch (err) { + // 404 = the stake account legitimately does not exist on-chain. + // Any other failure (network, 5xx, parse error) must propagate so + // callers don't mistake an outage for "not registered" and skip a + // deregistration / submit a duplicate registration. + if (err instanceof SdkApiError && err.statusCode === 404) { + this.logger.info("Stake key not yet registered"); + return false; + } + throw err; } } } From 051afae1443b7ca18415f35e4dc79b71ac1a85cf Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 16:39:51 +0300 Subject: [PATCH 31/65] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 628e237b..63506b29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cardano-raw-sdk", - "version": "1.4.2", + "version": "1.4.3", "main": "dist/index.js", "types": "dist/index.d.ts", "type": "module", From 3efcc80008a8e185a3ab40df1cd3aad4c27db3e8 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 18:49:45 +0300 Subject: [PATCH 32/65] change e2e to preprod --- .github/workflows/test-ada-transfer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-ada-transfer.yml b/.github/workflows/test-ada-transfer.yml index cf4cfc9e..17798593 100644 --- a/.github/workflows/test-ada-transfer.yml +++ b/.github/workflows/test-ada-transfer.yml @@ -6,7 +6,7 @@ on: network: description: 'Cardano network' required: true - default: 'mainnet' + default: 'preprod' type: choice options: - preprod From bf864a333e9de24e153bc9912ff64a952cd50a35 Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 18:53:06 +0300 Subject: [PATCH 33/65] ci: run e2e ADA transfer on preprod to match dispatch workflow default --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44d33711..cecde0a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,7 +105,7 @@ jobs: FIREBLOCKS_API_USER_KEY: ${{ secrets.FIREBLOCKS_API_KEY }} FIREBLOCKS_API_USER_SECRET_KEY: ${{ secrets.FIREBLOCKS_API_SECRET }} IAGON_API_KEY: ${{ secrets.IAGON_API_KEY }} - CARDANO_NETWORK: mainnet + CARDANO_NETWORK: preprod TRANSFER_AMOUNT_ADA: '3' SOURCE_VAULT: '0' DEST_VAULT: '1' From 672e57568c91eb878a58b112ddea49945185af1f Mon Sep 17 00:00:00 2001 From: SlavaSereb Date: Tue, 9 Jun 2026 18:59:13 +0300 Subject: [PATCH 34/65] remove fee estimation for e2e ADA transfer --- scripts/test-ada-transfer.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/test-ada-transfer.ts b/scripts/test-ada-transfer.ts index 1bb21b51..a997d725 100644 --- a/scripts/test-ada-transfer.ts +++ b/scripts/test-ada-transfer.ts @@ -90,12 +90,12 @@ async function main() { console.log("Estimating fee..."); - const estimate = await sdk.estimateAdaTransactionFee({ - lovelaceAmount, - recipientVaultAccountId: destVault, - }); - console.log(`Estimated fee: ${estimate.fee.ada} ADA`); - console.log(`Recipient receives: ${estimate.recipientReceives.ada} ADA`); + // const estimate = await sdk.estimateAdaTransactionFee({ + // lovelaceAmount, + // recipientVaultAccountId: destVault, + // }); + // console.log(`Estimated fee: ${estimate.fee.ada} ADA`); + // console.log(`Recipient receives: ${estimate.recipientReceives.ada} ADA`); console.log("\nExecuting transfer..."); From ee7462cdac32cde15345ea50bf24a03149b6699a Mon Sep 17 00:00:00 2001 From: tomer-shoham Date: Wed, 8 Jul 2026 12:24:51 +0300 Subject: [PATCH 35/65] docs(M-20): remove Docker Compose references and regenerate TypeDoc [FIREHOG-BYPASS] no prv key was exposed --- README.md | 30 +- docs/assets/hierarchy.js | 2 +- docs/assets/highlight.css | 14 - docs/assets/navigation.js | 2 +- docs/assets/search.js | 2 +- docs/classes/ApiController.html | 26 +- docs/classes/ErrorHandler.html | 6 +- docs/classes/FireblocksCardanoRawSDK.html | 107 +++---- docs/classes/FireblocksService.html | 12 +- docs/classes/IagonApiService.html | 28 +- docs/classes/Logger.html | 24 +- docs/classes/SdkApiError.html | 4 +- docs/classes/SdkManager.html | 16 +- docs/classes/StakingService.html | 28 +- docs/enums/CardanoAmounts.html | 23 +- docs/enums/CardanoConstants.html | 22 +- docs/enums/CertificateType.html | 4 +- docs/enums/DRepAction.html | 4 +- docs/enums/DRepKind.html | 4 +- docs/enums/GroupByOptions.html | 4 +- docs/enums/LogLevel.html | 4 +- docs/enums/Networks.html | 4 +- docs/enums/PoolStatus.html | 4 +- docs/enums/RewardType.html | 4 +- docs/enums/StakingOperation.html | 4 +- docs/enums/SupportedAssets.html | 6 +- docs/enums/TransactionType.html | 4 +- docs/enums/WebhookEventTypes.html | 4 +- docs/functions/assertRecipientAddress.html | 3 + docs/functions/assertTxSizeWithinLimit.html | 5 + docs/functions/blakeHash.html | 2 +- .../buildAdaTransactionWithCalculatedFee.html | 2 +- .../buildCntTransactionWithCalculatedFee.html | 2 +- ...olidationTransactionWithCalculatedFee.html | 2 +- .../buildDRepRegistrationCertificate.html | 2 +- .../functions/buildDelegationCertificate.html | 2 +- .../buildDeregistrationCertificate.html | 2 +- ...ultiTokenTransactionWithCalculatedFee.html | 2 +- docs/functions/buildPayload.html | 2 +- .../buildRegistrationCertificate.html | 2 +- docs/functions/buildTransaction.html | 2 +- .../buildVoteDelegationCertificate.html | 2 +- docs/functions/buildVotingProcedures.html | 16 +- docs/functions/calculateBalance.html | 7 - .../calculateMinLovelaceForUtxo.html | 2 +- docs/functions/calculateTokenAmount.html | 2 +- docs/functions/calculateTransactionFee.html | 2 +- docs/functions/calculateTtl.html | 2 +- docs/functions/countDistinctPolicies.html | 2 +- .../createAdaTransactionOutputs.html | 2 +- docs/functions/createConsolidationOutput.html | 2 +- .../createMultiTokenTransactionOutputs.html | 2 +- docs/functions/createTransactionInputs.html | 2 +- docs/functions/createTransactionOutputs.html | 2 +- docs/functions/decodeAddress.html | 2 +- docs/functions/decodeAssetName.html | 2 +- docs/functions/drepActionToDRepInfo.html | 2 +- docs/functions/embedSignaturesInTx.html | 2 +- docs/functions/encodeDRepId.html | 8 +- docs/functions/encodeStakeAddress.html | 2 +- docs/functions/fetchAndSelectUtxosForAda.html | 2 +- docs/functions/fetchAndSelectUtxosForCnt.html | 2 +- .../fetchAndSelectUtxosForMultiToken.html | 2 +- docs/functions/fetchUtxos.html | 2 +- docs/functions/filterUtxos.html | 2 +- docs/functions/findSuitableUtxo.html | 2 +- docs/functions/formatWithDecimals.html | 2 +- docs/functions/getAddressHrp.html | 2 +- .../getCertificateFromBaseAddress.html | 2 +- docs/functions/getConfig.html | 2 +- docs/functions/getDefaultProtocolParams.html | 3 + docs/functions/getExtraPolicies.html | 1 + docs/functions/getLovelaceAmount.html | 2 +- docs/functions/getProtocolParams.html | 3 + docs/functions/getSigningPayload.html | 2 +- .../getStakeAddressFromBaseAddress.html | 2 +- .../getStakeAddressFromCertificate.html | 2 +- docs/functions/getStakeAddressHrp.html | 2 +- docs/functions/getTxStatus.html | 4 +- docs/functions/initConfig.html | 2 +- docs/functions/isConfigInitialized.html | 2 +- docs/functions/isSpendableUtxo.html | 5 + docs/functions/parseAdaStringToLovelace.html | 2 +- docs/functions/resetConfig.html | 2 +- docs/functions/resetProtocolParams.html | 3 + docs/functions/sanitizeForLogging.html | 2 +- docs/functions/serializeCertificate.html | 2 +- docs/functions/serializeWithdrawals.html | 2 +- docs/functions/setProtocolParams.html | 4 + docs/functions/sortWitnesses.html | 2 +- docs/functions/stakeAddressBytesPrefix.html | 2 +- docs/functions/submitTransaction.html | 2 +- docs/functions/toHex.html | 2 +- docs/hierarchy.html | 2 +- docs/index.html | 272 ++++++++++++++++-- docs/interfaces/AccountAssetsResponse.html | 4 +- docs/interfaces/AdaFeeEstimationRequest.html | 6 +- docs/interfaces/AdaFeeEstimationResponse.html | 14 +- docs/interfaces/AdaTransferOpts.html | 12 +- docs/interfaces/AdaTransferResult.html | 6 +- docs/interfaces/AddressInfo.html | 4 +- docs/interfaces/AddressWithUtxo.html | 7 +- docs/interfaces/AssetInfoResponse.html | 4 +- docs/interfaces/AssetMetadata.html | 4 +- docs/interfaces/BalanceResponse.html | 4 +- docs/interfaces/BuildPayloadOptions.html | 6 +- docs/interfaces/CardanoRewardWithdrawal.html | 4 +- docs/interfaces/CardanoWitness.html | 4 +- docs/interfaces/CastVoteOptions.html | 10 +- docs/interfaces/CastVoteResult.html | 8 +- docs/interfaces/CntFeeEstimationRequest.html | 18 +- docs/interfaces/CntFeeEstimationResponse.html | 10 +- .../CntTransactionOutputsParams.html | 4 +- docs/interfaces/CntTransferOpts.html | 4 +- docs/interfaces/Config.html | 4 +- docs/interfaces/ConsolidateBatchResult.html | 5 + docs/interfaces/ConsolidateUtxosOpts.html | 14 +- docs/interfaces/ConsolidateUtxosResult.html | 16 +- .../ConsolidationTransactionOutputParams.html | 4 +- docs/interfaces/CurrentEpochResponse.html | 4 +- docs/interfaces/CustomConfig.html | 4 +- docs/interfaces/DRepDelegationOptions.html | 4 +- docs/interfaces/DRepInfo.html | 4 +- .../interfaces/DelegationHistoryResponse.html | 4 +- docs/interfaces/DelegationOptions.html | 4 +- docs/interfaces/DeregisterStakingOptions.html | 4 +- docs/interfaces/DetailedTransaction.html | 4 +- .../interfaces/DetailedTxHistoryResponse.html | 4 +- docs/interfaces/EnrichedAsset.html | 4 +- docs/interfaces/EnrichedBalanceResponse.html | 4 +- .../EnrichedGroupedBalanceResponse.html | 4 +- .../EnrichedWebhookPayloadData.html | 14 +- docs/interfaces/ExecuteTransactionOpts.html | 8 +- .../interfaces/GetTransactionHistoryOpts.html | 10 +- .../GetVaultAccountAddressOpts.html | 6 +- .../GetVaultAccountAddressesOpts.html | 4 +- docs/interfaces/GroupedBalanceResponse.html | 4 +- .../GroupedDetailedTxHistoryResponse.html | 4 +- .../GroupedTransactionHistoryResponse.html | 4 +- docs/interfaces/HealthStatusResponse.html | 4 +- docs/interfaces/INetworkConfiguration.html | 4 +- docs/interfaces/IStakeAddressResolver.html | 4 +- docs/interfaces/IStakingValidator.html | 4 +- docs/interfaces/ITransactionSigner.html | 4 +- docs/interfaces/ITransactionSubmitter.html | 4 +- docs/interfaces/IUtxoProvider.html | 4 +- docs/interfaces/LastUpdated.html | 4 +- .../MultiTokenFeeEstimationRequest.html | 4 +- .../MultiTokenFeeEstimationResponse.html | 8 +- .../MultiTokenTransactionOutputsParams.html | 8 +- docs/interfaces/MultiTokenTransferOpts.html | 8 +- docs/interfaces/MultiTokenTransferResult.html | 8 +- docs/interfaces/PaymentAddressesResponse.html | 4 +- docs/interfaces/PoolBlocksResponse.html | 4 +- docs/interfaces/PoolConfig.html | 4 +- docs/interfaces/PoolDelegatorEntry.html | 4 +- .../PoolDelegatorsListResponse.html | 4 +- docs/interfaces/PoolDelegatorsResponse.html | 4 +- docs/interfaces/PoolInfo.html | 4 +- docs/interfaces/PoolInfoResponse.html | 4 +- docs/interfaces/PoolMetadataResponse.html | 4 +- docs/interfaces/ProtocolParams.html | 14 + docs/interfaces/RegisterAsDRepOptions.html | 8 +- docs/interfaces/RegisterAsDRepResult.html | 6 +- docs/interfaces/RegisterStakingOptions.html | 4 +- .../RegistrationHistoryResponse.html | 4 +- docs/interfaces/RewardsData.html | 4 +- docs/interfaces/SDKConfig.html | 4 +- docs/interfaces/SdkManagerMetrics.html | 4 +- docs/interfaces/SigningContext.html | 4 +- docs/interfaces/StakeAccountInfo.html | 4 +- docs/interfaces/StakeAccountInfoResponse.html | 4 +- docs/interfaces/StakeAccountReward.html | 4 +- .../StakeAccountRewardsResponse.html | 4 +- docs/interfaces/StakeAccountWithdrawal.html | 4 +- docs/interfaces/StakeAddressInfo.html | 4 +- docs/interfaces/StakingReward.html | 4 +- docs/interfaces/StakingTransactionResult.html | 4 +- docs/interfaces/SubmitTransactionOpts.html | 6 +- docs/interfaces/TokenMetadata.html | 14 +- docs/interfaces/TokenTransferSpec.html | 8 +- docs/interfaces/TransactionBuildContext.html | 6 +- .../TransactionDetailsResponse.html | 4 +- docs/interfaces/TransactionHistoryItem.html | 4 +- .../TransactionHistoryResponse.html | 4 +- docs/interfaces/TransactionInput.html | 4 +- docs/interfaces/TransactionOutput.html | 4 +- docs/interfaces/TransactionPagination.html | 4 +- docs/interfaces/TransactionStatusOpts.html | 4 +- docs/interfaces/TransactionValue.html | 4 +- docs/interfaces/TransferResponse.html | 4 +- docs/interfaces/UtxoData.html | 4 +- docs/interfaces/UtxoForStaking.html | 4 +- docs/interfaces/UtxoIagonResponse.html | 4 +- docs/interfaces/VaultAccountOpts.html | 4 +- .../VaultBalanceAddressResponse.html | 6 +- docs/interfaces/VaultBalanceByAddress.html | 8 +- docs/interfaces/VaultBalanceByPolicy.html | 4 +- docs/interfaces/VaultBalanceByToken.html | 6 +- .../VaultBalancePolicyResponse.html | 6 +- docs/interfaces/VaultBalanceSummary.html | 6 +- .../interfaces/VaultBalanceTokenResponse.html | 4 +- docs/interfaces/VaultDestinationOpts.html | 4 +- .../interfaces/VaultIndexDestinationOpts.html | 4 +- docs/interfaces/VaultIndexOpts.html | 4 +- docs/interfaces/WebhookPayloadData.html | 14 +- docs/interfaces/WithdrawRewardsOptions.html | 4 +- .../interfaces/WithdrawalHistoryResponse.html | 4 +- .../createAdaTransactionOutputsParams.html | 4 +- .../fetchAndSelectUtxosForAdaParams.html | 8 +- .../fetchAndSelectUtxosForCntParams.html | 4 +- ...etchAndSelectUtxosForMultiTokenParams.html | 6 +- docs/interfaces/getBalanceByAddressOpts.html | 4 +- .../getBalanceByCredentialOpts.html | 4 +- docs/interfaces/getBalanceByStakeKeyOpts.html | 4 +- docs/interfaces/getVaultBalanceOpts.html | 4 +- docs/modules.html | 2 +- docs/types/CardanoCertificate.html | 2 +- docs/types/CborArray.html | 2 +- docs/types/CurrentEpochParams.html | 4 +- docs/types/NetworkInfo.html | 4 +- docs/types/OperationRequest.html | 2 +- docs/types/OperationResult.html | 2 +- docs/types/Pagination.html | 4 +- docs/types/ParamsForOperation.html | 2 +- docs/types/ResultForOperation.html | 2 +- docs/types/Tip.html | 4 +- docs/types/VaultBalanceResponse.html | 2 +- docs/variables/EXPECTED_SIGNATURE_COUNT.html | 2 +- .../variables/FireblocksWebhookConstants.html | 2 +- ...MIN_DREP_DELEGATION_AMOUNT_MULTIPLIER.html | 2 +- docs/variables/PAYMENT_KEY_CHANGE_INDEX.html | 2 +- .../REGISTRATION_VERIFICATION_DELAY_MS.html | 2 +- docs/variables/STAKE_KEY_PATH_INDEX.html | 2 +- .../WITNESS_COUNT_PAYMENT_AND_STAKE_KEY.html | 2 +- .../WITNESS_COUNT_PAYMENT_KEY_ONLY.html | 2 +- docs/variables/config.html | 2 +- docs/variables/iagonBaseUrl.html | 2 +- docs/variables/utxoLocks.html | 1 + 239 files changed, 915 insertions(+), 640 deletions(-) create mode 100644 docs/functions/assertRecipientAddress.html create mode 100644 docs/functions/assertTxSizeWithinLimit.html delete mode 100644 docs/functions/calculateBalance.html create mode 100644 docs/functions/getDefaultProtocolParams.html create mode 100644 docs/functions/getExtraPolicies.html create mode 100644 docs/functions/getProtocolParams.html create mode 100644 docs/functions/isSpendableUtxo.html create mode 100644 docs/functions/resetProtocolParams.html create mode 100644 docs/functions/setProtocolParams.html create mode 100644 docs/interfaces/ConsolidateBatchResult.html create mode 100644 docs/interfaces/ProtocolParams.html create mode 100644 docs/variables/utxoLocks.html diff --git a/README.md b/README.md index 9aff35a2..984a3dca 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ A TypeScript SDK for managing Cardano token transfers through Fireblocks, with i - 🔄 **Connection Pooling**: Efficient SDK instance management with automatic cleanup - 🌐 **Multi-Network Support**: Works with Cardano mainnet, preprod, and preview networks - 🚀 **REST API Server**: Optional Express server for HTTP-based operations -- 🐳 **Docker Support**: Easy deployment with Docker and Docker Compose +- 🐳 **Docker Support**: Easy deployment with Docker ## Table of Contents @@ -45,7 +45,7 @@ A TypeScript SDK for managing Cardano token transfers through Fireblocks, with i ### Prerequisites - Node.js 18+ (for SDK usage) -- Docker & Docker Compose (for API service deployment) +- Docker (for API service deployment) - Fireblocks API credentials - Iagon API key (required for balance queries, transaction history, and transfers) @@ -1416,32 +1416,6 @@ docker stop cardano-raw-sdk docker restart cardano-raw-sdk ``` -#### Docker Compose Configuration - -```yaml -version: "3.8" - -services: - fireblocks-sdk: - build: . - ports: - - "8000:8000" - environment: - - PORT=8000 - - CARDANO_NETWORK=mainnet - - POOL_MAX_SIZE=100 - env_file: - - .env - volumes: - - ./fireblocks_secret.key:/app/fireblocks_secret.key:ro - restart: unless-stopped - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8000/health"] - interval: 30s - timeout: 10s - retries: 3 -``` - ## Advanced Features ### Intelligent Caching diff --git a/docs/assets/hierarchy.js b/docs/assets/hierarchy.js index c11ae342..a85c9d79 100644 --- a/docs/assets/hierarchy.js +++ b/docs/assets/hierarchy.js @@ -1 +1 @@ -window.hierarchyData = "eJydkzFvwjAQhf/LzaaNDY0hGyoLUyXa0qFisJIDLIyDfBcJCeW/VwG1SlMqHCYPfk/v87vzCUJZMkH2OU61kFppIYdSrwQEXDvM2ZaeIDvBONXN4c0eIYNpUQQkmvt1CQJ21heQqadUQBUcZGA9Y1ibHOmxpXzY8t6BgNwZIsiAqRg01sGPvLncWlcE9A2RTla1gLFO/iZ/WN6+8zEy/VsdQVALaApoJS5N5Xia52Xl+eXAdCOyK+/7ajmUEyGHKl1dUCZdlBkSW2+aycTidCyxNSjVzZ77Ao93AFzzRVOkVyl6Rd83CKXOM9DJqEWwwI0lxjCl2QIPC6TK8Q2Oa5bI12vVXsVXNjvrN2/BeDLnvxmV/5+tdyM6GQmp9WU1tW4P5tkQL0vGKKDf4pgq6voLjHiQ+g==" \ No newline at end of file +window.hierarchyData = "eJydkzFvwjAQhf/LzabFJIQ4G4KFqRJt6VAxWMlBLIyDfBcJCeW/VwG1SlMqDJMHv6f3+d35BL6qmCD7VFEkZCqVkPEwWgvwuLGYs6kcQXYCFUXt4fQeIYNpUXgkWrhNBQJ2xhWQjcaJgNpbyMA4Rr/ROdJzR/lU8t6CgNxqIsiAqRi01sGPvL0sjS08ujNRsm4EqCj5m/xhuHznY2D6tzqAoBHQFtBJXOna8jTPq9rxy4HpRmRffu+rZTwcCxnL0fqCMu6jzJHYON1OJhSnZwmuIe1nL1yBxwcArvlCKeToKsVd0Q8OIj3PYKK6C7jErSFGP6X5Eg9LpNryDY5rlsDXp1J1sl9Z74zbvnntSJ//ZlD+f7a7G5moRMg0udSSJt31mGniVcUYBPRbHFJF03wBWBORBA==" \ No newline at end of file diff --git a/docs/assets/highlight.css b/docs/assets/highlight.css index 732226e1..2e43008f 100644 --- a/docs/assets/highlight.css +++ b/docs/assets/highlight.css @@ -25,10 +25,6 @@ --dark-hl-11: #C8C8C8; --light-hl-12: #267F99; --dark-hl-12: #4EC9B0; - --light-hl-13: #800000; - --dark-hl-13: #569CD6; - --light-hl-14: #0000FF; - --dark-hl-14: #CE9178; --light-code-background: #FFFFFF; --dark-code-background: #1E1E1E; } @@ -47,8 +43,6 @@ --hl-10: var(--light-hl-10); --hl-11: var(--light-hl-11); --hl-12: var(--light-hl-12); - --hl-13: var(--light-hl-13); - --hl-14: var(--light-hl-14); --code-background: var(--light-code-background); } } @@ -66,8 +60,6 @@ --hl-10: var(--dark-hl-10); --hl-11: var(--dark-hl-11); --hl-12: var(--dark-hl-12); - --hl-13: var(--dark-hl-13); - --hl-14: var(--dark-hl-14); --code-background: var(--dark-code-background); } } @@ -85,8 +77,6 @@ --hl-10: var(--light-hl-10); --hl-11: var(--light-hl-11); --hl-12: var(--light-hl-12); - --hl-13: var(--light-hl-13); - --hl-14: var(--light-hl-14); --code-background: var(--light-code-background); } @@ -104,8 +94,6 @@ --hl-10: var(--dark-hl-10); --hl-11: var(--dark-hl-11); --hl-12: var(--dark-hl-12); - --hl-13: var(--dark-hl-13); - --hl-14: var(--dark-hl-14); --code-background: var(--dark-code-background); } @@ -122,6 +110,4 @@ .hl-10 { color: var(--hl-10); } .hl-11 { color: var(--hl-11); } .hl-12 { color: var(--hl-12); } -.hl-13 { color: var(--hl-13); } -.hl-14 { color: var(--hl-14); } pre, code { background: var(--code-background); } diff --git a/docs/assets/navigation.js b/docs/assets/navigation.js index d4c63dc9..4e482345 100644 --- a/docs/assets/navigation.js +++ b/docs/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "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" \ No newline at end of file +window.navigationData = "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" \ No newline at end of file diff --git a/docs/assets/search.js b/docs/assets/search.js index bf3e1158..3c188fe3 100644 --- a/docs/assets/search.js +++ b/docs/assets/search.js @@ -1 +1 @@ -window.searchData = "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"; \ No newline at end of file +window.searchData = "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"; \ No newline at end of file diff --git a/docs/classes/ApiController.html b/docs/classes/ApiController.html index 2ef22429..da019910 100644 --- a/docs/classes/ApiController.html +++ b/docs/classes/ApiController.html @@ -11,7 +11,7 @@
const sdkManager = new SdkManager(config);
const controller = new ApiController(sdkManager);

app.use('/api', controller.getRouter());
-
Index

Constructors

Index

Constructors

Methods

  • Cast a governance vote as a DRep (Conway governance) +

Returns ApiController

Methods

  • Cast a governance vote as a DRep (Conway governance) POST /api/governance/vote

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<void>

  • Delegate to a DRep (Conway governance) +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<void>

  • Delegate to a DRep (Conway governance) POST /api/governance/delegate-drep

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<void>

  • Delegate to a stake pool +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<void>

  • Delegate to a stake pool POST /api/staking/delegate

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Deregister staking credential +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Deregister staking credential POST /api/staking/deregister

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Get asset information including metadata and decimals +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Get asset information including metadata and decimals GET /api/assets/:policyId/:assetName

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Get pool information +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Get pool information GET /api/pool/info/:poolId

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<void>

  • Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Get stake address for a vault account +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<void>

  • Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Get stake address for a vault account GET /api/staking/stake-address/:vaultAccountId

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Query staking rewards for a vault account +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Query staking rewards for a vault account GET /api/staking/rewards/:vaultAccountId

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Register a vault account as a DRep (Conway governance) +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Register a vault account as a DRep (Conway governance) POST /api/governance/register-drep

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<void>

  • Register staking credential for a vault account +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<void>

  • Register staking credential for a vault account POST /api/staking/register

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Withdraw staking rewards +

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

  • Withdraw staking rewards POST /api/staking/withdraw-rewards

    -

    Parameters

    • req: Request
    • res: Response

    Returns Promise<Response<any, Record<string, any>> | undefined>

+

Parameters

  • req: Request
  • res: Response

Returns Promise<Response<any, Record<string, any>> | undefined>

diff --git a/docs/classes/ErrorHandler.html b/docs/classes/ErrorHandler.html index d7eb27fc..fa2efeb1 100644 --- a/docs/classes/ErrorHandler.html +++ b/docs/classes/ErrorHandler.html @@ -1,7 +1,7 @@ -ErrorHandler | cardano-raw-sdk
cardano-raw-sdk
    Preparing search index...

    Class ErrorHandler

    Index

    Constructors

    constructor +ErrorHandler | cardano-raw-sdk
    cardano-raw-sdk
      Preparing search index...

      Class ErrorHandler

      Index

      Constructors

      Methods

      Constructors

      Methods

      • Handles API errors consistently

        +

      Constructors

      Methods

      • Handles API errors consistently

        Parameters

        • error: unknown

          The caught error

        • context: string

          Description of what operation failed

        Returns SdkApiError

        ApiError with structured error information

        -
      +
      diff --git a/docs/classes/FireblocksCardanoRawSDK.html b/docs/classes/FireblocksCardanoRawSDK.html index 1a4a7954..f73990cb 100644 --- a/docs/classes/FireblocksCardanoRawSDK.html +++ b/docs/classes/FireblocksCardanoRawSDK.html @@ -1,4 +1,4 @@ -FireblocksCardanoRawSDK | cardano-raw-sdk
      cardano-raw-sdk
        Preparing search index...

        Class FireblocksCardanoRawSDK

        Index

        Constructors

        constructor +FireblocksCardanoRawSDK | cardano-raw-sdk
        cardano-raw-sdk
          Preparing search index...

          Class FireblocksCardanoRawSDK

          Index

          Constructors

          Methods

          Returns FireblocksCardanoRawSDK

          Methods

          • Cast a governance vote as a DRep (Conway era)

            Submits a voting_procedures transaction allowing a registered DRep to vote Yes, No, or Abstain on a governance action.

            Parameters

            • options: CastVoteOptions

              Vote options including governance action ID and vote choice

              @@ -58,21 +58,21 @@
              const result = await sdk.castGovernanceVote({
              vaultAccountId: "0",
              governanceActionId: {
              txHash: "abc123...", // TX hash of the governance action proposal
              index: 0,
              },
              vote: "yes",
              });
              console.log(`Vote TX: ${result.txHash}`);
              -
          • Clear asset info cache

            Parameters

            • OptionalpolicyId: string

              Optional: Clear cache for specific policy ID only

            • OptionalassetName: string

              Op tional: Clear cache for specific asset only (requires policyId)

            Returns void

            // Clear entire cache
            sdk.clearAssetInfoCache();

            // Clear all assets for a specific policy
            sdk.clearAssetInfoCache("f0ff48bbb...");

            // Clear specific asset
            sdk.clearAssetInfoCache("f0ff48bbb...", "4e4654");
            -
          • Delegate ADA to a stake pool

            Delegates the staking credential to a specific stake pool. The staking credential must be registered first using registerStakingCredential().

            Parameters

            • options: DelegationOptions

              Delegation options including pool ID

              @@ -81,7 +81,7 @@
              const result = await sdk.delegateToPool({
              vaultAccountId: "0",
              poolId: "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy", // Pool ID in bech32 or hex
              fee: 300000 // 0.3 ADA
              });
              console.log(`Delegation TX: ${result.txHash}`);
              -
          • Deregister staking credential

            Deregisters the staking credential and withdraws all available rewards. Returns the 2 ADA deposit that was paid during registration.

            Parameters

            • options: DeregisterStakingOptions

              Deregistration options

              @@ -90,32 +90,35 @@
              const result = await sdk.deregisterStakingCredential({
              vaultAccountId: "0",
              fee: 300000 // 0.3 ADA
              });
              console.log(`Deregistration TX: ${result.txHash}`);
              -
          • Get detailed transaction history for all addresses in the vault account

            Parameters

            • options: { fromSlot?: number; groupByAddress?: boolean; limit?: number; offset?: number } = {}
              • OptionalfromSlot?: number
              • OptionalgroupByAddress?: boolean

                If true, returns data grouped by address. If false, returns flat array with address field

                -
              • Optionallimit?: number
              • Optionaloffset?: number

            Returns Promise<DetailedTxHistoryResponse | GroupedDetailedTxHistoryResponse>

          • Get transaction history for all addresses in the vault account

            +
          • Optionallimit?: number
          • Optionaloffset?: number

          Returns Promise<DetailedTxHistoryResponse | GroupedDetailedTxHistoryResponse>

          • Get asset cache statistics

            +
          • Optionallimit?: number
          • Optionaloffset?: number

          Returns Promise<TransactionHistoryResponse | GroupedTransactionHistoryResponse>

          • Get asset cache statistics

            Returns {
                entries: { age: number; asset: string; expiresIn: number }[];
                size: number;
                ttl: number;
            }

            Cache statistics including size, TTL, and entry details

            const stats = sdk.getAssetCacheStats();
            console.log(`Cache size: ${stats.size}`);
            console.log(`Cache TTL: ${stats.ttl}ms`);
            stats.entries.forEach(entry => {
            console.log(`${entry.asset}: age ${entry.age}ms, expires in ${entry.expiresIn}ms`);
            });
            -
          • Get asset information including metadata, decimals, and supply

            Retrieves detailed information about a Cardano native token including:

            • Asset name (decoded from hex)
            • @@ -130,45 +133,45 @@
            const assetInfo = await sdk.getAssetInfo(
            "f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a",
            "4e4654"
            );
            console.log("Token Name:", assetInfo.data.metadata?.name);
            console.log("Decimals:", assetInfo.data.metadata?.decimals);
            console.log("Total Supply:", assetInfo.data.total_supply);
            -
          • Get balance by address for a vault account

            Parameters

            • options: { groupByPolicy?: boolean; includeMetadata?: boolean; index?: number } = {}
              • OptionalgroupByPolicy?: boolean

                Group assets by policy (default: false)

              • OptionalincludeMetadata?: boolean

                Enrich tokens with metadata (default: false)

              • Optionalindex?: number

                Address index (default: 0)

                -

            Returns Promise<BalanceResponse | GroupedBalanceResponse>

          • Get balance by credential for a vault account

            +

          Returns Promise<BalanceResponse | GroupedBalanceResponse>

          • Get balance by credential for a vault account

            Parameters

            • options: { credential: string; groupByPolicy?: boolean; includeMetadata?: boolean }
              • credential: string

                Payment credential

              • OptionalgroupByPolicy?: boolean

                Group assets by policy (default: false)

              • OptionalincludeMetadata?: boolean

                Enrich tokens with metadata (default: false)

                -

            Returns Promise<BalanceResponse | GroupedBalanceResponse>

          • Get balance by stake key for a vault account +

          Returns Promise<BalanceResponse | GroupedBalanceResponse>

          • Get balance by stake key for a vault account Automatically derives the stake key from the vault account address. Note: The stake key is shared across all addresses in the vault account.

            Parameters

            • options: { groupByPolicy?: boolean; includeMetadata?: boolean } = {}
              • OptionalgroupByPolicy?: boolean

                Group assets by policy (default: false)

              • OptionalincludeMetadata?: boolean

                Enrich tokens with metadata (default: false)

                -

            Returns Promise<BalanceResponse | GroupedBalanceResponse>

          • Get cache statistics

            -

            Returns { addressCount: number; publicKeyCount: number }

          • Internal

            Get direct access to the Fireblocks service

            +

          Returns Promise<BalanceResponse | GroupedBalanceResponse>

          • Get cache statistics

            +

            Returns { addressCount: number; publicKeyCount: number }

          • Get aggregate pool delegator count and total active stake

            +

          Returns Promise<PoolBlocksResponse>

          • Get paginated list of individual pool delegators

            +

          Returns Promise<PoolDelegatorsResponse>

          • Get paginated list of individual pool delegators

            Parameters

            • poolId: string

              Pool ID in bech32 format (pool1...) or hex

            • Optionallimit: number

              Maximum number of results (default: 100)

            • Optionaloffset: number

              Pagination offset (default: 0)

              -

            Returns Promise<PoolDelegatorsListResponse>

          • Get staking pool information by pool ID

            +

          Returns Promise<PoolDelegatorsListResponse>

          • Get staking pool information by pool ID

            Returns live metrics including saturation, stake, delegator count, margin, and fixed cost.

            Parameters

            • poolId: string

              Pool ID in bech32 format (pool1...) or hex

            Returns Promise<PoolInfoResponse>

            Pool information including saturation and financial metrics

            -
          • Get public key for a vault account address with caching

            -

            Parameters

            • change: number = 0
            • addressIndex: number = 0

            Returns Promise<string>

          • Get the stake address for a vault account

            +

          Returns Promise<PoolMetadataResponse>

          • Get public key for a vault account address with caching

            +

            Parameters

            • change: number = 0
            • addressIndex: number = 0

            Returns Promise<string>

          • Get the stake address for a vault account

            Extracts the BASE address from the vault account and derives the stake address. The stake address is used to identify staking credentials and query staking-related information like rewards, delegation history, and registration status.

            @@ -178,21 +181,21 @@
            const stakeAddress = await sdk.getStakeAddress("0");
            console.log(`Stake address: ${stakeAddress}`);
            // Output: stake1u9r76...
            -
          • Get UTXOs for all addresses in a vault account, grouped by address.

            -

            Returns Promise<{ address: string; index: number; utxos: UtxoData[] }[]>

          • Get UTXOs for all addresses in a vault account, grouped by address.

            +

            Returns Promise<{ address: string; index: number; utxos: UtxoData[] }[]>

          • Retrieves the wallet addresses associated with a specific Fireblocks vault account.

            Returns Promise<VaultWalletAddress[]>

            A promise that resolves to an array of VaultWalletAddress objects.

            Error if the retrieval fails.

            -
          • Query staking rewards for a vault account

            +

          Returns Promise<VaultBalanceResponse>

          • Query staking rewards for a vault account

            Retrieves detailed information about staking rewards including:

            • Individual rewards per epoch
            • @@ -205,7 +208,7 @@
            const rewards = await sdk.queryStakingRewards("0");
            console.log(`Available rewards: ${rewards.availableRewards} Lovelace`);
            console.log(`Total rewards earned: ${rewards.totalRewards} Lovelace`);
            console.log(`Total withdrawn: ${rewards.totalWithdrawals} Lovelace`);

            // List rewards by epoch
            rewards.rewards.forEach(r => {
            console.log(`Epoch ${r.epoch}: ${r.amount} from pool ${r.poolId}`);
            });
            -
          • Register the vault account as a DRep (Delegated Representative) on Cardano

            Submits a Conway-era reg_drep_cert certificate to register the vault's stake credential as a DRep. This costs a 500 ADA deposit (refundable on deregistration). An optional anchor can point to publicly accessible DRep metadata.

            @@ -214,7 +217,7 @@
            // Register without metadata anchor
            const result = await sdk.registerAsDRep({ vaultAccountId: "0" });

            // Register with a metadata anchor
            const result = await sdk.registerAsDRep({
            vaultAccountId: "0",
            anchor: {
            url: "https://example.com/drep-metadata.json",
            dataHash: "abc123...", // blake2b-256 hex hash of the JSON file
            },
            });
            console.log(`DRep registration TX: ${result.txHash}, DRep ID: ${result.drepId}`);
            -
          • Register staking credential for a vault account

            This is the first step to enable staking. It registers the staking key on-chain and requires a deposit of 2 ADA (DEPOSIT_AMOUNT) which will be returned upon deregistration.

            Parameters

            • options: RegisterStakingOptions

              Registration options

              @@ -223,31 +226,31 @@
              const result = await sdk.registerStakingCredential({
              vaultAccountId: "0",
              depositAmount: 2000000, // 2 ADA
              fee: 300000 // 0.3 ADA
              });
              console.log(`Registration TX: ${result.txHash}`);
              -
          • Gracefully shutdown the SDK

            Closes all connections, cleans up resources, and prepares for application termination. Should be called when the application is shutting down.

            Returns Promise<void>

            Promise that resolves when shutdown is complete

            process.on('SIGTERM', async () => {
            console.log('Shutting down...');
            await sdk.shutdown();
            process.exit(0)
            });
            -
          • Parameters

            Returns Promise<
                {
                    amount: number;
                    fee: { ada: string; lovelace: string };
                    senderAddress: string;
                    tokenName: string;
                    tokenPolicyId: string;
                    txHash: string;
                },
            >

          • Parameters

            Returns Promise<
                {
                    amount: number;
                    fee: { ada: string; lovelace: string };
                    senderAddress: string;
                    tokenName: string;
                    tokenPolicyId: string;
                    txHash: string;
                },
            >

          • Transfers native ADA (lovelace) to a recipient address or vault.

            UTxO selection prefers ADA-only UTxOs. If multi-asset UTxOs must be spent, all their tokens are returned to the sender in the change output - no tokens are lost.

            Parameters

            Returns Promise<AdaTransferResult>

            AdaTransferResult including txHash, fee, and optional tokensPresentedInChange

            -
          • Verifies Fireblocks webhook authenticity using both JWKS and legacy methods

            +
          • Verifies Fireblocks webhook authenticity using both JWKS and legacy methods

            Parameters

            • rawBody: Buffer

              The raw request body as Buffer (before JSON parsing)

            • headers: Record<string, string | undefined>

              Request headers object (case-insensitive)

            • environment: "US" | "EU" | "EU2" | "SANDBOX" = "US"

              Fireblocks environment (US, EU, EU2, or SANDBOX). Defaults to US.

            Returns Promise<boolean>

            true if webhook is authentic, false otherwise

            Error if verification fails critically

            -
          • Withdraw staking rewards

            Withdraws accumulated staking rewards without deregistering the staking credential. You can continue to stake after withdrawing rewards.

            Parameters

            • options: WithdrawRewardsOptions

              Withdrawal options with optional limit

              @@ -256,6 +259,12 @@
              // Withdraw all available rewards
              const result = await sdk.withdrawRewards({
              vaultAccountId: "0",
              fee: 300000 // 0.3 ADA
              });

              // Withdraw up to 5 ADA
              const result = await sdk.withdrawRewards({
              vaultAccountId: "0",
              limit: 5000000, // 5 ADA in Lovelace
              fee: 300000
              });
              console.log(`Withdrawal TX: ${result.txHash}`);
              -
          • Parameters

            • params: {
                  assetCacheTTL?: number;
                  disableSslVerification?: boolean;
                  fireblocksConfig: ConfigurationOptions;
                  iagonApiKey: string;
                  network: Networks;
                  vaultAccountId: string;
              }
              • OptionalassetCacheTTL?: number

                Asset metadata cache TTL in milliseconds (default: 1 hour)

                +
          • Parameters

            • params: {
                  assetCacheTTL?: number;
                  disableSslVerification?: boolean;
                  fireblocksConfig: ConfigurationOptions;
                  iagonApiKey: string;
                  iagonBaseUrl?: string;
                  network: Networks;
                  protocolParams?: {
                      coinsPerUtxoByte?: number;
                      drepDeposit?: number;
                      minFeeA?: number;
                      minFeeB?: number;
                      stakeKeyDeposit?: number;
                  };
                  vaultAccountId: string;
              }
              • OptionalassetCacheTTL?: number

                Asset metadata cache TTL in milliseconds (default: 1 hour)

              • OptionaldisableSslVerification?: boolean

                Disable SSL certificate verification (use only in development)

                -
              • fireblocksConfig: ConfigurationOptions
              • iagonApiKey: string
              • network: Networks
              • vaultAccountId: string

            Returns Promise<FireblocksCardanoRawSDK>

          +
        • fireblocksConfig: ConfigurationOptions
        • iagonApiKey: string
        • OptionaliagonBaseUrl?: string

          Override the Iagon API base URL. Falls back to the IAGON_BASE_URL +environment variable and finally to the built-in default. Useful for +staging/private deployments and tests.

          +
        • network: Networks
        • OptionalprotocolParams?: {
              coinsPerUtxoByte?: number;
              drepDeposit?: number;
              minFeeA?: number;
              minFeeB?: number;
              stakeKeyDeposit?: number;
          }

          Override Cardano protocol parameters. +Use this to adapt to protocol changes without waiting for an SDK update. +Only specified fields will be overridden; others use defaults.

          +
        • vaultAccountId: string
        • Returns Promise<FireblocksCardanoRawSDK>

          diff --git a/docs/classes/FireblocksService.html b/docs/classes/FireblocksService.html index 3301a74c..17b0f8dc 100644 --- a/docs/classes/FireblocksService.html +++ b/docs/classes/FireblocksService.html @@ -12,7 +12,7 @@
          const config: FireblocksConfig = {
          apiKey: process.env.FIREBLOCKS_API_KEY,
          secretKey: process.env.FIREBLOCKS_SECRET_KEY,
          basePath: BasePath.US
          };

          const service = new FireblocksService(config);

          // Get vault address
          const address = await service.getVaultAccountAddress(
          '123', // vault account ID
          'ADA', // asset ID
          0 // address index
          );

          // Get all addresses for an asset
          const addresses = await service.getVaultAccountAddresses(
          '123',
          'BTC'
          );

          // Get public key
          const publicKey = await service.getAssetPublicKey(
          '123', // vault account ID
          'ETH', // asset ID
          0, // change index
          0 // address index
          );
          -
          Index

          Constructors

          Index

          Returns FireblocksService

          // Initialize with environment variables
          const service = new FireblocksService({
          apiKey: process.env.FIREBLOCKS_API_KEY!,
          secretKey: process.env.FIREBLOCKS_SECRET_KEY!,
          basePath: BasePath.US
          });

          // Initialize with EU endpoint
          const euService = new FireblocksService({
          apiKey: apiKey,
          secretKey: secretKey,
          basePath: BasePath.EU
          });
          -

          Methods

          • Parameters

            • params: {
                  amount: string;
                  assetId: string;
                  note?: string;
                  recipientAddress?: string;
                  recipientVaultAccountId?: string;
                  sourceVaultAccountId: string;
              }

            Returns Promise<{ networkFee?: string; txHash: string }>

          • Retrieves the public key for a specific vault account asset address.

            +

          Methods

          • Parameters

            • params: {
                  amount: string;
                  assetId: string;
                  note?: string;
                  recipientAddress?: string;
                  recipientVaultAccountId?: string;
                  sourceVaultAccountId: string;
              }

            Returns Promise<{ networkFee?: string; txHash: string }>

          • Retrieves the public key for a specific vault account asset address.

            This method fetches the public key corresponding to a specific address within a vault account. The address is identified by its BIP-44 derivation path components (change and addressIndex). Public keys are essential for various @@ -58,7 +58,7 @@

          • Address derivation
          • Other cryptographic operations
          -
          • Retrieves a specific vault account address by derivation index.

            +
          • Retrieves a specific vault account address by derivation index.

            This method fetches all addresses for a vault account/asset combination and returns the address at the specified BIP-44 derivation index. This is useful for managing multiple addresses within a single vault account, where each @@ -87,7 +87,7 @@ multiple API calls.

            The BIP-44 derivation path is: m/44'/coin_type'/account'/change/address_index where address_index is the value provided in the index parameter.

            -
          • Retrieves all addresses associated with a vault account for a specific asset.

            +
          • Retrieves all addresses associated with a vault account for a specific asset.

            This method fetches the complete list of addresses that have been generated for a vault account/asset combination. Each address includes metadata such as its derivation index, type, and optional tags or descriptions.

            @@ -113,7 +113,7 @@ to handle pagination through multiple API calls.

            The addresses are returned in the order provided by Fireblocks, which is typically sorted by creation time or derivation index.

            -
          • Signs a transaction via Fireblocks and waits for signing completion.

            +
          • Signs a transaction via Fireblocks and waits for signing completion.

            This method submits a transaction request to Fireblocks, monitors its status until completion, and extracts the signed message data. It's primarily used for message signing operations where the transaction is used to generate cryptographic signatures @@ -145,4 +145,4 @@

            This method is used internally by signMessage() but can also be called directly for custom transaction payloads. The transaction status polling is handled by the getTxStatus utility function, which waits for the transaction to complete.

            -
          +
          diff --git a/docs/classes/IagonApiService.html b/docs/classes/IagonApiService.html index b1a98ade..33305d3a 100644 --- a/docs/classes/IagonApiService.html +++ b/docs/classes/IagonApiService.html @@ -1,4 +1,4 @@ -IagonApiService | cardano-raw-sdk
          cardano-raw-sdk
            Preparing search index...

            Class IagonApiService

            Index

            Constructors

            constructor +IagonApiService | cardano-raw-sdk
            cardano-raw-sdk
              Preparing search index...

              Class IagonApiService

              Index

              Constructors

              Methods

              • Clear asset info cache

                +

              Constructors

              Methods

              • Clear asset info cache

                Parameters

                • OptionalpolicyId: string

                  Optional: Clear cache for specific policy ID only

                • OptionalassetName: string

                  Optional: Clear cache for specific asset only (requires policyId)

                  -

                Returns void

              • Assets on stake credential does not mean ownership of the assets. +

              Returns void

              • Get cache statistics

                -

                Returns {
                    entries: { age: number; asset: string; expiresIn: number }[];
                    size: number;
                    ttl: number;
                }

              • Get cache statistics

                +

                Returns {
                    entries: { age: number; asset: string; expiresIn: number }[];
                    size: number;
                    ttl: number;
                }

              • Get asset information with caching

                Parameters

                • policyId: string

                  The policy ID of the asset

                • assetName: string

                  The asset name in hex format

                • skipCache: boolean = false

                  Optional: bypass cache and fetch fresh data

                Returns Promise<AssetInfoResponse>

                Asset information including metadata

                -
              +
              diff --git a/docs/classes/Logger.html b/docs/classes/Logger.html index d8d48053..07f9bc2b 100644 --- a/docs/classes/Logger.html +++ b/docs/classes/Logger.html @@ -1,4 +1,4 @@ -Logger | cardano-raw-sdk
              cardano-raw-sdk
                Preparing search index...

                Class Logger

                Index

                Constructors

                constructor +Logger | cardano-raw-sdk
                cardano-raw-sdk
                  Preparing search index...

                  Class Logger

                  Index

                  Constructors

                  Methods

                  createChild debug error @@ -11,28 +11,28 @@ setSanitizeLogs

                  Constructors

                  • Create a new logger instance

                    Parameters

                    • context: string

                      The context for this logger (e.g. class name)

                      -

                    Returns Logger

                  Methods

                  • Create a child logger with a subcontext

                    +

                  Returns Logger

                  Methods

                  • Create a child logger with a subcontext

                    Parameters

                    • subContext: string

                      Subcontext name

                    Returns Logger

                    Child logger instance

                    -
                  • Log a debug message

                    Parameters

                    • message: string

                      Log message

                    • ...args: unknown[]

                      Additional arguments

                      -

                    Returns void

                  • Log an error message

                    +

                  Returns void

                  • Log an error message

                    Parameters

                    • message: string

                      Log message

                    • ...args: unknown[]

                      Additional arguments

                      -

                    Returns void

                  • Log an info message

                    +

                  Returns void

                  • Log an info message

                    Parameters

                    • message: string

                      Log message

                    • ...args: unknown[]

                      Additional arguments

                      -

                    Returns void

                  • Log a warning message

                    +

                  Returns void

                  • Log a warning message

                    Parameters

                    • message: string

                      Log message

                    • ...args: unknown[]

                      Additional arguments

                      -

                    Returns void

                  • Add custom keys that should be treated as sensitive

                    +

                  Returns void

                  • Add custom keys that should be treated as sensitive

                    Parameters

                    • ...keys: string[]

                      Array of key names to treat as sensitive

                      -

                    Returns void

                  • Get current log level

                    +

                  Returns void

                  • Enable or disable automatic sanitization of sensitive data in logs

                    +

                  Returns void

                  • Enable or disable automatic sanitization of sensitive data in logs

                    Parameters

                    • enabled: boolean

                      Whether to sanitize logs (default: true)

                      -

                    Returns void

                  +

                  Returns void

                  diff --git a/docs/classes/SdkApiError.html b/docs/classes/SdkApiError.html index 9b467396..0c7d848d 100644 --- a/docs/classes/SdkApiError.html +++ b/docs/classes/SdkApiError.html @@ -1,4 +1,4 @@ -SdkApiError | cardano-raw-sdk
                  cardano-raw-sdk
                    Preparing search index...

                    Class SdkApiError

                    Hierarchy

                    • Error
                      • SdkApiError
                    Index

                    Constructors

                    constructor +SdkApiError | cardano-raw-sdk
                    cardano-raw-sdk
                      Preparing search index...

                      Class SdkApiError

                      Hierarchy

                      • Error
                        • SdkApiError
                      Index

                      Constructors

                      • Parameters

                        • message: string
                        • OptionalstatusCode: number
                        • OptionalerrorType: string
                        • OptionalerrorInfo: unknown
                        • Optionalservice: string

                        Returns SdkApiError

                      Properties

                      cause?: unknown
                      errorInfo?: unknown
                      errorType?: string
                      message: string
                      name: string
                      service?: string
                      stack?: string
                      statusCode?: number
                      stackTraceLimit: number

                      The Error.stackTraceLimit property specifies the number of stack frames +

                      Constructors

                      • Parameters

                        • message: string
                        • OptionalstatusCode: number
                        • OptionalerrorType: string
                        • OptionalerrorInfo: unknown
                        • Optionalservice: string

                        Returns SdkApiError

                      Properties

                      cause?: unknown
                      errorInfo?: unknown
                      errorType?: string
                      message: string
                      name: string
                      service?: string
                      stack?: string
                      statusCode?: number
                      stackTraceLimit: number

                      The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)).

                      The default value is 10 but may be set to any valid JavaScript number. Changes diff --git a/docs/classes/SdkManager.html b/docs/classes/SdkManager.html index 8312f270..e6a7eba4 100644 --- a/docs/classes/SdkManager.html +++ b/docs/classes/SdkManager.html @@ -13,7 +13,7 @@

                      import { FireblocksCardanoRawSDK } from './FireblocksCardanoRawSDK.js';
                      import { Networks } from './types/index.js';

                      const config: ConfigurationOptions = {
                      apiKey: 'your-api-key',
                      secretKey: 'your-secret-key',
                      basePath: BasePath.US
                      };

                      const network = Networks.MAINNET;

                      const manager = new SdkManager(
                      config,
                      network,
                      {
                      maxPoolSize: 50,
                      idleTimeoutMs: 20 * 60 * 1000
                      },
                      async (vaultAccountId, fireblocksConfig, network) =>
                      FireblocksCardanoRawSDK.createInstance({
                      fireblocksConfig,
                      vaultAccountId,
                      network
                      })
                      );

                      // Get SDK for a vault account (async)
                      const sdk = await manager.getSdk('vault-123');
                      const balance = await sdk.getBalanceByAddress();
                      -
                      Index

                      Constructors

                      Index

                      Constructors

                      Methods

                      getMetrics getSdk releaseSdk @@ -25,8 +25,8 @@
                    • network: Networks

                      The Cardano network to use (mainnet, preprod, preview)

                    • OptionalpoolConfig: Partial<PoolConfig>

                      Optional pool configuration settings

                    • OptionalsdkFactory: (
                          vaultAccountId: string,
                          config: ConfigurationOptions,
                          network: Networks,
                      ) => Promise<FireblocksCardanoRawSDK>

                      Factory function to create FireblocksCardanoRawSDK instances (used to avoid circular dependency)

                      -
                    • Returns SdkManager

                      Methods

                      • Gets or creates a FireblocksCardanoRawSDK instance for a specific vault account.

                        +

                      Returns SdkManager

                      Methods

                      • Gets or creates a FireblocksCardanoRawSDK instance for a specific vault account.

                        Implements pooling with LRU eviction for efficient resource management. Each vault account gets its own FireblocksCardanoRawSDK instance that can be reused across requests. The SDK instance is initialized with the vaultAccountId, so methods don't need to fetch @@ -36,12 +36,12 @@

                        const sdk = await manager.getSdk('vault-123');
                        // SDK is pre-initialized with vault-123, no need to pass vaultAccountId again
                        const balance = await sdk.getBalanceByAddress();
                        const publicKey = await sdk.getPublicKey();
                        -
                      • Releases an SDK instance back to the pool.

                        Parameters

                        • vaultAccountId: string

                          The vault account ID

                          -

                        Returns void

                      • Sets the SDK factory function (called by FireblocksCardanoRawSDK to avoid circular dependency)

                        +

                      Returns void

                      • Sets the SDK factory function (called by FireblocksCardanoRawSDK to avoid circular dependency)

                        Parameters

                        • factory: (
                              vaultAccountId: string,
                              config: ConfigurationOptions,
                              network: Networks,
                          ) => Promise<FireblocksCardanoRawSDK>

                          Factory function to create FireblocksCardanoRawSDK instances

                          -

                        Returns void

                      • Acquires an SDK instance, runs a callback, then releases it back to the pool. +

                      Returns void

                      • Acquires an SDK instance, runs a callback, then releases it back to the pool. Guarantees release even if the callback throws.

                        Type Parameters

                        • T

                        Parameters

                        • vaultAccountId: string

                          The vault account ID

                        • callback: (sdk: FireblocksCardanoRawSDK) => Promise<T>

                          Async function that receives the SDK instance

                          @@ -49,4 +49,4 @@
                          const balance = await manager.withSdk('vault-123', (sdk) => sdk.getBalanceByAddress());
                           
                          -
                      +
                      diff --git a/docs/classes/StakingService.html b/docs/classes/StakingService.html index 69f9bd20..d4a1daee 100644 --- a/docs/classes/StakingService.html +++ b/docs/classes/StakingService.html @@ -1,6 +1,6 @@ StakingService | cardano-raw-sdk
                      cardano-raw-sdk
                        Preparing search index...

                        Class StakingService

                        Main Staking Service Orchestrates staking operations by coordinating specialized helper services

                        -
                        Index

                        Constructors

                        Index

                        Constructors

                        Methods

                        • Register staking credential for a vault account +

                        Constructors

                        Methods

                        +

                        Parameters

                        Returns Promise<
                            StakingTransactionResult & {
                                addressIndex: number;
                                stakeAddress: string;
                            },
                        >

                        diff --git a/docs/enums/CardanoAmounts.html b/docs/enums/CardanoAmounts.html index 3ee37645..f70eeed6 100644 --- a/docs/enums/CardanoAmounts.html +++ b/docs/enums/CardanoAmounts.html @@ -1,9 +1,8 @@ -CardanoAmounts | cardano-raw-sdk
                        cardano-raw-sdk
                          Preparing search index...

                          Enumeration CardanoAmounts

                          Index

                          Enumeration Members

                          COINS_PER_UTXO_BYTE +CardanoAmounts | cardano-raw-sdk
                          cardano-raw-sdk
                            Preparing search index...

                            Enumeration CardanoAmounts

                            Index

                            Enumeration Members

                            COINS_PER_UTXO_BYTE: 4310

                            Cardano protocol parameter: coins per UTXO byte Used for calculating minimum ADA required for outputs with native assets Mainnet value: 4310 lovelace per byte

                            -
                            DEPOSIT_AMOUNT: 2000000
                            DREP_REGISTRATION_DEPOSIT: 500000000

                            Deposit required to register as a DRep on Cardano mainnet (Conway era) +

                            DEPOSIT_AMOUNT: 2000000
                            DREP_REGISTRATION_DEPOSIT: 500000000

                            Deposit required to register as a DRep on Cardano mainnet (Conway era) 500 ADA = 500,000,000 lovelace

                            -
                            ESTIMATED_MAX_FEE: 500000

                            Conservative fee estimate for UTXO selection in CNT transfers +

                            ESTIMATED_MAX_FEE: 500000

                            Conservative fee estimate for UTXO selection in CNT transfers Used to ensure sufficient ADA is available before calculating actual fee Set to 0.5 ADA (500,000 lovelace) as a safe upper bound

                            -
                            GOVERNANCE_TX_FEE: 1000000

                            Fee for governance operations (DRep delegation, DRep registration) +

                            GOVERNANCE_TX_FEE: 1000000

                            Fee for governance operations (DRep delegation, DRep registration) These transactions involve governance certificates

                            -
                            MIN_UTXO_BASE_LOVELACE: 1000000

                            Base minimum lovelace for any UTXO (Cardano protocol parameter) -This is the minimum for ADA-only or single-policy UTXOs. -Coincidentally the same numeric value as GOVERNANCE_TX_FEE - these are -independent protocol constants that happen to share the value 1 ADA.

                            -
                            MIN_UTXO_PER_POLICY_LOVELACE: 150000

                            Additional lovelace required per token policy (Cardano protocol parameter) +

                            MIN_UTXO_PER_POLICY_LOVELACE: 150000

                            Additional lovelace required per token policy (Cardano protocol parameter) Each distinct policy in a UTXO adds ~0.15 ADA to the minimum requirement

                            -
                            STAKING_TX_FEE: 300000

                            Fee for staking operations (register, deregister, delegate, withdraw) +

                            STAKING_TX_FEE: 300000

                            Fee for staking operations (register, deregister, delegate, withdraw) These are ADA-only transactions with fixed-size certificates. Sized to cover 2-witness transactions (payment key + stake key)

                            -
                            TX_FEE_INITIAL_ESTIMATE: 200000

                            Initial fee estimate for the fee-convergence loop. +

                            TX_FEE_INITIAL_ESTIMATE: 200000

                            Initial fee estimate for the fee-convergence loop. A reasonable conservative starting point; the loop converges to the real fee.

                            -
                            TX_FEE_TOLERANCE: 1000

                            Stop the fee-convergence loop when the delta between iterations is within this bound (lovelace).

                            -
                            TX_TTL_SECS: 7200
                            +
                            TX_FEE_TOLERANCE: 1000

                            Stop the fee-convergence loop when the delta between iterations is within this bound (lovelace).

                            +
                            TX_TTL_SECS: 7200
                            diff --git a/docs/enums/CardanoConstants.html b/docs/enums/CardanoConstants.html index 1f25ccc7..d6c134d7 100644 --- a/docs/enums/CardanoConstants.html +++ b/docs/enums/CardanoConstants.html @@ -1,4 +1,4 @@ -CardanoConstants | cardano-raw-sdk
                            cardano-raw-sdk
                              Preparing search index...

                              Enumeration CardanoConstants

                              Index

                              Enumeration Members

                              ADA_COIN_TYPE +CardanoConstants | cardano-raw-sdk
                              cardano-raw-sdk
                                Preparing search index...

                                Enumeration CardanoConstants

                                Index

                                Enumeration Members

                                ADA_COIN_TYPE: 1815
                                ADA_DECIMALS: 6
                                ADA_TEST_COIN_TYPE: 1
                                CARDANO_BASE_ADDRESS_MIN_LENGTH: 57
                                CARDANO_PAYMENT_CREDENTIAL_OFFSET: 29
                                CHANGE_INDEX: 0
                                CHIMERIC_INDEX: 2
                                MAX_TX_INPUTS: 100

                                Maximum number of UTxO inputs per transaction. +

                                Enumeration Members

                                ADA_COIN_TYPE: 1815
                                ADA_DECIMALS: 6
                                ADA_TEST_COIN_TYPE: 1
                                CARDANO_BASE_ADDRESS_MIN_LENGTH: 57
                                CARDANO_PAYMENT_CREDENTIAL_OFFSET: 29
                                CHANGE_INDEX: 0
                                CHIMERIC_INDEX: 2
                                MAX_TX_INPUTS: 100

                                Maximum number of UTxO inputs per transaction. Cardano's 16KB transaction size limit allows ~100–150 inputs in practice. This cap prevents dust-attack vectors where many tiny UTxOs are sent to an address to make future transactions exceed the size limit.

                                -
                                MIN_FEE_A: 44
                                MIN_FEE_B: 155381
                                TX_FEE_MAX_ITERATIONS: 5

                                Maximum iterations for the transaction-fee convergence loop

                                -
                                TX_WITNESS_SIZE_BYTES: 139

                                Bytes per Ed25519 signature witness in the CBOR-encoded transaction

                                -
                                UTXO_LOCK_TTL_MS: 120000

                                TTL for in-memory UTxO locks (ms). Locks expire automatically to prevent +

                                MAX_TX_SIZE_BYTES: 16384

                                Maximum on-chain transaction size in bytes (Cardano protocol parameter +maxTxSize). Conway mainnet is 16,384. We refuse to sign anything larger +because the network would reject it.

                                +
                                MIN_FEE_A: 44
                                MIN_FEE_B: 155381
                                MIN_UTXO_BASE_LOVELACE: 1000000

                                Base minimum lovelace for any UTxO (Cardano protocol parameter). +Minimum ADA required for ADA-only or single-policy UTxOs. +Kept here (not in CardanoAmounts) to avoid a duplicate-value clash with +CardanoAmounts.GOVERNANCE_TX_FEE - both happen to be 1 ADA today but are +independent protocol constants that must be updated separately if either changes.

                                +
                                TX_FEE_MAX_ITERATIONS: 5

                                Maximum iterations for the transaction-fee convergence loop

                                +
                                TX_WITNESS_SIZE_BYTES: 139

                                Bytes per Ed25519 signature witness in the CBOR-encoded transaction

                                +
                                UTXO_LOCK_TTL_MS: 120000

                                TTL for in-memory UTxO locks (ms). Locks expire automatically to prevent permanent blocking if a request crashes before calling release().

                                -
                                +
                                diff --git a/docs/enums/CertificateType.html b/docs/enums/CertificateType.html index d34489ee..69727186 100644 --- a/docs/enums/CertificateType.html +++ b/docs/enums/CertificateType.html @@ -1,9 +1,9 @@ CertificateType | cardano-raw-sdk
                                cardano-raw-sdk
                                  Preparing search index...

                                  Enumeration CertificateType

                                  Certificate types for Cardano staking and governance

                                  -
                                  Index

                                  Enumeration Members

                                  Index

                                  Enumeration Members

                                  DELEGATION: 2
                                  DREP_REGISTRATION: 16
                                  STAKE_DEREGISTRATION: 8
                                  STAKE_KEY_DEREGISTRATION: 1
                                  STAKE_KEY_REGISTRATION: 0
                                  STAKE_REGISTRATION: 7
                                  VOTE_DELEGATION: 9
                                  +

                                  Enumeration Members

                                  DELEGATION: 2
                                  DREP_REGISTRATION: 16
                                  STAKE_DEREGISTRATION: 8
                                  STAKE_KEY_DEREGISTRATION: 1
                                  STAKE_KEY_REGISTRATION: 0
                                  STAKE_REGISTRATION: 7
                                  VOTE_DELEGATION: 9
                                  diff --git a/docs/enums/DRepAction.html b/docs/enums/DRepAction.html index fb118724..653c3b2a 100644 --- a/docs/enums/DRepAction.html +++ b/docs/enums/DRepAction.html @@ -1,5 +1,5 @@ DRepAction | cardano-raw-sdk
                                  cardano-raw-sdk
                                    Preparing search index...

                                    Enumeration DRepAction

                                    DRep (Delegated Representative) action types for Conway governance

                                    -
                                    Index

                                    Enumeration Members

                                    Index

                                    Enumeration Members

                                    ALWAYS_ABSTAIN: "always-abstain"
                                    ALWAYS_NO_CONFIDENCE: "always-no-confidence"
                                    CUSTOM_DREP: "custom-drep"
                                    +

                                    Enumeration Members

                                    ALWAYS_ABSTAIN: "always-abstain"
                                    ALWAYS_NO_CONFIDENCE: "always-no-confidence"
                                    CUSTOM_DREP: "custom-drep"
                                    diff --git a/docs/enums/DRepKind.html b/docs/enums/DRepKind.html index 192f8ca5..eeb0f698 100644 --- a/docs/enums/DRepKind.html +++ b/docs/enums/DRepKind.html @@ -1,6 +1,6 @@ DRepKind | cardano-raw-sdk
                                    cardano-raw-sdk
                                      Preparing search index...

                                      Enumeration DRepKind

                                      DRep kind enum matching Conway era specification

                                      -
                                      Index

                                      Enumeration Members

                                      Index

                                      Enumeration Members

                                      ALWAYS_ABSTAIN: 2
                                      ALWAYS_NO_CONFIDENCE: 3
                                      KEY_HASH: 0
                                      SCRIPT_HASH: 1
                                      +

                                      Enumeration Members

                                      ALWAYS_ABSTAIN: 2
                                      ALWAYS_NO_CONFIDENCE: 3
                                      KEY_HASH: 0
                                      SCRIPT_HASH: 1
                                      diff --git a/docs/enums/GroupByOptions.html b/docs/enums/GroupByOptions.html index f400cf87..49ed77ae 100644 --- a/docs/enums/GroupByOptions.html +++ b/docs/enums/GroupByOptions.html @@ -1,4 +1,4 @@ -GroupByOptions | cardano-raw-sdk
                                      cardano-raw-sdk
                                        Preparing search index...

                                        Enumeration GroupByOptions

                                        Index

                                        Enumeration Members

                                        ADDRESS +GroupByOptions | cardano-raw-sdk
                                        cardano-raw-sdk
                                          Preparing search index...

                                          Enumeration GroupByOptions

                                          Index

                                          Enumeration Members

                                          Enumeration Members

                                          ADDRESS: "address"
                                          POLICY: "policy"
                                          TOKEN: "token"
                                          +

                                          Enumeration Members

                                          ADDRESS: "address"
                                          POLICY: "policy"
                                          TOKEN: "token"
                                          diff --git a/docs/enums/LogLevel.html b/docs/enums/LogLevel.html index 082da09b..a2f2e1f9 100644 --- a/docs/enums/LogLevel.html +++ b/docs/enums/LogLevel.html @@ -1,6 +1,6 @@ -LogLevel | cardano-raw-sdk
                                          cardano-raw-sdk
                                            Preparing search index...

                                            Enumeration LogLevel

                                            Index

                                            Enumeration Members

                                            DEBUG +LogLevel | cardano-raw-sdk
                                            cardano-raw-sdk
                                              Preparing search index...

                                              Enumeration LogLevel

                                              Index

                                              Enumeration Members

                                              Enumeration Members

                                              DEBUG: 0
                                              ERROR: 3
                                              INFO: 1
                                              NONE: 4
                                              WARN: 2
                                              +

                                              Enumeration Members

                                              DEBUG: 0
                                              ERROR: 3
                                              INFO: 1
                                              NONE: 4
                                              WARN: 2
                                              diff --git a/docs/enums/Networks.html b/docs/enums/Networks.html index 52a61c7f..a720354d 100644 --- a/docs/enums/Networks.html +++ b/docs/enums/Networks.html @@ -1,4 +1,4 @@ -Networks | cardano-raw-sdk
                                              cardano-raw-sdk
                                                Preparing search index...

                                                Enumeration Networks

                                                Index

                                                Enumeration Members

                                                MAINNET +Networks | cardano-raw-sdk
                                                cardano-raw-sdk
                                                  Preparing search index...

                                                  Enumeration Networks

                                                  Index

                                                  Enumeration Members

                                                  Enumeration Members

                                                  MAINNET: "mainnet"
                                                  PREPROD: "preprod"
                                                  PREVIEW: "preview"
                                                  +

                                                  Enumeration Members

                                                  MAINNET: "mainnet"
                                                  PREPROD: "preprod"
                                                  PREVIEW: "preview"
                                                  diff --git a/docs/enums/PoolStatus.html b/docs/enums/PoolStatus.html index 917a6f24..aeb0dd5c 100644 --- a/docs/enums/PoolStatus.html +++ b/docs/enums/PoolStatus.html @@ -1,4 +1,4 @@ -PoolStatus | cardano-raw-sdk
                                                  cardano-raw-sdk
                                                    Preparing search index...

                                                    Enumeration PoolStatus

                                                    Index

                                                    Enumeration Members

                                                    ACTIVE +PoolStatus | cardano-raw-sdk
                                                    cardano-raw-sdk
                                                      Preparing search index...

                                                      Enumeration PoolStatus

                                                      Index

                                                      Enumeration Members

                                                      Enumeration Members

                                                      ACTIVE: "active"
                                                      RETIRING: "retiring"
                                                      RETRIED: "retried"
                                                      +

                                                      Enumeration Members

                                                      ACTIVE: "active"
                                                      RETIRING: "retiring"
                                                      RETRIED: "retried"
                                                      diff --git a/docs/enums/RewardType.html b/docs/enums/RewardType.html index c9a263c6..124c0ebc 100644 --- a/docs/enums/RewardType.html +++ b/docs/enums/RewardType.html @@ -1,6 +1,6 @@ -RewardType | cardano-raw-sdk
                                                      cardano-raw-sdk
                                                        Preparing search index...

                                                        Enumeration RewardType

                                                        Index

                                                        Enumeration Members

                                                        LEADER +RewardType | cardano-raw-sdk
                                                        cardano-raw-sdk
                                                          Preparing search index...

                                                          Enumeration RewardType

                                                          Index

                                                          Enumeration Members

                                                          Enumeration Members

                                                          LEADER: "leader"
                                                          MEMBER: "member"
                                                          REFUND: "refund"
                                                          RESERVES: "reserves"
                                                          TREASURY: "treasury"
                                                          +

                                                          Enumeration Members

                                                          LEADER: "leader"
                                                          MEMBER: "member"
                                                          REFUND: "refund"
                                                          RESERVES: "reserves"
                                                          TREASURY: "treasury"
                                                          diff --git a/docs/enums/StakingOperation.html b/docs/enums/StakingOperation.html index c9016116..82467b50 100644 --- a/docs/enums/StakingOperation.html +++ b/docs/enums/StakingOperation.html @@ -1,8 +1,8 @@ -StakingOperation | cardano-raw-sdk
                                                          cardano-raw-sdk
                                                            Preparing search index...

                                                            Enumeration StakingOperation

                                                            Index

                                                            Enumeration Members

                                                            CAST_VOTE +StakingOperation | cardano-raw-sdk
                                                            cardano-raw-sdk
                                                              Preparing search index...

                                                              Enumeration StakingOperation

                                                              Index

                                                              Enumeration Members

                                                              CAST_VOTE: "cast-vote"
                                                              DELEGATE: "delegate"
                                                              DEREGISTER: "deregister"
                                                              REGISTER: "register"
                                                              REGISTER_DREP: "register-drep"
                                                              VOTE_DELEGATE: "vote-delegate"
                                                              WITHDRAW_REWARDS: "withdraw-rewards"
                                                              +

                                                              Enumeration Members

                                                              CAST_VOTE: "cast-vote"
                                                              DELEGATE: "delegate"
                                                              DEREGISTER: "deregister"
                                                              REGISTER: "register"
                                                              REGISTER_DREP: "register-drep"
                                                              VOTE_DELEGATE: "vote-delegate"
                                                              WITHDRAW_REWARDS: "withdraw-rewards"
                                                              diff --git a/docs/enums/SupportedAssets.html b/docs/enums/SupportedAssets.html index 435ee695..726bc8c8 100644 --- a/docs/enums/SupportedAssets.html +++ b/docs/enums/SupportedAssets.html @@ -1,7 +1,7 @@ SupportedAssets | cardano-raw-sdk
                                                              cardano-raw-sdk
                                                                Preparing search index...

                                                                Enumeration SupportedAssets

                                                                Fireblocks asset IDs for Cardano networks These map to internal Fireblocks asset identifiers used for transaction signing

                                                                -
                                                                Index

                                                                Enumeration Members

                                                                ADA +
                                                                Index

                                                                Enumeration Members

                                                                Enumeration Members

                                                                ADA: "ADA"

                                                                Fireblocks asset ID for Cardano mainnet

                                                                -
                                                                ADA_TEST: "ADA_TEST"

                                                                Fireblocks asset ID for Cardano testnets (preprod/preview)

                                                                -
                                                                +
                                                                ADA_TEST: "ADA_TEST"

                                                                Fireblocks asset ID for Cardano testnets (preprod/preview)

                                                                +
                                                                diff --git a/docs/enums/TransactionType.html b/docs/enums/TransactionType.html index 0a1dcb2e..aa21676a 100644 --- a/docs/enums/TransactionType.html +++ b/docs/enums/TransactionType.html @@ -1,6 +1,6 @@ -TransactionType | cardano-raw-sdk
                                                                cardano-raw-sdk
                                                                  Preparing search index...

                                                                  Enumeration TransactionType

                                                                  Index

                                                                  Enumeration Members

                                                                  GET_BALANCE_BY_STAKE_KEY +TransactionType | cardano-raw-sdk
                                                                  cardano-raw-sdk
                                                                    Preparing search index...

                                                                    Enumeration TransactionType

                                                                    Index

                                                                    Enumeration Members

                                                                    GET_BALANCE_BY_STAKE_KEY: "getBalanceByStakeKey"
                                                                    GET_BLALANCE_BY_ADDRESS: "checkBalanceByAddress"
                                                                    GET_BLALNCE_BY_CREDENTIAL_ID: "getBalanceByCredential"
                                                                    GET_TRANSACTIONS_HISTORY: "getTransactionsHistory"
                                                                    TRANSFER: "TRANSFER"
                                                                    +

                                                                    Enumeration Members

                                                                    GET_BALANCE_BY_STAKE_KEY: "getBalanceByStakeKey"
                                                                    GET_BLALANCE_BY_ADDRESS: "checkBalanceByAddress"
                                                                    GET_BLALNCE_BY_CREDENTIAL_ID: "getBalanceByCredential"
                                                                    GET_TRANSACTIONS_HISTORY: "getTransactionsHistory"
                                                                    TRANSFER: "TRANSFER"
                                                                    diff --git a/docs/enums/WebhookEventTypes.html b/docs/enums/WebhookEventTypes.html index c3a733fe..74e55fae 100644 --- a/docs/enums/WebhookEventTypes.html +++ b/docs/enums/WebhookEventTypes.html @@ -1,5 +1,5 @@ -WebhookEventTypes | cardano-raw-sdk
                                                                    cardano-raw-sdk
                                                                      Preparing search index...

                                                                      Enumeration WebhookEventTypes

                                                                      Index

                                                                      Enumeration Members

                                                                      TRANSACTION_APPROVAL_STATUS_UPDATED +WebhookEventTypes | cardano-raw-sdk
                                                                      cardano-raw-sdk
                                                                        Preparing search index...

                                                                        Enumeration WebhookEventTypes

                                                                        Index

                                                                        Enumeration Members

                                                                        TRANSACTION_APPROVAL_STATUS_UPDATED: "transaction.approval_status.updated"
                                                                        TRANSACTION_CREATED: "transaction.created"
                                                                        TRANSACTION_NETWORK_RECORDS_PROCESSING_COMPLETED: "transaction.network_records.processing_completed"
                                                                        TRANSACTION_STATUS_UPDATED: "transaction.status.updated"
                                                                        +

                                                                        Enumeration Members

                                                                        TRANSACTION_APPROVAL_STATUS_UPDATED: "transaction.approval_status.updated"
                                                                        TRANSACTION_CREATED: "transaction.created"
                                                                        TRANSACTION_NETWORK_RECORDS_PROCESSING_COMPLETED: "transaction.network_records.processing_completed"
                                                                        TRANSACTION_STATUS_UPDATED: "transaction.status.updated"
                                                                        diff --git a/docs/functions/assertRecipientAddress.html b/docs/functions/assertRecipientAddress.html new file mode 100644 index 00000000..758b45b0 --- /dev/null +++ b/docs/functions/assertRecipientAddress.html @@ -0,0 +1,3 @@ +assertRecipientAddress | cardano-raw-sdk
                                                                        cardano-raw-sdk
                                                                          Preparing search index...

                                                                          Function assertRecipientAddress

                                                                          • Parses a bech32 Cardano address and asserts it is on the expected network. +Throws SdkApiError on parse failure or network mismatch.

                                                                            +

                                                                            Parameters

                                                                            • recipientAddress: string
                                                                            • network: Networks

                                                                            Returns void

                                                                          diff --git a/docs/functions/assertTxSizeWithinLimit.html b/docs/functions/assertTxSizeWithinLimit.html new file mode 100644 index 00000000..892c3c48 --- /dev/null +++ b/docs/functions/assertTxSizeWithinLimit.html @@ -0,0 +1,5 @@ +assertTxSizeWithinLimit | cardano-raw-sdk
                                                                          cardano-raw-sdk
                                                                            Preparing search index...

                                                                            Function assertTxSizeWithinLimit

                                                                            • Throws if a serialized transaction would exceed the network maxTxSize. +Uses a conservative projection that accounts for one Ed25519 witness and +the surrounding CBOR overhead, so the check is meaningful for the +already-signed and not-yet-signed forms alike.

                                                                              +

                                                                              Parameters

                                                                              • serializedTxBodyOrTx: Uint8Array
                                                                              • context: string

                                                                              Returns void

                                                                            diff --git a/docs/functions/blakeHash.html b/docs/functions/blakeHash.html index 4e2e5b1d..4e5453c1 100644 --- a/docs/functions/blakeHash.html +++ b/docs/functions/blakeHash.html @@ -1,2 +1,2 @@ blakeHash | cardano-raw-sdk
                                                                            cardano-raw-sdk
                                                                              Preparing search index...

                                                                              Function blakeHash

                                                                              • Blake2b hash with configurable digest size (default 28 bytes for address hash, 32 for TX hash)

                                                                                -

                                                                                Parameters

                                                                                • payload: Buffer
                                                                                • digestSize: number = 28

                                                                                Returns Buffer

                                                                              +

                                                                              Parameters

                                                                              • payload: Buffer
                                                                              • digestSize: number = 28

                                                                              Returns Buffer

                                                                              diff --git a/docs/functions/buildAdaTransactionWithCalculatedFee.html b/docs/functions/buildAdaTransactionWithCalculatedFee.html index 8c708458..7f75164e 100644 --- a/docs/functions/buildAdaTransactionWithCalculatedFee.html +++ b/docs/functions/buildAdaTransactionWithCalculatedFee.html @@ -4,4 +4,4 @@
                                                                            • txInputs: TransactionInput[]

                                                                              Transaction inputs

                                                                            • ttl: number

                                                                              Slot deadline for the transaction

                                                                            • estimatedWitnessCount: number

                                                                              Number of signatures - use WITNESS_COUNT_* constants

                                                                              -
                                                                            • Returns { fee: number; outputs: TransactionOutput[]; txBody: TransactionBody }

                                                                              +

                                                                              Returns { fee: number; outputs: TransactionOutput[]; txBody: TransactionBody }

                                                                              diff --git a/docs/functions/buildCntTransactionWithCalculatedFee.html b/docs/functions/buildCntTransactionWithCalculatedFee.html index d013bf1e..f43f6a7b 100644 --- a/docs/functions/buildCntTransactionWithCalculatedFee.html +++ b/docs/functions/buildCntTransactionWithCalculatedFee.html @@ -4,4 +4,4 @@
                                                                            • txInputs: TransactionInput[]

                                                                              Transaction inputs

                                                                            • ttl: number

                                                                              Slot deadline for the transaction

                                                                            • estimatedWitnessCount: number

                                                                              Number of signatures - use WITNESS_COUNT_* constants

                                                                              -
                                                                            • Returns { fee: number; outputs: TransactionOutput[]; txBody: TransactionBody }

                                                                              +

                                                                              Returns { fee: number; outputs: TransactionOutput[]; txBody: TransactionBody }

                                                                              diff --git a/docs/functions/buildConsolidationTransactionWithCalculatedFee.html b/docs/functions/buildConsolidationTransactionWithCalculatedFee.html index 7c21e883..f486fc3f 100644 --- a/docs/functions/buildConsolidationTransactionWithCalculatedFee.html +++ b/docs/functions/buildConsolidationTransactionWithCalculatedFee.html @@ -3,4 +3,4 @@
                                                                            • txInputs: TransactionInput[]

                                                                              Transaction inputs (all UTxOs at the address)

                                                                            • ttl: number

                                                                              Slot deadline for the transaction

                                                                            • estimatedWitnessCount: number

                                                                              Number of signatures - use WITNESS_COUNT_* constants

                                                                              -
                                                                            • Returns { fee: number; outputs: TransactionOutput[]; txBody: TransactionBody }

                                                                              +

                                                                              Returns { fee: number; outputs: TransactionOutput[]; txBody: TransactionBody }

                                                                              diff --git a/docs/functions/buildDRepRegistrationCertificate.html b/docs/functions/buildDRepRegistrationCertificate.html index 0fa9a9d5..10f2cff9 100644 --- a/docs/functions/buildDRepRegistrationCertificate.html +++ b/docs/functions/buildDRepRegistrationCertificate.html @@ -1,4 +1,4 @@ buildDRepRegistrationCertificate | cardano-raw-sdk
                                                                              cardano-raw-sdk
                                                                                Preparing search index...

                                                                                Function buildDRepRegistrationCertificate

                                                                                • Build DRep registration certificate (Conway era, cert type 16) CBOR structure: [16, [0, credential_bytes], coin_deposit, anchor_or_null] where anchor = [url_string, data_hash_bytes] if provided

                                                                                  -

                                                                                  Parameters

                                                                                  • credential: Buffer
                                                                                  • depositLovelace: number
                                                                                  • Optionalanchor: { dataHash: string; url: string }

                                                                                  Returns CborArray

                                                                                +

                                                                                Parameters

                                                                                • credential: Buffer
                                                                                • depositLovelace: number
                                                                                • Optionalanchor: { dataHash: string; url: string }

                                                                                Returns CborArray

                                                                                diff --git a/docs/functions/buildDelegationCertificate.html b/docs/functions/buildDelegationCertificate.html index 790f4ec3..962dea83 100644 --- a/docs/functions/buildDelegationCertificate.html +++ b/docs/functions/buildDelegationCertificate.html @@ -1,2 +1,2 @@ buildDelegationCertificate | cardano-raw-sdk
                                                                                cardano-raw-sdk
                                                                                  Preparing search index...

                                                                                  Function buildDelegationCertificate

                                                                                  • Build pool delegation certificate

                                                                                    -

                                                                                    Parameters

                                                                                    • credential: Buffer
                                                                                    • poolId: string

                                                                                    Returns Certificate

                                                                                  +

                                                                                  Parameters

                                                                                  • credential: Buffer
                                                                                  • poolId: string

                                                                                  Returns Certificate

                                                                                  diff --git a/docs/functions/buildDeregistrationCertificate.html b/docs/functions/buildDeregistrationCertificate.html index 7182ce1a..ed7029b1 100644 --- a/docs/functions/buildDeregistrationCertificate.html +++ b/docs/functions/buildDeregistrationCertificate.html @@ -2,4 +2,4 @@ Certificate type: 1 (STAKE_KEY_DEREGISTRATION) - no refund field Note: Conway-era deregistration (type 8, with refund) is not used here; Shelley-era certs remain valid on the Conway ledger.

                                                                                  -

                                                                                  Parameters

                                                                                  • credential: Buffer

                                                                                  Returns CardanoCertificate

                                                                                  +

                                                                                  Parameters

                                                                                  • credential: Buffer

                                                                                  Returns CardanoCertificate

                                                                                  diff --git a/docs/functions/buildMultiTokenTransactionWithCalculatedFee.html b/docs/functions/buildMultiTokenTransactionWithCalculatedFee.html index a8aaa224..7fc29e17 100644 --- a/docs/functions/buildMultiTokenTransactionWithCalculatedFee.html +++ b/docs/functions/buildMultiTokenTransactionWithCalculatedFee.html @@ -3,4 +3,4 @@
                                                                                • txInputs: TransactionInput[]

                                                                                  Transaction inputs

                                                                                • ttl: number

                                                                                  Slot deadline for the transaction

                                                                                • estimatedWitnessCount: number

                                                                                  Number of signatures - use WITNESS_COUNT_* constants

                                                                                  -
                                                                                • Returns { fee: number; outputs: TransactionOutput[]; txBody: TransactionBody }

                                                                                  +

                                                                                  Returns { fee: number; outputs: TransactionOutput[]; txBody: TransactionBody }

                                                                                  diff --git a/docs/functions/buildPayload.html b/docs/functions/buildPayload.html index 0ddb898d..5ca689fd 100644 --- a/docs/functions/buildPayload.html +++ b/docs/functions/buildPayload.html @@ -1,2 +1,2 @@ buildPayload | cardano-raw-sdk
                                                                                  cardano-raw-sdk
                                                                                    Preparing search index...

                                                                                    Function buildPayload

                                                                                    +

                                                                                    Parameters

                                                                                    Returns { deserialized: Map<number, unknown>; serialized: Buffer }

                                                                                    diff --git a/docs/functions/buildRegistrationCertificate.html b/docs/functions/buildRegistrationCertificate.html index b4083b2d..dfdd3ffe 100644 --- a/docs/functions/buildRegistrationCertificate.html +++ b/docs/functions/buildRegistrationCertificate.html @@ -2,4 +2,4 @@ Certificate type: 0 (STAKE_KEY_REGISTRATION) - no deposit field Note: Conway-era registration (type 7, with deposit) is not used here; Shelley-era certs remain valid on the Conway ledger.

                                                                                    -

                                                                                    Parameters

                                                                                    • credential: Buffer

                                                                                    Returns CardanoCertificate

                                                                                    +

                                                                                    Parameters

                                                                                    • credential: Buffer

                                                                                    Returns CardanoCertificate

                                                                                    diff --git a/docs/functions/buildTransaction.html b/docs/functions/buildTransaction.html index 6707208a..02cb768f 100644 --- a/docs/functions/buildTransaction.html +++ b/docs/functions/buildTransaction.html @@ -1 +1 @@ -buildTransaction | cardano-raw-sdk
                                                                                    cardano-raw-sdk
                                                                                      Preparing search index...

                                                                                      Function buildTransaction

                                                                                      • Parameters

                                                                                        • __namedParameters: {
                                                                                              fee: number;
                                                                                              ttl: number;
                                                                                              txInputs: TransactionInput[];
                                                                                              txOutputs: TransactionOutput[];
                                                                                          }

                                                                                        Returns TransactionBody

                                                                                      +buildTransaction | cardano-raw-sdk
                                                                                      cardano-raw-sdk
                                                                                        Preparing search index...

                                                                                        Function buildTransaction

                                                                                        • Parameters

                                                                                          • __namedParameters: {
                                                                                                fee: number;
                                                                                                ttl: number;
                                                                                                txInputs: TransactionInput[];
                                                                                                txOutputs: TransactionOutput[];
                                                                                            }

                                                                                          Returns TransactionBody

                                                                                        diff --git a/docs/functions/buildVoteDelegationCertificate.html b/docs/functions/buildVoteDelegationCertificate.html index 34c451d4..82c942e1 100644 --- a/docs/functions/buildVoteDelegationCertificate.html +++ b/docs/functions/buildVoteDelegationCertificate.html @@ -1,2 +1,2 @@ buildVoteDelegationCertificate | cardano-raw-sdk
                                                                                        cardano-raw-sdk
                                                                                          Preparing search index...

                                                                                          Function buildVoteDelegationCertificate

                                                                                          +

                                                                                          Parameters

                                                                                          Returns CborArray

                                                                                          diff --git a/docs/functions/buildVotingProcedures.html b/docs/functions/buildVotingProcedures.html index ebe0f64b..518e3459 100644 --- a/docs/functions/buildVotingProcedures.html +++ b/docs/functions/buildVotingProcedures.html @@ -2,8 +2,14 @@

                                                                                          CBOR structure: { voter => { gov_action_id => voting_procedure } }

                                                                                          Where: -voter = [1, [0, drep_key_hash_bytes]] (voter type 1 = DRep, cred type 0 = key) -gov_action_id = [tx_hash_bytes, index] -voting_procedure = [vote, anchor_or_null] (vote: 0=No, 1=Yes, 2=Abstain) -anchor = [url_string, data_hash_bytes]

                                                                                          -

                                                                                          Parameters

                                                                                          • credential: Buffer
                                                                                          • governanceActionId: { index: number; txHash: string }
                                                                                          • vote: 0 | 1 | 2
                                                                                          • Optionalanchor: { dataHash: string; url: string }

                                                                                          Returns Map<CborArray, Map<CborArray, CborArray>>

                                                                                          +voter = [2, drep_key_hash_bytes] (Conway CDDL: tag 2 = drep_keyhash, flat 2-tuple) +gov_action_id = [tx_hash_bytes, index] +voting_procedure = [vote, anchor_or_null] (vote: 0=No, 1=Yes, 2=Abstain) +anchor = [url_string, data_hash_bytes]

                                                                                          +

                                                                                          Conway voter tags (IntersectMBO/cardano-ledger conway.cddl): +0 = committee_hot_keyhash +1 = committee_hot_scripthash +2 = drep_keyhash ← this SDK +3 = drep_scripthash +4 = stake_pool_keyhash

                                                                                          +

                                                                                          Parameters

                                                                                          • credential: Buffer
                                                                                          • governanceActionId: { index: number; txHash: string }
                                                                                          • vote: 0 | 1 | 2
                                                                                          • Optionalanchor: { dataHash: string; url: string }

                                                                                          Returns Map<CborArray, Map<CborArray, CborArray>>

                                                                                          diff --git a/docs/functions/calculateBalance.html b/docs/functions/calculateBalance.html deleted file mode 100644 index 0f24b169..00000000 --- a/docs/functions/calculateBalance.html +++ /dev/null @@ -1,7 +0,0 @@ -calculateBalance | cardano-raw-sdk
                                                                                          cardano-raw-sdk
                                                                                            Preparing search index...

                                                                                            Function calculateBalance

                                                                                            • Calculate token balance for given addresses

                                                                                              -

                                                                                              Parameters

                                                                                              • iagonApiService: IagonApiService

                                                                                                Iagon API service instance

                                                                                                -
                                                                                              • policyId: string

                                                                                                Token policy ID

                                                                                                -
                                                                                              • tokenName: string

                                                                                                Token name

                                                                                                -
                                                                                              • addresses: string[]

                                                                                                Array of ADA addresses to calculate balance for

                                                                                                -

                                                                                              Returns Promise<{ confirmed: number; total: number; unconfirmed: number }>

                                                                                              Promise resolving to balance information including total, confirmed and unconfirmed amounts

                                                                                              -
                                                                                            diff --git a/docs/functions/calculateMinLovelaceForUtxo.html b/docs/functions/calculateMinLovelaceForUtxo.html index d509cf4f..a2afa355 100644 --- a/docs/functions/calculateMinLovelaceForUtxo.html +++ b/docs/functions/calculateMinLovelaceForUtxo.html @@ -11,4 +11,4 @@
                                                                                            calculateMinLovelaceForUtxo(0) // 1,000,000 (ADA-only)
                                                                                            calculateMinLovelaceForUtxo(1) // 1,150,000 (1 token policy)
                                                                                            calculateMinLovelaceForUtxo(3) // 1,450,000 (3 token policies)
                                                                                            -
                                                                                            +
                                                                                            diff --git a/docs/functions/calculateTokenAmount.html b/docs/functions/calculateTokenAmount.html index 1571a86f..e6b02d11 100644 --- a/docs/functions/calculateTokenAmount.html +++ b/docs/functions/calculateTokenAmount.html @@ -1 +1 @@ -calculateTokenAmount | cardano-raw-sdk
                                                                                            cardano-raw-sdk
                                                                                              Preparing search index...

                                                                                              Function calculateTokenAmount

                                                                                              • Parameters

                                                                                                • utxo: UtxoData
                                                                                                • policyId: string
                                                                                                • tokenName: string

                                                                                                Returns number

                                                                                              +calculateTokenAmount | cardano-raw-sdk
                                                                                              cardano-raw-sdk
                                                                                                Preparing search index...

                                                                                                Function calculateTokenAmount

                                                                                                • Parameters

                                                                                                  • utxo: UtxoData
                                                                                                  • policyId: string
                                                                                                  • tokenName: string

                                                                                                  Returns number

                                                                                                diff --git a/docs/functions/calculateTransactionFee.html b/docs/functions/calculateTransactionFee.html index 9994aa4c..197b91a4 100644 --- a/docs/functions/calculateTransactionFee.html +++ b/docs/functions/calculateTransactionFee.html @@ -1,4 +1,4 @@ calculateTransactionFee | cardano-raw-sdk
                                                                                                cardano-raw-sdk
                                                                                                  Preparing search index...

                                                                                                  Function calculateTransactionFee

                                                                                                  • Calculate the minimum required fee for a transaction based on its size

                                                                                                    Parameters

                                                                                                    • tx: Transaction

                                                                                                      The transaction to calculate fee for

                                                                                                    Returns number

                                                                                                    The calculated fee in lovelace

                                                                                                    -
                                                                                                  +
                                                                                                  diff --git a/docs/functions/calculateTtl.html b/docs/functions/calculateTtl.html index 080b6341..65c42e78 100644 --- a/docs/functions/calculateTtl.html +++ b/docs/functions/calculateTtl.html @@ -1,2 +1,2 @@ calculateTtl | cardano-raw-sdk
                                                                                                  cardano-raw-sdk
                                                                                                    Preparing search index...

                                                                                                    Function calculateTtl

                                                                                                    • Calculate TTL (time to live) for transaction

                                                                                                      -

                                                                                                      Parameters

                                                                                                      • currentSlot: number
                                                                                                      • ttlSecs: number = CardanoAmounts.TX_TTL_SECS

                                                                                                      Returns number

                                                                                                    +

                                                                                                    Parameters

                                                                                                    • currentSlot: number
                                                                                                    • ttlSecs: number = CardanoAmounts.TX_TTL_SECS

                                                                                                    Returns number

                                                                                                    diff --git a/docs/functions/countDistinctPolicies.html b/docs/functions/countDistinctPolicies.html index bd91bacc..725d6aeb 100644 --- a/docs/functions/countDistinctPolicies.html +++ b/docs/functions/countDistinctPolicies.html @@ -1,4 +1,4 @@ countDistinctPolicies | cardano-raw-sdk
                                                                                                    cardano-raw-sdk
                                                                                                      Preparing search index...

                                                                                                      Function countDistinctPolicies

                                                                                                      • Count distinct token policies from a collection of assets

                                                                                                        Parameters

                                                                                                        • assets: Record<string, number>

                                                                                                          Record of assetUnit (policyId.tokenName) to amount

                                                                                                        Returns number

                                                                                                        Number of distinct policies

                                                                                                        -
                                                                                                      +
                                                                                                      diff --git a/docs/functions/createAdaTransactionOutputs.html b/docs/functions/createAdaTransactionOutputs.html index fab9e814..92efb140 100644 --- a/docs/functions/createAdaTransactionOutputs.html +++ b/docs/functions/createAdaTransactionOutputs.html @@ -2,4 +2,4 @@

                                                                                                      Recipient output: pure ADA only. Change output: remaining ADA + ALL tokens from spent UTxOs (token preservation).

                                                                                                      Throws if either output would violate the Cardano min-UTxO rule.

                                                                                                      -

                                                                                                      Returns TransactionOutput[]

                                                                                                      +

                                                                                                      Returns TransactionOutput[]

                                                                                                      diff --git a/docs/functions/createConsolidationOutput.html b/docs/functions/createConsolidationOutput.html index 277bf187..4290dbd0 100644 --- a/docs/functions/createConsolidationOutput.html +++ b/docs/functions/createConsolidationOutput.html @@ -2,4 +2,4 @@

                                                                                                      Unlike regular transfers there is no change output - the single output IS the result. All ADA (minus fee) and ALL tokens from the input UTxOs are merged into it.

                                                                                                      Throws if the remaining lovelace after the fee is below the protocol minimum.

                                                                                                      -

                                                                                                      Returns TransactionOutput[]

                                                                                                      +

                                                                                                      Returns TransactionOutput[]

                                                                                                      diff --git a/docs/functions/createMultiTokenTransactionOutputs.html b/docs/functions/createMultiTokenTransactionOutputs.html index 14b79cfd..2d41ef62 100644 --- a/docs/functions/createMultiTokenTransactionOutputs.html +++ b/docs/functions/createMultiTokenTransactionOutputs.html @@ -2,4 +2,4 @@

                                                                                                      Recipient output: all requested tokens + minimum required ADA. Change output: remaining ADA + all unrequested (or partially-used) tokens.

                                                                                                      Throws if either output would violate the Cardano min-UTxO rule.

                                                                                                      -

                                                                                                      Returns TransactionOutput[]

                                                                                                      +

                                                                                                      Returns TransactionOutput[]

                                                                                                      diff --git a/docs/functions/createTransactionInputs.html b/docs/functions/createTransactionInputs.html index 65a15320..da46651b 100644 --- a/docs/functions/createTransactionInputs.html +++ b/docs/functions/createTransactionInputs.html @@ -1 +1 @@ -createTransactionInputs | cardano-raw-sdk
                                                                                                      cardano-raw-sdk
                                                                                                        Preparing search index...

                                                                                                        Function createTransactionInputs

                                                                                                        +createTransactionInputs | cardano-raw-sdk
                                                                                                        cardano-raw-sdk
                                                                                                          Preparing search index...

                                                                                                          Function createTransactionInputs

                                                                                                          diff --git a/docs/functions/createTransactionOutputs.html b/docs/functions/createTransactionOutputs.html index 1e8ba710..fde25207 100644 --- a/docs/functions/createTransactionOutputs.html +++ b/docs/functions/createTransactionOutputs.html @@ -1 +1 @@ -createTransactionOutputs | cardano-raw-sdk
                                                                                                          cardano-raw-sdk
                                                                                                            Preparing search index...

                                                                                                            Function createTransactionOutputs

                                                                                                            +createTransactionOutputs | cardano-raw-sdk
                                                                                                            cardano-raw-sdk
                                                                                                              Preparing search index...

                                                                                                              Function createTransactionOutputs

                                                                                                              diff --git a/docs/functions/decodeAddress.html b/docs/functions/decodeAddress.html index 0a2c940c..a963c0df 100644 --- a/docs/functions/decodeAddress.html +++ b/docs/functions/decodeAddress.html @@ -1,2 +1,2 @@ decodeAddress | cardano-raw-sdk
                                                                                                              cardano-raw-sdk
                                                                                                                Preparing search index...

                                                                                                                Function decodeAddress

                                                                                                                • Decode Cardano address from bech32 to bytes

                                                                                                                  -

                                                                                                                  Parameters

                                                                                                                  • encodedAddress: string
                                                                                                                  • mainnet: boolean

                                                                                                                  Returns Buffer

                                                                                                                +

                                                                                                                Parameters

                                                                                                                • encodedAddress: string
                                                                                                                • mainnet: boolean

                                                                                                                Returns Buffer

                                                                                                                diff --git a/docs/functions/decodeAssetName.html b/docs/functions/decodeAssetName.html index ca4af54e..99413422 100644 --- a/docs/functions/decodeAssetName.html +++ b/docs/functions/decodeAssetName.html @@ -1 +1 @@ -decodeAssetName | cardano-raw-sdk
                                                                                                                cardano-raw-sdk
                                                                                                                  Preparing search index...

                                                                                                                  Function decodeAssetName

                                                                                                                  • Parameters

                                                                                                                    • assetId: string

                                                                                                                    Returns string

                                                                                                                  +decodeAssetName | cardano-raw-sdk
                                                                                                                  cardano-raw-sdk
                                                                                                                    Preparing search index...

                                                                                                                    Function decodeAssetName

                                                                                                                    • Parameters

                                                                                                                      • assetId: string

                                                                                                                      Returns string

                                                                                                                    diff --git a/docs/functions/drepActionToDRepInfo.html b/docs/functions/drepActionToDRepInfo.html index 28ead283..7d754cad 100644 --- a/docs/functions/drepActionToDRepInfo.html +++ b/docs/functions/drepActionToDRepInfo.html @@ -1,3 +1,3 @@ drepActionToDRepInfo | cardano-raw-sdk
                                                                                                                    cardano-raw-sdk
                                                                                                                      Preparing search index...

                                                                                                                      Function drepActionToDRepInfo

                                                                                                                      +

                                                                                                                      Parameters

                                                                                                                      Returns DRepInfo

                                                                                                                      diff --git a/docs/functions/embedSignaturesInTx.html b/docs/functions/embedSignaturesInTx.html index e27e9970..40d33bca 100644 --- a/docs/functions/embedSignaturesInTx.html +++ b/docs/functions/embedSignaturesInTx.html @@ -1,3 +1,3 @@ embedSignaturesInTx | cardano-raw-sdk
                                                                                                                      cardano-raw-sdk
                                                                                                                        Preparing search index...

                                                                                                                        Function embedSignaturesInTx

                                                                                                                        • Embed signatures in transaction to create final signed transaction Witnesses are automatically sorted by key hash as required by Cardano

                                                                                                                          -

                                                                                                                          Parameters

                                                                                                                          • deserializedTxPayload: Map<number, unknown>
                                                                                                                          • signatures: CardanoWitness[]

                                                                                                                          Returns Buffer

                                                                                                                        +

                                                                                                                        Parameters

                                                                                                                        • deserializedTxPayload: Map<number, unknown>
                                                                                                                        • signatures: CardanoWitness[]

                                                                                                                        Returns Buffer

                                                                                                                        diff --git a/docs/functions/encodeDRepId.html b/docs/functions/encodeDRepId.html index a1e5f40c..97f77ac3 100644 --- a/docs/functions/encodeDRepId.html +++ b/docs/functions/encodeDRepId.html @@ -1,4 +1,4 @@ -encodeDRepId | cardano-raw-sdk
                                                                                                                        cardano-raw-sdk
                                                                                                                          Preparing search index...

                                                                                                                          Function encodeDRepId

                                                                                                                          • Encode a stake credential as a bech32 DRep ID -Key-based DRep: hrp="drep", header=0x22 -Script-based DRep: hrp="drep_script", header=0x23

                                                                                                                            -

                                                                                                                            Parameters

                                                                                                                            • credential: Buffer
                                                                                                                            • isScript: boolean = false

                                                                                                                            Returns string

                                                                                                                          +encodeDRepId | cardano-raw-sdk
                                                                                                                          cardano-raw-sdk
                                                                                                                            Preparing search index...

                                                                                                                            Function encodeDRepId

                                                                                                                            • Encode a stake credential as a CIP-129 DRep ID. +Per CIP-129 the HRP is always "drep"; the type is encoded in the leading +header byte (0x22 = key-hash, 0x23 = script-hash).

                                                                                                                              +

                                                                                                                              Parameters

                                                                                                                              • credential: Buffer
                                                                                                                              • isScript: boolean = false

                                                                                                                              Returns string

                                                                                                                            diff --git a/docs/functions/encodeStakeAddress.html b/docs/functions/encodeStakeAddress.html index 1dba8038..92f54cb7 100644 --- a/docs/functions/encodeStakeAddress.html +++ b/docs/functions/encodeStakeAddress.html @@ -1,2 +1,2 @@ encodeStakeAddress | cardano-raw-sdk
                                                                                                                            cardano-raw-sdk
                                                                                                                              Preparing search index...

                                                                                                                              Function encodeStakeAddress

                                                                                                                              • Encode stake address from bytes to bech32 format

                                                                                                                                -

                                                                                                                                Parameters

                                                                                                                                • decodedAddress: Buffer
                                                                                                                                • mainnet: boolean

                                                                                                                                Returns string

                                                                                                                              +

                                                                                                                              Parameters

                                                                                                                              • decodedAddress: Buffer
                                                                                                                              • mainnet: boolean

                                                                                                                              Returns string

                                                                                                                              diff --git a/docs/functions/fetchAndSelectUtxosForAda.html b/docs/functions/fetchAndSelectUtxosForAda.html index c0500293..80475be3 100644 --- a/docs/functions/fetchAndSelectUtxosForAda.html +++ b/docs/functions/fetchAndSelectUtxosForAda.html @@ -7,4 +7,4 @@

                                                                                                                              When multi-asset UTxOs are consumed, ALL their tokens MUST appear in the change output (Cardano protocol requirement). The returned changeTokenAssets captures the full token inventory the change output must carry.

                                                                                                                              -

                                                                                                                              Returns Promise<
                                                                                                                                  {
                                                                                                                                      accumulatedAda: number;
                                                                                                                                      changeTokenAssets: Record<string, number>;
                                                                                                                                      minChangeLovelace: number;
                                                                                                                                      release: () => void;
                                                                                                                                      selectedUtxos: UtxoData[];
                                                                                                                                  },
                                                                                                                              >

                                                                                                                              +

                                                                                                                              Returns Promise<
                                                                                                                                  {
                                                                                                                                      accumulatedAda: number;
                                                                                                                                      changeTokenAssets: Record<string, number>;
                                                                                                                                      minChangeLovelace: number;
                                                                                                                                      release: () => void;
                                                                                                                                      selectedUtxos: UtxoData[];
                                                                                                                                  },
                                                                                                                              >

                                                                                                                              diff --git a/docs/functions/fetchAndSelectUtxosForCnt.html b/docs/functions/fetchAndSelectUtxosForCnt.html index 87a4ff88..974cbb1b 100644 --- a/docs/functions/fetchAndSelectUtxosForCnt.html +++ b/docs/functions/fetchAndSelectUtxosForCnt.html @@ -1 +1 @@ -fetchAndSelectUtxosForCnt | cardano-raw-sdk
                                                                                                                              cardano-raw-sdk
                                                                                                                                Preparing search index...

                                                                                                                                Function fetchAndSelectUtxosForCnt

                                                                                                                                • Returns Promise<
                                                                                                                                      {
                                                                                                                                          accumulatedAda: number;
                                                                                                                                          accumulatedTokenAmount: number;
                                                                                                                                          minChangeLovelace: number;
                                                                                                                                          minRecipientLovelace: number;
                                                                                                                                          release: () => void;
                                                                                                                                          selectedUtxos: UtxoData[];
                                                                                                                                      },
                                                                                                                                  >

                                                                                                                                +fetchAndSelectUtxosForCnt | cardano-raw-sdk
                                                                                                                                cardano-raw-sdk
                                                                                                                                  Preparing search index...

                                                                                                                                  Function fetchAndSelectUtxosForCnt

                                                                                                                                  • Returns Promise<
                                                                                                                                        {
                                                                                                                                            accumulatedAda: number;
                                                                                                                                            accumulatedTokenAmount: number;
                                                                                                                                            minChangeLovelace: number;
                                                                                                                                            minRecipientLovelace: number;
                                                                                                                                            release: () => void;
                                                                                                                                            selectedUtxos: UtxoData[];
                                                                                                                                        },
                                                                                                                                    >

                                                                                                                                  diff --git a/docs/functions/fetchAndSelectUtxosForMultiToken.html b/docs/functions/fetchAndSelectUtxosForMultiToken.html index be27e58e..0708eebd 100644 --- a/docs/functions/fetchAndSelectUtxosForMultiToken.html +++ b/docs/functions/fetchAndSelectUtxosForMultiToken.html @@ -4,4 +4,4 @@ Phase 2: Supplement with ADA-only UTxOs (preferred) if the total ADA is insufficient to cover fee + minRecipient + minChange.

                                                                                                                                  Throws if any required token cannot be fully covered.

                                                                                                                                  -

                                                                                                                                  Returns Promise<
                                                                                                                                      {
                                                                                                                                          accumulatedAda: number;
                                                                                                                                          changeTokenAssets: Record<string, number>;
                                                                                                                                          minChangeLovelace: number;
                                                                                                                                          release: () => void;
                                                                                                                                          selectedUtxos: UtxoData[];
                                                                                                                                      },
                                                                                                                                  >

                                                                                                                                  +

                                                                                                                                  Returns Promise<
                                                                                                                                      {
                                                                                                                                          accumulatedAda: number;
                                                                                                                                          changeTokenAssets: Record<string, number>;
                                                                                                                                          minChangeLovelace: number;
                                                                                                                                          release: () => void;
                                                                                                                                          selectedUtxos: UtxoData[];
                                                                                                                                      },
                                                                                                                                  >

                                                                                                                                  diff --git a/docs/functions/fetchUtxos.html b/docs/functions/fetchUtxos.html index 71931fd2..681ee8ea 100644 --- a/docs/functions/fetchUtxos.html +++ b/docs/functions/fetchUtxos.html @@ -1 +1 @@ -fetchUtxos | cardano-raw-sdk
                                                                                                                                  cardano-raw-sdk
                                                                                                                                    Preparing search index...

                                                                                                                                    Function fetchUtxos

                                                                                                                                    +fetchUtxos | cardano-raw-sdk
                                                                                                                                    cardano-raw-sdk
                                                                                                                                      Preparing search index...

                                                                                                                                      Function fetchUtxos

                                                                                                                                      diff --git a/docs/functions/filterUtxos.html b/docs/functions/filterUtxos.html index 55b1f32b..ebb08838 100644 --- a/docs/functions/filterUtxos.html +++ b/docs/functions/filterUtxos.html @@ -1 +1 @@ -filterUtxos | cardano-raw-sdk
                                                                                                                                      cardano-raw-sdk
                                                                                                                                        Preparing search index...

                                                                                                                                        Function filterUtxos

                                                                                                                                        +filterUtxos | cardano-raw-sdk
                                                                                                                                        cardano-raw-sdk
                                                                                                                                          Preparing search index...

                                                                                                                                          Function filterUtxos

                                                                                                                                          diff --git a/docs/functions/findSuitableUtxo.html b/docs/functions/findSuitableUtxo.html index 02458d8a..07d7b551 100644 --- a/docs/functions/findSuitableUtxo.html +++ b/docs/functions/findSuitableUtxo.html @@ -1 +1 @@ -findSuitableUtxo | cardano-raw-sdk
                                                                                                                                          cardano-raw-sdk
                                                                                                                                            Preparing search index...

                                                                                                                                            Function findSuitableUtxo

                                                                                                                                            • Parameters

                                                                                                                                              • utxos: {
                                                                                                                                                    output_index: number;
                                                                                                                                                    transaction_id: string;
                                                                                                                                                    value: { assets?: Record<string, number>; lovelace: number };
                                                                                                                                                }[]
                                                                                                                                              • minAmount: number

                                                                                                                                              Returns UtxoForStaking | null

                                                                                                                                            +findSuitableUtxo | cardano-raw-sdk
                                                                                                                                            cardano-raw-sdk
                                                                                                                                              Preparing search index...

                                                                                                                                              Function findSuitableUtxo

                                                                                                                                              • Parameters

                                                                                                                                                • utxos: {
                                                                                                                                                      output_index: number;
                                                                                                                                                      transaction_id: string;
                                                                                                                                                      value: { assets?: Record<string, number>; lovelace: number };
                                                                                                                                                  }[]
                                                                                                                                                • minAmount: number

                                                                                                                                                Returns UtxoForStaking | null

                                                                                                                                              diff --git a/docs/functions/formatWithDecimals.html b/docs/functions/formatWithDecimals.html index 226eb431..9dbf88ec 100644 --- a/docs/functions/formatWithDecimals.html +++ b/docs/functions/formatWithDecimals.html @@ -7,4 +7,4 @@
                                                                                                                                              formatWithDecimals(1700000, 6) // { value: "1.700000", raw: "1,700,000" }
                                                                                                                                              formatWithDecimals(470000, 6) // { value: "0.470000", raw: "470,000" }
                                                                                                                                              -
                                                                                                                                              +
                                                                                                                                              diff --git a/docs/functions/getAddressHrp.html b/docs/functions/getAddressHrp.html index 7de66d9d..63802852 100644 --- a/docs/functions/getAddressHrp.html +++ b/docs/functions/getAddressHrp.html @@ -1,2 +1,2 @@ getAddressHrp | cardano-raw-sdk
                                                                                                                                              cardano-raw-sdk
                                                                                                                                                Preparing search index...

                                                                                                                                                Function getAddressHrp

                                                                                                                                                • Get address HRP based on network

                                                                                                                                                  -

                                                                                                                                                  Parameters

                                                                                                                                                  • mainnet: boolean

                                                                                                                                                  Returns string

                                                                                                                                                +

                                                                                                                                                Parameters

                                                                                                                                                • mainnet: boolean

                                                                                                                                                Returns string

                                                                                                                                                diff --git a/docs/functions/getCertificateFromBaseAddress.html b/docs/functions/getCertificateFromBaseAddress.html index a0d62bb3..43ca5c92 100644 --- a/docs/functions/getCertificateFromBaseAddress.html +++ b/docs/functions/getCertificateFromBaseAddress.html @@ -1,2 +1,2 @@ getCertificateFromBaseAddress | cardano-raw-sdk
                                                                                                                                                cardano-raw-sdk
                                                                                                                                                  Preparing search index...

                                                                                                                                                  Function getCertificateFromBaseAddress

                                                                                                                                                  • Extract stake credential (last 28 bytes) from a base address

                                                                                                                                                    -

                                                                                                                                                    Parameters

                                                                                                                                                    • baseAddress: string
                                                                                                                                                    • mainnet: boolean

                                                                                                                                                    Returns Buffer

                                                                                                                                                  +

                                                                                                                                                  Parameters

                                                                                                                                                  • baseAddress: string
                                                                                                                                                  • mainnet: boolean

                                                                                                                                                  Returns Buffer

                                                                                                                                                  diff --git a/docs/functions/getConfig.html b/docs/functions/getConfig.html index fae1eee4..41fd44df 100644 --- a/docs/functions/getConfig.html +++ b/docs/functions/getConfig.html @@ -1,4 +1,4 @@ getConfig | cardano-raw-sdk
                                                                                                                                                  cardano-raw-sdk
                                                                                                                                                    Preparing search index...

                                                                                                                                                    Function getConfig

                                                                                                                                                    • Get the config object (lazy initialization) Will automatically load from environment variables on first access

                                                                                                                                                      Returns Config

                                                                                                                                                      Config object

                                                                                                                                                      -
                                                                                                                                                    +
                                                                                                                                                    diff --git a/docs/functions/getDefaultProtocolParams.html b/docs/functions/getDefaultProtocolParams.html new file mode 100644 index 00000000..0eea6bca --- /dev/null +++ b/docs/functions/getDefaultProtocolParams.html @@ -0,0 +1,3 @@ +getDefaultProtocolParams | cardano-raw-sdk
                                                                                                                                                    cardano-raw-sdk
                                                                                                                                                      Preparing search index...

                                                                                                                                                      Function getDefaultProtocolParams

                                                                                                                                                      diff --git a/docs/functions/getExtraPolicies.html b/docs/functions/getExtraPolicies.html new file mode 100644 index 00000000..9d9292c4 --- /dev/null +++ b/docs/functions/getExtraPolicies.html @@ -0,0 +1 @@ +getExtraPolicies | cardano-raw-sdk
                                                                                                                                                      cardano-raw-sdk
                                                                                                                                                        Preparing search index...

                                                                                                                                                        Function getExtraPolicies

                                                                                                                                                        • Parameters

                                                                                                                                                          • changeAssets: Record<string, number>
                                                                                                                                                          • intendedPolicies: string[]

                                                                                                                                                          Returns string[]

                                                                                                                                                        diff --git a/docs/functions/getLovelaceAmount.html b/docs/functions/getLovelaceAmount.html index ee73a11f..e6bc1f0b 100644 --- a/docs/functions/getLovelaceAmount.html +++ b/docs/functions/getLovelaceAmount.html @@ -1 +1 @@ -getLovelaceAmount | cardano-raw-sdk
                                                                                                                                                        cardano-raw-sdk
                                                                                                                                                          Preparing search index...

                                                                                                                                                          Function getLovelaceAmount

                                                                                                                                                          +getLovelaceAmount | cardano-raw-sdk
                                                                                                                                                          cardano-raw-sdk
                                                                                                                                                            Preparing search index...

                                                                                                                                                            Function getLovelaceAmount

                                                                                                                                                            diff --git a/docs/functions/getProtocolParams.html b/docs/functions/getProtocolParams.html new file mode 100644 index 00000000..b425091e --- /dev/null +++ b/docs/functions/getProtocolParams.html @@ -0,0 +1,3 @@ +getProtocolParams | cardano-raw-sdk
                                                                                                                                                            cardano-raw-sdk
                                                                                                                                                              Preparing search index...

                                                                                                                                                              Function getProtocolParams

                                                                                                                                                              diff --git a/docs/functions/getSigningPayload.html b/docs/functions/getSigningPayload.html index df5b150a..36ef838f 100644 --- a/docs/functions/getSigningPayload.html +++ b/docs/functions/getSigningPayload.html @@ -1,2 +1,2 @@ getSigningPayload | cardano-raw-sdk
                                                                                                                                                              cardano-raw-sdk
                                                                                                                                                                Preparing search index...

                                                                                                                                                                Function getSigningPayload

                                                                                                                                                                • Calculate transaction hash for signing (32 byte Blake2b hash)

                                                                                                                                                                  -

                                                                                                                                                                  Parameters

                                                                                                                                                                  • serializedTx: Buffer

                                                                                                                                                                  Returns Buffer

                                                                                                                                                                +

                                                                                                                                                                Parameters

                                                                                                                                                                • serializedTx: Buffer

                                                                                                                                                                Returns Buffer

                                                                                                                                                                diff --git a/docs/functions/getStakeAddressFromBaseAddress.html b/docs/functions/getStakeAddressFromBaseAddress.html index fab176d7..15e6e616 100644 --- a/docs/functions/getStakeAddressFromBaseAddress.html +++ b/docs/functions/getStakeAddressFromBaseAddress.html @@ -1,2 +1,2 @@ getStakeAddressFromBaseAddress | cardano-raw-sdk
                                                                                                                                                                cardano-raw-sdk
                                                                                                                                                                  Preparing search index...

                                                                                                                                                                  Function getStakeAddressFromBaseAddress

                                                                                                                                                                  • Get stake address from base address

                                                                                                                                                                    -

                                                                                                                                                                    Parameters

                                                                                                                                                                    • baseAddress: string
                                                                                                                                                                    • mainnet: boolean

                                                                                                                                                                    Returns string

                                                                                                                                                                  +

                                                                                                                                                                  Parameters

                                                                                                                                                                  • baseAddress: string
                                                                                                                                                                  • mainnet: boolean

                                                                                                                                                                  Returns string

                                                                                                                                                                  diff --git a/docs/functions/getStakeAddressFromCertificate.html b/docs/functions/getStakeAddressFromCertificate.html index 643b40a9..ee453671 100644 --- a/docs/functions/getStakeAddressFromCertificate.html +++ b/docs/functions/getStakeAddressFromCertificate.html @@ -1,2 +1,2 @@ getStakeAddressFromCertificate | cardano-raw-sdk
                                                                                                                                                                  cardano-raw-sdk
                                                                                                                                                                    Preparing search index...

                                                                                                                                                                    Function getStakeAddressFromCertificate

                                                                                                                                                                    • Get stake address from certificate (credential hash)

                                                                                                                                                                      -

                                                                                                                                                                      Parameters

                                                                                                                                                                      • certificate: Buffer
                                                                                                                                                                      • mainnet: boolean

                                                                                                                                                                      Returns string

                                                                                                                                                                    +

                                                                                                                                                                    Parameters

                                                                                                                                                                    • certificate: Buffer
                                                                                                                                                                    • mainnet: boolean

                                                                                                                                                                    Returns string

                                                                                                                                                                    diff --git a/docs/functions/getStakeAddressHrp.html b/docs/functions/getStakeAddressHrp.html index c4ff744c..ca86f029 100644 --- a/docs/functions/getStakeAddressHrp.html +++ b/docs/functions/getStakeAddressHrp.html @@ -1,2 +1,2 @@ getStakeAddressHrp | cardano-raw-sdk
                                                                                                                                                                    cardano-raw-sdk
                                                                                                                                                                      Preparing search index...

                                                                                                                                                                      Function getStakeAddressHrp

                                                                                                                                                                      • Get stake address HRP (human readable part) based on network

                                                                                                                                                                        -

                                                                                                                                                                        Parameters

                                                                                                                                                                        • mainnet: boolean

                                                                                                                                                                        Returns string

                                                                                                                                                                      +

                                                                                                                                                                      Parameters

                                                                                                                                                                      • mainnet: boolean

                                                                                                                                                                      Returns string

                                                                                                                                                                      diff --git a/docs/functions/getTxStatus.html b/docs/functions/getTxStatus.html index c1205946..5bb238d8 100644 --- a/docs/functions/getTxStatus.html +++ b/docs/functions/getTxStatus.html @@ -4,7 +4,7 @@

                                                                                                                                                                      Parameters

                                                                                                                                                                      • txId: string

                                                                                                                                                                        The Fireblocks transaction ID to monitor

                                                                                                                                                                      • fireblocks: Fireblocks

                                                                                                                                                                        Initialized Fireblocks SDK instance for API calls

                                                                                                                                                                      • pollingInterval: number = 1000

                                                                                                                                                                        Optional interval between status checks in milliseconds (default: 1000ms)

                                                                                                                                                                        -
                                                                                                                                                                      • maxWaitMs: number = ...

                                                                                                                                                                        Maximum total time to wait before throwing a timeout error (default: 10 minutes)

                                                                                                                                                                        +
                                                                                                                                                                      • maxWaitMs: number = ...

                                                                                                                                                                        Maximum total time to wait before throwing a timeout error (default: 30 minutes, matching Fireblocks RAW signing timeout)

                                                                                                                                                                      Returns Promise<TransactionResponse>

                                                                                                                                                                      Promise resolving to the final TransactionResponse when completed or broadcasting

                                                                                                                                                                      If transaction is blocked - policy or compliance issue

                                                                                                                                                                      If transaction is cancelled - user or system cancellation

                                                                                                                                                                      @@ -34,4 +34,4 @@
                                                                                                                                                                      const txResponse = await fireblocks.transactions.createTransaction({...});
                                                                                                                                                                      const completedTx = await getTxStatus(txResponse.data.id, fireblocks, 2000);
                                                                                                                                                                      const signature = completedTx.signedMessages?.[0]?.signature;
                                                                                                                                                                      -
                                                                                                                                                                      +
                                                                                                                                                                      diff --git a/docs/functions/initConfig.html b/docs/functions/initConfig.html index e6ea95e9..33c5b8b9 100644 --- a/docs/functions/initConfig.html +++ b/docs/functions/initConfig.html @@ -4,4 +4,4 @@

                                                                                                                                                                      Returns void

                                                                                                                                                                      initConfig({
                                                                                                                                                                      FIREBLOCKS: {
                                                                                                                                                                      apiKey: "your-api-key",
                                                                                                                                                                      secretKey: "your-secret-key",
                                                                                                                                                                      basePath: BasePath.US
                                                                                                                                                                      }
                                                                                                                                                                      });
                                                                                                                                                                      -
                                                                                                                                                                      +
                                                                                                                                                                      diff --git a/docs/functions/isConfigInitialized.html b/docs/functions/isConfigInitialized.html index 56ba1020..7c7d6e32 100644 --- a/docs/functions/isConfigInitialized.html +++ b/docs/functions/isConfigInitialized.html @@ -1,2 +1,2 @@ isConfigInitialized | cardano-raw-sdk
                                                                                                                                                                      cardano-raw-sdk
                                                                                                                                                                        Preparing search index...

                                                                                                                                                                        Function isConfigInitialized

                                                                                                                                                                        • Check if config has been initialized

                                                                                                                                                                          -

                                                                                                                                                                          Returns boolean

                                                                                                                                                                        +

                                                                                                                                                                        Returns boolean

                                                                                                                                                                        diff --git a/docs/functions/isSpendableUtxo.html b/docs/functions/isSpendableUtxo.html new file mode 100644 index 00000000..5c60f1ac --- /dev/null +++ b/docs/functions/isSpendableUtxo.html @@ -0,0 +1,5 @@ +isSpendableUtxo | cardano-raw-sdk
                                                                                                                                                                        cardano-raw-sdk
                                                                                                                                                                          Preparing search index...

                                                                                                                                                                          Function isSpendableUtxo

                                                                                                                                                                          • A UTxO is spendable by this SDK only with a simple Ed25519 witness. +UTxOs carrying a datum_hash (Plutus v1/v2 inputs) or a script_hash +(script-locked outputs) require a Plutus/native-script witness and +must be excluded from selection.

                                                                                                                                                                            +

                                                                                                                                                                            Parameters

                                                                                                                                                                            Returns boolean

                                                                                                                                                                          diff --git a/docs/functions/parseAdaStringToLovelace.html b/docs/functions/parseAdaStringToLovelace.html index 50086482..43d4135b 100644 --- a/docs/functions/parseAdaStringToLovelace.html +++ b/docs/functions/parseAdaStringToLovelace.html @@ -1,3 +1,3 @@ parseAdaStringToLovelace | cardano-raw-sdk
                                                                                                                                                                          cardano-raw-sdk
                                                                                                                                                                            Preparing search index...

                                                                                                                                                                            Function parseAdaStringToLovelace

                                                                                                                                                                            • Parse an ADA decimal string (e.g. "0.178701") to lovelace as an integer. Uses string splitting to avoid floating-point precision loss.

                                                                                                                                                                              -

                                                                                                                                                                              Parameters

                                                                                                                                                                              • ada: string

                                                                                                                                                                              Returns number

                                                                                                                                                                            +

                                                                                                                                                                            Parameters

                                                                                                                                                                            • ada: string

                                                                                                                                                                            Returns number

                                                                                                                                                                            diff --git a/docs/functions/resetConfig.html b/docs/functions/resetConfig.html index f26b5360..861afa05 100644 --- a/docs/functions/resetConfig.html +++ b/docs/functions/resetConfig.html @@ -1,2 +1,2 @@ resetConfig | cardano-raw-sdk
                                                                                                                                                                            cardano-raw-sdk
                                                                                                                                                                              Preparing search index...

                                                                                                                                                                              Function resetConfig

                                                                                                                                                                              +

                                                                                                                                                                              Returns void

                                                                                                                                                                              diff --git a/docs/functions/resetProtocolParams.html b/docs/functions/resetProtocolParams.html new file mode 100644 index 00000000..1e439bf0 --- /dev/null +++ b/docs/functions/resetProtocolParams.html @@ -0,0 +1,3 @@ +resetProtocolParams | cardano-raw-sdk
                                                                                                                                                                              cardano-raw-sdk
                                                                                                                                                                                Preparing search index...

                                                                                                                                                                                Function resetProtocolParams

                                                                                                                                                                                • Reset protocol parameters to defaults. +Useful for testing or when switching networks.

                                                                                                                                                                                  +

                                                                                                                                                                                  Returns void

                                                                                                                                                                                diff --git a/docs/functions/sanitizeForLogging.html b/docs/functions/sanitizeForLogging.html index df6a3697..a796e5d9 100644 --- a/docs/functions/sanitizeForLogging.html +++ b/docs/functions/sanitizeForLogging.html @@ -7,4 +7,4 @@
                                                                                                                                                                                const data = {
                                                                                                                                                                                username: 'john',
                                                                                                                                                                                password: 'secret123',
                                                                                                                                                                                apiKey: 'sk_12345'
                                                                                                                                                                                };

                                                                                                                                                                                sanitizeForLogging(data);
                                                                                                                                                                                // Returns: { username: 'john', password: '[REDACTED]', apiKey: '[REDACTED]' }
                                                                                                                                                                                -
                                                                                                                                                                                +
                                                                                                                                                                                diff --git a/docs/functions/serializeCertificate.html b/docs/functions/serializeCertificate.html index f638836f..1f78958a 100644 --- a/docs/functions/serializeCertificate.html +++ b/docs/functions/serializeCertificate.html @@ -1,3 +1,3 @@ serializeCertificate | cardano-raw-sdk
                                                                                                                                                                                cardano-raw-sdk
                                                                                                                                                                                  Preparing search index...

                                                                                                                                                                                  Function serializeCertificate

                                                                                                                                                                                  • Serialize certificate for CBOR encoding Returns [0, certificate] array

                                                                                                                                                                                    -

                                                                                                                                                                                    Parameters

                                                                                                                                                                                    • certificate: Buffer

                                                                                                                                                                                    Returns [number, Uint8Array<ArrayBufferLike>]

                                                                                                                                                                                  +

                                                                                                                                                                                  Parameters

                                                                                                                                                                                  • certificate: Buffer

                                                                                                                                                                                  Returns [number, Uint8Array<ArrayBufferLike>]

                                                                                                                                                                                  diff --git a/docs/functions/serializeWithdrawals.html b/docs/functions/serializeWithdrawals.html index 801eaf11..5d80e501 100644 --- a/docs/functions/serializeWithdrawals.html +++ b/docs/functions/serializeWithdrawals.html @@ -1,2 +1,2 @@ serializeWithdrawals | cardano-raw-sdk
                                                                                                                                                                                  cardano-raw-sdk
                                                                                                                                                                                    Preparing search index...

                                                                                                                                                                                    Function serializeWithdrawals

                                                                                                                                                                                    +

                                                                                                                                                                                    Parameters

                                                                                                                                                                                    Returns Map<Uint8Array<ArrayBufferLike>, number>

                                                                                                                                                                                    diff --git a/docs/functions/setProtocolParams.html b/docs/functions/setProtocolParams.html new file mode 100644 index 00000000..b22e59d9 --- /dev/null +++ b/docs/functions/setProtocolParams.html @@ -0,0 +1,4 @@ +setProtocolParams | cardano-raw-sdk
                                                                                                                                                                                    cardano-raw-sdk
                                                                                                                                                                                      Preparing search index...

                                                                                                                                                                                      Function setProtocolParams

                                                                                                                                                                                      • Update protocol parameters. +Merges provided values with current params (partial update supported).

                                                                                                                                                                                        +

                                                                                                                                                                                        Parameters

                                                                                                                                                                                        • params: Partial<ProtocolParams>

                                                                                                                                                                                          Partial protocol parameters to update

                                                                                                                                                                                          +

                                                                                                                                                                                        Returns void

                                                                                                                                                                                      diff --git a/docs/functions/sortWitnesses.html b/docs/functions/sortWitnesses.html index fe22b831..2375ece7 100644 --- a/docs/functions/sortWitnesses.html +++ b/docs/functions/sortWitnesses.html @@ -1,3 +1,3 @@ sortWitnesses | cardano-raw-sdk
                                                                                                                                                                                      cardano-raw-sdk
                                                                                                                                                                                        Preparing search index...

                                                                                                                                                                                        Function sortWitnesses

                                                                                                                                                                                        +

                                                                                                                                                                                        Parameters

                                                                                                                                                                                        Returns CardanoWitness[]

                                                                                                                                                                                        diff --git a/docs/functions/stakeAddressBytesPrefix.html b/docs/functions/stakeAddressBytesPrefix.html index f8727182..f69d6c53 100644 --- a/docs/functions/stakeAddressBytesPrefix.html +++ b/docs/functions/stakeAddressBytesPrefix.html @@ -1,2 +1,2 @@ stakeAddressBytesPrefix | cardano-raw-sdk
                                                                                                                                                                                        cardano-raw-sdk
                                                                                                                                                                                          Preparing search index...

                                                                                                                                                                                          Function stakeAddressBytesPrefix

                                                                                                                                                                                          • Get stake address bytes prefix based on network

                                                                                                                                                                                            -

                                                                                                                                                                                            Parameters

                                                                                                                                                                                            • mainnet: boolean

                                                                                                                                                                                            Returns Buffer

                                                                                                                                                                                          +

                                                                                                                                                                                          Parameters

                                                                                                                                                                                          • mainnet: boolean

                                                                                                                                                                                          Returns Buffer

                                                                                                                                                                                          diff --git a/docs/functions/submitTransaction.html b/docs/functions/submitTransaction.html index 1d7bb1df..a86d3d37 100644 --- a/docs/functions/submitTransaction.html +++ b/docs/functions/submitTransaction.html @@ -1 +1 @@ -submitTransaction | cardano-raw-sdk
                                                                                                                                                                                          cardano-raw-sdk
                                                                                                                                                                                            Preparing search index...

                                                                                                                                                                                            Function submitTransaction

                                                                                                                                                                                            +submitTransaction | cardano-raw-sdk
                                                                                                                                                                                            cardano-raw-sdk
                                                                                                                                                                                              Preparing search index...

                                                                                                                                                                                              Function submitTransaction

                                                                                                                                                                                              diff --git a/docs/functions/toHex.html b/docs/functions/toHex.html index 4ce3bc23..635b412f 100644 --- a/docs/functions/toHex.html +++ b/docs/functions/toHex.html @@ -1 +1 @@ -toHex | cardano-raw-sdk
                                                                                                                                                                                              cardano-raw-sdk
                                                                                                                                                                                                Preparing search index...

                                                                                                                                                                                                Function toHex

                                                                                                                                                                                                +toHex | cardano-raw-sdk
                                                                                                                                                                                                cardano-raw-sdk
                                                                                                                                                                                                  Preparing search index...

                                                                                                                                                                                                  Function toHex

                                                                                                                                                                                                  diff --git a/docs/hierarchy.html b/docs/hierarchy.html index ebf14479..c5af04d9 100644 --- a/docs/hierarchy.html +++ b/docs/hierarchy.html @@ -1 +1 @@ -cardano-raw-sdk
                                                                                                                                                                                                  cardano-raw-sdk
                                                                                                                                                                                                    Preparing search index...
                                                                                                                                                                                                    +cardano-raw-sdk
                                                                                                                                                                                                    cardano-raw-sdk
                                                                                                                                                                                                      Preparing search index...
                                                                                                                                                                                                      diff --git a/docs/index.html b/docs/index.html index a6ce89f6..d4923755 100644 --- a/docs/index.html +++ b/docs/index.html @@ -34,7 +34,7 @@