blockapi 2.1.1__tar.gz → 2.2.0__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 (104) hide show
  1. {blockapi-2.1.1 → blockapi-2.2.0}/PKG-INFO +1 -1
  2. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/__init__.py +6 -1
  3. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/debank.py +127 -1
  4. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/models.py +178 -1
  5. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi.egg-info/PKG-INFO +1 -1
  6. {blockapi-2.1.1 → blockapi-2.2.0}/setup.py +1 -1
  7. {blockapi-2.1.1 → blockapi-2.2.0}/LICENSE.md +0 -0
  8. {blockapi-2.1.1 → blockapi-2.2.0}/README.md +0 -0
  9. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/__init__.py +0 -0
  10. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/services.py +0 -0
  11. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/__init__.py +0 -0
  12. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/test_blockapi.py +0 -0
  13. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/test_num.py +0 -0
  14. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/test_random_user_agent.py +0 -0
  15. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/__init__.py +0 -0
  16. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/__init__.py +0 -0
  17. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/conftest.py +0 -0
  18. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/covalenth/__init__.py +0 -0
  19. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/covalenth/test_ethereum.py +0 -0
  20. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/fake_sleep_provider.py +0 -0
  21. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/nft/__init__.py +0 -0
  22. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/nft/test_magic_eden.py +0 -0
  23. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/nft/test_opensea.py +0 -0
  24. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/nft/test_simple_hash.py +0 -0
  25. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/nft/test_unisat.py +0 -0
  26. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/perpetual/__init__.py +0 -0
  27. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/perpetual/test_perpetual.py +0 -0
  28. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/synthetix/__init__.py +0 -0
  29. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/synthetix/test_synthetix.py +0 -0
  30. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_blockchainos.py +0 -0
  31. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_blockchair_btc.py +0 -0
  32. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_blockchair_doge.py +0 -0
  33. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_blockchair_ltc.py +0 -0
  34. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_cosmos.py +0 -0
  35. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_ethplorer.py +0 -0
  36. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_multisources.py +0 -0
  37. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_optimistic_etherscan.py +0 -0
  38. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_solana.py +0 -0
  39. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_subscan_polkadot.py +0 -0
  40. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_sui.py +0 -0
  41. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/api/test_trezor_btc.py +0 -0
  42. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/test_base.py +0 -0
  43. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/test_blockchain_api.py +0 -0
  44. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/test_blockchain_mapping.py +0 -0
  45. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/test_data.py +0 -0
  46. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/test_enumerate_classes.py +0 -0
  47. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/test_generic.py +0 -0
  48. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test/v2/test_models.py +0 -0
  49. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/test_data.py +0 -0
  50. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/utils/__init__.py +0 -0
  51. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/utils/address.py +0 -0
  52. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/utils/datetime.py +0 -0
  53. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/utils/num.py +0 -0
  54. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/utils/user_agent.py +0 -0
  55. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/__init__.py +0 -0
  56. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/blockchainos.py +0 -0
  57. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/blockchair.py +0 -0
  58. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/cosmos.py +0 -0
  59. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/__init__.py +0 -0
  60. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/arbitrum.py +0 -0
  61. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/astar.py +0 -0
  62. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/avalanche.py +0 -0
  63. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/axie.py +0 -0
  64. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/base.py +0 -0
  65. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/binance_smart_chain.py +0 -0
  66. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/ethereum.py +0 -0
  67. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/fantom.py +0 -0
  68. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/heco.py +0 -0
  69. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/iotex.py +0 -0
  70. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/klaytn.py +0 -0
  71. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/moonbeam.py +0 -0
  72. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/palm.py +0 -0
  73. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/polygon.py +0 -0
  74. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/covalenth/rsk.py +0 -0
  75. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/debank_maps.py +0 -0
  76. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/ethplorer.py +0 -0
  77. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/nft/__init__.py +0 -0
  78. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/nft/magic_eden.py +0 -0
  79. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/nft/opensea.py +0 -0
  80. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/nft/simple_hash.py +0 -0
  81. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/nft/unisat.py +0 -0
  82. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/optimistic_etherscan.py +0 -0
  83. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/perpetual/__init__.py +0 -0
  84. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/perpetual/perp_abi.py +0 -0
  85. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/perpetual/perpetual.py +0 -0
  86. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/solana.py +0 -0
  87. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/subscan.py +0 -0
  88. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/sui.py +0 -0
  89. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/synthetix/__init__.py +0 -0
  90. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/synthetix/synthetix.py +0 -0
  91. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/synthetix/synthetix_abi.py +0 -0
  92. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/terra.py +0 -0
  93. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/trezor.py +0 -0
  94. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/api/web3_utils.py +0 -0
  95. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/base.py +0 -0
  96. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/blockchain_mapping.py +0 -0
  97. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/coin_mapping.py +0 -0
  98. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi/v2/coins.py +0 -0
  99. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi.egg-info/SOURCES.txt +0 -0
  100. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi.egg-info/dependency_links.txt +0 -0
  101. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi.egg-info/requires.txt +0 -0
  102. {blockapi-2.1.1 → blockapi-2.2.0}/blockapi.egg-info/top_level.txt +0 -0
  103. {blockapi-2.1.1 → blockapi-2.2.0}/pyproject.toml +0 -0
  104. {blockapi-2.1.1 → blockapi-2.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blockapi
3
- Version: 2.1.1
3
+ Version: 2.2.0
4
4
  Summary: BlockAPI library
5
5
  Home-page: https://github.com/crypkit/blockapi
6
6
  Author: Devmons s.r.o.
@@ -5,7 +5,12 @@ from blockapi.v2.api.blockchair import (
5
5
  BlockchairDogecoinApi,
6
6
  BlockchairLitecoinApi,
7
7
  )
8
- from blockapi.v2.api.debank import DebankApi
8
+ from blockapi.v2.api.debank import (
9
+ DebankApi,
10
+ DebankApp,
11
+ DebankAppDeposit,
12
+ DebankPrediction,
13
+ )
9
14
  from blockapi.v2.api.ethplorer import EthplorerApi
10
15
  from blockapi.v2.api.optimistic_etherscan import OptimismEtherscanApi
11
16
  from blockapi.v2.api.perpetual import PerpetualApi, perp_contract_address
@@ -9,7 +9,7 @@ from pydantic import BaseModel, validator
9
9
 
10
10
  from blockapi.utils.address import make_checksum_address
11
11
  from blockapi.utils.datetime import parse_dt
12
- from blockapi.utils.num import decimals_to_raw
12
+ from blockapi.utils.num import decimals_to_raw, to_decimal
13
13
  from blockapi.v2.api.debank_maps import (
14
14
  COINGECKO_IDS_BY_CONTRACTS,
15
15
  DEBANK_ASSET_TYPES,
@@ -35,6 +35,13 @@ from blockapi.v2.models import (
35
35
  Pool,
36
36
  PoolInfo,
37
37
  Protocol,
38
+ DebankApp,
39
+ DebankAppDeposit,
40
+ DebankPrediction,
41
+ DebankModelAppPortfolioItem,
42
+ DebankModelApp,
43
+ DebankModelDepositDetail,
44
+ DebankModelPredictionDetail,
38
45
  )
39
46
 
40
47
  logger = logging.getLogger(__name__)
@@ -592,6 +599,109 @@ class DebankPortfolioParser:
592
599
  return REWARD_ASSET_TYPE_MAP.get(asset_type, asset_type)
593
600
 
594
601
 
602
+ class DebankAppParser:
603
+ """Parser for Debank complex_app_list responses."""
604
+
605
+ def parse(self, response: list) -> list[DebankApp]:
606
+ """Parse the full response from get_complex_app_list."""
607
+ if not response:
608
+ return []
609
+
610
+ apps = []
611
+ for item in response:
612
+ app = self._parse_app(item)
613
+ if app:
614
+ apps.append(app)
615
+
616
+ return apps
617
+
618
+ def _parse_app(self, raw_app: dict) -> Optional[DebankApp]:
619
+ """Parse a single app from the response."""
620
+ try:
621
+ model = DebankModelApp(**raw_app)
622
+ except Exception as e:
623
+ logger.error(f'Failed to parse app: {e}')
624
+ return None
625
+
626
+ deposits = []
627
+ predictions = []
628
+
629
+ for portfolio_item in model.portfolio_item_list:
630
+ detail_types = portfolio_item.detail_types
631
+
632
+ if 'prediction' in detail_types:
633
+ prediction = self._parse_prediction(portfolio_item)
634
+ if prediction:
635
+ predictions.append(prediction)
636
+ else:
637
+ # Parse as deposit (common, etc.)
638
+ deposit = self._parse_deposit(portfolio_item)
639
+ if deposit:
640
+ deposits.append(deposit)
641
+
642
+ return DebankApp.from_api(
643
+ app_id=model.id,
644
+ name=model.name,
645
+ site_url=model.site_url,
646
+ logo_url=model.logo_url,
647
+ has_supported_portfolio=model.has_supported_portfolio,
648
+ deposits=deposits,
649
+ predictions=predictions,
650
+ )
651
+
652
+ def _parse_prediction(
653
+ self, item: DebankModelAppPortfolioItem
654
+ ) -> Optional[DebankPrediction]:
655
+ """Parse a prediction market position."""
656
+ try:
657
+ detail = DebankModelPredictionDetail(**item.detail)
658
+ except Exception as e:
659
+ logger.error(f'Failed to parse prediction detail: {e}')
660
+ return None
661
+
662
+ return DebankPrediction.from_api(
663
+ prediction_name=detail.name,
664
+ side=detail.side,
665
+ amount=detail.amount,
666
+ price=detail.price,
667
+ usd_value=item.stats.net_usd_value,
668
+ claimable=detail.claimable,
669
+ event_end_at=detail.event_end_at,
670
+ is_market_closed=detail.is_market_closed,
671
+ position_index=item.position_index,
672
+ update_at=item.update_at,
673
+ )
674
+
675
+ def _parse_deposit(
676
+ self, item: DebankModelAppPortfolioItem
677
+ ) -> Optional[DebankAppDeposit]:
678
+ """Parse a deposit/common type portfolio item."""
679
+ try:
680
+ detail = DebankModelDepositDetail(**item.detail)
681
+ except Exception as e:
682
+ logger.warning(f'Failed to parse deposit detail: {e}')
683
+ detail = DebankModelDepositDetail()
684
+
685
+ # Collect all tokens from supply, borrow, and reward lists
686
+ tokens = []
687
+ if detail.supply_token_list:
688
+ tokens.extend(detail.supply_token_list)
689
+ if detail.borrow_token_list:
690
+ tokens.extend(detail.borrow_token_list)
691
+ if detail.reward_token_list:
692
+ tokens.extend(detail.reward_token_list)
693
+
694
+ return DebankAppDeposit.from_api(
695
+ name=item.name,
696
+ asset_usd_value=item.stats.asset_usd_value,
697
+ debt_usd_value=item.stats.debt_usd_value,
698
+ net_usd_value=item.stats.net_usd_value,
699
+ tokens=tokens,
700
+ position_index=item.position_index,
701
+ update_at=item.update_at,
702
+ )
703
+
704
+
595
705
  class DebankApi(CustomizableBlockchainApi, BalanceMixin, IPortfolio):
596
706
  """
597
707
  DeBank OpenApi: https://open.debank.com/
@@ -613,6 +723,7 @@ class DebankApi(CustomizableBlockchainApi, BalanceMixin, IPortfolio):
613
723
  'get_portfolio': '/v1/user/all_complex_protocol_list?id={address}',
614
724
  'get_protocols': '/v1/protocol/all_list',
615
725
  'usage': '/v1/account/units',
726
+ 'get_complex_app_list': '/v1/user/complex_app_list?id={address}',
616
727
  }
617
728
 
618
729
  default_protocol_cache = DebankProtocolCache()
@@ -636,6 +747,7 @@ class DebankApi(CustomizableBlockchainApi, BalanceMixin, IPortfolio):
636
747
  self._protocol_parser, self._balance_parser
637
748
  )
638
749
  self._usage_parser = DebankUsageParser()
750
+ self._app_parser = DebankAppParser()
639
751
 
640
752
  def fetch_balances(self, address: str) -> FetchResult:
641
753
  return self.get_data(
@@ -652,6 +764,13 @@ class DebankApi(CustomizableBlockchainApi, BalanceMixin, IPortfolio):
652
764
  address=address,
653
765
  )
654
766
 
767
+ def fetch_debank_apps(self, address: str) -> FetchResult:
768
+ return self.get_data(
769
+ 'get_complex_app_list',
770
+ headers=self._headers,
771
+ address=address,
772
+ )
773
+
655
774
  def fetch_protocols(self) -> FetchResult:
656
775
  return self.get_data(
657
776
  'get_protocols',
@@ -685,6 +804,13 @@ class DebankApi(CustomizableBlockchainApi, BalanceMixin, IPortfolio):
685
804
 
686
805
  return self._protocol_parser.parse(response)
687
806
 
807
+ def parse_debank_apps(self, fetch_result: FetchResult) -> ParseResult:
808
+ if error := self._get_error(fetch_result.data):
809
+ return ParseResult(errors=[error])
810
+
811
+ apps = self._app_parser.parse(fetch_result.data)
812
+ return ParseResult(data=apps)
813
+
688
814
  def get_chains(self) -> list[DebankChain]:
689
815
  response = self.get('get_chains', headers=self._headers)
690
816
  if self._has_error(response):
@@ -8,6 +8,7 @@ import attr
8
8
 
9
9
  from blockapi.utils.datetime import parse_dt
10
10
  from blockapi.utils.num import raw_to_decimals, to_decimal, to_int
11
+ from pydantic import BaseModel
11
12
 
12
13
  UNKNOWN = 'unknown'
13
14
 
@@ -386,6 +387,7 @@ class AssetType(str, Enum):
386
387
  LOCKED = 'locked'
387
388
  NFT = 'nft'
388
389
  PENDING_TRANSACTION = 'pending_transaction'
390
+ PREDICTION = 'prediction'
389
391
  PRICED_VESTING = 'priced_vesting'
390
392
  REWARDS = 'rewards'
391
393
  STAKED = 'staked'
@@ -1172,6 +1174,181 @@ class Pool:
1172
1174
  self.items.extend(items)
1173
1175
 
1174
1176
 
1177
+ # --- Debank App Models ---
1178
+
1179
+
1180
+ class DebankModelAppStats(BaseModel):
1181
+ """Stats for a portfolio item."""
1182
+
1183
+ asset_usd_value: float
1184
+ debt_usd_value: float
1185
+ net_usd_value: float
1186
+
1187
+
1188
+ class DebankModelPredictionDetail(BaseModel):
1189
+ """Detail for prediction type portfolio items."""
1190
+
1191
+ name: str
1192
+ side: str
1193
+ amount: float
1194
+ price: float
1195
+ claimable: bool
1196
+ is_market_closed: bool
1197
+ event_end_at: Optional[float] = None
1198
+
1199
+
1200
+ class DebankModelDepositDetail(BaseModel):
1201
+ """Detail for deposit/common type portfolio items."""
1202
+
1203
+ supply_token_list: Optional[list[dict]] = None
1204
+ borrow_token_list: Optional[list[dict]] = None
1205
+ reward_token_list: Optional[list[dict]] = None
1206
+
1207
+
1208
+ class DebankModelAppPortfolioItem(BaseModel):
1209
+ """Portfolio item within an app."""
1210
+
1211
+ stats: DebankModelAppStats
1212
+ name: str
1213
+ detail_types: list[str]
1214
+ detail: dict
1215
+ position_index: str
1216
+ asset_dict: Optional[dict] = None
1217
+ asset_token_list: Optional[list[dict]] = None
1218
+ update_at: Optional[float] = None
1219
+ proxy_detail: Optional[dict] = None
1220
+
1221
+
1222
+ class DebankModelApp(BaseModel):
1223
+ """Debank App model from complex_app_list endpoint."""
1224
+
1225
+ id: str
1226
+ name: str
1227
+ portfolio_item_list: list[DebankModelAppPortfolioItem]
1228
+ site_url: Optional[str] = None
1229
+ logo_url: Optional[str] = None
1230
+ has_supported_portfolio: bool = False
1231
+
1232
+
1233
+ @attr.s(auto_attribs=True, slots=True, frozen=True)
1234
+ class DebankPrediction:
1235
+ """Represents a prediction market position (e.g., Polymarket)."""
1236
+
1237
+ prediction_name: str
1238
+ side: str
1239
+ amount: Decimal
1240
+ price: Decimal
1241
+ usd_value: Decimal
1242
+ claimable: bool
1243
+ event_end_at: Optional[datetime]
1244
+ is_market_closed: bool
1245
+ position_index: Optional[str]
1246
+ update_at: Optional[datetime]
1247
+
1248
+ @classmethod
1249
+ def from_api(
1250
+ cls,
1251
+ *,
1252
+ prediction_name: str,
1253
+ side: str,
1254
+ amount: Union[str, float, int],
1255
+ price: Union[str, float, int],
1256
+ usd_value: Union[str, float, int],
1257
+ claimable: bool,
1258
+ is_market_closed: bool,
1259
+ event_end_at: Optional[Union[int, float]] = None,
1260
+ position_index: Optional[str] = None,
1261
+ update_at: Optional[Union[int, float]] = None,
1262
+ ) -> 'DebankPrediction':
1263
+ return cls(
1264
+ prediction_name=prediction_name,
1265
+ side=side,
1266
+ amount=to_decimal(amount),
1267
+ price=to_decimal(price),
1268
+ usd_value=to_decimal(usd_value),
1269
+ claimable=claimable,
1270
+ event_end_at=parse_dt(event_end_at) if event_end_at is not None else None,
1271
+ is_market_closed=is_market_closed,
1272
+ position_index=position_index,
1273
+ update_at=parse_dt(update_at) if update_at is not None else None,
1274
+ )
1275
+
1276
+
1277
+ @attr.s(auto_attribs=True, slots=True, frozen=True)
1278
+ class DebankAppDeposit:
1279
+ """Represents a deposit/holding within a Debank App (e.g., Polymarket cash deposit)."""
1280
+
1281
+ name: str
1282
+ asset_usd_value: Decimal
1283
+ debt_usd_value: Decimal
1284
+ net_usd_value: Decimal
1285
+ tokens: list[dict] # Raw token data for flexibility
1286
+ position_index: Optional[str]
1287
+ update_at: Optional[datetime]
1288
+
1289
+ @classmethod
1290
+ def from_api(
1291
+ cls,
1292
+ *,
1293
+ name: str,
1294
+ asset_usd_value: Union[str, float, int],
1295
+ debt_usd_value: Union[str, float, int],
1296
+ net_usd_value: Union[str, float, int],
1297
+ position_index: str,
1298
+ tokens: Optional[list[dict]] = None,
1299
+ update_at: Optional[Union[int, float]] = None,
1300
+ ) -> 'DebankAppDeposit':
1301
+ return cls(
1302
+ name=name,
1303
+ asset_usd_value=to_decimal(asset_usd_value),
1304
+ debt_usd_value=to_decimal(debt_usd_value),
1305
+ net_usd_value=to_decimal(net_usd_value),
1306
+ tokens=tokens or [],
1307
+ position_index=position_index,
1308
+ update_at=parse_dt(update_at) if update_at else None,
1309
+ )
1310
+
1311
+ @property
1312
+ def token_symbols(self) -> list[str]:
1313
+ """Get list of token symbols in this deposit."""
1314
+ return [t.get('symbol', t.get('name', '')) for t in self.tokens]
1315
+
1316
+
1317
+ @attr.s(auto_attribs=True, slots=True, frozen=True)
1318
+ class DebankApp:
1319
+ """Represents a Debank App with its deposits and predictions."""
1320
+
1321
+ app_id: str
1322
+ name: str
1323
+ site_url: Optional[str]
1324
+ logo_url: Optional[str]
1325
+ has_supported_portfolio: bool
1326
+ deposits: list[DebankAppDeposit]
1327
+ predictions: list[DebankPrediction]
1328
+
1329
+ @classmethod
1330
+ def from_api(
1331
+ cls,
1332
+ *,
1333
+ app_id: str,
1334
+ name: str,
1335
+ site_url: Optional[str] = None,
1336
+ logo_url: Optional[str] = None,
1337
+ has_supported_portfolio: bool = False,
1338
+ deposits: Optional[list[DebankAppDeposit]] = None,
1339
+ predictions: Optional[list[DebankPrediction]] = None,
1340
+ ) -> 'DebankApp':
1341
+ return cls(
1342
+ app_id=app_id,
1343
+ name=name,
1344
+ site_url=site_url,
1345
+ logo_url=logo_url,
1346
+ has_supported_portfolio=has_supported_portfolio,
1347
+ deposits=deposits or [],
1348
+ predictions=predictions or [],
1349
+ )
1350
+
1351
+
1175
1352
  @attr.s(auto_attribs=True, slots=True)
1176
1353
  class FetchResult:
1177
1354
  status_code: Optional[int] = None
@@ -1204,7 +1381,7 @@ class FetchResult:
1204
1381
  @attr.s(auto_attribs=True, slots=True, frozen=True)
1205
1382
  class ParseResult:
1206
1383
  data: Optional[
1207
- list[Union[BalanceItem, Pool, NftToken, NftCollection, NftOffer]]
1384
+ list[Union[BalanceItem, Pool, NftToken, NftCollection, NftOffer, DebankApp]]
1208
1385
  ] = None
1209
1386
  warnings: Optional[list[Union[str, dict]]] = None
1210
1387
  errors: Optional[list[Union[str, dict]]] = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blockapi
3
- Version: 2.1.1
3
+ Version: 2.2.0
4
4
  Summary: BlockAPI library
5
5
  Home-page: https://github.com/crypkit/blockapi
6
6
  Author: Devmons s.r.o.
@@ -6,7 +6,7 @@ with open("README.md", "r") as f:
6
6
 
7
7
  PACKAGES = find_packages(where='.')
8
8
 
9
- __version__ = "2.1.1"
9
+ __version__ = "2.2.0"
10
10
 
11
11
  setuptools.setup(
12
12
  name='blockapi',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes