├── .github └── workflows │ ├── package-test.yml │ └── publish-to-test-pypi.yml ├── .gitignore ├── LICENSE ├── README.md ├── blockfrost ├── __init__.py ├── api │ ├── __init__.py │ ├── cardano │ │ ├── __init__.py │ │ ├── accounts.py │ │ ├── addresses.py │ │ ├── assets.py │ │ ├── blocks.py │ │ ├── epochs.py │ │ ├── ledger.py │ │ ├── mempool.py │ │ ├── metadata.py │ │ ├── network.py │ │ ├── pools.py │ │ ├── scripts.py │ │ ├── transactions.py │ │ └── utils.py │ ├── health.py │ ├── metrics.py │ └── nutlink.py ├── config.py ├── helpers.py ├── ipfs │ ├── __init__.py │ ├── add.py │ ├── gateway.py │ └── pins.py └── utils.py ├── requirements.txt ├── setup.py ├── shell.nix ├── test-requirements.txt └── tests ├── __init__.py ├── test_api.py ├── test_cardano_accounts.py ├── test_cardano_addresses.py ├── test_cardano_assets.py ├── test_cardano_blocks.py ├── test_cardano_epochs.py ├── test_cardano_ledger.py ├── test_cardano_mempool.py ├── test_cardano_metadata.py ├── test_cardano_network.py ├── test_cardano_pools.py ├── test_cardano_scripts.py ├── test_cardano_transactions.py ├── test_cardano_utils.py ├── test_health.py ├── test_helpers.py ├── test_ipfs_add.py ├── test_ipfs_gateway.py ├── test_ipfs_pins.py ├── test_metrics.py └── test_object_mapper.py /.github/workflows/package-test.yml: -------------------------------------------------------------------------------- 1 | name: Package Test 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | python-version: ["3.7", "3.8", "3.9", "3.10"] 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v2 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - name: Install dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | python -m pip install flake8 pytest 26 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 27 | if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi 28 | - name: Install package 29 | run: | 30 | python -m pip install . 31 | - name: Lint with flake8 32 | run: | 33 | # stop the build if there are Python syntax errors or undefined names 34 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 35 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 36 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 37 | - name: Test with pytest 38 | env: 39 | BLOCKFROST_PROJECT_ID_MAINNET: ${{ secrets.BLOCKFROST_PROJECT_ID_MAINNET }} 40 | run: | 41 | pytest 42 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-test-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python 🐍 distributions 📦 to PyPI and TestPyPI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | inputs: 9 | publish_to_pypi: 10 | description: "Publish to PyPI" 11 | required: true 12 | default: "false" 13 | 14 | jobs: 15 | build-n-publish: 16 | name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@master 20 | - name: Set up Python 3.9 21 | uses: actions/setup-python@v1 22 | with: 23 | python-version: 3.9 24 | - name: Install pypa/build 25 | run: > 26 | python -m 27 | pip install 28 | build 29 | --user 30 | - name: Build a binary wheel and a source tarball 31 | run: > 32 | python -m 33 | build 34 | --sdist 35 | --wheel 36 | --outdir dist/ 37 | . 38 | - name: Publish distribution 📦 to Test PyPI 39 | uses: pypa/gh-action-pypi-publish@release/v1 40 | with: 41 | skip_existing: true 42 | user: __token__ 43 | password: ${{ secrets.TESTNET_PYPI_API_TOKEN }} 44 | repository_url: https://test.pypi.org/legacy/ 45 | - name: Publish distribution 📦 to PyPI 46 | if: startsWith(github.ref, 'refs/tags') || github.event.inputs.publish_to_pypi == 'true' 47 | uses: pypa/gh-action-pypi-publish@release/v1 48 | with: 49 | user: __token__ 50 | password: ${{ secrets.PYPI_API_TOKEN }} 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # vscode 132 | .vscode/ 133 | 134 | # JetBrains 135 | .idea/ 136 | .DS_Store 137 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Package Test](https://img.shields.io/github/actions/workflow/status/blockfrost/blockfrost-python/package-test.yml?logo=GitHub&label=package%20test)](https://github.com/blockfrost/blockfrost-python/actions/workflows/package-test.yml) 2 | [![PyPI Latest Release](https://img.shields.io/pypi/v/blockfrost-python.svg?logo=pypi&label=pypi%20latest)](https://pypi.org/project/blockfrost-python/) 3 | [![PyPI - Downloads](https://img.shields.io/pypi/dm/blockfrost-python?logo=pypi&label=pypi%20downloads)](https://pypistats.org/packages/blockfrost-python) 4 | [![Package Status](https://img.shields.io/pypi/status/blockfrost-python.svg)](https://pypi.org/project/blockfrost-python/) 5 | [![License](https://img.shields.io/pypi/l/blockfrost-python.svg)](https://github.com/blockfrost/blockfrost-python/blob/master/LICENSE) 6 | [![Made by Five Binaries](https://img.shields.io/badge/made%20by-Five%20Binaries-darkviolet.svg)](https://fivebinaries.com/) 7 | [![Maintained by Mathias Frohlich](https://img.shields.io/badge/maintained%20by-Mathias%20Frohlich-blue.svg)](https://github.com/mathiasfrohlich) 8 | 9 | 10 | 11 | # blockfrost-python 12 | 13 |
14 | 15 |

A Python SDK for Blockfrost.io API

16 |

17 | Getting started • 18 | Installation • 19 | Usage 20 |

