diff --git a/README.md b/README.md index c04bb20..5ff3002 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ Install dependencies ``` pip install -r requirements.txt -pip install -r rest-requirements.txt +pip install -r test-requirements.txt ``` Install package diff --git a/blockfrost/api/__init__.py b/blockfrost/api/__init__.py index 6f7af1d..211923c 100644 --- a/blockfrost/api/__init__.py +++ b/blockfrost/api/__init__.py @@ -57,7 +57,9 @@ def root(self, **kwargs): account_mirs, \ account_addresses, \ account_addresses_assets, \ - account_addresses_total + account_addresses_total, \ + account_utxos, \ + account_transactions from .cardano.addresses import \ address, \ address_extended, \ @@ -75,12 +77,14 @@ def root(self, **kwargs): from .cardano.blocks import \ block_latest, \ block_latest_transactions, \ + block_latest_transactions_cbor, \ block, \ block_slot, \ block_epoch_slot, \ blocks_next, \ blocks_previous, \ block_transactions, \ + block_transactions_cbor, \ blocks_addresses from .cardano.epochs import \ epoch_latest, \ @@ -104,7 +108,8 @@ def root(self, **kwargs): metadata_label_json, \ metadata_label_cbor from .cardano.network import \ - network + network, \ + network_eras from .cardano.pools import \ pools, \ pools_extended, \ @@ -131,6 +136,8 @@ def root(self, **kwargs): transaction_submit, \ transaction_submit_cbor, \ transaction_redeemers, \ + transaction_required_signers, \ + transaction_cbor, \ transaction_evaluate, \ transaction_evaluate_cbor, \ transaction_evaluate_utxos @@ -142,5 +149,18 @@ def root(self, **kwargs): script_redeemers, \ script_datum, \ script_datum_cbor + from .cardano.governance import \ + governance_dreps, \ + governance_drep, \ + governance_drep_delegators, \ + governance_drep_metadata, \ + governance_drep_updates, \ + governance_drep_votes, \ + governance_proposals, \ + governance_proposal, \ + governance_proposal_parameters, \ + governance_proposal_withdrawals, \ + governance_proposal_votes, \ + governance_proposal_metadata from .cardano.utils import \ utils_addresses_xpub diff --git a/blockfrost/api/cardano/accounts.py b/blockfrost/api/cardano/accounts.py index ab5b4cd..d8ba67c 100644 --- a/blockfrost/api/cardano/accounts.py +++ b/blockfrost/api/cardano/accounts.py @@ -274,6 +274,68 @@ def account_addresses_assets(self, stake_address: str, **kwargs): ) +@list_request_wrapper +def account_utxos(self, stake_address: str, **kwargs): + """ + Obtain information about UTXOs of a specific account. + + https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1utxos/get + + :param stake_address: Bech32 stake address. + :type stake_address: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/accounts/{stake_address}/utxos", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@list_request_wrapper +def account_transactions(self, stake_address: str, **kwargs): + """ + Obtain information about transactions associated with a specific account. + + https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1transactions/get + + :param stake_address: Bech32 stake address. + :type stake_address: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/accounts/{stake_address}/transactions", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + @request_wrapper def account_addresses_total(self, stake_address: str, **kwargs): """ diff --git a/blockfrost/api/cardano/blocks.py b/blockfrost/api/cardano/blocks.py index aa4f6dd..8c7eb36 100644 --- a/blockfrost/api/cardano/blocks.py +++ b/blockfrost/api/cardano/blocks.py @@ -204,6 +204,62 @@ def block_transactions(self, hash_or_number: str, **kwargs): ) +@list_request_wrapper +def block_latest_transactions_cbor(self, **kwargs): + """ + Return the transactions within the latest block in CBOR format. + + https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1latest~1txs~1cbor/get + + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/blocks/latest/txs/cbor", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@list_request_wrapper +def block_transactions_cbor(self, hash_or_number: str, **kwargs): + """ + Return the transactions within the block in CBOR format. + + https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1{hash_or_number}~1txs~1cbor/get + + :param hash_or_number: Hash or number of the requested block. + :type hash_or_number: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/blocks/{hash_or_number}/txs/cbor", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + @list_request_wrapper def blocks_addresses(self, hash_or_number: str, **kwargs): """ diff --git a/blockfrost/api/cardano/governance.py b/blockfrost/api/cardano/governance.py new file mode 100644 index 0000000..26f6734 --- /dev/null +++ b/blockfrost/api/cardano/governance.py @@ -0,0 +1,335 @@ +import requests +from blockfrost.utils import request_wrapper, list_request_wrapper + + +@list_request_wrapper +def governance_dreps(self, **kwargs): + """ + Return the list of registered delegated representatives (DReps). + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1dreps/get + + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/dreps", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@request_wrapper +def governance_drep(self, drep_id: str, **kwargs): + """ + Return information about a specific delegated representative (DRep). + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1dreps~1{drep_id}/get + + :param drep_id: The DRep ID (Bech32 or hex encoded). + :type drep_id: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :returns object. + :rtype: Namespace + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/dreps/{drep_id}", + headers=self.default_headers + ) + + +@list_request_wrapper +def governance_drep_delegators(self, drep_id: str, **kwargs): + """ + Return the list of delegators to a specific DRep. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1dreps~1{drep_id}~1delegators/get + + :param drep_id: The DRep ID (Bech32 or hex encoded). + :type drep_id: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/dreps/{drep_id}/delegators", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@request_wrapper +def governance_drep_metadata(self, drep_id: str, **kwargs): + """ + Return the metadata of a specific DRep. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1dreps~1{drep_id}~1metadata/get + + :param drep_id: The DRep ID (Bech32 or hex encoded). + :type drep_id: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :returns object. + :rtype: Namespace + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/dreps/{drep_id}/metadata", + headers=self.default_headers + ) + + +@list_request_wrapper +def governance_drep_updates(self, drep_id: str, **kwargs): + """ + Return the list of certificate updates to a specific DRep. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1dreps~1{drep_id}~1updates/get + + :param drep_id: The DRep ID (Bech32 or hex encoded). + :type drep_id: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/dreps/{drep_id}/updates", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@list_request_wrapper +def governance_drep_votes(self, drep_id: str, **kwargs): + """ + Return the list of votes by a specific DRep. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1dreps~1{drep_id}~1votes/get + + :param drep_id: The DRep ID (Bech32 or hex encoded). + :type drep_id: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/dreps/{drep_id}/votes", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@list_request_wrapper +def governance_proposals(self, **kwargs): + """ + Return the list of governance proposals. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1proposals/get + + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/proposals", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@request_wrapper +def governance_proposal(self, tx_hash: str, cert_index: int, **kwargs): + """ + Return information about a specific governance proposal. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1proposals~1{tx_hash}~1{cert_index}/get + + :param tx_hash: The transaction hash of the proposal. + :type tx_hash: str + :param cert_index: The index of the certificate within the proposal transaction. + :type cert_index: int + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :returns object. + :rtype: Namespace + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/proposals/{tx_hash}/{cert_index}", + headers=self.default_headers + ) + + +@request_wrapper +def governance_proposal_parameters(self, tx_hash: str, cert_index: int, **kwargs): + """ + Return the parameters of a specific governance proposal. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1proposals~1{tx_hash}~1{cert_index}~1parameters/get + + :param tx_hash: The transaction hash of the proposal. + :type tx_hash: str + :param cert_index: The index of the certificate within the proposal transaction. + :type cert_index: int + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :returns object. + :rtype: Namespace + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/proposals/{tx_hash}/{cert_index}/parameters", + headers=self.default_headers + ) + + +@list_request_wrapper +def governance_proposal_withdrawals(self, tx_hash: str, cert_index: int, **kwargs): + """ + Return the withdrawals of a specific governance proposal. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1proposals~1{tx_hash}~1{cert_index}~1withdrawals/get + + :param tx_hash: The transaction hash of the proposal. + :type tx_hash: str + :param cert_index: The index of the certificate within the proposal transaction. + :type cert_index: int + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/proposals/{tx_hash}/{cert_index}/withdrawals", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@list_request_wrapper +def governance_proposal_votes(self, tx_hash: str, cert_index: int, **kwargs): + """ + Return the votes of a specific governance proposal. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1proposals~1{tx_hash}~1{cert_index}~1votes/get + + :param tx_hash: The transaction hash of the proposal. + :type tx_hash: str + :param cert_index: The index of the certificate within the proposal transaction. + :type cert_index: int + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :param gather_pages: Optional. Default: false. Will collect all pages into one return + :type gather_pages: bool + :param count: Optional. Default: 100. The number of results displayed on one page. + :type count: int + :param page: Optional. The page number for listing the results. + :type page: int + :param order: Optional. "asc" or "desc". Default: "asc". + :type order: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/proposals/{tx_hash}/{cert_index}/votes", + params=self.query_parameters(kwargs), + headers=self.default_headers + ) + + +@request_wrapper +def governance_proposal_metadata(self, tx_hash: str, cert_index: int, **kwargs): + """ + Return the metadata of a specific governance proposal. + + https://docs.blockfrost.io/#tag/Cardano-Governance/paths/~1governance~1proposals~1{tx_hash}~1{cert_index}~1metadata/get + + :param tx_hash: The transaction hash of the proposal. + :type tx_hash: str + :param cert_index: The index of the certificate within the proposal transaction. + :type cert_index: int + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :returns object. + :rtype: Namespace + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/governance/proposals/{tx_hash}/{cert_index}/metadata", + headers=self.default_headers + ) diff --git a/blockfrost/api/cardano/network.py b/blockfrost/api/cardano/network.py index 56d0899..882f7f4 100644 --- a/blockfrost/api/cardano/network.py +++ b/blockfrost/api/cardano/network.py @@ -1,5 +1,5 @@ import requests -from blockfrost.utils import request_wrapper +from blockfrost.utils import request_wrapper, list_request_wrapper @request_wrapper @@ -20,3 +20,23 @@ def network(self, **kwargs): url=f"{self.url}/network", headers=self.default_headers ) + + +@list_request_wrapper +def network_eras(self, **kwargs): + """ + Return the information about network eras. + + https://docs.blockfrost.io/#tag/Cardano-Network/paths/~1network~1eras/get + + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/network/eras", + headers=self.default_headers + ) diff --git a/blockfrost/api/cardano/transactions.py b/blockfrost/api/cardano/transactions.py index 0711824..5b49494 100644 --- a/blockfrost/api/cardano/transactions.py +++ b/blockfrost/api/cardano/transactions.py @@ -245,6 +245,50 @@ def transaction_redeemers(self, hash: str, **kwargs): ) +@list_request_wrapper +def transaction_required_signers(self, hash: str, **kwargs): + """ + Obtain the required signers of a specific transaction. + + https://docs.blockfrost.io/#tag/Cardano-Transactions/paths/~1txs~1{hash}~1required_signers/get + + :param hash: Hash of the requested transaction. + :type hash: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :returns A list of objects. + :rtype [Namespace] + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/txs/{hash}/required_signers", + headers=self.default_headers + ) + + +@request_wrapper +def transaction_cbor(self, hash: str, **kwargs): + """ + Obtain the transaction in CBOR format. + + https://docs.blockfrost.io/#tag/Cardano-Transactions/paths/~1txs~1{hash}~1cbor/get + + :param hash: Hash of the requested transaction. + :type hash: str + :param return_type: Optional. "object", "json" or "pandas". Default: "object". + :type return_type: str + :returns object. + :rtype: Namespace + :raises ApiError: If API fails + :raises Exception: If the API response is somehow malformed. + """ + return requests.get( + url=f"{self.url}/txs/{hash}/cbor", + headers=self.default_headers + ) + + @request_wrapper def transaction_submit(self, file_path: str, **kwargs): """ diff --git a/setup.py b/setup.py index baa808a..2f41473 100644 --- a/setup.py +++ b/setup.py @@ -8,8 +8,8 @@ setup( name='blockfrost-python', - version='0.6.0', - description='The official Python SDK for Blockfrost API v0.1.37', + version='0.7.0', + description='The official Python SDK for Blockfrost API v0.1.85', long_description=long_description, long_description_content_type='text/markdown', url='https://github.com/blockfrost/blockfrost-python', diff --git a/tests/test_cardano_accounts.py b/tests/test_cardano_accounts.py index fa9d74d..84fa372 100644 --- a/tests/test_cardano_accounts.py +++ b/tests/test_cardano_accounts.py @@ -243,6 +243,61 @@ def test_integration_account_addresses_assets(): assert api.account_addresses_assets(stake_address=stake_address) == [] +def test_account_utxos(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "address": "addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd", + "tx_hash": "39a7a284c2a0c3a6d5f594f2bd150c2f13c6b2a1f8a1e3d5c0f3b2a1d0e9f8a7", + "output_index": 0, + "amount": [ + { + "unit": "lovelace", + "quantity": "42000000" + } + ], + "block": "7eb8e27d18686c7db9a18f8bbcfe34e3fed6e047afaa2d969904d15e934847e6", + "data_hash": None, + "inline_datum": None, + "reference_script_hash": None + } + ] + requests_mock.get(f"{api.url}/accounts/{stake_address}/utxos", json=mock_data) + assert api.account_utxos(stake_address=stake_address) == convert_json_to_object(mock_data) + + +def test_integration_account_utxos(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.account_utxos(stake_address=stake_address) + + +def test_account_transactions(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "tx_hash": "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b", + "tx_index": 6, + "block_height": 69, + "block_time": 1635505891 + }, + { + "tx_hash": "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f", + "tx_index": 9, + "block_height": 4547, + "block_time": 1635505987 + } + ] + requests_mock.get(f"{api.url}/accounts/{stake_address}/transactions", json=mock_data) + assert api.account_transactions(stake_address=stake_address) == convert_json_to_object(mock_data) + + +def test_integration_account_transactions(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.account_transactions(stake_address=stake_address) + + def test_account_addresses_total(requests_mock): api = BlockFrostApi() mock_data = { diff --git a/tests/test_cardano_blocks.py b/tests/test_cardano_blocks.py index 5974634..1857d46 100644 --- a/tests/test_cardano_blocks.py +++ b/tests/test_cardano_blocks.py @@ -223,6 +223,47 @@ def test_integration_block_transactions(): assert api.block_transactions(hash_or_number=hash) +def test_block_latest_transactions_cbor(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "tx_hash": "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b", + "cbor": "84a8008282582098483df1666d5af7c4aca7ef28f112d225b81a34ef20b0ad4775bab0e41dbb30" + }, + { + "tx_hash": "4eef6bb7755d8afbeac526b799f3e32a624691d166657e9d862aaeb66682c036", + "cbor": "84a8008282582098483df1666d5af7c4aca7ef28f112d225b81a34ef20b0ad4775bab0e41dbb31" + } + ] + requests_mock.get(f"{api.url}/blocks/latest/txs/cbor", json=mock_data) + assert api.block_latest_transactions_cbor() == convert_json_to_object(mock_data) + + +def test_integration_block_latest_transactions_cbor(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + result = api.block_latest_transactions_cbor() + assert result or result == [] + + +def test_block_transactions_cbor(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "tx_hash": "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b", + "cbor": "84a8008282582098483df1666d5af7c4aca7ef28f112d225b81a34ef20b0ad4775bab0e41dbb30" + } + ] + requests_mock.get(f"{api.url}/blocks/{hash}/txs/cbor", json=mock_data) + assert api.block_transactions_cbor(hash_or_number=hash) == convert_json_to_object(mock_data) + + +def test_integration_block_transactions_cbor(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + assert api.block_transactions_cbor(hash_or_number=hash) + + def test_blocks_blocks_addresses(requests_mock): api = BlockFrostApi() mock_data = [ diff --git a/tests/test_cardano_governance.py b/tests/test_cardano_governance.py new file mode 100644 index 0000000..5be052e --- /dev/null +++ b/tests/test_cardano_governance.py @@ -0,0 +1,270 @@ +import os +from blockfrost import BlockFrostApi, ApiError +from blockfrost.utils import convert_json_to_object + +drep_id = 'drep1yfaaaaa270yjt6tu5skndugekprf5ykv5jshanl0c6gqx5qpstskf' +tx_hash = '51f495aa23f4b3b3aa90afde4a0e67823bb7ac4ac65f5ffbb138373b863f2f74' +cert_index = 0 +# proposal with metadata (treasury_withdrawals type) +metadata_tx_hash = '60ed6ab43c840ff888a8af30a1ed27b41e9f4a91a89822b2b63d1bfc52aeec45' + + +def test_governance_dreps(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "drep_id": drep_id, + "hex": "db1bc3c3f99fd22aa1e09f1c34c0ada5795c5e1f32f2652a246f73db" + }, + { + "drep_id": "drep1cxayn4fgy27yaucvhamsvqj3v6835mh3tjjx6t8rm65ndd3lcjm", + "hex": "c1d932a9a0457a13bc6195f7600c919d1e3476f715c86d2c8f7a9a66" + } + ] + requests_mock.get(f"{api.url}/governance/dreps", json=mock_data) + assert api.governance_dreps() == convert_json_to_object(mock_data) + + +def test_integration_governance_dreps(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_dreps() + + +def test_governance_drep(requests_mock): + api = BlockFrostApi() + mock_data = { + "drep_id": drep_id, + "hex": "db1bc3c3f99fd22aa1e09f1c34c0ada5795c5e1f32f2652a246f73db", + "amount": "2000000", + "active": True, + "active_epoch": 420, + "has_script": False, + "retired": False, + "expired": False, + "last_active_epoch": 500 + } + requests_mock.get(f"{api.url}/governance/dreps/{drep_id}", json=mock_data) + assert api.governance_drep(drep_id=drep_id) == convert_json_to_object(mock_data) + + +def test_integration_governance_drep(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_drep(drep_id=drep_id) + + +def test_governance_drep_delegators(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7", + "amount": "1029328" + } + ] + requests_mock.get(f"{api.url}/governance/dreps/{drep_id}/delegators", json=mock_data) + assert api.governance_drep_delegators(drep_id=drep_id) == convert_json_to_object(mock_data) + + +def test_integration_governance_drep_delegators(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_drep_delegators(drep_id=drep_id) + + +def test_governance_drep_metadata(requests_mock): + api = BlockFrostApi() + mock_data = { + "drep_id": drep_id, + "hex": "db1bc3c3f99fd22aa1e09f1c34c0ada5795c5e1f32f2652a246f73db", + "url": "https://example.com/drep-metadata.json", + "hash": "a14a5ad4a83b1c8b04c5175f0a16b4a2d8b1e5a08c7b6d1e2f3c4d5e6f7a8b9", + "json_metadata": None, + "bytes": "\\xa100" + } + requests_mock.get(f"{api.url}/governance/dreps/{drep_id}/metadata", json=mock_data) + assert api.governance_drep_metadata(drep_id=drep_id) == convert_json_to_object(mock_data) + + +def test_integration_governance_drep_metadata(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_drep_metadata(drep_id=drep_id) + + +def test_governance_drep_updates(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "tx_hash": "f5ca33220afcc0340d1fa3cba9b0e1f3c3c6e1eab57d688ed700ae56bbce9170", + "cert_index": 0, + "action": "registered" + }, + { + "tx_hash": "0e98ac6a8b1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7", + "cert_index": 0, + "action": "updated" + } + ] + requests_mock.get(f"{api.url}/governance/dreps/{drep_id}/updates", json=mock_data) + assert api.governance_drep_updates(drep_id=drep_id) == convert_json_to_object(mock_data) + + +def test_integration_governance_drep_updates(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_drep_updates(drep_id=drep_id) + + +def test_governance_drep_votes(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "tx_hash": "f5ca33220afcc0340d1fa3cba9b0e1f3c3c6e1eab57d688ed700ae56bbce9170", + "cert_index": 0, + "vote": "yes", + "proposal_tx_hash": "b302de9b2ed2bca4d65e40cae4ae6d0b8e7c0f1a2b3c4d5e6f7a8b9c0d1e2f3a", + "proposal_cert_index": 0 + } + ] + requests_mock.get(f"{api.url}/governance/dreps/{drep_id}/votes", json=mock_data) + assert api.governance_drep_votes(drep_id=drep_id) == convert_json_to_object(mock_data) + + +def test_integration_governance_drep_votes(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_drep_votes(drep_id=drep_id) + + +def test_governance_proposals(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "tx_hash": tx_hash, + "cert_index": cert_index, + "governance_type": "treasury_withdrawals", + "deposit": "100000000000", + "return_address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7", + "governance_description": None, + "ratified_epoch": None, + "enacted_epoch": None, + "dropped_epoch": None, + "expired_epoch": None, + "expiration": 550 + } + ] + requests_mock.get(f"{api.url}/governance/proposals", json=mock_data) + assert api.governance_proposals() == convert_json_to_object(mock_data) + + +def test_integration_governance_proposals(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_proposals() + + +def test_governance_proposal(requests_mock): + api = BlockFrostApi() + mock_data = { + "tx_hash": tx_hash, + "cert_index": cert_index, + "governance_type": "treasury_withdrawals", + "deposit": "100000000000", + "return_address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7", + "governance_description": None, + "ratified_epoch": None, + "enacted_epoch": None, + "dropped_epoch": None, + "expired_epoch": None, + "expiration": 550 + } + requests_mock.get(f"{api.url}/governance/proposals/{tx_hash}/{cert_index}", json=mock_data) + assert api.governance_proposal(tx_hash=tx_hash, cert_index=cert_index) == convert_json_to_object(mock_data) + + +def test_integration_governance_proposal(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_proposal(tx_hash=tx_hash, cert_index=cert_index) + + +def test_governance_proposal_parameters(requests_mock): + api = BlockFrostApi() + mock_data = { + "tx_hash": tx_hash, + "cert_index": cert_index, + "parameters": { + "min_fee_a": 44, + "min_fee_b": 155381, + "key_deposit": "2000000", + "pool_deposit": "500000000" + } + } + requests_mock.get(f"{api.url}/governance/proposals/{tx_hash}/{cert_index}/parameters", json=mock_data) + assert api.governance_proposal_parameters(tx_hash=tx_hash, cert_index=cert_index) == convert_json_to_object(mock_data) + + +def test_integration_governance_proposal_parameters(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_proposal_parameters(tx_hash=tx_hash, cert_index=cert_index) + + +def test_governance_proposal_withdrawals(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "stake_address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7", + "amount": "454541212442" + } + ] + requests_mock.get(f"{api.url}/governance/proposals/{tx_hash}/{cert_index}/withdrawals", json=mock_data) + assert api.governance_proposal_withdrawals(tx_hash=tx_hash, cert_index=cert_index) == convert_json_to_object(mock_data) + + +def test_integration_governance_proposal_withdrawals(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_proposal_withdrawals(tx_hash=tx_hash, cert_index=cert_index) + + +def test_governance_proposal_votes(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "tx_hash": "f5ca33220afcc0340d1fa3cba9b0e1f3c3c6e1eab57d688ed700ae56bbce9170", + "cert_index": 0, + "voter": "drep1mvdu8slennngja7w4un6knwezufra70887zuxpprd64jxfveahn", + "voter_role": "drep", + "vote": "yes" + } + ] + requests_mock.get(f"{api.url}/governance/proposals/{tx_hash}/{cert_index}/votes", json=mock_data) + assert api.governance_proposal_votes(tx_hash=tx_hash, cert_index=cert_index) == convert_json_to_object(mock_data) + + +def test_integration_governance_proposal_votes(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_proposal_votes(tx_hash=tx_hash, cert_index=cert_index) + + +def test_governance_proposal_metadata(requests_mock): + api = BlockFrostApi() + mock_data = { + "tx_hash": tx_hash, + "cert_index": cert_index, + "url": "https://example.com/proposal-metadata.json", + "hash": "a14a5ad4a83b1c8b04c5175f0a16b4a2d8b1e5a08c7b6d1e2f3c4d5e6f7a8b9", + "json_metadata": None, + "bytes": "\\xa100" + } + requests_mock.get(f"{api.url}/governance/proposals/{tx_hash}/{cert_index}/metadata", json=mock_data) + assert api.governance_proposal_metadata(tx_hash=tx_hash, cert_index=cert_index) == convert_json_to_object(mock_data) + + +def test_integration_governance_proposal_metadata(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + api.governance_proposal_metadata(tx_hash=metadata_tx_hash, cert_index=cert_index) diff --git a/tests/test_cardano_network.py b/tests/test_cardano_network.py index fde8fa1..7684daa 100644 --- a/tests/test_cardano_network.py +++ b/tests/test_cardano_network.py @@ -27,3 +27,51 @@ def test_integration_network(): if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) assert api.network() + + +def test_network_eras(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "start": { + "time": 0, + "slot": 0, + "epoch": 0 + }, + "end": { + "time": 89856000, + "slot": 4492800, + "epoch": 208 + }, + "parameters": { + "epoch_length": 21600, + "slot_length": 20, + "safe_zone": 4320 + } + }, + { + "start": { + "time": 89856000, + "slot": 4492800, + "epoch": 208 + }, + "end": { + "time": 101952000, + "slot": 16588800, + "epoch": 236 + }, + "parameters": { + "epoch_length": 432000, + "slot_length": 1, + "safe_zone": 129600 + } + } + ] + requests_mock.get(f"{api.url}/network/eras", json=mock_data) + assert api.network_eras() == convert_json_to_object(mock_data) + + +def test_integration_network_eras(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) + assert api.network_eras() diff --git a/tests/test_cardano_transactions.py b/tests/test_cardano_transactions.py index 5113e0b..1b4c461 100644 --- a/tests/test_cardano_transactions.py +++ b/tests/test_cardano_transactions.py @@ -361,6 +361,42 @@ def test_integration_transaction_redeemers(): assert api.transaction_redeemers(hash=hash) == [] +def test_transaction_required_signers(requests_mock): + api = BlockFrostApi() + mock_data = [ + { + "address": "ec26b89af41bef0f7585353831cb5da42b5b37185e0c8a526143b824" + } + ] + requests_mock.get(f"{api.url}/txs/{hash}/required_signers", json=mock_data) + assert api.transaction_required_signers( + hash=hash) == convert_json_to_object(mock_data) + + +def test_integration_transaction_required_signers(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv( + 'BLOCKFROST_PROJECT_ID_MAINNET')) + assert api.transaction_required_signers(hash=hash) == [] + + +def test_transaction_cbor(requests_mock): + api = BlockFrostApi() + mock_data = { + "cbor": "84a8008282582098483df1666d5af7c4aca7ef28f112d225b81a34ef20b0ad4775bab0e41dbb30" + } + requests_mock.get(f"{api.url}/txs/{hash}/cbor", json=mock_data) + assert api.transaction_cbor( + hash=hash) == convert_json_to_object(mock_data) + + +def test_integration_transaction_cbor(): + if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): + api = BlockFrostApi(project_id=os.getenv( + 'BLOCKFROST_PROJECT_ID_MAINNET')) + assert api.transaction_cbor(hash=hash) + + def test_transaction_submit(requests_mock): api = BlockFrostApi() mock_data = hash @@ -376,7 +412,7 @@ def test_integration_transaction_submit_cbor(): with pytest.raises(ApiError) as exc_info: api.transaction_submit_cbor(tx_cbor=tx_cbor) - assert exc_info.value.message.find("transaction submit error") > -1 + assert exc_info.value.status_code == 400 # Make sure that the tx cbor was correctly passed assert exc_info.value.message.find( "54bcb22a31a100080ffbf7edbac538b1517d99fa85ec77bfac089ab8249e2708") > -1 @@ -407,7 +443,8 @@ def test_integration_transaction_evaluate_cbor(): # } # Response with an ogmios error about missing input - assert result.result.EvaluationFailure.CannotCreateEvaluationContext.reason.find( + script_failure = getattr(result.result.EvaluationFailure.ScriptFailures, 'spend:0') + assert script_failure.CannotCreateEvaluationContext.reason.find( 'Unknown transaction input') > -1