Rust SDK
The Coalesce Finance Rust SDK for building on-chain programs that integrate with the permissioned lending protocol, or for off-chain Rust applications.
Installation
Add to your Cargo.toml:
[dependencies]
coalescefi-sdk = "0.1"
Feature Flags
# Default: std environment with all features
coalescefi-sdk = "0.1"
# No-std for on-chain programs (Solana BPF)
coalescefi-sdk = { version = "0.1", default-features = false }
Quick Start
use coalescefi_sdk::{
constants::localnet_program_id,
find_market_pda,
find_lender_position_pda,
accounts::decode_market,
};
use solana_program::pubkey::Pubkey;
// Derive PDAs
let program_id = localnet_program_id();
let borrower = Pubkey::new_unique();
let lender = Pubkey::new_unique();
let market_pda = find_market_pda(&borrower, 1, &program_id);
let lender_pda = find_lender_position_pda(&market_pda.address, &lender, &program_id);
// Parse account data (zero-copy)
let market = decode_market(&account_data)?;
println!("Total deposited: {}", market.total_deposited());
println!("Market bump: {}", market_pda.bump);
println!("Lender position PDA: {}", lender_pda.address);
PDA Derivation
All PDA functions return PdaResult { address, bump }:
use coalescefi_sdk::{
constants::localnet_program_id,
find_protocol_config_pda,
find_market_pda,
pdas::find_market_authority_pda,
find_vault_pda,
find_lender_position_pda,
find_borrower_whitelist_pda,
pdas::find_blacklist_check_pda,
};
use solana_program::pubkey::Pubkey;
let program_id = localnet_program_id();
let borrower = Pubkey::new_unique();
let lender = Pubkey::new_unique();
let blacklist_program = Pubkey::new_unique();
// Protocol config (singleton)
let config_pda = find_protocol_config_pda(&program_id);
// Market PDAs
let market_pda = find_market_pda(&borrower, 1, &program_id);
let authority_pda = find_market_authority_pda(&market_pda.address, &program_id);
let vault_pda = find_vault_pda(&market_pda.address, &program_id);
// Lender position
let lender_pda = find_lender_position_pda(&market_pda.address, &lender, &program_id);
// Borrower whitelist
let whitelist_pda = find_borrower_whitelist_pda(&borrower, &program_id);
let blacklist_check_pda = find_blacklist_check_pda(&lender, &blacklist_program);
Account Parsing
Zero-Copy Parsing (Recommended for On-Chain)
The SDK uses bytemuck for zero-copy account parsing:
use coalescefi_sdk::{accounts::decode_market, Market};
use solana_program::pubkey::Pubkey;
// Zero-copy parse (no allocation)
let market: &Market = decode_market(&account_data)?;
// Access fields directly
let borrower: Pubkey = market.borrower_pubkey();
let interest_rate: u16 = market.annual_interest_bps();
let maturity: i64 = market.maturity_timestamp();
let total_deposited: u64 = market.total_deposited();
Account Discriminators
Validate account types using discriminators:
use coalescefi_sdk::constants::{
DISC_PROTOCOL_CONFIG,
DISC_MARKET,
DISC_LENDER_POSITION,
DISC_BORROWER_WL,
};
use solana_program::program_error::ProgramError;
fn validate_market_account(data: &[u8]) -> Result<(), ProgramError> {
if data.len() < 8 || &data[..8] != DISC_MARKET {
return Err(ProgramError::InvalidAccountData);
}
Ok(())
}
Instruction Building
Build Instructions
use coalescefi_sdk::{
constants::{localnet_program_id, spl_token_program_id, system_program_id},
instructions::{create_deposit_instruction, DepositAccounts},
types::DepositArgs,
};
use solana_program::pubkey::Pubkey;
let program_id = localnet_program_id();
let accounts = DepositAccounts {
market: Pubkey::new_unique(),
lender: Pubkey::new_unique(),
lender_token_account: Pubkey::new_unique(),
vault: Pubkey::new_unique(),
lender_position: Pubkey::new_unique(),
blacklist_check: Pubkey::new_unique(),
protocol_config: Pubkey::new_unique(),
mint: Pubkey::new_unique(),
token_program: spl_token_program_id(),
system_program: system_program_id(),
};
let args = DepositArgs {
amount: 1_000_000, // 1 USDC
};
let ix = create_deposit_instruction(accounts, args, &program_id);
Available Instructions
| Function | Description |
|---|---|
create_initialize_protocol_instruction | Initialize protocol config |
create_set_fee_config_instruction | Update fee settings |
create_create_market_instruction | Create a new lending market |
create_deposit_instruction | Deposit tokens to a market |
create_borrow_instruction | Borrow from a market |
create_repay_instruction | Repay borrowed amount |
create_repay_interest_instruction | Repay accrued interest |
create_withdraw_instruction | Withdraw deposited tokens |
create_collect_fees_instruction | Collect protocol fees |
create_resettle_instruction | Re-settle after maturity |
create_close_lender_position_instruction | Close empty position |
create_withdraw_excess_instruction | Withdraw excess funds |
create_set_borrower_whitelist_instruction | Manage whitelist |
create_set_pause_instruction | Pause/unpause protocol |
create_set_blacklist_mode_instruction | Configure blacklist |
create_set_admin_instruction | Transfer admin role |
create_set_whitelist_manager_instruction | Set whitelist manager |
Error Handling
use coalescefi_sdk::errors::CoalescefiError;
match result {
Err(CoalescefiError::InsufficientBalance) => {
msg!("Not enough funds in account");
}
Err(CoalescefiError::Unauthorized) => {
msg!("Signer not authorized");
}
Err(CoalescefiError::NotMatured) => {
msg!("Market hasn't reached maturity");
}
Err(e) => {
msg!("Other error: {:?}", e);
}
Ok(_) => {}
}
Error Codes
use coalescefi_sdk::errors::CoalescefiError;
// Initialization errors (0-4)
CoalescefiError::AlreadyInitialized // 0
CoalescefiError::InvalidFeeRate // 1
CoalescefiError::InvalidCapacity // 2
CoalescefiError::InvalidMaturity // 3
CoalescefiError::MarketAlreadyExists // 4
// Authorization errors (5-9)
CoalescefiError::Unauthorized // 5
CoalescefiError::NotWhitelisted // 6
CoalescefiError::Blacklisted // 7
CoalescefiError::ProtocolPaused // 8
CoalescefiError::BorrowerHasActiveDebt // 9
// Account validation errors (10-16)
CoalescefiError::InvalidAddress // 10
CoalescefiError::InvalidMint // 11
CoalescefiError::InvalidVault // 12
CoalescefiError::InvalidPDA // 13
CoalescefiError::InvalidAccountOwner // 14
CoalescefiError::InvalidTokenProgram // 15
CoalescefiError::InvalidTokenAccountOwner // 16
// Input validation errors (17-20)
CoalescefiError::ZeroAmount // 17
CoalescefiError::ZeroScaledAmount // 18
CoalescefiError::InvalidScaleFactor // 19
CoalescefiError::InvalidTimestamp // 20
// Balance/capacity errors (21-27)
CoalescefiError::InsufficientBalance // 21
CoalescefiError::InsufficientScaledBalance // 22
CoalescefiError::NoBalance // 23
CoalescefiError::ZeroPayout // 24
CoalescefiError::CapExceeded // 25
CoalescefiError::BorrowAmountTooHigh // 26
CoalescefiError::GlobalCapacityExceeded // 27
// Market state errors (28-35)
CoalescefiError::MarketMatured // 28
CoalescefiError::NotMatured // 29
CoalescefiError::NotSettled // 30
CoalescefiError::SettlementNotImproved // 31
CoalescefiError::SettlementGracePeriod // 32
CoalescefiError::SettlementNotComplete // 33
CoalescefiError::PositionNotEmpty // 34
CoalescefiError::RepaymentExceedsDebt // 35
// Fee/withdrawal errors (36-40)
CoalescefiError::NoFeesToCollect // 36
CoalescefiError::FeeCollectionDuringDistress // 37
CoalescefiError::LendersPendingWithdrawals // 38
CoalescefiError::FeesNotCollected // 39
CoalescefiError::NoExcessToWithdraw // 40
// Operational errors (41-42)
CoalescefiError::MathOverflow // 41
CoalescefiError::PayoutBelowMinimum // 42
Constants
use coalescefi_sdk::constants::{
// Mathematical constants
WAD, // 10^18
BPS, // 10000
SECONDS_PER_YEAR, // 31_536_000
// Protocol limits
MAX_ANNUAL_INTEREST_BPS, // 10000 (100%)
MAX_FEE_RATE_BPS, // 10000 (100%)
USDC_DECIMALS, // 6
// Account sizes
PROTOCOL_CONFIG_SIZE, // 194 bytes
MARKET_SIZE, // 250 bytes
LENDER_POSITION_SIZE, // 128 bytes
BORROWER_WHITELIST_SIZE, // 96 bytes
// Seeds
SEED_PROTOCOL_CONFIG,
SEED_MARKET,
SEED_MARKET_AUTHORITY,
SEED_VAULT,
SEED_LENDER,
SEED_BORROWER_WHITELIST,
SEED_BLACKLIST,
};
No-Std Support
For on-chain programs, use the SDK without std:
[dependencies]
coalescefi-sdk = { version = "0.1", default-features = false }
#![no_std]
use coalescefi_sdk::find_market_pda;
// All core functionality works without std
For account layouts and byte offsets, see the architecture documentation.
Integration Example
Complete instruction-construction example:
use coalescefi_sdk::{
constants::{localnet_program_id, spl_token_program_id, system_program_id},
instructions::{create_deposit_instruction, DepositAccounts},
types::DepositArgs,
};
use solana_program::pubkey::Pubkey;
let program_id = localnet_program_id();
let accounts = DepositAccounts {
market: Pubkey::new_unique(),
lender: Pubkey::new_unique(),
lender_token_account: Pubkey::new_unique(),
vault: Pubkey::new_unique(),
lender_position: Pubkey::new_unique(),
blacklist_check: Pubkey::new_unique(),
protocol_config: Pubkey::new_unique(),
mint: Pubkey::new_unique(),
token_program: spl_token_program_id(),
system_program: system_program_id(),
};
let ix = create_deposit_instruction(accounts, DepositArgs { amount: 1_000_000 }, &program_id);
Testing
#[cfg(test)]
mod tests {
use super::*;
use coalescefi_sdk::constants::*;
#[test]
fn test_pda_derivation() {
let program_id = localnet_program_id();
let borrower = Pubkey::new_unique();
let market = find_market_pda(&borrower, 1, &program_id);
assert!(market.bump > 0);
assert_ne!(market.address, Pubkey::default());
}
#[test]
fn test_account_size() {
assert_eq!(std::mem::size_of::<Market>(), MARKET_SIZE);
}
}
Next Steps
- TypeScript SDK - For frontend applications
- Python SDK - For scripts and backend
- Error Codes - Error codes reference