strideutils 0.3.5__tar.gz → 0.3.7__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.5 → strideutils-0.3.7}/PKG-INFO +2 -4
  2. {strideutils-0.3.5 → strideutils-0.3.7}/pyproject.toml +2 -2
  3. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/stride_requests.py +101 -44
  4. {strideutils-0.3.5 → strideutils-0.3.7}/README.md +0 -0
  5. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/Makefile +0 -0
  6. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/__init__.py +0 -0
  7. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/coingecko.py +0 -0
  8. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/config_examples/strideutils_config.yaml.example +0 -0
  9. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/config_examples/strideutils_secrets.yaml.example +0 -0
  10. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/cryptography.py +0 -0
  11. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/exec_tx.py +0 -0
  12. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/invariant_helpers.py +0 -0
  13. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/run_safely.py +0 -0
  14. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/sheets_connector.py +0 -0
  15. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/slack_connector.py +0 -0
  16. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/stride_alerts.py +0 -0
  17. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/stride_config.py +0 -0
  18. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/stride_upstash.py +0 -0
  19. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/twilio_connector.py +0 -0
  20. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils/types.py +0 -0
  21. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/__init__.pyi +0 -0
  22. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/coingecko.pyi +0 -0
  23. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/cryptography.pyi +0 -0
  24. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/exec_tx.pyi +0 -0
  25. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/invariant_helpers.pyi +0 -0
  26. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/run_safely.pyi +0 -0
  27. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/sheets_connector.pyi +0 -0
  28. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/slack_connector.pyi +0 -0
  29. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/stride_alerts.pyi +0 -0
  30. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/stride_config.pyi +0 -0
  31. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/stride_requests.pyi +0 -0
  32. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/stride_upstash.pyi +0 -0
  33. {strideutils-0.3.5 → strideutils-0.3.7}/strideutils-stubs/twilio_connector.pyi +0 -0
@@ -1,13 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: strideutils
3
- Version: 0.3.5
3
+ Version: 0.3.7
4
4
  Summary:
5
5
  Author: Your Name
6
6
  Author-email: you@example.com
7
- Requires-Python: >=3.9
7
+ Requires-Python: >=3.11
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.9
10
- Classifier: Programming Language :: Python :: 3.10
11
9
  Classifier: Programming Language :: Python :: 3.11
12
10
  Classifier: Programming Language :: Python :: 3.12
13
11
  Requires-Dist: bech32 (>=1.2.0)
@@ -1,13 +1,13 @@
1
1
  [tool.poetry]
2
2
  name = "strideutils"
3
- version = "0.3.5"
3
+ version = "0.3.7"
4
4
  description = ""
5
5
  authors = ["Your Name <you@example.com>"]
6
6
  readme = "README.md"
7
7
  include = ["strideutils", "strideutils-stubs"]
8
8
 
9
9
  [tool.poetry.dependencies]
10
- python = ">=3.9"
10
+ python = ">=3.11"
11
11
  google-api-python-client = ">=2.100.0"
12
12
  google-auth-httplib2 = ">=0.1.1"
13
13
  google-auth-oauthlib = ">=1.1.0"
@@ -7,7 +7,6 @@ from functools import lru_cache
7
7
  from typing import Any, Dict, List, Optional, Tuple, Union, cast
8
8
 
9
9
  import bech32
10
- import dateutil.parser
11
10
  import pandas as pd
12
11
  import pytz
13
12
  import requests
