Expand description
§Overview
Implementation of a Solana Signer using Fireblocks as backend signer
§Prerequisites
A fireblocks account with API key. See developer portal and sign up for a sandbox account
§Installation
Add this to your Cargo.toml:
[dependencies]
fireblocks-solana-signer = "1"Or install via cargo:
cargo add fireblocks-solana-signer@1§TLDR
use {
fireblocks_solana_sdk::signature::FireblocksSigner,
solana_sdk::message::Message,
solana_client::rpc_client::{RpcClient, SerializableTransaction},
solana_sdk::instruction::Instruction,
solana_sdk::transaction::Transaction,
};
fn memo(message: &str) -> Instruction {
Instruction {
program_id: spl_memo_interface::v3::id(),
accounts: vec![],
data: message.as_bytes().to_vec(),
}
}
fn main() -> anyhow::Result<()> {
let signer: FireblocksSigner = FireblocksSigner::try_from_env(None)?;
let rpc = RpcClient::new(
std::env::var("RPC_URL")
.ok()
.unwrap_or("https://rpc.ankr.com/solana_devnet".to_string()),
);
let hash = rpc.get_latest_blockhash()?;
let message = Message::new(&[memo("fireblocks signer")], Some(&signer.pk));
let mut tx = Transaction::new_unsigned(message);
tx.try_sign(&[&signer], hash)?;
rpc.send_transaction(&tx)?;
Ok(())
}See example
§Transaction Broadcasting
By default, this signer only signs transactions and does not broadcast them. You control when transactions are sent to the network by calling rpc.send_transaction() yourself.
To enable automatic broadcasting (where Fireblocks signs and broadcasts in one step), set:
- Environment variable:
FIREBLOCKS_BROADCAST=true - Config file:
broadcast = truein the[signer]section
When auto-broadcasting is enabled, transactions are sent to the network immediately after signing, and you should not call send_transaction() yourself.
§Environment Variables
| Var | Example |
|---|---|
| FIREBLOCKS_SECRET | RSA private key of your API user |
| FIREBLOCKS_API_KEY | uuid of api user |
| FIREBLOCKS_ENDPOINT | https://sandbox-api.fireblocks.io |
| FIREBLOCKS_PUBKEY | optional pubkey, or lookup based on FIREBLOCKS_VAULT |
| FIREBLOCKS_DEVNET | set to any value if you are on devnet |
| FIREBLOCKS_VAULT | your vault id |
| FIREBLOCKS_POLL_TIMEOUT | in seconds, total time to check status of transaction |
| FIREBLOCKS_POLL_INTERVAL | in seconds |
| FIREBLOCKS_BROADCAST | set to “true” to auto-broadcast transactions (default: false) |
§Configuration Files (Optional)
As an alternative to environment variables, you can use configuration files with the config feature. This provides a more structured approach to managing multiple Fireblocks environments and credentials.
§Enabling the Config Feature
Add the config feature to your Cargo.toml:
[dependencies]
fireblocks-solana-signer = { version = "1", features = ["config"] }§Configuration File Setup
The config feature uses the fireblocks-config crate for configuration management. Configuration files are stored in the ~/.config/fireblocks/ directory using the microxdg crate.
File Structure:
- Default configuration:
~/.config/fireblocks/default.toml(always loaded) - Profile configurations:
~/.config/fireblocks/{profile}.toml(override default settings)
Example ~/.config/fireblocks/default.toml:
api_key = "your-sandbox-api-key-uuid"
secret_path = "/path/to/your/sandbox-private-key.pem"
url = "https://sandbox-api.fireblocks.io"
mainnet = false
[signer]
vault = "your-sandbox-vault-id"
poll_timeout = 30
poll_interval = 2
broadcast = false # default is falseExample ~/.config/fireblocks/production.toml:
api_key = "your-production-api-key"
secret_path = "/path/to/production-key.pem"
url = "https://api.fireblocks.io"
mainnet = true
[signer]
vault = "your-production-vault-id"
poll_timeout = 60
poll_interval = 3
broadcast = true§Using Configuration Files
use fireblocks_solana_sdk::signature::FireblocksSigner;
fn main() -> anyhow::Result<()> {
// Use default configuration profile
let signer = FireblocksSigner::try_from_config::<String>(
&[],
|tx_response| println!("Transaction status: {}", tx_response)
)?;
// Use specific configuration profiles
let signer = FireblocksSigner::try_from_config(
&["mainnet"],
|tx_response| eprintln!("Mainnet TX: {}", tx_response)
)?;
// Use multiple profiles (later profiles override earlier ones)
let signer = FireblocksSigner::try_from_config(
&["default", "production"],
|tx_response| println!("TX Update: {}", tx_response)
)?;
// Your transaction code here...
Ok(())
}How Profile Loading Works:
- Empty slice
&[]: Loads only~/.config/fireblocks/default.toml - Single profile
&["production"]: Loadsdefault.tomlfirst, thenproduction.tomloverrides any matching settings - Multiple profiles
&["staging", "production"]: Loadsdefault.toml, thenstaging.toml, thenproduction.toml(each overriding previous values)
§Benefits of Configuration Files
- Multiple Environments: Easily switch between sandbox, testnet, and mainnet
- Profile Management: Organize different configurations by environment or use case
- Version Control: Configuration files can be committed (without secrets) for team sharing
- Validation: Built-in validation and error handling for configuration values
- Flexibility: Override specific settings per profile while inheriting defaults
§Configuration vs Environment Variables
| Method | Best For | Pros | Cons |
|---|---|---|---|
| Environment Variables | Simple setups, CI/CD | Easy to set, widely supported | Hard to manage multiple environments |
| Configuration Files | Complex setups, multiple environments | Organized, version-controllable, flexible | Requires additional feature, more setup |
For detailed configuration options and file locations, see the fireblocks-config documentation.
§Development
§Prerequisites
-
Rust Nightly: Required for code formatting with advanced features
rustup install nightly -
Environment Setup: Create a
.envfile with your Fireblocks credentialscp env-sample .env # Edit .env with your actual Fireblocks API credentials
§Getting Started
-
Clone the repository
git clone https://github.com/CarteraMesh/fireblocks-solana-signer.git cd fireblocks-solana-signer -
Set up environment
# Copy and configure environment variables cp env-sample .env # Install Rust nightly for formatting rustup install nightly -
Build and test
# Build the project cargo build # Run tests (requires valid Fireblocks credentials in .env) cargo test # Format code (requires nightly) cargo +nightly fmt --all
§Code Formatting
This project uses advanced Rust formatting features that require nightly:
# Format all code
cargo +nightly fmt --all
# Check formatting
cargo +nightly fmt --all -- --check§Running Examples
# Make sure your .env file is configured first
cargo run --example memoStructs§
- Client
- A client for interacting with the Fireblocks API.
- Client
Builder - Builder for configuring and creating Fireblocks API clients.
- Fireblocks
Signer - A Solana signer implementation using Fireblocks as the backend signing service.
- Fireblocks
Signer Builder - Use builder syntax to set the inputs and finish with
build(). - Poll
Config - Configuration for polling Fireblocks transaction status.
- Poll
Config Builder - Use builder syntax to set the inputs and finish with
build(). - Transaction
Response
Enums§
- Asset
- EnvVar
- Environment variables used by the FireblocksSigner.
- Error
- Transaction
Status - TransactionStatus : The primary status of the transaction. For details, see Primary transaction statuses The primary status of the transaction. For details, see Primary transaction statuses
Constants§
- DEFAULT_
CLIENT_ TIMEOUT - FIREBLOCKS_
API - The production Fireblocks API endpoint.
- FIREBLOCKS_
SANDBOX_ API - The sandbox Fireblocks API endpoint for testing.
- SOL
- SOL_
TEST
Traits§
- FromStr
- Parse a value from a string
- Versioned
Transaction Extension - Extension trait for
VersionedTransactionthat adds support for partial signing and address lookup table operations.
Functions§
- build_
client_ and_ address_ blocking_ safe - Builds a Fireblocks client and retrieves the associated Solana address in a tokio-safe manner.
- build_
client_ safe - See
build_client_and_address_blocking_safe - keypair_
from_ seed - Constructs a
FireblocksSignerfrom caller-provided seed entropy.
Type Aliases§
- Result
- A type alias for
std::result::Resultwith this crate’sErrortype.