A TypeScript SDK for managing Cardano token transfers through Fireblocks, with integrated Iagon API services for balance queries, transaction history, and token operations.
- 🔐 Fireblocks Integration: Secure vault account management and transaction signing
- 🏦 Balance Queries: Check balances by address, credential, or stake key
- 💸 Token Transfers: Execute Cardano token transfers with automatic UTXO selection
- Native ADA transfers
- Single token (CNT) transfers
- Multi-token transfers (multiple tokens in one transaction)
- Address-to-address transfers
- Vault-to-vault transfers for seamless internal operations
- 💰 Fee Estimation: Accurate fee calculation for ADA, single-token, and multi-token transfers
- 🔧 UTxO Management: Consolidate fragmented UTxOs to optimize wallet efficiency
- 🗳️ Governance Operations: Full Conway-era governance support
- Register as a DRep (Delegated Representative)
- Cast votes on governance actions
- Delegate voting power to DReps
- 🏊 Staking Operations: Complete staking lifecycle management
- Register/deregister stake keys
- Delegate to stake pools
- Withdraw rewards
- 📊 Transaction History: Retrieve basic and detailed transaction history with pagination
- 🏊 Pool Information: Query stake pool metadata, delegators, and blocks
- 🔄 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
- Node.js 18+ (for SDK usage)
- Docker & Docker Compose (for API service deployment)
- Fireblocks API credentials
- Iagon API key (required for balance queries, transaction history, and transfers)
Install from GitHub (for use as a dependency in your project):
# Install directly from GitHub
npm install github:fireblocks/cardano-raw-sdk
# Or install a specific branch/tag
npm install github:fireblocks/cardano-raw-sdk#main
npm install github:fireblocks/cardano-raw-sdk#v1.0.0Or clone for development:
git clone https://github.com/fireblocks/cardano-raw-sdk.git
cd cardano-raw-sdk
npm install
npm run buildUse the SDK directly in your TypeScript/JavaScript application.
import { FireblocksCardanoRawSDK } from "cardano-raw-sdk";
import { Networks, SupportedAssets } from "cardano-raw-sdk/types";
import { BasePath } from "@fireblocks/ts-sdk";
// Initialize the SDK
const sdk = await FireblocksCardanoRawSDK.createInstance({
fireblocksConfig: {
apiKey: "your-fireblocks-api-key",
secretKey: "your-fireblocks-secret-key",
basePath: BasePath.US,
},
vaultAccountId: "your-vault-account-id",
network: Networks.MAINNET,
iagonApiKey: "your-iagon-api-key",
});// Get balance by address
const balance = await sdk.getBalanceByAddress({
index: 0,
groupByPolicy: false,
});
console.log("Balance:", balance);
// Get balance by stake key
const stakeBalance = await sdk.getBalanceByStakeKey({
stakeKey: "stake1u8a9qstrmj...",
groupByPolicy: true,
});Transfer ADA without any tokens:
const result = await sdk.transferAda({
index: 0, // Source address index
recipientAddress: "addr1qxy...",
adaAmount: 5000000, // 5 ADA in lovelace
});
console.log("Transaction Hash:", result.txHash);
console.log("Sender Address:", result.senderAddress);
console.log("Fee:", result.fee.ada, "ADA");The SDK supports two transfer modes:
1. Transfer to a Cardano Address
const transferResult = await sdk.transfer({
index: 0,
recipientAddress: "addr1qxy...",
tokenPolicyId: "f0ff48bbb7bbe9d5...",
tokenName: "4e49...",
requiredTokenAmount: 1000000,
});
console.log("Transaction Hash:", transferResult.txHash);
console.log("Sender Address:", transferResult.senderAddress);
console.log("Fee:", transferResult.fee.ada, "ADA"); // e.g., "0.170000 ADA"
console.log("Fee (lovelace):", transferResult.fee.lovelace); // e.g., "170000"2. Vault-to-Vault Transfer
const transferResult = await sdk.transfer({
index: 0, // Source address index
recipientVaultAccountId: "1", // Destination vault account
recipientIndex: 0, // Destination address index (optional, defaults to 0)
tokenPolicyId: "f0ff48bbb7bbe9d5...",
tokenName: "4e49...",
requiredTokenAmount: 1000000,
});
console.log("Transaction Hash:", transferResult.txHash);
console.log("Sender Address:", transferResult.senderAddress);
console.log("Fee:", transferResult.fee.ada, "ADA"); // e.g., "0.170000 ADA"Note: You must provide exactly one of recipientAddress or recipientVaultAccountId, not both.
Transfer multiple different tokens in a single transaction:
const result = await sdk.transferMultipleTokens({
index: 0,
recipientAddress: "addr1qxy...",
tokens: [
{
policyId: "f0ff48bbb7bbe9d5...",
assetName: "4e4654",
amount: 1000000,
},
{
policyId: "a1b2c3d4e5f6...",
assetName: "544f4b454e",
amount: 500000,
},
],
includeAda: true, // Optional: include extra ADA in the transfer
adaAmount: 2000000, // 2 ADA in lovelace (required if includeAda is true)
});
console.log("Transaction Hash:", result.txHash);
console.log("Fee:", result.fee.ada, "ADA");Estimate multi-token transfer fee:
const feeEstimate = await sdk.estimateMultiTokenTransactionFee({
index: 0,
recipientAddress: "addr1qxy...",
tokens: [
{ policyId: "f0ff48bbb...", assetName: "4e4654", amount: 1000000 },
{ policyId: "a1b2c3d4e5f6...", assetName: "544f4b454e", amount: 500000 },
],
});
console.log("Estimated Fee:", feeEstimate.fee.ada, "ADA");Consolidate fragmented UTxOs to optimize your wallet:
const result = await sdk.consolidateUtxos({
index: 0,
targetAddress: "addr1qxy...", // Optional: defaults to source address
});
console.log("Transaction Hash:", result.txHash);
console.log("Consolidated", result.inputCount, "UTxOs");
console.log("Fee:", result.fee.ada, "ADA");// Get basic transaction history
const history = await sdk.getTransactionHistory({
index: 0,
limit: 10,
offset: 0,
fromSlot: 100000,
});
// Get detailed transaction history with inputs/outputs
const detailedHistory = await sdk.getDetailedTxHistory({
index: 0,
limit: 10,
offset: 0,
});
// Get transaction details by hash
const txDetails = await sdk.getTransactionDetails("6c9e6d70a0ce7ca5d...");// Get detailed asset information including metadata and decimals
const assetInfo = await sdk.getAssetInfo(
"f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a", // Policy ID
"4e4654" // Asset name (hex)
);
console.log("Token Name:", assetInfo.data.metadata?.name);
console.log("Ticker:", assetInfo.data.metadata?.ticker);
console.log("Decimals:", assetInfo.data.metadata?.decimals);
console.log("Total Supply:", assetInfo.data.total_supply);
console.log("Logo:", assetInfo.data.metadata?.logo);
// Use decimals to format token amounts correctly
const rawAmount = 1000000;
const decimals = assetInfo.data.metadata?.decimals || 0;
const formattedAmount = rawAmount / Math.pow(10, decimals);
console.log(`Amount: ${formattedAmount} ${assetInfo.data.metadata?.ticker}`);Register as a DRep (Delegated Representative)
const result = await sdk.registerAsDRep({
vaultAccountId: "your-vault-id",
index: 0,
anchor: {
url: "https://example.com/drep-metadata.json",
dataHash: "abc123...", // Blake2b-256 hash of the metadata
},
});
console.log("Transaction Hash:", result.txHash);
console.log("DRep ID:", result.drepId);
// Note: Requires 500 ADA depositCast a Governance Vote (as a DRep)
const result = await sdk.castGovernanceVote({
vaultAccountId: "your-vault-id",
index: 0,
governanceActionTxHash: "abc123...",
governanceActionIndex: 0,
vote: "yes", // "yes", "no", or "abstain"
});
console.log("Transaction Hash:", result.txHash);Delegate Voting Power to a DRep
const result = await sdk.delegateToDRep({
vaultAccountId: "your-vault-id",
index: 0,
drepId: "drep1...", // DRep credential or key hash
});
console.log("Transaction Hash:", result.txHash);// Get pool metadata (name, ticker, description)
const metadata = await sdk.getPoolMetadata("pool1...");
console.log("Pool Name:", metadata.data.name);
console.log("Ticker:", metadata.data.ticker);
// Get pool delegators summary
const delegators = await sdk.getPoolDelegators("pool1...");
console.log("Total Delegators:", delegators.data.total_count);
console.log("Active Stake:", delegators.data.active_stake);
// Get detailed delegator list with pagination
const delegatorList = await sdk.getPoolDelegatorsList("pool1...", 10, 0);
// Get blocks produced by pool
const blocks = await sdk.getPoolBlocks("pool1...");
console.log("Total Blocks:", blocks.data.length);// Get vault account addresses
const addresses = await sdk.getVaultAccountAddresses();
// Get public key (change index, address index)
const publicKey = await sdk.getPublicKey(0, 0);// Clean up resources when done
await sdk.shutdown();Run the SDK as a REST API service using Docker or Node.js directly.
-
Clone the repository:
git clone https://github.com/fireblocks/cardano-raw-sdk.git cd cardano-raw-sdk -
Configure environment variables:
cp .env.example .env # Edit .env with your credentials -
Start the service:
docker-compose up -d
-
Access the API:
- API Base URL:
http://localhost:8000/api - Swagger Documentation:
http://localhost:8000/api-docs - Health Check:
http://localhost:8000/health
- API Base URL:
# Get balance by address
GET /api/balance/address/:vaultAccountId?assetId=ADA&index=0&groupByPolicy=false
# Get balance by credential
GET /api/balance/credential/:vaultAccountId/:credential?groupByPolicy=false
# Get balance by stake key
GET /api/balance/stake-key/:vaultAccountId/:stakeKey?groupByPolicy=falseSetup in Fireblocks Console:
- Go to Fireblocks Console → Settings → Webhooks
- Add webhook URL:
https://your-domain.com/api/webhook - Select events:
TRANSACTION_CREATED,TRANSACTION_STATUS_UPDATED, etc. - Fireblocks will automatically sign webhooks with both JWKS and legacy signatures
Important: Ensure your server's FIREBLOCKS_BASE_PATH environment variable matches your Fireblocks workspace:
- US workspace:
https://api.fireblocks.io(default) - EU workspace:
https://api.eu1.fireblocks.io - EU2 workspace:
https://api.eu2.fireblocks.io - Sandbox:
https://sandbox-api.fireblocks.io
Endpoint:
# Enrich Fireblocks webhook with automatic signature verification
POST /api/webhook
Content-Type: application/json
Headers:
- fireblocks-webhook-signature: <JWT signature> (added by Fireblocks)
- fireblocks-signature: <Legacy signature> (added by Fireblocks)
Body:
{
"eventType": "transaction.created",
"data": { ... }
}
# Response (enriched with CNT data):
{
"eventType": "transaction.created",
"data": {
...
"cardanoTokensData": {
"tx_hash": "...",
"inputs": [...],
"outputs": [...]
}
}
}Security: The endpoint automatically verifies webhook signatures using:
- JWKS (modern, automatic key rotation) - tries first
- Legacy RSA-SHA512 (static keys) - fallback
- Verification environment is automatically determined from
FIREBLOCKS_BASE_PATHconfig
Invalid signatures are rejected with 401 error.
# Get transaction details by hash
GET /api/tx/hash/:hash
# Get asset information (metadata, decimals, supply)
GET /api/assets/:policyId/:assetName
# Get transaction history
GET /api/tx/history/:vaultAccountId?index=0&limit=10&offset=0&fromSlot=100000
# Get detailed transaction history
GET /api/tx/address/:vaultAccountId?index=0&limit=10&offset=0ADA Transfers
# Transfer native ADA
POST /api/transfers/ada
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"recipientAddress": "addr1qxy...",
"adaAmount": 5000000,
"index": 0
}
# Estimate ADA transfer fee
POST /api/fee-estimate/ada
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"recipientAddress": "addr1qxy...",
"adaAmount": 5000000,
"index": 0
}Single Token (CNT) Transfers
# Execute transfer (to address)
POST /api/transfers
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"recipientAddress": "addr1qxy...",
"tokenPolicyId": "f0ff48bbb7bbe9d5...",
"tokenName": "4e49...",
"requiredTokenAmount": 1000000,
"index": 0
}
# Response:
{
"txHash": "a1b2c3d4e5f6...",
"senderAddress": "addr1qxy...",
"tokenName": "4e49...",
"fee": {
"lovelace": "170000",
"ada": "0.170000"
}
}
# Execute transfer (vault-to-vault)
POST /api/transfers
Content-Type: application/json
{
"vaultAccountId": "0",
"recipientVaultAccountId": "1",
"recipientIndex": 0,
"tokenPolicyId": "f0ff48bbb7bbe9d5...",
"tokenName": "4e49...",
"requiredTokenAmount": 1000000,
"index": 0
}Multi-Token Transfers
# Transfer multiple tokens in one transaction
POST /api/transfers/tokens
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"recipientAddress": "addr1qxy...",
"tokens": [
{
"policyId": "f0ff48bbb7bbe9d5...",
"assetName": "4e4654",
"amount": 1000000
},
{
"policyId": "a1b2c3d4e5f6...",
"assetName": "544f4b454e",
"amount": 500000
}
],
"includeAda": true,
"adaAmount": 2000000,
"index": 0
}
# Estimate multi-token transfer fee
POST /api/fee-estimate/tokens
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"recipientAddress": "addr1qxy...",
"tokens": [
{ "policyId": "f0ff48bbb...", "assetName": "4e4654", "amount": 1000000 }
],
"index": 0
}UTxO Consolidation
# Consolidate fragmented UTxOs
POST /api/utxos/consolidate
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"index": 0,
"targetAddress": "addr1qxy..." // Optional
}
# Response:
{
"txHash": "a1b2c3d4e5f6...",
"inputCount": 15,
"fee": {
"lovelace": "200000",
"ada": "0.200000"
}
}# Register as a DRep (requires 500 ADA deposit)
POST /api/governance/register-drep
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"index": 0,
"anchor": {
"url": "https://example.com/drep-metadata.json",
"dataHash": "abc123..."
}
}
# Cast a governance vote (as a DRep)
POST /api/governance/vote
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"index": 0,
"governanceActionTxHash": "abc123...",
"governanceActionIndex": 0,
"vote": "yes"
}
# Delegate voting power to a DRep
POST /api/governance/delegate-drep
Content-Type: application/json
{
"vaultAccountId": "your-vault-id",
"index": 0,
"drepId": "drep1..."
}# Get pool metadata (name, ticker, description)
GET /api/pools/:poolId/metadata
# Get pool delegators summary
GET /api/pools/:poolId/delegators?limit=10&offset=0
# Get detailed delegator list
GET /api/pools/:poolId/delegators/list?limit=10&offset=0
# Get blocks produced by pool
GET /api/pools/:poolId/blocks# Get balance
curl http://localhost:8000/api/balance/address/vault-123?assetId=ADA&index=0
# Get transaction history
curl http://localhost:8000/api/tx/history/vault-123?limit=5
# Get asset information
curl http://localhost:8000/api/assets/f0ff48bbb7bbe9d5.../4e4654
# Transfer native ADA
curl -X POST http://localhost:8000/api/transfers/ada \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "vault-123",
"recipientAddress": "addr1qxy...",
"adaAmount": 5000000,
"index": 0
}'
# Estimate ADA transfer fee
curl -X POST http://localhost:8000/api/fee-estimate/ada \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "vault-123",
"recipientAddress": "addr1qxy...",
"adaAmount": 5000000,
"index": 0
}'
# Execute CNT transfer (to address)
curl -X POST http://localhost:8000/api/transfers \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "vault-123",
"recipientAddress": "addr1qxy...",
"tokenPolicyId": "f0ff48bbb...",
"tokenName": "4e49...",
"requiredTokenAmount": 1000000
}'
# Response: {"txHash":"a1b2c3d4...","senderAddress":"addr1qxy...","tokenName":"4e49...","fee":{"lovelace":"170000","ada":"0.170000"}}
# Execute CNT transfer (vault-to-vault)
curl -X POST http://localhost:8000/api/transfers \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "0",
"recipientVaultAccountId": "1",
"tokenPolicyId": "f0ff48bbb...",
"tokenName": "4e49...",
"requiredTokenAmount": 1000000
}'
# Transfer multiple tokens
curl -X POST http://localhost:8000/api/transfers/tokens \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "vault-123",
"recipientAddress": "addr1qxy...",
"tokens": [
{"policyId": "f0ff48bbb...", "assetName": "4e4654", "amount": 1000000},
{"policyId": "a1b2c3d4e5f6...", "assetName": "544f4b454e", "amount": 500000}
],
"includeAda": true,
"adaAmount": 2000000
}'
# Consolidate UTxOs
curl -X POST http://localhost:8000/api/utxos/consolidate \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "vault-123",
"index": 0
}'
# Register as DRep
curl -X POST http://localhost:8000/api/governance/register-drep \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "vault-123",
"index": 0,
"anchor": {
"url": "https://example.com/drep-metadata.json",
"dataHash": "abc123..."
}
}'
# Cast governance vote
curl -X POST http://localhost:8000/api/governance/vote \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "vault-123",
"index": 0,
"governanceActionTxHash": "abc123...",
"governanceActionIndex": 0,
"vote": "yes"
}'
# Delegate to DRep
curl -X POST http://localhost:8000/api/governance/delegate-drep \
-H "Content-Type: application/json" \
-d '{
"vaultAccountId": "vault-123",
"index": 0,
"drepId": "drep1..."
}'
# Get pool metadata
curl http://localhost:8000/api/pools/pool1.../metadata
# Get pool delegators
curl http://localhost:8000/api/pools/pool1.../delegators?limit=10
# Get pool blocks
curl http://localhost:8000/api/pools/pool1.../blocksCopy .env.example to .env and fill in your values:
cp .env.example .env| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 8000 |
HTTP server port |
NODE_ENV |
No | production |
Set to development only when using self-signed certs locally |
FIREBLOCKS_API_USER_KEY |
Yes | - | Fireblocks API key UUID |
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 |
CARDANO_NETWORK |
No | mainnet |
mainnet or preprod |
Fireblocks base path options:
- US (default):
https://api.fireblocks.io
Store your Fireblocks secret key in a secure file:
# Create a secure directory
mkdir -p ~/.fireblocks
# Save your secret key
echo "YOUR_FIREBLOCKS_SECRET_KEY" > ~/.fireblocks/secret.key
# Set proper permissions
chmod 600 ~/.fireblocks/secret.key
# Update .env
FIREBLOCKS_API_USER_SECRET_KEY_PATH=/home/user/.fireblocks/secret.keyWhen running the API service, interactive API documentation is available at:
http://localhost:8000/api-docs
Generate TypeScript documentation:
npm run docsView the generated documentation by opening docs/index.html in your browser.
import { FireblocksCardanoRawSDK } from "cardano-raw-sdk";
import { Networks, SupportedAssets } from "cardano-raw-sdk/types";
import { BasePath } from "@fireblocks/ts-sdk";
async function transferTokens() {
const sdk = await FireblocksCardanoRawSDK.createInstance({
fireblocksConfig: {
apiKey: process.env.FIREBLOCKS_API_KEY!,
secretKey: process.env.FIREBLOCKS_SECRET_KEY!,
basePath: BasePath.US,
},
vaultAccountId: "vault-123",
network: Networks.MAINNET,
iagonApiKey: process.env.IAGON_API_KEY!,
});
try {
const result = await sdk.transfer({
recipientAddress: "addr1qxy...",
tokenPolicyId: "f0ff48bbb7bbe9d5...",
tokenName: "4e49...",
requiredTokenAmount: 1000000,
});
console.log("Transfer successful!");
console.log("Transaction Hash:", result.txHash);
console.log("Sender Address:", result.senderAddress);
console.log("Transaction Fee:", result.fee.ada, "ADA");
console.log("View on Cardanoscan:", `https://cardanoscan.io/transaction/${result.txHash}`);
} catch (error) {
console.error("Transfer failed:", error);
} finally {
await sdk.shutdown();
}
}
transferTokens();import { SdkManager } from "cardano-raw-sdk/pool";
import { FireblocksCardanoRawSDK } from "cardano-raw-sdk";
import { Networks } from "cardano-raw-sdk/types";
import { BasePath } from "@fireblocks/ts-sdk";
const manager = new SdkManager(
{
apiKey: process.env.FIREBLOCKS_API_KEY!,
secretKey: process.env.FIREBLOCKS_SECRET_KEY!,
basePath: BasePath.US,
},
Networks.MAINNET,
{
maxPoolSize: 50,
idleTimeoutMs: 20 * 60 * 1000,
},
async (vaultAccountId, fireblocksConfig, network) =>
FireblocksCardanoRawSDK.createInstance({
fireblocksConfig,
vaultAccountId,
network,
})
);
// Get SDK for a vault account (automatically pooled)
const sdk1 = await manager.getSdk("vault-123");
const balance1 = await sdk1.getBalanceByAddress({ index: 0 });
// Reuses the same SDK instance
const sdk2 = await manager.getSdk("vault-123");
const balance2 = await sdk2.getBalanceByAddress({ index: 0 });
// Release SDK back to pool when done
manager.releaseSdk("vault-123");
// Get pool metrics
const metrics = manager.getMetrics();
console.log("Pool Metrics:", metrics);
// Shutdown when done
await manager.shutdown();const axios = require("axios");
const API_BASE_URL = "http://localhost:8000/api";
async function getBalanceAndTransfer() {
try {
// Get balance
const balanceResponse = await axios.get(`${API_BASE_URL}/balance/address/vault-123`, {
params: {
assetId: "ADA",
index: 0,
groupByPolicy: true,
},
});
console.log("Balance:", balanceResponse.data);
// Execute transfer
const transferResponse = await axios.post(`${API_BASE_URL}/transfers`, {
vaultAccountId: "vault-123",
recipientAddress: "addr1qxy...",
tokenPolicyId: "f0ff48bbb7bbe9d5...",
tokenName: "4e49...",
requiredTokenAmount: 1000000,
});
console.log("Transfer Result:", transferResponse.data);
console.log("Transaction Hash:", transferResponse.data.txHash);
console.log("Fee:", transferResponse.data.fee.ada, "ADA");
} catch (error) {
console.error("Error:", error.response?.data || error.message);
}
}
getBalanceAndTransfer();import { FireblocksCardanoRawSDK } from "cardano-raw-sdk";
import { Networks, SupportedAssets } from "cardano-raw-sdk/types";
import { BasePath } from "@fireblocks/ts-sdk";
async function demonstrateCaching() {
const sdk = await FireblocksCardanoRawSDK.createInstance({
fireblocksConfig: {
apiKey: process.env.FIREBLOCKS_API_KEY!,
secretKey: process.env.FIREBLOCKS_SECRET_KEY!,
basePath: BasePath.US,
},
vaultAccountId: "vault-123",
network: Networks.MAINNET,
iagonApiKey: process.env.IAGON_API_KEY!,
});
// Check cache statistics
const stats = sdk.getCacheStats();
console.log(`Cache stats: ${stats.addressCount} addresses, ${stats.publicKeyCount} public keys`);
// Multiple operations benefit from caching
const balance1 = await sdk.getBalanceByAddress({ index: 0 });
const balance2 = await sdk.getBalanceByAddress({ index: 0 });
// Second balance check uses cached address - no Fireblocks API call!
// Clear cache if needed
sdk.clearCache();
await sdk.shutdown();
}
demonstrateCaching();import { FireblocksCardanoRawSDK } from "cardano-raw-sdk";
import { Networks } from "cardano-raw-sdk/types";
import { BasePath } from "@fireblocks/ts-sdk";
async function transferAda() {
const sdk = await FireblocksCardanoRawSDK.createInstance({
fireblocksConfig: {
apiKey: process.env.FIREBLOCKS_API_KEY!,
secretKey: process.env.FIREBLOCKS_SECRET_KEY!,
basePath: BasePath.US,
},
vaultAccountId: "vault-123",
network: Networks.MAINNET,
iagonApiKey: process.env.IAGON_API_KEY!,
});
try {
// Estimate fee first
const feeEstimate = await sdk.estimateAdaTransactionFee({
index: 0,
recipientAddress: "addr1qxy...",
adaAmount: 5000000, // 5 ADA
});
console.log("Estimated Fee:", feeEstimate.fee.ada, "ADA");
// Execute transfer
const result = await sdk.transferAda({
index: 0,
recipientAddress: "addr1qxy...",
adaAmount: 5000000,
});
console.log("ADA Transfer successful!");
console.log("Transaction Hash:", result.txHash);
console.log("Actual Fee:", result.fee.ada, "ADA");
} catch (error) {
console.error("Transfer failed:", error);
} finally {
await sdk.shutdown();
}
}
transferAda();import { FireblocksCardanoRawSDK } from "cardano-raw-sdk";
import { Networks } from "cardano-raw-sdk/types";
import { BasePath } from "@fireblocks/ts-sdk";
async function transferMultipleTokens() {
const sdk = await FireblocksCardanoRawSDK.createInstance({
fireblocksConfig: {
apiKey: process.env.FIREBLOCKS_API_KEY!,
secretKey: process.env.FIREBLOCKS_SECRET_KEY!,
basePath: BasePath.US,
},
vaultAccountId: "vault-123",
network: Networks.MAINNET,
iagonApiKey: process.env.IAGON_API_KEY!,
});
try {
const result = await sdk.transferMultipleTokens({
index: 0,
recipientAddress: "addr1qxy...",
tokens: [
{
policyId: "f0ff48bbb7bbe9d5...",
assetName: "4e4654",
amount: 1000000,
},
{
policyId: "a1b2c3d4e5f6...",
assetName: "544f4b454e",
amount: 500000,
},
],
includeAda: true,
adaAmount: 2000000, // Also send 2 ADA
});
console.log("Multi-token transfer successful!");
console.log("Transaction Hash:", result.txHash);
console.log("Fee:", result.fee.ada, "ADA");
} catch (error) {
console.error("Transfer failed:", error);
} finally {
await sdk.shutdown();
}
}
transferMultipleTokens();import { FireblocksCardanoRawSDK } from "cardano-raw-sdk";
import { Networks } from "cardano-raw-sdk/types";
import { BasePath } from "@fireblocks/ts-sdk";
async function consolidateUtxos() {
const sdk = await FireblocksCardanoRawSDK.createInstance({
fireblocksConfig: {
apiKey: process.env.FIREBLOCKS_API_KEY!,
secretKey: process.env.FIREBLOCKS_SECRET_KEY!,
basePath: BasePath.US,
},
vaultAccountId: "vault-123",
network: Networks.MAINNET,
iagonApiKey: process.env.IAGON_API_KEY!,
});
try {
const result = await sdk.consolidateUtxos({
index: 0,
// targetAddress: "addr1qxy..." // Optional: defaults to source address
});
console.log("UTxO consolidation successful!");
console.log("Transaction Hash:", result.txHash);
console.log("Consolidated", result.inputCount, "UTxOs");
console.log("Fee:", result.fee.ada, "ADA");
} catch (error) {
console.error("Consolidation failed:", error);
} finally {
await sdk.shutdown();
}
}
consolidateUtxos();import { FireblocksCardanoRawSDK } from "cardano-raw-sdk";
import { Networks } from "cardano-raw-sdk/types";
import { BasePath } from "@fireblocks/ts-sdk";
async function participateInGovernance() {
const sdk = await FireblocksCardanoRawSDK.createInstance({
fireblocksConfig: {
apiKey: process.env.FIREBLOCKS_API_KEY!,
secretKey: process.env.FIREBLOCKS_SECRET_KEY!,
basePath: BasePath.US,
},
vaultAccountId: "vault-123",
network: Networks.MAINNET,
iagonApiKey: process.env.IAGON_API_KEY!,
});
try {
// Register as a DRep (requires 500 ADA deposit)
const drepResult = await sdk.registerAsDRep({
vaultAccountId: "vault-123",
index: 0,
anchor: {
url: "https://example.com/drep-metadata.json",
dataHash: "abc123...",
},
});
console.log("Registered as DRep:", drepResult.drepId);
console.log("Transaction Hash:", drepResult.txHash);
// Cast a vote on a governance action
const voteResult = await sdk.castGovernanceVote({
vaultAccountId: "vault-123",
index: 0,
governanceActionTxHash: "abc123...",
governanceActionIndex: 0,
vote: "yes", // or "no" or "abstain"
});
console.log("Vote cast! TX Hash:", voteResult.txHash);
// Delegate voting power to another DRep
const delegateResult = await sdk.delegateToDRep({
vaultAccountId: "vault-123",
index: 0,
drepId: "drep1...",
});
console.log("Delegated to DRep! TX Hash:", delegateResult.txHash);
} catch (error) {
console.error("Governance operation failed:", error);
} finally {
await sdk.shutdown();
}
}
participateInGovernance();# Install dependencies
npm install
# Build the project
npm run build
# Run in development mode with hot reload
npm run dev
# Generate documentation
npm run docscardano-raw-sdk/
├── src/
│ ├── FireblocksCardanoRawSDK.ts # Main SDK class
│ ├── pool/
│ │ └── sdkManager.ts # Connection pooling manager
│ ├── services/
│ │ ├── fireblocks.service.ts # Fireblocks integration
│ │ └── iagon.api.service.ts # Iagon API client
│ ├── api/
│ │ ├── router.ts # Express routes
│ │ └── controllers/
│ │ └── controller.ts # API controllers
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ └── server.ts # Express server setup
├── docs/ # Generated documentation
├── Dockerfile # Docker configuration
├── docker-compose.yml # Docker Compose setup
├── package.json
├── tsconfig.json
└── README.md
docker build -t cardano-raw-sdk:latest .# Start services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down
# Restart services
docker-compose restartversion: "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: 3The SDK implements automatic caching to minimize Fireblocks API calls:
- Address Caching: Vault account addresses are cached by index
- Public Key Caching: Public keys are cached per asset/change/address combination
- Automatic Cache Management: Caches are cleared on SDK shutdown
- Manual Cache Control: Use
clearCache()andgetCacheStats()methods
// Check cache statistics
const stats = sdk.getCacheStats();
console.log("Cached addresses:", stats.addressCount);
console.log("Cached public keys:", stats.publicKeyCount);
// Clear cache manually if needed
sdk.clearCache();Benefits:
- Reduces API calls to Fireblocks by up to 90%
- Faster response times for repeated operations
- Lower costs and improved rate limit compliance
- Automatic per-vault-account isolation via SDK pooling
The SDK Manager implements intelligent connection pooling:
- LRU Eviction: Automatically removes least-recently-used idle connections
- Automatic Cleanup: Periodically removes idle connections
- Per-Vault Instances: Each vault account gets its own SDK instance
- Metrics: Track pool usage and performance
try {
const result = await sdk.transfer({ ... });
} catch (error) {
if (error.code === 'INSUFFICIENT_BALANCE') {
console.error('Insufficient balance:', error.details);
} else if (error instanceof SdkApiError) {
console.error('API Error:', error.statusCode, error.message);
} else {
console.error('Unexpected error:', error);
}
}- Mainnet: Production Cardano network (use
Networks.MAINNET) - Preprod: Pre-production testnet (use
Networks.PREPROD) - Preview: Preview testnet (not currently supported)
The SDK provides additional methods for advanced use cases:
// Access internal services for advanced operations
const fireblocksService = sdk.getFireblocksService();
const iagonApiService = sdk.getIagonApiService();
// Cache management
sdk.clearCache(); // Clear all cached addresses and public keys
const stats = sdk.getCacheStats(); // Get cache statistics
// Graceful shutdown
await sdk.shutdown(); // Clean up resources and clear cache-
"SDK factory not initialized" error
- Ensure you're passing the SDK factory function to SdkManager constructor
- Check that FireblocksCardanoRawSDK.createInstance is properly imported
-
"Insufficient balance" error
- Ensure sufficient ADA for transaction fees (minimum ~1.2 ADA)
- Verify token balance is sufficient for the transfer amount
-
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
⚠️ Never commit.envfiles or secret keys to version control⚠️ Store Fireblocks secret keys in secure locations with restricted permissions⚠️ Enable Fireblocks transaction approval policies for production