A stateless SDK and REST API server for interacting with Fireblocks and the Stacks Network, enabling secure operations on Stacks using Fireblocks services.
The SDK Typedocs can be found here: https://fireblocks.github.io/stacks-fireblocks-sdk/
Stacks Fireblocks SDK lets you securely execute Stacks transactions using Fireblocks vaults and raw signing. It's designed to simplify integration with Fireblocks for secure Stacks transactions.
| Mode | Use Case | How |
|---|---|---|
| TypeScript SDK | Import into your Node.js application | import { StacksSDK } from "stacks-fireblocks-sdk" |
| REST API Server | Dockerized service for non-TS environments | docker-compose up or node dist/server.js |
Install the package in your project:
npm install stacks-fireblocks-sdk
Import and use in your code:
import { StacksSDK, FireblocksConfig } from "stacks-fireblocks-sdk";
const config: FireblocksConfig = {
apiKey: process.env.FIREBLOCKS_API_KEY!,
apiSecret: fs.readFileSync(process.env.FIREBLOCKS_SECRET_KEY_PATH!, "utf8"),
testnet: true,
};
const sdk = await StacksSDK.create("YOUR_VAULT_ID", config);
Note: Importing the SDK does NOT start a server. The SDK is a pure library.
For non-TypeScript environments, run the SDK as a dockerized REST API service:
git clone https://github.com/fireblocks/stacks-fireblocks-sdk
cd stacks-fireblocks-sdk
cp .env.example .env
# Make sure your Fireblocks secret key is in ./secrets/fireblocks_secret.key
docker-compose up --build # Dev Mode
docker-compose -f docker-compose.yml up --build # Prod Mode
API will run on port
3000by default. Change viaPORTin.env.
git clone https://github.com/fireblocks/stacks-fireblocks-sdk
cd stacks-fireblocks-sdk
npm install
cp .env.example .env
Edit .env to include your API key, private key path, and Stacks network config.
npm run dev # Start REST API server with hot reload
npm run build # Build for production
Environment variables (via .env) control SDK behavior:
| Variable | Required | Default | Description |
|---|---|---|---|
| FIREBLOCKS_API_KEY | Yes | — | Your Fireblocks API key |
| FIREBLOCKS_SECRET_KEY_PATH | Yes | — | Path to your Fireblocks secret key file |
| FIREBLOCKS_BASE_PATH | No | BasePath.US from "@fireblocks/ts-sdk" | Base URL of the Fireblocks API |
| NETWORK | No | MAINNET | Stacks mainnet or testnet |
| PORT | No | 3000 | Port to run the REST API server |
.env:FIREBLOCKS_BASE_PATH=https://api.fireblocks.io/v1
FIREBLOCKS_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
FIREBLOCKS_SECRET_KEY_PATH=./secrets/fireblocks_secret.key
STACKS_NETWORK=TESTNET
PORT=3000
Note: Setting STACKS_NETWORK to anything other than TESTNET (or testnet) will set the network as mainnet.
🔒 Never commit your
.envfile or secret key to source control.
./secrets/fireblocks_secret.key
.env should reference this file relative to the project root:FIREBLOCKS_SECRET_KEY_PATH=./secrets/fireblocks_secret.key
volumes:
- ./secrets/fireblocks_secret.key:/app/secrets/fireblocks_secret.key:ro
import { StacksSDK, FireblocksConfig } from "stacks-fireblocks-sdk";
import fs from "fs";
const fireblocksConfig: FireblocksConfig = {
apiKey: process.env.FIREBLOCKS_API_KEY!,
apiSecret: fs.readFileSync(process.env.FIREBLOCKS_SECRET_KEY_PATH!, "utf8"),
testnet: true, // or false for mainnet
};
const sdk = await StacksSDK.create("YOUR_VAULT_ID", fireblocksConfig);
// Get Stacks address
const address = sdk.getAddress();
console.log("Stacks Address:", address);
// Get public key
const publicKey = sdk.getPublicKey();
console.log("Public Key:", publicKey);
// Get BTC rewards address (for stacking)
const btcAddress = sdk.getBtcRewardsAddress();
console.log("BTC Rewards Address:", btcAddress);
// Get native STX balance
const balanceResponse = await sdk.getBalance();
if (balanceResponse.success) {
console.log("STX Balance:", balanceResponse.balance);
}
// Get fungible token balances
const ftBalances = await sdk.getFtBalances();
if (ftBalances.success) {
ftBalances.data?.forEach((token) => {
console.log(`${token.token}: ${token.balance}`);
});
}
// Basic STX transfer
const transferResponse = await sdk.createNativeTransaction(
"ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG", // recipient
10.5, // amount in STX
false, // grossTransaction (if true, fee is deducted from amount)
"Payment for services", // optional note
);
if (transferResponse.success) {
console.log("Transaction Hash:", transferResponse.txHash);
}
// Gross transaction (fee deducted from recipient)
const grossTransfer = await sdk.createNativeTransaction(
"ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG",
10.5,
true, // fee will be deducted from the 10.5 STX
);
import { TokenType } from "stacks-fireblocks-sdk";
// Transfer sBTC (built-in token)
const ftTransfer = await sdk.createFTTransaction(
"ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG",
0.1, // amount in token units
TokenType.sBTC,
);
if (ftTransfer.success) {
console.log("Transaction Hash:", ftTransfer.txHash);
}
// Transfer custom SIP-010 token
// Note: tokenAssetName is the name from define-fungible-token (may differ from contract name)
const customTransfer = await sdk.createFTTransaction(
"ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG",
100, // amount in token units
TokenType.CUSTOM,
"SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9", // contract address
"my-token", // contract name
"my-token-asset", // asset name (from define-fungible-token)
);
Finding the asset name: For custom tokens, the
tokenAssetNameis found in the contract's source code in thedefine-fungible-tokendeclaration. View the contract on a block explorer (e.g., explorer.hiro.so) and look for(define-fungible-token <asset-name>). This may differ from the contract name - for example, USDCx contract (usdcx) defines its token asusdcx-token.
const status = await sdk.checkStatus();
if (status.success) {
console.log("Balance Information:");
console.log(" Total STX:", status.data?.balance.stx_total);
console.log(" Locked STX:", status.data?.balance.stx_locked);
console.log(" Unlock Height:", status.data?.balance.burnchain_unlock_height);
console.log("\nDelegation Status:");
console.log(" Is Delegated:", status.data?.delegation.is_delegated);
console.log(" Delegated To:", status.data?.delegation.delegated_to);
console.log(" Amount:", status.data?.delegation.amount_delegated);
}
Solo stacking requires you to provide a signer key and signature. You can use any valid secp256k1 key pair for your signer.
Generate signer signature: Use the Stacks Signature Generation Tool to generate your signer signature with the following parameters:
// Stack 150,000 STX for 6 cycles
const stackResponse = await sdk.stackSolo(
"02778d476704afa...", // Signer public key
"1997445c32fc172f...", // Signer signature
150000, // amount in STX
6, // lock period in cycles (1-12)
"1772114443795", // authId (same as used to generate signature)
);
if (stackResponse.success) {
console.log("Stacking Transaction Hash:", stackResponse.txHash);
console.log("BTC rewards will be sent to:", sdk.getBtcRewardsAddress());
} else {
console.error("Stacking failed:", stackResponse.error);
}
// Delegate to a stacking pool
const delegateResponse = await sdk.delegateToPool(
"SP21YTSM60CAY6D011EZVEVNKXVW8FVZE198XEFFP", // pool address
"stacking-pool-v1", // pool contract name
50000, // amount to delegate
12, // lock period in cycles
);
// Allow a pool to lock your STX
const allowCallerResponse = await sdk.allowContractCaller(
"SP21YTSM60CAY6D011EZVEVNKXVW8FVZE198XEFFP",
"stacking-pool-v1",
);
// Revoke delegation
const revokeResponse = await sdk.revokeDelegation();
// Get transaction status with error code mapping
const txStatus = await sdk.getTxStatusById("0xabcd1234...");
if (txStatus.success) {
console.log("Status:", txStatus.data?.tx_status);
if (txStatus.data?.tx_status !== "success") {
console.log("Error:", txStatus.data?.tx_error);
console.log("Error Code:", txStatus.data?.tx_result?.repr);
}
}
// Get transaction history (cached)
const history = await sdk.getTransactionHistory(true);
// Get fresh transaction history with pagination
const freshHistory = await sdk.getTransactionHistory(
false, // don't use cache
50, // limit
0, // offset
);
history.forEach((tx) => {
console.log(`${tx.transaction_hash}: ${tx.tx_type} - ${tx.tx_status}`);
});
| Method | Route | Description |
|---|---|---|
| GET | /api/:vaultId/address |
Fetch the Stacks address associated with the given vault |
| GET | /api/:vaultId/publicKey |
Retrieve the public key for the vault account |
| GET | /api/:vaultId/btc-rewards-address |
Get the BTC rewards address associated with the given vault (for stacking) |
| Method | Route | Description |
|---|---|---|
| GET | /api/:vaultId/balance |
Get the native STX balance |
| GET | /api/:vaultId/ft-balances |
Get all fungible token balances for the vault |
| Method | Route | Description |
|---|---|---|
| GET | /api/:vaultId/transactions |
List recent transactions for this vault |
| GET | /api/transactions/:txId |
Get detailed transaction status with error code mapping |
| POST | /api/:vaultId/transfer |
Transfer STX or Fungible Tokens to another address |
| Method | Route | Description |
|---|---|---|
| GET | /api/:vaultId/check-status |
Check account stacking status and delegation info |
| GET | /api/poxInfo |
Fetch current PoX info from blockchain |
| POST | /api/:vaultId/stacking/solo |
Solo stack STX with automatic signer signature |
| POST | /api/:vaultId/stacking/pool/delegate |
Delegate amonunt of STX to a stacking pool |
| POST | /api/:vaultId/stacking/pool/allow-contract-caller |
Allow a pool contract to lock your STX |
| POST | /api/:vaultId/revoke-delegation |
Revoke any active STX delegation |
| Method | Route | Description |
|---|---|---|
| GET | /api/metrics |
Prometheus-compatible service metrics |
/api/:vaultId/transactions/:txId endpoint.Expected ≈(Your STX / Total Stacked) × Total BTC from MinersPool Stacking:
✅ Lower minimum (pool operators set their own minimum)
✅ No signer infrastructure required
✅ Pool handles all technical operations
❌ Pool takes a commission
❌ Less control over reward address
Note: For pool stacking, delegate the amount you want to stack to the pool and allow the pool contract as contract-caller to lock your STX, the pool will handle the rest and lock STX when ready and distirbute rewards at the end of locking period.
Solo Stacking:
curl -X 'GET' \
'http://localhost:3000/api/123/address' \
-H 'accept: application/json'
curl -X 'GET' \
'http://localhost:3000/api/123/balance' \
-H 'accept: application/json'
curl -X 'POST' \
'http://localhost:3000/api/123/transfer' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"recipientAddress": "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG",
"amount": 100.5,
"assetType": "STX",
"grossTransaction": false,
"note": "Payment for services"
}'
curl -X 'POST' \
'http://localhost:3000/api/123/transfer' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"recipientAddress": "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG",
"amount": 100,
"assetType": "Custom",
"tokenContractAddress": "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9",
"tokenContractName": "my-token",
"tokenAssetName": "my-token-asset"
}'
Note:
tokenAssetNameis the name from the contract'sdefine-fungible-tokendeclaration, which may differ fromtokenContractName.
curl -X 'POST' \
'http://localhost:3000/api/123/stacking/solo' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"signerKey": "02778d476704afa540ac01438f62c371dc387",
"signerSig65Hex": "1997445c32fc1720b202995f656396b50c355",
"amount": 6520000,
"lockPeriod": 1,
"authId": "1"
}'
curl -X 'GET' \
'http://localhost:3000/api/123/status' \
-H 'accept: application/json'
curl -X 'GET' \
'http://localhost:3000/api/123/tx/0xabcd1234...' \
-H 'accept: application/json'
npm run dev
npm test
npm run build
Swagger UI API Documentation will be available at http://localhost:3000/api-docs after running the project.
.env or secrets.https://api.hiro.soSP000000000000000000002Q6VF78.pox-4https://api.testnet.hiro.soST000000000000000000002AMW42H.pox-4