Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cookbook for v1 #143

Open
wants to merge 21 commits into
base: feat/next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
cookbook fixes after review
  • Loading branch information
popenta committed Dec 18, 2024
commit e8201c9f38570c06390820603b9f16102a102df5
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 15,7 @@ repos:
hooks:
- id: isort
name: isort (python)
args: ["--profile", "black", "--filter-files"]
- repo: https://github.com/PyCQA/flake8
rev: 7.1.1
hooks:
Expand Down
179 changes: 132 additions & 47 deletions examples/v1.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions multiversx_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 26,7 @@
BlockCoordinates, BlockOnNetwork,
FungibleTokenMetadata,
GenericError, GenericResponse,
GetBlockArguments, NetworkConfig,
NetworkConfig,
NetworkProviderConfig,
NetworkStatus,
ProxyNetworkProvider,
Expand Down Expand Up @@ -80,7 80,7 @@
"FreezeOutcome", "UnFreezeOutcome", "WipeOutcome", "UpdateAttributesOutcome", "AddQuantityOutcome",
"BurnQuantityOutcome", "TransactionOnNetwork", "TransactionStatus", "ParsedSmartContractCallOutcome",
"AccountOnNetwork", "AccountStorage", "AccountStorageEntry", "AwaitingOptions", "BlockCoordinates",
"BlockOnNetwork", "FungibleTokenMetadata", "GetBlockArguments", "NetworkConfig", "NetworkStatus",
"BlockOnNetwork", "FungibleTokenMetadata", "NetworkConfig", "NetworkStatus",
"TokenAmountOnNetwork", "TokensCollectionMetadata", "TransactionCostResponse", "AccountAwaiter",
"LibraryConfig", "KeyPair"
]
4 changes: 2 additions & 2 deletions multiversx_sdk/network_providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 8,7 @@
from multiversx_sdk.network_providers.resources import (
AccountOnNetwork, AccountStorage, AccountStorageEntry, AwaitingOptions,
BlockCoordinates, BlockOnNetwork, FungibleTokenMetadata, GenericResponse,
GetBlockArguments, NetworkConfig, NetworkStatus, TokenAmountOnNetwork,
NetworkConfig, NetworkStatus, TokenAmountOnNetwork,
TokensCollectionMetadata, TransactionCostResponse)
from multiversx_sdk.network_providers.transaction_awaiter import \
TransactionAwaiter
Expand All @@ -21,6 21,6 @@
"TransactionDecoder", "TransactionMetadata", "NetworkProviderConfig",
"AccountOnNetwork", "AccountStorage", "AccountStorageEntry", "AwaitingOptions",
"BlockCoordinates", "BlockOnNetwork", "FungibleTokenMetadata",
"GetBlockArguments", "NetworkConfig", "NetworkStatus", "TokenAmountOnNetwork",
"NetworkConfig", "NetworkStatus", "TokenAmountOnNetwork",
"TokensCollectionMetadata", "TransactionCostResponse", "AccountAwaiter"
]
144 changes: 88 additions & 56 deletions multiversx_sdk/network_providers/api_network_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 3,62 @@

import requests

