sol-parser-sdk-python 0.5.7__py3-none-any.whl → 0.5.9__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.
@@ -43,6 +43,7 @@ from .event_types import (
43
43
  PumpFunCreateV2TokenEvent,
44
44
  PumpFunTradeEvent,
45
45
  PumpSwapBuyEvent,
46
+ PumpSwapCreatePoolEvent,
46
47
  PumpSwapSellEvent,
47
48
  MeteoraDlmmAddLiquidityEvent,
48
49
  MeteoraDlmmClaimFeeEvent,
@@ -83,6 +84,7 @@ def _d(*xs: int) -> int:
83
84
  _DISC_PUMPSWAP_BUY = _d(102, 6, 61, 18, 1, 218, 235, 234)
84
85
  _DISC_PUMPSWAP_SELL = _d(51, 230, 133, 164, 1, 127, 131, 173)
85
86
  _DISC_PUMPSWAP_BUY_EXACT_QUOTE_IN = _d(198, 46, 21, 82, 180, 217, 232, 112)
87
+ _DISC_PUMPSWAP_CREATE_POOL = _d(233, 146, 209, 142, 207, 104, 64, 188)
86
88
 
87
89
  _DISC_DAMM_SWAP = _d(27, 60, 21, 213, 138, 170, 187, 147)
88
90
  _DISC_DAMM_SWAP2 = _d(189, 66, 51, 168, 38, 80, 117, 153)
@@ -97,6 +99,50 @@ _DISC_METEORA_POOLS_ADD_LIQUIDITY = _d(181, 157, 89, 67, 143, 182, 52, 72)
97
99
  _DISC_METEORA_POOLS_REMOVE_LIQUIDITY = _d(80, 85, 209, 72, 24, 206, 177, 108)
98
100
  _DISC_METEORA_POOLS_CREATE_POOL = _d(95, 180, 10, 172, 84, 174, 232, 40)
99
101
 
102
+ _DISC_DLMM_ADD_LIQUIDITY = _d(181, 157, 89, 67, 143, 182, 52, 72)
103
+ _DISC_DLMM_ADD_LIQUIDITY2 = _d(228, 162, 78, 28, 70, 219, 116, 115)
104
+ _DISC_DLMM_CLAIM_FEE = _d(169, 32, 79, 137, 136, 232, 70, 137)
105
+ _DISC_DLMM_CLAIM_FEE2 = _d(112, 191, 101, 171, 28, 144, 127, 187)
106
+ _DISC_DLMM_CLOSE_POSITION = _d(123, 134, 81, 0, 49, 68, 98, 98)
107
+ _DISC_DLMM_CLOSE_POSITION2 = _d(174, 90, 35, 115, 186, 40, 147, 226)
108
+ _DISC_DLMM_INITIALIZE_BIN_ARRAY = _d(35, 86, 19, 185, 78, 212, 75, 211)
109
+ _DISC_DLMM_INITIALIZE_LB_PAIR = _d(45, 154, 237, 210, 221, 15, 166, 92)
110
+ _DISC_DLMM_INITIALIZE_LB_PAIR2 = _d(73, 59, 36, 120, 237, 83, 108, 198)
111
+ _DISC_DLMM_INITIALIZE_POSITION = _d(219, 192, 234, 71, 190, 191, 102, 80)
112
+ _DISC_DLMM_INITIALIZE_POSITION2 = _d(143, 19, 242, 145, 213, 15, 104, 115)
113
+ _DISC_DLMM_INITIALIZE_POSITION_PDA = _d(46, 82, 125, 146, 85, 141, 228, 153)
114
+ _DISC_DLMM_REMOVE_LIQUIDITY = _d(80, 85, 209, 72, 24, 206, 177, 108)
115
+ _DISC_DLMM_REMOVE_LIQUIDITY2 = _d(230, 215, 82, 127, 241, 101, 227, 146)
116
+ _DISC_DLMM_SWAP = _d(248, 198, 158, 145, 225, 117, 135, 200)
117
+ _DISC_DLMM_SWAP2 = _d(65, 75, 63, 76, 235, 91, 91, 136)
118
+ _DISC_DLMM_SWAP_EXACT_OUT = _d(250, 73, 101, 33, 38, 207, 75, 184)
119
+ _DISC_DLMM_SWAP_EXACT_OUT2 = _d(43, 215, 247, 132, 137, 60, 243, 81)
120
+ _DISC_DLMM_SWAP_WITH_PRICE_IMPACT = _d(56, 173, 230, 208, 173, 228, 156, 205)
121
+ _DISC_DLMM_SWAP_WITH_PRICE_IMPACT2 = _d(74, 98, 192, 214, 177, 51, 75, 51)
122
+
123
+ _DLMM_INSTRUCTION_DISCS = (
124
+ _DISC_DLMM_INITIALIZE_LB_PAIR,
125
+ _DISC_DLMM_INITIALIZE_LB_PAIR2,
126
+ _DISC_DLMM_INITIALIZE_BIN_ARRAY,
127
+ _DISC_DLMM_ADD_LIQUIDITY,
128
+ _DISC_DLMM_ADD_LIQUIDITY2,
129
+ _DISC_DLMM_REMOVE_LIQUIDITY,
130
+ _DISC_DLMM_REMOVE_LIQUIDITY2,
131
+ _DISC_DLMM_INITIALIZE_POSITION,
132
+ _DISC_DLMM_INITIALIZE_POSITION2,
133
+ _DISC_DLMM_INITIALIZE_POSITION_PDA,
134
+ _DISC_DLMM_SWAP,
135
+ _DISC_DLMM_SWAP2,
136
+ _DISC_DLMM_SWAP_EXACT_OUT,
137
+ _DISC_DLMM_SWAP_EXACT_OUT2,
138
+ _DISC_DLMM_SWAP_WITH_PRICE_IMPACT,
139
+ _DISC_DLMM_SWAP_WITH_PRICE_IMPACT2,
140
+ _DISC_DLMM_CLAIM_FEE,
141
+ _DISC_DLMM_CLAIM_FEE2,
142
+ _DISC_DLMM_CLOSE_POSITION,
143
+ _DISC_DLMM_CLOSE_POSITION2,
144
+ )
145
+
100
146
  _DISC_CLMM_SWAP = _d(248, 198, 158, 145, 225, 117, 135, 200)
101
147
  _DISC_CLMM_SWAP_V2 = _d(43, 4, 237, 11, 26, 201, 30, 98)
102
148
  _DISC_CLMM_INC_LIQ = _d(133, 29, 89, 223, 69, 238, 176, 10)
@@ -322,9 +368,10 @@ def normal_instruction_data_may_parse(program_id: str, instruction_data: bytes)
322
368
  if not instruction_data:
323
369
  return False
324
370
  if program_id == RAYDIUM_AMM_V4_PROGRAM_ID:
325
- return instruction_data[0] in (1, 3, 4, 7, 9, 11)
371
+ return instruction_data[0] in (1, 3, 4, 7, 9, 11, 16, 17)
326
372
  if program_id == METEORA_DLMM_PROGRAM_ID:
327
- return instruction_data[0] in (0, 1, 2, 7, 8, 11, 13, 14)
373
+ disc = _disc8(instruction_data)
374
+ return disc in _DLMM_INSTRUCTION_DISCS if disc is not None else False
328
375
  disc = _disc8(instruction_data)
329
376
  if disc is None:
330
377
  return False
@@ -345,7 +392,7 @@ def normal_instruction_data_may_parse(program_id: str, instruction_data: bytes)
345
392
  return disc in (
346
393
  _DISC_PUMPSWAP_BUY,
347
394
  _DISC_PUMPSWAP_SELL,
348
- _d(233, 146, 209, 142, 207, 104, 64, 188),
395
+ _DISC_PUMPSWAP_CREATE_POOL,
349
396
  _DISC_PUMPSWAP_BUY_EXACT_QUOTE_IN,
350
397
  _d(242, 35, 198, 137, 82, 225, 242, 182),
351
398
  _d(183, 18, 70, 156, 148, 109, 161, 34),
@@ -764,6 +811,12 @@ def _parse_pumpfun_create_v2(data: bytes, accounts: List[str], meta: EventMetada
764
811
  is_mayhem_mode = data[offset] == 1
765
812
  offset += 1
766
813
  is_cashback_enabled = data[offset] == 1 if offset < len(data) else False
814
+ if offset < len(data):
815
+ offset += 1
816
+ creator_fee_bps = struct.unpack_from("<Q", data, offset)[0] if offset + 8 <= len(data) else 0
817
+ if offset + 8 <= len(data):
818
+ offset += 8
819
+ is_holder_reward = data[offset] == 1 if offset < len(data) else False
767
820
 
768
821
  return DexEvent(
769
822
  type=EventType.PUMP_FUN_CREATE_V2,
@@ -796,6 +849,8 @@ def _parse_pumpfun_create_v2(data: bytes, accounts: List[str], meta: EventMetada
796
849
  token_total_supply=0,
797
850
  is_mayhem_mode=is_mayhem_mode,
798
851
  is_cashback_enabled=is_cashback_enabled,
852
+ creator_fee_bps=creator_fee_bps,
853
+ is_holder_reward=is_holder_reward,
799
854
  quote_mint=_get_account_safe(accounts, 16) or Z,
800
855
  quote_vault=_get_account_safe(accounts, 17),
801
856
  quote_token_program=_get_account_safe(accounts, 18),
@@ -906,6 +961,38 @@ def parse_pumpswap_instruction(
906
961
  fill_sell_tail(ev)
907
962
  return DexEvent(type=EventType.PUMP_SWAP_SELL, data=ev)
908
963
 
964
+ if discriminator == _DISC_PUMPSWAP_CREATE_POOL:
965
+ if len(accounts) < 8:
966
+ return None
967
+ return DexEvent(
968
+ type=EventType.PUMP_SWAP_CREATE_POOL,
969
+ data=PumpSwapCreatePoolEvent(
970
+ metadata=meta,
971
+ index=struct.unpack_from("<H", payload, 0)[0] if len(payload) >= 2 else 0,
972
+ pool=_get_account_safe(accounts, 0),
973
+ creator=_get_account_safe(accounts, 2),
974
+ base_mint=_get_account_safe(accounts, 3),
975
+ quote_mint=_get_account_safe(accounts, 4),
976
+ base_amount_in=struct.unpack_from("<Q", payload, 2)[0] if len(payload) >= 10 else 0,
977
+ quote_amount_in=struct.unpack_from("<Q", payload, 10)[0] if len(payload) >= 18 else 0,
978
+ lp_mint=_get_account_safe(accounts, 5),
979
+ user_base_token_account=_get_account_safe(accounts, 6),
980
+ user_quote_token_account=_get_account_safe(accounts, 7),
981
+ coin_creator=(
982
+ base58.b58encode(payload[18:50]).decode("ascii")
983
+ if len(payload) >= 50
984
+ else Z
985
+ ),
986
+ is_mayhem_mode=len(payload) > 50 and payload[50] == 1,
987
+ is_cashback_coin=len(payload) > 51 and payload[51] == 1,
988
+ creator_fee_bps=(
989
+ struct.unpack_from("<Q", payload, 52)[0] if len(payload) >= 60 else 0
990
+ ),
991
+ can_edit_creator_fee=len(payload) > 60 and payload[60] == 1,
992
+ is_holder_reward=len(payload) > 61 and payload[61] == 1,
993
+ ),
994
+ )
995
+
909
996
  return None
910
997
 
911
998
 
@@ -1024,94 +1111,124 @@ def parse_meteora_dlmm_instruction(
1024
1111
  block_time_us: Optional[int],
1025
1112
  grpc_recv_us: int,
1026
1113
  ) -> Optional[DexEvent]:
1027
- if not data or not accounts:
1114
+ if len(data) < 8 or not accounts:
1028
1115
  return None
1029
- instruction_type = data[0]
1030
- payload = data[1:]
1116
+ discriminator = struct.unpack_from("<Q", data, 0)[0]
1117
+ payload = data[8:]
1031
1118
  meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
1032
- pool = accounts[0]
1033
1119
 
1034
- if instruction_type == 0:
1120
+ if discriminator == _DISC_DLMM_INITIALIZE_LB_PAIR:
1035
1121
  if len(payload) < 6:
1036
1122
  return None
1123
+ pool = _get_account_safe(accounts, 0)
1037
1124
  return DexEvent(
1038
1125
  type=EventType.METEORA_DLMM_INITIALIZE_POOL,
1039
1126
  data=MeteoraDlmmInitializePoolEvent(
1040
1127
  metadata=meta,
1041
1128
  pool=pool,
1042
- creator=_get_account_safe(accounts, 1),
1129
+ creator=_get_account_safe(accounts, 8),
1043
1130
  active_bin_id=struct.unpack_from("<i", payload, 0)[0],
1044
1131
  bin_step=struct.unpack_from("<H", payload, 4)[0],
1045
1132
  ),
1046
1133
  )
1047
1134
 
1048
- if instruction_type == 1:
1135
+ if discriminator == _DISC_DLMM_INITIALIZE_LB_PAIR2:
1136
+ if len(payload) < 4:
1137
+ return None
1138
+ pool = _get_account_safe(accounts, 0)
1139
+ return DexEvent(
1140
+ type=EventType.METEORA_DLMM_INITIALIZE_POOL,
1141
+ data=MeteoraDlmmInitializePoolEvent(
1142
+ metadata=meta,
1143
+ pool=pool,
1144
+ creator=_get_account_safe(accounts, 8),
1145
+ active_bin_id=struct.unpack_from("<i", payload, 0)[0],
1146
+ bin_step=0,
1147
+ ),
1148
+ )
1149
+
1150
+ if discriminator == _DISC_DLMM_INITIALIZE_BIN_ARRAY:
1049
1151
  if len(payload) < 8:
1050
1152
  return None
1153
+ pool = _get_account_safe(accounts, 0)
1051
1154
  return DexEvent(
1052
1155
  type=EventType.METEORA_DLMM_INITIALIZE_BIN_ARRAY,
1053
1156
  data=MeteoraDlmmInitializeBinArrayEvent(
1054
1157
  metadata=meta,
1055
1158
  pool=pool,
1056
1159
  bin_array=_get_account_safe(accounts, 1),
1057
- index=struct.unpack_from("<Q", payload, 0)[0],
1160
+ index=struct.unpack_from("<q", payload, 0)[0],
1058
1161
  ),
1059
1162
  )
1060
1163
 
1061
- if instruction_type == 2:
1062
- if len(payload) < 32:
1063
- return None
1164
+ if discriminator in (_DISC_DLMM_ADD_LIQUIDITY, _DISC_DLMM_ADD_LIQUIDITY2):
1165
+ pool = _get_account_safe(accounts, 1)
1166
+ sender_index = 9 if discriminator == _DISC_DLMM_ADD_LIQUIDITY2 else 11
1064
1167
  return DexEvent(
1065
1168
  type=EventType.METEORA_DLMM_ADD_LIQUIDITY,
1066
1169
  data=MeteoraDlmmAddLiquidityEvent(
1067
1170
  metadata=meta,
1068
1171
  pool=pool,
1069
- from_addr=_get_account_safe(accounts, 1),
1070
- position=_get_account_safe(accounts, 2),
1172
+ from_addr=_get_account_safe(accounts, sender_index),
1173
+ position=_get_account_safe(accounts, 0),
1071
1174
  amounts=[0, 0],
1072
1175
  active_bin_id=0,
1073
1176
  ),
1074
1177
  )
1075
1178
 
1076
- if instruction_type == 7:
1077
- if len(payload) < 32:
1078
- return None
1179
+ if discriminator in (_DISC_DLMM_REMOVE_LIQUIDITY, _DISC_DLMM_REMOVE_LIQUIDITY2):
1180
+ pool = _get_account_safe(accounts, 1)
1181
+ sender_index = 9 if discriminator == _DISC_DLMM_REMOVE_LIQUIDITY2 else 11
1079
1182
  return DexEvent(
1080
1183
  type=EventType.METEORA_DLMM_REMOVE_LIQUIDITY,
1081
1184
  data=MeteoraDlmmRemoveLiquidityEvent(
1082
1185
  metadata=meta,
1083
1186
  pool=pool,
1084
- from_addr=_get_account_safe(accounts, 1),
1085
- position=_get_account_safe(accounts, 2),
1187
+ from_addr=_get_account_safe(accounts, sender_index),
1188
+ position=_get_account_safe(accounts, 0),
1086
1189
  amounts=[0, 0],
1087
1190
  active_bin_id=0,
1088
1191
  ),
1089
1192
  )
1090
1193
 
1091
- if instruction_type == 8:
1194
+ if discriminator in (
1195
+ _DISC_DLMM_INITIALIZE_POSITION,
1196
+ _DISC_DLMM_INITIALIZE_POSITION2,
1197
+ _DISC_DLMM_INITIALIZE_POSITION_PDA,
1198
+ ):
1092
1199
  if len(payload) < 8:
1093
1200
  return None
1201
+ lower_bin_id, width = struct.unpack_from("<ii", payload, 0)
1202
+ if width < 0:
1203
+ return None
1204
+ pda = discriminator == _DISC_DLMM_INITIALIZE_POSITION_PDA
1205
+ position_index, pool_index, owner_index = (2, 3, 4) if pda else (1, 2, 3)
1094
1206
  return DexEvent(
1095
1207
  type=EventType.METEORA_DLMM_CREATE_POSITION,
1096
1208
  data=MeteoraDlmmCreatePositionEvent(
1097
1209
  metadata=meta,
1098
- pool=pool,
1099
- position=_get_account_safe(accounts, 1),
1100
- owner=_get_account_safe(accounts, 2),
1101
- lower_bin_id=struct.unpack_from("<i", payload, 0)[0],
1102
- width=struct.unpack_from("<I", payload, 4)[0],
1210
+ pool=_get_account_safe(accounts, pool_index),
1211
+ position=_get_account_safe(accounts, position_index),
1212
+ owner=_get_account_safe(accounts, owner_index),
1213
+ lower_bin_id=lower_bin_id,
1214
+ width=width,
1103
1215
  ),
1104
1216
  )
1105
1217
 
1106
- if instruction_type == 11:
1107
- if len(payload) < 16:
1218
+ if discriminator in (_DISC_DLMM_SWAP, _DISC_DLMM_SWAP2):
1219
+ if len(payload) < 8:
1108
1220
  return None
1221
+ min_out = struct.unpack_from("<Q", payload, 8)[0] if len(payload) >= 16 else 0
1222
+ pool = _get_account_safe(accounts, 0)
1109
1223
  return DexEvent(
1110
1224
  type=EventType.METEORA_DLMM_SWAP,
1111
1225
  data=MeteoraDlmmSwapEvent(
1112
1226
  metadata=meta,
1113
1227
  pool=pool,
1114
- from_addr=_get_account_safe(accounts, 1),
1228
+ from_addr=_get_account_safe(accounts, 10),
1229
+ user_token_in=_get_account_safe(accounts, 4),
1230
+ user_token_out=_get_account_safe(accounts, 5),
1231
+ min_amount_out=min_out,
1115
1232
  start_bin_id=0,
1116
1233
  end_bin_id=0,
1117
1234
  amount_in=struct.unpack_from("<Q", payload, 0)[0],
@@ -1124,27 +1241,65 @@ def parse_meteora_dlmm_instruction(
1124
1241
  ),
1125
1242
  )
1126
1243
 
1127
- if instruction_type == 13:
1244
+ if discriminator in (
1245
+ _DISC_DLMM_SWAP_EXACT_OUT,
1246
+ _DISC_DLMM_SWAP_EXACT_OUT2,
1247
+ _DISC_DLMM_SWAP_WITH_PRICE_IMPACT,
1248
+ _DISC_DLMM_SWAP_WITH_PRICE_IMPACT2,
1249
+ ):
1250
+ exact_out = discriminator in (_DISC_DLMM_SWAP_EXACT_OUT, _DISC_DLMM_SWAP_EXACT_OUT2)
1251
+ if len(payload) < (16 if exact_out else 8):
1252
+ return None
1253
+ amount_out = 0
1254
+ if exact_out:
1255
+ amount_out = struct.unpack_from("<Q", payload, 8)[0]
1256
+ pool = _get_account_safe(accounts, 0)
1257
+ return DexEvent(
1258
+ type=EventType.METEORA_DLMM_SWAP,
1259
+ data=MeteoraDlmmSwapEvent(
1260
+ metadata=meta,
1261
+ pool=pool,
1262
+ from_addr=_get_account_safe(accounts, 10),
1263
+ user_token_in=_get_account_safe(accounts, 4),
1264
+ user_token_out=_get_account_safe(accounts, 5),
1265
+ min_amount_out=0,
1266
+ start_bin_id=0,
1267
+ end_bin_id=0,
1268
+ amount_in=struct.unpack_from("<Q", payload, 0)[0],
1269
+ amount_out=amount_out,
1270
+ swap_for_y=False,
1271
+ fee=0,
1272
+ protocol_fee=0,
1273
+ fee_bps="0",
1274
+ host_fee=0,
1275
+ ),
1276
+ )
1277
+
1278
+ if discriminator in (_DISC_DLMM_CLAIM_FEE, _DISC_DLMM_CLAIM_FEE2):
1279
+ pool = _get_account_safe(accounts, 0)
1280
+ owner_index = 2 if discriminator == _DISC_DLMM_CLAIM_FEE2 else 4
1128
1281
  return DexEvent(
1129
1282
  type=EventType.METEORA_DLMM_CLAIM_FEE,
1130
1283
  data=MeteoraDlmmClaimFeeEvent(
1131
1284
  metadata=meta,
1132
1285
  pool=pool,
1133
1286
  position=_get_account_safe(accounts, 1),
1134
- owner=_get_account_safe(accounts, 2),
1287
+ owner=_get_account_safe(accounts, owner_index),
1135
1288
  fee_x=0,
1136
1289
  fee_y=0,
1137
1290
  ),
1138
1291
  )
1139
1292
 
1140
- if instruction_type == 14:
1293
+ if discriminator in (_DISC_DLMM_CLOSE_POSITION, _DISC_DLMM_CLOSE_POSITION2):
1294
+ position = _get_account_safe(accounts, 0)
1295
+ v2 = discriminator == _DISC_DLMM_CLOSE_POSITION2
1141
1296
  return DexEvent(
1142
1297
  type=EventType.METEORA_DLMM_CLOSE_POSITION,
1143
1298
  data=MeteoraDlmmClosePositionEvent(
1144
1299
  metadata=meta,
1145
- pool=pool,
1146
- position=_get_account_safe(accounts, 1),
1147
- owner=_get_account_safe(accounts, 2),
1300
+ pool=Z if v2 else _get_account_safe(accounts, 1),
1301
+ position=position,
1302
+ owner=_get_account_safe(accounts, 1 if v2 else 4),
1148
1303
  ),
1149
1304
  )
1150
1305
 
@@ -1486,7 +1641,7 @@ def parse_raydium_cpmm_instruction(
1486
1641
  return None
1487
1642
  return legacy_dict_to_dex_event({"RaydiumCpmmSwap": {
1488
1643
  "metadata": meta,
1489
- "pool_id": Z,
1644
+ "pool_id": _get_account_safe(accounts, 3),
1490
1645
  "input_amount": 0, "output_amount": 0,
1491
1646
  "input_vault_before": 0, "output_vault_before": 0,
1492
1647
  "input_transfer_fee": 0, "output_transfer_fee": 0,
@@ -1497,7 +1652,7 @@ def parse_raydium_cpmm_instruction(
1497
1652
  return None
1498
1653
  return legacy_dict_to_dex_event({"RaydiumCpmmSwap": {
1499
1654
  "metadata": meta,
1500
- "pool_id": Z,
1655
+ "pool_id": _get_account_safe(accounts, 3),
1501
1656
  "input_amount": 0, "output_amount": 0,
1502
1657
  "input_vault_before": 0, "output_vault_before": 0,
1503
1658
  "input_transfer_fee": 0, "output_transfer_fee": 0,
@@ -1555,21 +1710,35 @@ def parse_raydium_amm_v4_instruction(
1555
1710
  instr_type = data[0]
1556
1711
  meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
1557
1712
 
1558
- if instr_type in (9, 11): # SwapBaseIn / SwapBaseOut
1713
+ if instr_type in (9, 11, 16, 17): # SwapBaseIn / SwapBaseOut
1714
+ if len(data) < 17:
1715
+ return None
1716
+ modern = instr_type in (16,17)
1717
+ if modern and len(accounts)<8:return None
1718
+ first, second = struct.unpack_from("<QQ", data, 1)
1719
+ shift = 1 if len(accounts) == 17 else 0
1720
+ def g(index: int) -> str:
1721
+ if modern:
1722
+ index = {0:0,1:1,2:2,5:3,6:4,15:5,16:6,17:7}.get(index,-1)
1723
+ else:
1724
+ index = index-shift if index>=5 else index
1725
+ return _get_account_safe(accounts,index)
1559
1726
  return legacy_dict_to_dex_event({"RaydiumAmmV4Swap": {
1560
1727
  "metadata": meta,
1561
1728
  "amm": _get_account_safe(accounts, 1),
1562
- "user_source_owner": _get_account_safe(accounts, 17),
1563
- "amount_in": 0, "minimum_amount_out": 0,
1564
- "max_amount_in": 0, "amount_out": 0,
1565
- "token_program": Z, "amm_authority": Z, "amm_open_orders": Z,
1566
- "pool_coin_token_account": Z, "pool_pc_token_account": Z,
1567
- "serum_program": Z, "serum_market": Z, "serum_bids": Z,
1568
- "serum_asks": Z, "serum_event_queue": Z,
1569
- "serum_coin_vault_account": Z, "serum_pc_vault_account": Z,
1570
- "serum_vault_signer": Z,
1571
- "user_source_token_account": Z,
1572
- "user_destination_token_account": Z,
1729
+ "user_source_owner": g(17),
1730
+ "amount_in": first if instr_type in (9,16) else 0,
1731
+ "minimum_amount_out": second if instr_type in (9,16) else 0,
1732
+ "max_amount_in": first if instr_type in (11,17) else 0,
1733
+ "amount_out": second if instr_type in (11,17) else 0,
1734
+ "token_program": g(0), "amm_authority": g(2), "amm_open_orders": g(3),
1735
+ "pool_coin_token_account": g(5), "pool_pc_token_account": g(6),
1736
+ "serum_program": g(7), "serum_market": g(8), "serum_bids": g(9),
1737
+ "serum_asks": g(10), "serum_event_queue": g(11),
1738
+ "serum_coin_vault_account": g(12), "serum_pc_vault_account": g(13),
1739
+ "serum_vault_signer": g(14),
1740
+ "user_source_token_account": g(15),
1741
+ "user_destination_token_account": g(16),
1573
1742
  }})
1574
1743
 
1575
1744
  return None
@@ -1599,9 +1768,10 @@ def parse_orca_whirlpool_instruction(
1599
1768
  sqrt_price_limit = int.from_bytes(data[24:40], "little")
1600
1769
  amount_specified_is_input = data[40] != 0
1601
1770
  a_to_b = data[41] != 0
1771
+ whirlpool_index = 4 if discriminator == _DISC_ORCA_SWAP_V2 else 2
1602
1772
  return legacy_dict_to_dex_event({"OrcaWhirlpoolSwap": {
1603
1773
  "metadata": meta,
1604
- "whirlpool": _get_account_safe(accounts, 1),
1774
+ "whirlpool": _get_account_safe(accounts, whirlpool_index),
1605
1775
  "a_to_b": a_to_b,
1606
1776
  "pre_sqrt_price": str(sqrt_price_limit), "post_sqrt_price": "0",
1607
1777
  "input_amount": amount if amount_specified_is_input else 0,
@@ -1679,7 +1849,7 @@ def parse_raydium_launchlab_instruction(
1679
1849
  payload = data[8:]
1680
1850
 
1681
1851
  if discriminator == _DISC_RAYDIUM_LAUNCHLAB_TRADE:
1682
- if len(payload) < 139:
1852
+ if len(payload) != 139 or payload[136] > 1 or payload[137] > 2 or payload[138] > 1:
1683
1853
  return None
1684
1854
  return legacy_dict_to_dex_event({"RaydiumLaunchlabTrade": {
1685
1855
  "metadata": meta,
@@ -1690,6 +1860,19 @@ def parse_raydium_launchlab_instruction(
1690
1860
  "is_buy": payload[136] == 0,
1691
1861
  "trade_direction": "Buy" if payload[136] == 0 else "Sell",
1692
1862
  "exact_in": payload[138] == 1,
1863
+ "pool_status": ("Fund","Migrate","Trade")[payload[137]],
1864
+ "total_base_sell": _u64_payload(payload,32),
1865
+ "virtual_base": _u64_payload(payload,40),
1866
+ "virtual_quote": _u64_payload(payload,48),
1867
+ "real_base_before": _u64_payload(payload,56),
1868
+ "real_quote_before": _u64_payload(payload,64),
1869
+ "real_base_after": _u64_payload(payload,72),
1870
+ "real_quote_after": _u64_payload(payload,80),
1871
+ "protocol_fee": _u64_payload(payload,104),
1872
+ "platform_fee": _u64_payload(payload,112),
1873
+ "creator_fee": _u64_payload(payload,120),
1874
+ "share_fee": _u64_payload(payload,128),
1875
+
1693
1876
  }})
1694
1877
  if discriminator == _DISC_RAYDIUM_LAUNCHLAB_POOL_CREATE:
1695
1878
  if len(payload) < 97:
@@ -1733,6 +1916,20 @@ def parse_raydium_launchlab_instruction(
1733
1916
  "is_buy": is_buy,
1734
1917
  "trade_direction": "Buy" if is_buy else "Sell",
1735
1918
  "exact_in": exact_in,
1919
+ "global_config": _get_account_safe(accounts,2),
1920
+ "platform_config": _get_account_safe(accounts,3),
1921
+ "user_base_token": _get_account_safe(accounts,5),
1922
+ "user_quote_token": _get_account_safe(accounts,6),
1923
+ "base_vault": _get_account_safe(accounts,7),
1924
+ "quote_vault": _get_account_safe(accounts,8),
1925
+ "base_mint": _get_account_safe(accounts,9),
1926
+ "quote_mint": _get_account_safe(accounts,10),
1927
+ "base_token_program": _get_account_safe(accounts,11),
1928
+ "quote_token_program": _get_account_safe(accounts,12),
1929
+ "system_program": _get_account_safe(accounts,15),
1930
+ "platform_associated_account": _get_account_safe(accounts,16),
1931
+ "creator_associated_account": _get_account_safe(accounts,17),
1932
+
1736
1933
  }})
1737
1934
  if discriminator in (
1738
1935
  _IX_RAYDIUM_LAUNCHLAB_INITIALIZE,
@@ -1758,7 +1955,16 @@ def parse_raydium_launchlab_instruction(
1758
1955
  _IX_RAYDIUM_LAUNCHLAB_MIGRATE_TO_AMM,
1759
1956
  _IX_RAYDIUM_LAUNCHLAB_MIGRATE_TO_CPSWAP,
1760
1957
  ):
1761
- return None
1958
+ cp = discriminator == _IX_RAYDIUM_LAUNCHLAB_MIGRATE_TO_CPSWAP
1959
+ if len(accounts) < (28 if cp else 32) or (not cp and len(payload) < 9): return None
1960
+ return legacy_dict_to_dex_event({"RaydiumLaunchlabMigrateAmm": {
1961
+ "metadata": meta, "old_pool": _get_account_safe(accounts,17 if cp else 23),
1962
+ "new_pool": _get_account_safe(accounts,5 if cp else 13), "user": _get_account_safe(accounts,0),
1963
+ "liquidity_amount": 0, "liquidity_amount_known": False,
1964
+ "base_mint": _get_account_safe(accounts,1), "quote_mint": _get_account_safe(accounts,2),
1965
+ "platform_config": _get_account_safe(accounts,3) if cp else Z,
1966
+ "destination_program": _get_account_safe(accounts,4 if cp else 12),
1967
+ }})
1762
1968
 
1763
1969
  return None
1764
1970
 
sol_parser/json_util.py CHANGED
@@ -11,7 +11,7 @@ from .grpc_types import EventType
11
11
 
12
12
 
13
13
  def dex_event_json_dumps(obj: Any, **kwargs: Any) -> str:
14
- return json.dumps(obj, default=str, **kwargs)
14
+ return json.dumps(_precise_metadata(dex_event_to_jsonable(obj)), default=str, **kwargs)
15
15
 
16
16
 
17
17
  def dex_event_to_jsonable(ev: Any) -> Any:
@@ -39,3 +39,17 @@ def format_dex_event_json(ev: Any, *, indent: int = 2, ensure_ascii: bool = Fals
39
39
  indent=indent,
40
40
  ensure_ascii=ensure_ascii,
41
41
  )
42
+
43
+
44
+ def _precise_metadata(obj):
45
+ """Match Node exact metadata JSON without changing floating latency metrics."""
46
+ if isinstance(obj,dict):
47
+ result={k:_precise_metadata(v) for k,v in obj.items()}
48
+ if "signature" in obj and all(k in obj for k in ("slot","tx_index","block_time_us","grpc_recv_us")):
49
+ from .grpc_types import EventMetadata
50
+ validated=EventMetadata(**{k:obj[k] for k in ("signature","slot","tx_index","block_time_us","grpc_recv_us")})
51
+ for k in ("slot","tx_index","block_time_us","grpc_recv_us"):result[k]=str(getattr(validated,k))
52
+ return result
53
+ if isinstance(obj,(tuple,list)):return [_precise_metadata(v) for v in obj]
54
+ if is_dataclass(obj):return _precise_metadata(asdict(obj))
55
+ return obj
@@ -0,0 +1,101 @@
1
+ """Native liquidity identity decoders and immutable raw subscription snapshots."""
2
+
3
+ from __future__ import annotations
4
+ from dataclasses import dataclass
5
+ import base58
6
+ from .accounts import AccountData
7
+ from .grpc_types import EventMetadata
8
+ from .transaction_route import PROGRAMS
9
+
10
+
11
+ @dataclass
12
+ class RawAccountSnapshotEvent:
13
+ metadata: EventMetadata
14
+ account: AccountData
15
+ write_version: int
16
+ is_startup: bool
17
+
18
+
19
+ @dataclass
20
+ class LiquidityAccountSnapshotEvent:
21
+ metadata: EventMetadata
22
+ pubkey: str
23
+ owner: str
24
+ kind: dict | str
25
+ data: bytes
26
+
27
+
28
+ def parse_liquidity_account(account: AccountData, metadata: EventMetadata):
29
+ if account.lamports <= 0 or account.executable:
30
+ return None
31
+ d, owner = bytes(account.data), account.owner
32
+ disc = d[:8]
33
+
34
+ def key(o):
35
+ return base58.b58encode(d[o : o + 32]).decode()
36
+
37
+ def number(o, n, signed=False):
38
+ return int.from_bytes(d[o : o + n], "little", signed=signed)
39
+
40
+ kind = None
41
+ if owner == PROGRAMS["LaunchLab"]:
42
+ if disc == bytes([247, 237, 227, 245, 215, 195, 222, 70]) and len(d) >= 429:
43
+ kind = {
44
+ "LaunchLabPool": dict(
45
+ base_mint=key(205),
46
+ quote_mint=key(237),
47
+ global_config=key(141),
48
+ platform_config=key(173),
49
+ )
50
+ }
51
+ elif disc == bytes([149, 8, 156, 202, 160, 252, 176, 217]) and len(d) >= 35:
52
+ kind = "LaunchLabGlobalConfig"
53
+ elif disc == bytes([160, 78, 128, 0, 248, 83, 230, 160]) and len(d) >= 728:
54
+ kind = "LaunchLabPlatformConfig"
55
+ elif owner == PROGRAMS["MeteoraDlmm"]:
56
+ if disc == bytes([33, 11, 49, 98, 181, 101, 177, 13]) and len(d) >= 904:
57
+ kind = {
58
+ "DlmmPool": dict(
59
+ token_x_mint=key(88),
60
+ token_y_mint=key(120),
61
+ active_id=number(76, 4, True),
62
+ bin_step=number(80, 2),
63
+ )
64
+ }
65
+ elif disc == bytes([92, 142, 92, 220, 5, 148, 70, 181]) and len(d) >= 10136:
66
+ kind = {"DlmmBinArray": dict(pool=key(24), index=number(8, 8, True))}
67
+ elif disc == bytes([80, 111, 124, 113, 55, 237, 18, 5]) and len(d) >= 1576:
68
+ kind = {"DlmmBitmap": dict(pool=key(8))}
69
+ elif owner == PROGRAMS["OrcaWhirlpool"]:
70
+ if disc == bytes([17, 216, 246, 142, 225, 199, 218, 56]) and len(d) >= 148:
71
+ bitmap, offset = number(44, 16), 60
72
+ if bitmap >> 88:
73
+ return None
74
+ for i in range(88):
75
+ if (
76
+ offset >= len(d)
77
+ or d[offset] not in (0, 1)
78
+ or d[offset] != (bitmap >> i) & 1
79
+ ):
80
+ return None
81
+ offset += 1 + (112 if d[offset] else 0)
82
+ if offset > len(d):
83
+ return None
84
+ kind = {
85
+ "OrcaDynamicTickArray": dict(
86
+ pool=key(12),
87
+ start_tick_index=number(8, 4, True),
88
+ tick_bitmap=bitmap,
89
+ )
90
+ }
91
+ elif disc == bytes([139, 194, 131, 179, 140, 179, 229, 244]) and len(d) >= 254:
92
+ kind = {"OrcaAdaptiveOracle": dict(pool=key(8))}
93
+ elif (
94
+ owner == PROGRAMS["RaydiumClmm"]
95
+ and disc == bytes([60, 150, 36, 219, 97, 128, 139, 153])
96
+ and len(d) >= 1832
97
+ ):
98
+ kind = {"ClmmBitmap": dict(pool=key(8))}
99
+ if kind is None:
100
+ return None
101
+ return LiquidityAccountSnapshotEvent(metadata, account.pubkey, owner, kind, d)