blockapi 0.48.2__tar.gz → 0.48.4__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 (156) hide show
  1. {blockapi-0.48.2 → blockapi-0.48.4}/PKG-INFO +1 -1
  2. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_solana.py +49 -1
  3. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/solana.py +110 -7
  4. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/PKG-INFO +1 -1
  5. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/requires.txt +1 -0
  6. {blockapi-0.48.2 → blockapi-0.48.4}/setup.py +2 -1
  7. {blockapi-0.48.2 → blockapi-0.48.4}/LICENSE.md +0 -0
  8. {blockapi-0.48.2 → blockapi-0.48.4}/README.md +0 -0
  9. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/__init__.py +0 -0
  10. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/__init__.py +0 -0
  11. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/alethio.py +0 -0
  12. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/amberdata.py +0 -0
  13. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/binance.py +0 -0
  14. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockchaininfo.py +0 -0
  15. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockchainos.py +0 -0
  16. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockchair.py +0 -0
  17. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockcypher.py +0 -0
  18. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockonomics.py +0 -0
  19. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockscout.py +0 -0
  20. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/btc.py +0 -0
  21. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/cardanoexplorer.py +0 -0
  22. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/chainso.py +0 -0
  23. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/cosmos.py +0 -0
  24. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/cryptoid.py +0 -0
  25. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/dcrdata.py +0 -0
  26. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/digonchain.py +0 -0
  27. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/eospark.py +0 -0
  28. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/etherscan.py +0 -0
  29. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/ethplorer.py +0 -0
  30. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/greymass.py +0 -0
  31. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/insight.py +0 -0
  32. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/kyber.py +0 -0
  33. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/neoscan.py +0 -0
  34. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/ontology.py +0 -0
  35. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/solana.py +0 -0
  36. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/stellar.py +0 -0
  37. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/subscan.py +0 -0
  38. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/terra_money.py +0 -0
  39. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/trezor.py +0 -0
  40. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/tronscan.py +0 -0
  41. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/tzscan.py +0 -0
  42. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/tzstats.py +0 -0
  43. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/zchain.py +0 -0
  44. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/zensystem.py +0 -0
  45. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/services.py +0 -0
  46. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/__init__.py +0 -0
  47. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/__init__.py +0 -0
  48. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_amberdata.py +0 -0
  49. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_balances.py +0 -0
  50. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_binance.py +0 -0
  51. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_blockchainos.py +0 -0
  52. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_blockchair.py +0 -0
  53. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_blockscout.py +0 -0
  54. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_btc.py +0 -0
  55. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_cosmos.py +0 -0
  56. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_dcrdata.py +0 -0
  57. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_digonchain.py +0 -0
  58. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_insight.py +0 -0
  59. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_kyber.py +0 -0
  60. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_ontio.py +0 -0
  61. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_solana.py +0 -0
  62. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_subscan.py +0 -0
  63. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_terra_money.py +0 -0
  64. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_tronscan.py +0 -0
  65. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_tzscan.py +0 -0
  66. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_tzstats.py +0 -0
  67. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/test_blockapi.py +0 -0
  68. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/test_num.py +0 -0
  69. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/test_random_user_agent.py +0 -0
  70. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/__init__.py +0 -0
  71. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/__init__.py +0 -0
  72. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/conftest.py +0 -0
  73. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/covalenth/__init__.py +0 -0
  74. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/covalenth/test_ethereum.py +0 -0
  75. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/fake_sleep_provider.py +0 -0
  76. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/nft/__init__.py +0 -0
  77. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/nft/test_magic_eden.py +0 -0
  78. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/nft/test_opensea.py +0 -0
  79. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/nft/test_simple_hash.py +0 -0
  80. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/perpetual/__init__.py +0 -0
  81. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/perpetual/test_perpetual.py +0 -0
  82. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/synthetix/__init__.py +0 -0
  83. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/synthetix/test_synthetix.py +0 -0
  84. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_blockchainos.py +0 -0
  85. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_blockchair_btc.py +0 -0
  86. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_blockchair_doge.py +0 -0
  87. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_blockchair_ltc.py +0 -0
  88. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_cosmos.py +0 -0
  89. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_ethplorer.py +0 -0
  90. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_multisources.py +0 -0
  91. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_optimistic_etherscan.py +0 -0
  92. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_subscan_polkadot.py +0 -0
  93. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_trezor_btc.py +0 -0
  94. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_base.py +0 -0
  95. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_blockchain_api.py +0 -0
  96. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_blockchain_mapping.py +0 -0
  97. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_data.py +0 -0
  98. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_enumerate_classes.py +0 -0
  99. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_generic.py +0 -0
  100. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_models.py +0 -0
  101. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test_data.py +0 -0
  102. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/__init__.py +0 -0
  103. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/address.py +0 -0
  104. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/datetime.py +0 -0
  105. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/ethereum.py +0 -0
  106. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/num.py +0 -0
  107. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/user_agent.py +0 -0
  108. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/__init__.py +0 -0
  109. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/__init__.py +0 -0
  110. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/blockchainos.py +0 -0
  111. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/blockchair.py +0 -0
  112. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/cosmos.py +0 -0
  113. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/__init__.py +0 -0
  114. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/arbitrum.py +0 -0
  115. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/astar.py +0 -0
  116. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/avalanche.py +0 -0
  117. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/axie.py +0 -0
  118. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/base.py +0 -0
  119. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/binance_smart_chain.py +0 -0
  120. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/ethereum.py +0 -0
  121. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/fantom.py +0 -0
  122. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/heco.py +0 -0
  123. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/iotex.py +0 -0
  124. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/klaytn.py +0 -0
  125. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/moonbeam.py +0 -0
  126. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/palm.py +0 -0
  127. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/polygon.py +0 -0
  128. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/rsk.py +0 -0
  129. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/debank.py +0 -0
  130. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/debank_maps.py +0 -0
  131. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/ethplorer.py +0 -0
  132. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/nft/__init__.py +0 -0
  133. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/nft/magic_eden.py +0 -0
  134. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/nft/opensea.py +0 -0
  135. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/nft/simple_hash.py +0 -0
  136. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/optimistic_etherscan.py +0 -0
  137. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/perpetual/__init__.py +0 -0
  138. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/perpetual/perp_abi.py +0 -0
  139. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/perpetual/perpetual.py +0 -0
  140. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/subscan.py +0 -0
  141. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/synthetix/__init__.py +0 -0
  142. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/synthetix/synthetix.py +0 -0
  143. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/synthetix/synthetix_abi.py +0 -0
  144. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/terra.py +0 -0
  145. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/trezor.py +0 -0
  146. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/web3_utils.py +0 -0
  147. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/base.py +0 -0
  148. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/blockchain_mapping.py +0 -0
  149. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/coin_mapping.py +0 -0
  150. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/coins.py +0 -0
  151. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/models.py +0 -0
  152. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/SOURCES.txt +0 -0
  153. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/dependency_links.txt +0 -0
  154. {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/top_level.txt +0 -0
  155. {blockapi-0.48.2 → blockapi-0.48.4}/pyproject.toml +0 -0
  156. {blockapi-0.48.2 → blockapi-0.48.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: blockapi
3
- Version: 0.48.2
3
+ Version: 0.48.4
4
4
  Summary: BlockAPI library
5
5
  Home-page: UNKNOWN
6
6
  Author: Devmons s.r.o.
@@ -3,6 +3,7 @@ from decimal import Decimal
3
3
 
4
4
  import pytest
5
5
  from requests_mock import ANY, Mocker
6
+ from solders.pubkey import Pubkey
6
7
 
7
8
  from blockapi.test.v2.api.conftest import read_file
8
9
  from blockapi.v2.api import SolanaApi, SolscanApi
@@ -99,7 +100,7 @@ def test_use_base_url_in_post(
99
100
  m.get(SONAR_TOKEN_LIST_URL, text=token_list_sonar_response)
100
101
  m.get(JUP_AG_BAN_LIST_URL, text=ban_list_jup_ag_response)
101
102
  m.post(ANY, text=get_text),
102
- api = SolanaApi(base_url='https://proxy/solana/')
103
+ api = SolanaApi(base_url='https://proxy/solana/', fetch_metaplex=False)
103
104
  api.get_balance(test_addr)
104
105
 
105
106
 
@@ -496,3 +497,50 @@ def balances_with_different_coins():
496
497
  is_wallet=True,
497
498
  ),
498
499
  ]
500
+
501
+
502
+ def test_derive_metaplex_account_address_from_mint(solana_api):
503
+ mint = 'C2PxCHLeDkp1BsG1uueu7aGEfXQkKoXxzTgMPQ5DA6QW'
504
+ metadata = solana_api.get_metadata_pda(mint)
505
+
506
+ assert metadata == '3G5bT5bgpdwiUbYHfoBSe6SDAwiQaTKc3TFGks7bA3Qw'
507
+
508
+
509
+ @pytest.mark.vcr()
510
+ def test_fetch_metaplex_account(solana_api, metaplex_content):
511
+ data = solana_api.fetch_metaplex_account(
512
+ '3G5bT5bgpdwiUbYHfoBSe6SDAwiQaTKc3TFGks7bA3Qw'
513
+ )
514
+ assert data == metaplex_content
515
+
516
+
517
+ @pytest.mark.vcr()
518
+ def test_parse_metaplex_account(solana_api, metaplex_content):
519
+ token = solana_api.parse_metaplex_account(metaplex_content, decimals=9)
520
+ assert token['symbol'] == 'ACT'
521
+ assert token['name'] == 'Act I : The AI Prophecy'
522
+ assert token['decimals'] == 9
523
+ assert token['chainId'] == 101
524
+ assert token['tags'] == ['SOLANA', 'MEME']
525
+ assert (
526
+ token['logoURI']
527
+ == 'https://gateway.pinata.cloud/ipfs/QmS4m4cBjukg7YhimqhfRUb2pUE4bBPXbrbMBb5hzxNbUA'
528
+ )
529
+
530
+
531
+ @pytest.fixture
532
+ def metaplex_content():
533
+ return (
534
+ 'BJ5okXRntUEewuGiuQOIMWt8+YkvFnY7/DoghPyz5qwNo8wWpzohlcKPjWIDCl3nggGst3'
535
+ 'CeFvU+P1wA6yNMhm8gAAAAQWN0IEkgOiBUaGUgQUkgUHJvcGhlY3kAAAAAAAAAAAAKAAAA'
536
+ 'QUNUAAAAAAAAAMgAAABodHRwczovL2dhdGV3YXkucGluYXRhLmNsb3VkL2lwZnMvUW1WZk'
537
+ '1QQ1VQczVWMW9tYUR2b25pMzh3OGFFSnBGVGpFWlJOUm1vVnlleFhXZQAAAAAAAAAAAAAA'
538
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
539
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
540
+ 'AAAAAAAAAAAAAf4BAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
541
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
542
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
543
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
544
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
545
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ=='
546
+ )
@@ -1,8 +1,12 @@
1
+ import base64
1
2
  import json
3
+ import logging
2
4
  from typing import Dict, Iterable, Optional, Set
3
5
 
6
+ import requests
4
7
  from cytoolz import reduceby
5
8
  from requests import Response
9
+ from solders.pubkey import Pubkey
6
10
 
7
11
  from blockapi.utils.user_agent import get_random_user_agent
8
12
  from blockapi.v2.base import (
@@ -24,6 +28,8 @@ from blockapi.v2.models import (
24
28
  ParseResult,
25
29
  )
26
30
 
31
+ logger = logging.getLogger(__name__)
32
+
27
33
  SOL_TOKEN_LIST_URL = 'https://token-list-api.solana.cloud/v1/list'
28
34
  JUP_AG_TOKEN_LIST_URL = (
29
35
  'https://raw.githubusercontent.com/jup-ag/token-list/main/validated-tokens.csv'
@@ -54,16 +60,24 @@ class SolanaApi(CustomizableBlockchainApi, BalanceMixin):
54
60
 
55
61
  token_program_id = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
56
62
  token2022_program_id = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'
63
+ METADATA_PROGRAM_ID = Pubkey.from_string(
64
+ "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
65
+ )
66
+
57
67
  _tokens_map: Optional[Dict[str, Dict]] = None
58
68
  _ban_list: Optional[Set[str]] = None
59
69
 
70
+ def __init__(self, base_url: Optional[str] = None, fetch_metaplex: bool = True):
71
+ super().__init__(base_url)
72
+ self.fetch_metaplex = fetch_metaplex
73
+
60
74
  @property
61
75
  def tokens_map(self) -> Dict[str, Dict]:
62
76
  if self._tokens_map is None:
63
77
  map_jup_ag = self._get_token_map_jup_ag()
64
78
  map_solana = self._get_token_map_solana()
65
- map_sonar = self._get_token_map_sonar()
66
- self._tokens_map = {**map_jup_ag, **map_sonar, **map_solana}
79
+ self._tokens_map = {**map_jup_ag, **map_solana}
80
+
67
81
  return self._tokens_map
68
82
 
69
83
  def _get_token_map_solana(self) -> Dict[str, Dict]:
@@ -266,15 +280,19 @@ class SolanaApi(CustomizableBlockchainApi, BalanceMixin):
266
280
 
267
281
  address = info['mint']
268
282
 
269
- # TODO unknown token is for 99% NFT, add loading NFT metadata using metaplex
270
- # token account
271
- if address not in self.tokens_map:
272
- return
273
-
274
283
  # ignore banned tokens
275
284
  if address in self.ban_list:
276
285
  return
277
286
 
287
+ # TODO unknown token is for 99% NFT, add loading NFT metadata using metaplex
288
+ # token account
289
+ # TODO move fetching tokens_map to fetch
290
+ if address not in self.tokens_map:
291
+ if not self.update_token_from_metaplex(
292
+ address, decimals=info['tokenAmount']['decimals']
293
+ ):
294
+ return
295
+
278
296
  return BalanceItem.from_api(
279
297
  balance_raw=info['tokenAmount']['amount'],
280
298
  coin=self.get_token_data(address),
@@ -301,6 +319,91 @@ class SolanaApi(CustomizableBlockchainApi, BalanceMixin):
301
319
  ),
302
320
  )
303
321
 
322
+ def update_token_from_metaplex(self, address: str, decimals: int) -> bool:
323
+ if not self.fetch_metaplex:
324
+ return False
325
+
326
+ pda = self.get_metadata_pda(address)
327
+ content = self.fetch_metaplex_account(pda)
328
+ token = self.parse_metaplex_account(content, decimals)
329
+ if not token or not token.get('symbol'):
330
+ return False
331
+
332
+ self.tokens_map[address] = token
333
+ return True
334
+
335
+ def get_metadata_pda(self, mint_address: str) -> str:
336
+ """
337
+ Derives Metaplex metadata address from mint address
338
+
339
+ :param mint_address: Token mint to get metadata for, e.g. `8ZperFA2cciv2rN2YC1QC3YjkuGcTQYiq9vc9aKmdoWM`
340
+ :return: Token metadata account
341
+ """
342
+ return str(
343
+ Pubkey.find_program_address(
344
+ [
345
+ b"metadata",
346
+ bytes(self.METADATA_PROGRAM_ID),
347
+ bytes(Pubkey.from_string(mint_address)),
348
+ ],
349
+ self.METADATA_PROGRAM_ID,
350
+ )[0]
351
+ )
352
+
353
+ def fetch_metaplex_account(self, metadata_pda: str) -> Optional[str]:
354
+ """
355
+ Fetch and validate metaplex account content
356
+ :param metadata_pda: address of metadata account
357
+ :return: base64 encoded account content
358
+ """
359
+ response = self._request(
360
+ 'getAccountInfo', [metadata_pda, {"encoding": "base64"}]
361
+ )
362
+ value = response['result']['value']
363
+ if value:
364
+ data = value['data'][0]
365
+ if value['data'][1] != 'base64':
366
+ return None
367
+
368
+ if value['owner'] != 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s':
369
+ return None
370
+
371
+ return data
372
+
373
+ return None
374
+
375
+ def parse_metaplex_account(self, content: str, decimals: int) -> Optional[dict]:
376
+ if not content:
377
+ return None
378
+
379
+ raw = base64.b64decode(content)
380
+
381
+ url = raw[119:255].decode('utf-8').rstrip('\x00')
382
+ try:
383
+ data = requests.get(url)
384
+ except Exception as e:
385
+ logger.error(e)
386
+ data = None
387
+
388
+ if data:
389
+ data.raise_for_status()
390
+ parsed = data.json()
391
+ return dict(
392
+ name=parsed.get('name'),
393
+ symbol=parsed.get('symbol'),
394
+ tags=parsed.get('tags'),
395
+ logoURI=parsed.get('image'),
396
+ decimals=decimals,
397
+ chainId=101,
398
+ )
399
+
400
+ return dict(
401
+ name=raw[69:101].decode('utf-8').rstrip('\x00'),
402
+ symbol=raw[105:115].decode('utf-8').rstrip('\x00'),
403
+ decimals=decimals,
404
+ chainId=101,
405
+ )
406
+
304
407
  def _request(self, method, params):
305
408
  body = json.dumps(
306
409
  {'jsonrpc': '2.0', 'id': 1, 'method': method, 'params': params}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: blockapi
3
- Version: 0.48.2
3
+ Version: 0.48.4
4
4
  Summary: BlockAPI library
5
5
  Home-page: UNKNOWN
6
6
  Author: Devmons s.r.o.
@@ -14,3 +14,4 @@ pytest
14
14
  pytest-vcr
15
15
  requests_mock>=1.9.3
16
16
  attrs<=22.1.0,>=17.4.0
17
+ solders==0.22.0
@@ -6,7 +6,7 @@ with open("README.md", "r") as f:
6
6
 
7
7
  PACKAGES = find_packages(where='.')
8
8
 
9
- __version__ = "0.48.2"
9
+ __version__ = "0.48.4"
10
10
 
11
11
  setuptools.setup(
12
12
  name='blockapi',
@@ -34,5 +34,6 @@ setuptools.setup(
34
34
  'pytest-vcr',
35
35
  'requests_mock>=1.9.3',
36
36
  'attrs>=17.4.0,<=22.1.0',
37
+ 'solders==0.22.0',
37
38
  ],
38
39
  )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes