Skip to content

Commit

Permalink
feat: work in progress on asset, application related state ops
Browse files Browse the repository at this point in the history
  • Loading branch information
aorumbayev committed Jul 12, 2024
1 parent 44000c9 commit 9339659
Show file tree
Hide file tree
Showing 14 changed files with 2,576 additions and 22 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ ignore = [
"RET503", # false negatives when involving typing.Never, covered by mypy anyway
"RET504",
"RET505", # stylistic choices for readability
"S101", # allow asserts
"S101", # allow asserts
]
unfixable = [
"F841", # don't delete unused local variables automatically
Expand All @@ -238,6 +238,7 @@ unfixable = [
"PT", # no pytest rules
]
"scripts/**/*.py" = ["T201"]
"scripts/refresh_test_artifacts.py" = ["S603"]

[tool.ruff.lint.flake8-annotations]
allow-star-arg-any = true
Expand Down
6 changes: 2 additions & 4 deletions scripts/refresh_test_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@ def compile_contract(folder: Path) -> None:
"hatch",
"run",
"puyapy",
"--log-level",
"debug",
str(contract_path),
"--out-dir",
"data",
]
subprocess.run(
compile_cmd, # noqa: S603
compile_cmd,
check=True,
env=ENV_WITH_NO_COLOR,
encoding="utf-8",
Expand All @@ -56,7 +54,7 @@ def generate_client(folder: Path) -> None:
str(client_path),
]
subprocess.run(
generate_cmd, # noqa: S603
generate_cmd,
check=True,
env=ENV_WITH_NO_COLOR,
encoding="utf-8",
Expand Down
22 changes: 19 additions & 3 deletions src/algopy_testing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import secrets
import string
from collections import ChainMap
from contextlib import contextmanager
from contextvars import ContextVar
from dataclasses import dataclass
Expand Down Expand Up @@ -659,7 +660,22 @@ def any_asset(
raise ValueError("Asset with such ID already exists in testing context!")

new_asset = algopy.Asset(asset_id or next(self._asset_id))
self._asset_data[int(new_asset.id)] = AssetFields(**asset_fields)
default_asset_fields = {
"total": self.any_uint64(),
"decimals": self.any_uint64(1, 6),
"default_frozen": False,
"unit_name": self.any_bytes(4),
"name": self.any_bytes(50),
"url": self.any_bytes(10),
"metadata_hash": self.any_bytes(32),
"manager": self.any_account(),
"freeze": self.any_account(),
"clawback": self.any_account(),
"creator": self.any_account(),
"reserve": self.any_account(),
}
merged_fields = dict(ChainMap(asset_fields, default_asset_fields)) # type: ignore[arg-type]
self._asset_data[int(new_asset.id)] = AssetFields(**merged_fields) # type: ignore[typeddict-item]
return new_asset

def any_application( # type: ignore[misc]
Expand Down Expand Up @@ -929,7 +945,8 @@ def any_application_call_transaction( # type: ignore[misc] # noqa: PLR0913
}
new_txn = algopy.gtxn.ApplicationCallTransaction(NULL_GTXN_GROUP_INDEX)

for key, value in {**kwargs, **dynamic_params}.items():
merged_params = dict(ChainMap(kwargs, dynamic_params))
for key, value in merged_params.items():
setattr(new_txn, key, value)

self.set_scratch_space(new_txn.txn_id, scratch_space or {})
Expand Down Expand Up @@ -1170,7 +1187,6 @@ def reset(self) -> None:
self._app_id = iter(range(1, 2**64))


#
_var: ContextVar[AlgopyTestContext] = ContextVar("_var")


Expand Down
87 changes: 78 additions & 9 deletions src/algopy_testing/op.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,13 +866,13 @@ def get_asset_param(a: algopy.Asset | algopy.UInt64 | int) -> tuple[typing.Any,
if not active_txn:
raise ValueError("No active transaction found to reference asset")

asset_id = a.value if isinstance(a, (algopy.Asset)) else int(a)
asset_data = active_txn.assets[asset_id]
asset_id = int(a.id) if isinstance(a, (algopy.Asset)) else int(a)
asset_data = context.get_asset(asset_id)

if asset_data is None:
return None, False

param = "config_" + name
param = name.replace("asset_", "")
value = getattr(asset_data, param, None)
return value, True

Expand Down Expand Up @@ -955,12 +955,81 @@ def asset_frozen(


class _AppParamsGet:
def __getattr__(self, name: str) -> Any:
raise NotImplementedError(
f"AppParamsGet.{name} is currently not available as a native "
"`algorand-python-testing` type. Use your own preferred testing "
"framework of choice to mock the behaviour."
)
@staticmethod
def _get_app_param_from_ctx(
index: algopy.Application | algopy.UInt64 | int, param: str
) -> tuple[Any, bool]:
from algopy_testing import get_test_context

context = get_test_context()

if not context:
raise ValueError(
"Test context is not initialized! Use `with algopy_testing_context()` "
"to access the context manager."
)

try:
application = context.get_application_data()[int(index)]
except IndexError:
return None, False

try:
response = getattr(application, param)
except AttributeError:
return None, False
else:
return response, True

@staticmethod
def app_approval_program(
a: algopy.Application | algopy.UInt64 | int, /
) -> tuple[algopy.Bytes, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "approval_program")

@staticmethod
def app_clear_state_program(
a: algopy.Application | algopy.UInt64 | int, /
) -> tuple[algopy.Bytes, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "clear_state_program")

@staticmethod
def app_global_num_uint(
a: algopy.Application | algopy.UInt64 | int, /
) -> tuple[algopy.UInt64, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "global_num_uint")

@staticmethod
def app_global_num_byte_slice(
a: algopy.Application | algopy.UInt64 | int, /
) -> tuple[algopy.UInt64, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "global_num_byte_slice")

@staticmethod
def app_local_num_uint(
a: algopy.Application | algopy.UInt64 | int, /
) -> tuple[algopy.UInt64, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "local_num_uint")

@staticmethod
def app_local_num_byte_slice(
a: algopy.Application | algopy.UInt64 | int, /
) -> tuple[algopy.UInt64, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "local_num_byte_slice")

@staticmethod
def app_extra_program_pages(
a: algopy.Application | algopy.UInt64 | int, /
) -> tuple[algopy.UInt64, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "extra_program_pages")

@staticmethod
def app_creator(a: algopy.Application | algopy.UInt64 | int, /) -> tuple[algopy.Account, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "creator")

@staticmethod
def app_address(a: algopy.Application | algopy.UInt64 | int, /) -> tuple[algopy.Account, bool]:
return _AppParamsGet._get_app_param_from_ctx(a, "address")


AppParamsGet = _AppParamsGet()
Expand Down
Empty file.
73 changes: 73 additions & 0 deletions tests/artifacts/StateOps/contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from algopy import Account, ARC4Contract, Asset, Bytes, UInt64, arc4, op


class StateOpsContract(ARC4Contract):
@arc4.abimethod()
def verify_asset_holding_get(self, a: Account, b: Asset) -> UInt64:
balance, _val = op.AssetHoldingGet.asset_balance(a, b)
return balance

@arc4.abimethod()
def verify_asset_frozen_get(self, a: Account, b: Asset) -> bool:
frozen, _val = op.AssetHoldingGet.asset_frozen(a, b)
return frozen

@arc4.abimethod()
def verify_asset_params_get_total(self, a: Asset) -> UInt64:
total, _val = op.AssetParamsGet.asset_total(a)
return total

@arc4.abimethod()
def verify_asset_params_get_decimals(self, a: Asset) -> UInt64:
decimals, _val = op.AssetParamsGet.asset_decimals(a)
return decimals

@arc4.abimethod()
def verify_asset_params_get_default_frozen(self, a: Asset) -> bool:
default_frozen, _val = op.AssetParamsGet.asset_default_frozen(a)
return default_frozen

@arc4.abimethod()
def verify_asset_params_get_unit_name(self, a: Asset) -> Bytes:
unit_name, _val = op.AssetParamsGet.asset_unit_name(a)
return unit_name

@arc4.abimethod()
def verify_asset_params_get_name(self, a: Asset) -> Bytes:
name, _val = op.AssetParamsGet.asset_name(a)
return name

@arc4.abimethod()
def verify_asset_params_get_url(self, a: Asset) -> Bytes:
url, _val = op.AssetParamsGet.asset_url(a)
return url

@arc4.abimethod()
def verify_asset_params_get_metadata_hash(self, a: Asset) -> Bytes:
metadata_hash, _val = op.AssetParamsGet.asset_metadata_hash(a)
return metadata_hash

@arc4.abimethod()
def verify_asset_params_get_manager(self, a: Asset) -> Bytes:
manager, _val = op.AssetParamsGet.asset_manager(a)
return manager.bytes

@arc4.abimethod()
def verify_asset_params_get_reserve(self, a: Asset) -> Bytes:
reserve, _val = op.AssetParamsGet.asset_reserve(a)
return reserve.bytes

@arc4.abimethod()
def verify_asset_params_get_freeze(self, a: Asset) -> Bytes:
freeze, _val = op.AssetParamsGet.asset_freeze(a)
return freeze.bytes

@arc4.abimethod()
def verify_asset_params_get_clawback(self, a: Asset) -> Bytes:
clawback, _val = op.AssetParamsGet.asset_clawback(a)
return clawback.bytes

@arc4.abimethod()
def verify_asset_params_get_creator(self, a: Asset) -> Bytes:
creator, _val = op.AssetParamsGet.asset_creator(a)
return creator.bytes
Loading

0 comments on commit 9339659

Please sign in to comment.