21 |
22 | 23 | ## Getting started 24 | 25 | To use this SDK, you first need login into [blockfrost.io](https://blockfrost.io) and create your project to retrieve 26 | your API key. 27 | 28 | 29 | 30 |
31 | 32 | ## Installation 33 | 34 | [![PyPI Latest Release](https://img.shields.io/pypi/v/blockfrost-python.svg)](https://pypi.org/project/blockfrost-python/) 35 | 36 | ```console 37 | pip install blockfrost-python 38 | ``` 39 | 40 |
41 | 42 | ## Usage 43 | 44 | Using the SDK is pretty straight-forward as you can see from the following examples. 45 | 46 | ### Cardano 47 | 48 | ```python 49 | from blockfrost import BlockFrostApi, ApiError, ApiUrls 50 | 51 | api = BlockFrostApi( 52 | project_id='YOUR API KEY HERE', # or export environment variable BLOCKFROST_PROJECT_ID 53 | # optional: pass base_url or export BLOCKFROST_API_URL to use testnet, defaults to ApiUrls.mainnet.value 54 | base_url=ApiUrls.testnet.value, 55 | ) 56 | try: 57 | health = api.health() 58 | print(health) # prints object: HealthResponse(is_healthy=True) 59 | health = api.health(return_type='json') # Can be useful if python wrapper is behind api version 60 | print(health) # prints json: {"is_healthy":True} 61 | health = api.health(return_type='pandas') 62 | print(health) # prints Dataframe: is_healthy 63 | # 0 True 64 | 65 | 66 | account_rewards = api.account_rewards( 67 | stake_address='stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7', 68 | count=20, 69 | ) 70 | print(account_rewards[0].epoch) # prints 221 71 | print(len(account_rewards)) # prints 20 72 | 73 | account_rewards = api.account_rewards( 74 | stake_address='stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7', 75 | count=20, 76 | gather_pages=True, # will collect all pages 77 | ) 78 | print(account_rewards[0].epoch) # prints 221 79 | print(len(account_rewards)) # prints 57 80 | 81 | address = api.address( 82 | address='addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz') 83 | print(address.type) # prints 'shelley' 84 | for amount in address.amount: 85 | print(amount.unit) # prints 'lovelace' 86 | 87 | except ApiError as e: 88 | print(e) 89 | ``` 90 | 91 | ### IPFS 92 | 93 | ```python 94 | from blockfrost import BlockFrostIPFS, ApiError 95 | 96 | ipfs = BlockFrostIPFS( 97 | project_id='YOUR API KEY HERE' # or export environment variable BLOCKFROST_PROJECT_ID 98 | ) 99 | file_hash = None 100 | try: 101 | ipfs_object = ipfs.add('./README.md') 102 | file_hash = ipfs_object.ipfs_hash 103 | print(file_hash) 104 | except ApiError as e: 105 | print(e) 106 | 107 | try: 108 | with open('./README_downloaded.md', 'w') as file: 109 | file_data = ipfs.gateway(IPFS_path=file_hash).text 110 | file.write(file_data) 111 | except ApiError as e: 112 | print(e) 113 | ``` 114 | 115 | ### Verifying Secure Webhook signature 116 | 117 | Webhooks enable Blockfrost to push real-time notifications to your application. In order to prevent malicious actor from pretending to be Blockfrost every webhook request is signed. The signature is included in a request's `Blockfrost-Signature` header. This allows you to verify that the events were sent by Blockfrost, not by a third party. 118 | To learn more about Secure Webhooks, see [Secure Webhooks Docs](https://blockfrost.dev/docs/start-building/webhooks/). 119 | 120 | You can verify the signature using `verifyWebhookSignature` function. 121 | 122 | Example: 123 | 124 | ```python 125 | # Example of Python Flask app with /webhook endpoint 126 | # for processing events sent by Blockfrost Secure Webhooks 127 | from flask import Flask, request, json 128 | from blockfrost import verify_webhook_signature, SignatureVerificationError 129 | 130 | SECRET_AUTH_TOKEN = "SECRET-WEBHOOK-AUTH-TOKEN" 131 | 132 | app = Flask(__name__) 133 | 134 | @app.route('/webhook', methods=['POST']) 135 | def webhook(): 136 | if request.method == 'POST': 137 | # Validate webhook signature 138 | request_bytes = request.get_data() 139 | try: 140 | verify_webhook_signature( 141 | request_bytes, request.headers['Blockfrost-Signature'], SECRET_AUTH_TOKEN) 142 | except SignatureVerificationError as e: 143 | # for easier debugging you can access passed header and request_body values (e.header, e.request_body) 144 | print('Webhook signature is invalid.', e) 145 | return 'Invalid signature', 403 146 | 147 | # Get the payload as JSON 148 | event = request.json 149 | 150 | print('Received request id {}, webhook_id: {}'.format( 151 | event['id'], event['webhook_id'])) 152 | 153 | if event['type'] == "block": 154 | # process Block event 155 | print('Received block hash {}'.format(event['payload']['hash'])) 156 | elif event['type'] == "...": 157 | # truncated 158 | else: 159 | # Unexpected event type 160 | print('Unexpected event type {}'.format(event['type'])) 161 | 162 | return 'Webhook received', 200 163 | else: 164 | return 'POST Method not supported', 405 165 | 166 | 167 | 168 | if __name__ == "__main__": 169 | app.run(host='0.0.0.0', port=6666) 170 | ``` 171 | 172 | ## Development 173 | 174 | Install dependencies 175 | 176 | ``` 177 | pip install -r requirements.txt 178 | pip install -r rest-requirements.txt 179 | ``` 180 | 181 | Install package 182 | 183 | ``` 184 | pip install . 185 | ``` 186 | 187 | Run integration and unit tests: 188 | 189 | ``` 190 | pytest 191 | ``` 192 | 193 | _For integration tests you need to set env variable `BLOCKFROST_PROJECT_ID_MAINNET`_ 194 | 195 | ### Release workflow 196 | 197 | To release the package create a new release via GitHub releases. 198 | This action triggers the automated release workflow that packages and uploads the distribution to PyPI. 199 | -------------------------------------------------------------------------------- /blockfrost/__init__.py: -------------------------------------------------------------------------------- 1 | from blockfrost.api import BlockFrostApi 2 | from blockfrost.ipfs import BlockFrostIPFS 3 | from blockfrost.config import ApiUrls 4 | from blockfrost.utils import ApiError, Namespace 5 | from blockfrost.helpers import SignatureVerificationError, verify_webhook_signature 6 | -------------------------------------------------------------------------------- /blockfrost/api/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | from dataclasses import dataclass 4 | 5 | from blockfrost.config import DEFAULT_API_VERSION 6 | 7 | from ..utils import Api, ApiUrls, request_wrapper 8 | 9 | 10 | class BlockFrostApi(Api): 11 | 12 | def __init__(self, project_id: str = None, base_url: str = None, api_version: str = None): 13 | super().__init__( 14 | project_id=project_id, 15 | base_url=base_url if base_url else os.environ.get( 16 | 'BLOCKFROST_API_URL', default=ApiUrls.mainnet.value), 17 | # if custom base_url is specified then also use specified api_version 18 | api_version=api_version if base_url else os.environ.get('BLOCKFROST_API_VERSION', 19 | default=DEFAULT_API_VERSION)) 20 | 21 | @request_wrapper 22 | def root(self, **kwargs): 23 | """ 24 | Root endpoint has no other function than to point end users to documentation. 25 | 26 | https://docs.blockfrost.io/#tag/Health/paths/~1/get 27 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 28 | :type return_type: str 29 | :returns RootResponse object. 30 | :rtype RootResponse 31 | :raises ApiError: If API fails 32 | :raises Exception: If the API response is somehow malformed. 33 | """ 34 | return requests.get( 35 | url=f"{self.url}/", 36 | headers=self.authentication_header 37 | ) 38 | 39 | from .health import \ 40 | health, \ 41 | clock 42 | from .metrics import \ 43 | metrics, \ 44 | metrics_endpoints 45 | from .nutlink import \ 46 | nutlink_address, \ 47 | nutlink_address_tickers, \ 48 | nutlink_address_ticker, \ 49 | nutlink_ticker 50 | from .cardano.accounts import \ 51 | accounts, \ 52 | account_rewards, \ 53 | account_history, \ 54 | account_delegations, \ 55 | account_registrations, \ 56 | account_withdrawals, \ 57 | account_mirs, \ 58 | account_addresses, \ 59 | account_addresses_assets, \ 60 | account_addresses_total 61 | from .cardano.addresses import \ 62 | address, \ 63 | address_extended, \ 64 | address_total, \ 65 | address_utxos, \ 66 | address_utxos_asset, \ 67 | address_transactions 68 | from .cardano.assets import \ 69 | assets, \ 70 | asset, \ 71 | asset_history, \ 72 | asset_transactions, \ 73 | asset_addresses, \ 74 | assets_policy 75 | from .cardano.blocks import \ 76 | block_latest, \ 77 | block_latest_transactions, \ 78 | block, \ 79 | block_slot, \ 80 | block_epoch_slot, \ 81 | blocks_next, \ 82 | blocks_previous, \ 83 | block_transactions, \ 84 | blocks_addresses 85 | from .cardano.epochs import \ 86 | epoch_latest, \ 87 | epoch_latest_parameters, \ 88 | epoch, \ 89 | epochs_next, \ 90 | epochs_previous, \ 91 | epoch_stakes, \ 92 | epoch_pool_stakes, \ 93 | epoch_blocks, \ 94 | epoch_pool_blocks, \ 95 | epoch_protocol_parameters 96 | from .cardano.ledger import \ 97 | genesis 98 | from .cardano.mempool import \ 99 | mempool, \ 100 | mempool_address, \ 101 | mempool_tx 102 | from .cardano.metadata import \ 103 | metadata_labels, \ 104 | metadata_label_json, \ 105 | metadata_label_cbor 106 | from .cardano.network import \ 107 | network 108 | from .cardano.pools import \ 109 | pools, \ 110 | pools_extended, \ 111 | pools_retired, \ 112 | pools_retiring, \ 113 | pool, \ 114 | pool_history, \ 115 | pool_metadata, \ 116 | pool_relays, \ 117 | pool_delegators, \ 118 | pool_blocks, \ 119 | pool_updates 120 | from .cardano.transactions import \ 121 | transaction, \ 122 | transaction_utxos, \ 123 | transaction_stakes, \ 124 | transaction_delegations, \ 125 | transaction_withdrawals, \ 126 | transaction_mirs, \ 127 | transaction_pool_updates, \ 128 | transaction_pool_retires, \ 129 | transaction_metadata, \ 130 | transaction_metadata_cbor, \ 131 | transaction_submit, \ 132 | transaction_submit_cbor, \ 133 | transaction_redeemers, \ 134 | transaction_evaluate, \ 135 | transaction_evaluate_cbor, \ 136 | transaction_evaluate_utxos 137 | from .cardano.scripts import \ 138 | scripts, \ 139 | script, \ 140 | script_json, \ 141 | script_cbor, \ 142 | script_redeemers, \ 143 | script_datum, \ 144 | script_datum_cbor 145 | from .cardano.utils import \ 146 | utils_addresses_xpub 147 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockfrost/blockfrost-python/cb7418e8ce19b6761490cd14f65ebf34cd7e1527/blockfrost/api/cardano/__init__.py -------------------------------------------------------------------------------- /blockfrost/api/cardano/accounts.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper, list_request_wrapper 3 | 4 | 5 | @request_wrapper 6 | def accounts(self, stake_address: str, **kwargs): 7 | """ 8 | Obtain information about a specific networkStake account. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}/get 11 | 12 | :param stake_address: Bech32 stake address. 13 | :type stake_address: str 14 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 15 | :type return_type: str 16 | :returns object. 17 | :rtype: Namespace 18 | :raises ApiError: If API fails 19 | :raises Exception: If the API response is somehow malformed. 20 | """ 21 | return requests.get( 22 | url=f"{self.url}/accounts/{stake_address}", 23 | headers=self.default_headers 24 | ) 25 | 26 | 27 | @list_request_wrapper 28 | def account_rewards(self, stake_address: str, **kwargs): 29 | """ 30 | Obtain information about the history of a specific account. 31 | 32 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1rewards/get 33 | 34 | :param stake_address: Bech32 stake address. 35 | :type stake_address: str 36 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 37 | :type return_type: str 38 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 39 | :type gather_pages: bool 40 | :param count: Optional. Default: 100. The number of results displayed on one page. 41 | :type count: int 42 | :param page: Optional. The page number for listing the results. 43 | :type page: int 44 | :param order: Optional. "asc" or "desc". Default: "asc". 45 | :type order: str 46 | :returns A list of objects. 47 | :rtype [Namespace] 48 | :raises ApiError: If API fails 49 | :raises Exception: If the API response is somehow malformed. 50 | """ 51 | return requests.get( 52 | url=f"{self.url}/accounts/{stake_address}/rewards", 53 | params=self.query_parameters(kwargs), 54 | headers=self.default_headers 55 | ) 56 | 57 | 58 | @list_request_wrapper 59 | def account_history(self, stake_address: str, **kwargs): 60 | """ 61 | Obtain information about the history of a specific account. 62 | 63 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1history/get 64 | 65 | :param stake_address: Bech32 stake address. 66 | :type stake_address: str 67 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 68 | :type return_type: str 69 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 70 | :type gather_pages: bool 71 | :param count: Optional. Default: 100. The number of results displayed on one page. 72 | :type count: int 73 | :param page: Optional. The page number for listing the results. 74 | :type page: int 75 | :param order: Optional. "asc" or "desc". Default: "asc". 76 | :type order: str 77 | :returns A list of objects. 78 | :rtype [Namespace] 79 | :raises ApiError: If API fails 80 | :raises Exception: If the API response is somehow malformed. 81 | """ 82 | return requests.get( 83 | url=f"{self.url}/accounts/{stake_address}/history", 84 | params=self.query_parameters(kwargs), 85 | headers=self.default_headers 86 | ) 87 | 88 | 89 | @list_request_wrapper 90 | def account_delegations(self, stake_address: str, **kwargs): 91 | """ 92 | Obtain information about the delegation of a specific account. 93 | 94 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1delegations/get 95 | 96 | :param stake_address: Bech32 stake address. 97 | :type stake_address: str 98 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 99 | :type return_type: str 100 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 101 | :type gather_pages: bool 102 | :param count: Optional. Default: 100. The number of results displayed on one page. 103 | :type count: int 104 | :param page: Optional. The page number for listing the results. 105 | :type page: int 106 | :param order: Optional. "asc" or "desc". Default: "asc". 107 | :type order: str 108 | :returns A list of objects. 109 | :rtype [Namespace] 110 | :raises ApiError: If API fails 111 | :raises Exception: If the API response is somehow malformed. 112 | """ 113 | return requests.get( 114 | url=f"{self.url}/accounts/{stake_address}/delegations", 115 | params=self.query_parameters(kwargs), 116 | headers=self.default_headers 117 | ) 118 | 119 | 120 | @list_request_wrapper 121 | def account_registrations(self, stake_address: str, **kwargs): 122 | """ 123 | Obtain information about the registrations and deregistrations of a specific account. 124 | 125 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1registrations/get 126 | 127 | :param stake_address: Bech32 stake address. 128 | :type stake_address: str 129 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 130 | :type return_type: str 131 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 132 | :type gather_pages: bool 133 | :param count: Optional. Default: 100. The number of results displayed on one page. 134 | :type count: int 135 | :param page: Optional. The page number for listing the results. 136 | :type page: int 137 | :param order: Optional. "asc" or "desc". Default: "asc". 138 | :type order: str 139 | :returns A list of objects. 140 | :rtype [Namespace] 141 | :raises ApiError: If API fails 142 | :raises Exception: If the API response is somehow malformed. 143 | """ 144 | return requests.get( 145 | url=f"{self.url}/accounts/{stake_address}/registrations", 146 | params=self.query_parameters(kwargs), 147 | headers=self.default_headers 148 | ) 149 | 150 | 151 | @list_request_wrapper 152 | def account_withdrawals(self, stake_address: str, **kwargs): 153 | """ 154 | Obtain information about the withdrawals of a specific account. 155 | 156 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1withdrawals/get 157 | 158 | :param stake_address: Bech32 stake address. 159 | :type stake_address: str 160 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 161 | :type return_type: str 162 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 163 | :type gather_pages: bool 164 | :param count: Optional. Default: 100. The number of results displayed on one page. 165 | :type count: int 166 | :param page: Optional. The page number for listing the results. 167 | :type page: int 168 | :param order: Optional. "asc" or "desc". Default: "asc". 169 | :type order: str 170 | :returns A list of objects. 171 | :rtype [Namespace] 172 | :raises ApiError: If API fails 173 | :raises Exception: If the API response is somehow malformed. 174 | """ 175 | return requests.get( 176 | url=f"{self.url}/accounts/{stake_address}/withdrawals", 177 | params=self.query_parameters(kwargs), 178 | headers=self.default_headers 179 | ) 180 | 181 | 182 | @list_request_wrapper 183 | def account_mirs(self, stake_address: str, **kwargs): 184 | """ 185 | Obtain information about the MIRs of a specific account. 186 | 187 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1mirs/get 188 | 189 | :param stake_address: Bech32 stake address. 190 | :type stake_address: str 191 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 192 | :type return_type: str 193 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 194 | :type gather_pages: bool 195 | :param count: Optional. Default: 100. The number of results displayed on one page. 196 | :type count: int 197 | :param page: Optional. The page number for listing the results. 198 | :type page: int 199 | :param order: Optional. "asc" or "desc". Default: "asc". 200 | :type order: str 201 | :returns A list of objects. 202 | :rtype [Namespace] 203 | :raises ApiError: If API fails 204 | :raises Exception: If the API response is somehow malformed. 205 | """ 206 | return requests.get( 207 | url=f"{self.url}/accounts/{stake_address}/mirs", 208 | params=self.query_parameters(kwargs), 209 | headers=self.default_headers 210 | ) 211 | 212 | 213 | @list_request_wrapper 214 | def account_addresses(self, stake_address: str, **kwargs): 215 | """ 216 | Obtain information about the addresses of a specific account. 217 | 218 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1addresses/get 219 | 220 | :param stake_address: Bech32 stake address. 221 | :type stake_address: str 222 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 223 | :type return_type: str 224 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 225 | :type gather_pages: bool 226 | :param count: Optional. Default: 100. The number of results displayed on one page. 227 | :type count: int 228 | :param page: Optional. The page number for listing the results. 229 | :type page: int 230 | :param order: Optional. "asc" or "desc". Default: "asc". 231 | :type order: str 232 | :returns A list of objects. 233 | :rtype [Namespace] 234 | :raises ApiError: If API fails 235 | :raises Exception: If the API response is somehow malformed. 236 | """ 237 | return requests.get( 238 | url=f"{self.url}/accounts/{stake_address}/addresses", 239 | params=self.query_parameters(kwargs), 240 | headers=self.default_headers 241 | ) 242 | 243 | 244 | @list_request_wrapper 245 | def account_addresses_assets(self, stake_address: str, **kwargs): 246 | """ 247 | Obtain information about assets associated with addresses of a specific account. 248 | 249 | Be careful, as an account could be part of a mangled address and does not necessarily mean the addresses are owned by user as the account. 250 | 251 | https://docs.blockfrost.io/#tag/Cardano-Accounts/paths/~1accounts~1{stake_address}~1addresses~1assets/get 252 | 253 | :param stake_address: Bech32 stake address. 254 | :type stake_address: str 255 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 256 | :type return_type: str 257 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 258 | :type gather_pages: bool 259 | :param count: Optional. Default: 100. The number of results displayed on one page. 260 | :type count: int 261 | :param page: Optional. The page number for listing the results. 262 | :type page: int 263 | :param order: Optional. "asc" or "desc". Default: "asc". 264 | :type order: str 265 | :returns A list of objects. 266 | :rtype [Namespace] 267 | :raises ApiError: If API fails 268 | :raises Exception: If the API response is somehow malformed. 269 | """ 270 | return requests.get( 271 | url=f"{self.url}/accounts/{stake_address}/addresses/assets", 272 | params=self.query_parameters(kwargs), 273 | headers=self.default_headers 274 | ) 275 | 276 | 277 | @request_wrapper 278 | def account_addresses_total(self, stake_address: str, **kwargs): 279 | """ 280 | Obtain summed details about all addresses associated with a given account. 281 | 282 | Be careful, as an account could be part of a mangled address and does not necessarily mean the addresses are owned by user as the account. 283 | 284 | https://docs.blockfrost.io/#tag/Cardano-Addresses/paths/~1accounts~1{stake_address}~1addresses~1total/get 285 | 286 | :param stake_address: Bech32 address. 287 | :type stake_address: str 288 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 289 | :type return_type: str 290 | :returns object. 291 | :rtype: Namespace 292 | :raises ApiError: If API fails 293 | :raises Exception: If the API response is somehow malformed. 294 | """ 295 | return requests.get( 296 | url=f"{self.url}/accounts/{stake_address}/addresses/total", 297 | headers=self.default_headers 298 | ) 299 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/addresses.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper, list_request_wrapper 3 | 4 | 5 | @request_wrapper 6 | def address(self, address: str, **kwargs): 7 | """ 8 | Obtain information about a specific address. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Addresses/paths/~1addresses~1{address}/get 11 | 12 | :param address: Bech32 address. 13 | :type address: str 14 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 15 | :type return_type: str 16 | :returns object. 17 | :rtype: Namespace 18 | :raises ApiError: If API fails 19 | :raises Exception: If the API response is somehow malformed. 20 | """ 21 | return requests.get( 22 | url=f"{self.url}/addresses/{address}", 23 | headers=self.default_headers 24 | ) 25 | 26 | 27 | @request_wrapper 28 | def address_extended(self, address: str, **kwargs): 29 | """ 30 | Obtain information about a specific address. 31 | 32 | https://docs.blockfrost.io/#tag/Cardano-Addresses/paths/~1addresses~1{address}~1extended/get 33 | 34 | :param address: Bech32 address. 35 | :type address: str 36 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 37 | :type return_type: str 38 | :returns object. 39 | :rtype: Namespace 40 | :raises ApiError: If API fails 41 | :raises Exception: If the API response is somehow malformed. 42 | """ 43 | return requests.get( 44 | url=f"{self.url}/addresses/{address}/extended", 45 | headers=self.default_headers 46 | ) 47 | 48 | 49 | @request_wrapper 50 | def address_total(self, address: str, **kwargs): 51 | """ 52 | Obtain details about an address. 53 | 54 | https://docs.blockfrost.io/#tag/Cardano-Addresses/paths/~1addresses~1{address}~1total/get 55 | 56 | :param address: Bech32 address. 57 | :type address: str 58 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 59 | :type return_type: str 60 | :returns object. 61 | :rtype: Namespace 62 | :raises ApiError: If API fails 63 | :raises Exception: If the API response is somehow malformed. 64 | """ 65 | return requests.get( 66 | url=f"{self.url}/addresses/{address}/total", 67 | headers=self.default_headers 68 | ) 69 | 70 | 71 | @list_request_wrapper 72 | def address_utxos(self, address: str, **kwargs): 73 | """ 74 | UTXOs of the address. 75 | 76 | https://docs.blockfrost.io/#tag/Cardano-Addresses/paths/~1addresses~1{address}~1utxos/get 77 | 78 | :param address: Bech32 address. 79 | :type address: str 80 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 81 | :type return_type: str 82 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 83 | :type gather_pages: bool 84 | :param count: Optional. Default: 100. The number of results displayed on one page. 85 | :type count: int 86 | :param page: Optional. The page number for listing the results. 87 | :type page: int 88 | :param order: Optional. "asc" or "desc". Default: "asc". 89 | :type order: str 90 | :returns A list of objects. 91 | :rtype [Namespace] 92 | :raises ApiError: If API fails 93 | :raises Exception: If the API response is somehow malformed. 94 | """ 95 | return requests.get( 96 | url=f"{self.url}/addresses/{address}/utxos", 97 | params=self.query_parameters(kwargs), 98 | headers=self.default_headers 99 | ) 100 | 101 | 102 | @list_request_wrapper 103 | def address_utxos_asset(self, address: str, asset: str, **kwargs): 104 | """ 105 | UTXOs of the address. 106 | 107 | https://docs.blockfrost.io/#tag/Cardano-Addresses/paths/~1addresses~1{address}~1utxos~1{asset}/get 108 | 109 | :param address: Bech32 address. 110 | :type address: str 111 | :param asset: Concatenation of the policy_id and hex-encoded asset_name. 112 | :type asset: str 113 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 114 | :type return_type: str 115 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 116 | :type gather_pages: bool 117 | :param count: Optional. Default: 100. The number of results displayed on one page. 118 | :type count: int 119 | :param page: Optional. The page number for listing the results. 120 | :type page: int 121 | :param order: Optional. "asc" or "desc". Default: "asc". 122 | :type order: str 123 | :returns A list of objects. 124 | :rtype [Namespace] 125 | :raises ApiError: If API fails 126 | :raises Exception: If the API response is somehow malformed. 127 | """ 128 | return requests.get( 129 | url=f"{self.url}/addresses/{address}/utxos/{asset}", 130 | params=self.query_parameters(kwargs), 131 | headers=self.default_headers 132 | ) 133 | 134 | 135 | @list_request_wrapper 136 | def address_transactions(self, address: str, from_block: str = None, to_block: str = None, 137 | **kwargs): 138 | """ 139 | Transactions on the address. 140 | 141 | https://docs.blockfrost.io/#tag/Cardano-Addresses/paths/~1addresses~1{address}~1transactions/get 142 | 143 | :param address: Bech32 address. 144 | :type address: str 145 | :param from: The block number and optionally also index from which (inclusive) to start search for results, concatenated using colon. Has to be lower than or equal to to parameter. 146 | :type from: str 147 | :param to: The block number and optionally also index where (inclusive) to end the search for results, concatenated using colon. Has to be higher than or equal to from parameter. 148 | :type to: str 149 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 150 | :type return_type: str 151 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 152 | :type gather_pages: bool 153 | :param count: Optional. Default: 100. The number of results displayed on one page. 154 | :type count: int 155 | :param page: Optional. The page number for listing the results. 156 | :type page: int 157 | :param order: Optional. "asc" or "desc". Default: "asc". 158 | :type order: str 159 | :returns A list of objects. 160 | :rtype [Namespace] 161 | :raises ApiError: If API fails 162 | :raises Exception: If the API response is somehow malformed. 163 | """ 164 | return requests.get( 165 | url=f"{self.url}/addresses/{address}/transactions", 166 | params={ 167 | 'from': from_block, 168 | 'to': to_block, 169 | **self.query_parameters(kwargs) 170 | }, 171 | headers=self.default_headers 172 | ) 173 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/assets.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper, list_request_wrapper 3 | 4 | 5 | @list_request_wrapper 6 | def assets(self, **kwargs): 7 | """ 8 | List of assets. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Assets/paths/~1assets/get 11 | 12 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 13 | :type return_type: str 14 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 15 | :type gather_pages: bool 16 | :param count: Optional. Default: 100. The number of results displayed on one page. 17 | :type count: int 18 | :param page: Optional. The page number for listing the results. 19 | :type page: int 20 | :param order: Optional. "asc" or "desc". Default: "asc". 21 | :type order: str 22 | :returns A list of objects. 23 | :rtype [Namespace] 24 | :raises ApiError: If API fails 25 | :raises Exception: If the API response is somehow malformed. 26 | """ 27 | return requests.get( 28 | url=f"{self.url}/assets", 29 | params=self.query_parameters(kwargs), 30 | headers=self.default_headers 31 | ) 32 | 33 | 34 | @request_wrapper 35 | def asset(self, asset: str, **kwargs): 36 | """ 37 | Information about a specific asset 38 | 39 | https://docs.blockfrost.io/#tag/Cardano-Assets/paths/~1assets~1{asset}/get 40 | 41 | :param asset: Concatenation of the policy_id and hex-encoded asset_name. 42 | :type asset: str 43 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 44 | :type return_type: str 45 | :returns object. 46 | :rtype: Namespace 47 | :raises ApiError: If API fails 48 | :raises Exception: If the API response is somehow malformed. 49 | """ 50 | return requests.get( 51 | url=f"{self.url}/assets/{asset}", 52 | headers=self.default_headers 53 | ) 54 | 55 | 56 | @list_request_wrapper 57 | def asset_history(self, asset: str, **kwargs): 58 | """ 59 | History of a specific asset 60 | 61 | https://docs.blockfrost.io/#tag/Cardano-Assets/paths/~1assets~1{asset}~1history/get 62 | 63 | :param asset: Concatenation of the policy_id and hex-encoded asset_name. 64 | :type asset: str 65 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 66 | :type return_type: str 67 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 68 | :type gather_pages: bool 69 | :param count: Optional. Default: 100. The number of results displayed on one page. 70 | :type count: int 71 | :param page: Optional. The page number for listing the results. 72 | :type page: int 73 | :param order: Optional. "asc" or "desc". Default: "asc". 74 | :type order: str 75 | :returns A list of objects. 76 | :rtype [Namespace] 77 | :raises ApiError: If API fails 78 | :raises Exception: If the API response is somehow malformed. 79 | """ 80 | return requests.get( 81 | url=f"{self.url}/assets/{asset}/history", 82 | params=self.query_parameters(kwargs), 83 | headers=self.default_headers 84 | ) 85 | 86 | 87 | @list_request_wrapper 88 | def asset_transactions(self, asset: str, **kwargs): 89 | """ 90 | List of a specific asset transactions 91 | 92 | https://docs.blockfrost.io/#tag/Cardano-Assets/paths/~1assets~1{asset}~1transactions/get 93 | 94 | :param asset: Concatenation of the policy_id and hex-encoded asset_name. 95 | :type asset: str 96 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 97 | :type return_type: str 98 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 99 | :type gather_pages: bool 100 | :param count: Optional. Default: 100. The number of results displayed on one page. 101 | :type count: int 102 | :param page: Optional. The page number for listing the results. 103 | :type page: int 104 | :param order: Optional. "asc" or "desc". Default: "asc". 105 | :type order: str 106 | :returns A list of objects. 107 | :rtype [Namespace] 108 | :raises ApiError: If API fails 109 | :raises Exception: If the API response is somehow malformed. 110 | """ 111 | return requests.get( 112 | url=f"{self.url}/assets/{asset}/transactions", 113 | params=self.query_parameters(kwargs), 114 | headers=self.default_headers 115 | ) 116 | 117 | 118 | @list_request_wrapper 119 | def asset_addresses(self, asset: str, **kwargs): 120 | """ 121 | List of a addresses containing a specific asset 122 | 123 | https://docs.blockfrost.io/#tag/Cardano-Assets/paths/~1assets~1{asset}~1addresses/get 124 | 125 | :param asset: Concatenation of the policy_id and hex-encoded asset_name. 126 | :type asset: str 127 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 128 | :type return_type: str 129 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 130 | :type gather_pages: bool 131 | :param count: Optional. Default: 100. The number of results displayed on one page. 132 | :type count: int 133 | :param page: Optional. The page number for listing the results. 134 | :type page: int 135 | :param order: Optional. "asc" or "desc". Default: "asc". 136 | :type order: str 137 | :returns A list of objects. 138 | :rtype [Namespace] 139 | :raises ApiError: If API fails 140 | :raises Exception: If the API response is somehow malformed. 141 | """ 142 | return requests.get( 143 | url=f"{self.url}/assets/{asset}/addresses", 144 | params=self.query_parameters(kwargs), 145 | headers=self.default_headers 146 | ) 147 | 148 | 149 | @list_request_wrapper 150 | def assets_policy(self, policy_id: str, **kwargs): 151 | """ 152 | List of asset minted under a specific policy 153 | 154 | https://docs.blockfrost.io/#tag/Cardano-Assets/paths/~1assets~1policy~1{policy_id}/get 155 | 156 | :param policy_id: Specific policy_id. 157 | :type policy_id: str 158 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 159 | :type return_type: str 160 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 161 | :type gather_pages: bool 162 | :param count: Optional. Default: 100. The number of results displayed on one page. 163 | :type count: int 164 | :param page: Optional. The page number for listing the results. 165 | :type page: int 166 | :param order: Optional. "asc" or "desc". Default: "asc". 167 | :type order: str 168 | :returns A list of objects. 169 | :rtype [Namespace] 170 | :raises ApiError: If API fails 171 | :raises Exception: If the API response is somehow malformed. 172 | """ 173 | return requests.get( 174 | url=f"{self.url}/assets/policy/{policy_id}", 175 | params=self.query_parameters(kwargs), 176 | headers=self.default_headers 177 | ) 178 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/blocks.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper, list_request_wrapper 3 | 4 | 5 | @request_wrapper 6 | def block_latest(self, **kwargs): 7 | """ 8 | Return the latest block available to the backends, also known as the tip of the blockchain. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1latest/get 11 | 12 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 13 | :type return_type: str 14 | :returns object. 15 | :rtype: Namespace 16 | :raises ApiError: If API fails 17 | :raises Exception: If the API response is somehow malformed. 18 | """ 19 | return requests.get( 20 | url=f"{self.url}/blocks/latest", 21 | headers=self.default_headers 22 | ) 23 | 24 | 25 | @list_request_wrapper 26 | def block_latest_transactions(self, **kwargs): 27 | """ 28 | Return the transactions within the latest block. 29 | 30 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1latest~1txs/get 31 | 32 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 33 | :type gather_pages: bool 34 | :param count: Optional. Default: 100. The number of results displayed on one page. 35 | :type count: int 36 | :param page: Optional. The page number for listing the results. 37 | :type page: int 38 | :param order: Optional. "asc" or "desc". Default: "asc". 39 | :type order: str 40 | :returns A list of str objects. 41 | :rtype [str] 42 | :raises ApiError: If API fails 43 | :raises Exception: If the API response is somehow malformed. 44 | """ 45 | return requests.get( 46 | url=f"{self.url}/blocks/latest/txs", 47 | params=self.query_parameters(kwargs), 48 | headers=self.default_headers 49 | ) 50 | 51 | 52 | @request_wrapper 53 | def block(self, hash_or_number: str, **kwargs): 54 | """ 55 | Return the content of a requested block. 56 | 57 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1{hash_or_number}/get 58 | 59 | :param hash_or_number: Hash or number of the requested block. 60 | :type hash_or_number: str 61 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 62 | :type return_type: str 63 | :returns object. 64 | :rtype: Namespace 65 | :raises ApiError: If API fails 66 | :raises Exception: If the API response is somehow malformed. 67 | """ 68 | return requests.get( 69 | url=f"{self.url}/blocks/{hash_or_number}", 70 | headers=self.default_headers 71 | ) 72 | 73 | 74 | @request_wrapper 75 | def block_slot(self, slot_number: int, **kwargs): 76 | """ 77 | Return the content of a requested block for a specific slot. 78 | 79 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1slot~1{slot_number}/get 80 | 81 | :param slot_number: Slot position for requested block. 82 | :type slot_number: int 83 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 84 | :type return_type: str 85 | :returns object. 86 | :rtype: Namespace 87 | :raises ApiError: If API fails 88 | :raises Exception: If the API response is somehow malformed. 89 | """ 90 | return requests.get( 91 | url=f"{self.url}/blocks/slot/{slot_number}", 92 | headers=self.default_headers 93 | ) 94 | 95 | 96 | @request_wrapper 97 | def block_epoch_slot(self, epoch_number: int, slot_number: int, **kwargs): 98 | """ 99 | Return the content of a requested block for a specific slot in an epoch. 100 | 101 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1epoch~1{epoch_number}~1slot~1{slot_number}/get 102 | 103 | :param epoch_number: Epoch for specific epoch slot. 104 | :type epoch_number: int 105 | :param slot_number: Slot position for requested block (epoch_slot). 106 | :type slot_number: int 107 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 108 | :type return_type: str 109 | :returns object. 110 | :rtype: Namespace 111 | :raises ApiError: If API fails 112 | :raises Exception: If the API response is somehow malformed. 113 | """ 114 | return requests.get( 115 | url=f"{self.url}/blocks/epoch/{epoch_number}/slot/{slot_number}", 116 | headers=self.default_headers 117 | ) 118 | 119 | 120 | @list_request_wrapper 121 | def blocks_next(self, hash_or_number: str, **kwargs): 122 | """ 123 | Return the list of blocks following a specific block. 124 | 125 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1{hash_or_number}~1next/get 126 | 127 | :param hash_or_number: Hash or number of the requested block. 128 | :type hash_or_number: str 129 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 130 | :type return_type: str 131 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 132 | :type gather_pages: bool 133 | :param count: Optional. Default: 100. The number of results displayed on one page. 134 | :type count: int 135 | :param page: Optional. The page number for listing the results. 136 | :type page: int 137 | :returns A list of objects. 138 | :rtype [Namespace] 139 | :raises ApiError: If API fails 140 | :raises Exception: If the API response is somehow malformed. 141 | """ 142 | return requests.get( 143 | url=f"{self.url}/blocks/{hash_or_number}/next", 144 | params=self.query_parameters(kwargs), 145 | headers=self.default_headers 146 | ) 147 | 148 | 149 | @list_request_wrapper 150 | def blocks_previous(self, hash_or_number: str, **kwargs): 151 | """ 152 | Return the list of blocks preceding a specific block. 153 | 154 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1{hash_or_number}~1previous/get 155 | 156 | :param hash_or_number: Hash or number of the requested block. 157 | :type hash_or_number: str 158 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 159 | :type return_type: str 160 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 161 | :type gather_pages: bool 162 | :param count: Optional. Default: 100. The number of results displayed on one page. 163 | :type count: int 164 | :param page: Optional. The page number for listing the results. 165 | :type page: int 166 | :returns A list of objects. 167 | :rtype [Namespace] 168 | :raises ApiError: If API fails 169 | :raises Exception: If the API response is somehow malformed. 170 | """ 171 | return requests.get( 172 | url=f"{self.url}/blocks/{hash_or_number}/previous", 173 | params=self.query_parameters(kwargs), 174 | headers=self.default_headers 175 | ) 176 | 177 | 178 | @list_request_wrapper 179 | def block_transactions(self, hash_or_number: str, **kwargs): 180 | """ 181 | Return the transactions within the block. 182 | 183 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1{hash_or_number}~1txs/get 184 | 185 | :param hash_or_number: Hash or number of the requested block. 186 | :type hash_or_number: str 187 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 188 | :type gather_pages: bool 189 | :param count: Optional. Default: 100. The number of results displayed on one page. 190 | :type count: int 191 | :param page: Optional. The page number for listing the results. 192 | :type page: int 193 | :param order: Optional. "asc" or "desc". Default: "asc". 194 | :type order: str 195 | :returns A list of str objects. 196 | :rtype [str] 197 | :raises ApiError: If API fails 198 | :raises Exception: If the API response is somehow malformed. 199 | """ 200 | return requests.get( 201 | url=f"{self.url}/blocks/{hash_or_number}/txs", 202 | params=self.query_parameters(kwargs), 203 | headers=self.default_headers 204 | ) 205 | 206 | 207 | @list_request_wrapper 208 | def blocks_addresses(self, hash_or_number: str, **kwargs): 209 | """ 210 | Return list of addresses affected in the specified block with additional information, sorted by the bech32 address, ascending. 211 | 212 | https://docs.blockfrost.io/#tag/Cardano-Blocks/paths/~1blocks~1{hash_or_number}~1addresses/get 213 | 214 | :param hash_or_number: Hash or number of the requested block. 215 | :type hash_or_number: str 216 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 217 | :type return_type: str 218 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 219 | :type gather_pages: bool 220 | :param count: Optional. Default: 100. The number of results displayed on one page. 221 | :type count: int 222 | :param page: Optional. The page number for listing the results. 223 | :type page: int 224 | :returns A list of objects. 225 | :rtype [Namespace] 226 | :raises ApiError: If API fails 227 | :raises Exception: If the API response is somehow malformed. 228 | """ 229 | return requests.get( 230 | url=f"{self.url}/blocks/{hash_or_number}/addresses", 231 | params=self.query_parameters(kwargs), 232 | headers=self.default_headers 233 | ) 234 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/epochs.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from dataclasses import dataclass, field 3 | from blockfrost.utils import request_wrapper, list_request_wrapper 4 | 5 | 6 | @request_wrapper 7 | def epoch_latest(self, **kwargs): 8 | """ 9 | Return the information about the latest, therefore current, epoch. 10 | 11 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1latest/get 12 | 13 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 14 | :type return_type: str 15 | :returns object. 16 | :rtype: Namespace 17 | :raises ApiError: If API fails 18 | :raises Exception: If the API response is somehow malformed. 19 | """ 20 | return requests.get( 21 | url=f"{self.url}/epochs/latest", 22 | headers=self.default_headers 23 | ) 24 | 25 | 26 | @request_wrapper 27 | def epoch_latest_parameters(self, **kwargs): 28 | """ 29 | Return the protocol parameters for the latest epoch. 30 | 31 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1latest~1parameters/get 32 | 33 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 34 | :type return_type: str 35 | :returns object. 36 | :rtype: Namespace 37 | :raises ApiError: If API fails 38 | :raises Exception: If the API response is somehow malformed. 39 | """ 40 | return requests.get( 41 | url=f"{self.url}/epochs/latest/parameters", 42 | headers=self.default_headers 43 | ) 44 | 45 | 46 | @request_wrapper 47 | def epoch(self, number: int, **kwargs): 48 | """ 49 | Return the content of the requested epoch. 50 | 51 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1{number}/get 52 | 53 | :param number: Number of the epoch. 54 | :type number: int 55 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 56 | :type return_type: str 57 | :returns object. 58 | :rtype: Namespace 59 | :raises ApiError: If API fails 60 | :raises Exception: If the API response is somehow malformed. 61 | """ 62 | return requests.get( 63 | url=f"{self.url}/epochs/{number}", 64 | headers=self.default_headers 65 | ) 66 | 67 | 68 | @list_request_wrapper 69 | def epochs_next(self, number: int, **kwargs): 70 | """ 71 | Return the list of epochs following a specific epoch. 72 | 73 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1{number}~1next/get 74 | 75 | :param number: Number of the epoch. 76 | :type number: int 77 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 78 | :type return_type: str 79 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 80 | :type gather_pages: bool 81 | :param count: Optional. Default: 100. The number of results displayed on one page. 82 | :type count: int 83 | :param page: Optional. The page number for listing the results. 84 | :type page: int 85 | :returns A list of objects. 86 | :rtype [Namespace] 87 | :raises ApiError: If API fails 88 | :raises Exception: If the API response is somehow malformed. 89 | """ 90 | return requests.get( 91 | url=f"{self.url}/epochs/{number}/next", 92 | params=self.query_parameters(kwargs), 93 | headers=self.default_headers 94 | ) 95 | 96 | 97 | @list_request_wrapper 98 | def epochs_previous(self, number: int, **kwargs): 99 | """ 100 | Return the list of epochs preceding a specific epoch. 101 | 102 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1{number}~1previous/get 103 | 104 | :param number: Number of the epoch. 105 | :type number: int 106 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 107 | :type return_type: str 108 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 109 | :type gather_pages: bool 110 | :param count: Optional. Default: 100. The number of results displayed on one page. 111 | :type count: int 112 | :param page: Optional. The page number for listing the results. 113 | :type page: int 114 | :returns A list of objects. 115 | :rtype [Namespace] 116 | :raises ApiError: If API fails 117 | :raises Exception: If the API response is somehow malformed. 118 | """ 119 | return requests.get( 120 | url=f"{self.url}/epochs/{number}/previous", 121 | params=self.query_parameters(kwargs), 122 | headers=self.default_headers 123 | ) 124 | 125 | 126 | @list_request_wrapper 127 | def epoch_stakes(self, number: int, **kwargs): 128 | """ 129 | Return the active stake distribution for the specified epoch. 130 | 131 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1{number}~1stakes/get 132 | 133 | :param number: Number of the epoch. 134 | :type number: int 135 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 136 | :type return_type: str 137 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 138 | :type gather_pages: bool 139 | :param count: Optional. Default: 100. The number of results displayed on one page. 140 | :type count: int 141 | :param page: Optional. The page number for listing the results. 142 | :type page: int 143 | :returns A list of objects. 144 | :rtype [Namespace] 145 | :raises ApiError: If API fails 146 | :raises Exception: If the API response is somehow malformed. 147 | """ 148 | return requests.get( 149 | url=f"{self.url}/epochs/{number}/stakes", 150 | params=self.query_parameters(kwargs), 151 | headers=self.default_headers 152 | ) 153 | 154 | 155 | @list_request_wrapper 156 | def epoch_pool_stakes(self, number: int, pool_id: str, **kwargs): 157 | """ 158 | Return the active stake distribution for the epoch specified by stake pool. 159 | 160 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1{number}~1stakes~1{pool_id}/get 161 | 162 | :param number: Number of the epoch. 163 | :type number: int 164 | :param pool_id: Stake pool ID to filter. 165 | :type pool_id: int 166 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 167 | :type return_type: str 168 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 169 | :type gather_pages: bool 170 | :param count: Optional. Default: 100. The number of results displayed on one page. 171 | :type count: int 172 | :param page: Optional. The page number for listing the results. 173 | :type page: int 174 | :returns A list of objects. 175 | :rtype [Namespace] 176 | :raises ApiError: If API fails 177 | :raises Exception: If the API response is somehow malformed. 178 | """ 179 | return requests.get( 180 | url=f"{self.url}/epochs/{number}/stakes/{pool_id}", 181 | params=self.query_parameters(kwargs), 182 | headers=self.default_headers 183 | ) 184 | 185 | 186 | @list_request_wrapper 187 | def epoch_blocks(self, number: int, **kwargs): 188 | """ 189 | Return the blocks minted for the epoch specified. 190 | 191 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1{number}~1blocks/get 192 | 193 | :param number: Number of the epoch. 194 | :type number: int 195 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 196 | :type gather_pages: bool 197 | :param count: Optional. Default: 100. The number of results displayed on one page. 198 | :type count: int 199 | :param page: Optional. The page number for listing the results. 200 | :type page: int 201 | :param order: Optional. "asc" or "desc". Default: "asc". 202 | :type order: str 203 | :returns A list of str objects. 204 | :rtype [str] 205 | :raises ApiError: If API fails 206 | :raises Exception: If the API response is somehow malformed. 207 | """ 208 | return requests.get( 209 | url=f"{self.url}/epochs/{number}/blocks", 210 | params=self.query_parameters(kwargs), 211 | headers=self.default_headers 212 | ) 213 | 214 | 215 | @list_request_wrapper 216 | def epoch_pool_blocks(self, number: int, pool_id: str, **kwargs): 217 | """ 218 | Return the block minted for the epoch specified by stake pool. 219 | 220 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1{number}~1blocks~1{pool_id}/get 221 | 222 | :param number: Number of the epoch. 223 | :type number: int 224 | :param pool_id: Stake pool ID to filter. 225 | :type pool_id: int 226 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 227 | :type gather_pages: bool 228 | :param count: Optional. Default: 100. The number of results displayed on one page. 229 | :type count: int 230 | :param page: Optional. The page number for listing the results. 231 | :type page: int 232 | :param order: Optional. "asc" or "desc". Default: "asc". 233 | :type order: str 234 | :returns A list of str objects. 235 | :rtype [str] 236 | :raises ApiError: If API fails 237 | :raises Exception: If the API response is somehow malformed. 238 | """ 239 | return requests.get( 240 | url=f"{self.url}/epochs/{number}/blocks/{pool_id}", 241 | params=self.query_parameters(kwargs), 242 | headers=self.default_headers 243 | ) 244 | 245 | 246 | @request_wrapper 247 | def epoch_protocol_parameters(self, number: int, **kwargs): 248 | """ 249 | Return the protocol parameters for the epoch specified. 250 | 251 | https://docs.blockfrost.io/#tag/Cardano-Epochs/paths/~1epochs~1{number}~1parameters/get 252 | 253 | :param number: Number of the epoch. 254 | :type number: int 255 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 256 | :type return_type: str 257 | :returns object. 258 | :rtype: Namespace 259 | :raises ApiError: If API fails 260 | :raises Exception: If the API response is somehow malformed. 261 | """ 262 | return requests.get( 263 | url=f"{self.url}/epochs/{number}/parameters", 264 | headers=self.default_headers 265 | ) 266 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/ledger.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper 3 | 4 | 5 | @request_wrapper 6 | def genesis(self, **kwargs): 7 | """ 8 | Return the information about blockchain genesis. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Ledger/paths/~1genesis/get 11 | 12 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 13 | :type return_type: str 14 | :returns object. 15 | :rtype: Namespace 16 | :raises ApiError: If API fails 17 | :raises Exception: If the API response is somehow malformed. 18 | """ 19 | return requests.get( 20 | url=f"{self.url}/genesis", 21 | headers=self.default_headers 22 | ) 23 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/mempool.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import list_request_wrapper 3 | 4 | 5 | @list_request_wrapper 6 | def mempool(self, **kwargs): 7 | """ 8 | Obtains transactions that are currently stored in Blockfrost mempool, waiting to be included in a newly minted block. 9 | Returns only transactions submitted via Blockfrost.io. 10 | 11 | https://docs.blockfrost.io/#tag/Cardano-Mempool/paths/~1mempool/get 12 | 13 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 14 | :type return_type: str 15 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 16 | :type gather_pages: bool 17 | :param count: Optional. Default: 100. The number of results displayed on one page. 18 | :type count: int 19 | :param page: Optional. The page number for listing the results. 20 | :type page: int 21 | :returns A list of objects. 22 | :rtype [Namespace] 23 | :raises ApiError: If API fails 24 | :raises Exception: If the API response is somehow malformed. 25 | """ 26 | return requests.get( 27 | url=f"{self.url}/mempool", 28 | params=self.query_parameters(kwargs), 29 | headers=self.default_headers 30 | ) 31 | 32 | @list_request_wrapper 33 | def mempool_tx(self, hash: str, **kwargs): 34 | """ 35 | Obtains mempool transaction 36 | 37 | https://docs.blockfrost.io/#tag/Cardano-Mempool/paths/~1mempool~1%7Bhash%7D/get 38 | 39 | :param hash: Hash of the requested transaction. 40 | :type hash: str 41 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 42 | :type return_type: str 43 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 44 | :type gather_pages: bool 45 | :param count: Optional. Default: 100. The number of results displayed on one page. 46 | :type count: int 47 | :param page: Optional. The page number for listing the results. 48 | :type page: int 49 | :returns A list of objects. 50 | :rtype [Namespace] 51 | :raises ApiError: If API fails 52 | :raises Exception: If the API response is somehow malformed. 53 | """ 54 | return requests.get( 55 | url=f"{self.url}/mempool/{hash}", 56 | params=self.query_parameters(kwargs), 57 | headers=self.default_headers 58 | ) 59 | 60 | @list_request_wrapper 61 | def mempool_address(self, address: str, **kwargs): 62 | """ 63 | Obtains list of mempool transactions where at least one of the transaction inputs or outputs belongs to the address (paginated). 64 | Shows only transactions submitted via Blockfrost.io. 65 | 66 | https://docs.blockfrost.io/#tag/Cardano-Mempool/paths/~1mempool~1addresses~1%7Baddress%7D/get 67 | 68 | :param address: Bech32 address. 69 | :type address: str 70 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 71 | :type return_type: str 72 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 73 | :type gather_pages: bool 74 | :param count: Optional. Default: 100. The number of results displayed on one page. 75 | :type count: int 76 | :param page: Optional. The page number for listing the results. 77 | :type page: int 78 | :returns A list of objects. 79 | :rtype [Namespace] 80 | :raises ApiError: If API fails 81 | :raises Exception: If the API response is somehow malformed. 82 | """ 83 | return requests.get( 84 | url=f"{self.url}/mempool/addresses/{address}", 85 | params=self.query_parameters(kwargs), 86 | headers=self.default_headers 87 | ) 88 | 89 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/metadata.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import list_request_wrapper 3 | 4 | 5 | @list_request_wrapper 6 | def metadata_labels(self, **kwargs): 7 | """ 8 | List of all used transaction metadata labels. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Metadata/paths/~1metadata~1txs~1labels/get 11 | 12 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 13 | :type return_type: str 14 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 15 | :type gather_pages: bool 16 | :param count: Optional. Default: 100. The number of results displayed on one page. 17 | :type count: int 18 | :param page: Optional. The page number for listing the results. 19 | :type page: int 20 | :param order: Optional. "asc" or "desc". Default: "asc". 21 | :type order: str 22 | :returns A list of objects. 23 | :rtype [Namespace] 24 | :raises ApiError: If API fails 25 | :raises Exception: If the API response is somehow malformed. 26 | """ 27 | return requests.get( 28 | url=f"{self.url}/metadata/txs/labels", 29 | params=self.query_parameters(kwargs), 30 | headers=self.default_headers 31 | ) 32 | 33 | 34 | @list_request_wrapper 35 | def metadata_label_json(self, label: str, **kwargs): 36 | """ 37 | Transaction metadata per label. 38 | 39 | https://docs.blockfrost.io/#tag/Cardano-Metadata/paths/~1metadata~1txs~1labels~1{label}/get 40 | 41 | :param label: Metadata label 42 | :type label: str 43 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 44 | :type return_type: str 45 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 46 | :type gather_pages: bool 47 | :param count: Optional. Default: 100. The number of results displayed on one page. 48 | :type count: int 49 | :param page: Optional. The page number for listing the results. 50 | :type page: int 51 | :param order: Optional. "asc" or "desc". Default: "asc". 52 | :type order: str 53 | :returns A list of objects. 54 | :rtype [Namespace] 55 | :raises ApiError: If API fails 56 | :raises Exception: If the API response is somehow malformed. 57 | """ 58 | return requests.get( 59 | url=f"{self.url}/metadata/txs/labels/{label}", 60 | params=self.query_parameters(kwargs), 61 | headers=self.default_headers 62 | ) 63 | 64 | 65 | @list_request_wrapper 66 | def metadata_label_cbor(self, label: str, **kwargs): 67 | """ 68 | Transaction metadata per label. 69 | 70 | https://docs.blockfrost.io/#tag/Cardano-Metadata/paths/~1metadata~1txs~1labels~1{label}~1cbor/get 71 | 72 | :param label: Metadata label 73 | :type label: str 74 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 75 | :type return_type: str 76 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 77 | :type gather_pages: bool 78 | :param count: Optional. Default: 100. The number of results displayed on one page. 79 | :type count: int 80 | :param page: Optional. The page number for listing the results. 81 | :type page: int 82 | :param order: Optional. "asc" or "desc". Default: "asc". 83 | :type order: str 84 | :returns A list of objects. 85 | :rtype [Namespace] 86 | :raises ApiError: If API fails 87 | :raises Exception: If the API response is somehow malformed. 88 | """ 89 | return requests.get( 90 | url=f"{self.url}/metadata/txs/labels/{label}/cbor", 91 | params=self.query_parameters(kwargs), 92 | headers=self.default_headers 93 | ) 94 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/network.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper 3 | 4 | 5 | @request_wrapper 6 | def network(self, **kwargs): 7 | """ 8 | Return detailed network information. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Network/paths/~1network/get 11 | 12 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 13 | :type return_type: str 14 | :returns object. 15 | :rtype: Namespace 16 | :raises ApiError: If API fails 17 | :raises Exception: If the API response is somehow malformed. 18 | """ 19 | return requests.get( 20 | url=f"{self.url}/network", 21 | headers=self.default_headers 22 | ) 23 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/pools.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper, list_request_wrapper 3 | 4 | 5 | @list_request_wrapper 6 | def pools(self, **kwargs): 7 | """ 8 | List of registered stake pools. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools/get 11 | 12 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 13 | :type gather_pages: bool 14 | :param count: Optional. Default: 100. The number of results displayed on one page. 15 | :type count: int 16 | :param page: Optional. The page number for listing the results. 17 | :type page: int 18 | :param order: Optional. "asc" or "desc". Default: "asc". 19 | :type order: str 20 | :returns A list of str objects. 21 | :rtype [str] 22 | :raises ApiError: If API fails 23 | :raises Exception: If the API response is somehow malformed. 24 | """ 25 | return requests.get( 26 | url=f"{self.url}/pools", 27 | params=self.query_parameters(kwargs), 28 | headers=self.default_headers 29 | ) 30 | 31 | 32 | @list_request_wrapper 33 | def pools_extended(self, **kwargs): 34 | """ 35 | List of registered stake pools with additional information. 36 | 37 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1extended/get 38 | 39 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 40 | :type gather_pages: bool 41 | :param count: Optional. Default: 100. The number of results displayed on one page. 42 | :type count: int 43 | :param page: Optional. The page number for listing the results. 44 | :type page: int 45 | :param order: Optional. "asc" or "desc". Default: "asc". 46 | :type order: str 47 | :returns A list of objects. 48 | :rtype [Namespace] 49 | :raises ApiError: If API fails 50 | :raises Exception: If the API response is somehow malformed. 51 | """ 52 | return requests.get( 53 | url=f"{self.url}/pools/extended", 54 | params=self.query_parameters(kwargs), 55 | headers=self.default_headers 56 | ) 57 | 58 | 59 | @list_request_wrapper 60 | def pools_retired(self, **kwargs): 61 | """ 62 | List of already retired pools. 63 | 64 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1retired/get 65 | 66 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 67 | :type gather_pages: bool 68 | :param count: Optional. Default: 100. The number of results displayed on one page. 69 | :type count: int 70 | :param page: Optional. The page number for listing the results. 71 | :type page: int 72 | :param order: Optional. "asc" or "desc". Default: "asc". 73 | :type order: str 74 | :returns A list of objects. 75 | :rtype [Namespace] 76 | :raises ApiError: If API fails 77 | :raises Exception: If the API response is somehow malformed. 78 | """ 79 | return requests.get( 80 | url=f"{self.url}/pools/retired", 81 | params=self.query_parameters(kwargs), 82 | headers=self.default_headers 83 | ) 84 | 85 | 86 | @list_request_wrapper 87 | def pools_retiring(self, **kwargs): 88 | """ 89 | List of stake pools retiring in the upcoming epochs 90 | 91 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1retiring/get 92 | 93 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 94 | :type gather_pages: bool 95 | :param count: Optional. Default: 100. The number of results displayed on one page. 96 | :type count: int 97 | :param page: Optional. The page number for listing the results. 98 | :type page: int 99 | :param order: Optional. "asc" or "desc". Default: "asc". 100 | :type order: str 101 | :returns A list of objects. 102 | :rtype [Namespace] 103 | :raises ApiError: If API fails 104 | :raises Exception: If the API response is somehow malformed. 105 | """ 106 | return requests.get( 107 | url=f"{self.url}/pools/retiring", 108 | params=self.query_parameters(kwargs), 109 | headers=self.default_headers 110 | ) 111 | 112 | 113 | @request_wrapper 114 | def pool(self, pool_id: str, **kwargs): 115 | """ 116 | Pool information. 117 | 118 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1{pool_id}/get 119 | 120 | :param pool_id: Bech32 or hexadecimal pool ID. 121 | :type pool_id: str 122 | :returns object. 123 | :rtype: Namespace 124 | :raises ApiError: If API fails 125 | :raises Exception: If the API response is somehow malformed. 126 | """ 127 | return requests.get( 128 | url=f"{self.url}/pools/{pool_id}", 129 | headers=self.default_headers 130 | ) 131 | 132 | 133 | @list_request_wrapper 134 | def pool_history(self, pool_id: str, **kwargs): 135 | """ 136 | History of stake pool parameters over epochs. 137 | 138 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1{pool_id}~1history/get 139 | 140 | :param pool_id: Bech32 or hexadecimal pool ID. 141 | :type pool_id: str 142 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 143 | :type gather_pages: bool 144 | :param count: Optional. Default: 100. The number of results displayed on one page. 145 | :type count: int 146 | :param page: Optional. The page number for listing the results. 147 | :type page: int 148 | :param order: Optional. "asc" or "desc". Default: "asc". 149 | :type order: str 150 | :returns A list of objects. 151 | :rtype [Namespace] 152 | :raises ApiError: If API fails 153 | :raises Exception: If the API response is somehow malformed. 154 | """ 155 | return requests.get( 156 | url=f"{self.url}/pools/{pool_id}/history", 157 | params=self.query_parameters(kwargs), 158 | headers=self.default_headers 159 | ) 160 | 161 | 162 | @request_wrapper 163 | def pool_metadata(self, pool_id: str, **kwargs): 164 | """ 165 | Stake pool registration metadata. 166 | 167 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1{pool_id}~1metadata/get 168 | 169 | :param pool_id: Bech32 or hexadecimal pool ID. 170 | :type pool_id: str 171 | :returns object. 172 | :rtype: Namespace 173 | :raises ApiError: If API fails 174 | :raises Exception: If the API response is somehow malformed. 175 | """ 176 | return requests.get( 177 | url=f"{self.url}/pools/{pool_id}/metadata", 178 | headers=self.default_headers 179 | ) 180 | 181 | 182 | @list_request_wrapper 183 | def pool_relays(self, pool_id: str, **kwargs): 184 | """ 185 | Relays of a stake pool. 186 | 187 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1{pool_id}~1relays/get 188 | 189 | :param pool_id: Bech32 or hexadecimal pool ID. 190 | :type pool_id: str 191 | :returns A list of objects. 192 | :rtype [Namespace] 193 | :raises ApiError: If API fails 194 | :raises Exception: If the API response is somehow malformed. 195 | """ 196 | return requests.get( 197 | url=f"{self.url}/pools/{pool_id}/relays", 198 | headers=self.default_headers 199 | ) 200 | 201 | 202 | @list_request_wrapper 203 | def pool_delegators(self, pool_id: str, **kwargs): 204 | """ 205 | List of current stake pools delegators. 206 | 207 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1{pool_id}~1delegators/get 208 | 209 | :param pool_id: Bech32 or hexadecimal pool ID. 210 | :type pool_id: str 211 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 212 | :type gather_pages: bool 213 | :param count: Optional. Default: 100. The number of results displayed on one page. 214 | :type count: int 215 | :param page: Optional. The page number for listing the results. 216 | :type page: int 217 | :param order: Optional. "asc" or "desc". Default: "asc". 218 | :type order: str 219 | :returns A list of objects. 220 | :rtype [Namespace] 221 | :raises ApiError: If API fails 222 | :raises Exception: If the API response is somehow malformed. 223 | """ 224 | return requests.get( 225 | url=f"{self.url}/pools/{pool_id}/delegators", 226 | params=self.query_parameters(kwargs), 227 | headers=self.default_headers 228 | ) 229 | 230 | 231 | @list_request_wrapper 232 | def pool_blocks(self, pool_id: str, **kwargs): 233 | """ 234 | List of stake pools blocks. 235 | 236 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1{pool_id}~1blocks/get 237 | 238 | :param pool_id: Bech32 or hexadecimal pool ID. 239 | :type pool_id: str 240 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 241 | :type gather_pages: bool 242 | :param count: Optional. Default: 100. The number of results displayed on one page. 243 | :type count: int 244 | :param page: Optional. The page number for listing the results. 245 | :type page: int 246 | :param order: Optional. "asc" or "desc". Default: "asc". 247 | :type order: str 248 | :returns A list of str objects. 249 | :rtype [str] 250 | :raises ApiError: If API fails 251 | :raises Exception: If the API response is somehow malformed. 252 | """ 253 | return requests.get( 254 | url=f"{self.url}/pools/{pool_id}/blocks", 255 | params=self.query_parameters(kwargs), 256 | headers=self.default_headers 257 | ) 258 | 259 | 260 | @list_request_wrapper 261 | def pool_updates(self, pool_id: str, **kwargs): 262 | """ 263 | List of certificate updates to the stake pool. 264 | 265 | https://docs.blockfrost.io/#tag/Cardano-Pools/paths/~1pools~1{pool_id}~1updates/get 266 | 267 | :param pool_id: Bech32 or hexadecimal pool ID. 268 | :type pool_id: str 269 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 270 | :type gather_pages: bool 271 | :param count: Optional. Default: 100. The number of results displayed on one page. 272 | :type count: int 273 | :param page: Optional. The page number for listing the results. 274 | :type page: int 275 | :param order: Optional. "asc" or "desc". Default: "asc". 276 | :type order: str 277 | :returns A list of objects. 278 | :rtype [Namespace] 279 | :raises ApiError: If API fails 280 | :raises Exception: If the API response is somehow malformed. 281 | """ 282 | return requests.get( 283 | url=f"{self.url}/pools/{pool_id}/updates", 284 | params=self.query_parameters(kwargs), 285 | headers=self.default_headers 286 | ) 287 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/scripts.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper, list_request_wrapper 3 | 4 | 5 | @list_request_wrapper 6 | def scripts(self, **kwargs): 7 | """ 8 | List of scripts. 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Scripts/paths/~1scripts/get 11 | 12 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 13 | :type return_type: str 14 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 15 | :type gather_pages: bool 16 | :param count: Optional. Default: 100. The number of results displayed on one page. 17 | :type count: int 18 | :param page: Optional. The page number for listing the results. 19 | :type page: int 20 | :param order: Optional. "asc" or "desc". Default: "asc". 21 | :type order: str 22 | :returns A list of objects. 23 | :rtype [Namespace] 24 | :raises ApiError: If API fails 25 | :raises Exception: If the API response is somehow malformed. 26 | """ 27 | return requests.get( 28 | url=f"{self.url}/scripts", 29 | params=self.query_parameters(kwargs), 30 | headers=self.default_headers 31 | ) 32 | 33 | 34 | @request_wrapper 35 | def script(self, script_hash: str, **kwargs): 36 | """ 37 | Information about a specific script. 38 | 39 | https://docs.blockfrost.io/#tag/Cardano-Scripts/paths/~1scripts~1{script_hash}/get 40 | 41 | :param script_hash: Hash of the script. 42 | :type script_hash: str 43 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 44 | :type return_type: str 45 | :returns object. 46 | :rtype: Namespace 47 | :raises ApiError: If API fails 48 | :raises Exception: If the API response is somehow malformed. 49 | """ 50 | return requests.get( 51 | url=f"{self.url}/scripts/{script_hash}", 52 | headers=self.default_headers 53 | ) 54 | 55 | 56 | @request_wrapper 57 | def script_json(self, script_hash: str, **kwargs): 58 | """ 59 | JSON representation of a timelock script. 60 | 61 | https://docs.blockfrost.io/#tag/Cardano-Scripts/paths/~1scripts~1{script_hash}~1json/get 62 | 63 | :param script_hash: Hash of the script. 64 | :type script_hash: str 65 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 66 | :type return_type: str 67 | :returns object. 68 | :rtype: Namespace 69 | :raises ApiError: If API fails 70 | :raises Exception: If the API response is somehow malformed. 71 | """ 72 | return requests.get( 73 | url=f"{self.url}/scripts/{script_hash}/json", 74 | headers=self.default_headers 75 | ) 76 | 77 | 78 | @request_wrapper 79 | def script_cbor(self, script_hash: str, **kwargs): 80 | """ 81 | CBOR representation of a plutus script 82 | 83 | https://docs.blockfrost.io/#tag/Cardano-Scripts/paths/~1scripts~1{script_hash}~1cbor/get 84 | 85 | :param script_hash: Hash of the script. 86 | :type script_hash: str 87 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 88 | :type return_type: str 89 | :returns object. 90 | :rtype: Namespace 91 | :raises ApiError: If API fails 92 | :raises Exception: If the API response is somehow malformed. 93 | """ 94 | return requests.get( 95 | url=f"{self.url}/scripts/{script_hash}/cbor", 96 | headers=self.default_headers 97 | ) 98 | 99 | 100 | @list_request_wrapper 101 | def script_redeemers(self, script_hash: str, **kwargs): 102 | """ 103 | List of redeemers of a specific script. 104 | 105 | https://docs.blockfrost.io/#tag/Cardano-Scripts/paths/~1scripts~1{script_hash}~1redeemers/get 106 | 107 | :param script_hash: Hash of the script. 108 | :type script_hash: str 109 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 110 | :type return_type: str 111 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 112 | :type gather_pages: bool 113 | :param count: Optional. Default: 100. The number of results displayed on one page. 114 | :type count: int 115 | :param page: Optional. The page number for listing the results. 116 | :type page: int 117 | :param order: Optional. "asc" or "desc". Default: "asc". 118 | :type order: str 119 | :returns A list of objects. 120 | :rtype [Namespace] 121 | :raises ApiError: If API fails 122 | :raises Exception: If the API response is somehow malformed. 123 | """ 124 | return requests.get( 125 | url=f"{self.url}/scripts/{script_hash}/redeemers", 126 | params=self.query_parameters(kwargs), 127 | headers=self.default_headers 128 | ) 129 | 130 | 131 | @request_wrapper 132 | def script_datum(self, datum_hash: str, **kwargs): 133 | """ 134 | Query JSON value of a datum by its hash. 135 | 136 | https://docs.blockfrost.io/#tag/Cardano-Scripts/paths/~1scripts~1datum~1{datum_hash}/get 137 | 138 | :param datum_hash: Hash of the datum. 139 | :type datum_hash: str 140 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 141 | :type return_type: str 142 | :returns object. 143 | :rtype: Namespace 144 | :raises ApiError: If API fails 145 | :raises Exception: If the API response is somehow malformed. 146 | """ 147 | return requests.get( 148 | url=f"{self.url}/scripts/datum/{datum_hash}", 149 | headers=self.default_headers 150 | ) 151 | 152 | @request_wrapper 153 | def script_datum_cbor(self, datum_hash: str, **kwargs): 154 | """ 155 | Query CBOR value of a datum by its hash. 156 | 157 | https://docs.blockfrost.io/#tag/Cardano-Scripts/paths/~1scripts~1datum~1{datum_hash}~1cbor/get 158 | 159 | :param datum_hash: Hash of the datum. 160 | :type datum_hash: str 161 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 162 | :type return_type: str 163 | :returns object. 164 | :rtype: Namespace 165 | :raises ApiError: If API fails 166 | :raises Exception: If the API response is somehow malformed. 167 | """ 168 | return requests.get( 169 | url=f"{self.url}/scripts/datum/{datum_hash}/cbor", 170 | headers=self.default_headers 171 | ) 172 | -------------------------------------------------------------------------------- /blockfrost/api/cardano/utils.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from blockfrost.utils import request_wrapper, list_request_wrapper 3 | 4 | 5 | @list_request_wrapper 6 | def utils_addresses_xpub(self, xpub: str, role: int, index: int, **kwargs): 7 | """ 8 | Derive Shelley address from an xpub 9 | 10 | https://docs.blockfrost.io/#tag/Cardano-Utilities/paths/~1utils~1addresses~1xpub~1{xpub}~1{role}~1{index}/get 11 | 12 | :param xpub: Hex xpub. 13 | :type xpub: str 14 | :param role: Account role. 15 | :type role: int 16 | :param index: Address index. 17 | :type index: int 18 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 19 | :type return_type: str 20 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 21 | :type gather_pages: bool 22 | :param count: Optional. Default: 100. The number of results displayed on one page. 23 | :type count: int 24 | :param page: Optional. The page number for listing the results. 25 | :type page: int 26 | :param order: Optional. "asc" or "desc". Default: "asc". 27 | :type order: str 28 | :returns A list of objects. 29 | :rtype [Namespace] 30 | :raises ApiError: If API fails 31 | :raises Exception: If the API response is somehow malformed. 32 | """ 33 | return requests.get( 34 | url=f"{self.url}/utils/addresses/xpub/{xpub}/{role}/{index}", 35 | params=self.query_parameters(kwargs), 36 | headers=self.default_headers 37 | ) 38 | -------------------------------------------------------------------------------- /blockfrost/api/health.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from ..utils import request_wrapper 3 | 4 | 5 | @request_wrapper 6 | def health(self, **kwargs): 7 | """ 8 | Return backend status as a boolean. Your application should handle situations when backend for the given chain is unavailable. 9 | 10 | https://docs.blockfrost.io/#tag/Health/paths/~1health/get 11 | 12 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 13 | :type return_type: str 14 | :returns HealthResponse object. 15 | :rtype HealthResponse 16 | :raises ApiError: If API fails 17 | :raises Exception: If the API response is somehow malformed. 18 | """ 19 | return requests.get( 20 | url=f"{self.url}/health", 21 | headers=self.default_headers 22 | ) 23 | 24 | 25 | @request_wrapper 26 | def clock(self, **kwargs): 27 | """ 28 | This endpoint provides the current UNIX time. Your application might use this to verify if the client clock is not out of sync. 29 | 30 | https://docs.blockfrost.io/#tag/Health/paths/~1health~1clock/get 31 | 32 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 33 | :type return_type: str 34 | :returns ClockResponse object. 35 | :rtype ClockResponse 36 | :raises ApiError: If API fails 37 | :raises Exception: If the API response is somehow malformed. 38 | """ 39 | return requests.get( 40 | url=f"{self.url}/health/clock", 41 | headers=self.default_headers 42 | ) 43 | -------------------------------------------------------------------------------- /blockfrost/api/metrics.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from dataclasses import dataclass 3 | from ..utils import list_request_wrapper 4 | 5 | 6 | @list_request_wrapper 7 | def metrics(self, **kwargs): 8 | """ 9 | History of your Blockfrost usage metrics in the past 30 days. 10 | 11 | https://docs.blockfrost.io/#tag/Metrics/paths/~1metrics~1/get 12 | 13 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 14 | :type return_type: str 15 | :returns A list of objects. 16 | :rtype [Namespace] 17 | :raises ApiError: If API fails 18 | :raises Exception: If the API response is somehow malformed. 19 | """ 20 | return requests.get( 21 | url=f"{self.url}/metrics", 22 | headers=self.default_headers 23 | ) 24 | 25 | 26 | @list_request_wrapper 27 | def metrics_endpoints(self, **kwargs): 28 | """ 29 | History of your Blockfrost usage metrics per endpoint in the past 30 days. 30 | 31 | https://docs.blockfrost.io/#tag/Metrics/paths/~1metrics~1endpoints/get 32 | 33 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 34 | :type return_type: str 35 | :returns A list of objects. 36 | :rtype [Namespace] 37 | :raises ApiError: If API fails 38 | :raises Exception: If the API response is somehow malformed. 39 | """ 40 | return requests.get( 41 | url=f"{self.url}/metrics/endpoints", 42 | headers=self.default_headers 43 | ) 44 | -------------------------------------------------------------------------------- /blockfrost/api/nutlink.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from dataclasses import dataclass 3 | from ..utils import request_wrapper, list_request_wrapper 4 | 5 | 6 | @request_wrapper 7 | def nutlink_address(self, address: str, **kwargs): 8 | """ 9 | List metadata about specific address 10 | 11 | https://docs.blockfrost.io/#tag/Nut.link 12 | 13 | :param address: Address of a metadata oracle. 14 | :type address: str 15 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 16 | :type return_type: str 17 | :returns NutlinkAddressResponse object. 18 | :rtype NutlinkAddressResponse 19 | :raises ApiError: If API fails 20 | :raises Exception: If the API response is somehow malformed. 21 | """ 22 | return requests.get( 23 | url=f"{self.url}/nutlink/{address}", 24 | headers=self.default_headers 25 | ) 26 | 27 | 28 | @list_request_wrapper 29 | def nutlink_address_tickers(self, address: str, **kwargs): 30 | """ 31 | List tickers for a specific metadata oracle 32 | 33 | https://docs.blockfrost.io/#tag/Nut.link/paths/~1nutlink~1{address}~1tickers/get 34 | 35 | :param address: Address of a metadata oracle. 36 | :type address: str 37 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 38 | :type return_type: str 39 | :param gather_pages: Optional. Default: 100. Will collect all pages into one return 40 | :type gather_pages: bool 41 | :param count: Optional. Default: 1. The number of results displayed on one page. 42 | :type count: int 43 | :param page: Optional. The page number for listing the results. 44 | :type page: int 45 | :param order: Optional. "asc" or "desc". Default: "asc". 46 | :type order: str 47 | :returns A list of NutlinkAddressTickersResponse objects. 48 | :rtype [NutlinkAddressTickersResponse] 49 | :raises ApiError: If API fails 50 | :raises Exception: If the API response is somehow malformed. 51 | """ 52 | return requests.get( 53 | url=f"{self.url}/nutlink/{address}/tickers", 54 | params=self.query_parameters(kwargs), 55 | headers=self.default_headers 56 | ) 57 | 58 | 59 | @list_request_wrapper 60 | def nutlink_address_ticker(self, address: str, ticker: str, **kwargs): 61 | """ 62 | List of records of a specific ticker 63 | 64 | https://docs.blockfrost.io/#tag/Nut.link/paths/~1nutlink~1{address}~1tickers~1{ticker}/get 65 | 66 | :param address: Address of a metadata oracle. 67 | :type address: str 68 | :param ticker: Ticker for the pool record. 69 | :type ticker: str 70 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 71 | :type return_type: str 72 | :param gather_pages: Optional. Default: 100. Will collect all pages into one return 73 | :type gather_pages: bool 74 | :param count: Optional. Default: 1. The number of results displayed on one page. 75 | :type count: int 76 | :param page: Optional. The page number for listing the results. 77 | :type page: int 78 | :param order: Optional. "asc" or "desc". Default: "asc". 79 | :type order: str 80 | :returns A list of NutlinkAddressTickerResponse objects. 81 | :rtype [NutlinkAddressTickerResponse] 82 | :raises ApiError: If API fails 83 | :raises Exception: If the API response is somehow malformed. 84 | """ 85 | return requests.get( 86 | url=f"{self.url}/nutlink/{address}/tickers/{ticker}", 87 | params=self.query_parameters(kwargs), 88 | headers=self.default_headers 89 | ) 90 | 91 | 92 | @list_request_wrapper 93 | def nutlink_ticker(self, ticker: str, **kwargs): 94 | """ 95 | List of records of a specific ticker 96 | 97 | https://docs.blockfrost.io/#tag/Nut.link/paths/~1nutlink~1tickers~1{ticker}/get 98 | 99 | :param ticker: Ticker for the pool record. 100 | :type ticker: str 101 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 102 | :type return_type: str 103 | :param gather_pages: Optional. Default: 100. Will collect all pages into one return 104 | :type gather_pages: bool 105 | :param count: Optional. Default: 1. The number of results displayed on one page. 106 | :type count: int 107 | :param page: Optional. The page number for listing the results. 108 | :type page: int 109 | :param order: Optional. "asc" or "desc". Default: "asc". 110 | :type order: str 111 | :returns A list of NutlinkTickerResponse objects. 112 | :rtype [NutlinkTickerResponse] 113 | :raises ApiError: If API fails 114 | :raises Exception: If the API response is somehow malformed. 115 | """ 116 | return requests.get( 117 | url=f"{self.url}/nutlink/tickers/{ticker}", 118 | params=self.query_parameters(kwargs), 119 | headers=self.default_headers 120 | ) 121 | -------------------------------------------------------------------------------- /blockfrost/config.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | try: 3 | from importlib.metadata import version 4 | except ImportError: # for Python<3.8 5 | from importlib_metadata import version 6 | 7 | class ApiUrls(Enum): 8 | mainnet = 'https://cardano-mainnet.blockfrost.io/api' 9 | preprod = 'https://cardano-preprod.blockfrost.io/api' 10 | preview = 'https://cardano-preview.blockfrost.io/api' 11 | testnet = 'https://cardano-testnet.blockfrost.io/api' 12 | ipfs = 'https://ipfs.blockfrost.io/api' 13 | 14 | 15 | DEFAULT_API_VERSION = 'v0' 16 | DEFAULT_ORDER = 'asc' 17 | DEFAULT_PAGINATION_PAGE_COUNT = 1 18 | DEFAULT_PAGINATION_PAGE_ITEMS_COUNT = 100 19 | 20 | ADDRESS_GAP_LIMIT = 20 21 | 22 | package_name = 'blockfrost-python' 23 | version = version(package_name) 24 | USER_AGENT = f'{package_name} {version}' 25 | -------------------------------------------------------------------------------- /blockfrost/helpers.py: -------------------------------------------------------------------------------- 1 | import hmac 2 | import hashlib 3 | import time 4 | 5 | 6 | class SignatureVerificationError(Exception): 7 | def __init__(self, message, header, request_body): 8 | self.message = message 9 | self.header = header 10 | self.request_body = request_body 11 | super().__init__(self.message) 12 | 13 | 14 | def get_unix_timestamp(): 15 | return int(time.time()) 16 | 17 | 18 | def verify_webhook_signature(request_body, signature_header, secret, timestamp_tolerance_seconds=600): 19 | # Parse signature header 20 | # Example of Blockfrost-Signature header: t=1648550558,v1=162381a59040c97d9b323cdfec02facdfce0968490ec1732f5d938334c1eed4e,v1=...) 21 | tokens = signature_header.split(',') 22 | timestamp = None 23 | signatures = [] 24 | for token in tokens: 25 | key, value = token.split('=') 26 | if key == 't': 27 | timestamp = value 28 | elif key == 'v1': 29 | signatures.append(value) 30 | else: 31 | print('Cannot parse part of the Blockfrost-Signature header, key "{}" is not supported by this version of Blockfrost SDK. Please upgrade.'.format(key)) 32 | 33 | if timestamp is None or timestamp.isnumeric() is False or len(tokens) < 2: 34 | # timestamp and at least one signature must be present 35 | raise SignatureVerificationError( 36 | 'Invalid signature header format.', signature_header, request_body) 37 | 38 | if len(signatures) == 0: 39 | # There are no signatures that this version of SDK supports 40 | raise SignatureVerificationError( 41 | 'No signatures with supported version scheme.', signature_header, request_body) 42 | 43 | has_valid_signature = False 44 | for signature in signatures: 45 | # Recreate signature by concatenating the timestamp with the payload (all in bytes), 46 | # then compute HMAC using sha256 and provided secret (webhook auth token) 47 | signature_payload = timestamp.encode() + b"." + request_body 48 | local_signature = hmac.new( 49 | secret.encode(), signature_payload, hashlib.sha256).hexdigest() 50 | 51 | # computed signature should match at least one signature parsed from a signature header 52 | if (hmac.compare_digest(signature, local_signature)): 53 | has_valid_signature = True 54 | break 55 | 56 | if has_valid_signature == False: 57 | raise SignatureVerificationError( 58 | 'No signature matches the expected signature for the payload.', signature_header, request_body) 59 | 60 | current_timestamp = get_unix_timestamp() 61 | 62 | if (current_timestamp - int(timestamp) > timestamp_tolerance_seconds): 63 | # Event is older than timestamp_tolerance_seconds 64 | raise SignatureVerificationError( 65 | 'Signature\'s timestamp is outside of the time tolerance.', signature_header, request_body) 66 | else: 67 | # Successfully validate the signature only if it is within timestamp_tolerance_seconds tolerance 68 | return True 69 | -------------------------------------------------------------------------------- /blockfrost/ipfs/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from blockfrost.config import DEFAULT_API_VERSION 4 | from ..utils import Api, ApiUrls 5 | 6 | 7 | class BlockFrostIPFS(Api): 8 | 9 | def __init__(self, project_id: str = None, base_url: str = None, api_version: str = None): 10 | super().__init__( 11 | project_id=project_id, 12 | base_url=base_url if base_url else os.environ.get( 13 | 'BLOCKFROST_IPFS_URL', default=ApiUrls.ipfs.value), 14 | api_version=api_version if base_url else os.environ.get('BLOCKFROST_API_VERSION', 15 | default=DEFAULT_API_VERSION)) 16 | 17 | from .add import add 18 | from .gateway import gateway 19 | from .pins import \ 20 | pin_object, \ 21 | pined_list, \ 22 | pined_object, \ 23 | pined_object_remove 24 | -------------------------------------------------------------------------------- /blockfrost/ipfs/add.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from dataclasses import dataclass 3 | from ..utils import request_wrapper 4 | 5 | 6 | @request_wrapper 7 | def add(self, file_path: str, **kwargs): 8 | """ 9 | Add a file or directory to IPFS 10 | 11 | You need to `/ipfs/pin/add` an object to avoid it being garbage collected. 12 | This usage is being counted in your user account quota. 13 | 14 | https://docs.blockfrost.io/#tag/IPFS-Add/paths/~1ipfs~1add/post 15 | 16 | :param file_path: Path to file. 17 | :type file_path: str 18 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 19 | :type return_type: str 20 | :returns IPFSObject object. 21 | :rtype IPFSObject 22 | :raises ApiError: If API fails 23 | :raises Exception: If the API response is somehow malformed. 24 | """ 25 | with open(file_path, 'rb') as file: 26 | return requests.post( 27 | url=f"{self.url}/ipfs/add", 28 | headers=self.default_headers, 29 | files={'file': file}, 30 | ) 31 | -------------------------------------------------------------------------------- /blockfrost/ipfs/gateway.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from ..utils import simple_request_wrapper 3 | 4 | 5 | @simple_request_wrapper 6 | def gateway(self, IPFS_path: str, **kwargs): 7 | """ 8 | Retrieve an object from the IFPS gateway (useful if you do not want to rely on a public gateway, such as ipfs.blockfrost.dev). 9 | 10 | https://docs.blockfrost.io/#tag/IPFS-Gateway 11 | 12 | :param IPFS_path: Path to the IPFS object. 13 | :type IPFS_path: str 14 | :returns file text. 15 | :rtype data 16 | :raises ApiError: If API fails 17 | :raises Exception: If the API response is somehow malformed. 18 | """ 19 | 20 | response = requests.get( 21 | url=f"{self.url}/ipfs/gateway/{IPFS_path}", 22 | headers=self.default_headers, 23 | ) 24 | return response 25 | -------------------------------------------------------------------------------- /blockfrost/ipfs/pins.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from dataclasses import dataclass 3 | from ..utils import request_wrapper, list_request_wrapper 4 | 5 | 6 | @request_wrapper 7 | def pin_object(self, IPFS_path: str, **kwargs): 8 | """ 9 | Pinned objects are counted in your user storage quota. 10 | 11 | https://docs.blockfrost.io/#tag/IPFS-Pins 12 | 13 | :param IPFS_path: Path to the IPFS object. 14 | :type IPFS_path: str 15 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 16 | :type return_type: str 17 | :returns object. 18 | :rtype: Namespace 19 | :raises ApiError: If API fails 20 | :raises Exception: If the API response is somehow malformed. 21 | """ 22 | return requests.post( 23 | url=f"{self.url}/ipfs/pin/add/{IPFS_path}", 24 | headers=self.default_headers, 25 | ) 26 | 27 | 28 | @list_request_wrapper 29 | def pined_list(self, **kwargs): 30 | """ 31 | List objects pinned to local storage 32 | 33 | https://docs.blockfrost.io/#tag/IPFS-Pins/paths/~1ipfs~1pin~1list~1/get 34 | 35 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 36 | :type return_type: str 37 | :param gather_pages: Optional. Default: false. Will collect all pages into one return 38 | :type gather_pages: bool 39 | :param count: Optional. Default: 100. The number of results displayed on one page. 40 | :type count: int 41 | :param page: Optional. The page number for listing the results. 42 | :type page: int 43 | :param order: Optional. "asc" or "desc". Default: "asc". 44 | :type order: str 45 | :returns A list of objects. 46 | :rtype [Namespace] 47 | :raises ApiError: If API fails 48 | :raises Exception: If the API response is somehow malformed. 49 | """ 50 | return requests.get( 51 | url=f"{self.url}/ipfs/pin/list", 52 | params=self.query_parameters(kwargs), 53 | headers=self.default_headers, 54 | ) 55 | 56 | 57 | @request_wrapper 58 | def pined_object(self, IPFS_path: str, **kwargs): 59 | """ 60 | List objects pinned to local storage 61 | 62 | https://docs.blockfrost.io/#tag/IPFS-Pins/paths/~1ipfs~1pin~1list~1{IPFS_path}/get 63 | 64 | :param IPFS_path: Path to the IPFS object. 65 | :type IPFS_path: str 66 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 67 | :type return_type: str 68 | :returns object. 69 | :rtype: Namespace 70 | :raises ApiError: If API fails 71 | :raises Exception: If the API response is somehow malformed. 72 | """ 73 | return requests.get( 74 | url=f"{self.url}/ipfs/pin/list/{IPFS_path}", 75 | headers=self.default_headers, 76 | ) 77 | 78 | 79 | @request_wrapper 80 | def pined_object_remove(self, IPFS_path: str, **kwargs): 81 | """ 82 | Remove pinned objects from local storage 83 | 84 | https://docs.blockfrost.io/#tag/IPFS-Pins/paths/~1ipfs~1pin~1remove~1{IPFS_path}/post 85 | 86 | :param IPFS_path: Path to the IPFS object. 87 | :type IPFS_path: str 88 | :param return_type: Optional. "object", "json" or "pandas". Default: "object". 89 | :type return_type: str 90 | :returns object. 91 | :rtype: Namespace 92 | :raises ApiError: If API fails 93 | :raises Exception: If the API response is somehow malformed. 94 | """ 95 | return requests.post( 96 | url=f"{self.url}/ipfs/pin/remove/{IPFS_path}", 97 | headers=self.default_headers, 98 | ) 99 | -------------------------------------------------------------------------------- /blockfrost/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from requests import Response 5 | from functools import wraps 6 | 7 | from .config import ApiUrls, USER_AGENT, DEFAULT_API_VERSION, DEFAULT_PAGINATION_PAGE_ITEMS_COUNT 8 | from types import SimpleNamespace 9 | 10 | 11 | class ApiError(Exception): 12 | 13 | def __init__(self, response: Response): 14 | try: 15 | # assume Response json 16 | response_json = response.json() 17 | super().__init__(response_json) 18 | self.status_code = response_json['status_code'] 19 | self.error = response_json['error'] 20 | self.message = response_json['message'] 21 | except Exception: 22 | super().__init__(response) 23 | self.status_code = response.status_code 24 | self.error = None 25 | self.message = None 26 | 27 | 28 | class Namespace(SimpleNamespace): 29 | def to_dict(self): 30 | return self.__dict__ 31 | 32 | def to_json(self): 33 | return json.dumps(self.to_dict()) 34 | 35 | 36 | def convert_json_to_object(json_response): 37 | return json.loads(json.dumps(json_response), object_hook=lambda d: Namespace(**d)) 38 | 39 | 40 | def convert_json_to_pandas(json_response): 41 | try: 42 | import pandas as pd 43 | return pd.json_normalize(json_response) 44 | except ImportError as error: 45 | raise ImportError( 46 | "To use \"return_type='pandas'\" you must pip install pandas") 47 | 48 | 49 | def simple_request_wrapper(func): 50 | @wraps(func) 51 | def error_wrapper(*args, **kwargs): 52 | request_response: Response = func(*args, **kwargs) 53 | if request_response.status_code != 200: 54 | raise ApiError(request_response) 55 | else: 56 | return request_response 57 | 58 | return error_wrapper 59 | 60 | 61 | def request_wrapper(func): 62 | @wraps(func) 63 | def error_wrapper(*args, **kwargs): 64 | request_response: Response = func(*args, **kwargs) 65 | if request_response.status_code != 200: 66 | raise ApiError(request_response) 67 | else: 68 | if 'return_type' in kwargs: 69 | if kwargs['return_type'] == 'object': 70 | return convert_json_to_object(request_response.json()) 71 | elif kwargs['return_type'] == 'pandas': 72 | return convert_json_to_pandas(request_response.json()) 73 | elif kwargs['return_type'] == 'json': 74 | return request_response.json() 75 | else: 76 | return convert_json_to_object(request_response.json()) 77 | 78 | return error_wrapper 79 | 80 | 81 | def list_request_wrapper(func): 82 | @wraps(func) 83 | def pagination(*args, **kwargs): 84 | def recursive_append(json_list, *args, **kwargs): 85 | request_response: Response = func(*args, **kwargs) 86 | if request_response.status_code != 200: 87 | raise ApiError(request_response) 88 | json_list.extend(request_response.json()) 89 | if 'count' not in kwargs: 90 | expected_result_length = DEFAULT_PAGINATION_PAGE_ITEMS_COUNT 91 | else: 92 | expected_result_length = kwargs['count'] 93 | if len(request_response.json()) == expected_result_length: 94 | if 'page' not in kwargs: 95 | kwargs['page'] = 2 96 | else: 97 | kwargs['page'] = kwargs['page'] + 1 98 | recursive_append(json_list, *args, **kwargs) 99 | else: 100 | return json_list 101 | 102 | if 'gather_pages' in kwargs and kwargs['gather_pages'] is True: 103 | json_list = [] 104 | recursive_append(json_list, *args, **kwargs) 105 | request_json = json_list 106 | else: 107 | request_response: Response = func(*args, **kwargs) 108 | if request_response.status_code != 200: 109 | raise ApiError(request_response) 110 | request_json = request_response.json() 111 | if 'return_type' in kwargs: 112 | if kwargs['return_type'] == 'object': 113 | return convert_json_to_object(request_json) 114 | elif kwargs['return_type'] == 'pandas': 115 | return convert_json_to_pandas(request_json) 116 | elif kwargs['return_type'] == 'json': 117 | return request_json 118 | else: 119 | return convert_json_to_object(request_json) 120 | 121 | return pagination 122 | 123 | 124 | class Api: 125 | 126 | def __init__( 127 | self, 128 | project_id: str = None, 129 | base_url: str = None, 130 | api_version: str = None, 131 | ): 132 | self.project_id = project_id if project_id else os.environ.get( 133 | 'BLOCKFROST_PROJECT_ID') 134 | self.api_version = api_version if api_version else os.environ.get('BLOCKFROST_API_VERSION', 135 | default=DEFAULT_API_VERSION) 136 | self.base_url = base_url 137 | 138 | @property 139 | def url(self): 140 | return f"{self.base_url}/{self.api_version}" if self.api_version else f"{self.base_url}" 141 | 142 | @property 143 | def authentication_header(self): 144 | return { 145 | 'project_id': self.project_id 146 | } 147 | 148 | @property 149 | def user_agent_header(self): 150 | return { 151 | 'User-Agent': USER_AGENT 152 | } 153 | 154 | @property 155 | def default_headers(self): 156 | return {**self.authentication_header, **self.user_agent_header} 157 | 158 | @staticmethod 159 | def query_parameters(kwargs: dict): 160 | """ 161 | count 162 | integer <= 100 163 | Default: 100 164 | The number of results displayed on one page. 165 | 166 | page 167 | integer 168 | Default: 1 169 | The page number for listing the results. 170 | 171 | order 172 | string 173 | Default: "asc" 174 | Enum: "asc" "desc" 175 | The ordering of items from the point of view of the blockchain, not the page listing itself. By default, we return oldest first, newest last. 176 | """ 177 | return { 178 | "count": kwargs.get('count', None), 179 | "page": kwargs.get('page', None), 180 | "order": kwargs.get('order', None), 181 | } 182 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import pathlib 3 | from setuptools import setup, find_packages 4 | 5 | HERE = pathlib.Path(__file__).parent 6 | 7 | long_description = (HERE / 'README.md').read_text(encoding='utf-8') 8 | 9 | setup( 10 | name='blockfrost-python', 11 | version='0.6.0', 12 | description='The official Python SDK for Blockfrost API v0.1.37', 13 | long_description=long_description, 14 | long_description_content_type='text/markdown', 15 | url='https://github.com/blockfrost/blockfrost-python', 16 | # Author details 17 | author='blockfrost.io', 18 | author_email='contact@blockfrost.io', 19 | ghostwriter='https://github.com/mathiasfrohlich', 20 | license='Apache-2.0', 21 | keywords='blockfrost blockchain cardano ipfs', 22 | packages=find_packages(exclude=['tests', 'tests.*']), 23 | python_requires='>=3.7, <4', 24 | requires=[ 25 | "importlib_metadata", 26 | ], 27 | install_requires=[ 28 | "requests", 29 | ], 30 | tests_require=[ 31 | "pytest", 32 | "mock", 33 | "requests-mock", 34 | "pandas", 35 | ], 36 | 37 | classifiers=[ # Optional 38 | # How mature is this project? Common values are 39 | # 3 - Alpha 40 | # 4 - Beta 41 | # 5 - Production/Stable 42 | 'Development Status :: 4 - Beta', 43 | 44 | 'Intended Audience :: Developers', 45 | 46 | 'Topic :: Software Development :: Build Tools', 47 | 48 | 'License :: OSI Approved :: Apache Software License', 49 | 50 | 'Programming Language :: Python :: 3', 51 | 'Programming Language :: Python :: 3.7', 52 | 'Programming Language :: Python :: 3.8', 53 | 'Programming Language :: Python :: 3.9', 54 | 'Programming Language :: Python :: 3.10', 55 | 'Programming Language :: Python :: 3 :: Only', 56 | ], 57 | ) 58 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {} }: 2 | pkgs.mkShell { 3 | buildInputs = [ 4 | (pkgs.python3.withPackages (ps: [ 5 | ps.requests 6 | # tests 7 | ps.setuptools 8 | ps.pytest 9 | ps.mock 10 | ps.requests-mock 11 | ps.pandas 12 | ]) 13 | ) 14 | ]; 15 | 16 | shellHook = '' 17 | echo 18 | echo '# blockfrost-python development shell' 19 | echo 20 | echo '## to run unit tests, use' 21 | echo 'pytest' 22 | echo 23 | echo '## to run integration tests, use' 24 | echo 'export BLOCKFROST_PROJECT_ID_MAINNET=mainnet..' 25 | echo 'pytest' 26 | ''; 27 | } 28 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | mock 3 | requests-mock 4 | pandas -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockfrost/blockfrost-python/cb7418e8ce19b6761490cd14f65ebf34cd7e1527/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | 6 | def test_root(requests_mock): 7 | api = BlockFrostApi() 8 | mock_data = { 9 | "url": "https://blockfrost.io/", 10 | "version": "0.1.0" 11 | } 12 | requests_mock.get(api.url + '/', json=mock_data) 13 | assert api.root() == convert_json_to_object(mock_data) 14 | 15 | 16 | def test_integration_root(): 17 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 18 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 19 | assert api.root() 20 | 21 | 22 | def test_integration_root(): 23 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 24 | assert True 25 | -------------------------------------------------------------------------------- /tests/test_cardano_accounts.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | stake_address = 'stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7' 6 | 7 | 8 | def test_accounts(requests_mock): 9 | api = BlockFrostApi() 10 | mock_data = { 11 | "stake_address": stake_address, 12 | "active": True, 13 | "active_epoch": 412, 14 | "controlled_amount": "619154618165", 15 | "rewards_sum": "319154618165", 16 | "withdrawals_sum": "12125369253", 17 | "reserves_sum": "319154618165", 18 | "treasury_sum": "12000000", 19 | "withdrawable_amount": "319154618165", 20 | "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy" 21 | } 22 | requests_mock.get(f"{api.url}/accounts/{stake_address}", json=mock_data) 23 | assert api.accounts(stake_address=stake_address) == convert_json_to_object(mock_data) 24 | 25 | 26 | def test_integration_accounts(): 27 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 28 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 29 | assert api.accounts(stake_address=stake_address) 30 | 31 | 32 | def test_account_rewards(requests_mock): 33 | api = BlockFrostApi() 34 | mock_data = [ 35 | { 36 | "epoch": 215, 37 | "amount": "12695385", 38 | "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy", 39 | "type": "member" 40 | }, 41 | { 42 | "epoch": 216, 43 | "amount": "3586329", 44 | "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy", 45 | "type": "member" 46 | }, 47 | { 48 | "epoch": 217, 49 | "amount": "1", 50 | "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy", 51 | "type": "member" 52 | }, 53 | { 54 | "epoch": 217, 55 | "amount": "1337", 56 | "pool_id": "pool1cytwr0n7eas6du2h2xshl8ypa1yqr18f0erlhhjcuczysiunjcs", 57 | "type": "leader" 58 | }, 59 | { 60 | "epoch": 218, 61 | "amount": "1395265", 62 | "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy", 63 | "type": "member" 64 | }, 65 | { 66 | "epoch": 218, 67 | "amount": "500000000", 68 | "pool_id": "pool1cytwr0n7eas6du2h2xshl8ypa1yqr18f0erlhhjcuczysiunjcs", 69 | "type": "pool_deposit_refund" 70 | } 71 | ] 72 | requests_mock.get(f"{api.url}/accounts/{stake_address}/rewards", json=mock_data) 73 | assert api.account_rewards(stake_address=stake_address) == convert_json_to_object(mock_data) 74 | 75 | 76 | def test_integration_account_rewards(): 77 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 78 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 79 | assert api.account_rewards(stake_address=stake_address) 80 | 81 | 82 | def test_account_history(requests_mock): 83 | api = BlockFrostApi() 84 | mock_data = [ 85 | { 86 | "active_epoch": 210, 87 | "amount": "12695385", 88 | "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy" 89 | }, 90 | { 91 | "active_epoch": 211, 92 | "amount": "22695385", 93 | "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy" 94 | } 95 | ] 96 | requests_mock.get(f"{api.url}/accounts/{stake_address}/history", json=mock_data) 97 | assert api.account_history(stake_address=stake_address) == convert_json_to_object(mock_data) 98 | 99 | 100 | def test_integration_account_history(): 101 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 102 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 103 | assert api.account_history(stake_address=stake_address) 104 | 105 | 106 | def test_account_delegations(requests_mock): 107 | api = BlockFrostApi() 108 | mock_data = [ 109 | { 110 | "active_epoch": 210, 111 | "tx_hash": "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531", 112 | "amount": "12695385", 113 | "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy" 114 | }, 115 | { 116 | "active_epoch": 242, 117 | "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0", 118 | "amount": "12691385", 119 | "pool_id": "pool1kchver88u3kygsak8wgll7htr8uxn5v35lfrsyy842nkscrzyvj" 120 | } 121 | ] 122 | requests_mock.get(f"{api.url}/accounts/{stake_address}/delegations", json=mock_data) 123 | assert api.account_delegations(stake_address=stake_address) == convert_json_to_object(mock_data) 124 | 125 | 126 | def test_integration_account_delegations(): 127 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 128 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 129 | assert api.account_delegations(stake_address=stake_address) 130 | 131 | 132 | def test_account_registrations(requests_mock): 133 | api = BlockFrostApi() 134 | mock_data = [ 135 | { 136 | "tx_hash": "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531", 137 | "action": "registered" 138 | }, 139 | { 140 | "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0", 141 | "action": "deregistered" 142 | } 143 | ] 144 | requests_mock.get(f"{api.url}/accounts/{stake_address}/registrations", json=mock_data) 145 | assert api.account_registrations(stake_address=stake_address) == convert_json_to_object(mock_data) 146 | 147 | 148 | def test_integration_account_registrations(): 149 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 150 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 151 | assert api.account_registrations(stake_address=stake_address) 152 | 153 | 154 | def test_account_withdrawals(requests_mock): 155 | api = BlockFrostApi() 156 | mock_data = [ 157 | { 158 | "tx_hash": "48a9625c841eea0dd2bb6cf551eabe6523b7290c9ce34be74eedef2dd8f7ecc5", 159 | "amount": "454541212442" 160 | }, 161 | { 162 | "tx_hash": "4230b0cbccf6f449f0847d8ad1d634a7a49df60d8c142bb8cc2dbc8ca03d9e34", 163 | "amount": "97846969" 164 | } 165 | ] 166 | requests_mock.get(f"{api.url}/accounts/{stake_address}/withdrawals", json=mock_data) 167 | assert api.account_withdrawals(stake_address=stake_address) == convert_json_to_object(mock_data) 168 | 169 | 170 | def test_integration_account_withdrawals(): 171 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 172 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 173 | assert api.account_withdrawals(stake_address=stake_address) 174 | 175 | 176 | def test_account_mirs(requests_mock): 177 | api = BlockFrostApi() 178 | mock_data = [ 179 | { 180 | "tx_hash": "69705bba1d687a816ff5a04ec0c358a1f1ef075ab7f9c6cc2763e792581cec6d", 181 | "amount": "2193707473" 182 | }, 183 | { 184 | "tx_hash": "baaa77b63d4d7d2bb3ab02c9b85978c2092c336dede7f59e31ad65452d510c13", 185 | "amount": "14520198574" 186 | } 187 | ] 188 | requests_mock.get(f"{api.url}/accounts/{stake_address}/mirs", json=mock_data) 189 | assert api.account_mirs(stake_address=stake_address) == convert_json_to_object(mock_data) 190 | 191 | 192 | def test_integration_account_mirs(): 193 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 194 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 195 | assert api.account_mirs(stake_address=stake_address) 196 | 197 | 198 | def test_account_addresses(requests_mock): 199 | api = BlockFrostApi() 200 | mock_data = [ 201 | { 202 | "address": "addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd" 203 | }, 204 | { 205 | "address": "addr1qys3czp8s9thc6u2fqed9yq3h24nyw28uk0m6mkgn9dkckjf79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9suth4w4" 206 | }, 207 | { 208 | "address": "addr1q8j55h253zcvl326sk5qdt2n8z7eghzspe0ekxgncr796s2f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sjmd35m" 209 | }, 210 | { 211 | "address": "addr1q8f7gxrprank3drhx8k5grlux7ene0nlwun8y9thu8mc3yjf79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sls6vnt" 212 | } 213 | ] 214 | requests_mock.get(f"{api.url}/accounts/{stake_address}/addresses", json=mock_data) 215 | assert api.account_addresses(stake_address=stake_address) == convert_json_to_object(mock_data) 216 | 217 | 218 | def test_integration_account_addresses(): 219 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 220 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 221 | assert api.account_addresses(stake_address=stake_address) 222 | 223 | 224 | def test_account_addresses_assets(requests_mock): 225 | api = BlockFrostApi() 226 | mock_data = [ 227 | { 228 | "unit": "d5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc537061636542756433343132", 229 | "quantity": "1" 230 | }, 231 | { 232 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 233 | "quantity": "125" 234 | } 235 | ] 236 | requests_mock.get(f"{api.url}/accounts/{stake_address}/addresses/assets", json=mock_data) 237 | assert api.account_addresses_assets(stake_address=stake_address) == convert_json_to_object(mock_data) 238 | 239 | 240 | def test_integration_account_addresses_assets(): 241 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 242 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 243 | assert api.account_addresses_assets(stake_address=stake_address) == [] 244 | 245 | 246 | def test_account_addresses_total(requests_mock): 247 | api = BlockFrostApi() 248 | mock_data = { 249 | "stake_address": "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk", 250 | "received_sum": [ 251 | { 252 | "unit": "lovelace", 253 | "quantity": "42000000" 254 | }, 255 | { 256 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 257 | "quantity": "12" 258 | } 259 | ], 260 | "sent_sum": [ 261 | { 262 | "unit": "lovelace", 263 | "quantity": "42000000" 264 | }, 265 | { 266 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 267 | "quantity": "12" 268 | } 269 | ], 270 | "tx_count": 12 271 | } 272 | requests_mock.get(f"{api.url}/accounts/{stake_address}/addresses/total", json=mock_data) 273 | assert api.account_addresses_total(stake_address=stake_address) == convert_json_to_object(mock_data) 274 | 275 | 276 | def test_integration_account_addresses_total(): 277 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 278 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 279 | assert api.account_addresses_total(stake_address=stake_address) 280 | -------------------------------------------------------------------------------- /tests/test_cardano_addresses.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from blockfrost import BlockFrostApi, ApiError 4 | from blockfrost.utils import convert_json_to_object 5 | 6 | address = 'addr1qxk49ptelk7uda7acrczz30a7fu778sax5aapa38nhmve3eu7yzv8ay6qvmlywtgvt7exaxt783dxuzv03qal7muda5srnx35s' 7 | stake_address = 'stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7' 8 | asset = 'f4988f549728dc76b58d7677849443caf6e5385dc67e6c25f6aa901e506978656c54696c653235' 9 | 10 | 11 | def test_address(requests_mock): 12 | api = BlockFrostApi() 13 | mock_data = { 14 | "address": address, 15 | "amount": [ 16 | { 17 | "unit": "lovelace", 18 | "quantity": "42000000" 19 | }, 20 | { 21 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 22 | "quantity": "12" 23 | } 24 | ], 25 | "stake_address": stake_address, 26 | "type": "shelley", 27 | "script": False, 28 | } 29 | requests_mock.get(f"{api.url}/addresses/{address}", json=mock_data) 30 | assert api.address(address=address) == convert_json_to_object(mock_data) 31 | 32 | 33 | def test_integration_address(): 34 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 35 | api = BlockFrostApi(project_id=os.getenv( 36 | 'BLOCKFROST_PROJECT_ID_MAINNET')) 37 | assert api.address(address=address) 38 | 39 | 40 | def test_address_extended(requests_mock): 41 | api = BlockFrostApi() 42 | mock_data = { 43 | "address": address, 44 | "amount": [ 45 | { 46 | "unit": "lovelace", 47 | "quantity": "42000000", 48 | "decimals": 6, 49 | "has_nft_onchain_metadata": None 50 | }, 51 | { 52 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 53 | "quantity": "12", 54 | "decimals": None, 55 | "has_nft_onchain_metadata": True 56 | } 57 | ], 58 | "stake_address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7", 59 | "type": "shelley", 60 | "script": False 61 | } 62 | requests_mock.get( 63 | f"{api.url}/addresses/{address}/extended", json=mock_data) 64 | assert api.address_extended( 65 | address=address) == convert_json_to_object(mock_data) 66 | 67 | 68 | def test_integration_address_extended(): 69 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 70 | api = BlockFrostApi(project_id=os.getenv( 71 | 'BLOCKFROST_PROJECT_ID_MAINNET')) 72 | assert api.address_extended(address=address) 73 | 74 | 75 | def test_address_total(requests_mock): 76 | api = BlockFrostApi() 77 | mock_data = { 78 | "address": address, 79 | "received_sum": [ 80 | { 81 | "unit": "lovelace", 82 | "quantity": "42000000" 83 | }, 84 | { 85 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 86 | "quantity": "12" 87 | } 88 | ], 89 | "sent_sum": [ 90 | { 91 | "unit": "lovelace", 92 | "quantity": "42000000" 93 | }, 94 | { 95 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 96 | "quantity": "12" 97 | } 98 | ], 99 | "tx_count": 12 100 | } 101 | requests_mock.get(f"{api.url}/addresses/{address}/total", json=mock_data) 102 | assert api.address_total( 103 | address=address) == convert_json_to_object(mock_data) 104 | 105 | 106 | def test_integration_address_total(): 107 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 108 | api = BlockFrostApi(project_id=os.getenv( 109 | 'BLOCKFROST_PROJECT_ID_MAINNET')) 110 | assert api.address_total(address=address) 111 | 112 | 113 | def test_address_utxos(requests_mock): 114 | api = BlockFrostApi() 115 | mock_data = [ 116 | { 117 | "tx_hash": "39a7a284c2a0948189dc45dec670211cd4d72f7b66c5726c08d9b3df11e44d58", 118 | "tx_index": 1, 119 | "output_index": 0, 120 | "amount": [ 121 | { 122 | "unit": "lovelace", 123 | "quantity": "42000000" 124 | } 125 | ], 126 | "block": "7eb8e27d18686c7db9a18f8bbcfe34e3fed6e047afaa2d969904d15e934847e6", 127 | "data_hash": "9e478573ab81ea7a8e31891ce0648b81229f408d596a3483e6f4f9b92d3cf710" 128 | }, 129 | { 130 | "tx_hash": "4c4e67bafa15e742c13c592b65c8f74c769cd7d9af04c848099672d1ba391b49", 131 | "tx_index": 1, 132 | "output_index": 0, 133 | "amount": [ 134 | { 135 | "unit": "lovelace", 136 | "quantity": "729235000" 137 | } 138 | ], 139 | "block": "953f1b80eb7c11a7ffcd67cbd4fde66e824a451aca5a4065725e5174b81685b7", 140 | "data_hash": None 141 | }, 142 | { 143 | "tx_hash": "768c63e27a1c816a83dc7b07e78af673b2400de8849ea7e7b734ae1333d100d2", 144 | "tx_index": 1, 145 | "output_index": 1, 146 | "amount": [ 147 | { 148 | "unit": "lovelace", 149 | "quantity": "42000000" 150 | }, 151 | { 152 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 153 | "quantity": "12" 154 | } 155 | ], 156 | "block": "5c571f83fe6c784d3fbc223792627ccf0eea96773100f9aedecf8b1eda4544d7", 157 | "data_hash": None 158 | } 159 | ] 160 | requests_mock.get(f"{api.url}/addresses/{address}/utxos", json=mock_data) 161 | assert api.address_utxos( 162 | address=address) == convert_json_to_object(mock_data) 163 | 164 | 165 | def test_integration_address_utxos(): 166 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 167 | api = BlockFrostApi(project_id=os.getenv( 168 | 'BLOCKFROST_PROJECT_ID_MAINNET')) 169 | assert api.address_utxos(address=address) == [] 170 | 171 | 172 | def test_address_utxos_asset(requests_mock): 173 | api = BlockFrostApi() 174 | mock_data = [ 175 | { 176 | "tx_hash": "39a7a284c2a0948189dc45dec670211cd4d72f7b66c5726c08d9b3df11e44d58", 177 | "output_index": 0, 178 | "amount": [ 179 | { 180 | "unit": "lovelace", 181 | "quantity": "42000000" 182 | } 183 | ], 184 | "block": "7eb8e27d18686c7db9a18f8bbcfe34e3fed6e047afaa2d969904d15e934847e6", 185 | "data_hash": "9e478573ab81ea7a8e31891ce0648b81229f408d596a3483e6f4f9b92d3cf710" 186 | }, 187 | { 188 | "tx_hash": "4c4e67bafa15e742c13c592b65c8f74c769cd7d9af04c848099672d1ba391b49", 189 | "output_index": 0, 190 | "amount": [ 191 | { 192 | "unit": "lovelace", 193 | "quantity": "729235000" 194 | } 195 | ], 196 | "block": "953f1b80eb7c11a7ffcd67cbd4fde66e824a451aca5a4065725e5174b81685b7", 197 | "data_hash": None 198 | }, 199 | { 200 | "tx_hash": "768c63e27a1c816a83dc7b07e78af673b2400de8849ea7e7b734ae1333d100d2", 201 | "output_index": 1, 202 | "amount": [ 203 | { 204 | "unit": "lovelace", 205 | "quantity": "42000000" 206 | }, 207 | { 208 | "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 209 | "quantity": "12" 210 | } 211 | ], 212 | "block": "5c571f83fe6c784d3fbc223792627ccf0eea96773100f9aedecf8b1eda4544d7", 213 | "data_hash": None 214 | } 215 | ] 216 | requests_mock.get( 217 | f"{api.url}/addresses/{address}/utxos/{asset}", json=mock_data) 218 | assert api.address_utxos_asset( 219 | address=address, asset=asset) == convert_json_to_object(mock_data) 220 | 221 | 222 | def test_integration_address_utxos_asset(): 223 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 224 | api = BlockFrostApi(project_id=os.getenv( 225 | 'BLOCKFROST_PROJECT_ID_MAINNET')) 226 | assert api.address_utxos_asset(address=address, asset=asset) == [] 227 | 228 | 229 | def test_address_transactions(requests_mock): 230 | api = BlockFrostApi() 231 | mock_data = [ 232 | { 233 | "tx_hash": "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b", 234 | "tx_index": 6, 235 | "block_height": 69, 236 | "block_time": 1635505891 237 | }, 238 | { 239 | "tx_hash": "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f", 240 | "tx_index": 9, 241 | "block_height": 4547, 242 | "block_time": 1635505987 243 | }, 244 | { 245 | "tx_hash": "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b", 246 | "tx_index": 0, 247 | "block_height": 564654, 248 | "block_time": 1834505492 249 | } 250 | ] 251 | requests_mock.get( 252 | f"{api.url}/addresses/{address}/transactions", json=mock_data) 253 | assert api.address_transactions( 254 | address=address) == convert_json_to_object(mock_data) 255 | 256 | 257 | def test_integration_address_transactions(): 258 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 259 | api = BlockFrostApi(project_id=os.getenv( 260 | 'BLOCKFROST_PROJECT_ID_MAINNET')) 261 | assert api.address_transactions(address=address) 262 | -------------------------------------------------------------------------------- /tests/test_cardano_assets.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | asset = '00000002df633853f6a47465c9496721d2d5b1291b8398016c0e87ae6e7574636f696e' 6 | policy_id = '00000002df633853f6a47465c9496721d2d5b1291b8398016c0e87ae' 7 | 8 | 9 | def test_assets(requests_mock): 10 | api = BlockFrostApi() 11 | mock_data = [ 12 | { 13 | "asset": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 14 | "quantity": "1" 15 | }, 16 | { 17 | "asset": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e75d", 18 | "quantity": "100000" 19 | }, 20 | { 21 | "asset": "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad", 22 | "quantity": "18605647" 23 | } 24 | ] 25 | requests_mock.get(f"{api.url}/assets", json=mock_data) 26 | assert api.assets() == convert_json_to_object(mock_data) 27 | 28 | 29 | def test_integration_assets(): 30 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 31 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 32 | assert api.assets() 33 | 34 | 35 | def test_asset(requests_mock): 36 | api = BlockFrostApi() 37 | mock_data = { 38 | "asset": asset, 39 | "policy_id": policy_id, 40 | "asset_name": "6e7574636f696e", 41 | "fingerprint": "asset1pkpwyknlvul7az0xx8czhl60pyel45rpje4z8w", 42 | "quantity": "12000", 43 | "initial_mint_tx_hash": "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad", 44 | "mint_or_burn_count": 1, 45 | "onchain_metadata": { 46 | "name": "My NFT token", 47 | "image": "ipfs://ipfs/QmfKyJ4tuvHowwKQCbCHj4L5T3fSj8cjs7Aau8V7BWv226" 48 | }, 49 | "metadata": { 50 | "name": "nutcoin", 51 | "description": "The Nut Coin", 52 | "ticker": "nutc", 53 | "url": "https://www.stakenuts.com/", 54 | "logo": "iVBORw0KGgoAAAANSUhEUgAAADAAAAAoCAYAAAC4h3lxAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH5QITCDUPjqwFHwAAB9xJREFUWMPVWXtsU9cZ/8499/r6dZ3E9rUdO7ZDEgglFWO8KaOsJW0pCLRKrN1AqqYVkqoqrYo0ja7bpElru1WairStFKY9WzaE1E1tx+jokKqwtqFNyhKahEJJyJNgJ37E9r1+3HvO/sFR4vhx7SBtfH/F3/l93/f7ne/4PBxEKYU72dj/ZfH772v1TU+HtqbTaX8wOO01GPQpRVH7JEm+vGHDuq6z7/8jUSoHKtaBKkEUFUXdajDy1hUrmrs6zn/wWS7m7pZVjMUirKGUTnzc+e9xLcTrPPVfZzDz06Sc2lyQGEIyAPzT7Xa+dvE/3e+XLaCxoflHsVj8MAAYs74aa/WHoenwvpkZKeFy2Z5NJlOPUkqXZccFwSSrKjlyffjLH+TL6XTUGTGL/6hklD3ldIrj2M5MRmkLBMcvaRLQ1Nj88sxM/HCBfMP+eu/OYGDqe6l0WmpoqJ/88upgrU7HrQNA/cFg6MlkKiLlBtVUO40cx54BgHvLIT/HJLvdeqh/4NKxogKWN7fsCoUi7xTLxLJ4vLq6ak//wKVOrdXtttrTDMPsqJA8AAAwDErdu3VL3alTf5ma9eWCpoKhn5dKpCiqJxicPucQPVu0FHaInn35yHMcKwPAa4SQ3QCwFgDWUko3qSr5vqqSgTypuEg4Mo/zvA74/Y0rZSnZU8akSHV17k2fXfy0txjI5224kEym1s/1EUI7LBbztweHrkzkizn49LP6U6feepFSeggAQK/n04SQZ8bGrxdeQjZrbRvGzLH5hcibRqOhPplMfS1fIY5jz4xPDBdcGggho2h3z9sOLRazdG3wqp9SMgUlzGZ17SSEPsRx7J8CwfGu3PF57WhqqjfN/VxVJUxKUrIdITAXKpDJKFscosdfaFy0u+/K9aXTmXe0kAcAmA5Nng5Hbj6Tj/wCAYFAcN7uEY3GXGazMSHLqVVFapgBoMPna9yqhRAAgCTJMa3YUjZPgNFkSlWYx5eUkx+0tKx83V3rF+cVYJjruWCe133DIXqMmrNrFSDabRcWkywYmG5XFOW6aHcfb9324CoAgMmbo9MIoXkneCajiAihV/c/8eSiBSw4BxyiZxQA6m7H7FBKT2CMn2MY5jFFUX6ZO+5w2j8aHZ7YH40FByrJD5DnHGAY5uTtIA8AgBDaR4F2Yxb3WizCgmtA4ObUPSazodduqz3Suu0hf0U1cjvgdNSJ1dWWveFwdDUAtAiC2Uopdcdi8c9Zlh3GmDGl05mtAKAvo47EcdwThJCjqqpWFxALlNITomg73tff21GRAJez7iVK4WGGYfoJIQduBsbm7UrLm1ueCoUiv65kpiilw1ZbzcFoZOYoIcRTAn6eYZgXJm+Oni+Vd3YJbdyweSch9HlK6SpVVfcyDDq7Yf3m2XPBIXraKyV/a4b9UkLawbLsZgB4rwR8CyGkw13r+5fX27BckwBAEJ47oKpk8+DgUIdod7fV1vqOAMDrlZLPmqKoB+rrvXIgOP6w0WjYy3Ls5RL4bUk52bVm9fqnCk7M3CXU2ND8+MxM7BcIIftiyRYyntcdHh0bmr0wfmXl6p2SJB2KRmP3l4j7zejYUFtRAQAAgslm1Bv4nyGEDpYiIwjmjw0G/RjP866JiclNqqqWfKLq9fyZkdHBBXcnl9O71GDgD8bj0ncRQqZ8sRgzL9yYHH2pqICsOUTPLgA4CXNeZFmzWIS/YhYfjUZmvqPjuceSckrz25pS2h2cmlhbaBwhzr6kfsnL8Xhif55YYFl23Y3Jkdl7EVMoUSA4/q6qqNsBIPd11e52u45FwtG3CSH7yiEPAGC1Vt9dXGBmanDoygFLlbAjtzZCCMyC6VeaOpA1l9N7l1kwtauKaozHE28YTQaQpeR7+TqjxXheR0fHhhgt2CX1S3clEtKC16HL5djYe+niBU0CcmYA2W21/Qih5ZqDcoxlMZ24MaJJAABA87IVJ8Lh6N65Pr1B/+LIyLUfAhRZQvnM6ah7ZDHkAQB0vK6/HHxNTc2ruT5Zkldn/y5LACFk+2LIAwAwCGl6yGSt88KHXbmrBCHkqEgAz+vWLFZALJb4qNwYhFDhCSknkSwnQ4sVgDFeWg7+gQe2r1tAmkGTFQlACHWVg89nhJA9ot3dphV/eeCLp/Pw6K5IQP0S39uLFXCLwDG7zf1cKZxD9LSlUunHc/12u/2t2Vzl/rzu8zb8PZlM7bwdQgDgPK/nX2nddt+53//ht3LW2dS0fF0iLj2vquojuQFmwXRucPBKa8UCmpe1iOFwpAsAfLdJBFBKwVIlXJ2JxqKCxbwyHkvoCkAlv9/71U+7Oq+UJWDZ0hViJBL1cRynbNq0sSeeiPl6ei4NqIqq6TSmlB7X6bjuTEY5pgWfzwxGPZhMpt39/b3vzvWXFGCzulZjjM/DrauDwcAr8bjcgzGjZUuVBMH8k2uDX7wCAFDr8n2LEPI7SqmhTP6SzVbz6MDlz0/nDpT8EmOM22HOvUeWU2wp8iyLgRL6hk7Hrc2SBwC4MTlykmXZRozxn00mbVcphNA5jJmV+chr6oDd5l6jN/A/TqfSuwEAGITGMIsvGo3GTwTB3Dc2NjGSxdZYq4VIOOoNBANnKE0XPXE3brjHOTQ08k2MmVZOxzVJCbkFIQSCYEphzPaFQuGzTpfjb319PZ8UFXin/5OvrHPg/9HueAH/BSUqOuNZm4fyAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTE5VDA4OjUyOjI1KzAwOjAwCmFGlgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0xOVQwODo1MjoyMyswMDowMBjsyxAAAAAASUVORK5CYII=", 55 | "decimals": 6 56 | } 57 | } 58 | requests_mock.get(f"{api.url}/assets/{asset}", json=mock_data) 59 | assert api.asset(asset=asset) == convert_json_to_object(mock_data) 60 | 61 | 62 | def test_integration_asset(): 63 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 64 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 65 | assert api.asset(asset=asset) 66 | 67 | 68 | def test_asset_history(requests_mock): 69 | api = BlockFrostApi() 70 | mock_data = [ 71 | { 72 | "tx_hash": "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531", 73 | "amount": "10", 74 | "action": "minted" 75 | }, 76 | { 77 | "tx_hash": "9c190bc1ac88b2ab0c05a82d7de8b71b67a9316377e865748a89d4426c0d3005", 78 | "amount": "5", 79 | "action": "burned" 80 | }, 81 | { 82 | "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0", 83 | "amount": "5", 84 | "action": "burned" 85 | } 86 | ] 87 | requests_mock.get(f"{api.url}/assets/{asset}/history", json=mock_data) 88 | assert api.asset_history(asset=asset) == convert_json_to_object(mock_data) 89 | 90 | 91 | def test_integration_asset_history(): 92 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 93 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 94 | assert api.asset_history(asset=asset) 95 | 96 | 97 | def test_asset_transactions(requests_mock): 98 | api = BlockFrostApi() 99 | mock_data = [ 100 | { 101 | "tx_hash": "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b", 102 | "tx_index": 6, 103 | "block_height": 69, 104 | "block_time": 1635505891 105 | }, 106 | { 107 | "tx_hash": "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f", 108 | "tx_index": 9, 109 | "block_height": 4547, 110 | "block_time": 1635505987 111 | }, 112 | { 113 | "tx_hash": "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b", 114 | "tx_index": 0, 115 | "block_height": 564654, 116 | "block_time": 1834505492 117 | } 118 | ] 119 | requests_mock.get(f"{api.url}/assets/{asset}/transactions", json=mock_data) 120 | assert api.asset_transactions(asset=asset) == convert_json_to_object(mock_data) 121 | 122 | 123 | def test_integration_asset_transactions(): 124 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 125 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 126 | assert api.asset_transactions(asset=asset) 127 | 128 | 129 | def test_asset_addresses(requests_mock): 130 | api = BlockFrostApi() 131 | mock_data = [ 132 | { 133 | "address": "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz", 134 | "quantity": "1" 135 | }, 136 | { 137 | "address": "addr1qyhr4exrgavdcn3qhfcc9f939fzsch2re5ry9cwvcdyh4x4re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qdpvhza", 138 | "quantity": "100000" 139 | }, 140 | { 141 | "address": "addr1q8zup8m9ue3p98kxlxl9q8rnyan8hw3ul282tsl9s326dfj088lvedv4zckcj24arcpasr0gua4c5gq4zw2rpcpjk2lq8cmd9l", 142 | "quantity": "18605647" 143 | } 144 | ] 145 | requests_mock.get(f"{api.url}/assets/{asset}/addresses", json=mock_data) 146 | assert api.asset_addresses(asset=asset) == convert_json_to_object(mock_data) 147 | 148 | 149 | def test_integration_asset_addresses(): 150 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 151 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 152 | assert api.asset_addresses(asset=asset) 153 | 154 | 155 | def test_assets_policy(requests_mock): 156 | api = BlockFrostApi() 157 | mock_data = [ 158 | { 159 | "asset": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", 160 | "quantity": "1" 161 | }, 162 | { 163 | "asset": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a766e", 164 | "quantity": "100000" 165 | }, 166 | { 167 | "asset": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb574636f696e", 168 | "quantity": "18605647" 169 | } 170 | ] 171 | requests_mock.get(f"{api.url}/assets/policy/{policy_id}", json=mock_data) 172 | assert api.assets_policy(policy_id=policy_id) == convert_json_to_object(mock_data) 173 | 174 | 175 | def test_integration_assets_policy(): 176 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 177 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 178 | assert api.assets_policy(policy_id=policy_id) 179 | -------------------------------------------------------------------------------- /tests/test_cardano_blocks.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | # hash = '4ea1ba291e8eef538635a53e59fddba7810d1679631cc3aed7c8e6c4091a516a' 6 | hash = '796b28e192f1c9040e3749feb1bd2b35ce9a262976c7db95b43a3d3c417d37d4' 7 | slot_number = 46138897 8 | epoch_number = 304 9 | 10 | 11 | def test_block_latest(requests_mock): 12 | api = BlockFrostApi() 13 | mock_data = { 14 | "time": 1641338934, 15 | "height": 15243593, 16 | "hash": hash, 17 | "slot": slot_number, 18 | "epoch": epoch_number, 19 | "epoch_slot": 12, 20 | "slot_leader": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy", 21 | "size": 3, 22 | "tx_count": 1, 23 | "output": "128314491794", 24 | "fees": "592661", 25 | "block_vrf": "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty", 26 | "previous_block": "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05", 27 | "next_block": "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa", 28 | "confirmations": 4698 29 | } 30 | requests_mock.get(f"{api.url}/blocks/latest", json=mock_data) 31 | assert api.block_latest() == convert_json_to_object(mock_data) 32 | 33 | 34 | def test_integration_block_latest(): 35 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 36 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 37 | assert api.block_latest() 38 | 39 | 40 | def test_block_latest_transactions(requests_mock): 41 | api = BlockFrostApi() 42 | mock_data = [ 43 | "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b", 44 | "4eef6bb7755d8afbeac526b799f3e32a624691d166657e9d862aaeb66682c036", 45 | "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f", 46 | "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b" 47 | ] 48 | requests_mock.get(f"{api.url}/blocks/latest/txs", json=mock_data) 49 | assert api.block_latest_transactions() == convert_json_to_object(mock_data) 50 | 51 | 52 | def test_integration_block_latest_transactions(): 53 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 54 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 55 | assert (api.block_latest_transactions() or 56 | api.block_latest_transactions() == []) 57 | 58 | 59 | def test_block(requests_mock): 60 | api = BlockFrostApi() 61 | mock_data = { 62 | "time": 1641338934, 63 | "height": 15243593, 64 | "hash": hash, 65 | "slot": slot_number, 66 | "epoch": epoch_number, 67 | "epoch_slot": 12, 68 | "slot_leader": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy", 69 | "size": 3, 70 | "tx_count": 1, 71 | "output": "128314491794", 72 | "fees": "592661", 73 | "block_vrf": "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty", 74 | "previous_block": "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05", 75 | "next_block": "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa", 76 | "confirmations": 4698 77 | } 78 | requests_mock.get(f"{api.url}/blocks/{hash}", json=mock_data) 79 | assert api.block(hash_or_number=hash) == convert_json_to_object(mock_data) 80 | 81 | 82 | def test_integration_block(): 83 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 84 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 85 | assert api.block(hash_or_number=hash) 86 | 87 | 88 | def test_block_slot(requests_mock): 89 | api = BlockFrostApi() 90 | mock_data = { 91 | "time": 1641338934, 92 | "height": 15243593, 93 | "hash": hash, 94 | "slot": slot_number, 95 | "epoch": epoch_number, 96 | "epoch_slot": 12, 97 | "slot_leader": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy", 98 | "size": 3, 99 | "tx_count": 1, 100 | "output": "128314491794", 101 | "fees": "592661", 102 | "block_vrf": "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty", 103 | "previous_block": "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05", 104 | "next_block": "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa", 105 | "confirmations": 4698 106 | } 107 | requests_mock.get(f"{api.url}/blocks/slot/{slot_number}", json=mock_data) 108 | assert api.block_slot(slot_number=slot_number) == convert_json_to_object(mock_data) 109 | 110 | 111 | def test_integration_block_slot(): 112 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 113 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 114 | assert api.block_slot(slot_number=slot_number) 115 | 116 | 117 | def test_block_epoch_slot(requests_mock): 118 | api = BlockFrostApi() 119 | mock_data = { 120 | "time": 1641338934, 121 | "height": 15243593, 122 | "hash": hash, 123 | "slot": slot_number, 124 | "epoch": epoch_number, 125 | "epoch_slot": 12, 126 | "slot_leader": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy", 127 | "size": 3, 128 | "tx_count": 1, 129 | "output": "128314491794", 130 | "fees": "592661", 131 | "block_vrf": "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty", 132 | "previous_block": "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05", 133 | "next_block": "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa", 134 | "confirmations": 4698 135 | } 136 | requests_mock.get(f"{api.url}/blocks/epoch/{epoch_number}/slot/{slot_number}", json=mock_data) 137 | assert api.block_epoch_slot(epoch_number=epoch_number, slot_number=slot_number) == convert_json_to_object(mock_data) 138 | 139 | 140 | def test_integration_block_epoch_slot(): 141 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 142 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 143 | assert api.block_epoch_slot(epoch_number=epoch_number, slot_number=174097) 144 | 145 | 146 | def test_blocks_next(requests_mock): 147 | api = BlockFrostApi() 148 | mock_data = [ 149 | { 150 | "time": 1641338934, 151 | "height": 15243593, 152 | "hash": hash, 153 | "slot": slot_number, 154 | "epoch": epoch_number, 155 | "epoch_slot": 12, 156 | "slot_leader": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy", 157 | "size": 3, 158 | "tx_count": 1, 159 | "output": "128314491794", 160 | "fees": "592661", 161 | "block_vrf": "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty", 162 | "previous_block": "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05", 163 | "next_block": "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa", 164 | "confirmations": 4698 165 | } 166 | ] 167 | requests_mock.get(f"{api.url}/blocks/{hash}/next", json=mock_data) 168 | assert api.blocks_next(hash_or_number=hash) == convert_json_to_object(mock_data) 169 | 170 | 171 | def test_integration_blocks_next(): 172 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 173 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 174 | assert api.blocks_next(hash_or_number=hash) 175 | 176 | 177 | def test_blocks_previous(requests_mock): 178 | api = BlockFrostApi() 179 | mock_data = [ 180 | { 181 | "time": 1641338934, 182 | "height": 15243593, 183 | "hash": hash, 184 | "slot": slot_number, 185 | "epoch": epoch_number, 186 | "epoch_slot": 12, 187 | "slot_leader": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy", 188 | "size": 3, 189 | "tx_count": 1, 190 | "output": "128314491794", 191 | "fees": "592661", 192 | "block_vrf": "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty", 193 | "previous_block": "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05", 194 | "next_block": "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa", 195 | "confirmations": 4698 196 | } 197 | ] 198 | requests_mock.get(f"{api.url}/blocks/{hash}/previous", json=mock_data) 199 | assert api.blocks_previous(hash_or_number=hash) == convert_json_to_object(mock_data) 200 | 201 | 202 | def test_integration_blocks_previous(): 203 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 204 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 205 | assert api.blocks_previous(hash_or_number=hash) 206 | 207 | 208 | def test_block_transactions(requests_mock): 209 | api = BlockFrostApi() 210 | mock_data = [ 211 | "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b", 212 | "4eef6bb7755d8afbeac526b799f3e32a624691d166657e9d862aaeb66682c036", 213 | "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f", 214 | "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b" 215 | ] 216 | requests_mock.get(f"{api.url}/blocks/{hash}/txs", json=mock_data) 217 | assert api.block_transactions(hash_or_number=hash) == convert_json_to_object(mock_data) 218 | 219 | 220 | def test_integration_block_transactions(): 221 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 222 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 223 | assert api.block_transactions(hash_or_number=hash) 224 | 225 | 226 | def test_blocks_blocks_addresses(requests_mock): 227 | api = BlockFrostApi() 228 | mock_data = [ 229 | { 230 | "address": "addr1q9ld26v2lv8wvrxxmvg90pn8n8n5k6tdst06q2s856rwmvnueldzuuqmnsye359fqrk8hwvenjnqultn7djtrlft7jnq7dy7wv", 231 | "transactions": [ 232 | { 233 | "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dce628516157f0" 234 | } 235 | ] 236 | }, 237 | { 238 | "address": "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz", 239 | "transactions": [ 240 | { 241 | "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dce628516157d0" 242 | } 243 | ] 244 | } 245 | ] 246 | requests_mock.get(f"{api.url}/blocks/{hash}/addresses", json=mock_data) 247 | assert api.blocks_addresses(hash_or_number=hash) == convert_json_to_object(mock_data) 248 | 249 | 250 | def test_integration_blocks_addresses(): 251 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 252 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 253 | assert api.blocks_addresses(hash_or_number=hash) 254 | -------------------------------------------------------------------------------- /tests/test_cardano_epochs.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | epoch = 225 6 | pool_id = 'pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy' 7 | 8 | 9 | def test_epoch_latest(requests_mock): 10 | api = BlockFrostApi() 11 | mock_data = { 12 | "epoch": epoch, 13 | "start_time": 1603403091, 14 | "end_time": 1603835086, 15 | "first_block_time": 1603403092, 16 | "last_block_time": 1603835084, 17 | "block_count": 21298, 18 | "tx_count": 17856, 19 | "output": "7849943934049314", 20 | "fees": "4203312194", 21 | "active_stake": "784953934049314" 22 | } 23 | requests_mock.get(f"{api.url}/epochs/latest", json=mock_data) 24 | assert api.epoch_latest() == convert_json_to_object(mock_data) 25 | 26 | 27 | def test_integration_epoch_latest(): 28 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 29 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 30 | assert api.epoch_latest() 31 | 32 | 33 | def test_epoch_latest_parameters(requests_mock): 34 | api = BlockFrostApi() 35 | mock_data = { 36 | "epoch": epoch, 37 | "min_fee_a": 44, 38 | "min_fee_b": 155381, 39 | "max_block_size": 65536, 40 | "max_tx_size": 16384, 41 | "max_block_header_size": 1100, 42 | "key_deposit": "2000000", 43 | "pool_deposit": "500000000", 44 | "e_max": 18, 45 | "n_opt": 150, 46 | "a0": 0.3, 47 | "rho": 0.003, 48 | "tau": 0.2, 49 | "decentralisation_param": 0.5, 50 | "extra_entropy": None, 51 | "protocol_major_ver": 2, 52 | "protocol_minor_ver": 0, 53 | "min_utxo": "1000000", 54 | "min_pool_cost": "340000000", 55 | "nonce": "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81", 56 | "price_mem": 0.0577, 57 | "price_step": 0.0000721, 58 | "max_tx_ex_mem": "10000000", 59 | "max_tx_ex_steps": "10000000000", 60 | "max_block_ex_mem": "50000000", 61 | "max_block_ex_steps": "40000000000", 62 | "max_val_size": "5000", 63 | "collateral_percent": 150, 64 | "max_collateral_inputs": 3, 65 | "coins_per_utxo_word": "34482" 66 | } 67 | requests_mock.get(f"{api.url}/epochs/latest/parameters", json=mock_data) 68 | assert api.epoch_latest_parameters() == convert_json_to_object(mock_data) 69 | 70 | 71 | def test_integration_epoch_latest_parameters(): 72 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 73 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 74 | assert api.epoch_latest_parameters() 75 | 76 | 77 | def test_epoch(requests_mock): 78 | api = BlockFrostApi() 79 | mock_data = { 80 | "epoch": epoch, 81 | "start_time": 1603403091, 82 | "end_time": 1603835086, 83 | "first_block_time": 1603403092, 84 | "last_block_time": 1603835084, 85 | "block_count": 21298, 86 | "tx_count": 17856, 87 | "output": "7849943934049314", 88 | "fees": "4203312194", 89 | "active_stake": "784953934049314" 90 | } 91 | requests_mock.get(f"{api.url}/epochs/{epoch}", json=mock_data) 92 | assert api.epoch(number=epoch) == convert_json_to_object(mock_data) 93 | 94 | 95 | def test_integration_epoch(): 96 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 97 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 98 | assert api.epoch(number=epoch) 99 | 100 | 101 | def test_epochs_next(requests_mock): 102 | api = BlockFrostApi() 103 | mock_data = [ 104 | { 105 | "epoch": 225, 106 | "start_time": 1603403091, 107 | "end_time": 1603835086, 108 | "first_block_time": 1603403092, 109 | "last_block_time": 1603835084, 110 | "block_count": 21298, 111 | "tx_count": 17856, 112 | "output": "7849943934049314", 113 | "fees": "4203312194", 114 | "active_stake": "784953934049314" 115 | } 116 | ] 117 | requests_mock.get(f"{api.url}/epochs/{epoch}/next", json=mock_data) 118 | assert api.epochs_next(number=epoch) == convert_json_to_object(mock_data) 119 | 120 | 121 | def test_integration_epochs_next(): 122 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 123 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 124 | assert api.epochs_next(number=epoch) 125 | 126 | 127 | def test_epochs_previous(requests_mock): 128 | api = BlockFrostApi() 129 | mock_data = [ 130 | { 131 | "epoch": 225, 132 | "start_time": 1603403091, 133 | "end_time": 1603835086, 134 | "first_block_time": 1603403092, 135 | "last_block_time": 1603835084, 136 | "block_count": 21298, 137 | "tx_count": 17856, 138 | "output": "7849943934049314", 139 | "fees": "4203312194", 140 | "active_stake": "784953934049314" 141 | } 142 | ] 143 | requests_mock.get(f"{api.url}/epochs/{epoch}/previous", json=mock_data) 144 | assert api.epochs_previous(number=epoch) == convert_json_to_object(mock_data) 145 | 146 | 147 | def test_integration_epochs_previous(): 148 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 149 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 150 | assert api.epochs_previous(number=epoch) 151 | 152 | 153 | def test_epoch_stakes(requests_mock): 154 | api = BlockFrostApi() 155 | mock_data = [ 156 | { 157 | "stake_address": "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk", 158 | "pool_id": pool_id, 159 | "amount": "4440295078" 160 | } 161 | ] 162 | requests_mock.get(f"{api.url}/epochs/{epoch}/stakes", json=mock_data) 163 | assert api.epoch_stakes(number=epoch) == convert_json_to_object(mock_data) 164 | 165 | 166 | def test_integration_epoch_stakes(): 167 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 168 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 169 | assert api.epoch_stakes(number=epoch) 170 | 171 | 172 | def test_epoch_pool_stakes(requests_mock): 173 | api = BlockFrostApi() 174 | mock_data = [ 175 | { 176 | "stake_address": "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk", 177 | "amount": "4440295078" 178 | } 179 | ] 180 | requests_mock.get(f"{api.url}/epochs/{epoch}/stakes/{pool_id}", json=mock_data) 181 | assert api.epoch_pool_stakes(number=epoch, pool_id=pool_id) == convert_json_to_object(mock_data) 182 | 183 | 184 | def test_integration_epoch_pool_stakes(): 185 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 186 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 187 | assert api.epoch_pool_stakes(number=epoch, pool_id=pool_id) 188 | 189 | 190 | def test_epoch_blocks(requests_mock): 191 | api = BlockFrostApi() 192 | mock_data = [ 193 | "d0fa315687e99ccdc96b14cc2ea74a767405d64427b648c470731a9b69e4606e", 194 | "38bc6efb92a830a0ed22a64f979d120d26483fd3c811f6622a8c62175f530878", 195 | "f3258fcd8b975c061b4fcdcfcbb438807134d6961ec278c200151274893b6b7d" 196 | ] 197 | requests_mock.get(f"{api.url}/epochs/{epoch}/blocks", json=mock_data) 198 | assert api.epoch_blocks(number=epoch) == convert_json_to_object(mock_data) 199 | 200 | 201 | def test_integration_epoch_blocks(): 202 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 203 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 204 | assert api.epoch_blocks(number=epoch) 205 | 206 | 207 | def test_epoch_pool_blocks(requests_mock): 208 | api = BlockFrostApi() 209 | mock_data = [ 210 | "d0fa315687e99ccdc96b14cc2ea74a767405d64427b648c470731a9b69e4606e", 211 | "38bc6efb92a830a0ed22a64f979d120d26483fd3c811f6622a8c62175f530878", 212 | "f3258fcd8b975c061b4fcdcfcbb438807134d6961ec278c200151274893b6b7d" 213 | ] 214 | requests_mock.get(f"{api.url}/epochs/{epoch}/blocks/{pool_id}", json=mock_data) 215 | assert api.epoch_pool_blocks(number=epoch, pool_id=pool_id) == convert_json_to_object(mock_data) 216 | 217 | 218 | def test_integration_epoch_pool_blocks(): 219 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 220 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 221 | assert api.epoch_pool_blocks(number=epoch, pool_id=pool_id) 222 | 223 | 224 | def test_epoch_latest_parameters(requests_mock): 225 | api = BlockFrostApi() 226 | mock_data = { 227 | "epoch": epoch, 228 | "min_fee_a": 44, 229 | "min_fee_b": 155381, 230 | "max_block_size": 65536, 231 | "max_tx_size": 16384, 232 | "max_block_header_size": 1100, 233 | "key_deposit": "2000000", 234 | "pool_deposit": "500000000", 235 | "e_max": 18, 236 | "n_opt": 150, 237 | "a0": 0.3, 238 | "rho": 0.003, 239 | "tau": 0.2, 240 | "decentralisation_param": 0.5, 241 | "extra_entropy": None, 242 | "protocol_major_ver": 2, 243 | "protocol_minor_ver": 0, 244 | "min_utxo": "1000000", 245 | "min_pool_cost": "340000000", 246 | "nonce": "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81", 247 | "price_mem": 0.001, 248 | "price_step": 0.01, 249 | "max_tx_ex_mem": "11000000000", 250 | "max_tx_ex_steps": "11000000000", 251 | "max_block_ex_mem": "110000000000", 252 | "max_block_ex_steps": "110000000000", 253 | "max_val_size": "5000", 254 | "collateral_percent": 1.5, 255 | "max_collateral_inputs": 6, 256 | "coins_per_utxo_word": "34482" 257 | } 258 | requests_mock.get(f"{api.url}/epochs/{epoch}/parameters", json=mock_data) 259 | assert api.epoch_protocol_parameters(number=epoch) == convert_json_to_object(mock_data) 260 | 261 | 262 | def test_integration_epoch_latest_parameters(): 263 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 264 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 265 | assert api.epoch_protocol_parameters(number=epoch) 266 | -------------------------------------------------------------------------------- /tests/test_cardano_ledger.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | 6 | 7 | def test_genesis(requests_mock): 8 | api = BlockFrostApi() 9 | mock_data = { 10 | "active_slots_coefficient": 0.05, 11 | "update_quorum": 5, 12 | "max_lovelace_supply": "45000000000000000", 13 | "network_magic": 764824073, 14 | "epoch_length": 432000, 15 | "system_start": 1506203091, 16 | "slots_per_kes_period": 129600, 17 | "slot_length": 1, 18 | "max_kes_evolutions": 62, 19 | "security_param": 2160 20 | } 21 | requests_mock.get(f"{api.url}/genesis", json=mock_data) 22 | assert api.genesis() == convert_json_to_object(mock_data) 23 | 24 | 25 | def test_integration_genesis(): 26 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 27 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 28 | assert api.genesis() 29 | -------------------------------------------------------------------------------- /tests/test_cardano_mempool.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | 6 | def test_mempool(requests_mock): 7 | api = BlockFrostApi() 8 | mock_data = [ 9 | { 10 | "tx_hash": "6a3511c5418edb5659c925c3287bf891fb641b67cb81380de4d4b2e21bf9ca20" 11 | }, 12 | { 13 | "tx_hash": "9006fe580833f1a49126f698a4378473c413747486c4d43f9c8e0053b434f60c" 14 | }, 15 | { 16 | "tx_hash": "b4ccc2ef8a381f15e68c7ad654a5789f5e31fd7f6467060663e204849f9b8247" 17 | }, 18 | { 19 | "tx_hash": "f9e07beb51a459f8dbf02d5d68793ecabda5514bcbdfe95137a3fc1826479fb7" 20 | }, 21 | { 22 | "tx_hash": "adcb286ccb45d7c4d43827a632c32781c86122410b38d7f39b40c06ef55a792b" 23 | } 24 | ] 25 | requests_mock.get(f"{api.url}/mempool", json=mock_data) 26 | assert api.mempool() == convert_json_to_object(mock_data) 27 | 28 | 29 | def test_integration_mempool(): 30 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 31 | api = BlockFrostApi(project_id=os.getenv( 32 | 'BLOCKFROST_PROJECT_ID_MAINNET')) 33 | assert api.mempool() 34 | 35 | 36 | def test_mempool_tx(requests_mock): 37 | api = BlockFrostApi() 38 | hash = '7f9abe0b87064b6b39ec51f0f8ae976f586273c3ac123362ff550d94690d2881' 39 | mock_data = [ 40 | { 41 | "tx": { 42 | "hash": "7f9abe0b87064b6b39ec51f0f8ae976f586273c3ac123362ff550d94690d2881", 43 | "output_amount": [ 44 | { 45 | "unit": "lovelace", 46 | "quantity": "4597809591" 47 | } 48 | ], 49 | "fees": "168493", 50 | "deposit": "0", 51 | "size": 293, 52 | "invalid_before": None, 53 | "invalid_hereafter": "32304188", 54 | "utxo_count": 3, 55 | "withdrawal_count": 0, 56 | "mir_cert_count": 0, 57 | "delegation_count": 0, 58 | "stake_cert_count": 0, 59 | "pool_update_count": 0, 60 | "pool_retire_count": 0, 61 | "asset_mint_or_burn_count": 0, 62 | "redeemer_count": 0, 63 | "valid_contract": True 64 | }, 65 | "inputs": [ 66 | { 67 | "address": "addr_test1qq7ee7hjrux0zhptjpvt3rfp9rw0xfgkxwpussw8am5qwx24v0taaqmfkr2w88wfaqr6e5yz8nx42hdwulu6tw25dnuqgxnk57", 68 | "tx_hash": "9c5c73008491e4ce07b2c868624ae8af5ea53e5d071723f700554c576400397b", 69 | "output_index": 1, 70 | "collateral": False, 71 | "reference": False 72 | } 73 | ], 74 | "outputs": [ 75 | { 76 | "address": "addr_test1qru6ah9weaq77re5jkjv65fgteppd3y7m5ezar4x7nyav5csg06mvtj45v4nhstgf92qghdz3rrf9x5f0h9ac8n48zrs7tv53q", 77 | "amount": [ 78 | { 79 | "unit": "lovelace", 80 | "quantity": "50000000" 81 | } 82 | ], 83 | "output_index": 0, 84 | "data_hash": None, 85 | "inline_datum": None, 86 | "collateral": False, 87 | "reference_script_hash": None 88 | }, 89 | { 90 | "address": "addr_test1qq7ee7hjrux0zhptjpvt3rfp9rw0xfgkxwpussw8am5qwx24v0taaqmfkr2w88wfaqr6e5yz8nx42hdwulu6tw25dnuqgxnk57", 91 | "amount": [ 92 | { 93 | "unit": "lovelace", 94 | "quantity": "4547809591" 95 | } 96 | ], 97 | "output_index": 1, 98 | "data_hash": None, 99 | "inline_datum": None, 100 | "collateral": False, 101 | "reference_script_hash": None 102 | } 103 | ] 104 | } 105 | ] 106 | requests_mock.get(f"{api.url}/mempool/{hash}", json=mock_data) 107 | assert api.mempool_tx(hash) == convert_json_to_object(mock_data) 108 | 109 | 110 | # def test_integration_mempool(): 111 | # if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 112 | # api = BlockFrostApi(project_id=os.getenv( 113 | # 'BLOCKFROST_PROJECT_ID_MAINNET')) 114 | # assert api.mempool_tx() 115 | 116 | def test_mempool_address(requests_mock): 117 | address = 'addr1qyptln5t5s0mastzc9rksn6wdqp9ynt67ahw0nhzukar5keu7yzv8ay6qvmlywtgvt7exaxt783dxuzv03qal7muda5sl42hg6' 118 | api = BlockFrostApi() 119 | mock_data = [ 120 | { 121 | "tx_hash": "6a3511c5418edb5659c925c3287bf891fb641b67cb81380de4d4b2e21bf9ca20" 122 | } 123 | ] 124 | requests_mock.get( 125 | f"{api.url}/mempool/addresses/{address}", json=mock_data) 126 | assert api.mempool_address(address) == convert_json_to_object(mock_data) 127 | 128 | 129 | # def test_integration_mempool_address(): 130 | # if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 131 | # api = BlockFrostApi(project_id=os.getenv( 132 | # 'BLOCKFROST_PROJECT_ID_MAINNET')) 133 | # assert api.mempool_address() 134 | -------------------------------------------------------------------------------- /tests/test_cardano_metadata.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | 6 | label = "1990" 7 | 8 | 9 | def test_metadata_labels(requests_mock): 10 | api = BlockFrostApi() 11 | mock_data = [ 12 | { 13 | "label": "1990", 14 | "cip10": None, 15 | "count": "1" 16 | }, 17 | { 18 | "label": "1967", 19 | "cip10": "nut.link metadata oracles registry", 20 | "count": "3" 21 | }, 22 | { 23 | "label": "1968", 24 | "cip10": "nut.link metadata oracles data points", 25 | "count": "16321" 26 | } 27 | ] 28 | requests_mock.get(f"{api.url}/metadata/txs/labels", json=mock_data) 29 | assert api.metadata_labels() == convert_json_to_object(mock_data) 30 | 31 | 32 | def test_integration_metadata_labels(): 33 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 34 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 35 | assert api.metadata_labels() 36 | 37 | 38 | def test_metadata_label_json(requests_mock): 39 | api = BlockFrostApi() 40 | mock_data = [ 41 | { 42 | "tx_hash": "257d75c8ddb0434e9b63e29ebb6241add2b835a307aa33aedba2effe09ed4ec8", 43 | "json_metadata": { 44 | "ADAUSD": [ 45 | { 46 | "value": "0.10409800535729975", 47 | "source": "ergoOracles" 48 | } 49 | ] 50 | } 51 | }, 52 | { 53 | "tx_hash": "e865f2cc01ca7381cf98dcdc4de07a5e8674b8ea16e6a18e3ed60c186fde2b9c", 54 | "json_metadata": { 55 | "ADAUSD": [ 56 | { 57 | "value": "0.15409850555139935", 58 | "source": "ergoOracles" 59 | } 60 | ] 61 | } 62 | }, 63 | { 64 | "tx_hash": "4237501da3cfdd53ade91e8911e764bd0699d88fd43b12f44a1f459b89bc91be", 65 | "json_metadata": None 66 | } 67 | ] 68 | requests_mock.get(f"{api.url}/metadata/txs/labels/{label}", json=mock_data) 69 | assert api.metadata_label_json(label=label) == convert_json_to_object(mock_data) 70 | 71 | 72 | def test_integration_metadata_label_json(): 73 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 74 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 75 | assert api.metadata_label_json(label=label) 76 | 77 | 78 | def test_metadata_label_cbor(requests_mock): 79 | api = BlockFrostApi() 80 | mock_data = [ 81 | { 82 | "tx_hash": "257d75c8ddb0434e9b63e29ebb6241add2b835a307aa33aedba2effe09ed4ec8", 83 | "cbor_metadata": None, 84 | "metadata": None 85 | }, 86 | { 87 | "tx_hash": "e865f2cc01ca7381cf98dcdc4de07a5e8674b8ea16e6a18e3ed60c186fde2b9c", 88 | "cbor_metadata": None, 89 | "metadata": None 90 | }, 91 | { 92 | "tx_hash": "4237501da3cfdd53ade91e8911e764bd0699d88fd43b12f44a1f459b89bc91be", 93 | "cbor_metadata": "\\xa100a16b436f6d62696e6174696f6e8601010101010c", 94 | "metadata": "a100a16b436f6d62696e6174696f6e8601010101010c" 95 | } 96 | ] 97 | requests_mock.get(f"{api.url}/metadata/txs/labels/{label}/cbor", json=mock_data) 98 | assert api.metadata_label_cbor(label=label) == convert_json_to_object(mock_data) 99 | 100 | 101 | def test_integration_metadata_label_cbor(): 102 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 103 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 104 | assert api.metadata_label_cbor(label=label) 105 | -------------------------------------------------------------------------------- /tests/test_cardano_network.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | 6 | def test_network(requests_mock): 7 | api = BlockFrostApi() 8 | mock_data = { 9 | "supply": { 10 | "max": "45000000000000000", 11 | "total": "32890715183299160", 12 | "circulating": "32412601976210393", 13 | "locked": "125006953355", 14 | "treasury": "98635632000000", 15 | "reserves": "46635632000000" 16 | }, 17 | "stake": { 18 | "live": "23204950463991654", 19 | "active": "22210233523456321" 20 | } 21 | } 22 | requests_mock.get(f"{api.url}/network", json=mock_data) 23 | assert api.network() == convert_json_to_object(mock_data) 24 | 25 | 26 | def test_integration_network(): 27 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 28 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 29 | assert api.network() 30 | -------------------------------------------------------------------------------- /tests/test_cardano_pools.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | pool_id = 'pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy' 6 | 7 | 8 | def test_pools(requests_mock): 9 | api = BlockFrostApi() 10 | mock_data = [ 11 | "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy", 12 | "pool1hn7hlwrschqykupwwrtdfkvt2u4uaxvsgxyh6z63703p2knj288", 13 | "pool1ztjyjfsh432eqetadf82uwuxklh28xc85zcphpwq6mmezavzad2" 14 | ] 15 | requests_mock.get(f"{api.url}/pools", json=mock_data) 16 | assert api.pools() == convert_json_to_object(mock_data) 17 | 18 | 19 | def test_integration_pools(): 20 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 21 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 22 | assert api.pools() 23 | 24 | 25 | def test_pools_extended(requests_mock): 26 | api = BlockFrostApi() 27 | mock_data = [ 28 | { 29 | "pool_id": "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q", 30 | "active_stake": "1541200000", 31 | "live_stake": "1541400000" 32 | }, 33 | { 34 | "pool_id": "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx", 35 | "active_stake": "22200000", 36 | "live_stake": "48955550" 37 | }, 38 | { 39 | "pool_id": "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w", 40 | "active_stake": "9989541215", 41 | "live_stake": "168445464878" 42 | } 43 | ] 44 | requests_mock.get(f"{api.url}/pools/extended", json=mock_data) 45 | assert api.pools_extended() == convert_json_to_object(mock_data) 46 | 47 | 48 | def test_integration_pools_extended(): 49 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 50 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 51 | assert api.pools_extended() 52 | 53 | 54 | def test_pools_retired(requests_mock): 55 | api = BlockFrostApi() 56 | mock_data = [ 57 | { 58 | "pool_id": "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q", 59 | "epoch": 225 60 | }, 61 | { 62 | "pool_id": "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx", 63 | "epoch": 215 64 | }, 65 | { 66 | "pool_id": "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w", 67 | "epoch": 231 68 | } 69 | ] 70 | requests_mock.get(f"{api.url}/pools/retired", json=mock_data) 71 | assert api.pools_retired() == convert_json_to_object(mock_data) 72 | 73 | 74 | def test_integration_pools_retired(): 75 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 76 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 77 | assert api.pools_retired() 78 | 79 | 80 | def test_pools_retiring(requests_mock): 81 | api = BlockFrostApi() 82 | mock_data = [ 83 | { 84 | "pool_id": "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q", 85 | "epoch": 225 86 | }, 87 | { 88 | "pool_id": "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx", 89 | "epoch": 215 90 | }, 91 | { 92 | "pool_id": "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w", 93 | "epoch": 231 94 | } 95 | ] 96 | requests_mock.get(f"{api.url}/pools/retiring", json=mock_data) 97 | assert api.pools_retiring() == convert_json_to_object(mock_data) 98 | 99 | 100 | def test_integration_pools_retiring(): 101 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 102 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 103 | assert api.pools_retiring() 104 | 105 | 106 | def test_pool(requests_mock): 107 | api = BlockFrostApi() 108 | mock_data = { 109 | "pool_id": pool_id, 110 | "hex": "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735", 111 | "vrf_key": "0b5245f9934ec2151116fb8ec00f35fd00e0aa3b075c4ed12cce440f999d8233", 112 | "blocks_minted": 69, 113 | "blocks_epoch": 4, 114 | "live_stake": "6900000000", 115 | "live_size": 0.42, 116 | "live_saturation": 0.93, 117 | "live_delegators": 127, 118 | "active_stake": "4200000000", 119 | "active_size": 0.43, 120 | "declared_pledge": "5000000000", 121 | "live_pledge": "5000000001", 122 | "margin_cost": 0.05, 123 | "fixed_cost": "340000000", 124 | "reward_account": "stake1uxkptsa4lkr55jleztw43t37vgdn88l6ghclfwuxld2eykgpgvg3f", 125 | "owners": [ 126 | "stake1u98nnlkvkk23vtvf9273uq7cph5ww6u2yq2389psuqet90sv4xv9v" 127 | ], 128 | "registration": [ 129 | "9f83e5484f543e05b52e99988272a31da373f3aab4c064c76db96643a355d9dc", 130 | "7ce3b8c433bf401a190d58c8c483d8e3564dfd29ae8633c8b1b3e6c814403e95", 131 | "3e6e1200ce92977c3fe5996bd4d7d7e192bcb7e231bc762f9f240c76766535b9" 132 | ], 133 | "retirement": [ 134 | "252f622976d39e646815db75a77289cf16df4ad2b287dd8e3a889ce14c13d1a8" 135 | ] 136 | } 137 | requests_mock.get(f"{api.url}/pools/{pool_id}", json=mock_data) 138 | assert api.pool(pool_id=pool_id) == convert_json_to_object(mock_data) 139 | 140 | 141 | def test_integration_pool(): 142 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 143 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 144 | assert api.pool(pool_id=pool_id) 145 | 146 | 147 | def test_pool_history(requests_mock): 148 | api = BlockFrostApi() 149 | mock_data = [ 150 | { 151 | "epoch": 233, 152 | "blocks": 22, 153 | "active_stake": "20485965693569", 154 | "active_size": 1.2345, 155 | "delegators_count": 115, 156 | "rewards": "206936253674159", 157 | "fees": "1290968354" 158 | } 159 | ] 160 | requests_mock.get(f"{api.url}/pools/{pool_id}/history", json=mock_data) 161 | assert api.pool_history(pool_id=pool_id) == convert_json_to_object(mock_data) 162 | 163 | 164 | def test_integration_pool_history(): 165 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 166 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 167 | assert api.pool_history(pool_id=pool_id) 168 | 169 | 170 | def test_pool_metadata(requests_mock): 171 | api = BlockFrostApi() 172 | mock_data = { 173 | "pool_id": pool_id, 174 | "hex": "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735", 175 | "url": "https://stakenuts.com/mainnet.json", 176 | "hash": "47c0c68cb57f4a5b4a87bad896fc274678e7aea98e200fa14a1cb40c0cab1d8c", 177 | "ticker": "NUTS", 178 | "name": "Stake Nuts", 179 | "description": "The best pool ever", 180 | "homepage": "https://stakentus.com/" 181 | } 182 | requests_mock.get(f"{api.url}/pools/{pool_id}/metadata", json=mock_data) 183 | assert api.pool_metadata(pool_id=pool_id) == convert_json_to_object(mock_data) 184 | 185 | 186 | def test_integration_pool_metadata(): 187 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 188 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 189 | assert api.pool_metadata(pool_id=pool_id) 190 | 191 | 192 | def test_pool_relays(requests_mock): 193 | api = BlockFrostApi() 194 | mock_data = [ 195 | { 196 | "ipv4": "4.4.4.4", 197 | "ipv6": "https://stakenuts.com/mainnet.json", 198 | "dns": "relay1.stakenuts.com", 199 | "dns_srv": "_relays._tcp.relays.stakenuts.com", 200 | "port": 3001 201 | } 202 | ] 203 | requests_mock.get(f"{api.url}/pools/{pool_id}/relays", json=mock_data) 204 | assert api.pool_relays(pool_id=pool_id) == convert_json_to_object(mock_data) 205 | 206 | 207 | def test_integration_pool_relays(): 208 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 209 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 210 | assert api.pool_relays(pool_id=pool_id) 211 | 212 | 213 | def test_pool_delegators(requests_mock): 214 | api = BlockFrostApi() 215 | mock_data = [ 216 | { 217 | "address": "stake1ux4vspfvwuus9uwyp5p3f0ky7a30jq5j80jxse0fr7pa56sgn8kha", 218 | "live_stake": "1137959159981411" 219 | }, 220 | { 221 | "address": "stake1uylayej7esmarzd4mk4aru37zh9yz0luj3g9fsvgpfaxulq564r5u", 222 | "live_stake": "16958865648" 223 | }, 224 | { 225 | "address": "stake1u8lr2pnrgf8f7vrs9lt79hc3sxm8s2w4rwvgpncks3axx6q93d4ck", 226 | "live_stake": "18605647" 227 | } 228 | ] 229 | requests_mock.get(f"{api.url}/pools/{pool_id}/delegators", json=mock_data) 230 | assert api.pool_delegators(pool_id=pool_id) == convert_json_to_object(mock_data) 231 | 232 | 233 | def test_integration_pool_delegators(): 234 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 235 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 236 | assert api.pool_delegators(pool_id=pool_id) 237 | 238 | 239 | def test_pool_blocks(requests_mock): 240 | api = BlockFrostApi() 241 | mock_data = [ 242 | "d8982ca42cfe76b747cc681d35d671050a9e41e9cfe26573eb214e94fe6ff21d", 243 | "026436c539e2ce84c7f77ffe669f4e4bbbb3b9c53512e5857dcba8bb0b4e9a8c", 244 | "bcc8487f419b8c668a18ea2120822a05df6dfe1de1f0fac3feba88cf760f303c", 245 | "86bf7b4a274e0f8ec9816171667c1b4a0cfc661dc21563f271acea9482b62df7" 246 | ] 247 | requests_mock.get(f"{api.url}/pools/{pool_id}/blocks", json=mock_data) 248 | assert api.pool_blocks(pool_id=pool_id) == convert_json_to_object(mock_data) 249 | 250 | 251 | def test_integration_pool_blocks(): 252 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 253 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 254 | assert api.pool_blocks(pool_id=pool_id) 255 | 256 | 257 | def test_pool_updates(requests_mock): 258 | api = BlockFrostApi() 259 | mock_data = [ 260 | { 261 | "tx_hash": "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad", 262 | "cert_index": 0, 263 | "action": "registered" 264 | }, 265 | { 266 | "tx_hash": "9c190bc1ac88b2ab0c05a82d7de8b71b67a9316377e865748a89d4426c0d3005", 267 | "cert_index": 0, 268 | "action": "deregistered" 269 | }, 270 | { 271 | "tx_hash": "e14a75b0eb2625de7055f1f580d70426311b78e0d36dd695a6bdc96c7b3d80e0", 272 | "cert_index": 1, 273 | "action": "registered" 274 | } 275 | ] 276 | requests_mock.get(f"{api.url}/pools/{pool_id}/updates", json=mock_data) 277 | assert api.pool_updates(pool_id=pool_id) == convert_json_to_object(mock_data) 278 | 279 | 280 | def test_integration_pool_updates(): 281 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 282 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 283 | assert api.pool_updates(pool_id=pool_id) 284 | -------------------------------------------------------------------------------- /tests/test_cardano_scripts.py: -------------------------------------------------------------------------------- 1 | import os, json 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | script_hash = "65c197d565e88a20885e535f93755682444d3c02fd44dd70883fe89e" 6 | datum_hash = "923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec" 7 | 8 | 9 | def test_scripts(requests_mock): 10 | api = BlockFrostApi() 11 | mock_data = [ 12 | { 13 | "script_hash": script_hash 14 | }, 15 | { 16 | "script_hash": "e1457a0c47dfb7a2f6b8fbb059bdceab163c05d34f195b87b9f2b30e" 17 | }, 18 | { 19 | "script_hash": "a6e63c0ff05c96943d1cc30bf53112ffff0f34b45986021ca058ec54" 20 | } 21 | ] 22 | requests_mock.get(f"{api.url}/scripts", json=mock_data) 23 | assert api.scripts() == convert_json_to_object(mock_data) 24 | 25 | 26 | def test_integration_scripts(): 27 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 28 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 29 | assert api.scripts() 30 | 31 | 32 | def test_script(requests_mock): 33 | api = BlockFrostApi() 34 | mock_data = { 35 | "script_hash": "13a3efd825703a352a8f71f4e2758d08c28c564e8dfcce9f77776ad1", 36 | "type": "plutus", 37 | "serialised_size": 3119 38 | } 39 | requests_mock.get(f"{api.url}/scripts/{script_hash}", json=mock_data) 40 | assert api.script(script_hash=script_hash) == convert_json_to_object(mock_data) 41 | 42 | 43 | def test_integration_script(): 44 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 45 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 46 | assert api.script(script_hash=script_hash) 47 | 48 | 49 | def test_script_json(requests_mock): 50 | api = BlockFrostApi() 51 | mock_data = { 52 | "json": { 53 | "type": "atLeast", 54 | "scripts": [ 55 | { 56 | "type": "sig", 57 | "keyHash": "654891a4db2ea44b5263f4079a33efa0358ba90769e3d8f86a4a0f81" 58 | }, 59 | { 60 | "type": "sig", 61 | "keyHash": "8685ad48f9bebb8fdb6447abbe140645e0bf743ff98da62e63e2147f" 62 | }, 63 | { 64 | "type": "sig", 65 | "keyHash": "cb0f3b3f91693374ff7ce1d473cf6e721c7bab52b0737f04164e5a2d" 66 | } 67 | ], 68 | "required": 2 69 | } 70 | } 71 | requests_mock.get(f"{api.url}/scripts/{script_hash}/json", json=mock_data) 72 | assert api.script_json(script_hash=script_hash) == convert_json_to_object(mock_data) 73 | 74 | 75 | def test_integration_script_json(): 76 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 77 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 78 | assert api.script_json(script_hash=script_hash) 79 | 80 | 81 | def test_script_cbor(requests_mock): 82 | api = BlockFrostApi() 83 | mock_data = { 84 | "cbor": "4e4d01000033222220051200120011" 85 | } 86 | requests_mock.get(f"{api.url}/scripts/{script_hash}/cbor", json=mock_data) 87 | assert api.script_cbor(script_hash=script_hash) == convert_json_to_object(mock_data) 88 | 89 | 90 | def test_integration_script_cbor(): 91 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 92 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 93 | assert api.script_cbor(script_hash=script_hash) 94 | 95 | 96 | def test_script_redeemers(requests_mock): 97 | api = BlockFrostApi() 98 | mock_data = [ 99 | { 100 | "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dce628516157f0", 101 | "tx_index": 0, 102 | "purpose": "spend", 103 | "unit_mem": "1700", 104 | "unit_steps": "476468", 105 | "fee": "172033" 106 | } 107 | ] 108 | requests_mock.get(f"{api.url}/scripts/{script_hash}/redeemers", json=mock_data) 109 | assert api.script_redeemers(script_hash=script_hash) == convert_json_to_object(mock_data) 110 | 111 | 112 | def test_integration_script_redeemers(): 113 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 114 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 115 | assert api.script_redeemers(script_hash=script_hash) == [] 116 | 117 | 118 | def test_script_datum(requests_mock): 119 | api = BlockFrostApi() 120 | mock_data = { 121 | "json_value": { 122 | "int": 42 123 | } 124 | } 125 | requests_mock.get(f"{api.url}/scripts/datum/{datum_hash}", json=mock_data) 126 | assert api.script_datum(datum_hash=datum_hash) == convert_json_to_object(mock_data) 127 | 128 | 129 | def test_integration_script_datum(): 130 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 131 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 132 | assert api.script_datum(datum_hash=datum_hash) 133 | 134 | def test_script_datum_cbor(requests_mock): 135 | api = BlockFrostApi() 136 | mock_data = { 137 | "cbor": "4e4d01000033222220051200120011" 138 | } 139 | requests_mock.get(f"{api.url}/scripts/datum/{datum_hash}/cbor", json=mock_data) 140 | assert api.script_datum_cbor(datum_hash=datum_hash) == convert_json_to_object(mock_data) 141 | 142 | 143 | def test_integration_script_datum_cbor(): 144 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 145 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 146 | assert api.script_datum_cbor(datum_hash=datum_hash) 147 | -------------------------------------------------------------------------------- /tests/test_cardano_utils.py: -------------------------------------------------------------------------------- 1 | import os, json 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | hash = "8f55e18a94e4c0951e5b8bd8910b2cb20aa4d742b1608fda3a06793d39fb07b1" 6 | xpub = "d507c8f866691bd96e131334c355188b1a1d0b2fa0ab11545075aab332d77d9eb19657ad13ee581b56b0f8d744d66ca356b93d42fe176b3de007d53e9c4c4e7a" 7 | role = 0 8 | index = 0 9 | 10 | 11 | def test_utils_addresses_xpub(requests_mock): 12 | api = BlockFrostApi() 13 | mock_data = [ 14 | { 15 | "xpub": "d507c8f866691bd96e131334c355188b1a1d0b2fa0ab11545075aab332d77d9eb19657ad13ee581b56b0f8d744d66ca356b93d42fe176b3de007d53e9c4c4e7a", 16 | "role": 0, 17 | "index": 0, 18 | "address": "addr1q90sqnljxky88s0jsnps48jd872p7znzwym0jpzqnax6qs5nfrlkaatu28n0qzmqh7f2cpksxhpc9jefx3wrl0a2wu8q5amen7" 19 | } 20 | ] 21 | requests_mock.get(f"{api.url}/utils/addresses/xpub/{xpub}/{role}/{index}", json=mock_data) 22 | assert api.utils_addresses_xpub(xpub, role, index) == convert_json_to_object(mock_data) 23 | 24 | 25 | def test_integration_utils_addresses_xpub(): 26 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 27 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 28 | assert api.utils_addresses_xpub(xpub, role, index) 29 | 30 | 31 | def test_utils_transaction_evaluate(requests_mock): 32 | api = BlockFrostApi() 33 | mock_data = hash 34 | requests_mock.post(f"{api.url}/utils/txs/evaluate", json=mock_data) 35 | assert api.transaction_evaluate(file_path="./README.md") == convert_json_to_object(mock_data) 36 | -------------------------------------------------------------------------------- /tests/test_health.py: -------------------------------------------------------------------------------- 1 | import os 2 | from blockfrost import BlockFrostApi, ApiError 3 | from blockfrost.utils import convert_json_to_object 4 | 5 | 6 | def test_health(requests_mock): 7 | api = BlockFrostApi() 8 | mock_data = { 9 | "is_healthy": True 10 | } 11 | requests_mock.get(api.url + '/health', json=mock_data) 12 | assert api.health() == convert_json_to_object(mock_data) 13 | 14 | 15 | def test_integration_health(): 16 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 17 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 18 | assert api.health() 19 | 20 | 21 | def test_clock(requests_mock): 22 | api = BlockFrostApi() 23 | mock_data = { 24 | "server_time": 1603400958947 25 | } 26 | requests_mock.get(api.url + '/health/clock', json=mock_data) 27 | assert api.clock() == convert_json_to_object(mock_data) 28 | 29 | 30 | def test_integration_clock(): 31 | if os.getenv('BLOCKFROST_PROJECT_ID_MAINNET'): 32 | api = BlockFrostApi(project_id=os.getenv('BLOCKFROST_PROJECT_ID_MAINNET')) 33 | assert api.clock() 34 | -------------------------------------------------------------------------------- /tests/test_helpers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import mock 3 | import pytest 4 | from blockfrost import SignatureVerificationError, verify_webhook_signature 5 | 6 | request_body = b'{"id":"47668401-c3a4-42d4-bac1-ad46515924a3","webhook_id":"cf68eb9c-635f-415e-a5a8-6233638f28d7","created":1650013856,"type":"block","payload":{"time":1650013853,"height":7126256,"hash":"f49521b67b440e5030adf124aee8f88881b7682ba07acf06c2781405b0f806a4","slot":58447562,"epoch":332,"epoch_slot":386762,"slot_leader":"pool1njjr0zn7uvydjy8067nprgwlyxqnznp9wgllfnag24nycgkda25","size":34617,"tx_count":13,"output":"13403118309871","fees":"4986390","block_vrf":"vrf_vk197w95j9alkwt8l4g7xkccknhn4pqwx65c5saxnn5ej3cpmps72msgpw69d","previous_block":"9e3f5bfc9f0be44cf6e14db9ed5f1efb6b637baff0ea1740bb6711786c724915","next_block":null,"confirmations":0}}' 7 | success_fixtures_list = [ 8 | { 9 | 'description': 'valid signature', 10 | 'request_body': request_body, 11 | 'signature_header': 't=1650013856,v1=f4c3bb2a8b0c8e21fa7d5fdada2ee87c9c6f6b0b159cc22e483146917e195c3e', 12 | 'secret': '59a1eb46-96f4-4f0b-8a03-b4d26e70593a', 13 | 'current_timestamp_mock': 1650013856 + 1, 14 | 'result': True 15 | }, 16 | { 17 | 'description': '2 signatures, one valid and one invalid', 18 | 'request_body': request_body, 19 | 'signature_header': 't=1650013856,v1=abc,v1=f4c3bb2a8b0c8e21fa7d5fdada2ee87c9c6f6b0b159cc22e483146917e195c3e', 20 | 'secret': '59a1eb46-96f4-4f0b-8a03-b4d26e70593a', 21 | 'current_timestamp_mock': 1650013856 + 1, 22 | 'result': True 23 | } 24 | ] 25 | 26 | error_fixtures_list = [ 27 | { 28 | 'description': 'throws due to invalid header fromat', 29 | 'request_body': request_body, 30 | 'signature_header': 'v1=f4c3bb2a8b0c8e21fa7d5fdada2ee87c9c6f6b0b159cc22e483146917e195c3e', 31 | 'secret': '59a1eb46-96f4-4f0b-8a03-b4d26e70593a', 32 | 'current_timestamp_mock': 1650013856 + 1, 33 | 'result_error': 'Invalid signature header format.' 34 | }, 35 | { 36 | 'description': 'throws due to sig version not supported by this sdk', 37 | 'request_body': request_body, 38 | 'signature_header': 't=1650013856,v42=abc', 39 | 'secret': '59a1eb46-96f4-4f0b-8a03-b4d26e70593a', 40 | 'current_timestamp_mock': 1650013856 + 1, 41 | 'result_error': 'No signatures with supported version scheme.' 42 | }, 43 | { 44 | 'description': 'throws due to no signature match', 45 | 'request_body': request_body, 46 | 'signature_header': 't=1650013856,v1=abc', 47 | 'secret': '59a1eb46-96f4-4f0b-8a03-b4d26e70593a', 48 | 'current_timestamp_mock': 1650013856 + 1, 49 | 'result_error': 'No signature matches the expected signature for the payload.' 50 | }, 51 | { 52 | 'description': 'throws due to timestamp out of tolerance zone', 53 | 'request_body': request_body, 54 | 'signature_header': 't=1650013856,v1=f4c3bb2a8b0c8e21fa7d5fdada2ee87c9c6f6b0b159cc22e483146917e195c3e', 55 | 'secret': '59a1eb46-96f4-4f0b-8a03-b4d26e70593a', 56 | 'current_timestamp_mock': 1650013856 + 7200, 57 | 'result_error': 'Signature\'s timestamp is outside of the time tolerance.' 58 | } 59 | ] 60 | 61 | 62 | @pytest.mark.parametrize("fixture", success_fixtures_list) 63 | def test_verify_webhook_signature(fixture): 64 | with mock.patch('blockfrost.helpers.get_unix_timestamp', return_value=fixture['current_timestamp_mock']): 65 | res = verify_webhook_signature( 66 | fixture['request_body'], fixture['signature_header'], fixture['secret']) 67 | assert res == fixture['result'] 68 | 69 | 70 | @pytest.mark.parametrize("fixture", error_fixtures_list) 71 | def test_verify_webhook_signature_fails(fixture): 72 | with mock.patch('blockfrost.helpers.get_unix_timestamp', return_value=fixture['current_timestamp_mock']): 73 | with pytest.raises(SignatureVerificationError) as e_info: 74 | verify_webhook_signature( 75 | fixture['request_body'], fixture['signature_header'], fixture['secret']) 76 | assert str(e_info.value) == fixture['result_error'] 77 | assert e_info.value.header == fixture['signature_header'] 78 | assert e_info.value.request_body == fixture['request_body'] 79 | -------------------------------------------------------------------------------- /tests/test_ipfs_add.py: -------------------------------------------------------------------------------- 1 | from blockfrost import BlockFrostIPFS, ApiError 2 | from blockfrost.utils import convert_json_to_object 3 | 4 | file_path = "README.md" 5 | 6 | 7 | def test_add(requests_mock): 8 | ipfs = BlockFrostIPFS() 9 | mock_data = { 10 | "name": file_path, 11 | "ipfs_hash": "QmZbHqiCxKEVX7QfijzJTkZiSi3WEVTcvANgNAWzDYgZDr", 12 | "size": 125297 13 | } 14 | requests_mock.post(f"{ipfs.url}/ipfs/add", json=mock_data) 15 | assert ipfs.add(file_path=file_path) == convert_json_to_object(mock_data) 16 | -------------------------------------------------------------------------------- /tests/test_ipfs_gateway.py: -------------------------------------------------------------------------------- 1 | from blockfrost import BlockFrostIPFS, ApiError 2 | from blockfrost.utils import convert_json_to_object 3 | 4 | IPFS_path = "QmZbHqiCxKEVX7QfijzJTkZiSi3WEVTcvANgNAWzDYgZDr" 5 | 6 | 7 | def test_gateway(requests_mock): 8 | ipfs = BlockFrostIPFS() 9 | mock_data = "data" 10 | requests_mock.get(f"{ipfs.url}/ipfs/gateway/{IPFS_path}", text=mock_data) 11 | assert ipfs.gateway(IPFS_path=IPFS_path).text == convert_json_to_object(mock_data) 12 | -------------------------------------------------------------------------------- /tests/test_ipfs_pins.py: -------------------------------------------------------------------------------- 1 | from blockfrost import BlockFrostIPFS, ApiError 2 | from blockfrost.utils import convert_json_to_object 3 | 4 | IPFS_path = "QmZbHqiCxKEVX7QfijzJTkZiSi3WEVTcvANgNAWzDYgZDr" 5 | 6 | 7 | def test_pin_object(requests_mock): 8 | ipfs = BlockFrostIPFS() 9 | mock_data = { 10 | "ipfs_hash": IPFS_path, 11 | "state": "queued" 12 | } 13 | requests_mock.post(f"{ipfs.url}/ipfs/pin/add/{IPFS_path}", json=mock_data) 14 | assert ipfs.pin_object(IPFS_path=IPFS_path) == convert_json_to_object(mock_data) 15 | 16 | 17 | def test_pined_list(requests_mock): 18 | ipfs = BlockFrostIPFS() 19 | mock_data = [ 20 | { 21 | "time_created": 1615551024, 22 | "time_pinned": 1615551024, 23 | "ipfs_hash": IPFS_path, 24 | "size": 1615551024, 25 | "state": "pinned" 26 | } 27 | ] 28 | requests_mock.get(f"{ipfs.url}/ipfs/pin/list", json=mock_data) 29 | assert ipfs.pined_list() == convert_json_to_object(mock_data) 30 | 31 | 32 | def test_pined_object(requests_mock): 33 | ipfs = BlockFrostIPFS() 34 | mock_data = { 35 | "time_created": 1615551024, 36 | "time_pinned": 1615551024, 37 | "ipfs_hash": IPFS_path, 38 | "size": 1615551024, 39 | "state": "pinned" 40 | } 41 | requests_mock.get(f"{ipfs.url}/ipfs/pin/list/{IPFS_path}", json=mock_data) 42 | assert ipfs.pined_object(IPFS_path=IPFS_path) == convert_json_to_object(mock_data) 43 | 44 | 45 | def test_pined_object_remove(requests_mock): 46 | ipfs = BlockFrostIPFS() 47 | mock_data = { 48 | "ipfs_hash": IPFS_path, 49 | "state": "unpinned" 50 | } 51 | requests_mock.post(f"{ipfs.url}/ipfs/pin/remove/{IPFS_path}", json=mock_data) 52 | assert ipfs.pined_object_remove(IPFS_path=IPFS_path) == convert_json_to_object(mock_data) 53 | -------------------------------------------------------------------------------- /tests/test_metrics.py: -------------------------------------------------------------------------------- 1 | from blockfrost import BlockFrostApi, ApiError 2 | from blockfrost.utils import convert_json_to_object 3 | 4 | 5 | def test_metrics(requests_mock): 6 | api = BlockFrostApi() 7 | mock_data = [ 8 | { 9 | "time": 1612543884, 10 | "calls": 42 11 | }, 12 | { 13 | "time": 1614523884, 14 | "calls": 6942 15 | } 16 | ] 17 | 18 | requests_mock.get(api.url + '/metrics', json=mock_data) 19 | assert api.metrics() == convert_json_to_object(mock_data) 20 | 21 | 22 | def test_metrics_endpoints(requests_mock): 23 | api = BlockFrostApi() 24 | mock_data = [ 25 | { 26 | "time": 1612543814, 27 | "calls": 182, 28 | "endpoint": "block" 29 | }, 30 | { 31 | "time": 1612543814, 32 | "calls": 42, 33 | "endpoint": "epoch" 34 | }, 35 | ] 36 | 37 | requests_mock.get(api.url + '/metrics/endpoints', json=mock_data) 38 | assert api.metrics_endpoints() == convert_json_to_object(mock_data) 39 | -------------------------------------------------------------------------------- /tests/test_object_mapper.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from pandas._testing import assert_frame_equal 3 | from blockfrost import BlockFrostApi, ApiError 4 | from blockfrost.utils import convert_json_to_object 5 | 6 | api = BlockFrostApi() 7 | 8 | mock_data = { 9 | "is_healthy": True 10 | } 11 | 12 | 13 | def test_default_mapper(requests_mock): 14 | requests_mock.get(api.url + '/health', json=mock_data) 15 | assert api.health() == convert_json_to_object(mock_data) 16 | 17 | 18 | def test_object_mapper(requests_mock): 19 | requests_mock.get(api.url + '/health', json=mock_data) 20 | assert api.health(return_type="object") == convert_json_to_object(mock_data) 21 | 22 | 23 | def test_json_mapper(requests_mock): 24 | requests_mock.get(api.url + '/health', json=mock_data) 25 | assert api.health(return_type="json") == mock_data 26 | 27 | 28 | def test_pandas_mapper(requests_mock): 29 | requests_mock.get(api.url + '/health', json=mock_data) 30 | mock_pandas = pd.json_normalize(mock_data) 31 | assert_frame_equal(api.health(return_type="pandas"), mock_pandas) 32 | --------------------------------------------------------------------------------