sol-parser-sdk-python 0.5.5__py3-none-any.whl → 0.5.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sol_parser/__init__.py CHANGED
@@ -6,6 +6,7 @@ from .parser import (
6
6
  StreamingEventListener,
7
7
  parse_log,
8
8
  parse_log_optimized,
9
+ parse_log_optimized_with_program_id,
9
10
  parse_log_unified,
10
11
  parse_logs_only,
11
12
  parse_logs_streaming,
@@ -142,6 +143,9 @@ from .event_types import (
142
143
  MeteoraDammV2AddLiquidityEvent,
143
144
  MeteoraDammV2RemoveLiquidityEvent,
144
145
  MeteoraDammV2InitializePoolEvent,
146
+ MeteoraDbcCurveCompleteEvent,
147
+ MeteoraDbcInitializePoolEvent,
148
+ MeteoraDbcSwapEvent,
145
149
  # RaydiumLaunchlab events
146
150
  RaydiumLaunchlabTradeEvent,
147
151
  RaydiumLaunchlabPoolCreateEvent,
@@ -235,6 +239,7 @@ __all__ = [
235
239
  "enrich_dex_events_with_subscribe_tx_info",
236
240
  "parse_log_unified",
237
241
  "parse_log_optimized",
242
+ "parse_log_optimized_with_program_id",
238
243
  "parse_log",
239
244
  "parse_logs_only",
240
245
  "parse_logs_streaming",
@@ -409,6 +414,9 @@ __all__ = [
409
414
  "MeteoraDammV2AddLiquidityEvent",
410
415
  "MeteoraDammV2RemoveLiquidityEvent",
411
416
  "MeteoraDammV2InitializePoolEvent",
417
+ "MeteoraDbcCurveCompleteEvent",
418
+ "MeteoraDbcInitializePoolEvent",
419
+ "MeteoraDbcSwapEvent",
412
420
  "RaydiumLaunchlabTradeEvent",
413
421
  "RaydiumLaunchlabPoolCreateEvent",
414
422
  "RaydiumLaunchlabMigrateAmmEvent",
@@ -13,6 +13,14 @@ from ..dex_parsers import DexEvent
13
13
 
14
14
  from . import rpc_wallet
15
15
  from . import utils as acc_utils
16
+ from .raydium_orca import (
17
+ ORCA_WHIRLPOOL_PROGRAM_ID,
18
+ RAYDIUM_CLMM_PROGRAM_ID,
19
+ RAYDIUM_CPMM_PROGRAM_ID,
20
+ parse_orca_whirlpool_account,
21
+ parse_raydium_clmm_account,
22
+ parse_raydium_cpmm_account,
23
+ )
16
24
 
17
25
  # 程序 ID(与 Rust ``accounts/program_ids`` / ``instr/program_ids`` 一致)
18
26
  PUMPFUN_PROGRAM_ID = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
@@ -173,6 +181,16 @@ ACCOUNT_EVENT_TYPES = frozenset(
173
181
  EventType.ACCOUNT_PUMP_FUN_USER_VOLUME_ACCUMULATOR,
174
182
  EventType.ACCOUNT_PUMP_SWAP_GLOBAL_CONFIG,
175
183
  EventType.ACCOUNT_PUMP_SWAP_POOL,
184
+ EventType.ACCOUNT_RAYDIUM_CLMM_AMM_CONFIG,
185
+ EventType.ACCOUNT_RAYDIUM_CLMM_POOL_STATE,
186
+ EventType.ACCOUNT_RAYDIUM_CLMM_TICK_ARRAY_STATE,
187
+ EventType.ACCOUNT_RAYDIUM_CPMM_AMM_CONFIG,
188
+ EventType.ACCOUNT_RAYDIUM_CPMM_POOL_STATE,
189
+ EventType.ACCOUNT_ORCA_WHIRLPOOL,
190
+ EventType.ACCOUNT_ORCA_POSITION,
191
+ EventType.ACCOUNT_ORCA_TICK_ARRAY,
192
+ EventType.ACCOUNT_ORCA_FEE_TIER,
193
+ EventType.ACCOUNT_ORCA_WHIRLPOOLS_CONFIG,
176
194
  )
177
195
  )
178
196
 
@@ -219,6 +237,43 @@ def parse_account_unified(
219
237
  return _filter_account_event(ev, event_type_filter)
220
238
  return None
221
239
 
240
+ if account.owner == RAYDIUM_CLMM_PROGRAM_ID:
241
+ should_parse_clmm = event_type_filter is None or (
242
+ event_type_filter.should_include(EventType.ACCOUNT_RAYDIUM_CLMM_AMM_CONFIG)
243
+ or event_type_filter.should_include(EventType.ACCOUNT_RAYDIUM_CLMM_POOL_STATE)
244
+ or event_type_filter.should_include(EventType.ACCOUNT_RAYDIUM_CLMM_TICK_ARRAY_STATE)
245
+ )
246
+ if should_parse_clmm:
247
+ ev = parse_raydium_clmm_account(account, metadata)
248
+ if ev is not None:
249
+ return _filter_account_event(ev, event_type_filter)
250
+ return None
251
+
252
+ if account.owner == RAYDIUM_CPMM_PROGRAM_ID:
253
+ should_parse_cpmm = event_type_filter is None or (
254
+ event_type_filter.should_include(EventType.ACCOUNT_RAYDIUM_CPMM_AMM_CONFIG)
255
+ or event_type_filter.should_include(EventType.ACCOUNT_RAYDIUM_CPMM_POOL_STATE)
256
+ )
257
+ if should_parse_cpmm:
258
+ ev = parse_raydium_cpmm_account(account, metadata)
259
+ if ev is not None:
260
+ return _filter_account_event(ev, event_type_filter)
261
+ return None
262
+
263
+ if account.owner == ORCA_WHIRLPOOL_PROGRAM_ID:
264
+ should_parse_orca = event_type_filter is None or (
265
+ event_type_filter.should_include(EventType.ACCOUNT_ORCA_WHIRLPOOL)
266
+ or event_type_filter.should_include(EventType.ACCOUNT_ORCA_POSITION)
267
+ or event_type_filter.should_include(EventType.ACCOUNT_ORCA_TICK_ARRAY)
268
+ or event_type_filter.should_include(EventType.ACCOUNT_ORCA_FEE_TIER)
269
+ or event_type_filter.should_include(EventType.ACCOUNT_ORCA_WHIRLPOOLS_CONFIG)
270
+ )
271
+ if should_parse_orca:
272
+ ev = parse_orca_whirlpool_account(account, metadata)
273
+ if ev is not None:
274
+ return _filter_account_event(ev, event_type_filter)
275
+ return None
276
+
222
277
  if acc_utils.is_nonce_account(data):
223
278
  if event_type_filter is not None:
224
279
  if not event_type_filter.should_include(EventType.NONCE_ACCOUNT):
@@ -848,12 +903,18 @@ __all__ = [
848
903
  "is_pumpfun_global_account",
849
904
  "parse_pumpswap_global_config",
850
905
  "parse_pumpswap_pool",
906
+ "parse_raydium_clmm_account",
907
+ "parse_raydium_cpmm_account",
908
+ "parse_orca_whirlpool_account",
851
909
  "is_global_config_account",
852
910
  "is_pool_account",
853
911
  "has_discriminator",
854
912
  "PUMPFUN_PROGRAM_ID",
855
913
  "PUMP_FEES_PROGRAM_ID",
856
914
  "PUMPSWAP_PROGRAM_ID",
915
+ "RAYDIUM_CLMM_PROGRAM_ID",
916
+ "RAYDIUM_CPMM_PROGRAM_ID",
917
+ "ORCA_WHIRLPOOL_PROGRAM_ID",
857
918
  "rpc_resolve_user_wallet_pubkey",
858
919
  "user_wallet_pubkey_for_onchain_account",
859
920
  "rpc_wallet",
@@ -0,0 +1,515 @@
1
+ """Raydium CLMM/CPMM and Orca Whirlpool account parsers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import struct
6
+ from typing import Any, Callable, Optional, TypeVar
7
+
8
+ import base58
9
+
10
+ from ..dex_parsers import DexEvent
11
+ from ..grpc_types import EventMetadata, EventType
12
+
13
+ RAYDIUM_CLMM_PROGRAM_ID = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK"
14
+ RAYDIUM_CPMM_PROGRAM_ID = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"
15
+ ORCA_WHIRLPOOL_PROGRAM_ID = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"
16
+
17
+ CLMM_AMM_CONFIG_DISC = bytes([218, 244, 33, 104, 203, 203, 43, 111])
18
+ CLMM_POOL_STATE_DISC = bytes([247, 237, 227, 245, 215, 195, 222, 70])
19
+ CLMM_TICK_ARRAY_STATE_DISC = bytes([192, 155, 85, 205, 49, 249, 129, 42])
20
+ CLMM_AMM_CONFIG_BODY = 109
21
+ CLMM_POOL_STATE_BODY = 1536
22
+ CLMM_TICK_ARRAY_STATE_BODY = 10232
23
+ CLMM_TICK_ARRAY_LEN = 60
24
+
25
+ CPMM_AMM_CONFIG_DISC = CLMM_AMM_CONFIG_DISC
26
+ CPMM_POOL_STATE_DISC = CLMM_POOL_STATE_DISC
27
+ CPMM_AMM_CONFIG_BODY = 228
28
+ CPMM_POOL_STATE_BODY = 629
29
+
30
+ ORCA_WHIRLPOOL_DISC = bytes([63, 149, 209, 12, 225, 128, 99, 9])
31
+ ORCA_POSITION_DISC = bytes([170, 188, 143, 228, 122, 64, 247, 208])
32
+ ORCA_TICK_ARRAY_DISC = bytes([69, 97, 189, 190, 110, 7, 66, 187])
33
+ ORCA_FEE_TIER_DISC = bytes([56, 75, 159, 76, 142, 68, 190, 105])
34
+ ORCA_WHIRLPOOLS_CONFIG_DISC = bytes([157, 20, 49, 224, 217, 87, 193, 254])
35
+ ORCA_WHIRLPOOL_BODY = 645
36
+ ORCA_POSITION_BODY = 208
37
+ ORCA_TICK_ARRAY_BODY = 9980
38
+ ORCA_FEE_TIER_BODY = 36
39
+ ORCA_WHIRLPOOLS_CONFIG_BODY = 98
40
+ ORCA_TICK_ARRAY_LEN = 88
41
+
42
+ T = TypeVar("T")
43
+
44
+
45
+ class Reader:
46
+ def __init__(self, data: bytes):
47
+ self.data = data
48
+ self.offset = 0
49
+
50
+ def _take(self, size: int) -> int:
51
+ if self.offset + size > len(self.data):
52
+ raise ValueError("account data is truncated")
53
+ start = self.offset
54
+ self.offset += size
55
+ return start
56
+
57
+ def u8(self) -> int:
58
+ return self.data[self._take(1)]
59
+
60
+ def bool(self) -> bool:
61
+ return self.u8() != 0
62
+
63
+ def u16(self) -> int:
64
+ return struct.unpack_from("<H", self.data, self._take(2))[0]
65
+
66
+ def u32(self) -> int:
67
+ return struct.unpack_from("<I", self.data, self._take(4))[0]
68
+
69
+ def i32(self) -> int:
70
+ return struct.unpack_from("<i", self.data, self._take(4))[0]
71
+
72
+ def u64(self) -> int:
73
+ return struct.unpack_from("<Q", self.data, self._take(8))[0]
74
+
75
+ def u128(self) -> int:
76
+ start = self._take(16)
77
+ return int.from_bytes(self.data[start : start + 16], "little", signed=False)
78
+
79
+ def i128(self) -> int:
80
+ start = self._take(16)
81
+ return int.from_bytes(self.data[start : start + 16], "little", signed=True)
82
+
83
+ def pubkey(self) -> str:
84
+ start = self._take(32)
85
+ return base58.b58encode(self.data[start : start + 32]).decode("ascii")
86
+
87
+ def bytes(self, length: int) -> list[int]:
88
+ start = self._take(length)
89
+ return list(self.data[start : start + length])
90
+
91
+
92
+ def _event(event_type: EventType, data: dict[str, Any]) -> DexEvent:
93
+ return DexEvent(type=event_type, data=data)
94
+
95
+
96
+ def _parse_body(data: bytes, parse: Callable[[Reader], T]) -> Optional[T]:
97
+ try:
98
+ return parse(Reader(data))
99
+ except (IndexError, struct.error, ValueError):
100
+ return None
101
+
102
+
103
+ def _body_after_discriminator(data: bytes, disc: bytes, body_size: int) -> Optional[bytes]:
104
+ if len(data) < 8 + body_size or not data.startswith(disc):
105
+ return None
106
+ return data[8:]
107
+
108
+
109
+ def _array(count: int, read: Callable[[], T]) -> list[T]:
110
+ return [read() for _ in range(count)]
111
+
112
+
113
+ def parse_raydium_clmm_account(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
114
+ if account.owner != RAYDIUM_CLMM_PROGRAM_ID:
115
+ return None
116
+ return (
117
+ parse_raydium_clmm_amm_config(account, metadata)
118
+ or parse_raydium_clmm_pool_state(account, metadata)
119
+ or parse_raydium_clmm_tick_array_state(account, metadata)
120
+ )
121
+
122
+
123
+ def parse_raydium_cpmm_account(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
124
+ if account.owner != RAYDIUM_CPMM_PROGRAM_ID:
125
+ return None
126
+ return parse_raydium_cpmm_amm_config(account, metadata) or parse_raydium_cpmm_pool_state(
127
+ account, metadata
128
+ )
129
+
130
+
131
+ def parse_orca_whirlpool_account(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
132
+ if account.owner != ORCA_WHIRLPOOL_PROGRAM_ID:
133
+ return None
134
+ return (
135
+ parse_orca_whirlpool(account, metadata)
136
+ or parse_orca_position(account, metadata)
137
+ or parse_orca_tick_array(account, metadata)
138
+ or parse_orca_fee_tier(account, metadata)
139
+ or parse_orca_whirlpools_config(account, metadata)
140
+ )
141
+
142
+
143
+ def parse_raydium_clmm_amm_config(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
144
+ body = _body_after_discriminator(account.data, CLMM_AMM_CONFIG_DISC, CLMM_AMM_CONFIG_BODY)
145
+ if body is None:
146
+ return None
147
+ amm_config = _parse_body(
148
+ body,
149
+ lambda r: {
150
+ "bump": r.u8(),
151
+ "index": r.u16(),
152
+ "owner": r.pubkey(),
153
+ "protocol_fee_rate": r.u32(),
154
+ "trade_fee_rate": r.u32(),
155
+ "tick_spacing": r.u16(),
156
+ "fund_fee_rate": r.u32(),
157
+ "padding_u32": r.u32(),
158
+ "fund_owner": r.pubkey(),
159
+ "padding": _array(3, r.u64),
160
+ },
161
+ )
162
+ if amm_config is None:
163
+ return None
164
+ return _event(
165
+ EventType.ACCOUNT_RAYDIUM_CLMM_AMM_CONFIG,
166
+ {"metadata": metadata, "pubkey": account.pubkey, "amm_config": amm_config},
167
+ )
168
+
169
+
170
+ def parse_raydium_clmm_pool_state(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
171
+ body = _body_after_discriminator(account.data, CLMM_POOL_STATE_DISC, CLMM_POOL_STATE_BODY)
172
+ if body is None:
173
+ return None
174
+ pool_state = _parse_body(body, _read_clmm_pool_state)
175
+ if pool_state is None:
176
+ return None
177
+ return _event(
178
+ EventType.ACCOUNT_RAYDIUM_CLMM_POOL_STATE,
179
+ {"metadata": metadata, "pubkey": account.pubkey, "pool_state": pool_state},
180
+ )
181
+
182
+
183
+ def _read_clmm_pool_state(r: Reader) -> dict[str, Any]:
184
+ return {
185
+ "bump": [r.u8()],
186
+ "amm_config": r.pubkey(),
187
+ "owner": r.pubkey(),
188
+ "token_mint_0": r.pubkey(),
189
+ "token_mint_1": r.pubkey(),
190
+ "token_vault_0": r.pubkey(),
191
+ "token_vault_1": r.pubkey(),
192
+ "observation_key": r.pubkey(),
193
+ "mint_decimals_0": r.u8(),
194
+ "mint_decimals_1": r.u8(),
195
+ "tick_spacing": r.u16(),
196
+ "liquidity": r.u128(),
197
+ "sqrt_price_x64": r.u128(),
198
+ "tick_current": r.i32(),
199
+ "padding3": r.u16(),
200
+ "padding4": r.u16(),
201
+ "fee_growth_global_0_x64": r.u128(),
202
+ "fee_growth_global_1_x64": r.u128(),
203
+ "protocol_fees_token_0": r.u64(),
204
+ "protocol_fees_token_1": r.u64(),
205
+ "padding5": _array(4, r.u128),
206
+ "status": r.u8(),
207
+ "fee_on": r.u8(),
208
+ "padding": r.bytes(6),
209
+ "reward_infos": _array(3, lambda: _read_clmm_reward_info(r)),
210
+ "tick_array_bitmap": _array(16, r.u64),
211
+ "padding6": _array(4, r.u64),
212
+ "fund_fees_token_0": r.u64(),
213
+ "fund_fees_token_1": r.u64(),
214
+ "open_time": r.u64(),
215
+ "recent_epoch": r.u64(),
216
+ "dynamic_fee_info": _read_clmm_dynamic_fee_info(r),
217
+ "padding1": _array(14, r.u64),
218
+ "padding2": _array(32, r.u64),
219
+ }
220
+
221
+
222
+ def parse_raydium_clmm_tick_array_state(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
223
+ body = _body_after_discriminator(account.data, CLMM_TICK_ARRAY_STATE_DISC, CLMM_TICK_ARRAY_STATE_BODY)
224
+ if body is None:
225
+ return None
226
+ tick_array_state = _parse_body(
227
+ body,
228
+ lambda r: {
229
+ "pool_id": r.pubkey(),
230
+ "start_tick_index": r.i32(),
231
+ "ticks": _array(CLMM_TICK_ARRAY_LEN, lambda: _read_clmm_tick(r)),
232
+ "initialized_tick_count": r.u8(),
233
+ "recent_epoch": r.u64(),
234
+ "padding": r.bytes(107),
235
+ },
236
+ )
237
+ if tick_array_state is None:
238
+ return None
239
+ return _event(
240
+ EventType.ACCOUNT_RAYDIUM_CLMM_TICK_ARRAY_STATE,
241
+ {"metadata": metadata, "pubkey": account.pubkey, "tick_array_state": tick_array_state},
242
+ )
243
+
244
+
245
+ def _read_clmm_reward_info(r: Reader) -> dict[str, Any]:
246
+ return {
247
+ "reward_state": r.u8(),
248
+ "open_time": r.u64(),
249
+ "end_time": r.u64(),
250
+ "last_update_time": r.u64(),
251
+ "emissions_per_second_x64": r.u128(),
252
+ "reward_total_emitted": r.u64(),
253
+ "reward_claimed": r.u64(),
254
+ "token_mint": r.pubkey(),
255
+ "token_vault": r.pubkey(),
256
+ "authority": r.pubkey(),
257
+ "reward_growth_global_x64": r.u128(),
258
+ }
259
+
260
+
261
+ def _read_clmm_dynamic_fee_info(r: Reader) -> dict[str, Any]:
262
+ return {
263
+ "filter_period": r.u16(),
264
+ "decay_period": r.u16(),
265
+ "reduction_factor": r.u16(),
266
+ "dynamic_fee_control": r.u32(),
267
+ "max_volatility_accumulator": r.u32(),
268
+ "tick_spacing_index_reference": r.i32(),
269
+ "volatility_reference": r.u32(),
270
+ "volatility_accumulator": r.u32(),
271
+ "last_update_timestamp": r.u64(),
272
+ "padding": r.bytes(46),
273
+ }
274
+
275
+
276
+ def _read_clmm_tick(r: Reader) -> dict[str, Any]:
277
+ return {
278
+ "tick": r.i32(),
279
+ "liquidity_net": r.i128(),
280
+ "liquidity_gross": r.u128(),
281
+ "fee_growth_outside_0_x64": r.u128(),
282
+ "fee_growth_outside_1_x64": r.u128(),
283
+ "reward_growths_outside_x64": _array(3, r.u128),
284
+ "order_phase": r.u64(),
285
+ "orders_amount": r.u64(),
286
+ "part_filled_orders_remaining": r.u64(),
287
+ "unfilled_ratio_x64": r.u128(),
288
+ "padding": _array(3, r.u32),
289
+ }
290
+
291
+
292
+ def parse_raydium_cpmm_amm_config(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
293
+ body = _body_after_discriminator(account.data, CPMM_AMM_CONFIG_DISC, CPMM_AMM_CONFIG_BODY)
294
+ if body is None:
295
+ return None
296
+ amm_config = _parse_body(
297
+ body,
298
+ lambda r: {
299
+ "bump": r.u8(),
300
+ "disable_create_pool": r.bool(),
301
+ "index": r.u16(),
302
+ "trade_fee_rate": r.u64(),
303
+ "protocol_fee_rate": r.u64(),
304
+ "fund_fee_rate": r.u64(),
305
+ "create_pool_fee": r.u64(),
306
+ "protocol_owner": r.pubkey(),
307
+ "fund_owner": r.pubkey(),
308
+ "creator_fee_rate": r.u64(),
309
+ "padding": _array(15, r.u64),
310
+ },
311
+ )
312
+ if amm_config is None:
313
+ return None
314
+ return _event(
315
+ EventType.ACCOUNT_RAYDIUM_CPMM_AMM_CONFIG,
316
+ {"metadata": metadata, "pubkey": account.pubkey, "amm_config": amm_config},
317
+ )
318
+
319
+
320
+ def parse_raydium_cpmm_pool_state(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
321
+ body = _body_after_discriminator(account.data, CPMM_POOL_STATE_DISC, CPMM_POOL_STATE_BODY)
322
+ if body is None:
323
+ return None
324
+ pool_state = _parse_body(body, _read_cpmm_pool_state)
325
+ if pool_state is None:
326
+ return None
327
+ return _event(
328
+ EventType.ACCOUNT_RAYDIUM_CPMM_POOL_STATE,
329
+ {"metadata": metadata, "pubkey": account.pubkey, "pool_state": pool_state},
330
+ )
331
+
332
+
333
+ def _read_cpmm_pool_state(r: Reader) -> dict[str, Any]:
334
+ return {
335
+ "amm_config": r.pubkey(),
336
+ "pool_creator": r.pubkey(),
337
+ "token_0_vault": r.pubkey(),
338
+ "token_1_vault": r.pubkey(),
339
+ "lp_mint": r.pubkey(),
340
+ "token_0_mint": r.pubkey(),
341
+ "token_1_mint": r.pubkey(),
342
+ "token_0_program": r.pubkey(),
343
+ "token_1_program": r.pubkey(),
344
+ "observation_key": r.pubkey(),
345
+ "auth_bump": r.u8(),
346
+ "status": r.u8(),
347
+ "lp_mint_decimals": r.u8(),
348
+ "mint_0_decimals": r.u8(),
349
+ "mint_1_decimals": r.u8(),
350
+ "lp_supply": r.u64(),
351
+ "protocol_fees_token_0": r.u64(),
352
+ "protocol_fees_token_1": r.u64(),
353
+ "fund_fees_token_0": r.u64(),
354
+ "fund_fees_token_1": r.u64(),
355
+ "open_time": r.u64(),
356
+ "recent_epoch": r.u64(),
357
+ "creator_fee_on": r.u8(),
358
+ "enable_creator_fee": r.bool(),
359
+ "padding1": r.bytes(6),
360
+ "creator_fees_token_0": r.u64(),
361
+ "creator_fees_token_1": r.u64(),
362
+ "padding": _array(28, r.u64),
363
+ }
364
+
365
+
366
+ def parse_orca_whirlpool(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
367
+ body = _body_after_discriminator(account.data, ORCA_WHIRLPOOL_DISC, ORCA_WHIRLPOOL_BODY)
368
+ if body is None:
369
+ return None
370
+ whirlpool = _parse_body(
371
+ body,
372
+ lambda r: {
373
+ "whirlpools_config": r.pubkey(),
374
+ "whirlpool_bump": r.u8(),
375
+ "tick_spacing": r.u16(),
376
+ "tick_spacing_seed": r.bytes(2),
377
+ "fee_rate": r.u16(),
378
+ "protocol_fee_rate": r.u16(),
379
+ "liquidity": r.u128(),
380
+ "sqrt_price": r.u128(),
381
+ "tick_current_index": r.i32(),
382
+ "protocol_fee_owed_a": r.u64(),
383
+ "protocol_fee_owed_b": r.u64(),
384
+ "token_mint_a": r.pubkey(),
385
+ "token_vault_a": r.pubkey(),
386
+ "fee_growth_global_a": r.u128(),
387
+ "token_mint_b": r.pubkey(),
388
+ "token_vault_b": r.pubkey(),
389
+ "fee_growth_global_b": r.u128(),
390
+ "reward_last_updated_timestamp": r.u64(),
391
+ "reward_infos": _array(3, lambda: _read_orca_whirlpool_reward_info(r)),
392
+ },
393
+ )
394
+ if whirlpool is None:
395
+ return None
396
+ return _event(
397
+ EventType.ACCOUNT_ORCA_WHIRLPOOL,
398
+ {"metadata": metadata, "pubkey": account.pubkey, "whirlpool": whirlpool},
399
+ )
400
+
401
+
402
+ def parse_orca_position(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
403
+ body = _body_after_discriminator(account.data, ORCA_POSITION_DISC, ORCA_POSITION_BODY)
404
+ if body is None:
405
+ return None
406
+ position = _parse_body(
407
+ body,
408
+ lambda r: {
409
+ "whirlpool": r.pubkey(),
410
+ "position_mint": r.pubkey(),
411
+ "liquidity": r.u128(),
412
+ "tick_lower_index": r.i32(),
413
+ "tick_upper_index": r.i32(),
414
+ "fee_growth_checkpoint_a": r.u128(),
415
+ "fee_owed_a": r.u64(),
416
+ "fee_growth_checkpoint_b": r.u128(),
417
+ "fee_owed_b": r.u64(),
418
+ "reward_infos": _array(3, lambda: _read_orca_position_reward_info(r)),
419
+ },
420
+ )
421
+ if position is None:
422
+ return None
423
+ return _event(
424
+ EventType.ACCOUNT_ORCA_POSITION,
425
+ {"metadata": metadata, "pubkey": account.pubkey, "position": position},
426
+ )
427
+
428
+
429
+ def parse_orca_tick_array(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
430
+ body = _body_after_discriminator(account.data, ORCA_TICK_ARRAY_DISC, ORCA_TICK_ARRAY_BODY)
431
+ if body is None:
432
+ return None
433
+ tick_array = _parse_body(
434
+ body,
435
+ lambda r: {
436
+ "start_tick_index": r.i32(),
437
+ "ticks": _array(ORCA_TICK_ARRAY_LEN, lambda: _read_orca_tick(r)),
438
+ "whirlpool": r.pubkey(),
439
+ },
440
+ )
441
+ if tick_array is None:
442
+ return None
443
+ return _event(
444
+ EventType.ACCOUNT_ORCA_TICK_ARRAY,
445
+ {"metadata": metadata, "pubkey": account.pubkey, "tick_array": tick_array},
446
+ )
447
+
448
+
449
+ def parse_orca_fee_tier(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
450
+ body = _body_after_discriminator(account.data, ORCA_FEE_TIER_DISC, ORCA_FEE_TIER_BODY)
451
+ if body is None:
452
+ return None
453
+ fee_tier = _parse_body(
454
+ body,
455
+ lambda r: {
456
+ "whirlpools_config": r.pubkey(),
457
+ "tick_spacing": r.u16(),
458
+ "default_fee_rate": r.u16(),
459
+ },
460
+ )
461
+ if fee_tier is None:
462
+ return None
463
+ return _event(
464
+ EventType.ACCOUNT_ORCA_FEE_TIER,
465
+ {"metadata": metadata, "pubkey": account.pubkey, "fee_tier": fee_tier},
466
+ )
467
+
468
+
469
+ def parse_orca_whirlpools_config(account: Any, metadata: EventMetadata) -> Optional[DexEvent]:
470
+ body = _body_after_discriminator(account.data, ORCA_WHIRLPOOLS_CONFIG_DISC, ORCA_WHIRLPOOLS_CONFIG_BODY)
471
+ if body is None:
472
+ return None
473
+ config = _parse_body(
474
+ body,
475
+ lambda r: {
476
+ "fee_authority": r.pubkey(),
477
+ "collect_protocol_fees_authority": r.pubkey(),
478
+ "reward_emissions_super_authority": r.pubkey(),
479
+ "default_protocol_fee_rate": r.u16(),
480
+ },
481
+ )
482
+ if config is None:
483
+ return None
484
+ return _event(
485
+ EventType.ACCOUNT_ORCA_WHIRLPOOLS_CONFIG,
486
+ {"metadata": metadata, "pubkey": account.pubkey, "config": config},
487
+ )
488
+
489
+
490
+ def _read_orca_whirlpool_reward_info(r: Reader) -> dict[str, Any]:
491
+ return {
492
+ "mint": r.pubkey(),
493
+ "vault": r.pubkey(),
494
+ "authority": r.pubkey(),
495
+ "emissions_per_second_x64": r.u128(),
496
+ "growth_global_x64": r.u128(),
497
+ }
498
+
499
+
500
+ def _read_orca_position_reward_info(r: Reader) -> dict[str, Any]:
501
+ return {
502
+ "growth_inside_checkpoint": r.u128(),
503
+ "amount_owed": r.u64(),
504
+ }
505
+
506
+
507
+ def _read_orca_tick(r: Reader) -> dict[str, Any]:
508
+ return {
509
+ "initialized": r.bool(),
510
+ "liquidity_net": r.i128(),
511
+ "liquidity_gross": r.u128(),
512
+ "fee_growth_outside_a": r.u128(),
513
+ "fee_growth_outside_b": r.u128(),
514
+ "reward_growths_outside": _array(3, r.u128),
515
+ }