feat: add Bonk migration events and bump to v0.1.9

- Add BonkMigrateToAmmEvent and BonkMigrateToCpswapEvent
- Update parsers and event handlers
- Add rustfmt.toml configuration
- Update documentation and examples
This commit is contained in:
ysq
2025-08-03 21:59:08 +08:00
parent 4c90931e28
commit 109c50cf06
8 changed files with 356 additions and 100 deletions
@@ -1,13 +1,13 @@
use crate::impl_unified_event;
use crate::streaming::event_parser::common::EventMetadata;
use crate::streaming::event_parser::protocols::bonk::types::{
CurveParams, MintParams, PoolStatus, TradeDirection, VestingParams,
};
use crate::streaming::event_parser::common::EventMetadata;
use crate::impl_unified_event;
use borsh::BorshDeserialize;
use serde::{Deserialize, Serialize};
use solana_sdk::pubkey::Pubkey;
/// 买入事件
/// Trade event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct BonkTradeEvent {
#[borsh(skip)]
@@ -53,7 +53,7 @@ pub struct BonkTradeEvent {
pub is_bot: bool,
}
// 使用宏生成UnifiedEvent实现,指定需要合并的字段
// Macro to generate UnifiedEvent implementation, specifying the fields to be merged
impl_unified_event!(
BonkTradeEvent,
pool_state,
@@ -73,7 +73,7 @@ impl_unified_event!(
pool_status
);
/// 创建池事件
/// Create pool event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct BonkPoolCreateEvent {
#[borsh(skip)]
@@ -100,7 +100,7 @@ pub struct BonkPoolCreateEvent {
pub platform_config: Pubkey,
}
// 使用宏生成UnifiedEvent实现,指定需要合并的字段
// Macro to generate UnifiedEvent implementation, specifying the fields to be merged
impl_unified_event!(
BonkPoolCreateEvent,
pool_state,
@@ -111,16 +111,138 @@ impl_unified_event!(
vesting_param
);
/// 事件鉴别器常量
/// Create pool event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct BonkMigrateToAmmEvent {
#[borsh(skip)]
pub metadata: EventMetadata,
pub base_lot_size: u64,
pub quote_lot_size: u64,
pub market_vault_signer_nonce: u8,
#[borsh(skip)]
pub payer: Pubkey,
#[borsh(skip)]
pub base_mint: Pubkey,
#[borsh(skip)]
pub quote_mint: Pubkey,
#[borsh(skip)]
pub openbook_program: Pubkey,
#[borsh(skip)]
pub market: Pubkey,
#[borsh(skip)]
pub request_queue: Pubkey,
#[borsh(skip)]
pub event_queue: Pubkey,
#[borsh(skip)]
pub bids: Pubkey,
#[borsh(skip)]
pub asks: Pubkey,
#[borsh(skip)]
pub market_vault_signer: Pubkey,
#[borsh(skip)]
pub market_base_vault: Pubkey,
#[borsh(skip)]
pub market_quote_vault: Pubkey,
#[borsh(skip)]
pub amm_program: Pubkey,
#[borsh(skip)]
pub amm_pool: Pubkey,
#[borsh(skip)]
pub amm_authority: Pubkey,
#[borsh(skip)]
pub amm_open_orders: Pubkey,
#[borsh(skip)]
pub amm_lp_mint: Pubkey,
#[borsh(skip)]
pub amm_base_vault: Pubkey,
#[borsh(skip)]
pub amm_quote_vault: Pubkey,
#[borsh(skip)]
pub amm_target_orders: Pubkey,
#[borsh(skip)]
pub amm_config: Pubkey,
#[borsh(skip)]
pub amm_create_fee_destination: Pubkey,
#[borsh(skip)]
pub authority: Pubkey,
#[borsh(skip)]
pub pool_state: Pubkey,
#[borsh(skip)]
pub global_config: Pubkey,
#[borsh(skip)]
pub base_vault: Pubkey,
#[borsh(skip)]
pub quote_vault: Pubkey,
#[borsh(skip)]
pub pool_lp_token: Pubkey,
#[borsh(skip)]
pub spl_token_program: Pubkey,
#[borsh(skip)]
pub associated_token_program: Pubkey,
#[borsh(skip)]
pub system_program: Pubkey,
#[borsh(skip)]
pub rent_program: Pubkey,
}
// Macro to generate UnifiedEvent implementation, specifying the fields to be merged
impl_unified_event!(
BonkMigrateToAmmEvent,
base_lot_size,
quote_lot_size,
market_vault_signer_nonce
);
// Migrate to CP Swap event
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct BonkMigrateToCpswapEvent {
pub metadata: EventMetadata,
pub payer: Pubkey,
pub base_mint: Pubkey,
pub quote_mint: Pubkey,
pub platform_config: Pubkey,
pub cpswap_program: Pubkey,
pub cpswap_pool: Pubkey,
pub cpswap_authority: Pubkey,
pub cpswap_lp_mint: Pubkey,
pub cpswap_base_vault: Pubkey,
pub cpswap_quote_vault: Pubkey,
pub cpswap_config: Pubkey,
pub cpswap_create_pool_fee: Pubkey,
pub cpswap_observation: Pubkey,
pub lock_program: Pubkey,
pub lock_authority: Pubkey,
pub lock_lp_vault: Pubkey,
pub authority: Pubkey,
pub pool_state: Pubkey,
pub global_config: Pubkey,
pub base_vault: Pubkey,
pub quote_vault: Pubkey,
pub pool_lp_token: Pubkey,
pub base_token_program: Pubkey,
pub quote_token_program: Pubkey,
pub associated_token_program: Pubkey,
pub system_program: Pubkey,
pub rent_program: Pubkey,
pub metadata_program: Pubkey,
pub remaining_accounts: Vec<Pubkey>,
}
// Macro to generate UnifiedEvent implementation, specifying the fields to be merged
impl_unified_event!(BonkMigrateToCpswapEvent,);
/// Event discriminator constants
pub mod discriminators {
// 事件鉴别器
// Event discriminators
pub const TRADE_EVENT: &str = "0xe445a52e51cb9a1dbddb7fd34ee661ee";
pub const POOL_CREATE_EVENT: &str = "0xe445a52e51cb9a1d97d7e20976a173ae";
// 指令鉴别器
// Instruction discriminators
pub const BUY_EXACT_IN: &[u8] = &[250, 234, 13, 123, 213, 156, 19, 236];
pub const BUY_EXACT_OUT: &[u8] = &[24, 211, 116, 40, 105, 3, 153, 56];
pub const SELL_EXACT_IN: &[u8] = &[149, 39, 222, 155, 211, 124, 152, 26];
pub const SELL_EXACT_OUT: &[u8] = &[95, 200, 71, 34, 8, 9, 11, 166];
pub const INITIALIZE: &[u8] = &[175, 175, 109, 31, 13, 152, 155, 237];
pub const MIGRATE_TO_AMM: &[u8] = &[207, 82, 192, 145, 254, 207, 145, 223];
pub const MIGRATE_TO_CP_SWAP: &[u8] = &[136, 92, 200, 103, 28, 218, 144, 140];
}
@@ -6,16 +6,17 @@ use crate::streaming::event_parser::{
common::{utils::*, EventMetadata, EventType, ProtocolType},
core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent},
protocols::bonk::{
discriminators, BonkPoolCreateEvent, BonkTradeEvent, ConstantCurve, CurveParams,
FixedCurve, LinearCurve, MintParams, TradeDirection, VestingParams,
discriminators, BonkMigrateToAmmEvent, BonkMigrateToCpswapEvent, BonkPoolCreateEvent,
BonkTradeEvent, ConstantCurve, CurveParams, FixedCurve, LinearCurve, MintParams,
TradeDirection, VestingParams,
},
};
/// Bonk程序ID
/// Bonk Program ID
pub const BONK_PROGRAM_ID: Pubkey =
solana_sdk::pubkey!("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj");
/// Bonk事件解析器
/// Bonk Event Parser
pub struct BonkEventParser {
inner: GenericEventParser,
}
@@ -28,7 +29,7 @@ impl Default for BonkEventParser {
impl BonkEventParser {
pub fn new() -> Self {
// 配置所有事件类型
// Configure all event types
let configs = vec![
GenericEventParseConfig {
inner_instruction_discriminator: discriminators::TRADE_EVENT,
@@ -65,6 +66,20 @@ impl BonkEventParser {
inner_instruction_parser: Self::parse_pool_create_inner_instruction,
instruction_parser: Self::parse_initialize_instruction,
},
GenericEventParseConfig {
inner_instruction_discriminator: "",
instruction_discriminator: discriminators::MIGRATE_TO_AMM,
event_type: EventType::BonkMigrateToAmm,
inner_instruction_parser: Self::parse_migrate_to_amm_inner_instruction,
instruction_parser: Self::parse_migrate_to_amm_instruction,
},
GenericEventParseConfig {
inner_instruction_discriminator: "",
instruction_discriminator: discriminators::MIGRATE_TO_CP_SWAP,
event_type: EventType::BonkMigrateToCpswap,
inner_instruction_parser: Self::parse_migrate_to_cpswap_inner_instruction,
instruction_parser: Self::parse_migrate_to_cpswap_instruction,
},
];
let inner = GenericEventParser::new(BONK_PROGRAM_ID, ProtocolType::Bonk, configs);
@@ -72,7 +87,7 @@ impl BonkEventParser {
Self { inner }
}
/// 解析创建池事件
/// Parse pool creation event
fn parse_pool_create_inner_instruction(
data: &[u8],
metadata: EventMetadata,
@@ -80,27 +95,36 @@ impl BonkEventParser {
if let Ok(event) = borsh::from_slice::<BonkPoolCreateEvent>(data) {
let mut metadata = metadata;
metadata.set_id(metadata.signature.to_string());
Some(Box::new(BonkPoolCreateEvent {
metadata,
..event
}))
Some(Box::new(BonkPoolCreateEvent { metadata, ..event }))
} else {
None
}
}
/// 解析交易事件
/// Parse migrate to AMM event
fn parse_migrate_to_amm_inner_instruction(
_data: &[u8],
_metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
None
}
/// Parse migrate to CP Swap event
fn parse_migrate_to_cpswap_inner_instruction(
_data: &[u8],
_metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
None
}
/// Parse trade event
fn parse_trade_inner_instruction(
data: &[u8],
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
if let Ok(event) = borsh::from_slice::<BonkTradeEvent>(data) {
let mut metadata = metadata;
metadata.set_id(format!(
"{}-{}",
metadata.signature,
event.pool_state
));
metadata.set_id(format!("{}-{}", metadata.signature, event.pool_state));
if metadata.event_type == EventType::BonkBuyExactIn
|| metadata.event_type == EventType::BonkBuyExactOut
{
@@ -109,19 +133,17 @@ impl BonkEventParser {
}
} else if (metadata.event_type == EventType::BonkSellExactIn
|| metadata.event_type == EventType::BonkSellExactOut)
&& event.trade_direction != TradeDirection::Sell {
return None;
}
Some(Box::new(BonkTradeEvent {
metadata,
..event
}))
&& event.trade_direction != TradeDirection::Sell
{
return None;
}
Some(Box::new(BonkTradeEvent { metadata, ..event }))
} else {
None
}
}
/// 解析买入指令事件
/// Parse buy instruction event
fn parse_buy_exact_in_instruction(
data: &[u8],
accounts: &[Pubkey],
@@ -258,7 +280,7 @@ impl BonkEventParser {
}))
}
/// 解析初始化事件
/// Parse initialize event
fn parse_initialize_instruction(
data: &[u8],
accounts: &[Pubkey],
@@ -294,13 +316,13 @@ impl BonkEventParser {
}))
}
/// 解析 MintParams 结构
/// Parse MintParams structure
fn parse_mint_params(data: &[u8], offset: &mut usize) -> Option<MintParams> {
// 读取decimals (1字节)
// Read decimals (1 byte)
let decimals = read_u8(data, *offset)?;
*offset += 1;
// 读取name字符串长度和内容
// Read name string length and content
let name_len = read_u32_le(data, *offset)? as usize;
*offset += 4;
if data.len() < *offset + name_len {
@@ -309,7 +331,7 @@ impl BonkEventParser {
let name = String::from_utf8(data[*offset..*offset + name_len].to_vec()).ok()?;
*offset += name_len;
// 读取symbol字符串长度和内容
// Read symbol string length and content
let symbol_len = read_u32_le(data, *offset)? as usize;
*offset += 4;
if data.len() < *offset + symbol_len {
@@ -318,7 +340,7 @@ impl BonkEventParser {
let symbol = String::from_utf8(data[*offset..*offset + symbol_len].to_vec()).ok()?;
*offset += symbol_len;
// 读取uri字符串长度和内容
// Read uri string length and content
let uri_len = read_u32_le(data, *offset)? as usize;
*offset += 4;
if data.len() < *offset + uri_len {
@@ -327,17 +349,12 @@ impl BonkEventParser {
let uri = String::from_utf8(data[*offset..*offset + uri_len].to_vec()).ok()?;
*offset += uri_len;
Some(MintParams {
decimals,
name,
symbol,
uri,
})
Some(MintParams { decimals, name, symbol, uri })
}
/// 解析 CurveParams 结构
/// Parse CurveParams structure
fn parse_curve_params(data: &[u8], offset: &mut usize) -> Option<CurveParams> {
// 读取curve类型标识符 (1字节)
// Read curve type identifier (1 byte)
let curve_type = read_u8(data, *offset)?;
*offset += 1;
@@ -372,11 +389,7 @@ impl BonkEventParser {
*offset += 1;
Some(CurveParams::Fixed {
data: FixedCurve {
supply,
total_quote_fund_raising,
migrate_type,
},
data: FixedCurve { supply, total_quote_fund_raising, migrate_type },
})
}
2 => {
@@ -389,18 +402,14 @@ impl BonkEventParser {
*offset += 1;
Some(CurveParams::Linear {
data: LinearCurve {
supply,
total_quote_fund_raising,
migrate_type,
},
data: LinearCurve { supply, total_quote_fund_raising, migrate_type },
})
}
_ => None,
}
}
/// 解析 VestingParams 结构
/// Parse VestingParams structure
fn parse_vesting_params(data: &[u8], offset: &mut usize) -> Option<VestingParams> {
let total_locked_amount = read_u64_le(data, *offset)?;
*offset += 8;
@@ -409,11 +418,109 @@ impl BonkEventParser {
let unlock_period = read_u64_le(data, *offset)?;
*offset += 8;
Some(VestingParams {
total_locked_amount,
cliff_period,
unlock_period,
})
Some(VestingParams { total_locked_amount, cliff_period, unlock_period })
}
/// Parse migrate to AMM event
fn parse_migrate_to_amm_instruction(
data: &[u8],
accounts: &[Pubkey],
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
if data.len() < 16 {
return None;
}
let base_lot_size = u64::from_le_bytes(data[0..8].try_into().unwrap());
let quote_lot_size = u64::from_le_bytes(data[8..16].try_into().unwrap());
let market_vault_signer_nonce = data[16];
let mut metadata = metadata;
metadata.set_id(metadata.signature.to_string());
Some(Box::new(BonkMigrateToAmmEvent {
metadata,
base_lot_size,
quote_lot_size,
market_vault_signer_nonce,
payer: accounts[0],
base_mint: accounts[1],
quote_mint: accounts[2],
openbook_program: accounts[3],
market: accounts[4],
request_queue: accounts[5],
event_queue: accounts[6],
bids: accounts[7],
asks: accounts[8],
market_vault_signer: accounts[9],
market_base_vault: accounts[10],
market_quote_vault: accounts[11],
amm_program: accounts[12],
amm_pool: accounts[13],
amm_authority: accounts[14],
amm_open_orders: accounts[15],
amm_lp_mint: accounts[16],
amm_base_vault: accounts[17],
amm_quote_vault: accounts[18],
amm_target_orders: accounts[19],
amm_config: accounts[20],
amm_create_fee_destination: accounts[21],
authority: accounts[22],
pool_state: accounts[23],
global_config: accounts[24],
base_vault: accounts[25],
quote_vault: accounts[26],
pool_lp_token: accounts[27],
spl_token_program: accounts[28],
associated_token_program: accounts[29],
system_program: accounts[30],
rent_program: accounts[31],
..Default::default()
}))
}
/// Parse migrate to CP Swap event
fn parse_migrate_to_cpswap_instruction(
_data: &[u8],
accounts: &[Pubkey],
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
let mut metadata = metadata;
metadata.set_id(metadata.signature.to_string());
Some(Box::new(BonkMigrateToCpswapEvent {
metadata,
payer: accounts[0],
base_mint: accounts[1],
quote_mint: accounts[2],
platform_config: accounts[3],
cpswap_program: accounts[4],
cpswap_pool: accounts[5],
cpswap_authority: accounts[6],
cpswap_lp_mint: accounts[7],
cpswap_base_vault: accounts[8],
cpswap_quote_vault: accounts[9],
cpswap_config: accounts[10],
cpswap_create_pool_fee: accounts[11],
cpswap_observation: accounts[12],
lock_program: accounts[13],
lock_authority: accounts[14],
lock_lp_vault: accounts[15],
authority: accounts[16],
pool_state: accounts[17],
global_config: accounts[18],
base_vault: accounts[19],
quote_vault: accounts[20],
pool_lp_token: accounts[21],
base_token_program: accounts[22],
quote_token_program: accounts[23],
associated_token_program: accounts[24],
system_program: accounts[25],
rent_program: accounts[26],
metadata_program: accounts[27],
remaining_accounts: accounts[28..].to_vec(),
..Default::default()
}))
}
}