from multiversx_sdk.core import (Address, Token, TokenComputer, Transaction,
TransactionOnNetwork)
from multiversx_sdk.core import Address, Token, TokenComputer, Transaction, TransactionOnNetwork
from multiversx_sdk.core.config import LibraryConfig
from multiversx_sdk.core.constants import METACHAIN_ID
from multiversx_sdk.network_providers.account_awaiter import AccountAwaiter
from multiversx_sdk.network_providers.config import NetworkProviderConfig
from multiversx_sdk.network_providers.constants import (
BASE_USER_AGENT, DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS)
from multiversx_sdk.network_providers.errors import (GenericError,
TransactionFetchingError)
BASE_USER_AGENT,
DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS,
)
from multiversx_sdk.network_providers.errors import GenericError, TransactionFetchingError
from multiversx_sdk.network_providers.http_resources import (
account_from_api_response, account_storage_entry_from_response,
account_storage_from_response, block_from_response,
account_from_api_response,
account_storage_entry_from_response,
account_storage_from_response,
block_from_response,
definition_of_fungible_token_from_api_response,
definition_of_tokens_collection_from_api_response,
smart_contract_query_to_vm_query_request, token_amount_from_api_response,
transaction_cost_estimation_from_response, transaction_from_api_response,
smart_contract_query_to_vm_query_request,
token_amount_from_api_response,
transaction_cost_estimation_from_response,
transaction_from_api_response,
transaction_from_simulate_response,
transactions_from_send_multiple_response,
vm_query_response_to_smart_contract_query_response)
vm_query_response_to_smart_contract_query_response,
)
from multiversx_sdk.network_providers.interface import INetworkProvider
from multiversx_sdk.network_providers.proxy_network_provider import \
ProxyNetworkProvider
from multiversx_sdk.network_providers.proxy_network_provider import ProxyNetworkProvider
from multiversx_sdk.network_providers.resources import (
AccountOnNetwork, AccountStorage, AccountStorageEntry, AwaitingOptions,
BlockOnNetwork, FungibleTokenMetadata, GetBlockArguments, NetworkConfig,
NetworkStatus, TokenAmountOnNetwork, TokensCollectionMetadata,
TransactionCostResponse)
AccountOnNetwork,
AccountStorage,
AccountStorageEntry,
AwaitingOptions,
BlockOnNetwork,
FungibleTokenMetadata,
NetworkConfig,
NetworkStatus,
TokenAmountOnNetwork,
TokensCollectionMetadata,
TransactionCostResponse,
)
from multiversx_sdk.network_providers.shared import convert_tx_hash_to_string
from multiversx_sdk.network_providers.transaction_awaiter import \
TransactionAwaiter
from multiversx_sdk.network_providers.transaction_awaiter import TransactionAwaiter
from multiversx_sdk.network_providers.user_agent import extend_user_agent
from multiversx_sdk.smart_contracts.smart_contract_query import (
SmartContractQuery, SmartContractQueryResponse)
SmartContractQuery,
SmartContractQueryResponse,
)


class ApiNetworkProvider(INetworkProvider):
def __init__(self,
url: str,
address_hrp: Optional[str] = None,
config: Optional[NetworkProviderConfig] = None) -> None:
def __init__(
self,
url: str,
address_hrp: Optional[str] = None,
config: Optional[NetworkProviderConfig] = None,
) -> None:
self.url = url
self.address_hrp = address_hrp or LibraryConfig.default_address_hrp
self.backing_proxy = ProxyNetworkProvider(url, self.address_hrp)
Expand All @@ -60,22 75,21 @@ def get_network_status(self, shard: int = METACHAIN_ID) -> NetworkStatus:
"""Fetches the current status of the network."""
return self.backing_proxy.get_network_status(shard)

def get_block(self, arguments: GetBlockArguments) -> BlockOnNetwork:
def get_block(self, block_hash: Union[str, bytes]) -> BlockOnNetwork:
"""Fetches a block by hash."""
if not arguments.block_hash:
raise Exception("Block hash not provided. Please set the `block_hash` in the arguments.")
block_hash = block_hash.hex() if isinstance(block_hash, bytes) else block_hash

result = self.do_get_generic(f"blocks/{arguments.block_hash.hex()}")
result = self.do_get_generic(f"blocks/{block_hash}")
return block_from_response(result)

def get_latest_block(self, shard: Optional[int] = None) -> BlockOnNetwork:
def get_latest_block(self) -> BlockOnNetwork:
"""Fetches the latest block of a shard."""
result = self.do_get_generic("/blocks/latest")
return block_from_response(result)

def get_account(self, address: Address) -> AccountOnNetwork:
"""Fetches account information for a given address."""
response = self.do_get_generic(f'accounts/{address.to_bech32()}')
response = self.do_get_generic(f"accounts/{address.to_bech32()}")
account = account_from_api_response(response)
return account

Expand All @@ -90,44 104,56 @@ def get_account_storage(self, address: Address) -> AccountStorage:
def get_account_storage_entry(self, address: Address, entry_key: str) -> AccountStorageEntry:
"""Fetches a specific storage entry of an account."""
key_as_hex = entry_key.encode().hex()
response: dict[str, Any] = self.do_get_generic(f"address/{address.to_bech32()}/key/{key_as_hex}")
response: dict[str, Any] = self.do_get_generic(
f"address/{address.to_bech32()}/key/{key_as_hex}"
)
return account_storage_entry_from_response(response.get("data", {}), entry_key)

def await_account_on_condition(
self, address: Address, condition: Callable[[AccountOnNetwork],
bool],
options: Optional[AwaitingOptions] = None) -> AccountOnNetwork:
self,
address: Address,
condition: Callable[[AccountOnNetwork], bool],
options: Optional[AwaitingOptions] = None,
) -> AccountOnNetwork:
"""Waits until an account satisfies a given condition."""
if options is None:
options = AwaitingOptions(patience_in_milliseconds=DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS)
options = AwaitingOptions(
patience_in_milliseconds=DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS
)

