-
Notifications
You must be signed in to change notification settings - Fork 0
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
Smart Contract queries controller #16
Changes from all commits
8816915
b0e5fc4
10330c9
f5293ee
1cb307c
a5e84e1
474b307
618e63d
bfd94ad
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 |
---|---|---|
@@ -1,2 +1,2 @@ | ||
[flake8] | ||
ignore = E501 | ||
ignore = E501, E722 | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,12 @@ If using VSCode, restart it or follow these steps: | |
|
||
Run the tests as follows: | ||
|
||
This command runs all tests: | ||
``` | ||
pytest . | ||
``` | ||
|
||
If you want to skip network interaction tests run: | ||
``` | ||
pytest -m "not networkInteraction" | ||
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. Perhaps we should have it the reversed way? That is, by default all except Fine this way, as well - tests of |
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from multiversx_sdk.core.adapters.query_runner_adapter import \ | ||
QueryRunnerAdapter | ||
|
||
__all__ = ["QueryRunnerAdapter"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from typing import List, Optional, Protocol, Sequence | ||
|
||
from multiversx_sdk.core.address import Address | ||
from multiversx_sdk.core.contract_query import ContractQuery | ||
from multiversx_sdk.core.interfaces import IAddress | ||
from multiversx_sdk.core.smart_contract_query import ( | ||
SmartContractQuery, SmartContractQueryResponse) | ||
|
||
|
||
class IQuery(Protocol): | ||
def get_contract(self) -> IAddress: | ||
... | ||
|
||
def get_function(self) -> str: | ||
... | ||
|
||
def get_encoded_arguments(self) -> Sequence[str]: | ||
... | ||
|
||
def get_caller(self) -> Optional[IAddress]: | ||
... | ||
|
||
def get_value(self) -> int: | ||
... | ||
|
||
|
||
class IQueryResponse(Protocol): | ||
return_data: List[str] | ||
return_code: str | ||
return_message: str | ||
gas_used: int | ||
|
||
def get_return_data_parts(self) -> List[bytes]: | ||
... | ||
|
||
|
||
class INetworkProvider(Protocol): | ||
def query_contract(self, query: IQuery) -> IQueryResponse: | ||
... | ||
|
||
|
||
class QueryRunnerAdapter: | ||
def __init__(self, network_provider: INetworkProvider) -> None: | ||
self.network_provider = network_provider | ||
|
||
def run_query(self, query: SmartContractQuery) -> SmartContractQueryResponse: | ||
adapted_query = ContractQuery( | ||
contract=Address.new_from_bech32(query.contract), | ||
function=query.function, | ||
value=query.value if query.value else 0, | ||
encoded_arguments=[arg.hex() for arg in query.arguments], | ||
caller=Address.new_from_bech32(query.caller) if query.caller else None | ||
) | ||
|
||
adapted_query_response = self.network_provider.query_contract(adapted_query) | ||
|
||
return SmartContractQueryResponse( | ||
function=query.function, | ||
return_code=adapted_query_response.return_code, | ||
return_message=adapted_query_response.return_message, | ||
return_data_parts=adapted_query_response.get_return_data_parts() | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
|
||
|
||
from typing import List, Optional, Sequence | ||
|
||
from multiversx_sdk.core.interfaces import IAddress | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from typing import Any, List, Optional, Protocol | ||
|
||
from multiversx_sdk.core.smart_contract_query import ( | ||
SmartContractQuery, SmartContractQueryResponse) | ||
|
||
|
||
class IQueryRunner(Protocol): | ||
def run_query(self, query: SmartContractQuery) -> SmartContractQueryResponse: | ||
... | ||
|
||
|
||
class SmartContractQueriesController: | ||
def __init__(self, query_runner: IQueryRunner) -> None: | ||
self.query_runner = query_runner | ||
|
||
def create_query( | ||
self, | ||
contract: str, | ||
function: str, | ||
arguments: List[bytes], | ||
caller: Optional[str] = None, | ||
value: Optional[int] = None | ||
) -> SmartContractQuery: | ||
return SmartContractQuery( | ||
contract=contract, | ||
function=function, | ||
arguments=arguments, | ||
caller=caller, | ||
value=value | ||
) | ||
|
||
def run_query(self, query: SmartContractQuery) -> SmartContractQueryResponse: | ||
query_response = self.query_runner.run_query(query) | ||
return query_response | ||
|
||
def parse_query_response(self, response: SmartContractQueryResponse) -> List[Any]: | ||
return response.return_data_parts |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import base64 | ||
|
||
import pytest | ||
|
||
from multiversx_sdk.core.adapters.query_runner_adapter import \ | ||
QueryRunnerAdapter | ||
from multiversx_sdk.core.smart_contract_queries_controller import \ | ||
SmartContractQueriesController | ||
from multiversx_sdk.core.smart_contract_query import ( | ||
SmartContractQuery, SmartContractQueryResponse) | ||
from multiversx_sdk.network_providers.contract_query_response import \ | ||
ContractQueryResponse | ||
from multiversx_sdk.network_providers.proxy_network_provider import \ | ||
ProxyNetworkProvider | ||
from multiversx_sdk.testutils.mock_network_provider import MockNetworkProvider | ||
|
||
|
||
class TestSmartContractQueriesController: | ||
provider = ProxyNetworkProvider("https://devnet-api.multiversx.com") | ||
Comment on lines
+18
to
+19
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. use 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. there's no point in using the annotation for the class because there is no actual network interaction. In the first two tests i just create a query, then in the other tests i use a mock network provider and the last test actually interacts with the network but that one is annotated with |
||
query_runner = QueryRunnerAdapter(network_provider=provider) | ||
controller = SmartContractQueriesController(query_runner=query_runner) | ||
|
||
def test_create_query_without_arguments(self): | ||
contract = "erd1qqqqqqqqqqqqqpgqsnwuj85zv7t0wnxfetyqqyjvvg444lpk7uasxv8ktx" | ||
function = "getSum" | ||
|
||
query = self.controller.create_query( | ||
contract=contract, | ||
function=function, | ||
arguments=[] | ||
) | ||
assert query.contract == contract | ||
assert query.function == function | ||
assert query.arguments == [] | ||
assert query.caller is None | ||
assert query.value is None | ||
|
||
def test_create_query_with_arguments(self): | ||
contract = "erd1qqqqqqqqqqqqqpgqsnwuj85zv7t0wnxfetyqqyjvvg444lpk7uasxv8ktx" | ||
function = "getSum" | ||
|
||
query = self.controller.create_query( | ||
contract=contract, | ||
function=function, | ||
arguments=[int.to_bytes(7), "abba".encode()] | ||
) | ||
assert query.contract == contract | ||
assert query.function == function | ||
assert query.arguments == [int.to_bytes(7), "abba".encode()] | ||
assert query.caller is None | ||
assert query.value is None | ||
|
||
def test_run_query_with_mock_provider(self): | ||
network_provider = MockNetworkProvider() | ||
query_runner = QueryRunnerAdapter(network_provider=network_provider) | ||
controller = SmartContractQueriesController(query_runner) | ||
|
||
contract_query_response = ContractQueryResponse() | ||
contract_query_response.return_code = "ok" | ||
contract_query_response.return_data = [base64.b64encode("abba".encode()).decode()] | ||
|
||
network_provider.mock_query_contract_on_function("bar", contract_query_response) | ||
|
||
query = SmartContractQuery( | ||
contract="erd1qqqqqqqqqqqqqpgqvc7gdl0p4s97guh498wgz75k8sav6sjfjlwqh679jy", | ||
function="bar", | ||
arguments=[] | ||
) | ||
|
||
response = controller.run_query(query) | ||
assert response.return_code == "ok" | ||
assert response.return_data_parts == ["abba".encode()] | ||
|
||
def test_parse_query_response(self): | ||
network_provider = MockNetworkProvider() | ||
query_runner = QueryRunnerAdapter(network_provider=network_provider) | ||
controller = SmartContractQueriesController(query_runner) | ||
|
||
response = SmartContractQueryResponse( | ||
function="bar", | ||
return_code="ok", | ||
return_message="ok", | ||
return_data_parts=["abba".encode()] | ||
) | ||
|
||
parsed = controller.parse_query_response(response) | ||
assert parsed == ["abba".encode()] | ||
|
||
@pytest.mark.networkInteraction | ||
def test_run_query_on_network(self): | ||
contract = "erd1qqqqqqqqqqqqqpgqsnwuj85zv7t0wnxfetyqqyjvvg444lpk7uasxv8ktx" | ||
function = "getSum" | ||
|
||
query = self.controller.create_query( | ||
contract=contract, | ||
function=function, | ||
arguments=[] | ||
) | ||
|
||
query_response = self.controller.run_query(query) | ||
assert query_response.return_code == "ok" | ||
assert query_response.return_message == "" | ||
assert query_response.return_data_parts == [b'\x05'] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from typing import List, Optional | ||
|
||
|
||
class SmartContractQuery: | ||
def __init__( | ||
self, | ||
contract: str, | ||
function: str, | ||
arguments: List[bytes], | ||
caller: Optional[str] = None, | ||
value: Optional[int] = None | ||
) -> None: | ||
self.contract = contract | ||
self.function = function | ||
self.arguments = arguments | ||
self.caller = caller | ||
self.value = value | ||
|
||
|
||
class SmartContractQueryResponse: | ||
def __init__( | ||
self, | ||
function: str, | ||
return_code: str, | ||
return_message: str, | ||
return_data_parts: List[bytes] | ||
) -> None: | ||
self.function = function | ||
self.return_code = return_code | ||
self.return_message = return_message | ||
self.return_data_parts = return_data_parts |
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.
For reference (for reviewers):