@@ -327,6 +326,61 @@ def get_unbonding_records(
327
326
  return host_zone_unbondings
328
327
 
329
328
 
329
+ def get_unbonding_queue_records(chain_id: str, block_height: int = 0) -> List[Dict[str, Any]]:
330
+ """
331
+ Queries all the unbonding records that are ready to initiate an unbonding
332
+ This is identified by records in status UNBONDING_QUEUE with a non-zero unbonding amount
333
+ """
334
+ unbonding_records = get_unbonding_records(
335
+ chain_id=chain_id,
336
+ block_height=block_height,
337
+ status=types.UnbondingRecordStatus.UNBONDING_QUEUE,
338
+ )
339
+ return [ur for ur in unbonding_records if int(ur["native_token_amount"]) > 0]
340
+
341
+
342
+ def get_unbonded_records(
343
+ chain_id: str,
344
+ block_height: int = 0,
345
+ filter_unbonded_last_epoch: bool = False,
346
+ ) -> List[Dict[str, Any]]:
347
+ """
348
+ Queries the list of all unbonding records records that have finished unbonding
349
+ This is identified by records in status EXIT_TRANSFER_QUEUE with an unbonding time
350
+ in the past
351
+ Optionally filter to records that just unbonded last epoch
352
+ """
353
+ unbonding_records = get_unbonding_records(
354
+ chain_id=chain_id,
355
+ block_height=block_height,
356
+ status=types.UnbondingRecordStatus.EXIT_TRANSFER_QUEUE,
357
+ )
358
+
359
+ # If we're fetching an older block, use that block's time as the "current time"
360
+ current_time = int(datetime.datetime.now().timestamp() * 1e9)
361
+ if block_height != 0:
362
+ current_time = int(get_block_time(block_height).timestamp() * 1e9)
363
+
364
+ # Filter to the records that have finished unbonding, and optionally also
365
+ # filter to records that just finished during the last epoch
366
+ day = 24 * 60 * 60 * 1e9
367
+ filtered_unbonding_records = []
368
+ for unbonding_record in unbonding_records:
369
+ unbonding_time = int(unbonding_record["unbonding_time"])
370
+
371
+ # Ignore records that haven't finished unbonding yet
372
+ if unbonding_time > current_time:
373
+ continue
374
+
375
+ # If filtering from last epoch, ignore records that unbonded over a day ago
376
+ if filter_unbonded_last_epoch and (current_time - unbonding_time) > day:
377
+ continue
378
+
379
+ filtered_unbonding_records.append(unbonding_record)
380
+
381
+ return filtered_unbonding_records
382
+
383
+
330
384
  def get_lsm_deposit_records(chain_id: str) -> List[Dict[str, Any]]:
331
385
  """
332
386
  Queries all the LSM deposit records
@@ -630,7 +684,7 @@ def get_block_time(height: int, rpc_endpoint: str = config.stride.rpc_endpoint)
630
684
  url = f"{rpc_endpoint}/block?height={height}"
631
685
  resp = request(url)
632
686
  block_time = resp["result"]["block"]["header"]["time"]
633
- block_datetime = dateutil.parser.parse(block_time).replace(tzinfo=None)
687
+ block_datetime = datetime.datetime.fromisoformat(block_time)
634
688
  return block_datetime
635
689
 
636
690
 
@@ -757,64 +811,67 @@ def generate_vesting_account(
757
811
 
758
812
 
759
813
  def get_block_height_in_past(
760
- time_in_past: datetime.timedelta = datetime.timedelta(hours=24),
761
- max_acceptable_error_seconds: int = 60 * 5,
814
+ target_time: Optional[datetime.datetime] = None,
815
+ target_time_offset: Optional[datetime.timedelta] = None,
816
+ error_tolerance: datetime.timedelta = datetime.timedelta(minutes=5),
762
817
  seconds_per_block: int = 6,
763
- prev_time: Optional[str] = None,
764
- verbose: bool = False,
765
818
  rpc_endpoint: str = config.stride.rpc_endpoint,
766
819
  ) -> int:
767
820
  """
768
821
  Gets the block height at a specified time by binary searching at different heights
769
822
 
770
823
  Args:
771
- time_in_past: target time to search for
772
- max_acceptable_error_seconds: max time delta before the height is considered find
824
+ target_time (datetime): the target time to search for
825
+ (e.g. search for the block at time 2024-01-01 01:00:00 UTC)
826
+ target_time_offset (timedelta): the target time to search for,
827
+ defined as an offset from the current time
828
+ (e.g. search for the block 24 hours ago)
829
+ error_tolerance (timedelta): max time delta before the height is considered found
773
830
  seconds_per_block: block time to use for the estimation start
774
- prev_time: Optional time to start the search at (instead of using time_in_past)
775
831
 
776
832
  Returns:
777
833
  The block height at the specified time
778
834
  """
835
+ # Get the current time and latest block height
836
+ current_time = datetime.datetime.now(tz=datetime.timezone.utc)
779
837
  latest_block = get_latest_block(rpc_endpoint=rpc_endpoint)
780
838
 
781
- # set parameters so we can search for the block height
782
- blocks_per_day = (24 * 60 * 60) // seconds_per_block
783
- if prev_time is not None:
784
- if type(prev_time) is str:
785
- prev_time: datetime.datetime = pd.to_datetime(prev_time) # type: ignore
786
- ts = (datetime.datetime.utcnow() - cast(datetime.datetime, prev_time)).total_seconds()
787
- num_days_back = ts / (60 * 60 * 24)
788
- time_in_past = datetime.timedelta(seconds=ts)
839
+ # Depending on whether the target time or target_time_offset was used,
840
+ # calculate the parameter that was not provided
841
+ if target_time and not target_time_offset:
842
+ target_time_offset = current_time - target_time
843
+ elif target_time_offset and not target_time:
844
+ target_time = current_time - target_time_offset
789
845
  else:
790
- num_days_back = time_in_past.total_seconds() / (60 * 60 * 24)
791
- blocks_in_past = int(num_days_back * blocks_per_day)
792
- time_estimate_error = float(100000)
793
-
794
- current_time = datetime.datetime.utcnow()
795
-
796
- # do an iterative search to find the block
797
- while abs(time_estimate_error) > max_acceptable_error_seconds:
798
- backwards_looking_height = latest_block - blocks_in_past
799
- backwards_looking_time = get_block_time(backwards_looking_height, rpc_endpoint=rpc_endpoint)
800
- time_estimate_error = (current_time - backwards_looking_time - time_in_past).total_seconds()
801
- error_in_blocks = time_estimate_error / seconds_per_block
802
- blocks_in_past -= int(error_in_blocks)
803
- if verbose:
804
- print("\tEstimated block: {}".format(backwards_looking_height))
805
- print("\tEstimated time: {}".format(backwards_looking_time))
806
- print("\tError in blocks: {}".format(error_in_blocks))
807
- print("\tTotal Seconds: {}".format(time_estimate_error))
808
- print("\t-------------------------")
809
- block_last_day = latest_block - blocks_in_past
810
- return block_last_day
811
-
812
-
813
- def get_block_height_one_day_ago(verbose: bool = False) -> int:
846
+ raise ValueError("Either target time or target time offset must be specified (but not both)")
847
+
848
+ # Using the target offset, determine the block delta to start our search, based
849
+ # on the estimated block time
850
+ blocks_in_past = int(target_time_offset.total_seconds() / seconds_per_block)
851
+ estimated_height = latest_block - blocks_in_past
852
+
853
+ # Continually loop over the following until we find a block that lines up with
854
+ # our target time (within our margin of error)
855
+ # - Guess a height
856
+ # - Find the time at that height
857
+ # - Check if the time is close enough to our target
858
+ # - Refine our block height guess and repeat
859
+ while True:
860
+ estimated_time = get_block_time(estimated_height, rpc_endpoint=rpc_endpoint)
861
+
862
+ time_error = target_time.timestamp() - estimated_time.timestamp()
863
+ if abs(time_error) < error_tolerance.total_seconds():
864
+ return estimated_height
865
+
866
+ blocks_correction = int(time_error / seconds_per_block)
867
+ estimated_height += blocks_correction
868
+
869
+
870
+ def get_block_height_one_day_ago() -> int:
814
871
  """
815
872
  Get the block height from 1 day ago
816
873
  """
817
- return get_block_height_in_past(datetime.timedelta(hours=24), verbose=verbose)
874
+ return get_block_height_in_past(target_time_offset=datetime.timedelta(hours=24))
818
875
 
819
876
 
820
877
  def get_sttoken_apr(token: str) -> Optional[float]:
@@ -1030,7 +1087,7 @@ def get_stride_epoch_start_time() -> datetime.datetime:
1030
1087
  Returns the epoch start time for the current "stride" epoch
1031
1088
  """
1032
1089
  stride_epoch = get_stride_epoch()
1033
- return dateutil.parser.parse(stride_epoch["current_epoch_start_time"]).replace(tzinfo=None)
1090
+ return datetime.datetime.fromisoformat(stride_epoch["current_epoch_start_time"])
1034
1091
 
1035
1092
 
1036
1093
  def get_day_epoch_start_time() -> datetime.datetime:
@@ -1038,7 +1095,7 @@ def get_day_epoch_start_time() -> datetime.datetime:
1038
1095
  Returns the epoch start time for the current "day" epoch
1039
1096
  """
1040
1097
  day_epoch = get_day_epoch()
1041
- return dateutil.parser.parse(day_epoch["current_epoch_start_time"]).replace(tzinfo=None)
1098
+ return datetime.datetime.fromisoformat(day_epoch["current_epoch_start_time"])
1042
1099
 
1043
1100
 
1044
1101
  def get_stride_epoch_start_height() -> int:
File without changes