awaiter = AccountAwaiter(
fetcher=self,
polling_interval_in_milliseconds=options.polling_interval_in_milliseconds,
timeout_interval_in_milliseconds=options.timeout_in_milliseconds,
patience_time_in_milliseconds=options.patience_in_milliseconds
patience_time_in_milliseconds=options.patience_in_milliseconds,
)

return awaiter.await_on_condition(address=address, condition=condition)

def send_transaction(self, transaction: Transaction) -> bytes:
"""Broadcasts a transaction and returns its hash."""
response = self.do_post_generic("transactions", transaction.to_dictionary())
return bytes.fromhex(response.get('txHash', ''))
return bytes.fromhex(response.get("txHash", ""))

def simulate_transaction(self, transaction: Transaction, check_signature: bool = False) -> TransactionOnNetwork:
def simulate_transaction(
self, transaction: Transaction, check_signature: bool = False
) -> TransactionOnNetwork:
"""Simulates a transaction."""
url = 'transaction/simulate?checkSignature=false'
url = "transaction/simulate?checkSignature=false"

if check_signature:
url = 'transaction/simulate'
url = "transaction/simulate"

response: dict[str, Any] = self.do_post_generic(url, transaction.to_dictionary())
return transaction_from_simulate_response(transaction, response.get("data", {}).get("result", {}))
return transaction_from_simulate_response(
transaction, response.get("data", {}).get("result", {})
)

def estimate_transaction_cost(self, transaction: Transaction) -> TransactionCostResponse:
"""Estimates the cost of a transaction."""
response: dict[str, Any] = self.do_post_generic('transaction/cost', transaction.to_dictionary())
response: dict[str, Any] = self.do_post_generic(
"transaction/cost", transaction.to_dictionary()
)
return transaction_cost_estimation_from_response(response.get("data", {}))

