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 |
- Fireblocks workspace with raw signing enabled.
- Fireblocks API key and secret key file.
- Node.js v18+
- Docker and Docker Compose (for REST API server mode).
- Secure Stacks Transactions: All transactions are Fireblocks-signed and submitted to Stacks.
- Fireblocks raw signing support
- Native STX transfers: Send STX with optional gross transactions (fee deduction from recipient)
- Fungible token transfers: Support for SIP-010 token transfers (sBTC, USDC, etc.)
- Stacking functionality:
- Solo stacking
- Pool delegation and stacking
- Delegation management (delegate, revoke, allow contract caller)
- Account status and eligibility checking
- Transaction monitoring: Real-time transaction status polling with error code mapping
- REST API mode: Easily integrate through HTTP requests.
- Vault pooling: Efficient per-vault instance management.
Install the package in your project:
npm install stacks-fireblocks-sdkImport 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 ModeAPI 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 .envEdit .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 productionEnvironment 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 |
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=3000Note: 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.
- Place your Fireblocks private key at:
./secrets/fireblocks_secret.key
- Your
.envshould reference this file relative to the project root:
FIREBLOCKS_SECRET_KEY_PATH=./secrets/fireblocks_secret.key- Docker Compose mounts this file automatically:
volumes:
- ./secrets/fireblocks_secret.key:/app/secrets/fireblocks_secret.key:roimport { 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:
- Function: "stack-stx"
- Max Amount: Maximum STX amount to authorize, equal or more to what you'll stack
- Lock period: Number of cycles (1-12)
- Auth ID: Random integer for replay protection, must be the same one used to generate the signature
- Reward cycle: Current reward cycle
- PoX address: Your BTC rewards address
- If you plan to run your own signer to earn full rewards, use your signer's public key here
- If using a hosted signer service, use their public key and signature
// 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 |
- * IMPORTANT NOTE **: Transactions could sometimes pass at blockchain level but fail at smart contract level,
in this case a {success: true, txid: } 200 response will be returned to user, please double check
the success of the transaction by polling the txid status with the
/api/:vaultId/transactions/:txIdendpoint.
- Minimum Amount: Must meet the dynamic minimum threshold (request will fail otherwise)
- Lock Period: 1-12 reward cycles (each cycle β 2 weeks)
- No Active Delegation: Account must not be delegated to an address
- Timing: Submit during reward phase (with more than 10 blocks away from prepare phase)
- Each cycle is approximately 2,100 Bitcoin blocks (~2 weeks)
- Reward Phase: ~2,000 blocks - safe to submit stacking requests
- Prepare Phase: ~100 blocks - risky window before next cycle
- SDK automatically checks timing safety before stacking
- Rewards are paid directly to your BTC address each cycle
- Amount:
Expected β(Your STX / Total Stacked) Γ Total BTC from Miners
Pool 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:
- β Keep all rewards (no commission)
- β Full control over reward address
- β Higher rewards for large holders
- β Must meet higher minimum threshold (typically 90,000+ STX)
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 devnpm testnpm run buildSwagger UI API Documentation will be available at http://localhost:3000/api-docs after running the project.
- Never commit your
.envor secrets. - Use secrets management in production.
- Fireblocks raw signing provides secure transaction signing without exposing private keys.
- All transactions are signed within Fireblocks secure infrastructure.
- Network: Stacks Mainnet
- API:
https://api.hiro.so - PoX Contract:
SP000000000000000000002Q6VF78.pox-4
- Network: Stacks Testnet
- API:
https://api.testnet.hiro.so - PoX Contract:
ST000000000000000000002AMW42H.pox-4