-
Notifications
You must be signed in to change notification settings - Fork 36
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
Added suport for contract queries using abi file #436
Changes from 5 commits
dd1442d
1d47395
0e80e04
30b7606
140a871
6e7a967
3413f8c
650a8e6
c86bfff
37151e7
f3168b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
import logging | ||
import sys | ||
from pathlib import Path | ||
from typing import Any, List, Optional, Tuple | ||
from typing import Any, List, Optional, Tuple, cast | ||
|
||
from multiversx_sdk import (Address, Mnemonic, UserPEM, UserSecretKey, | ||
UserWallet) | ||
|
@@ -134,6 +134,9 @@ def wallet_new(args: Any): | |
else: | ||
mnemonic = Mnemonic.generate() | ||
|
||
# this is done to get rid of the Pylance error: possibly unbound | ||
mnemonic = cast(Mnemonic, mnemonic) # type: ignore | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can also fix the warning by extracting the above logic to a separate function e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indeed, extracted to a separate function. |
||
|
||
print(f"Mnemonic: {mnemonic.get_text()}") | ||
print(f"Wallet address: {mnemonic.derive_key().generate_public_key().to_address(address_hrp).to_bech32()}") | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
import base64 | ||
import logging | ||
from pathlib import Path | ||
from typing import Any, List, Optional, Protocol, Sequence, Union | ||
from typing import Any, Dict, List, Optional, Protocol, Sequence, Union | ||
|
||
from multiversx_sdk import (Address, SmartContractTransactionsFactory, Token, | ||
from multiversx_sdk import (Address, QueryRunnerAdapter, | ||
SmartContractQueriesController, | ||
SmartContractQueryResponse, | ||
SmartContractTransactionsFactory, Token, | ||
TokenComputer, TokenTransfer, Transaction, | ||
TransactionPayload) | ||
from multiversx_sdk.abi import Abi | ||
|
@@ -63,6 +65,10 @@ | |
return_data: List[str] | ||
return_code: str | ||
return_message: str | ||
gas_used: int | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we reference the latest & greatest feat/next of sdk-py, maybe we can drop this interface (or maybe not yet)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we can drop it. |
||
|
||
def get_return_data_parts(self) -> List[bytes]: | ||
... | ||
|
||
|
||
class IConfig(Protocol): | ||
|
@@ -95,7 +101,7 @@ | |
guardian: str) -> Transaction: | ||
args = arguments if arguments else [] | ||
if should_prepare_args: | ||
args = self.prepare_args_for_factory(args) | ||
args = self._prepare_args_for_factory(args) | ||
|
||
tx = self._factory.create_transaction_for_deploy( | ||
sender=owner.address, | ||
|
@@ -133,7 +139,7 @@ | |
|
||
args = arguments if arguments else [] | ||
if should_prepare_args: | ||
args = self.prepare_args_for_factory(args) | ||
args = self._prepare_args_for_factory(args) | ||
|
||
tx = self._factory.create_transaction_for_execute( | ||
sender=caller.address, | ||
|
@@ -170,7 +176,7 @@ | |
guardian: str) -> Transaction: | ||
args = arguments if arguments else [] | ||
if should_prepare_args: | ||
args = self.prepare_args_for_factory(args) | ||
args = self._prepare_args_for_factory(args) | ||
|
||
tx = self._factory.create_transaction_for_upgrade( | ||
sender=owner.address, | ||
|
@@ -192,6 +198,40 @@ | |
|
||
return tx | ||
|
||
def query_contract(self, | ||
Check warning on line 201 in multiversx_sdk_cli/contracts.py GitHub Actions / runner / mypy
Check warning on line 201 in multiversx_sdk_cli/contracts.py GitHub Actions / runner / mypy
Check warning on line 201 in multiversx_sdk_cli/contracts.py GitHub Actions / runner / mypy
Check warning on line 201 in multiversx_sdk_cli/contracts.py GitHub Actions / runner / mypy
|
||
contract_address: IAddress, | ||
proxy: INetworkProvider, | ||
function: str, | ||
arguments: List[Any], | ||
should_prepare_args: bool) -> List[Any]: | ||
args = arguments if arguments else [] | ||
if should_prepare_args: | ||
args = self._prepare_args_for_factory(args) | ||
|
||
query_runner = QueryRunnerAdapter(proxy) | ||
sc_query_controller = SmartContractQueriesController(query_runner, self._abi) | ||
|
||
query = sc_query_controller.create_query( | ||
contract=contract_address.to_bech32(), | ||
function=function, | ||
arguments=args | ||
) | ||
|
||
response = sc_query_controller.run_query(query) | ||
|
||
if self._abi: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This if-else is not consistent. b) errors for bad queries 1/ What is the reason you return different types? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good to me |
||
return sc_query_controller.parse_query_response(response) | ||
else: | ||
return [self._query_response_to_dict(response)] | ||
|
||
def _query_response_to_dict(self, response: SmartContractQueryResponse) -> Dict[str, Any]: | ||
return { | ||
"function": response.function, | ||
"returnCode": response.return_code, | ||
"returnMessage": response.return_message, | ||
"returnDataParts": [part.hex() for part in response.return_data_parts] | ||
} | ||
|
||
def _prepare_token_transfers(self, transfers: List[str]) -> List[TokenTransfer]: | ||
token_computer = TokenComputer() | ||
token_transfers: List[TokenTransfer] = [] | ||
|
@@ -207,12 +247,12 @@ | |
|
||
return token_transfers | ||
|
||
def prepare_args_for_factory(self, arguments: List[str]) -> List[Any]: | ||
def _prepare_args_for_factory(self, arguments: List[str]) -> List[Any]: | ||
args: List[Any] = [] | ||
|
||
for arg in arguments: | ||
if arg.startswith(HEX_PREFIX): | ||
args.append(hex_to_bytes(arg)) | ||
args.append(self._hex_to_bytes(arg)) | ||
elif arg.isnumeric(): | ||
args.append(int(arg)) | ||
elif arg.startswith(DEFAULT_HRP): | ||
|
@@ -228,64 +268,11 @@ | |
|
||
return args | ||
|
||
|
||
def query_contract( | ||
contract_address: IAddress, | ||
proxy: INetworkProvider, | ||
function: str, | ||
arguments: List[Any], | ||
value: int = 0, | ||
caller: Optional[IAddress] = None | ||
) -> List[Any]: | ||
response_data = query_detailed(contract_address, proxy, function, arguments, value, caller) | ||
return_data = response_data.return_data | ||
return [_interpret_return_data(data) for data in return_data] | ||
|
||
|
||
def query_detailed(contract_address: IAddress, proxy: INetworkProvider, function: str, arguments: List[Any], | ||
value: int = 0, caller: Optional[IAddress] = None) -> Any: | ||
arguments = arguments or [] | ||
# Temporary workaround, until we use sdk-core's serializer. | ||
arguments_hex = [_prepare_argument(arg) for arg in arguments] | ||
prepared_arguments_bytes = [bytes.fromhex(arg) for arg in arguments_hex] | ||
|
||
query = ContractQuery(contract_address, function, value, prepared_arguments_bytes, caller) | ||
|
||
response = proxy.query_contract(query) | ||
# Temporary workaround, until we add "isSuccess" on the response class. | ||
if response.return_code != "ok": | ||
raise RuntimeError(f"Query failed: {response.return_message}") | ||
return response | ||
|
||
|
||
def _interpret_return_data(data: str) -> Any: | ||
if not data: | ||
return data | ||
|
||
try: | ||
as_bytes = base64.b64decode(data) | ||
as_hex = as_bytes.hex() | ||
as_number = _interpret_as_number_if_safely(as_hex) | ||
|
||
result = QueryResult(data, as_hex, as_number) | ||
return result | ||
except Exception: | ||
logger.warn(f"Cannot interpret return data: {data}") | ||
return None | ||
|
||
|
||
def _interpret_as_number_if_safely(as_hex: str) -> Optional[int]: | ||
""" | ||
Makes sure the string can be safely converted to an int (and then back to a string). | ||
|
||
See: | ||
- https://stackoverflow.com/questions/73693104/valueerror-exceeds-the-limit-4300-for-integer-string-conversion | ||
- https://github.com/python/cpython/issues/95778 | ||
""" | ||
try: | ||
return int(str(int(as_hex or "0", 16))) | ||
except Exception: | ||
return None | ||
def _hex_to_bytes(self, arg: str): | ||
argument = arg[len(HEX_PREFIX):] | ||
argument = argument.upper() | ||
argument = ensure_even_length(argument) | ||
return bytes.fromhex(argument) | ||
|
||
|
||
def prepare_execute_transaction_data(function: str, arguments: List[Any]) -> TransactionPayload: | ||
|
@@ -297,14 +284,7 @@ | |
return TransactionPayload.from_str(tx_data) | ||
|
||
|
||
def hex_to_bytes(arg: str): | ||
argument = arg[len(HEX_PREFIX):] | ||
argument = argument.upper() | ||
argument = ensure_even_length(argument) | ||
return bytes.fromhex(argument) | ||
|
||
|
||
# only used for contract queries and stake operations | ||
# only used for stake operations | ||
def _prepare_argument(argument: Any): | ||
as_str = str(argument) | ||
as_hex = _to_hex(as_str) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo
below
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed