Building a Telegram Bot with Breeze SDK
This guide demonstrates how to build a fully functional Telegram bot for DeFi yield farming using the Breeze SDK. The bot provides a conversational interface for users to deposit stablecoins, earn yield, and withdraw funds seamlessly.
Functionality
Your browser does not support the video tag.
Test it yourself
If the bot will be running on our hardware, then you will be able to test it yourself.
You can find it under @breeze_integration_bot handle or under this link .
Code Location
GitHub Repo Find out the github repository with the full code
The Breeze SDK (@breezebaby/breeze-sdk) abstracts away complex API interactions, providing a clean interface for deposit/withdraw operations and real-time portfolio management.
Breeze SDK Telegram Bot - Complete Implementation Guide
Overview
The Breeze SDK Telegram Bot enables users to:
Manage Wallets : Generate new keypairs or import existing ones
Real-time Portfolio Tracking : View current balances and yield positions with live APY data
Multi-Asset Support : Deposit and withdraw 8 different assets (USDC, USDT, USDS, JupSOL, JLP, mSOL, JitoSOL, SOL)
Per-Asset APY Display : See real-time APY for each supported asset
Detailed Balance Views : See comprehensive breakdowns of all positions
Yield History : Track earnings over time with pagination support
Seamless Deposits : Deposit any supported asset into yield-generating strategies
Flexible Withdrawals : Withdraw funds with percentage-based or custom amounts
Transaction Management : Sign and submit transactions directly through Telegram
Info : The bot uses Strategy ID + Mint for deposits/withdrawals, fetching real-time APY per asset from the Breeze API.
Project Setup
Dependencies
{
"dependencies" : {
"@breezebaby/breeze-sdk" : "^2.0.1" ,
"@solana/web3.js" : "^1.98.2" ,
"@solana/spl-token" : "^0.4.13" ,
"node-telegram-bot-api" : "^0.66.0" ,
"bs58" : "^6.0.0" ,
"dotenv" : "^16.5.0"
},
"devDependencies" : {
"typescript" : "^5.8.3" ,
"@types/node" : "^24.0.3" ,
"@types/node-telegram-bot-api" : "^0.64.9" ,
"tsx" : "^4.21.0"
}
}
TypeScript Configuration
{
"compilerOptions" : {
"target" : "ES2020" ,
"module" : "ES2020" ,
"moduleResolution" : "bundler" ,
"esModuleInterop" : true ,
"allowSyntheticDefaultImports" : true ,
"skipLibCheck" : true ,
"outDir" : "./dist" ,
"rootDir" : "./src"
}
}
Warning : Make sure to set "type": "module" in your package.json to use ES modules.
Core Implementation
Asset Configuration
The bot supports 8 assets with their respective mints and decimals:
interface AssetConfig {
symbol : string ;
mint : string ;
decimals : number ;
}
const AVAILABLE_ASSETS : AssetConfig [] = [
{ symbol: 'USDC' , mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' , decimals: 6 },
{ symbol: 'USDT' , mint: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' , decimals: 6 },
{ symbol: 'USDS' , mint: 'USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA' , decimals: 6 },
{ symbol: 'JupSOL' , mint: 'jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v' , decimals: 9 },
{ symbol: 'JLP' , mint: '27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4' , decimals: 6 },
{ symbol: 'mSOL' , mint: 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So' , decimals: 9 },
{ symbol: 'JitoSOL' , mint: 'J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn' , decimals: 9 },
{ symbol: 'SOL' , mint: 'So11111111111111111111111111111111111111112' , decimals: 9 },
];
Environment Variables
const BOT_TOKEN = process . env . TELEGRAM_BOT_TOKEN || process . env . BOT_TOKEN ! ;
const SOLANA_RPC_URL = process . env . SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com' ;
const BREEZE_API_KEY = process . env . BREEZE_API_KEY ! ;
const STRATEGY_ID = process . env . STRATEGY_ID ! ;
const BREEZE_API_BASE_URL = 'https://api.breeze.baby' ;
Strategy Info Type
interface StrategyInfo {
strategy_id : string ;
strategy_name : string ;
assets : string [];
apy : number ;
apy_per_asset : Record < string , number >; // mint -> APY mapping
}
SDK Initialization
import { BreezeSDK } from '@breezebaby/breeze-sdk' ;
class BreezeBot {
private bot : TelegramBot ;
private connection : Connection ;
private breezeSDK : BreezeSDK ;
private users : Map < number , UserData > = new Map ();
private strategyInfo : StrategyInfo | null = null ;
private strategyInfoLastFetch : number = 0 ;
private readonly STRATEGY_INFO_CACHE_TTL = 60000 ; // 1 minute cache
constructor () {
this . bot = new TelegramBot ( BOT_TOKEN , { polling: true });
this . connection = new Connection ( SOLANA_RPC_URL );
// Initialize Breeze SDK for balance/yield queries
this . breezeSDK = new BreezeSDK ({
baseUrl: 'https://api.breeze.baby/' ,
apiKey: BREEZE_API_KEY
});
this . setupHandlers ();
// Fetch strategy info on startup
this . fetchStrategyInfo ();
}
}
User Data Management
interface UserData {
keypair ?: Keypair ;
publicKey ?: string ;
currentMenu ?: string ;
selectedAsset ?: AssetConfig ; // Currently selected asset for deposit/withdraw
pendingTransaction ?: {
serializedTx : string ;
type : 'deposit' | 'withdraw' ;
amount ?: number ;
asset ?: string ;
};
}
SDK Methods Overview
The bot uses three main SDK methods:
getStrategyInfo(strategyId) - Fetches APY per asset
getBreezeBalances({ userId, strategyId }) - Fetches user’s balances in the strategy
createDepositTransaction / createWithdrawTransaction - Creates transactions with strategyId + baseAsset
Strategy Info & APY Integration
Fetching Strategy Info
The bot fetches strategy info using the SDK to get real-time APY for each asset:
private async fetchStrategyInfo (): Promise < StrategyInfo | null > {
const now = Date . now ();
// Use cache if valid
if (this.strategyInfo && ( now - this.strategyInfoLastFetch) < this . STRATEGY_INFO_CACHE_TTL ) {
return this . strategyInfo ;
}
try {
// Use SDK method
this . strategyInfo = await this . breezeSDK . getStrategyInfo ( STRATEGY_ID );
this . strategyInfoLastFetch = now ;
return this . strategyInfo ;
} catch ( error ) {
console . error ( 'Error fetching strategy info:' , error );
return this . strategyInfo ;
}
}
// Get APY for a specific mint
private getApyForMint ( mint : string ) : number | null {
if (!this.strategyInfo?.apy_per_asset) return null;
return this . strategyInfo . apy_per_asset [ mint ] ?? null ;
}
Fetching Breeze Balances
User balances are fetched using getBreezeBalances with the strategyId:
// Get total Breeze balance
private async getUserCurrentValue ( userPublicKey : string ): Promise < number > {
try {
const data = await this . breezeSDK . getBreezeBalances ({
userId: userPublicKey ,
strategyId: STRATEGY_ID
});
let total = 0 ;
for ( const balance of data . data ) {
total += balance . total_position_value / Math . pow ( 10 , balance . decimals );
}
return total ;
} catch (error) {
console. error ( 'Error fetching user current value:' , error );
return 0 ;
}
}
// Get all Breeze balances mapped by symbol
private async getUserBreezeBalances ( userPublicKey : string ): Promise < Map < string , { balance: number ; apy : number } >> {
const balances = new Map < string , { balance : number ; apy : number }>();
try {
const data = await this . breezeSDK . getBreezeBalances ({
userId: userPublicKey ,
strategyId: STRATEGY_ID ,
sortBy: 'balance' ,
sortOrder: 'desc'
});
for ( const balance of data . data ) {
const positionValue = balance . total_position_value / Math . pow ( 10 , balance . decimals );
balances . set ( balance . token_symbol , {
balance: positionValue ,
apy: balance . apy
});
}
} catch (error) {
console. error ( 'Error fetching user Breeze balances:' , error );
}
return balances ;
}
Deposit Transaction Processing
Deposits use the SDK’s createDepositTransaction with strategyId + baseAsset:
private async processDeposit ( chatId : number , percentage ?: number , customAmount ?: number ) {
const userData = this . users . get ( chatId ) ! ;
const selectedAsset = userData . selectedAsset ;
// ... amount calculation logic ...
try {
// Use SDK to create deposit transaction with strategyId + baseAsset
const data = await this . breezeSDK . createDepositTransaction ({
strategyId: STRATEGY_ID ,
baseAsset: selectedAsset . mint ,
amount: Number ( tokenAmount ),
all: isAll ,
userKey: userData . publicKey ! ,
payerKey: userData . publicKey !
});
// Check if response is string (success) or error object
if ( typeof data === 'object' && 'message' in data ) {
await this . bot . sendMessage ( chatId , `❌ Error: ${ data . message } ` );
return ;
}
userData . pendingTransaction = {
serializedTx: data as string ,
type: 'deposit' ,
amount: humanAmount ,
asset: selectedAsset . symbol
};
await this . showTransactionConfirmation ( chatId , 'deposit' , humanAmount , selectedAsset . symbol );
} catch ( error ) {
console . error ( 'Deposit error:' , error );
await this . bot . sendMessage ( chatId , '❌ Failed to create deposit transaction. Please try again.' );
}
}
Withdraw Transaction Processing
Withdrawals use the SDK’s createWithdrawTransaction with strategyId + baseAsset:
private async processWithdraw ( chatId : number , percentage ?: number , customAmount ?: number ) {
const userData = this . users . get ( chatId ) ! ;
const selectedAsset = userData . selectedAsset ;
// ... amount calculation logic ...
try {
// Use SDK to create withdraw transaction with strategyId + baseAsset
const data = await this . breezeSDK . createWithdrawTransaction ({
strategyId: STRATEGY_ID ,
baseAsset: selectedAsset . mint ,
amount: tokenAmount ,
all: isAll ,
userKey: userData . publicKey ! ,
payerKey: userData . publicKey !
});
// Check if response is string (success) or error object
if ( typeof data === 'object' && 'message' in data ) {
await this . bot . sendMessage ( chatId , `❌ Error: ${ data . message } ` );
return ;
}
userData . pendingTransaction = {
serializedTx: data as string ,
type: 'withdraw' ,
amount: humanAmount ,
asset: selectedAsset . symbol
};
await this . showTransactionConfirmation ( chatId , 'withdraw' , humanAmount , selectedAsset . symbol );
} catch ( error ) {
console . error ( 'Withdraw error:' , error );
await this . bot . sendMessage ( chatId , '❌ Failed to create withdrawal transaction. Please try again.' );
}
}
User Interface Components
Main Dashboard with Per-Asset APY
private async showMainInterface ( chatId : number ) {
const userData = this . users . get ( chatId ) ! ;
const publicKey = userData . publicKey ! ;
// Fetch strategy info for APY
await this . fetchStrategyInfo ();
// Get wallet balances for all assets
const walletBalances = await this . getAllBalances ( publicKey );
const breezeBalance = await this . getUserCurrentValue ( publicKey );
let balanceStr = '' ;
for ( const asset of AVAILABLE_ASSETS ) {
const balance = walletBalances . get ( asset . symbol );
const apy = this . getApyForMint ( asset . mint );
const apyStr = apy !== null ? ` ( ${ apy . toFixed ( 2 ) } % APY)` : '' ;
if ( asset . symbol === 'SOL' ) {
balanceStr += `• ${ asset . symbol } : ${ balance ?. human . toFixed ( 4 ) || '0.0000' } ◎ ${ apyStr } \n ` ;
} else {
balanceStr += `• ${ asset . symbol } : ${ balance ?. human . toFixed ( 2 ) || '0.00' }${ apyStr } \n ` ;
}
}
const message =
'🌊 **BREEZE INTEGRATION BOT** 🌊 \n\n ' +
`💳 Wallet: \` ${ publicKey . slice ( 0 , 8 ) } ... ${ publicKey . slice ( - 8 ) } \`\n\n ` +
'💰 **Wallet Balances:** \n ' +
balanceStr + ' \n ' +
`🌊 **Breeze Balance:** $ ${ breezeBalance . toFixed ( 2 ) } \n\n ` +
'🚀 Ready to earn yield with Breeze!' ;
// ... keyboard setup
}
Deposit Interface with Asset Selection
private async showDepositInterface ( chatId : number ) {
// Fetch strategy info for APY and available assets
await this . fetchStrategyInfo ();
const availableAssets = this . strategyInfo ?. assets || [];
let message = '📥 **Deposit to Breeze** 📥 \n\n ' +
'Select the asset you want to deposit: \n\n ' ;
// Build keyboard with all available assets and their APYs
const assetButtons : TelegramBot . InlineKeyboardButton [][] = [];
for ( const asset of AVAILABLE_ASSETS ) {
const apy = this . getApyForMint ( asset . mint );
const isAvailable = availableAssets . length === 0 || availableAssets . includes ( asset . mint );
if ( isAvailable && apy !== null ) {
assetButtons . push ([{
text: ` ${ asset . symbol } ( ${ apy . toFixed ( 2 ) } % APY)` ,
callback_data: `deposit_asset_ ${ asset . symbol } `
}]);
}
}
assetButtons . push ([{ text: '🔙 Back' , callback_data: 'earn_yield' }]);
await this . bot . sendMessage ( chatId , message , {
parse_mode: 'Markdown' ,
reply_markup: { inline_keyboard: assetButtons }
});
}
Withdraw Interface with Asset Selection
private async showWithdrawInterface ( chatId : number ) {
const userData = this . users . get ( chatId ) ! ;
// Get all Breeze balances
const breezeBalances = await this . getUserBreezeBalances ( userData . publicKey ! );
let message = '📤 **Withdraw from Breeze** 📤 \n\n ' +
'Select the asset you want to withdraw: \n\n ' ;
// Build keyboard with assets that have balances
const assetButtons : TelegramBot . InlineKeyboardButton [][] = [];
for ( const asset of AVAILABLE_ASSETS ) {
const breezeBal = breezeBalances . get ( asset . symbol );
if ( breezeBal && breezeBal . balance > 0 ) {
const displayBalance = asset . decimals > 6
? breezeBal . balance . toFixed ( 4 )
: breezeBal . balance . toFixed ( 2 );
assetButtons . push ([{
text: ` ${ asset . symbol } : ${ displayBalance } ` ,
callback_data: `withdraw_asset_ ${ asset . symbol } `
}]);
}
}
assetButtons . push ([{ text: '🔙 Back' , callback_data: 'earn_yield' }]);
await this . bot . sendMessage ( chatId , message , {
parse_mode: 'Markdown' ,
reply_markup: { inline_keyboard: assetButtons }
});
}
Callback Handler for Asset Selection
private async handleCallbackQuery ( query : TelegramBot . CallbackQuery ) {
const chatId = query . message ! . chat . id ;
const data = query . data ! ;
await this . bot . answerCallbackQuery ( query . id );
// Handle asset selection for deposit
if ( data . startsWith ( 'deposit_asset_' )) {
const symbol = data . replace ( 'deposit_asset_' , '' );
const asset = getAssetBySymbol ( symbol );
if ( asset ) {
this . users . get ( chatId ) ! . selectedAsset = asset ;
await this . showDepositAmountSelection ( chatId );
}
return ;
}
// Handle asset selection for withdraw
if ( data . startsWith ( 'withdraw_asset_' )) {
const symbol = data . replace ( 'withdraw_asset_' , '' );
const asset = getAssetBySymbol ( symbol );
if ( asset ) {
this . users . get ( chatId ) ! . selectedAsset = asset ;
await this . showWithdrawAmountSelection ( chatId );
}
return ;
}
switch ( data ) {
case 'deposit_amount_50' :
await this . processDeposit ( chatId , 50 );
break ;
case 'deposit_amount_100' :
await this . processDeposit ( chatId , 100 );
break ;
case 'withdraw_amount_50' :
await this . processWithdraw ( chatId , 50 );
break ;
case 'withdraw_amount_100' :
await this . processWithdraw ( chatId , 100 );
break ;
// ... other cases
}
}
Transaction Management
Transaction Signing and Submission
private async confirmTransaction ( chatId : number ) {
const userData = this . users . get ( chatId ) ! ;
const pendingTx = userData . pendingTransaction ;
if ( ! pendingTx ) {
await this . bot . sendMessage ( chatId , '❌ No pending transaction found.' );
return ;
}
try {
// Deserialize and sign the transaction
const transaction = VersionedTransaction . deserialize (
Buffer . from ( pendingTx . serializedTx , 'base64' )
);
transaction . sign ([ userData . keypair ! ]);
// Submit to Solana network
const signature = await this . connection . sendTransaction ( transaction );
await this . bot . sendMessage ( chatId , '⏳ Transaction sent! Waiting for confirmation...' );
// Wait for confirmation
const confirmation = await this . connection . confirmTransaction ( signature , 'confirmed' );
if ( confirmation . value . err ) {
await this . bot . sendMessage ( chatId , '❌ Transaction failed!' );
return ;
}
const action = pendingTx . type === 'deposit' ? 'deposited to' : 'withdrawn from' ;
await this . bot . sendMessage ( chatId ,
`🎉 **Successfully ${ action } Breeze!** \n\n ` +
`💰 Amount: ${ pendingTx . amount ?. toFixed ( 2 ) } ${ pendingTx . asset } \n ` +
`🔗 Transaction: \` ${ signature } \` ` ,
{ parse_mode: 'Markdown' }
);
// Clear pending transaction
userData . pendingTransaction = undefined ;
setTimeout (() => this . showMainInterface ( chatId ), 2000 );
} catch ( error ) {
console . error ( 'Transaction error:' , error );
await this . bot . sendMessage ( chatId , '❌ Failed to process transaction. Please try again.' );
}
}
Key Features
Multi-Asset Support
8 Supported Assets : USDC, USDT, USDS, JupSOL, JLP, mSOL, JitoSOL, SOL
Dynamic Asset Lists : Available assets fetched from strategy info
Per-Asset APY Display : Real-time APY shown for each asset
Strategy-Based Integration
Strategy ID : Single strategy manages all assets
Mint-Based Routing : Transactions use strategy_id + base_asset (mint)
APY Per Asset : Strategy info provides apy_per_asset mapping
Wallet Management
Keypair Generation : Create new Solana keypairs securely
Private Key Import : Import existing wallets using base58 encoded private keys
Balance Tracking : Real-time SOL and SPL token balance monitoring
Advanced Yield Farming
Seamless Deposits : Deposit any supported asset into yield strategies
Flexible Withdrawals : Withdraw with percentage options (50%, 100%) or custom amounts
Real-time Portfolio : Live tracking of deposited amounts and yield earnings
Live APY Data : Real-time yield rates from the strategy info API
Environment Setup
# Required environment variables
TELEGRAM_BOT_TOKEN = your_telegram_bot_token
SOLANA_RPC_URL = https://api.mainnet-beta.solana.com
BREEZE_API_KEY = your_breeze_api_key
STRATEGY_ID = your_strategy_id
Build and Run
# Install dependencies
npm install
# Build the project
npm run build
# Start the bot (production)
npm start
# Start the bot (development with hot reload)
npm run dev
SDK Methods Used
All interactions with the Breeze API are done through the SDK:
Strategy Info
// Get strategy metadata including APY per asset
const strategyInfo = await breezeSDK . getStrategyInfo ( STRATEGY_ID );
// Returns: { strategy_id, strategy_name, assets, apy, apy_per_asset }
Balance Queries
// Get user balances for a specific strategy
const balances = await breezeSDK . getBreezeBalances ({
userId: userPublicKey ,
strategyId: STRATEGY_ID ,
sortBy: 'balance' ,
sortOrder: 'desc'
});
// Returns: { data: BreezeBalance[], meta: { page, per_page, total, ... } }
Transactions
// Create deposit transaction
const depositTx = await breezeSDK . createDepositTransaction ({
strategyId: STRATEGY_ID ,
baseAsset: mintAddress ,
amount: tokenAmount ,
all: false ,
userKey: userPublicKey ,
payerKey: userPublicKey
});
// Create withdraw transaction
const withdrawTx = await breezeSDK . createWithdrawTransaction ({
strategyId: STRATEGY_ID ,
baseAsset: mintAddress ,
amount: tokenAmount ,
all: false ,
userKey: userPublicKey ,
payerKey: userPublicKey
});
Type Definitions
interface BreezeBalance {
strategy_name : string ;
strategy_id : string ;
fund_id : string ;
token_symbol : string ;
decimals : number ;
total_position_value : number ;
total_deposited_value : number ;
yield_earned : number ;
apy : number ;
last_updated : string ;
}
interface StrategyInfo {
strategy_id : string ;
strategy_name : string ;
assets : string [];
apy : number ;
apy_per_asset : Record < string , number >;
}
Best Practices
Security Considerations
Private Key Handling : Never log or expose private keys in plain text
Environment Variables : Use secure environment variable management
Input Validation : Validate all user inputs before processing
Rate Limiting : Implement rate limiting to prevent abuse
Strategy Info Caching : 1-minute TTL cache for strategy info
Connection Pooling : Reuse Solana RPC connections
Async Operations : Use async/await for all I/O operations
Error Recovery : Implement robust error handling and retry logic
Troubleshooting
API Connection Issues
Verify API key is correct and active
Check network connectivity to Breeze API
Ensure proper environment variable configuration
Verify STRATEGY_ID is valid
Transaction Failures
Check user has sufficient SOL for transaction fees
Verify token account exists and has sufficient balance
Ensure transaction isn’t expired or malformed
Check Solana network status
Balance Discrepancies
Strategy info provides real-time APY data
Check for pending transactions
Verify decimal conversion accuracy
Conclusion
The Breeze SDK Telegram Bot provides a comprehensive interface for multi-asset yield farming:
Strategy-Based Architecture : Single strategy ID manages all supported assets
Real-Time APY : Per-asset APY fetched via getStrategyInfo()
Multi-Asset Support : 8 different assets for deposits and withdrawals
Full SDK Integration : All operations use SDK methods:
getStrategyInfo() - APY per asset
getBreezeBalances() - User balances with strategy filter
createDepositTransaction() / createWithdrawTransaction() - Transactions with strategyId + baseAsset
Type Safety : Full TypeScript support for robust development
For additional support and advanced integration patterns, consult the Breeze SDK documentation or reach out to the development team.