sol-parser-sdk-python 0.5.7__py3-none-any.whl → 0.5.8__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)
@@ -324,7 +370,8 @@ def normal_instruction_data_may_parse(program_id: str, instruction_data: bytes)
324
370
  if program_id == RAYDIUM_AMM_V4_PROGRAM_ID:
325
371
  return instruction_data[0] in (1, 3, 4, 7, 9, 11)
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,
@@ -1556,20 +1711,28 @@ def parse_raydium_amm_v4_instruction(
1556
1711
  meta = _make_meta(signature, slot, tx_index, block_time_us, grpc_recv_us)
1557
1712
 
1558
1713
  if instr_type in (9, 11): # SwapBaseIn / SwapBaseOut
1714
+ if len(data) < 17:
1715
+ return None
1716
+ first, second = struct.unpack_from("<QQ", data, 1)
1717
+ shift = 1 if len(accounts) == 17 else 0
1718
+ def g(index: int) -> str:
1719
+ return _get_account_safe(accounts, index - shift if index >= 5 else index)
1559
1720
  return legacy_dict_to_dex_event({"RaydiumAmmV4Swap": {
1560
1721
  "metadata": meta,
1561
1722
  "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,
1723
+ "user_source_owner": g(17),
1724
+ "amount_in": first if instr_type == 9 else 0,
1725
+ "minimum_amount_out": second if instr_type == 9 else 0,
1726
+ "max_amount_in": first if instr_type == 11 else 0,
1727
+ "amount_out": second if instr_type == 11 else 0,
1728
+ "token_program": g(0), "amm_authority": g(2), "amm_open_orders": g(3),
1729
+ "pool_coin_token_account": g(5), "pool_pc_token_account": g(6),
1730
+ "serum_program": g(7), "serum_market": g(8), "serum_bids": g(9),
1731
+ "serum_asks": g(10), "serum_event_queue": g(11),
1732
+ "serum_coin_vault_account": g(12), "serum_pc_vault_account": g(13),
1733
+ "serum_vault_signer": g(14),
1734
+ "user_source_token_account": g(15),
1735
+ "user_destination_token_account": g(16),
1573
1736
  }})
1574
1737
 
1575
1738
  return None
@@ -1599,9 +1762,10 @@ def parse_orca_whirlpool_instruction(
1599
1762
  sqrt_price_limit = int.from_bytes(data[24:40], "little")
1600
1763
  amount_specified_is_input = data[40] != 0
1601
1764
  a_to_b = data[41] != 0
1765
+ whirlpool_index = 4 if discriminator == _DISC_ORCA_SWAP_V2 else 2
1602
1766
  return legacy_dict_to_dex_event({"OrcaWhirlpoolSwap": {
1603
1767
  "metadata": meta,
1604
- "whirlpool": _get_account_safe(accounts, 1),
1768
+ "whirlpool": _get_account_safe(accounts, whirlpool_index),
1605
1769
  "a_to_b": a_to_b,
1606
1770
  "pre_sqrt_price": str(sqrt_price_limit), "post_sqrt_price": "0",
1607
1771
  "input_amount": amount if amount_specified_is_input else 0,
@@ -49,16 +49,19 @@ def _ix_lane(ix_name: Any) -> int:
49
49
  return 0
50
50
 
51
51
 
52
- PumpfunLaneBase = Tuple[str, str, bool, int]
52
+ OccurrenceBase = Tuple[Any, ...]
53
53
 
54
54
 
55
- def _next_occurrence(base: PumpfunLaneBase, counts: Dict[PumpfunLaneBase, int]) -> int:
55
+ def _next_occurrence(base: OccurrenceBase, counts: Dict[OccurrenceBase, int]) -> int:
56
56
  current = counts.get(base, 0)
57
57
  counts[base] = current + 1
58
58
  return current
59
59
 
60
60
 
61
- def _dedupe_key(ev: DexEvent, pumpfun_lane_counts: Dict[PumpfunLaneBase, int]) -> Optional[str]:
61
+ def _dedupe_key(
62
+ ev: DexEvent,
63
+ occurrence_counts: Dict[OccurrenceBase, int],
64
+ ) -> Optional[str]:
62
65
  data = ev.data
63
66
  if data is None:
64
67
  return None
@@ -71,7 +74,7 @@ def _dedupe_key(ev: DexEvent, pumpfun_lane_counts: Dict[PumpfunLaneBase, int]) -
71
74
  bool(getattr(data, "is_buy", False)),
72
75
  lane,
73
76
  )
74
- occ = _next_occurrence(base, pumpfun_lane_counts)
77
+ occ = _next_occurrence(("PumpFun", *base), occurrence_counts)
75
78
  return f"PumpFunTrade|{base[0]}|{base[1]}|{base[2]}|{base[3]}|{occ}"
76
79
 
77
80
  t = ev.type
@@ -110,13 +113,32 @@ def _dedupe_key(ev: DexEvent, pumpfun_lane_counts: Dict[PumpfunLaneBase, int]) -
110
113
  if t == EventType.PUMP_SWAP_LIQUIDITY_REMOVED:
111
114
  return f"PumpSwapLiquidityRemoved|{getattr(data, 'pool', '')}|{getattr(data, 'user', '')}"
112
115
  if t == EventType.RAYDIUM_CLMM_SWAP:
113
- return f"RaydiumClmmSwap|{getattr(data, 'pool_state', '')}|{bool(getattr(data, 'zero_for_one', False))}"
116
+ pool = getattr(data, "pool_state", "")
117
+ occurrence = _next_occurrence(("RaydiumClmm", pool), occurrence_counts)
118
+ return f"RaydiumClmmSwap|{pool}|{occurrence}"
119
+ if t == EventType.RAYDIUM_CPMM_SWAP:
120
+ pool = getattr(data, "pool_id", "")
121
+ occurrence = _next_occurrence(("RaydiumCpmm", pool), occurrence_counts)
122
+ return f"RaydiumCpmmSwap|{pool}|{occurrence}"
114
123
  if t == EventType.RAYDIUM_AMM_V4_SWAP:
115
- return f"RaydiumAmmV4Swap|{getattr(data, 'amm', '')}"
124
+ base_out = getattr(data, "max_amount_in", 0) != 0
125
+ amount = getattr(data, "amount_out" if base_out else "amount_in", 0)
126
+ base = (base_out, amount)
127
+ occurrence = _next_occurrence(("RaydiumAmmV4", *base), occurrence_counts)
128
+ return f"RaydiumAmmV4Swap|{base_out}|{amount}|{occurrence}"
129
+ if t == EventType.ORCA_WHIRLPOOL_SWAP:
130
+ whirlpool = getattr(data, "whirlpool", "")
131
+ occurrence = _next_occurrence(("OrcaWhirlpool", whirlpool), occurrence_counts)
132
+ return f"OrcaWhirlpoolSwap|{whirlpool}|{occurrence}"
116
133
  if t == EventType.METEORA_DLMM_SWAP:
134
+ base = (
135
+ getattr(data, "pool", ""),
136
+ getattr(data, "from_addr", ""),
137
+ bool(getattr(data, "swap_for_y", False)),
138
+ )
139
+ occurrence = _next_occurrence(("MeteoraDlmm", *base), occurrence_counts)
117
140
  return (
118
- f"MeteoraDlmmSwap|{getattr(data, 'pool', '')}|"
119
- f"{getattr(data, 'from_addr', '')}|{bool(getattr(data, 'swap_for_y', False))}"
141
+ f"MeteoraDlmmSwap|{base[0]}|{base[1]}|{base[2]}|{occurrence}"
120
142
  )
121
143
  return None
122
144
 
@@ -163,6 +185,8 @@ def _merge_pumpfun_trade(log: Any, ix: Any) -> None:
163
185
  "quote_amount",
164
186
  "virtual_quote_reserves",
165
187
  "real_quote_reserves",
188
+ "holder_rewards_bps",
189
+ "holder_rewards",
166
190
  ):
167
191
  if getattr(log, attr, 0) == 0 and getattr(ix, attr, 0) != 0:
168
192
  setattr(log, attr, getattr(ix, attr))
@@ -195,6 +219,7 @@ def _merge_pumpfun_create(log: Any, ix: Any) -> None:
195
219
  "real_token_reserves",
196
220
  "token_total_supply",
197
221
  "virtual_quote_reserves",
222
+ "creator_fee_bps",
198
223
  ):
199
224
  if getattr(log, attr, 0) == 0 and getattr(ix, attr, 0) != 0:
200
225
  setattr(log, attr, getattr(ix, attr))
@@ -204,6 +229,9 @@ def _merge_pumpfun_create(log: Any, ix: Any) -> None:
204
229
  log.is_cashback_enabled = bool(getattr(log, "is_cashback_enabled", False)) or bool(
205
230
  getattr(ix, "is_cashback_enabled", False)
206
231
  )
232
+ log.is_holder_reward = bool(getattr(log, "is_holder_reward", False)) or bool(
233
+ getattr(ix, "is_holder_reward", False)
234
+ )
207
235
 
208
236
 
209
237
  def _merge_pumpfun_create_v2(log: Any, ix: Any) -> None:
@@ -283,6 +311,20 @@ def _merge_grpc_instruction_into_log(log_ev: DexEvent, ix_ev: DexEvent) -> None:
283
311
  "coin_creator",
284
312
  ):
285
313
  _fill_attr(log, attr, ix)
314
+ if getattr(log, "creator_fee_bps", 0) == 0 and getattr(ix, "creator_fee_bps", 0) != 0:
315
+ log.creator_fee_bps = ix.creator_fee_bps
316
+ log.is_mayhem_mode = bool(getattr(log, "is_mayhem_mode", False)) or bool(
317
+ getattr(ix, "is_mayhem_mode", False)
318
+ )
319
+ log.is_cashback_coin = bool(getattr(log, "is_cashback_coin", False)) or bool(
320
+ getattr(ix, "is_cashback_coin", False)
321
+ )
322
+ log.can_edit_creator_fee = bool(getattr(log, "can_edit_creator_fee", False)) or bool(
323
+ getattr(ix, "can_edit_creator_fee", False)
324
+ )
325
+ log.is_holder_reward = bool(getattr(log, "is_holder_reward", False)) or bool(
326
+ getattr(ix, "is_holder_reward", False)
327
+ )
286
328
  elif (
287
329
  log_ev.type == EventType.PUMP_SWAP_LIQUIDITY_ADDED
288
330
  and ix_ev.type == EventType.PUMP_SWAP_LIQUIDITY_ADDED
@@ -312,6 +354,8 @@ def _merge_grpc_instruction_into_log(log_ev: DexEvent, ix_ev: DexEvent) -> None:
312
354
  "serum_vault_signer",
313
355
  "user_source_token_account",
314
356
  "user_destination_token_account",
357
+ "user_source_owner",
358
+ "amm",
315
359
  ):
316
360
  _fill_attr(log, attr, ix)
317
361
  elif log_ev.type == EventType.RAYDIUM_LAUNCHLAB_POOL_CREATE and ix_ev.type == EventType.RAYDIUM_LAUNCHLAB_POOL_CREATE:
@@ -329,17 +373,17 @@ def dedupe_log_instruction_events(
329
373
  ) -> List[DexEvent]:
330
374
  out: List[DexEvent] = []
331
375
  index_by_key: Dict[str, int] = {}
332
- log_pumpfun_counts: Dict[PumpfunLaneBase, int] = {}
333
- ix_pumpfun_counts: Dict[PumpfunLaneBase, int] = {}
376
+ log_occurrences: Dict[OccurrenceBase, int] = {}
377
+ ix_occurrences: Dict[OccurrenceBase, int] = {}
334
378
 
335
379
  for ev in log_events:
336
- key = _dedupe_key(ev, log_pumpfun_counts)
380
+ key = _dedupe_key(ev, log_occurrences)
337
381
  if key is not None:
338
382
  index_by_key[key] = len(out)
339
383
  out.append(ev)
340
384
 
341
385
  for ev in instruction_events:
342
- key = _dedupe_key(ev, ix_pumpfun_counts)
386
+ key = _dedupe_key(ev, ix_occurrences)
343
387
  if key is None:
344
388
  out.append(ev)
345
389
  continue