fireblocks / fireblocks-sdk-py Goto Github PK
View Code? Open in Web Editor NEWOfficial Python SDK for Fireblocks API
Home Page: http://docs.fireblocks.com/api/swagger-ui/
License: MIT License
Official Python SDK for Fireblocks API
Home Page: http://docs.fireblocks.com/api/swagger-ui/
License: MIT License
Is your feature request related to a problem? Please describe.
Some organization may require internet proxy in order to call the APIs.
Describe the solution you'd like
The Python SDK should provide support for users to configure proxy settings if required in order to make API calls to Fireblocks
Additional context
A temporary workaround is documented here - https://fireblocks.atlassian.net/wiki/spaces/SE/pages/901808765/Using+Proxies+w+Fireblocks+Python+SDK
Describe the bug
The method get_external_wallet()
is not retrieving the customerRefId property, even though this property is set for a wallet (not null).
To Reproduce
Steps to reproduce the behavior:
from fireblocks_sdk import FireblocksSDK
import json
with open('/path/to/secret.key', 'r') as file: # Replace '/path/to/secret.key' with a valid path
fireblocks = FireblocksSDK(
private_key=file.read(),
api_key='my_api_key', # Replace 'my_api_key' with a valid API Key
api_base_url='https://api.fireblocks.io'
)
my_external_wallet_id = 'aac59cad-6bc7-931c-bddf-cbc633b455ea' # A valid walletId
ew = fireblocks.get_external_wallet(my_external_wallet_id)
print("##### Retrieving one external wallet:")
print(json.dumps(ew, indent=2))
external_wallets = fireblocks.get_external_wallets()
print("##### Retrieving all the external wallets and getting only the same retrieved one above:")
for wallet in external_wallets:
if wallet['id'] == ew['id']:
print(json.dumps(wallet, indent=2))
break
Expected behavior
It should display two dictionaries printed out, according to API specifications (https://docs.fireblocks.com/api/?python#externalwallet) and they should be exactly the same object.
Actual behavior
The obtained object by using get_external_wallet()
(single) doesn't have the property customerRefId
, but the obtained object by using get_external_wallets()
(multiple) does have that property:
##### Retrieving one external wallet:
{
"id": "aac59cad-6bc7-931c-bddf-cbc633b455ea",
"name": "Ricardo_ETH-ETH_TEST-EV3K05V9CDX2",
"assets": [
{
"id": "ETH_TEST",
"status": "APPROVED",
"address": "0x26B13e3927022988cd7D805D5bc85cD9Fc06208b",
"tag": ""
}
]
}
##### Retrieving all the external wallets and getting only the same retrieved one above:
{
"id": "aac59cad-6bc7-931c-bddf-cbc633b455ea",
"name": "Ricardo_ETH-ETH_TEST-EV3K05V9CDX2",
"customerRefId": "Ricardo_ETH-ETH_TEST-EV3K05V9CDX2",
"assets": [
{
"id": "ETH_TEST",
"status": "APPROVED",
"address": "0x26B13e3927022988cd7D805D5bc85cD9Fc06208b",
"tag": ""
}
]
}
Versions:
When getting a 4xx or 5xx response, the sdk raises FireblocksApiException
, with the following args:
('Got an error from fireblocks server: {"message":"The external tx id that was provided in the request, already exists","code":1438}',)
Error code is presented inside the error message, which is tricky to parse and extract. Would be great to have the error_code
integer property in FireblocksApiException
.
Describe the bug
In some cases, when receiving a 404 from the server, the response is HTML and not JSON. In this case, response.json()
will throw a JSONDecodeError
. This propagates to the caller instead of being correctly handled and logged.
To Reproduce
Steps to reproduce the behavior:
JSONDecodeError
on the 404Expected behavior
Non-JSON response codes should correctly be handled and not result in an exception.
Versions (please complete the following information):
The create_vault_account
API method has the autoFuel
parameter. But it can't be passed with the Python SDK.
Describe the bug
I am constantly getting 500 internal error responses for get_vault_assets_balance
. Other times I am also receiving:
requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
To Reproduce
Please see below
Expected behavior
I am trying to make a snapshot of our Fireblocks account for backup and also accounting purposes. I am trying to iterate over each vault and its assets and gathering balances for each.
Versions (please complete the following information):
Additional context
Here is an excerpt of my related code snippets and logs:
def catch_and_retry_fireblocks_exception(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
max_retries = 3
retries = 0
while retries < max_retries:
try:
return func(self, *args, **kwargs)
except FireblocksApiException as e:
self.logger.error(
f"[Fireblocks] Error occurred for function {func.__name__}: {e}"
)
self.logger.error(f"[Fireblocks] Arguments: {args}, {kwargs}")
# Handle the exception as needed
retries += 1
if retries < max_retries:
# Wait for 1 second before retrying
time.sleep(0.1)
self.logger.info(f"[Fireblocks] Retrying {func.__name__}...")
else:
self.logger.warning(
f"[Fireblocks] Maximum retries reached. Skipping {func.__name__}."
)
return wrapper
class FireblocksSDKWrapper:
def __init__(self, api_key: str, private_key: str):
self.fireblocks = FireblocksSDK(api_key=api_key, private_key=private_key)
self.logger = Logger(name=__name__, level="INFO").get_logger()
@catch_and_retry_fireblocks_exception
def get_vault_accounts_paginated(self):
next_page = True
paged_filter = PagedVaultAccountsRequestFilters()
accounts = []
while next_page:
vaults = self.fireblocks.get_vault_accounts_with_page_info(paged_filter)
accounts = vaults["accounts"]
accounts.extend(accounts)
# Paginate through accounts
if vaults.get("paging").get("after") is None:
next_page = False
else:
paged_filter.after = vaults.get("paging").get("after")
return accounts
@catch_and_retry_fireblocks_exception
def get_internal_wallets(self):
return self.fireblocks.get_internal_wallets()
@catch_and_retry_fireblocks_exception
def get_deposit_addresses(self, vault_account_id: typing.Any, asset_id: typing.Any):
return self.fireblocks.get_deposit_addresses(vault_account_id, asset_id)
@catch_and_retry_fireblocks_exception
def get_vault_assets_balance(
self,
account_name_prefix: typing.Any | None = None,
account_name_suffix: typing.Any | None = None,
):
return self.fireblocks.get_vault_assets_balance(
account_name_prefix,
account_name_suffix,
)
@dataclasses.dataclass
class FireblocksAccountingLoader(ExchangeLoaderBase):
fireblocks: typing.Optional[FireblocksSDKWrapper] = dataclasses.field(init=True)
vault_accounts: typing.List[dict] = dataclasses.field(init=False)
internal_wallets: typing.List[dict] = dataclasses.field(init=False)
accounts: typing.List[dict] = dataclasses.field(init=False)
logger: logging.Logger = dataclasses.field(
default_factory=lambda: logging.Logger(__name__)
)
def __post_init__(self):
self.vault_accounts = self.fireblocks.get_vault_accounts_paginated()
self.internal_wallets = self.fireblocks.get_internal_wallets()
self.accounts = self.vault_accounts # + self.internal_wallets
def fetch_accounts(self, utc_timestamp: str) -> pd.DataFrame:
df = pd.DataFrame(self.accounts)
# explode the assets column
df = df.explode("assets")
# Cast nan assets to {}
df.assets = df.assets.fillna({i: {} for i in df.index})
# create new columns for each key in the dictionaries
df["asset_id"] = df["assets"].apply(lambda x: x["id"] if "id" in x else None)
df["asset_total"] = df["assets"].apply(
lambda x: x["total"] if "total" in x else None
)
df["asset_balance"] = df["assets"].apply(
lambda x: x["balance"] if "balance" in x else None
)
df["account_type"] = df["name"].apply(
lambda x: (
"internal_wallet" if x == "Gas Station Wallet" else "vault_account"
)
)
# Cast id to int
df["id"] = df["id"].astype(int)
# Remove "Network Deposits" vault accounts
df = df[df.name != "Network Deposits"]
# Add the timestamp to the dataframe
df["timestamp"] = utc_timestamp
# drop the original assets column
df = df.drop("assets", axis=1)
# Reset index
df = df.reset_index(drop=True)
# Fetch asset_id to a dict
# Get the vault account asset information
for row, col in df.iterrows():
# Skip if the account is an internal wallet
if col.account_type == "internal_wallet":
# Get an index of the internal wallet
internal_wallet_index = [
i for i, x in enumerate(self.internal_wallets) if x["id"] == col.id
][0]
asset_info = self.internal_wallets[internal_wallet_index]["assets"]
elif col.account_type == "vault_account" and col.asset_id is not None:
asset_info = self.fireblocks.get_deposit_addresses(
vault_account_id=col.id,
asset_id=col.asset_id,
)
else:
asset_info = []
# Restructure the asset_info into a dictionary
asset_dict = {}
for asset in asset_info:
asset_dict[asset["address"]] = {
"asset_id": asset["assetId"] if "assetId" in asset else None,
"address": asset["address"] if "address" in asset else None,
"tag": asset["tag"] if "tag" in asset else None,
"description": (
asset["description"] if "description" in asset else None
),
"type": asset["type"] if "type" in asset else None,
"addressFormat": (
asset["addressFormat"] if "addressFormat" in asset else None
),
"legacyAddress": (
asset["legacyAddress"] if "legacyAddress" in asset else None
),
"bip44AddressIndex": (
asset["bip44AddressIndex"]
if "bip44AddressIndex" in asset
else None
),
}
# Add asset_info as a column to the dataframe being iterated over
df.loc[row, "addresses"] = [[asset_dict]]
# Get additional balance information
df_assets = pd.DataFrame()
for vault_name in df.name.unique():
# Skip if the account is an internal wallet
if vault_name == "Gas Station Wallet":
continue
asset_balances = pd.DataFrame(
self.fireblocks.get_vault_assets_balance(vault_name)
)
asset_balances = asset_balances.rename(columns={"id": "asset_id"})
asset_balances["name"] = vault_name
df_assets = pd.concat([df_assets, asset_balances], axis=0)
# Merge the asset balances with the dataframe
df_merged = pd.merge(df, df_assets, on=["name", "asset_id"])
# Calculate prices
asset_ids = df_merged.asset_id.unique()
asset_prices = {}
for asset_id in asset_ids:
try:
asset_id_split = asset_id.split("_")[0]
asset_prices[asset_id] = self.calculate_usd_price(
asset=asset_id_split,
exchange=ccxt.bitmart(), # Using bitmart since only they have RND
)
except Exception as e: # pylint: disable=broad-except
self.logger.info(
"[Fireblocks] Could not fetch price for %s (error: %s)",
asset_id,
e,
)
continue
for row, col in df_merged.iterrows():
try:
asset_price = asset_prices[col.asset_id]
df_merged.loc[row, "usd_price"] = asset_prices[col.asset_id]
df_merged.loc[row, "usd_value"] = (
col.asset_total * asset_price if asset_price is not None else None
)
except Exception as e: # pylint: disable=broad-except
self.logger.info(
"[Fireblocks] Could not set price for %s: %s)", col.asset_id, e
)
continue
return df_merged
def to_dataframe(self, utc_timestamp: pd.Timestamp = None):
return self.fetch_accounts(
utc_timestamp=(
pd.Timestamp.utcnow() if utc_timestamp is None else utc_timestamp
)
)
2024-03-07 10:56:19,315 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 10:56:19,426 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
2024-03-07 11:08:53,224 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 11:08:53,327 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
2024-03-07 11:11:35,880 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 11:11:35,984 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
2024-03-07 11:14:23,272 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 11:14:23,378 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
2024-03-07 11:17:10,825 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 11:17:10,932 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
2024-03-07 11:19:52,531 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 11:19:52,640 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
2024-03-07 11:22:33,295 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 11:22:33,401 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
2024-03-07 11:25:17,782 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 11:25:17,890 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
2024-03-07 11:28:00,107 - rand_treasury_monitoring.utils.fireblocks - ERROR - [Fireblocks] Error occurred for function get_vault_assets_balance: Got an error from fireblocks server: 500
2024-03-07 11:28:00,211 - rand_treasury_monitoring.utils.fireblocks - INFO - [Fireblocks] Retrying get_vault_assets_balance...
Exception has occurred: ConnectionError (note: full exception trace is shown but execution is paused at: <module>)
('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
http.client.RemoteDisconnected: Remote end closed connection without response
During handling of the above exception, another exception occurred:
urllib3.exceptions.ProtocolError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
During handling of the above exception, another exception occurred:
File "/Users/adr/Dev/Rand-Network/treasury-balance-service/rand_treasury_monitoring/utils/fireblocks.py", line 74, in get_vault_assets_balance
return self.fireblocks.get_vault_assets_balance(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/adr/Dev/Rand-Network/treasury-balance-service/rand_treasury_monitoring/utils/fireblocks.py", line 17, in wrapper
return func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/adr/Dev/Rand-Network/treasury-balance-service/rand_treasury_monitoring/data/loaders.py", line 316, in fetch_accounts
self.fireblocks.get_vault_assets_balance(vault_name)
File "/Users/adr/Dev/Rand-Network/treasury-balance-service/rand_treasury_monitoring/data/loaders.py", line 357, in to_dataframe
return self.fetch_accounts(
^^^^^^^^^^^^^^^^^^^^
File "/var/folders/w6/dh79cy2x0sjdkb2y4vrs8kq00000gn/T/ipykernel_18952/3364672574.py", line 1, in <module> (Current frame)
loader.to_dataframe()
requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
Describe the bug
get_users()
crashes with
FireblocksApiException: Got an error from fireblocks server: { "message": "ok", "code": 0}
To Reproduce
Steps to reproduce the behavior:
.get_users()
Expected behavior
Should return the list of users.
Versions (please complete the following information):
Hi,
I am trying to make a basic request to Fireblocks API using python but getting stopped out at the from/import line. It can’t find the module even though it is shown in my list of python packages.
I’ve copied the line directly from the Github. Any ideas what is going on?
If its just list these 4 lines, I will still get the module error (but if I comment out the 4th line the file executes smoothly)
Ive also confirmed the fireblocks-sdk is in correct path location as other modules
Release 1.6.3 in 7e3d66c you've removed treat_as_gross_amount
from the arguments list, but not from the body preparation, so create_transaction
does not work:
Traceback (most recent call last):
File "/xxx/enricher.py", line 1147, in post
tx_result = self.fireblocks.create_transaction(**kwargs)
File "/xxx/venv/lib/python3.9/site-packages/fireblocks_sdk/sdk.py", line 720, in create_transaction
if treat_as_gross_amount:
NameError: name 'treat_as_gross_amount' is not defined
It'd be great if we had a method for parsing the JSON messages sent via Webhooks into Python objects (ideally MyPy-annotated classes). I think this would make it easier to program against the Webhooks API because devs wouldn't have to refer to the documentation for type information--it'd be right there in the code.
Is your feature request related to a problem? Please describe.
I'm frustrated that I can't add retries to the FireblocksSDK like those described here.
Describe the solution you'd like
I'd like a session: requests.Session
argument to be added to the FireblocksSDK
constructor. Then, all requests would be routed through it instead of the global requests
package.
Additional context
Allowing users to provide their own session would give a lot more flexibility in addition to retries, like adding logging.
Describe the bug
The documentation should be more precise in install requirments. pip install fireblocks-sdk is not enough.
To Reproduce
pip install fireblocks-sdk
gets errorTraceback (most recent call last): File "/home/michael/PycharmProjects/pythonProject/venv/bin/pip3", line 5, in <module> from pip._internal.cli.main import main File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/cli/main.py", line 9, in <module> from pip._internal.cli.autocompletion import autocomplete File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/cli/autocompletion.py", line 10, in <module> from pip._internal.cli.main_parser import create_main_parser File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/cli/main_parser.py", line 9, in <module> from pip._internal.build_env import get_runnable_pip File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/build_env.py", line 20, in <module> from pip._internal.cli.spinners import open_spinner File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/cli/spinners.py", line 9, in <module> from pip._internal.utils.logging import get_indentation File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/utils/logging.py", line 29, in <module> from pip._internal.utils.misc import ensure_dir File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py", line 42, in <module> from pip._internal.locations import get_major_minor_version File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/locations/__init__.py", line 67, in <module> from . import _distutils File "/home/michael/PycharmProjects/pythonProject/venv/lib/python3.8/site-packages/pip/_internal/locations/_distutils.py", line 20, in <module> from distutils.cmd import Command as DistutilsCommand ModuleNotFoundError: No module named 'distutils.cmd'
Expected behavior
pip install fireblocks-sdk should work
Versions (please complete the following information):
Describe the bug
In method _post_request()
, idempotency_key
parameter doesnt work
Maybe it's simply bug on (sdk.py, line:1595)
To Reproduce
call method(e.g. create_transaction) twice with same idempotency_key
parameter
(it always makes two transactions)
Expected behavior
keep idempotency
(when set idempotency_key
parameter, add Idempotency-Key
header)
Versions (please complete the following information):
Is your feature request related to a problem? Please describe.
Not so much a problem, but for a service readiness probe I am calling get_supported_assets
to check the FireblocksSDK is ready and working. It would be nice to call a ping
endpoint just to confirm everything is ready as the SDK doesn't check the correctness are instantiation time.
Describe the solution you'd like
just a simple method like
def ping(self):
"""Pings Fireblocks API to confirm private_key and api_key are working correctly"""
return self._get_request("/v1/ping")
>>> fireblocks = FireblocksSDK('a', 'b')
>>> fireblocks.ping()
raise ValueError("Could not deserialize key data....")
>>> fireblocks = FireblocksSDK(os.getenv('FIREBLOCKS_PRIVATE_KEY'), os.getenv('FIREBLOCKS_API_KEY'))
>>> fireblocks.ping()
'pong'
The bug
When I make a call to this endpoint, the API returns the wrong asset despite this asset being present in the vault account.
To Reproduce
DEBUG:urllib3.connectionpool:https://api.fireblocks.io:443 "GET /v1/exchange_accounts/vault_acct_id/BTC HTTP/1.1" 200 88
{'id': 'ETH', 'balance': '0.2', 'total': '0.2', 'available': '0.2', 'lockedAmount': '0.00000000'}
Versions (please complete the following information):
Describe the bug
The ContractUploadRequest
class uses a literal dict
to type hints the input_fields_metadata
parameter. Literal type hints are supported from Python 3.9 onward.
To Reproduce
Steps to reproduce the behavior:
import fireblocks_sdk
input_fields_metadata: Optional[dict[str, FieldMetadata]] = None,
TypeError: 'type' object is not subscriptable
Expected behavior
Able to import fireblocks_sdk
on Python >= 3.6
Versions (please complete the following information):
Additional context
The bug was introduced in PR #146
Is your feature request related to a problem? Please describe.
I'm frustrated that I can't specify different timeouts for different requests because there is just a single timeout variable in the FireblocksSDK object.
Describe the solution you'd like
An optional timeout=None
parameter added to all the methods in FireblocksSDK
.
In the function set_gas_station_configuration parameter maxGasPrice is optional, however it is always added to body
body = {
"gasThreshold": gas_threshold,
"gasCap": gas_cap,
"maxGasPrice": max_gas_price,
}
fireblocks-sdk-py/fireblocks_sdk/sdk.py
Line 1947 in 39fb079
Calling function without max_gas_price is rejected with error "maxGasPrice must be string".
This change of code resolved issue for me:
body = {
"gasThreshold": gas_threshold,
"gasCap": gas_cap #,
#optional "maxGasPrice": max_gas_price,
}
if max_gas_price:
body["maxGasPrice"]=max_gas_price
Denis
Is your feature request related to a problem? Please describe.
Sorry me again. This is a tiny issue but I am a pedantic person.
In the method:
def set_gas_station_configuration(self, gas_threshold, gas_cap, max_gas_price):
"""Set configuration of the Gas Station account
Args:
gasThreshold (str)
gasCap (str)
maxGasPrice (str, optional)
"""
url = f"/v1/gas_station/configuration"
body = {
"gasThreshold": gas_threshold,
"gasCap": gas_cap,
"maxGasPrice": max_gas_price
}
return self._put_request(url, body)
It would be good if max_gas_price
is optional, for it to be a keyword arg equal to None
Describe the solution you'd like
def set_gas_station_configuration(self, gas_threshold, gas_cap, max_gas_price=None):
Additional context
/usr/local/lib/python3.8/site-packages/fireblocks_sdk/sdk.py in get_vault_accounts(self, name_prefix, name_suffix, min_amount_threshold)
51 url = url + "?" + urllib.parse.urlencode(params)
52
---> 53 return self._get_request(url)
54
55 def get_vault_account(self, vault_account_id):
/usr/local/lib/python3.8/site-packages/fireblocks_sdk/sdk.py in _get_request(self, path)
1021
1022 def _get_request(self, path):
-> 1023 token = self.token_provider.sign_jwt(path)
1024 headers = {
1025 "X-API-Key": self.api_key,
/usr/local/lib/python3.8/site-packages/fireblocks_sdk/sdk_token_provider.py in sign_jwt(self, path, body_json)
26 }
27
---> 28 return jwt.encode(token, key=self.private_key, algorithm="RS256").decode('utf-8')
AttributeError: 'str' object has no attribute 'decode'
There is following if construction in create_transaction method:
if amount:
body["amount"] = amount
In case of amount=0 (contract call for example), bool(0) returns False, so amount field will be empty.
Better to use:
if amount is not None:
body["amount"] = amount
class DestinationTransferPeerPath(TransferPeerPath):
def __init__(self, peer_type, peer_id=None, one_time_address=None):
"""Defines a destination for a transfer
Args:
peer_type (str): either VAULT_ACCOUNT, EXCHANGE_ACCOUNT, INTERNAL_WALLET, EXTERNAL_WALLET, FIAT_ACCOUNT, NETWORK_CONNECTION, ONE_TIME_ADDRESS or UNKNOWN_PEER
peer_id (str): the account/wallet id
one_time_address (JSON object): The destination address (and tag) for a non whitelisted address.
"""
TransferPeerPath.__init__(self, peer_type, peer_id)
if one_time_address != None:
self.oneTimeAddress = one_time_address
Default value of peer_id is None . So if I try to create transaction to ONE_TIME_ADDRESS destination like this:
destination = DestinationTransferPeerPath(peer_type='ONE_TIME_ADDRESS',
one_time_address={'address': destination_address})
I get the following error:
Got an error from fireblocks server: {"message":"Destination ID must not be provided for one time address transactions","code":1409}
The only way to avoid the error is to delete id attribute.
del destination.id
I was forced to update my sdk version from 1.15.2 to 2.3.0. I am using python 3.7.12. When I try to use this updated sdk, I get the following error:
ModuleNotFoundError: No module named 'importlib.metadata'
This error occurs on line 4 in the sdk.py file. Python 3.7 does not have importlib.metadata as part of its bundled library. You can see that this package is labeled "new" in the following link for python3.8 (https://docs.python.org/3/library/importlib.metadata.html).
For the sdk to stay compatible with python3.7, I think line 4 should be changed to the following
try:
from importlib.metadata import version
except ImportError: # for Python<3.8
from importlib_metadata import version
Greetings. When I installed fireblocks-sdk with pip install fireblocks-sdk
, I got the following error:
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
flask-jwt-extended 4.2.1 requires PyJWT<3.0,>=2.0, but you have pyjwt 1.7.1 which is incompatible.
The error was solved when I downgrade flask-jwt-extended to version 3.25.1, but this is not what I want, since I need to use some features of flask-jwt-extended 4.x.x.
Please, upgrade fireblocks-sdk dependencies, or at least PyJWT in order to work with the latest version of each library. Thank you in advance.
It was tested in:
Python: 3.9.5
PIP: 21.1.1
OS: Windows 10 x64
Documentation states that the tag
argument is only to be used with Ripple
accounts, but without this argument the API returns and confusing error code 500
. Please correct as it makes these issues a debugging hell...
fireblocks-sdk-py/fireblocks_sdk/sdk.py
Line 820 in 75139a0
>>> from fireblocks_sdk import ONE_TIME_ADDRESS
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/lv/Downloads/fireblocks-sdk-py/fireblocks_sdk/__init__.py", line 1, in <module>
from fireblocks_sdk.sdk import FireblocksSDK
File "/home/lv/Downloads/fireblocks-sdk-py/fireblocks_sdk/sdk.py", line 6, in <module>
from .api_types import FireblocksApiException, TRANSACTION_TYPES, TRANSACTION_STATUS_TYPES, PEER_TYPES, TransferPeerPath, DestinationTransferPeerPath, TransferTicketTerm, TRANSACTION_TRANSFER, SIGNING_ALGORITHM, UnsignedMessage, FEE_LEVEL
File "/home/lv/Downloads/fireblocks-sdk-py/fireblocks_sdk/api_types.py", line 39, in <module>
TRANSACTION_TYPES = (TRANSACTION_TRANSFER, TRANSACTION_MINT, TRANSACTION_BURN, TRANSACTION_SUPPLY_TO_COMPOUND, TRANSACTION_REDEEM_FROM_COMPOUND, RAW, CONTRACT_CALL, ONE_TIME_ADDRESS)
NameError: name 'ONE_TIME_ADDRESS' is not defined
Sometimes, your API can respond very long.
Can you pls add the timeout
parameter, which we can pass to the client, so that we can limit the request time at the client side.
Would be also great to have a default client timeout for any request, which can be set for a FireblocksSDK
instance.
Describe the bug
In order to estimate the fee of a transaction involving multiple inputs, I need to pass the list of inputs to the estimate_fee_for_transaction
, but the function doesn't have an extra_parameters
argument. create_transaction
has it: I think the 2 functions should have very similar signatures
Versions (please complete the following information):
The library is unusable in an asynchronous application like FastAPI as it causes blocking network IO calls, which disrupts the async event loop.
Implement an AsyncFireblockSDK that uses a library like httpx instead of requests
.
httpx itself has clever ways to separate the actual HTTP IO mechanism (which has to be async) from the building of the Request and Response objects themselves, so perhaps this would be pretty simple to build to mimic it's design. It also works synchronously so it is easy to replace requests with httpx entirely.
Second parameter of _post_request()
is body
, not a idempotency_key
:
fireblocks-sdk-py/fireblocks_sdk/sdk.py
Line 1105 in 7a70d37
So the following code is invalid:
fireblocks-sdk-py/fireblocks_sdk/sdk.py
Line 549 in 7a70d37
There should be either:
return self._post_request(f"/v1/vault/accounts/{vault_account_id}/{asset_id}", {}, idempotency_key)
or
return self._post_request(f"/v1/vault/accounts/{vault_account_id}/{asset_id}", idempotency_key=idempotency_key)
There is a header Idempotency-Key
, described in the API spec.
Actually it can't be passed within Python SDK.
Describe the bug
In Python 3.9 and fireblocks_sdk 2.5.0, I can't run any code importing anything from fireblocks_sdk
.
To Reproduce
Steps to reproduce the behavior:
Run the following snippet:
from fireblocks_sdk import FireblocksSDK
with open('FIREBLOCKS_SECRET_KEY_PATH', 'r') as file:
fireblocks = FireblocksSDK(
private_key=file.read(),
api_key='FIREBLOCKS_API_KEY',
api_base_url='https://api.fireblocks.io'
)
Expected behavior
The code runs with no exceptions.
Screenshots
Versions (please complete the following information):
Additional context
This snippet was run in Windows 11
Workaround
I downgraded fireblocks_sdk to 2.4.0 and the code runs with no exceptions.
Fireblocks API External wallet create API library function has issue while implementing using function
Details :- As per the REST API documentation, the parameter customerRefId is optional, but in the fireblocks_sdk library provided for python, the default value for customerRefId is not provided.
customerRefId field is mentioned as optional in documentation but inside github code it is not optional . i tried to implemnt this and it it mandatory to mention customerRefId .i have attached screen shots also for reference purpose
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.