mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-11-02 07:16:31 +00:00
feat(pumpfun): preserve creation ALT keys through streamer bridge
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@ sdk-ultra-perf = ["sol-parser-sdk/ultra-perf"]
|
||||
|
||||
[dependencies]
|
||||
# Unreleased parser API; replace the Git pin with the next registry release before publishing.
|
||||
sol-parser-sdk = { version = "=0.10.0", git = "https://github.com/0xfnzero/sol-parser-sdk", rev = "0e32287e4056113ff047e2199dc1b5e8289e8f98", default-features = false }
|
||||
sol-parser-sdk = { version = "=0.10.0", git = "https://github.com/0xfnzero/sol-parser-sdk", rev = "e3e8d6620e2a128c71d5c37cde219a38d438c73e", default-features = false }
|
||||
solana-sdk = "=4.0.1"
|
||||
solana-client = "=4.2.2"
|
||||
solana-transaction-status = { version = "=4.2.2", features = ["agave-unstable-api"] }
|
||||
|
||||
@@ -303,6 +303,22 @@ account value is also available from
|
||||
|
||||
`PumpFunCreateTokenEvent.transaction_version` reports the transaction message version as `Some(YellowstoneMessageVersion::Legacy)`, `V0` or `V1` on Shred/gRPC/RPC. JSON uses `"legacy"`, `"v0"` and `"v1"`. It remains `None` without transaction context (for example, parsing an isolated event log). This is independent of the Pump instruction name in `ix_name`.
|
||||
|
||||
Pump.fun creation events expose `alt_addresses: Option<Vec<Pubkey>>`: only the
|
||||
`MessageAddressTableLookup.account_key` values for the whole transaction, in
|
||||
message order, including repeated keys. This is not the list of loaded addresses
|
||||
or a claim that every table is used by the creation instruction. It is `None`
|
||||
when there are no lookups or no transaction context. Reading it requires no ALT
|
||||
resolution or RPC; use it to prioritize cache warming outside the receive task.
|
||||
Only creation events with nonempty lookups allocate a list.
|
||||
|
||||
```rust
|
||||
if let Some(tables) = &create.alt_addresses {
|
||||
for address in tables {
|
||||
println!("ALT table: {address}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a id="trade-provenance-and-parsing-boundaries"></a>
|
||||
|
||||
## Trade provenance and parsing boundaries
|
||||
|
||||
@@ -283,6 +283,20 @@ log-only 的 `CreatePoolEvent` payload 因为 IDL 不包含该字段,会保持
|
||||
|
||||
`PumpFunCreateTokenEvent.transaction_version` 在 Shred/gRPC/RPC 路径下表示交易消息版本:`Some(YellowstoneMessageVersion::Legacy)`、`V0` 或 `V1`,JSON 分别为 `"legacy"`、`"v0"`、`"v1"`。缺少交易上下文时(例如单独解析一条事件日志)为 `None`。该字段与 `ix_name` 表示的 Pump 指令版本相互独立。
|
||||
|
||||
Pump.fun 创建事件提供 `alt_addresses: Option<Vec<Pubkey>>`,仅包含整笔交易的
|
||||
`MessageAddressTableLookup.account_key`,保留消息顺序和重复项。它不是表内加载的
|
||||
账户列表,也不代表每张表都用于创建指令;没有 ALT 或缺少交易上下文时为 `None`。
|
||||
读取该字段无需展开 ALT,也不调用 RPC;使用者可据此在接收任务之外安排缓存预热。
|
||||
仅带非空 ALT 列表的创建事件会分配列表。
|
||||
|
||||
```rust
|
||||
if let Some(tables) = &create.alt_addresses {
|
||||
for address in tables {
|
||||
println!("ALT table: {address}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a id="交易来源与解析边界"></a>
|
||||
|
||||
## 交易来源与解析边界
|
||||
|
||||
@@ -93,6 +93,7 @@ fn print_trade(e: &PumpFunTradeEvent, now_us: i64) {
|
||||
|
||||
fn print_create_legacy(e: &PumpFunCreateTokenEvent, now_us: i64) {
|
||||
println!("Transaction version: {:?}", e.transaction_version);
|
||||
println!("ALT table addresses: {:?}", e.alt_addresses);
|
||||
let latency_us = now_us - e.metadata.recv_us;
|
||||
println!(
|
||||
"│ CREATE | sig={:.8}.. | name={} symbol={} | mint={:.8}.. | latency={} μs",
|
||||
@@ -101,6 +102,7 @@ fn print_create_legacy(e: &PumpFunCreateTokenEvent, now_us: i64) {
|
||||
}
|
||||
|
||||
fn print_create_v2(e: &PumpFunCreateV2TokenEvent, now_us: i64) {
|
||||
println!("ALT table addresses: {:?}", e.alt_addresses);
|
||||
let latency_us = now_us - e.metadata.recv_us;
|
||||
println!(
|
||||
"│ CREATE_V2 | sig={:.8}.. | name={} symbol={} | mint={:.8}.. | latency={} μs",
|
||||
|
||||
@@ -7,6 +7,11 @@ use crate::streaming::event_parser::protocols::pumpfun::types::{BondingCurve, Gl
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpFunCreateTokenEvent {
|
||||
/// ALT account keys referenced by the whole transaction, in message order.
|
||||
/// None when there are no lookups or transaction context is unavailable.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub alt_addresses: Option<Vec<Pubkey>>,
|
||||
/// Transaction message version from Shred/gRPC/RPC context, independent of create/create_v2.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
@@ -91,6 +96,11 @@ pub struct PumpFunCreateTokenEvent {
|
||||
/// CreateV2 事件:与 create_v2 指令 16 个账户一致(见 parser 注释)。
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpFunCreateV2TokenEvent {
|
||||
/// ALT account keys referenced by the whole transaction, in message order.
|
||||
/// None when there are no lookups or transaction context is unavailable.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub alt_addresses: Option<Vec<Pubkey>>,
|
||||
#[borsh(skip)]
|
||||
pub metadata: EventMetadata,
|
||||
pub name: String,
|
||||
|
||||
@@ -339,6 +339,7 @@ mod tests {
|
||||
let quote_vault = Pubkey::new_unique();
|
||||
let quote_token_program = Pubkey::new_unique();
|
||||
let c = PbPumpCreateV2 {
|
||||
alt_addresses: Some(vec![global, event_authority]),
|
||||
transaction_version: Some(sol_parser_sdk::YellowstoneMessageVersion::V1),
|
||||
metadata: EventMetadata {
|
||||
signature: Signature::default(),
|
||||
@@ -366,6 +367,7 @@ mod tests {
|
||||
let ev = convert_parser_event(PbDexEvent::PumpFunCreateV2(c), None, 999).expect("convert");
|
||||
match ev {
|
||||
DexEvent::PumpFunCreateTokenEvent(st) => {
|
||||
assert_eq!(st.alt_addresses, Some(vec![global, event_authority]));
|
||||
assert_eq!(
|
||||
st.transaction_version,
|
||||
Some(sol_parser_sdk::YellowstoneMessageVersion::V1)
|
||||
|
||||
@@ -36,6 +36,7 @@ pub(crate) fn pumpfun_create_token_from_parser(
|
||||
meta: EventMetadata,
|
||||
) -> PumpFunCreateTokenEvent {
|
||||
PumpFunCreateTokenEvent {
|
||||
alt_addresses: c.alt_addresses,
|
||||
transaction_version: c.transaction_version,
|
||||
metadata: meta,
|
||||
name: c.name,
|
||||
@@ -83,6 +84,7 @@ pub(crate) fn pumpfun_create_token_from_parser_v2(
|
||||
meta: EventMetadata,
|
||||
) -> PumpFunCreateTokenEvent {
|
||||
PumpFunCreateTokenEvent {
|
||||
alt_addresses: c.alt_addresses,
|
||||
transaction_version: c.transaction_version,
|
||||
metadata: meta,
|
||||
name: c.name,
|
||||
|
||||
@@ -27,6 +27,10 @@ async fn reported_alt_creates_and_multi_hop_survive_the_streamer_facade() {
|
||||
serde_json::from_value(case["wire_rpc"].clone()).unwrap();
|
||||
let signed = rpc.transaction.transaction.decode().unwrap();
|
||||
signed.verify_and_hash_message().unwrap();
|
||||
let expected_alt_addresses =
|
||||
signed.message.address_table_lookups().filter(|lookups| !lookups.is_empty()).map(
|
||||
|lookups| lookups.iter().map(|lookup| lookup.account_key).collect::<Vec<_>>(),
|
||||
);
|
||||
let captured = Arc::new(Mutex::new(Vec::new()));
|
||||
let output = captured.clone();
|
||||
process_shred_transaction(
|
||||
@@ -45,6 +49,7 @@ async fn reported_alt_creates_and_multi_hop_survive_the_streamer_facade() {
|
||||
};
|
||||
let DexEvent::PumpFunTradeEvent(trade) = &events[1] else { panic!("trade missing") };
|
||||
assert_eq!(create.ix_name, "create_v2");
|
||||
assert_eq!(create.alt_addresses, expected_alt_addresses);
|
||||
assert_eq!(create.mint, trade.mint);
|
||||
assert!(trade.is_created_buy && trade.is_buy);
|
||||
for event in &*events {
|
||||
@@ -100,6 +105,10 @@ async fn reported_alt_creates_and_multi_hop_survive_the_streamer_facade() {
|
||||
..Default::default()
|
||||
}, &[Protocol::PumpFun], Some(&filter), None);
|
||||
for executed in [rpc_events, grpc_events] {
|
||||
let DexEvent::PumpFunCreateTokenEvent(c) = &executed[0] else {
|
||||
panic!("create event")
|
||||
};
|
||||
assert_eq!(c.alt_addresses, expected_alt_addresses);
|
||||
assert!(
|
||||
matches!(&executed[0], DexEvent::PumpFunCreateTokenEvent(c) if c.ix_name == "create_v2"),
|
||||
"{} mode {mode}: {:?}",
|
||||
|
||||
Reference in New Issue
Block a user