strideutils 0.3.3__tar.gz → 0.3.5__tar.gz

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.
Files changed (33) hide show
  1. {strideutils-0.3.3 → strideutils-0.3.5}/PKG-INFO +1 -1
  2. {strideutils-0.3.3 → strideutils-0.3.5}/pyproject.toml +1 -1
  3. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/stride_config.py +2 -0
  4. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/stride_requests.py +294 -8
  5. strideutils-0.3.5/strideutils/types.py +23 -0
  6. {strideutils-0.3.3 → strideutils-0.3.5}/README.md +0 -0
  7. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/Makefile +0 -0
  8. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/__init__.py +0 -0
  9. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/coingecko.py +0 -0
  10. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/config_examples/strideutils_config.yaml.example +0 -0
  11. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/config_examples/strideutils_secrets.yaml.example +0 -0
  12. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/cryptography.py +0 -0
  13. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/exec_tx.py +0 -0
  14. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/invariant_helpers.py +0 -0
  15. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/run_safely.py +0 -0
  16. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/sheets_connector.py +0 -0
  17. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/slack_connector.py +0 -0
  18. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/stride_alerts.py +0 -0
  19. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/stride_upstash.py +0 -0
  20. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils/twilio_connector.py +0 -0
  21. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/__init__.pyi +0 -0
  22. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/coingecko.pyi +0 -0
  23. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/cryptography.pyi +0 -0
  24. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/exec_tx.pyi +0 -0
  25. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/invariant_helpers.pyi +0 -0
  26. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/run_safely.pyi +0 -0
  27. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/sheets_connector.pyi +0 -0
  28. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/slack_connector.pyi +0 -0
  29. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/stride_alerts.pyi +0 -0
  30. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/stride_config.pyi +0 -0
  31. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/stride_requests.pyi +0 -0
  32. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/stride_upstash.pyi +0 -0
  33. {strideutils-0.3.3 → strideutils-0.3.5}/strideutils-stubs/twilio_connector.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: strideutils
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary:
5
5
  Author: Your Name
6
6
  Author-email: you@example.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "strideutils"
3
- version = "0.3.3"
3
+ version = "0.3.5"
4
4
  description = ""
5
5
  authors = ["Your Name <you@example.com>"]
6
6
  readme = "README.md"
@@ -105,6 +105,8 @@ class HostChainConfig(ChainConfig):
105
105
  denom_on_stride: str = ""
106
106
  # Indicates whether the chain has ICA enabled
107
107
  ica_enabled: bool = True
108
+ # Indicates whether the chain has LSM enabled
109
+ lsm_enabled: bool = False
108
110
 
109
111
  def __repr__(self) -> str:
110
112
  return f"HostChainConfig(name={self.name}, id={self.id})"
@@ -13,7 +13,7 @@ import pytz
13
13
  import requests
14
14
  from requests.adapters import HTTPAdapter, Retry
15
15
 
16
- from strideutils import stride_upstash
16
+ from strideutils import stride_upstash, types
17
17
  from strideutils.stride_config import config
18
18
 
19
19
  ACCOUNT_QUERY = "cosmos/auth/v1beta1/accounts/{address}"
@@ -34,8 +34,12 @@ TX_BY_ACCOUNT_EVENT = "tx.acc_seq='{address}/{sequence}'"
34
34
 
35
35
  HOST_ZONE_QUERY = "Stride-Labs/stride/stakeibc/host_zone"
36
36
  EPOCHS_QUERY = "Stridelabs/stride/epochs"
37
-
37
+ PENDING_ICQ_QUERY = "Stride-Labs/stride/interchainquery/pending_queries"
38
38
  RATE_LIMIT_QUERY = "Stride-Labs/ibc-rate-limiting/ratelimit/ratelimits"
