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 1/4] 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 2/4] 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 3/4] 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 4/4] 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