def send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[bytes]]:
Expand All @@ -137,21 163,23 @@ def send_transactions(self, transactions: list[Transaction]) -> tuple[int, list[
If a transaction is not accepted, its hash is empty in the returned list.
"""
transactions_as_dictionaries = [transaction.to_dictionary() for transaction in transactions]
response: dict[str, Any] = self.do_post_generic('transaction/send-multiple', transactions_as_dictionaries)
response: dict[str, Any] = self.do_post_generic(
"transaction/send-multiple", transactions_as_dictionaries
)
return transactions_from_send_multiple_response(response.get("data", {}), len(transactions))

def get_transaction(self, transaction_hash: Union[str, bytes]) -> TransactionOnNetwork:
"""Fetches a transaction that was previously broadcasted (maybe already processed by the network)."""
transaction_hash = convert_tx_hash_to_string(transaction_hash)
try:
response = self.do_get_generic(f'transactions/{transaction_hash}')
response = self.do_get_generic(f"transactions/{transaction_hash}")
except GenericError as ge:
raise TransactionFetchingError(ge.url, ge.data)
return transaction_from_api_response(transaction_hash, response)

def await_transaction_completed(
self, transaction_hash: Union[str, bytes],
options: Optional[AwaitingOptions] = None) -> TransactionOnNetwork:
self, transaction_hash: Union[str, bytes], options: Optional[AwaitingOptions] = None
) -> TransactionOnNetwork:
"""Waits until the transaction is completely processed."""
transaction_hash = convert_tx_hash_to_string(transaction_hash)

Expand All @@ -162,16 190,17 @@ def await_transaction_completed(
fetcher=self,
polling_interval_in_milliseconds=options.polling_interval_in_milliseconds,
timeout_interval_in_milliseconds=options.timeout_in_milliseconds,
patience_time_in_milliseconds=options.patience_in_milliseconds
patience_time_in_milliseconds=options.patience_in_milliseconds,
)

return awaiter.await_completed(transaction_hash)

def await_transaction_on_condition(
self, transaction_hash: Union[str, bytes],
condition: Callable[[TransactionOnNetwork],
bool],
options: Optional[AwaitingOptions] = None) -> TransactionOnNetwork:
self,
transaction_hash: Union[str, bytes],
condition: Callable[[TransactionOnNetwork], bool],
options: Optional[AwaitingOptions] = None,
) -> TransactionOnNetwork:
"""Waits until a transaction satisfies a given condition."""
transaction_hash = convert_tx_hash_to_string(transaction_hash)

Expand All @@ -182,7 211,7 @@ def await_transaction_on_condition(
fetcher=self,
polling_interval_in_milliseconds=options.polling_interval_in_milliseconds,
timeout_interval_in_milliseconds=options.timeout_in_milliseconds,
patience_time_in_milliseconds=options.patience_in_milliseconds
patience_time_in_milliseconds=options.patience_in_milliseconds,
)

return awaiter.await_on_condition(transaction_hash, condition)
Expand All @@ -196,7 225,9 @@ def get_token_of_account(self, address: Address, token: Token) -> TokenAmountOnN
identifier = TokenComputer().compute_extended_identifier(token)
result = self.do_get_generic(f"accounts/{address.to_bech32()}/nfts/{identifier}")
else:
result = self.do_get_generic(f"accounts/{address.to_bech32()}/tokens/{token.identifier}")
result = self.do_get_generic(
f"accounts/{address.to_bech32()}/tokens/{token.identifier}"
)

return token_amount_from_api_response(result)

Expand Down Expand Up @@ -228,12 259,12 @@ def get_definition_of_tokens_collection(self, collection_name: str) -> TokensCol

def query_contract(self, query: SmartContractQuery) -> SmartContractQueryResponse:
request = smart_contract_query_to_vm_query_request(query)
response = self.do_post_generic('query', request)
response = self.do_post_generic("query", request)
return vm_query_response_to_smart_contract_query_response(response, query.function)

def do_get_generic(self, url: str, url_parameters: Optional[dict[str, Any]] = None) -> Any:
"""Does a generic GET request against the network(handles API enveloping)."""
url = f'{self.url}/{url}'
url = f"{self.url}/{url}"

if url_parameters is not None:
params = urllib.parse.urlencode(url_parameters)
Expand All @@ -243,9 274,10 @@ def do_get_generic(self, url: str, url_parameters: Optional[dict[str, Any]] = No
return response

def do_post_generic(
self, url: str, data: Any, url_parameters: Optional[dict[str, Any]] = None) -> Any:
self, url: str, data: Any, url_parameters: Optional[dict[str, Any]] = None
) -> Any:
"""Does a generic GET request against the network(handles API enveloping)."""
url = f'{self.url}/{url}'
url = f"{self.url}/{url}"

if url_parameters is not None:
params = urllib.parse.urlencode(url_parameters)
Expand Down
14 changes: 3 additions & 11 deletions multiversx_sdk/network_providers/api_network_provider_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 9,7 @@
from multiversx_sdk.network_providers.config import NetworkProviderConfig
from multiversx_sdk.network_providers.http_resources import \
account_from_api_response
from multiversx_sdk.network_providers.resources import (GetBlockArguments,
TokenAmountOnNetwork)
from multiversx_sdk.network_providers.resources import TokenAmountOnNetwork
from multiversx_sdk.smart_contracts.smart_contract_query import \
SmartContractQuery
from multiversx_sdk.testutils.wallets import load_wallets
Expand Down Expand Up @@ -41,15 40,8 @@ def test_get_network_status(self):
assert result.raw

def test_get_block(self):
args = GetBlockArguments(block_nonce=8639242)

with pytest.raises(Exception, match="Block hash not provided. Please set the `block_hash` in the arguments."):
self.api.get_block(args)

args = GetBlockArguments(
block_hash=bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f")
)
result_by_hash = self.api.get_block(args)
block_hash=bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f")
result_by_hash = self.api.get_block(block_hash)

assert result_by_hash.hash == bytes.fromhex("ded535cc0afb2dc5f9787e9560dc48d0b83564a3f994a390b228d894d854699f")
assert result_by_hash.nonce == 8639242
Expand Down
8 changes: 1 addition & 7 deletions multiversx_sdk/network_providers/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 6,7 @@
from multiversx_sdk.core.transaction_on_network import TransactionOnNetwork
from multiversx_sdk.network_providers.resources import (
AccountOnNetwork, AccountStorage, AccountStorageEntry, AwaitingOptions,
BlockOnNetwork, FungibleTokenMetadata, GetBlockArguments, NetworkConfig,
BlockOnNetwork, FungibleTokenMetadata, NetworkConfig,
NetworkStatus, TokenAmountOnNetwork, TokensCollectionMetadata,
TransactionCostResponse)
from multiversx_sdk.smart_contracts.smart_contract_query import (
Expand All @@ -20,12 20,6 @@ def get_network_config(self) -> NetworkConfig:
def get_network_status(self, shard: int) -> NetworkStatus:
...

def get_block(self, arguments: GetBlockArguments) -> BlockOnNetwork:
...

def get_latest_block(self, shard: int) -> BlockOnNetwork:
...

def get_account(self, address: Address) -> AccountOnNetwork:
...

Expand Down
Loading
Loading