39
+ DEPOSIT_RECORDS_QUERY = "Stride-Labs/stride/records/deposit_record_by_host_zone/{chain_id}"
40
+ UNBONDING_RECORDS_QUERY = "Stride-Labs/stride/records/epoch_unbonding_record"
41
+ LSM_RECORDS_QUERY = "Stride-Labs/stride/stakeibc/lsm_deposits"
42
+ CALLBACK_DATA_QUERY = "Stride-Labs/stride/icacallbacks/callback_data"
39
43
 
40
44
  STAKETIA = "staketia"
41
45
  STAKEDYM = "stakedym"
@@ -50,6 +54,8 @@ MULTISIG_REDEMPTION_RECORDS_QUERY = "Stride-Labs/stride/{module}/redemption_reco
50
54
 
51
55
  COSMWASM_CONTRACT_QUERY = "cosmwasm/wasm/v1/contract/{contract_address}/smart/{query}"
52
56
 
57
+ HOST_CHANNELS_QUERY = "https://channels.main.stridenet.co/api/data/{chain_id}"
58
+
53
59
  requests_session = requests.Session()
54
60
 
55
61
  retries = Retry(total=5, backoff_factor=0.5, status_forcelist=[400, 403, 404, 500, 502, 503, 504])
@@ -244,6 +250,109 @@ def get_host_zone(chain_id: str, cast_types: bool = True, **kwargs) -> Dict[str,
244
250
  return host_zone
245
251
 
246
252
 
253
+ def get_deposit_records(
254
+ chain_id: str,
255
+ status: Optional[types.DepositRecordStatus] = None,
256
+ block_height: int = 0,
257
+ ) -> List[Dict[str, Any]]:
258
+ """
259
+ Queries all the deposit records for a given host zone with optional status filter
260
+
261
+ E.g.
262
+ [
263
+ {
264
+ "id": "36310",
265
+ "amount": "5305210",
266
+ "denom": "uluna",
267
+ "host_zone_id": "phoenix-1",
268
+ "status": "DELEGATION_QUEUE",
269
+ "deposit_epoch_number": "2424",
270
+ "source": "STRIDE"
271
+ },
272
+ ]
273
+ """
274
+ url = f"{config.stride.api_endpoint}/{DEPOSIT_RECORDS_QUERY.format(chain_id=chain_id)}"
275
+ deposit_records = request(url, block_height=block_height)["deposit_record"]
276
+ if status:
277
+ deposit_records = [dr for dr in deposit_records if dr["status"] == status]
278
+ return deposit_records
279
+
280
+
281
+ def get_unbonding_records(
282
+ chain_id: str,
283
+ status: Optional[types.UnbondingRecordStatus] = None,
284
+ block_height: int = 0,
285
+ ) -> List[Dict[str, Any]]:
286
+ """
287
+ Queries all the unbonding records for a given host zone with optional status filter
288
+
289
+ E.g.
290
+ [
291
+ {
292
+ "epoch_number": "575",
293
+ "st_token_amount": "3218590",
294
+ "native_token_amount": "0",
295
+ "denom": "ucmdx",
296
+ "host_zone_id": "comdex-1",
297
+ "unbonding_time": "1713812526371636619",
298
+ "status": "CLAIMABLE",
299
+ "user_redemption_records": [
300
+ "comdex-1.575.comdex1gwnfjj65zhxmtnpxzy625cpx692lfjuwlqeg8p",
301
+ ...
302
+ ]
303
+ },
304
+ ...
305
+ ]
306
+ """
307
+ # Query the epoch unbonding records
308
+ url = f"{config.stride.api_endpoint}/{UNBONDING_RECORDS_QUERY}"
309
+ epoch_unbondings = query_list_with_pagination(url, rel_key="epoch_unbonding_record", block_height=block_height)
310
+
311
+ # The structure from the response consists of:
312
+ # - List of epoch unbonding records - one record per epoch
313
+ # - Within each epoch unbonding record, there's one host zone unbonding record per host
314
+ # This function returns the host zone unbonding record
315
+ host_zone_unbondings = []
316
+ for epoch_unbonding in epoch_unbondings:
317
+ epoch_number = epoch_unbonding["epoch_number"]
318
+ for host_zone_unbonding in epoch_unbonding["host_zone_unbondings"]:
319
+
320
+ chain_id_match = host_zone_unbonding["host_zone_id"] == chain_id
321
+ status_match = not status or host_zone_unbonding["status"] == status
322
+
323
+ if chain_id_match and status_match:
324
+ host_zone_unbonding["epoch_number"] = epoch_number
325
+ host_zone_unbondings.append(host_zone_unbonding)
326
+
327
+ return host_zone_unbondings
328
+
329
+
330
+ def get_lsm_deposit_records(chain_id: str) -> List[Dict[str, Any]]:
331
+ """
332
+ Queries all the LSM deposit records
333
+
334
+ [
335
+ {
336
+ "deposit_id": "800000.cosmoshub-4.strideXXX.cosmosvaloperXXX/1",
337
+ "chain_id": "cosmoshub-4",
338
+ "ibc_denom": "ibc/cosmosvaloperXXX/1",
339
+ "staker_address": "strideXXX",
340
+ "validator_address": "cosmosvaloperXXX",
341
+ "amount": "10000",
342
+ "st_token": {
343
+ "amount": "10000",
344
+ "denom": "stuatom"
345
+ }
346
+ "status": "DEPOSIT_PENDING"
347
+ },
348
+ ]
349
+ """
350
+ params = {"chain_id": chain_id}
351
+ url = f"{config.stride.api_endpoint}/{LSM_RECORDS_QUERY}"
352
+ deposit_records = request(url, params=params)["deposits"]
353
+ return deposit_records
354
+
355
+
247
356
  def get_multisig_delegation_records(module: str, include_archived: bool = False) -> List[Dict]:
248
357
  """
249
358
  Queries the delegation records from staketia or stakedym
@@ -338,6 +447,76 @@ def get_rate_limits(**kwargs):
338
447
  return host_zone
339
448
 
340
449
 
450
+ def get_pending_interchain_queries(chain_id: Optional[str] = None, block_height: int = 0) -> List[Dict[str, Any]]:
451
+ """
452
+ Gets the list of all pending ICQs on stride
453
+
454
+ E.g.
455
+ [
456
+ {
457
+ "id": "86170fade2b01f18a1f5e448c8a9dabdc60293151828f881a40708396ec383e0",
458
+ "connection_id": "connection-0",
459
+ "chain_id": "cosmoshub-4",
460
+ "query_type": "store/bank/key",
461
+ "request_data": ...bytestring...,
462
+ "callback_module": "stakeibc",
463
+ "callback_id": "withdrawalbalance",
464
+ "callback_data": ...bytestring...,
465
+ "timeout_policy": "REJECT_QUERY_RESPONSE",
466
+ "timeout_duration": "1hr",
467
+ "timeout_timestamp": "1714689884000000000",
468
+ "request_sent": true,
469
+ "submission_height": 80000
470
+ }
471
+ ...
472
+ ]
473
+ """
474
+ url = f"{config.stride.api_endpoint}/{PENDING_ICQ_QUERY}"
475
+ queries = request(url, block_height=block_height)["pending_queries"]
476
+ if chain_id:
477
+ queries = [query for query in queries if query["chain_id"] == chain_id]
478
+ return queries
479
+
480
+
481
+ def get_callback_data(
482
+ chain_id: Optional[str] = None,
483
+ channel_id: Optional[str] = None,
484
+ port_id: Optional[str] = None,
485
+ callback_id: Optional[str] = None,
486
+ block_height: int = 0,
487
+ ) -> List[Dict[str, Any]]:
488
+ """
489
+ Gets all the ica or ibc callback data stored on stride, with optional filters
490
+
491
+ E.g.
492
+ [
493
+ {
494
+ "callback_key": "icacontroller-cosmoshub-4.DELEGATION.channel-1.23",
495
+ "port_id": "icacontroller-cosmoshub-4.DELEGATION",
496
+ "channel_id": "channel-1",
497
+ "sequence": "23",
498
+ "callback_id": "delegate",
499
+ "callback_args": "...bytestring..."
500
+ }
501
+ ]
502
+ """
503
+ url = f"{config.stride.api_endpoint}/{CALLBACK_DATA_QUERY}"
504
+ callback_data = query_list_with_pagination(url, rel_key="callback_data", block_height=block_height)
505
+
506
+ filtered_callback_data = []
507
+ for data in callback_data:
508
+
509
+ chain_id_match = not chain_id or chain_id in data["port_id"]
510
+ channel_id_match = not channel_id or data["channel_id"] == channel_id
511
+ port_id_match = not port_id or data["port_id"] == port_id
512
+ callback_id_match = not callback_id or data["callback_id"] == callback_id
513
+
514
+ if all([chain_id_match, channel_id_match, port_id_match, callback_id_match]):
515
+ filtered_callback_data.append(data)
516
+
517
+ return filtered_callback_data
518
+
519
+
341
520
  def get_validators(api_endpoint: str = config.stride.api_endpoint) -> List[Dict[str, Any]]:
342
521
  """
343
522
  Queries the list of validators from a chain
@@ -790,17 +969,124 @@ def get_epochs() -> List[Dict]:
790
969
  return epochs["epochs"]
791
970
 
792
971
 
793
- def get_day_epoch() -> int:
972
+ def get_day_epoch() -> Dict[str, Any]:
794
973
  """
795
- Get the day epoch from the stride-labs/stride/epochs endpoint
974
+ Returns the "day" epoch struct on Stride
975
+
976
+ E.g.
977
+ {
978
+ "identifier": "day",
979
+ "start_time": "2022-09-04T19:00:00.451745Z",
980
+ "duration": "86400s",
981
+ "current_epoch": "607",
982
+ "current_epoch_start_time": "2024-05-02T19:00:00.451745Z",
983
+ "epoch_counting_started": true,
984
+ "current_epoch_start_height": "8829138"
985
+ }
796
986
  """
797
- epochs_request = f"{config.stride.api_endpoint}/Stridelabs/stride/epochs"
798
- epochs = request(epochs_request)["epochs"]
799
- epochs_df = pd.DataFrame(epochs)
800
- day_epoch = int(epochs_df[epochs_df["identifier"] == "day"]["current_epoch"].values[0])
987
+ epochs = get_epochs()
988
+ day_epoch = [epoch for epoch in epochs if epoch["identifier"] == "day"][0]
801
989
  return day_epoch
802
990
 
803
991
 
992
+ def get_stride_epoch() -> Dict[str, Any]:
993
+ """
994
+ Returns the "stride" epoch struct on stride
995
+
996
+ E.g.
997
+ {
998
+ "identifier": "stride_epoch",
999
+ "start_time": "2022-09-04T19:00:00.451745Z",
1000
+ "duration": "21600s",
1001
+ "current_epoch": "2425",
1002
+ "current_epoch_start_time": "2024-05-02T19:00:00.451745Z",
1003
+ "epoch_counting_started": true,
1004
+ "current_epoch_start_height": "8829138"
1005
+ }
1006
+ """
1007
+ epochs = get_epochs()
1008
+ stride_epoch = [epoch for epoch in epochs if epoch["identifier"] == "stride_epoch"][0]
1009
+ return stride_epoch
1010
+
1011
+
1012
+ def get_stride_epoch_number() -> int:
1013
+ """
1014
+ Returns the epoch number for the current "stride" epoch
1015
+ """
1016
+ stride_epoch = get_stride_epoch()
1017
+ return int(stride_epoch["current_epoch"])
1018
+
1019
+
1020
+ def get_day_epoch_number() -> int:
1021
+ """
1022
+ Returns the epoch number for the current "day" epoch
1023
+ """
1024
+ day_epoch = get_day_epoch()
1025
+ return int(day_epoch["current_epoch"])
1026
+
1027
+
1028
+ def get_stride_epoch_start_time() -> datetime.datetime:
1029
+ """
1030
+ Returns the epoch start time for the current "stride" epoch
1031
+ """
1032
+ stride_epoch = get_stride_epoch()
1033
+ return dateutil.parser.parse(stride_epoch["current_epoch_start_time"]).replace(tzinfo=None)
1034
+
1035
+
1036
+ def get_day_epoch_start_time() -> datetime.datetime:
1037
+ """
1038
+ Returns the epoch start time for the current "day" epoch
1039
+ """
1040
+ day_epoch = get_day_epoch()
1041
+ return dateutil.parser.parse(day_epoch["current_epoch_start_time"]).replace(tzinfo=None)
1042
+
1043
+
1044
+ def get_stride_epoch_start_height() -> int:
1045
+ """
1046
+ Returns the epoch start time for the current "stride" epoch
1047
+ """
1048
+ stride_epoch = get_stride_epoch()
1049
+ return int(stride_epoch["current_epoch_start_height"])
1050
+
1051
+
1052
+ def get_day_epoch_start_height() -> int:
1053
+ """
1054
+ Returns the epoch start time for the current "day" epoch
1055
+ """
1056
+ day_epoch = get_day_epoch()
1057
+ return int(day_epoch["current_epoch_start_height"])
1058
+
1059
+
1060
+ def get_host_zone_channels(chain_id: str) -> Dict[str, Any]:
1061
+ """
1062
+ Returns the JSON response from the channels endpoint, which holds the client and connection info,
1063
+ as well as all the ICA channels
1064
+
1065
+ E.g.
1066
+ {
1067
+ "chain_id": "cosmoshub-4",
1068
+ "client_id": "07-tendermint-0",
1069
+ "counterparty_client_id": "07-tendermint-913",
1070
+ "connection_id": "connection-0",
1071
+ "counterparty_connection_id": "connection-635",
1072
+ "transfer_channel_id": "channel-0",
1073
+ "counterparty_transfer_channel_id": "channel-391",
1074
+ "ica_channels": [
1075
+ {
1076
+ "type": "DELEGATION",
1077
+ "port_id": "icacontroller-cosmoshub-4.DELEGATION",
1078
+ "channel_id": "channel-203",
1079
+ "counterparty_channel_id": "channel-810",
1080
+ "state": "STATE_OPEN",
1081
+ "counterparty_state": "STATE_OPEN"
1082
+ },
1083
+ ...
1084
+ ]
1085
+ }
1086
+ """
1087
+ return request(HOST_CHANNELS_QUERY.format(chain_id=chain_id))
1088
+
1089
+
804
1090
  def query_wasm_contract(contract_address: str, query_msg: Dict, api_endpoint: str, block_height: int = 0) -> Dict:
805
1091
  """
806
1092
  Queries a cosmowasm contract address with the provided query message
@@ -0,0 +1,23 @@
1
+ from enum import Enum
2
+
3
+
4
+ class DepositRecordStatus(str, Enum):
5
+ TRANSFER_QUEUE = "TRANSFER_QUEUE"
6
+ TRANSFER_IN_PROGRESS = "TRANSFER_IN_PROGRESS"
7
+ DELEGATION_QUEUE = "DELEGATION_QUEUE"
8
+ DELEGATION_IN_PROGRESS = "DELEGATION_IN_PROGRESS"
9
+
10
+
11
+ class CallbackId(str, Enum):
12
+ TRANSFER = "transfer"
13
+ DELEGATE = "delegate"
14
+ UNDELEGATE = "undelegate"
15
+ REDEMPTION = "redemption"
16
+
17
+
18
+ class UnbondingRecordStatus(str, Enum):
19
+ UNBONDING_QUEUE = "UNBONDING_QUEUE"
20
+ UNBONDING_IN_PROGRESS = "UNBONDING_IN_PROGRESS"
21
+ EXIT_TRANSFER_QUEUE = "EXIT_TRANSFER_QUEUE"
22
+ EXIT_TRANSFER_IN_PROGRESS = "EXIT_TRANSFER_IN_PROGRESS"
23
+ CLAIMABLE = "CLAIMABLE"
File without changes