From 4e2c2d9cce4cc12747085d3ce1991b9e80cc2bbd Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:18:10 +0000 Subject: [PATCH 1/5] refactor: query params typehinting --- client/api/api_nodes.py | 5 +- client/api/blocks.py | 17 +++-- client/api/commits.py | 4 +- client/api/evm.py | 6 +- client/api/node.py | 5 +- client/api/peers.py | 7 +- client/api/receipts.py | 18 ++++- client/api/rounds.py | 9 ++- client/api/tokens.py | 43 +++++++++--- client/api/transactions.py | 26 ++++--- client/api/validators.py | 21 ++++-- client/api/votes.py | 9 ++- client/api/wallets.py | 44 +++++++++--- client/types/__init__.py | 12 ++++ client/types/api_nodes.py | 12 ++++ client/types/blocks.py | 32 +++++++++ client/types/evm.py | 17 +++++ client/types/node.py | 10 +++ client/types/peers.py | 14 ++++ client/types/receipts.py | 39 +++++++++++ client/types/tokens.py | 59 ++++++++++++++++ client/types/transactions.py | 54 +++++++++++++++ client/types/validators.py | 39 +++++++++++ client/types/votes.py | 35 ++++++++++ client/types/wallets.py | 47 +++++++++++++ tests/api/test_blocks.py | 4 +- tests/api/test_commits.py | 2 +- tests/api/test_receipts.py | 49 ++++++++++++++ tests/api/test_tokens.py | 119 ++++++++++++++++++++++++++++----- tests/api/test_transactions.py | 30 ++++++--- tests/api/test_wallets.py | 18 +++-- 31 files changed, 715 insertions(+), 91 deletions(-) create mode 100644 client/types/__init__.py create mode 100644 client/types/api_nodes.py create mode 100644 client/types/blocks.py create mode 100644 client/types/evm.py create mode 100644 client/types/node.py create mode 100644 client/types/peers.py create mode 100644 client/types/receipts.py create mode 100644 client/types/tokens.py create mode 100644 client/types/transactions.py create mode 100644 client/types/validators.py create mode 100644 client/types/votes.py create mode 100644 client/types/wallets.py diff --git a/client/api/api_nodes.py b/client/api/api_nodes.py index f4fca6a..4948c06 100644 --- a/client/api/api_nodes.py +++ b/client/api/api_nodes.py @@ -1,7 +1,10 @@ +from typing import Optional + from client.resource import Resource +from client.types.api_nodes import ApiNodesQuery class ApiNodes(Resource): - def all(self, query={}): + def all(self, query: Optional[ApiNodesQuery] = None): return self.with_endpoint('api').request_get('api-nodes', query) diff --git a/client/api/blocks.py b/client/api/blocks.py index 2203f5b..a8cf6e1 100644 --- a/client/api/blocks.py +++ b/client/api/blocks.py @@ -1,13 +1,16 @@ +from typing import Optional + from client.resource import Resource +from client.types.blocks import BlockTransactionsQuery, BlocksQuery class Blocks(Resource): - def all(self, query={}): + def all(self, query: Optional[BlocksQuery] = None): return self.with_endpoint('api').request_get('blocks', query) - def get(self, block_id): - return self.with_endpoint('api').request_get(f'blocks/{block_id}') + def get(self, block_hash: str): + return self.with_endpoint('api').request_get(f'blocks/{block_hash}') def first(self): return self.with_endpoint('api').request_get('blocks/first') @@ -15,7 +18,11 @@ def first(self): def last(self): return self.with_endpoint('api').request_get('blocks/last') - def transactions(self, block_id, query={}): + def transactions( + self, + block_hash: str, + query: Optional[BlockTransactionsQuery] = None, + ): return self.with_endpoint('api').request_get( - f'blocks/{block_id}/transactions', query + f'blocks/{block_hash}/transactions', query ) diff --git a/client/api/commits.py b/client/api/commits.py index b94206c..3a98e26 100644 --- a/client/api/commits.py +++ b/client/api/commits.py @@ -3,5 +3,5 @@ class Commits(Resource): - def show(self, block_number): - return self.with_endpoint('api').request_get(f'commits/{block_number}') + def get(self, height: int): + return self.with_endpoint('api').request_get(f'commits/{height}') diff --git a/client/api/evm.py b/client/api/evm.py index 9386134..c8189c3 100644 --- a/client/api/evm.py +++ b/client/api/evm.py @@ -1,10 +1,10 @@ -from typing import Any from client.resource import Resource +from client.types.evm import EvmBodyPartialParams class EVM(Resource): - def call(self, params: dict[str, Any]): + def call(self, payload: EvmBodyPartialParams): return self.with_endpoint('evm').request_post('', { 'jsonrpc': "2.0", - **params, + **payload, }) diff --git a/client/api/node.py b/client/api/node.py index 9bb0304..c0b6fb8 100644 --- a/client/api/node.py +++ b/client/api/node.py @@ -1,4 +1,7 @@ +from typing import Optional + from client.resource import Resource +from client.types.node import NodeFeesQuery class Node(Resource): @@ -19,5 +22,5 @@ def crypto(self): 'node/configuration/crypto' ) - def fees(self, query={}): + def fees(self, query: Optional[NodeFeesQuery] = None): return self.with_endpoint('api').request_get('node/fees', query) diff --git a/client/api/peers.py b/client/api/peers.py index 17c9389..7d660da 100644 --- a/client/api/peers.py +++ b/client/api/peers.py @@ -1,10 +1,13 @@ +from typing import Optional + from client.resource import Resource +from client.types.peers import PeersQuery class Peers(Resource): - def all(self, query={}): + def all(self, query: Optional[PeersQuery] = None): return self.with_endpoint('api').request_get('peers', query) - def get(self, ip): + def get(self, ip: str): return self.with_endpoint('api').request_get(f'peers/{ip}') diff --git a/client/api/receipts.py b/client/api/receipts.py index 903585a..73944fa 100644 --- a/client/api/receipts.py +++ b/client/api/receipts.py @@ -1,11 +1,23 @@ +from typing import Optional + from client.resource import Resource +from client.types.receipts import ReceiptContractsQuery, ReceiptQuery, ReceiptsQuery class Receipts(Resource): - def all(self, query={}): + def all(self, query: Optional[ReceiptsQuery] = None): return self.with_endpoint('api').request_get('receipts', query) - def get(self, transaction_hash: str): + def get( + self, + transaction_hash: str, + query: Optional[ReceiptQuery] = None, + ): + return self.with_endpoint('api').request_get( + f'receipts/{transaction_hash}', query + ) + + def contracts(self, query: Optional[ReceiptContractsQuery] = None): return self.with_endpoint('api').request_get( - f'receipts/{transaction_hash}' + 'receipts/contracts', query ) diff --git a/client/api/rounds.py b/client/api/rounds.py index 53ba93f..143dcf9 100644 --- a/client/api/rounds.py +++ b/client/api/rounds.py @@ -1,15 +1,18 @@ +from typing import Optional + from client.resource import Resource +from client.types import PaginatedQuery class Rounds(Resource): - def all(self, query={}): + def all(self, query: Optional[PaginatedQuery] = None): return self.with_endpoint('api').request_get('rounds', query) - def show(self, round_id): + def show(self, round_id: str): return self.with_endpoint('api').request_get(f'rounds/{round_id}') - def validators(self, round_id): + def validators(self, round_id: str): return self.with_endpoint('api').request_get( f'rounds/{round_id}/validators' ) diff --git a/client/api/tokens.py b/client/api/tokens.py index 6ee33ad..e8c38e5 100644 --- a/client/api/tokens.py +++ b/client/api/tokens.py @@ -1,27 +1,54 @@ +from typing import Optional + from client.resource import Resource +from client.types import PaginatedQuery +from client.types.tokens import TokenApprovalsQuery, TokenLookupQuery, TokenTransfersQuery, TokensQuery class Tokens(Resource): - def all(self, query={}): + def all(self, query: Optional[TokensQuery] = None): return self.with_endpoint('api').request_get('tokens', query) - def get(self, address): + def transfers(self, query: Optional[TokenTransfersQuery] = None): return self.with_endpoint('api').request_get( - f'tokens/{address}' + 'tokens/transfers', query + ) + + def approvals(self, query: Optional[TokenApprovalsQuery] = None): + return self.with_endpoint('api').request_get( + 'tokens/approvals', query + ) + + def whitelist(self, query: Optional[PaginatedQuery] = None): + return self.with_endpoint('api').request_get( + 'tokens/whitelist', query ) - def holders(self, address, query={}): + def get(self, address: str): return self.with_endpoint('api').request_get( - f'tokens/{address}/holders', query + f'tokens/{address}' ) - def transfers_by_token(self, address, query={}): + def transfers_for( + self, + address: str, + query: Optional[TokenLookupQuery] = None, + ): return self.with_endpoint('api').request_get( f'tokens/{address}/transfers', query ) - def transfers(self, query={}): + def approvals_for( + self, + address: str, + query: Optional[TokenLookupQuery] = None, + ): return self.with_endpoint('api').request_get( - 'tokens/transfers', query + f'tokens/{address}/approvals', query + ) + + def holders_for(self, address: str): + return self.with_endpoint('api').request_get( + f'tokens/{address}/holders' ) diff --git a/client/api/transactions.py b/client/api/transactions.py index 7587a19..ee2cb30 100644 --- a/client/api/transactions.py +++ b/client/api/transactions.py @@ -1,30 +1,40 @@ +from typing import Optional, Sequence + from client.resource import Resource +from client.types.transactions import TransactionGetQuery, TransactionsQuery, UnconfirmedTransactionsQuery class Transactions(Resource): - def all(self, query={}): + def all(self, query: Optional[TransactionsQuery] = None): return self.with_endpoint('api').request_get( 'transactions', query ) - def create(self, transactions): + def create(self, transactions: Sequence[str]): return self.with_endpoint('transactions').request_post( 'transactions', data={'transactions': transactions} ) - def get(self, transaction_id): + def get( + self, + transaction_id: str, + query: Optional[TransactionGetQuery] = None, + ): return self.with_endpoint('api').request_get( - f'transactions/{transaction_id}' + f'transactions/{transaction_id}', query ) - def all_unconfirmed(self, query={}): - return self.with_endpoint('api').request_get( + def all_unconfirmed( + self, + query: Optional[UnconfirmedTransactionsQuery] = None, + ): + return self.with_endpoint('transactions').request_get( 'transactions/unconfirmed', query ) - def get_unconfirmed(self, transaction_id): - return self.with_endpoint('api').request_get( + def get_unconfirmed(self, transaction_id: str): + return self.with_endpoint('transactions').request_get( f'transactions/unconfirmed/{transaction_id}' ) diff --git a/client/api/validators.py b/client/api/validators.py index eed26ab..009c677 100644 --- a/client/api/validators.py +++ b/client/api/validators.py @@ -1,22 +1,35 @@ +from typing import Optional + +from client.api.blocks import BlocksQuery +from client.api.wallets import WalletsQuery from client.resource import Resource +from client.types.validators import ValidatorsQuery class Validators(Resource): - def all(self, query={}): + def all(self, query: Optional[ValidatorsQuery] = None): return self.with_endpoint('api').request_get('validators', query) - def get(self, validator_id): + def get(self, validator_id: str): return self.with_endpoint('api').request_get( f'validators/{validator_id}' ) - def blocks(self, validator_id, query={}): + def blocks( + self, + validator_id: str, + query: Optional[BlocksQuery] = None, + ): return self.with_endpoint('api').request_get( f'validators/{validator_id}/blocks', query ) - def voters(self, validator_id, query={}): + def voters( + self, + validator_id: str, + query: Optional[WalletsQuery] = None, + ): return self.with_endpoint('api').request_get( f'validators/{validator_id}/voters', query ) diff --git a/client/api/votes.py b/client/api/votes.py index 4bc7632..4c8c73a 100644 --- a/client/api/votes.py +++ b/client/api/votes.py @@ -1,10 +1,13 @@ +from typing import Optional + from client.resource import Resource +from client.types.votes import VoteQuery, VotesQuery class Votes(Resource): - def all(self, query={}): + def all(self, query: Optional[VotesQuery] = None): return self.with_endpoint('api').request_get('votes', query) - def get(self, vote_id): - return self.with_endpoint('api').request_get(f'votes/{vote_id}') + def get(self, vote_id: str, query: Optional[VoteQuery] = None): + return self.with_endpoint('api').request_get(f'votes/{vote_id}', query) diff --git a/client/api/wallets.py b/client/api/wallets.py index b070ec3..26c5def 100644 --- a/client/api/wallets.py +++ b/client/api/wallets.py @@ -1,45 +1,69 @@ +from typing import Optional + from client.resource import Resource +from client.types.transactions import TransactionsQuery +from client.types.wallets import WalletTokensForQuery, WalletTokensQuery, WalletsQuery class Wallets(Resource): - def all(self, query={}): + def all(self, query: Optional[WalletsQuery] = None): return self.with_endpoint('api').request_get('wallets', query) - def top(self, query={}): + def top(self, query: Optional[WalletsQuery] = None): return self.with_endpoint('api').request_get('wallets/top', query) - def get(self, wallet_id): + def get(self, wallet_id: str): return self.with_endpoint('api').request_get( f'wallets/{wallet_id}' ) - def transactions(self, wallet_id, query={}): + def transactions( + self, + wallet_id: str, + query: Optional[TransactionsQuery] = None, + ): return self.with_endpoint('api').request_get( f'wallets/{wallet_id}/transactions', query ) - def sent_transactions(self, wallet_id, query={}): + def sent_transactions( + self, + wallet_id: str, + query: Optional[TransactionsQuery] = None, + ): return self.with_endpoint('api').request_get( f'wallets/{wallet_id}/transactions/sent', query ) - def received_transactions(self, wallet_id, query={}): + def received_transactions( + self, + wallet_id: str, + query: Optional[TransactionsQuery] = None, + ): return self.with_endpoint('api').request_get( f'wallets/{wallet_id}/transactions/received', query ) - def votes(self, wallet_id, query={}): + def votes( + self, + wallet_id: str, + query: Optional[TransactionsQuery] = None, + ): return self.with_endpoint('api').request_get( f'wallets/{wallet_id}/votes', query ) - def tokens(self, wallet_id, query={}): + def tokens_for( + self, + address: str, + query: Optional[WalletTokensForQuery] = None, + ): return self.with_endpoint('api').request_get( - f'wallets/{wallet_id}/tokens', query + f'wallets/{address}/tokens', query ) - def tokens_for(self, query={}): + def tokens(self, query: Optional[WalletTokensQuery] = None): return self.with_endpoint('api').request_get( 'wallets/tokens', query ) diff --git a/client/types/__init__.py b/client/types/__init__.py new file mode 100644 index 0000000..0890872 --- /dev/null +++ b/client/types/__init__.py @@ -0,0 +1,12 @@ +from typing import TypedDict + + +PaginatedQuery = TypedDict( + 'PaginatedQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + }, + total=False, +) diff --git a/client/types/api_nodes.py b/client/types/api_nodes.py new file mode 100644 index 0000000..17579c3 --- /dev/null +++ b/client/types/api_nodes.py @@ -0,0 +1,12 @@ +from typing import TypedDict + + +ApiNodesQuery = TypedDict( + 'ApiNodesQuery', + { + 'ip': str, + 'orderBy': str, + 'version': str, + }, + total=False, +) diff --git a/client/types/blocks.py b/client/types/blocks.py new file mode 100644 index 0000000..046c046 --- /dev/null +++ b/client/types/blocks.py @@ -0,0 +1,32 @@ +from typing import TypedDict + + +BlocksQuery = TypedDict( + 'BlocksQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'transform': bool, + 'orderBy': str, + 'id': str, + 'number': int, + 'height.from': int, + 'height.to': int, + 'timestamp': int, + 'timestamp.from': int, + 'timestamp.to': int, + }, + total=False, +) + +BlockTransactionsQuery = TypedDict( + 'BlockTransactionsQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'orderBy': str, + }, + total=False, +) diff --git a/client/types/evm.py b/client/types/evm.py new file mode 100644 index 0000000..613188b --- /dev/null +++ b/client/types/evm.py @@ -0,0 +1,17 @@ +from typing import Literal, Optional, TypedDict + + +PayloadData = dict[str, str | int | float] + +BlockParameter = Literal["earliest", "latest", "safe", "finalized", "pending"] + +EvmBodyPartialParams = TypedDict( + 'EvmBodyPartialParams', + { + 'jsonrpc': Optional[str], + 'method': str, + 'params': list[PayloadData] | list[PayloadData | BlockParameter | str], + 'id': Optional[int | float], + }, + total=False, +) diff --git a/client/types/node.py b/client/types/node.py new file mode 100644 index 0000000..3a942fc --- /dev/null +++ b/client/types/node.py @@ -0,0 +1,10 @@ +from typing import TypedDict + + +NodeFeesQuery = TypedDict( + 'NodeFeesQuery', + { + 'days': int, + }, + total=False, +) diff --git a/client/types/peers.py b/client/types/peers.py new file mode 100644 index 0000000..4eb5dd5 --- /dev/null +++ b/client/types/peers.py @@ -0,0 +1,14 @@ +from typing import TypedDict + + +PeersQuery = TypedDict( + 'PeersQuery', + { + 'page': int, + 'limit': int, + 'ip': str, + 'orderBy': str, + 'version': str, + }, + total=False, +) diff --git a/client/types/receipts.py b/client/types/receipts.py new file mode 100644 index 0000000..974fa50 --- /dev/null +++ b/client/types/receipts.py @@ -0,0 +1,39 @@ +from typing import TypedDict + + +ReceiptsQuery = TypedDict( + 'ReceiptsQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'from': str, + 'to': str, + 'fullReceipt': bool, + 'includeTokens': bool, + 'transactionHash': str, + }, + total=False, +) + +ReceiptQuery = TypedDict( + 'ReceiptQuery', + { + 'fullReceipt': bool, + 'includeTokens': bool, + }, + total=False, +) + +ReceiptContractsQuery = TypedDict( + 'ReceiptContractsQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'from': str, + 'fullReceipt': bool, + 'includeTokens': bool, + }, + total=False, +) diff --git a/client/types/tokens.py b/client/types/tokens.py new file mode 100644 index 0000000..eed242d --- /dev/null +++ b/client/types/tokens.py @@ -0,0 +1,59 @@ +from typing import List, TypedDict + + +TokensQuery = TypedDict( + 'TokensQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'ignoreWhitelist': bool, + 'name': str, + 'whitelist': List[str], + }, + total=False, +) + +TokenLookupQuery = TypedDict( + 'TokenLookupQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'from': str, + 'to': str, + 'transactionHash': str, + }, + total=False, +) + +TokenTransfersQuery = TypedDict( + 'TokenTransfersQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'from': str, + 'to': str, + 'transactionHash': str, + 'addresses': List[str], + 'ignoreWhitelist': bool, + 'whitelist': List[str], + }, + total=False, +) + +TokenApprovalsQuery = TypedDict( + 'TokenApprovalsQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'from': str, + 'to': str, + 'transactionHash': str, + 'addresses': List[str], + 'whitelist': List[str], + }, + total=False, +) diff --git a/client/types/transactions.py b/client/types/transactions.py new file mode 100644 index 0000000..53aa2c9 --- /dev/null +++ b/client/types/transactions.py @@ -0,0 +1,54 @@ +from typing import List, TypedDict + + +TransactionsQuery = TypedDict( + 'TransactionsQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'address': str, + 'asset': str, + 'blockHash': str, + 'from': str, + 'gasPrice': int, + 'hash': str, + 'nonce': int, + 'senderId': str, + 'senderPublicKey': str, + 'timestamp': int, + 'to': str, + 'transactionIndex': int, + 'value': int, + 'fullReceipt': bool, + 'includeTokens': bool, + 'orderBy': str, + }, + total=False, +) + +UnconfirmedTransactionsQuery = TypedDict( + 'UnconfirmedTransactionsQuery', + { + 'page': int, + 'limit': int, + 'orderBy': str, + }, + total=False, +) + +TransactionGetQuery = TypedDict( + 'TransactionGetQuery', + { + 'fullReceipt': bool, + 'includeTokens': bool, + }, + total=False, +) + +TransactionCreateParams = TypedDict( + 'TransactionCreateParams', + { + 'transactions': List[str], + }, +) diff --git a/client/types/validators.py b/client/types/validators.py new file mode 100644 index 0000000..48c447d --- /dev/null +++ b/client/types/validators.py @@ -0,0 +1,39 @@ +from typing import TypedDict + + +ValidatorsQuery = TypedDict( + 'ValidatorsQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'address': str, + 'attributes': str, + 'blocks.last.hash': str, + 'blocks.last.number': int, + 'blocks.produced': int, + 'forged.fees': int, + 'forged.fees.from': int, + 'forged.fees.to': int, + 'forged.rewards': int, + 'forged.rewards.from': int, + 'forged.rewards.to': int, + 'forged.total': int, + 'forged.total.from': int, + 'forged.total.to': int, + 'isResigned': bool, + 'production.approval': int, + 'production.approval.from': int, + 'production.approval.to': int, + 'publicKey': str, + 'rank': int, + 'rank.from': int, + 'rank.to': int, + 'username': str, + 'votes': int, + 'votes.from': int, + 'votes.to': int, + 'orderBy': str, + }, + total=False, +) diff --git a/client/types/votes.py b/client/types/votes.py new file mode 100644 index 0000000..43d1d23 --- /dev/null +++ b/client/types/votes.py @@ -0,0 +1,35 @@ +from typing import TypedDict + + +VotesQuery = TypedDict( + 'VotesQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'address': str, + 'blockHash': str, + 'data': str, + 'from': str, + 'gasPrice': int, + 'hash': str, + 'nonce': int, + 'senderId': str, + 'senderPublicKey': str, + 'timestamp': int, + 'to': str, + 'transactionIndex': int, + 'value': int, + 'fullReceipt': bool, + 'orderBy': str, + }, + total=False, +) + +VoteQuery = TypedDict( + 'VoteQuery', + { + 'fullReceipt': bool, + }, + total=False, +) diff --git a/client/types/wallets.py b/client/types/wallets.py new file mode 100644 index 0000000..b9ece9f --- /dev/null +++ b/client/types/wallets.py @@ -0,0 +1,47 @@ +from typing import List, TypedDict + + +WalletsQuery = TypedDict( + 'WalletsQuery', + { + 'page': int, + 'limit': int, + 'offset': int, + 'address': str, + 'publicKey': str, + 'balance': int, + 'balance.from': int, + 'balance.to': int, + 'nonce': int, + 'nonce.from': int, + 'nonce.to': int, + 'attributes': str, + 'orderBy': str, + }, + total=False, +) + +WalletTokensForQuery = TypedDict( + 'WalletTokensForQuery', + { + 'addresses': List[str], + 'ignoreWhitelist': bool, + 'minBalance': int, + 'name': str, + 'whitelist': List[str], + }, + total=False, +) + +WalletTokensQuery = TypedDict( + 'WalletTokensQuery', + { + 'addresses': List[str], + 'ignoreWhitelist': bool, + 'page': int, + 'limit': int, + 'minBalance': int, + 'whitelist': List[str], + }, + total=False, +) diff --git a/tests/api/test_blocks.py b/tests/api/test_blocks.py index 6073d83..da7f8df 100644 --- a/tests/api/test_blocks.py +++ b/tests/api/test_blocks.py @@ -32,7 +32,7 @@ def test_all_calls_correct_url_with_params(): 'page': 5, 'limit': 69, 'orderBy': 'timestamp.epoch', - 'height': 6838329, + 'number': 6838329, }) assert len(responses.calls) == 1 url = responses.calls[0].request.url @@ -40,7 +40,7 @@ def test_all_calls_correct_url_with_params(): assert 'page=5' in url assert 'limit=69' in url assert 'orderBy=timestamp.epoch' in url - assert 'height=6838329' in url + assert 'number=6838329' in url def test_get_calls_correct_url(): diff --git a/tests/api/test_commits.py b/tests/api/test_commits.py index dc50112..28c88e3 100644 --- a/tests/api/test_commits.py +++ b/tests/api/test_commits.py @@ -12,6 +12,6 @@ def test_show_calls_correct_url(): ) client = ArkClient('http://127.0.0.1:4002/api') - client.commits.show(1) + client.commits.get(1) assert len(responses.calls) == 1 assert responses.calls[0].request.url == 'http://127.0.0.1:4002/api/commits/1' diff --git a/tests/api/test_receipts.py b/tests/api/test_receipts.py index c1e20b1..bc16f25 100644 --- a/tests/api/test_receipts.py +++ b/tests/api/test_receipts.py @@ -53,3 +53,52 @@ def test_get_calls_correct_url(): assert responses.calls[0].request.url == ( f'http://127.0.0.1:4002/api/receipts/{transaction_hash}' ) + + +def test_get_calls_correct_url_with_params(): + transaction_hash = '12345' + responses.add( + responses.GET, + f'http://127.0.0.1:4002/api/receipts/{transaction_hash}', + json={'success': True}, + status=200 + ) + + client = ArkClient('http://127.0.0.1:4002/api') + client.receipts.get(transaction_hash, {'format': 'hex'}) + assert len(responses.calls) == 1 + url = responses.calls[0].request.url + assert url.startswith(f'http://127.0.0.1:4002/api/receipts/{transaction_hash}?') + assert 'format=hex' in url + + +def test_contracts_calls_correct_url(): + responses.add( + responses.GET, + 'http://127.0.0.1:4002/api/receipts/contracts', + json={'success': True}, + status=200 + ) + + client = ArkClient('http://127.0.0.1:4002/api') + client.receipts.contracts() + assert len(responses.calls) == 1 + assert responses.calls[0].request.url == ( + 'http://127.0.0.1:4002/api/receipts/contracts' + ) + + +def test_contracts_calls_correct_url_with_params(): + responses.add( + responses.GET, + 'http://127.0.0.1:4002/api/receipts/contracts', + json={'success': True}, + status=200 + ) + + client = ArkClient('http://127.0.0.1:4002/api') + client.receipts.contracts({'status': 'success'}) + assert len(responses.calls) == 1 + url = responses.calls[0].request.url + assert url.startswith('http://127.0.0.1:4002/api/receipts/contracts?') + assert 'status=success' in url diff --git a/tests/api/test_tokens.py b/tests/api/test_tokens.py index 3aa6ec5..7505f96 100644 --- a/tests/api/test_tokens.py +++ b/tests/api/test_tokens.py @@ -53,7 +53,7 @@ def test_get_calls_correct_url(): ) -def test_holders_calls_correct_url(): +def test_holders_for_calls_correct_url(): address = '0x1234567890abcdef' responses.add( responses.GET, @@ -63,65 +63,82 @@ def test_holders_calls_correct_url(): ) client = ArkClient('http://127.0.0.1:4002/api') - client.tokens.holders(address) + client.tokens.holders_for(address) assert len(responses.calls) == 1 assert responses.calls[0].request.url == ( 'http://127.0.0.1:4002/api/tokens/0x1234567890abcdef/holders' ) -def test_holders_calls_correct_url_with_params(): +def test_transfers_for_calls_correct_url(): address = '0x1234567890abcdef' responses.add( responses.GET, - 'http://127.0.0.1:4002/api/tokens/{}/holders'.format(address), + 'http://127.0.0.1:4002/api/tokens/{}/transfers'.format(address), + json={'success': True}, + status=200 + ) + + client = ArkClient('http://127.0.0.1:4002/api') + client.tokens.transfers_for(address) + assert len(responses.calls) == 1 + assert responses.calls[0].request.url == ( + 'http://127.0.0.1:4002/api/tokens/0x1234567890abcdef/transfers' + ) + + +def test_transfers_for_calls_correct_url_with_params(): + address = '0x1234567890abcdef' + responses.add( + responses.GET, + 'http://127.0.0.1:4002/api/tokens/{}/transfers'.format(address), json={'success': True}, status=200 ) client = ArkClient('http://127.0.0.1:4002/api') - client.tokens.holders(address, {'page': 3, 'limit': 50}) + client.tokens.transfers_for(address, {'page': 2, 'limit': 25}) assert len(responses.calls) == 1 url = responses.calls[0].request.url assert url.startswith( - 'http://127.0.0.1:4002/api/tokens/0x1234567890abcdef/holders?' + 'http://127.0.0.1:4002/api/tokens/0x1234567890abcdef/transfers?' ) - assert 'page=3' in url - assert 'limit=50' in url + assert 'page=2' in url + assert 'limit=25' in url -def test_transfers_by_token_calls_correct_url(): +def test_approvals_for_calls_correct_url(): address = '0x1234567890abcdef' responses.add( responses.GET, - 'http://127.0.0.1:4002/api/tokens/{}/transfers'.format(address), + 'http://127.0.0.1:4002/api/tokens/{}/approvals'.format(address), json={'success': True}, status=200 ) client = ArkClient('http://127.0.0.1:4002/api') - client.tokens.transfers_by_token(address) + client.tokens.approvals_for(address) assert len(responses.calls) == 1 assert responses.calls[0].request.url == ( - 'http://127.0.0.1:4002/api/tokens/0x1234567890abcdef/transfers' + 'http://127.0.0.1:4002/api/tokens/0x1234567890abcdef/approvals' ) -def test_transfers_by_token_calls_correct_url_with_params(): +def test_approvals_for_calls_correct_url_with_params(): address = '0x1234567890abcdef' responses.add( responses.GET, - 'http://127.0.0.1:4002/api/tokens/{}/transfers'.format(address), + 'http://127.0.0.1:4002/api/tokens/{}/approvals'.format(address), json={'success': True}, status=200 ) client = ArkClient('http://127.0.0.1:4002/api') - client.tokens.transfers_by_token(address, {'page': 2, 'limit': 25}) + client.tokens.approvals_for(address, {'page': 2, 'limit': 25}) assert len(responses.calls) == 1 url = responses.calls[0].request.url assert url.startswith( - 'http://127.0.0.1:4002/api/tokens/0x1234567890abcdef/transfers?' + 'http://127.0.0.1:4002/api/tokens/0x1234567890abcdef/approvals?' ) assert 'page=2' in url assert 'limit=25' in url @@ -160,3 +177,73 @@ def test_transfers_calls_correct_url_with_params(): ) assert 'page=1' in url assert 'limit=10' in url + + +def test_approvals_calls_correct_url(): + responses.add( + responses.GET, + 'http://127.0.0.1:4002/api/tokens/approvals', + json={'success': True}, + status=200 + ) + + client = ArkClient('http://127.0.0.1:4002/api') + client.tokens.approvals() + assert len(responses.calls) == 1 + assert responses.calls[0].request.url == ( + 'http://127.0.0.1:4002/api/tokens/approvals' + ) + + +def test_approvals_calls_correct_url_with_params(): + responses.add( + responses.GET, + 'http://127.0.0.1:4002/api/tokens/approvals', + json={'success': True}, + status=200 + ) + + client = ArkClient('http://127.0.0.1:4002/api') + client.tokens.approvals({'page': 1, 'limit': 10}) + assert len(responses.calls) == 1 + url = responses.calls[0].request.url + assert url.startswith( + 'http://127.0.0.1:4002/api/tokens/approvals?' + ) + assert 'page=1' in url + assert 'limit=10' in url + + +def test_whitelist_calls_correct_url(): + responses.add( + responses.GET, + 'http://127.0.0.1:4002/api/tokens/whitelist', + json={'success': True}, + status=200 + ) + + client = ArkClient('http://127.0.0.1:4002/api') + client.tokens.whitelist() + assert len(responses.calls) == 1 + assert responses.calls[0].request.url == ( + 'http://127.0.0.1:4002/api/tokens/whitelist' + ) + + +def test_whitelist_calls_correct_url_with_params(): + responses.add( + responses.GET, + 'http://127.0.0.1:4002/api/tokens/whitelist', + json={'success': True}, + status=200 + ) + + client = ArkClient('http://127.0.0.1:4002/api') + client.tokens.whitelist({'page': 3, 'limit': 50}) + assert len(responses.calls) == 1 + url = responses.calls[0].request.url + assert url.startswith( + 'http://127.0.0.1:4002/api/tokens/whitelist?' + ) + assert 'page=3' in url + assert 'limit=50' in url diff --git a/tests/api/test_transactions.py b/tests/api/test_transactions.py index 25cc196..b0d4fe1 100644 --- a/tests/api/test_transactions.py +++ b/tests/api/test_transactions.py @@ -87,28 +87,36 @@ def test_get_calls_correct_url(): def test_all_unconfirmed_calls_correct_url(): responses.add( responses.GET, - 'http://127.0.0.1:4002/api/transactions/unconfirmed', + 'http://127.0.0.1:4002/tx/api/transactions/unconfirmed', json={'success': True}, status=200 ) - client = ArkClient('http://127.0.0.1:4002/api') + client = ArkClient({ + 'api': 'http://127.0.0.1:4002/api', + 'transactions': 'http://127.0.0.1:4002/tx/api', + 'evm': None, + }) client.transactions.all_unconfirmed() assert len(responses.calls) == 1 assert responses.calls[0].request.url == ( - 'http://127.0.0.1:4002/api/transactions/unconfirmed' + 'http://127.0.0.1:4002/tx/api/transactions/unconfirmed' ) def test_all_unconfirmed_calls_correct_url_with_params(): responses.add( responses.GET, - 'http://127.0.0.1:4002/api/transactions/unconfirmed', + 'http://127.0.0.1:4002/tx/api/transactions/unconfirmed', json={'success': True}, status=200 ) - client = ArkClient('http://127.0.0.1:4002/api') + client = ArkClient({ + 'api': 'http://127.0.0.1:4002/api', + 'transactions': 'http://127.0.0.1:4002/tx/api', + 'evm': None, + }) client.transactions.all_unconfirmed({ 'page': 5, 'limit': 69, @@ -117,7 +125,7 @@ def test_all_unconfirmed_calls_correct_url_with_params(): assert len(responses.calls) == 1 url = responses.calls[0].request.url assert url.startswith( - 'http://127.0.0.1:4002/api/transactions/unconfirmed?' + 'http://127.0.0.1:4002/tx/api/transactions/unconfirmed?' ) assert 'page=5' in url assert 'limit=69' in url @@ -129,18 +137,22 @@ def test_get_unconfirmed_calls_correct_url(): responses.add( responses.GET, - 'http://127.0.0.1:4002/api/transactions/unconfirmed/{}'.format( + 'http://127.0.0.1:4002/tx/api/transactions/unconfirmed/{}'.format( transaction_id ), json={'success': True}, status=200 ) - client = ArkClient('http://127.0.0.1:4002/api') + client = ArkClient({ + 'api': 'http://127.0.0.1:4002/api', + 'transactions': 'http://127.0.0.1:4002/tx/api', + 'evm': None, + }) client.transactions.get_unconfirmed(transaction_id) assert len(responses.calls) == 1 assert responses.calls[0].request.url == ( - 'http://127.0.0.1:4002/api/transactions/unconfirmed/12345' + 'http://127.0.0.1:4002/tx/api/transactions/unconfirmed/12345' ) diff --git a/tests/api/test_wallets.py b/tests/api/test_wallets.py index c9b0ed2..e6f602a 100644 --- a/tests/api/test_wallets.py +++ b/tests/api/test_wallets.py @@ -257,7 +257,7 @@ def test_votes_calls_correct_url_with_params(): assert 'limit=69' in url -def test_tokens_calls_correct_url(): +def test_tokens_for_calls_correct_url(): wallet_id = '12345' responses.add( responses.GET, @@ -267,14 +267,14 @@ def test_tokens_calls_correct_url(): ) client = ArkClient('http://127.0.0.1:4002/api') - client.wallets.tokens(wallet_id) + client.wallets.tokens_for(wallet_id) assert len(responses.calls) == 1 assert responses.calls[0].request.url == ( 'http://127.0.0.1:4002/api/wallets/12345/tokens' ) -def test_tokens_calls_correct_url_with_params(): +def test_tokens_for_calls_correct_url_with_params(): wallet_id = '12345' responses.add( responses.GET, @@ -284,7 +284,7 @@ def test_tokens_calls_correct_url_with_params(): ) client = ArkClient('http://127.0.0.1:4002/api') - client.wallets.tokens(wallet_id, {'page': 5, 'limit': 69}) + client.wallets.tokens_for(wallet_id, {'page': 5, 'limit': 69}) assert len(responses.calls) == 1 url = responses.calls[0].request.url assert url.startswith( @@ -294,7 +294,7 @@ def test_tokens_calls_correct_url_with_params(): assert 'limit=69' in url -def test_tokens_for_calls_correct_url(): +def test_tokens_calls_correct_url(): responses.add( responses.GET, 'http://127.0.0.1:4002/api/wallets/tokens', @@ -303,9 +303,7 @@ def test_tokens_for_calls_correct_url(): ) client = ArkClient('http://127.0.0.1:4002/api') - client.wallets.tokens_for({ - 'addresses': '0xabc,0xdef', - }) + client.wallets.tokens({'addresses': '0xabc,0xdef'}) assert len(responses.calls) == 1 url = responses.calls[0].request.url assert url.startswith( @@ -314,7 +312,7 @@ def test_tokens_for_calls_correct_url(): assert 'addresses=0xabc%2C0xdef' in url -def test_tokens_for_calls_correct_url_with_params(): +def test_tokens_calls_correct_url_with_params(): responses.add( responses.GET, 'http://127.0.0.1:4002/api/wallets/tokens', @@ -323,7 +321,7 @@ def test_tokens_for_calls_correct_url_with_params(): ) client = ArkClient('http://127.0.0.1:4002/api') - client.wallets.tokens_for({ + client.wallets.tokens({ 'addresses': '0xabc,0xdef', 'page': 2, 'limit': 50, From d8e527e574701af3da5edece3a9905023cca2f7f Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:49:15 +0000 Subject: [PATCH 2/5] refactor: method response typehints --- client/api/api_nodes.py | 5 +- client/api/blockchain.py | 4 +- client/api/blocks.py | 14 +-- client/api/commits.py | 4 +- client/api/contracts.py | 6 +- client/api/node.py | 28 +++--- client/api/peers.py | 7 +- client/api/receipts.py | 10 +- client/api/rounds.py | 9 +- client/api/tokens.py | 31 +++++-- client/api/transactions.py | 22 +++-- client/api/validators.py | 14 +-- client/api/votes.py | 6 +- client/api/wallets.py | 24 ++--- client/types/__init__.py | 43 ++++++--- client/types/api_nodes.py | 21 +++-- client/types/blockchain.py | 11 +++ client/types/blocks.py | 59 +++++++++--- client/types/commits.py | 7 ++ client/types/contracts.py | 15 +++ client/types/evm.py | 18 ++-- client/types/node.py | 172 +++++++++++++++++++++++++++++++++-- client/types/peers.py | 33 ++++--- client/types/receipts.py | 33 +++++-- client/types/rounds.py | 8 ++ client/types/tokens.py | 81 +++++++++++++++-- client/types/transactions.py | 111 +++++++++++++++++----- client/types/validators.py | 1 + client/types/votes.py | 11 +-- client/types/wallets.py | 70 +++++++++----- 30 files changed, 673 insertions(+), 205 deletions(-) create mode 100644 client/types/blockchain.py create mode 100644 client/types/commits.py create mode 100644 client/types/contracts.py create mode 100644 client/types/rounds.py diff --git a/client/api/api_nodes.py b/client/api/api_nodes.py index 4948c06..d2295e0 100644 --- a/client/api/api_nodes.py +++ b/client/api/api_nodes.py @@ -1,10 +1,11 @@ from typing import Optional from client.resource import Resource -from client.types.api_nodes import ApiNodesQuery +from client.types import PaginatedResponse +from client.types.api_nodes import ApiNodeResponse, ApiNodesQuery class ApiNodes(Resource): - def all(self, query: Optional[ApiNodesQuery] = None): + def all(self, query: Optional[ApiNodesQuery] = None) -> PaginatedResponse[ApiNodeResponse]: return self.with_endpoint('api').request_get('api-nodes', query) diff --git a/client/api/blockchain.py b/client/api/blockchain.py index ee5797e..fa3f5a0 100644 --- a/client/api/blockchain.py +++ b/client/api/blockchain.py @@ -1,7 +1,9 @@ from client.resource import Resource +from client.types import Response +from client.types.blockchain import BlockchainResponse class Blockchain(Resource): - def blockchain(self): + def blockchain(self) -> Response[BlockchainResponse]: return self.with_endpoint('api').request_get('blockchain') diff --git a/client/api/blocks.py b/client/api/blocks.py index a8cf6e1..a4cf23b 100644 --- a/client/api/blocks.py +++ b/client/api/blocks.py @@ -1,28 +1,30 @@ from typing import Optional from client.resource import Resource -from client.types.blocks import BlockTransactionsQuery, BlocksQuery +from client.types import PaginatedResponse, Response +from client.types.blocks import BlockResponse, BlockTransactionsQuery, BlocksQuery +from client.types.transactions import TransactionResponse class Blocks(Resource): - def all(self, query: Optional[BlocksQuery] = None): + def all(self, query: Optional[BlocksQuery] = None) -> PaginatedResponse[BlockResponse]: return self.with_endpoint('api').request_get('blocks', query) - def get(self, block_hash: str): + def get(self, block_hash: str) -> Response[BlockResponse]: return self.with_endpoint('api').request_get(f'blocks/{block_hash}') - def first(self): + def first(self) -> Response[BlockResponse]: return self.with_endpoint('api').request_get('blocks/first') - def last(self): + def last(self) -> Response[BlockResponse]: return self.with_endpoint('api').request_get('blocks/last') def transactions( self, block_hash: str, query: Optional[BlockTransactionsQuery] = None, - ): + ) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('api').request_get( f'blocks/{block_hash}/transactions', query ) diff --git a/client/api/commits.py b/client/api/commits.py index 3a98e26..3430c6b 100644 --- a/client/api/commits.py +++ b/client/api/commits.py @@ -1,7 +1,9 @@ from client.resource import Resource +from client.types import Response +from client.types.commits import CommitResponse class Commits(Resource): - def get(self, height: int): + def get(self, height: int) -> Response[CommitResponse]: return self.with_endpoint('api').request_get(f'commits/{height}') diff --git a/client/api/contracts.py b/client/api/contracts.py index 1148236..eedd4b5 100644 --- a/client/api/contracts.py +++ b/client/api/contracts.py @@ -1,10 +1,12 @@ from client.resource import Resource +from client.types import Response +from client.types.contracts import ContractAbiResponse, ContractResponse class Contracts(Resource): - def all(self): + def all(self) -> Response[ContractResponse]: return self.with_endpoint('api').request_get('contracts') - def abi(self, name: str, implementation: str): + def abi(self, name: str, implementation: str) -> Response[ContractAbiResponse]: return self.with_endpoint('api').request_get(f'contracts/{name}/{implementation}/abi') diff --git a/client/api/node.py b/client/api/node.py index c0b6fb8..7bc17a2 100644 --- a/client/api/node.py +++ b/client/api/node.py @@ -1,26 +1,30 @@ from typing import Optional from client.resource import Resource -from client.types.node import NodeFeesQuery +from client.types import Response +from client.types.node import ( + NodeConfigurationResponse, + NodeCryptoResponse, + NodeFeesQuery, + NodeFeesResponse, + NodeStatusResponse, + NodeSyncingResponse, +) class Node(Resource): - def status(self): + def status(self) -> Response[NodeStatusResponse]: return self.with_endpoint('api').request_get('node/status') - def syncing(self): + def syncing(self) -> Response[NodeSyncingResponse]: return self.with_endpoint('api').request_get('node/syncing') - def configuration(self): - return self.with_endpoint('api').request_get( - 'node/configuration' - ) + def configuration(self) -> Response[NodeConfigurationResponse]: + return self.with_endpoint('api').request_get('node/configuration') - def crypto(self): - return self.with_endpoint('api').request_get( - 'node/configuration/crypto' - ) + def crypto(self) -> Response[NodeCryptoResponse]: + return self.with_endpoint('api').request_get('node/configuration/crypto') - def fees(self, query: Optional[NodeFeesQuery] = None): + def fees(self, query: Optional[NodeFeesQuery] = None) -> Response[NodeFeesResponse]: return self.with_endpoint('api').request_get('node/fees', query) diff --git a/client/api/peers.py b/client/api/peers.py index 7d660da..ffab350 100644 --- a/client/api/peers.py +++ b/client/api/peers.py @@ -1,13 +1,14 @@ from typing import Optional from client.resource import Resource -from client.types.peers import PeersQuery +from client.types import PaginatedResponse, Response +from client.types.peers import PeerResponse, PeersQuery class Peers(Resource): - def all(self, query: Optional[PeersQuery] = None): + def all(self, query: Optional[PeersQuery] = None) -> PaginatedResponse[PeerResponse]: return self.with_endpoint('api').request_get('peers', query) - def get(self, ip: str): + def get(self, ip: str) -> Response[PeerResponse]: return self.with_endpoint('api').request_get(f'peers/{ip}') diff --git a/client/api/receipts.py b/client/api/receipts.py index 73944fa..765e39d 100644 --- a/client/api/receipts.py +++ b/client/api/receipts.py @@ -1,23 +1,25 @@ from typing import Optional from client.resource import Resource -from client.types.receipts import ReceiptContractsQuery, ReceiptQuery, ReceiptsQuery +from client.types import PaginatedResponse, Response +from client.types.receipts import ReceiptContractsQuery, ReceiptQuery, ReceiptResponse, ReceiptsQuery class Receipts(Resource): - def all(self, query: Optional[ReceiptsQuery] = None): + + def all(self, query: Optional[ReceiptsQuery] = None) -> PaginatedResponse[ReceiptResponse]: return self.with_endpoint('api').request_get('receipts', query) def get( self, transaction_hash: str, query: Optional[ReceiptQuery] = None, - ): + ) -> Response[ReceiptResponse]: return self.with_endpoint('api').request_get( f'receipts/{transaction_hash}', query ) - def contracts(self, query: Optional[ReceiptContractsQuery] = None): + def contracts(self, query: Optional[ReceiptContractsQuery] = None) -> PaginatedResponse[ReceiptResponse]: return self.with_endpoint('api').request_get( 'receipts/contracts', query ) diff --git a/client/api/rounds.py b/client/api/rounds.py index 143dcf9..82ab693 100644 --- a/client/api/rounds.py +++ b/client/api/rounds.py @@ -1,18 +1,19 @@ from typing import Optional from client.resource import Resource -from client.types import PaginatedQuery +from client.types import PaginatedQuery, PaginatedResponse, Response +from client.types.rounds import RoundResponse class Rounds(Resource): - def all(self, query: Optional[PaginatedQuery] = None): + def all(self, query: Optional[PaginatedQuery] = None) -> PaginatedResponse[RoundResponse]: return self.with_endpoint('api').request_get('rounds', query) - def show(self, round_id: str): + def show(self, round_id: str) -> Response[RoundResponse]: return self.with_endpoint('api').request_get(f'rounds/{round_id}') - def validators(self, round_id: str): + def validators(self, round_id: str) -> Response[RoundResponse]: return self.with_endpoint('api').request_get( f'rounds/{round_id}/validators' ) diff --git a/client/api/tokens.py b/client/api/tokens.py index e8c38e5..93f5285 100644 --- a/client/api/tokens.py +++ b/client/api/tokens.py @@ -1,31 +1,42 @@ from typing import Optional from client.resource import Resource -from client.types import PaginatedQuery -from client.types.tokens import TokenApprovalsQuery, TokenLookupQuery, TokenTransfersQuery, TokensQuery +from client.types import PaginatedQuery, PaginatedResponse, Response +from client.types.tokens import ( + TokenActionsResponse, + TokenAddressHoldersResponse, + TokenAddressesResponse, + TokenApprovalsQuery, + TokenLookupQuery, + TokenPaginatedResponseResults, + TokenResponse, + TokenTransfersQuery, + TokenWhitelistResponse, + TokensQuery, +) class Tokens(Resource): - def all(self, query: Optional[TokensQuery] = None): + def all(self, query: Optional[TokensQuery] = None) -> PaginatedResponse[TokenResponse]: return self.with_endpoint('api').request_get('tokens', query) - def transfers(self, query: Optional[TokenTransfersQuery] = None): + def transfers(self, query: Optional[TokenTransfersQuery] = None) -> TokenPaginatedResponseResults[TokenActionsResponse]: return self.with_endpoint('api').request_get( 'tokens/transfers', query ) - def approvals(self, query: Optional[TokenApprovalsQuery] = None): + def approvals(self, query: Optional[TokenApprovalsQuery] = None) -> TokenPaginatedResponseResults[TokenActionsResponse]: return self.with_endpoint('api').request_get( 'tokens/approvals', query ) - def whitelist(self, query: Optional[PaginatedQuery] = None): + def whitelist(self, query: Optional[PaginatedQuery] = None) -> PaginatedResponse[TokenWhitelistResponse]: return self.with_endpoint('api').request_get( 'tokens/whitelist', query ) - def get(self, address: str): + def get(self, address: str) -> Response[TokenAddressesResponse]: return self.with_endpoint('api').request_get( f'tokens/{address}' ) @@ -34,7 +45,7 @@ def transfers_for( self, address: str, query: Optional[TokenLookupQuery] = None, - ): + ) -> TokenPaginatedResponseResults[TokenActionsResponse]: return self.with_endpoint('api').request_get( f'tokens/{address}/transfers', query ) @@ -43,12 +54,12 @@ def approvals_for( self, address: str, query: Optional[TokenLookupQuery] = None, - ): + ) -> TokenPaginatedResponseResults[TokenActionsResponse]: return self.with_endpoint('api').request_get( f'tokens/{address}/approvals', query ) - def holders_for(self, address: str): + def holders_for(self, address: str) -> PaginatedResponse[TokenAddressHoldersResponse]: return self.with_endpoint('api').request_get( f'tokens/{address}/holders' ) diff --git a/client/api/transactions.py b/client/api/transactions.py index ee2cb30..ab6d8d3 100644 --- a/client/api/transactions.py +++ b/client/api/transactions.py @@ -1,17 +1,25 @@ from typing import Optional, Sequence from client.resource import Resource -from client.types.transactions import TransactionGetQuery, TransactionsQuery, UnconfirmedTransactionsQuery +from client.types import PaginatedResponse, Response +from client.types.transactions import ( + TransactionConfigurationResponse, + TransactionCreateResponse, + TransactionGetQuery, + TransactionResponse, + TransactionsQuery, + UnconfirmedTransactionsQuery, +) class Transactions(Resource): - def all(self, query: Optional[TransactionsQuery] = None): + def all(self, query: Optional[TransactionsQuery] = None) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('api').request_get( 'transactions', query ) - def create(self, transactions: Sequence[str]): + def create(self, transactions: Sequence[str]) -> Response[TransactionCreateResponse]: return self.with_endpoint('transactions').request_post( 'transactions', data={'transactions': transactions} ) @@ -20,7 +28,7 @@ def get( self, transaction_id: str, query: Optional[TransactionGetQuery] = None, - ): + ) -> Response[TransactionResponse]: return self.with_endpoint('api').request_get( f'transactions/{transaction_id}', query ) @@ -28,17 +36,17 @@ def get( def all_unconfirmed( self, query: Optional[UnconfirmedTransactionsQuery] = None, - ): + ) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('transactions').request_get( 'transactions/unconfirmed', query ) - def get_unconfirmed(self, transaction_id: str): + def get_unconfirmed(self, transaction_id: str) -> Response[TransactionResponse]: return self.with_endpoint('transactions').request_get( f'transactions/unconfirmed/{transaction_id}' ) - def configuration(self): + def configuration(self) -> Response[TransactionConfigurationResponse]: return self.with_endpoint('transactions').request_get( 'configuration' ) diff --git a/client/api/validators.py b/client/api/validators.py index 009c677..54bda0e 100644 --- a/client/api/validators.py +++ b/client/api/validators.py @@ -1,17 +1,19 @@ from typing import Optional -from client.api.blocks import BlocksQuery -from client.api.wallets import WalletsQuery from client.resource import Resource +from client.types import PaginatedResponse, Response +from client.types.blocks import BlocksQuery +from client.types.transactions import TransactionResponse from client.types.validators import ValidatorsQuery +from client.types.wallets import WalletResponse, WalletsQuery class Validators(Resource): - def all(self, query: Optional[ValidatorsQuery] = None): + def all(self, query: Optional[ValidatorsQuery] = None) -> PaginatedResponse[WalletResponse]: return self.with_endpoint('api').request_get('validators', query) - def get(self, validator_id: str): + def get(self, validator_id: str) -> Response[WalletResponse]: return self.with_endpoint('api').request_get( f'validators/{validator_id}' ) @@ -20,7 +22,7 @@ def blocks( self, validator_id: str, query: Optional[BlocksQuery] = None, - ): + ) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('api').request_get( f'validators/{validator_id}/blocks', query ) @@ -29,7 +31,7 @@ def voters( self, validator_id: str, query: Optional[WalletsQuery] = None, - ): + ) -> PaginatedResponse[WalletResponse]: return self.with_endpoint('api').request_get( f'validators/{validator_id}/voters', query ) diff --git a/client/api/votes.py b/client/api/votes.py index 4c8c73a..5e22e3b 100644 --- a/client/api/votes.py +++ b/client/api/votes.py @@ -1,13 +1,15 @@ from typing import Optional from client.resource import Resource +from client.types import PaginatedResponse, Response +from client.types.transactions import TransactionResponse from client.types.votes import VoteQuery, VotesQuery class Votes(Resource): - def all(self, query: Optional[VotesQuery] = None): + def all(self, query: Optional[VotesQuery] = None) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('api').request_get('votes', query) - def get(self, vote_id: str, query: Optional[VoteQuery] = None): + def get(self, vote_id: str, query: Optional[VoteQuery] = None) -> Response[TransactionResponse]: return self.with_endpoint('api').request_get(f'votes/{vote_id}', query) diff --git a/client/api/wallets.py b/client/api/wallets.py index 26c5def..9f1e648 100644 --- a/client/api/wallets.py +++ b/client/api/wallets.py @@ -1,19 +1,21 @@ from typing import Optional from client.resource import Resource -from client.types.transactions import TransactionsQuery -from client.types.wallets import WalletTokensForQuery, WalletTokensQuery, WalletsQuery +from client.types import PaginatedResponse, Response +from client.types.transactions import TransactionResponse, TransactionsQuery +from client.types.wallets import WalletResponse, WalletTokensForQuery, WalletTokensQuery, WalletsQuery +from client.types.tokens import TokenActionsResponse, TokenPaginatedResponseResults class Wallets(Resource): - def all(self, query: Optional[WalletsQuery] = None): + def all(self, query: Optional[WalletsQuery] = None) -> PaginatedResponse[WalletResponse]: return self.with_endpoint('api').request_get('wallets', query) - def top(self, query: Optional[WalletsQuery] = None): + def top(self, query: Optional[WalletsQuery] = None) -> PaginatedResponse[WalletResponse]: return self.with_endpoint('api').request_get('wallets/top', query) - def get(self, wallet_id: str): + def get(self, wallet_id: str) -> Response[WalletResponse]: return self.with_endpoint('api').request_get( f'wallets/{wallet_id}' ) @@ -22,7 +24,7 @@ def transactions( self, wallet_id: str, query: Optional[TransactionsQuery] = None, - ): + ) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('api').request_get( f'wallets/{wallet_id}/transactions', query ) @@ -31,7 +33,7 @@ def sent_transactions( self, wallet_id: str, query: Optional[TransactionsQuery] = None, - ): + ) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('api').request_get( f'wallets/{wallet_id}/transactions/sent', query ) @@ -40,7 +42,7 @@ def received_transactions( self, wallet_id: str, query: Optional[TransactionsQuery] = None, - ): + ) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('api').request_get( f'wallets/{wallet_id}/transactions/received', query ) @@ -49,7 +51,7 @@ def votes( self, wallet_id: str, query: Optional[TransactionsQuery] = None, - ): + ) -> PaginatedResponse[TransactionResponse]: return self.with_endpoint('api').request_get( f'wallets/{wallet_id}/votes', query ) @@ -58,12 +60,12 @@ def tokens_for( self, address: str, query: Optional[WalletTokensForQuery] = None, - ): + ) -> TokenPaginatedResponseResults[TokenActionsResponse]: return self.with_endpoint('api').request_get( f'wallets/{address}/tokens', query ) - def tokens(self, query: Optional[WalletTokensQuery] = None): + def tokens(self, query: Optional[WalletTokensQuery] = None) -> TokenPaginatedResponseResults[TokenActionsResponse]: return self.with_endpoint('api').request_get( 'wallets/tokens', query ) diff --git a/client/types/__init__.py b/client/types/__init__.py index 0890872..ca1e204 100644 --- a/client/types/__init__.py +++ b/client/types/__init__.py @@ -1,12 +1,31 @@ -from typing import TypedDict - - -PaginatedQuery = TypedDict( - 'PaginatedQuery', - { - 'page': int, - 'limit': int, - 'offset': int, - }, - total=False, -) +from typing import Generic, TypeVar, TypedDict + + +T = TypeVar('T') + + +class PaginatedQuery(TypedDict, total=False): + page: int + limit: int + offset: int + + +class ResponseMeta(TypedDict): + totalCountIsEstimate: bool + count: int + first: str + last: str + next: str | None + pageCount: int + previous: str | None + self: str + totalCount: int + + +class Response(TypedDict, Generic[T]): + data: T + + +class PaginatedResponse(TypedDict, Generic[T]): + meta: ResponseMeta + data: list[T] diff --git a/client/types/api_nodes.py b/client/types/api_nodes.py index 17579c3..dc2e22b 100644 --- a/client/types/api_nodes.py +++ b/client/types/api_nodes.py @@ -1,12 +1,15 @@ from typing import TypedDict -ApiNodesQuery = TypedDict( - 'ApiNodesQuery', - { - 'ip': str, - 'orderBy': str, - 'version': str, - }, - total=False, -) +class ApiNodeResponse(TypedDict): + url: str + version: str + height: int + latency: int + status: str + + +class ApiNodesQuery(TypedDict, total=False): + ip: str + orderBy: str + version: str diff --git a/client/types/blockchain.py b/client/types/blockchain.py new file mode 100644 index 0000000..b73a1db --- /dev/null +++ b/client/types/blockchain.py @@ -0,0 +1,11 @@ +from typing import TypedDict + + +class BlockchainBlock(TypedDict): + hash: str + number: int + + +class BlockchainResponse(TypedDict): + block: BlockchainBlock + supply: str diff --git a/client/types/blocks.py b/client/types/blocks.py index 046c046..d40ae6f 100644 --- a/client/types/blocks.py +++ b/client/types/blocks.py @@ -1,6 +1,50 @@ -from typing import TypedDict +from typing import NotRequired, TypedDict +from client.types import PaginatedQuery +from client.types.transactions import Transaction + +class Block(TypedDict): + hash: str + round: int + number: int + reward: str + version: int + fee: str + stateRoot: str + timestamp: str + transactionsRoot: str + amount: str + gasUsed: int + transactions: list[Transaction] + payloadSize: int + parentHash: str + publicKey: str + proposer: str + transactionCount: int + + +class BlockResponse(TypedDict): + hash: str + number: int + confirmations: int + amount: str + fee: str + reward: str + total: str + proposer: str + publicKey: str + username: NotRequired[str] + transactionsRoot: str + payloadSize: int + parentHash: str + signature: str + timestamp: str + transactionsCount: int + version: int + + +# Functional form required: keys contain dots (e.g. 'height.from') which are not valid Python identifiers BlocksQuery = TypedDict( 'BlocksQuery', { @@ -20,13 +64,6 @@ total=False, ) -BlockTransactionsQuery = TypedDict( - 'BlockTransactionsQuery', - { - 'page': int, - 'limit': int, - 'offset': int, - 'orderBy': str, - }, - total=False, -) + +class BlockTransactionsQuery(PaginatedQuery, total=False): + orderBy: str diff --git a/client/types/commits.py b/client/types/commits.py new file mode 100644 index 0000000..ebcb4c7 --- /dev/null +++ b/client/types/commits.py @@ -0,0 +1,7 @@ +from typing import TypedDict + + +class CommitResponse(TypedDict): + blockNumber: str + signature: str + validators: list[str] diff --git a/client/types/contracts.py b/client/types/contracts.py new file mode 100644 index 0000000..5fad7fc --- /dev/null +++ b/client/types/contracts.py @@ -0,0 +1,15 @@ +from typing import Any, TypedDict + + +class Contract(TypedDict): + activeImplementation: str + address: str + implementations: list[str] + proxy: str + + +ContractResponse = dict[str, Contract] + + +class ContractAbiResponse(TypedDict): + abi: list[Any] diff --git a/client/types/evm.py b/client/types/evm.py index 613188b..1b04736 100644 --- a/client/types/evm.py +++ b/client/types/evm.py @@ -1,17 +1,13 @@ -from typing import Literal, Optional, TypedDict +from typing import Literal, NotRequired, TypedDict PayloadData = dict[str, str | int | float] BlockParameter = Literal["earliest", "latest", "safe", "finalized", "pending"] -EvmBodyPartialParams = TypedDict( - 'EvmBodyPartialParams', - { - 'jsonrpc': Optional[str], - 'method': str, - 'params': list[PayloadData] | list[PayloadData | BlockParameter | str], - 'id': Optional[int | float], - }, - total=False, -) + +class EvmBodyPartialParams(TypedDict, total=False): + jsonrpc: str + method: str + params: list[PayloadData] | list[PayloadData | BlockParameter | str] + id: int | float | None diff --git a/client/types/node.py b/client/types/node.py index 3a942fc..39fd2d6 100644 --- a/client/types/node.py +++ b/client/types/node.py @@ -1,10 +1,166 @@ -from typing import TypedDict +from typing import NotRequired, TypedDict +from client.types.blocks import Block -NodeFeesQuery = TypedDict( - 'NodeFeesQuery', - { - 'days': int, - }, - total=False, -) + +class NodeStatusResponse(TypedDict): + blocksCount: int + now: int + synced: bool + timestamp: int + + +class NodeSyncingResponse(TypedDict): + blocks: int + blockNumber: int + id: int + syncing: bool + + +class NodeConfigurationTransactionPoolDynamicFees(TypedDict): + enabled: bool + + +class NodeConfigurationTransactionPool(TypedDict): + dynamicFees: NodeConfigurationTransactionPoolDynamicFees + + +class NodeConfigurationCore(TypedDict): + version: str + + +class NativeGasLimits(TypedDict, total=False): + vote: int + transfer: int + multiPayment: int + multiSignature: int + usernameResignation: int + usernameRegistration: int + validatorResignation: int + validatorRegistration: int + + +class ConstantGas(TypedDict, total=False): + minimumGasFee: int + maximumGasLimit: int + minimumGasLimit: int + nativeGasLimits: NativeGasLimits + nativeFeeMultiplier: int + + +class StaticFees(TypedDict, total=False): + vote: int + transfer: int + multiPayment: int + multiSignature: int + usernameResignation: int + usernameRegistration: int + validatorResignation: int + validatorRegistration: int + + +class ConstantFees(TypedDict, total=False): + staticFees: StaticFees + + +class ConstantBlock(TypedDict, total=False): + version: int + maxPayload: int + maxGasLimit: int + maxTransactions: int + + +class ConstantAddress(TypedDict, total=False): + keccak256: bool + + +class ConstantSatoshi(TypedDict, total=False): + decimals: int + denomination: int + + +class ConfigurationTimeouts(TypedDict): + blockTime: int + tolerance: int + stageTimeout: int + blockPrepareTime: int + stageTimeoutIncrease: int + + +class Constant(TypedDict, total=False): + gas: ConstantGas + fees: ConstantFees + block: ConstantBlock + epoch: str + height: int + reward: str + address: ConstantAddress + evmSpec: str + satoshi: ConstantSatoshi + timeouts: ConfigurationTimeouts + activeValidators: int + multiPaymentLimit: int + vendorFieldLength: int + + +class NodeConfigurationResponse(TypedDict): + constants: Constant + core: NodeConfigurationCore + explorer: str + nethash: str + ports: dict[str, int] + slip44: int + symbol: str + token: str + transactionPool: NodeConfigurationTransactionPool + version: int + wif: int + + +class NetworkClient(TypedDict): + token: str + symbol: str + explorer: str + + +class Network(TypedDict): + wif: int + name: str + client: NetworkClient + slip44: int + chainId: int + nethash: str + pubKeyHash: int + messagePrefix: str + + +class GenesisBlockProof(TypedDict): + round: int + signature: str + validators: list + + +class GenesisBlock(TypedDict): + block: Block + proof: GenesisBlockProof + serialized: str + + +class NodeCryptoResponse(TypedDict): + network: Network + milestones: list[Constant] + genesisBlock: GenesisBlock + + +class TransactionTypeFee(TypedDict): + avg: str + max: str + min: str + sum: str + + +NodeFeesResponse = dict[str, TransactionTypeFee] + + +class NodeFeesQuery(TypedDict, total=False): + days: int diff --git a/client/types/peers.py b/client/types/peers.py index 4eb5dd5..c6e3181 100644 --- a/client/types/peers.py +++ b/client/types/peers.py @@ -1,14 +1,25 @@ from typing import TypedDict +from client.types import PaginatedQuery -PeersQuery = TypedDict( - 'PeersQuery', - { - 'page': int, - 'limit': int, - 'ip': str, - 'orderBy': str, - 'version': str, - }, - total=False, -) + +class Plugin(TypedDict): + enabled: bool + estimateTotalCount: bool + port: int + + +class PeerResponse(TypedDict): + blockNumber: int + ip: str + latency: int + plugins: dict[str, Plugin] + port: int + ports: dict[str, int] + version: str + + +class PeersQuery(PaginatedQuery, total=False): + ip: str + orderBy: str + version: str diff --git a/client/types/receipts.py b/client/types/receipts.py index 974fa50..e08bac8 100644 --- a/client/types/receipts.py +++ b/client/types/receipts.py @@ -1,6 +1,23 @@ -from typing import TypedDict +from typing import Literal, TypedDict +class ReceiptLog(TypedDict): + data: str + topics: list[str] + address: str + + +class ReceiptResponse(TypedDict): + transactionHash: str + status: Literal[1, 0] + gasUsed: int + gasRefunded: int + contractAddress: None + logs: list[ReceiptLog] + output: str + + +# Functional form required: keys include 'from' which is a reserved Python keyword ReceiptsQuery = TypedDict( 'ReceiptsQuery', { @@ -16,15 +33,13 @@ total=False, ) -ReceiptQuery = TypedDict( - 'ReceiptQuery', - { - 'fullReceipt': bool, - 'includeTokens': bool, - }, - total=False, -) +class ReceiptQuery(TypedDict, total=False): + fullReceipt: bool + includeTokens: bool + + +# Functional form required: keys include 'from' which is a reserved Python keyword ReceiptContractsQuery = TypedDict( 'ReceiptContractsQuery', { diff --git a/client/types/rounds.py b/client/types/rounds.py new file mode 100644 index 0000000..df78ee3 --- /dev/null +++ b/client/types/rounds.py @@ -0,0 +1,8 @@ +from typing import TypedDict + + +class RoundResponse(TypedDict): + round: str + roundHeight: str + validators: list[str] + votes: list[str] diff --git a/client/types/tokens.py b/client/types/tokens.py index eed242d..a7caa0a 100644 --- a/client/types/tokens.py +++ b/client/types/tokens.py @@ -1,19 +1,78 @@ -from typing import List, TypedDict +from typing import Generic, List, TypeVar, TypedDict +from client.types import PaginatedQuery, ResponseMeta -TokensQuery = TypedDict( - 'TokensQuery', +T = TypeVar('T') + + +class TokenResponse(TypedDict): + address: str + decimals: int + deploymentHash: str + name: str + totalSupply: str + + +class TokenAddressesResponse(TypedDict): + addresses: dict[str, str] + decimals: int + name: str + supply: str + symbol: str + token: str + + +class TokenActionToken(TypedDict): + address: str + name: str + symbol: str + decimals: int + + +# Functional form required: keys include 'from' which is a reserved Python keyword +TokenActionsResponse = TypedDict( + 'TokenActionsResponse', { - 'page': int, - 'limit': int, - 'offset': int, - 'ignoreWhitelist': bool, - 'name': str, - 'whitelist': List[str], + 'transactionHash': str, + 'from': str, + 'to': str, + 'value': str, + 'functionSig': str, + 'blockNumber': str, + 'timestamp': str, + 'token': TokenActionToken, }, - total=False, ) + +class TokenWhitelistResponse(TypedDict): + address: str + comment: str + createdAt: str + + +class TokenAddressHoldersResponse(TypedDict): + address: str + balance: int + tokenAddress: str + + +class TokenPaginatedResponseData(TypedDict, Generic[T]): + data: T + + +class TokenPaginatedResponseResults(TypedDict, Generic[T]): + meta: ResponseMeta + results: list[T] + + +class TokensQuery(PaginatedQuery, total=False): + ignoreWhitelist: bool + name: str + whitelist: List[str] + + +# Functional form required: keys include 'from' which is a reserved Python keyword TokenLookupQuery = TypedDict( 'TokenLookupQuery', { @@ -27,6 +86,7 @@ total=False, ) +# Functional form required: keys include 'from' which is a reserved Python keyword TokenTransfersQuery = TypedDict( 'TokenTransfersQuery', { @@ -43,6 +103,7 @@ total=False, ) +# Functional form required: keys include 'from' which is a reserved Python keyword TokenApprovalsQuery = TypedDict( 'TokenApprovalsQuery', { diff --git a/client/types/transactions.py b/client/types/transactions.py index 53aa2c9..2ecb06c 100644 --- a/client/types/transactions.py +++ b/client/types/transactions.py @@ -1,6 +1,84 @@ from typing import List, TypedDict +from client.types import PaginatedQuery + +# Functional form required: keys include 'from' which is a reserved Python keyword +Transaction = TypedDict( + 'Transaction', + { + 'hash': str, + 'fee': str, + 'type': int, + 'nonce': str, + 'value': str, + 'network': int, + 'version': int, + 'sequence': int, + 'signature': str, + 'typeGroup': int, + 'expiration': int, + 'to': str, + 'senderPublicKey': str, + 'from': str, + }, +) + + +class TransactionReceipt(TypedDict): + gasRefunded: int + gasUsed: int + success: bool + + +# Functional form required: keys include 'from' which is a reserved Python keyword +TransactionResponse = TypedDict( + 'TransactionResponse', + { + 'hash': str, + 'value': str, + 'blockNumber': str, + 'confirmations': int, + 'data': str, + 'gas': str, + 'gasPrice': str, + 'nonce': str, + 'to': str, + 'from': str, + 'senderPublicKey': str, + 'signature': str, + 'timestamp': str, + 'receipt': TransactionReceipt, + }, +) + + +class TransactionConfigurationTransactionPool(TypedDict): + maxTransactionAge: int + maxTransactionBytes: int + maxTransactionsInPool: int + maxTransactionsPerRequest: int + maxTransactionsPerSender: int + + +class TransactionConfigurationCore(TypedDict): + version: str + + +class TransactionConfigurationResponse(TypedDict): + core: TransactionConfigurationCore + height: int + transactionPool: TransactionConfigurationTransactionPool + + +class TransactionCreateResponse(TypedDict): + accept: list[int] + broadcast: list[int] + excess: list[int] + invalid: list[int] + + +# Functional form required: keys include 'from' which is a reserved Python keyword TransactionsQuery = TypedDict( 'TransactionsQuery', { @@ -27,28 +105,15 @@ total=False, ) -UnconfirmedTransactionsQuery = TypedDict( - 'UnconfirmedTransactionsQuery', - { - 'page': int, - 'limit': int, - 'orderBy': str, - }, - total=False, -) -TransactionGetQuery = TypedDict( - 'TransactionGetQuery', - { - 'fullReceipt': bool, - 'includeTokens': bool, - }, - total=False, -) +class UnconfirmedTransactionsQuery(PaginatedQuery, total=False): + orderBy: str -TransactionCreateParams = TypedDict( - 'TransactionCreateParams', - { - 'transactions': List[str], - }, -) + +class TransactionGetQuery(TypedDict, total=False): + fullReceipt: bool + includeTokens: bool + + +class TransactionCreateParams(TypedDict): + transactions: List[str] diff --git a/client/types/validators.py b/client/types/validators.py index 48c447d..59f2abd 100644 --- a/client/types/validators.py +++ b/client/types/validators.py @@ -1,6 +1,7 @@ from typing import TypedDict +# Functional form required: keys contain dots (e.g. 'blocks.last.hash') which are not valid Python identifiers ValidatorsQuery = TypedDict( 'ValidatorsQuery', { diff --git a/client/types/votes.py b/client/types/votes.py index 43d1d23..08c38b8 100644 --- a/client/types/votes.py +++ b/client/types/votes.py @@ -1,6 +1,7 @@ from typing import TypedDict +# Functional form required: keys include 'from' which is a reserved Python keyword VotesQuery = TypedDict( 'VotesQuery', { @@ -26,10 +27,6 @@ total=False, ) -VoteQuery = TypedDict( - 'VoteQuery', - { - 'fullReceipt': bool, - }, - total=False, -) + +class VoteQuery(TypedDict, total=False): + fullReceipt: bool diff --git a/client/types/wallets.py b/client/types/wallets.py index b9ece9f..2d29f5b 100644 --- a/client/types/wallets.py +++ b/client/types/wallets.py @@ -1,6 +1,41 @@ from typing import List, TypedDict +from client.types import PaginatedQuery + +class ValidatorLastBlock(TypedDict): + id: str + height: int + timestamp: int + + +class WalletAttributes(TypedDict, total=False): + username: str + vote: str + validatorRank: int + validatorApproval: int + validatorResigned: bool + validatorLastBlock: ValidatorLastBlock + validatorPublicKey: str + validatorForgedFees: str + validatorForgedTotal: str + validatorVoteBalance: str + validatorVotersCount: int + validatorForgedRewards: str + validatorProducedBlocks: int + + +class WalletResponse(TypedDict): + address: str + publicKey: str + balance: str + nonce: str + attributes: WalletAttributes + updated_at: str + tokenCount: int + + +# Functional form required: keys contain dots (e.g. 'balance.from') which are not valid Python identifiers WalletsQuery = TypedDict( 'WalletsQuery', { @@ -21,27 +56,16 @@ total=False, ) -WalletTokensForQuery = TypedDict( - 'WalletTokensForQuery', - { - 'addresses': List[str], - 'ignoreWhitelist': bool, - 'minBalance': int, - 'name': str, - 'whitelist': List[str], - }, - total=False, -) -WalletTokensQuery = TypedDict( - 'WalletTokensQuery', - { - 'addresses': List[str], - 'ignoreWhitelist': bool, - 'page': int, - 'limit': int, - 'minBalance': int, - 'whitelist': List[str], - }, - total=False, -) +class WalletTokensForQuery(TypedDict, total=False): + ignoreWhitelist: bool + minBalance: int + name: str + whitelist: List[str] + + +class WalletTokensQuery(PaginatedQuery, total=False): + addresses: List[str] + ignoreWhitelist: bool + minBalance: int + whitelist: List[str] From 117c09ab540c42c62386fdbc4689784b28ccc238 Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:55:25 +0000 Subject: [PATCH 3/5] wip --- client/types/__init__.py | 4 ++-- client/types/tokens.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/types/__init__.py b/client/types/__init__.py index ca1e204..c6b1198 100644 --- a/client/types/__init__.py +++ b/client/types/__init__.py @@ -22,10 +22,10 @@ class ResponseMeta(TypedDict): totalCount: int -class Response(TypedDict, Generic[T]): +class Response(Generic[T]): data: T -class PaginatedResponse(TypedDict, Generic[T]): +class PaginatedResponse(Generic[T]): meta: ResponseMeta data: list[T] diff --git a/client/types/tokens.py b/client/types/tokens.py index a7caa0a..1580020 100644 --- a/client/types/tokens.py +++ b/client/types/tokens.py @@ -57,11 +57,11 @@ class TokenAddressHoldersResponse(TypedDict): tokenAddress: str -class TokenPaginatedResponseData(TypedDict, Generic[T]): +class TokenPaginatedResponseData(Generic[T]): data: T -class TokenPaginatedResponseResults(TypedDict, Generic[T]): +class TokenPaginatedResponseResults(Generic[T]): meta: ResponseMeta results: list[T] From 17f4de001025e4b3f3a2bfff53d8c13efb07bc33 Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:00:56 +0000 Subject: [PATCH 4/5] wip --- client/types/blocks.py | 4 +++- client/types/evm.py | 4 +++- client/types/node.py | 4 +++- requirements.txt | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/client/types/blocks.py b/client/types/blocks.py index d40ae6f..26171de 100644 --- a/client/types/blocks.py +++ b/client/types/blocks.py @@ -1,4 +1,6 @@ -from typing import NotRequired, TypedDict +from typing import TypedDict + +from typing_extensions import NotRequired from client.types import PaginatedQuery from client.types.transactions import Transaction diff --git a/client/types/evm.py b/client/types/evm.py index 1b04736..5337af7 100644 --- a/client/types/evm.py +++ b/client/types/evm.py @@ -1,4 +1,6 @@ -from typing import Literal, NotRequired, TypedDict +from typing import Literal, TypedDict + +from typing_extensions import NotRequired PayloadData = dict[str, str | int | float] diff --git a/client/types/node.py b/client/types/node.py index 39fd2d6..d89a668 100644 --- a/client/types/node.py +++ b/client/types/node.py @@ -1,4 +1,6 @@ -from typing import NotRequired, TypedDict +from typing import TypedDict + +from typing_extensions import NotRequired from client.types.blocks import Block diff --git a/requirements.txt b/requirements.txt index c7f3ce8..1fb24c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,4 +23,5 @@ requests==2.32.3 responses==0.10.15 setuptools==70.1.0 six==1.16.0 +typing_extensions==4.15.0 urllib3==2.2.2 From 2ec34e2b3f44d52de691ba039b083d2dab5210cf Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:07:33 +0000 Subject: [PATCH 5/5] wip --- client/types/blocks.py | 6 ++---- client/types/evm.py | 2 -- client/types/node.py | 2 -- requirements.txt | 1 - 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/client/types/blocks.py b/client/types/blocks.py index 26171de..85192a2 100644 --- a/client/types/blocks.py +++ b/client/types/blocks.py @@ -1,7 +1,5 @@ from typing import TypedDict -from typing_extensions import NotRequired - from client.types import PaginatedQuery from client.types.transactions import Transaction @@ -26,7 +24,7 @@ class Block(TypedDict): transactionCount: int -class BlockResponse(TypedDict): +class BlockResponse(TypedDict, total=False): hash: str number: int confirmations: int @@ -36,7 +34,7 @@ class BlockResponse(TypedDict): total: str proposer: str publicKey: str - username: NotRequired[str] + username: str transactionsRoot: str payloadSize: int parentHash: str diff --git a/client/types/evm.py b/client/types/evm.py index 5337af7..14f170e 100644 --- a/client/types/evm.py +++ b/client/types/evm.py @@ -1,7 +1,5 @@ from typing import Literal, TypedDict -from typing_extensions import NotRequired - PayloadData = dict[str, str | int | float] diff --git a/client/types/node.py b/client/types/node.py index d89a668..b8fa39a 100644 --- a/client/types/node.py +++ b/client/types/node.py @@ -1,7 +1,5 @@ from typing import TypedDict -from typing_extensions import NotRequired - from client.types.blocks import Block diff --git a/requirements.txt b/requirements.txt index 1fb24c8..c7f3ce8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,5 +23,4 @@ requests==2.32.3 responses==0.10.15 setuptools==70.1.0 six==1.16.0 -typing_extensions==4.15.0 urllib3==2.2.2