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.
- {blockapi-0.48.2 → blockapi-0.48.4}/PKG-INFO +1 -1
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_solana.py +49 -1
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/solana.py +110 -7
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/PKG-INFO +1 -1
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/requires.txt +1 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/setup.py +2 -1
- {blockapi-0.48.2 → blockapi-0.48.4}/LICENSE.md +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/README.md +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/alethio.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/amberdata.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/binance.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockchaininfo.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockchainos.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockchair.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockcypher.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockonomics.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/blockscout.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/btc.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/cardanoexplorer.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/chainso.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/cosmos.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/cryptoid.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/dcrdata.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/digonchain.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/eospark.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/etherscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/ethplorer.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/greymass.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/insight.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/kyber.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/neoscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/ontology.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/solana.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/stellar.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/subscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/terra_money.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/trezor.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/tronscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/tzscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/tzstats.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/zchain.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/api/zensystem.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/services.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_amberdata.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_balances.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_binance.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_blockchainos.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_blockchair.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_blockscout.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_btc.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_cosmos.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_dcrdata.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_digonchain.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_insight.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_kyber.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_ontio.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_solana.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_subscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_terra_money.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_tronscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_tzscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/api/test_tzstats.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/test_blockapi.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/test_num.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/test_random_user_agent.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/conftest.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/covalenth/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/covalenth/test_ethereum.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/fake_sleep_provider.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/nft/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/nft/test_magic_eden.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/nft/test_opensea.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/nft/test_simple_hash.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/perpetual/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/perpetual/test_perpetual.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/synthetix/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/synthetix/test_synthetix.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_blockchainos.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_blockchair_btc.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_blockchair_doge.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_blockchair_ltc.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_cosmos.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_ethplorer.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_multisources.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_optimistic_etherscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_subscan_polkadot.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/api/test_trezor_btc.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_base.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_blockchain_api.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_blockchain_mapping.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_data.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_enumerate_classes.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_generic.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test/v2/test_models.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/test_data.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/address.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/datetime.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/ethereum.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/num.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/utils/user_agent.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/blockchainos.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/blockchair.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/cosmos.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/arbitrum.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/astar.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/avalanche.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/axie.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/base.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/binance_smart_chain.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/ethereum.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/fantom.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/heco.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/iotex.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/klaytn.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/moonbeam.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/palm.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/polygon.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/covalenth/rsk.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/debank.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/debank_maps.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/ethplorer.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/nft/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/nft/magic_eden.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/nft/opensea.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/nft/simple_hash.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/optimistic_etherscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/perpetual/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/perpetual/perp_abi.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/perpetual/perpetual.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/subscan.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/synthetix/__init__.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/synthetix/synthetix.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/synthetix/synthetix_abi.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/terra.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/trezor.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/api/web3_utils.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/base.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/blockchain_mapping.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/coin_mapping.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/coins.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi/v2/models.py +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/SOURCES.txt +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/dependency_links.txt +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/blockapi.egg-info/top_level.txt +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/pyproject.toml +0 -0
- {blockapi-0.48.2 → blockapi-0.48.4}/setup.cfg +0 -0
|
@@ -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
|
-
|
|
66
|
-
|
|
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}
|
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|