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

api, tests: streamline invoke return types #319

Merged
merged 1 commit into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 4 additions & 4 deletions examples/contract-deploy-update-destroy.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ async def main(neoxp: shared.NeoExpress):
contract = GenericContract(contract_hash)
print("Calling `add` with input 1, result is: ", end="")
# using test_invoke here because we don't really care about the result being persisted to the chain
result = await facade.test_invoke(contract.call_function("add", [1]))
print(unwrap.as_int(result))
receipt = await facade.test_invoke(contract.call_function("add", [1]))
print(unwrap.as_int(receipt.result))

print("Updating contract with version 2...", end="")
nef_v2 = nef.NEF.from_file(files_path + "contract_v2.nef")
Expand All @@ -52,8 +52,8 @@ async def main(neoxp: shared.NeoExpress):

print("Calling `add` with input 1, result is: ", end="")
# Using test_invoke here because we don't really care about the result being persisted to the chain
result = await facade.test_invoke(contract.call_function("add", [1]))
print(unwrap.as_int(result))
receipt = await facade.test_invoke(contract.call_function("add", [1]))
print(unwrap.as_int(receipt.result))

print("Destroying contract...", end="")
# destroy also doesn't give any return value. So if it doesn't fail then it means success
Expand Down
7 changes: 4 additions & 3 deletions examples/nep-11-airdrop.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ async def example_airdrop(neoxp: shared.NeoExpress):

# Wrap the NFT contract
ntf = NEP11NonDivisibleContract(shared.nep11_token_hash)
balance = len(await facade.test_invoke(ntf.token_ids_owned_by(account.address)))
print(f"Current NFT balance: {balance}")
receipt = await facade.test_invoke(ntf.token_ids_owned_by(account.address))
print(f"Current NFT balance: {len(receipt.result)}")

# First we have to mint the NFTs to our own wallet
# We do this by sending 10 GAS to the contract. We do this in 2 separate transactions because the NFT is
Expand All @@ -47,7 +47,8 @@ async def example_airdrop(neoxp: shared.NeoExpress):
)
)
print(receipt.result)
token_ids = await facade.test_invoke(ntf.token_ids_owned_by(account.address))
receipt = await facade.test_invoke(ntf.token_ids_owned_by(account.address))
token_ids = receipt.result
print(f"New NFT token balance: {len(token_ids)}, ids: {token_ids}")

# Now let's airdrop the NFTs
Expand Down
8 changes: 4 additions & 4 deletions examples/nep17-airdrop.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ async def example_airdrop(neoxp: shared.NeoExpress):

# Use the generic NEP17 class to wrap the token
token = NEP17Contract(shared.coz_token_hash)
balance = await facade.test_invoke(token.balance_of(account.address))
print(f"Current COZ token balance: {balance}")
receipt = await facade.test_invoke(token.balance_of(account.address))
print(f"Current COZ token balance: {receipt.result}")

# First we have to mint the tokens to our own wallet
# We do this by sending NEO to the contract
Expand All @@ -46,8 +46,8 @@ async def example_airdrop(neoxp: shared.NeoExpress):

print(receipt.result)

balance = await facade.test_invoke(token.balance_of(account.address))
print(f"New COZ token balance: {balance}")
receipt = await facade.test_invoke(token.balance_of(account.address))
print(f"New COZ token balance: {receipt.result}")

# Now let's airdrop the tokens
destination_addresses = [
Expand Down
2 changes: 1 addition & 1 deletion examples/nep17-transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ async def example_transfer_other(neoxp: shared.NeoExpress):
facade = ChainFacade(rpc_host=neoxp.rpc_host)
facade.add_signer(
sign_insecure_with_account(account, password="123"),
Signer(account.script_hash), # default scope is CALLED_BY_ENTRY
Signer(account.script_hash), # default scope is te/CALLED_BY_ENTRY
)

source = account.address
Expand Down
3 changes: 2 additions & 1 deletion examples/vote.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ async def example_vote(neoxp: shared.NeoExpress):
# Dedicated Neo native contract wrapper
neo = NeoToken()
# get a list of candidates that can be voted on
candidates = await facade.test_invoke(neo.candidates_registered())
receipt = await facade.test_invoke(neo.candidates_registered())
candidates = receipt.result
# the example chain only has 1 candidate, use that
candidate_pk = candidates[0].public_key

Expand Down
77 changes: 62 additions & 15 deletions neo3/api/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ async def test_invoke(
f: ContractMethodResult[ReturnType],
*,
signers: Optional[Sequence[verification.Signer]] = None,
) -> ReturnType:
) -> InvokeReceipt[ReturnType]:
"""
Call a contract method in read-only mode.
This does not persist any state on the actual chain and therefore does not require signing or paying GAS.
Expand All @@ -180,9 +180,9 @@ async def test_invoke_multi(
f: list[ContractMethodResult],
*,
signers: Optional[Sequence[verification.Signer]] = None,
) -> Sequence:
) -> InvokeReceipt[Sequence]:
"""
Call all contract methods in one go (concurrently) and return the list of results.
Call all contract methods in one go (concatenated in 1 script) and return the list of results.

Args:
f: list of functions to call.
Expand All @@ -193,16 +193,43 @@ async def test_invoke_multi(
"""
if signers is None:
signers = self.signers
return await asyncio.gather(
*map(lambda c: self.test_invoke(c, signers=signers), f)
script = bytearray()
for call in f: # type: ContractMethodResult
script.extend(call.script)

wrapped: ContractMethodResult[None] = ContractMethodResult(script)
receipt = await self.test_invoke_raw(wrapped, signers=signers)

results = []
stack_offset = 0
for call in f:
res_cpy = deepcopy(receipt.result)
# adjust the stack so that it becomes transparent for the post-processing functions.
res_cpy.stack = res_cpy.stack[
stack_offset : stack_offset + call.return_count
]
if call.execution_processor is None:
results.append(res_cpy)
else:
results.append(call.execution_processor(res_cpy, 0))
stack_offset += call.return_count
return InvokeReceipt[Sequence](
receipt.tx_hash,
receipt.included_in_block,
receipt.confirmations,
receipt.gas_consumed,
receipt.state,
receipt.exception,
receipt.notifications,
results,
)

async def test_invoke_raw(
self,
f: ContractMethodResult[ReturnType],
*,
signers: Optional[Sequence[verification.Signer]] = None,
) -> noderpc.ExecutionResult:
) -> InvokeReceipt[noderpc.ExecutionResult]:
"""
Call a contract method in read-only mode.
This does not persist any state on the actual chain and therefore does not require signing or paying GAS.
Expand All @@ -217,15 +244,16 @@ async def test_invoke_raw(
"""
if signers is None:
signers = self.signers
return await self._test_invoke(f, signers=signers, return_raw=True)
res = await self._test_invoke(f, signers=signers, return_raw=True)
return cast(InvokeReceipt[noderpc.ExecutionResult], res)

async def _test_invoke(
self,
f: ContractMethodResult[ReturnType],
*,
signers: Optional[Sequence[verification.Signer]] = None,
return_raw: Optional[bool] = False,
):
) -> InvokeReceipt[ReturnType]:
"""
Args:
f:
Expand All @@ -234,9 +262,21 @@ async def _test_invoke(
"""
async with noderpc.NeoRpcClient(self.rpc_host) as client:
res = await client.invoke_script(f.script, signers)

if f.execution_processor is None or return_raw or res.state != "HALT":
return res
return f.execution_processor(res, 0)
result = res
else:
result = f.execution_processor(res, 0)
return InvokeReceipt[ReturnType](
types.UInt256.zero(),
-1,
-1,
res.gas_consumed,
res.state,
res.exception,
res.notifications,
result,
)

async def invoke(
self,
Expand Down Expand Up @@ -448,7 +488,7 @@ async def invoke_multi(
append_network_fee: int = 0,
append_system_fee: int = 0,
_post_processing: bool = True,
) -> Sequence:
) -> InvokeReceipt[Sequence]:
"""
Call all contract methods (concatenated) in one go and persist results on the chain. Costs GAS.
Waits for tx to be included in a block. Automatically post processes the execution results according to the
Expand Down Expand Up @@ -499,7 +539,16 @@ async def invoke_multi(
else:
results.append(call.execution_processor(res_cpy, 0))
stack_offset += call.return_count
return results
return InvokeReceipt[Sequence](
receipt.tx_hash,
receipt.included_in_block,
receipt.confirmations,
receipt.execution.gas_consumed,
receipt.execution.state,
receipt.execution.exception,
receipt.execution.notifications,
results,
)

async def invoke_multi_fast(
self,
Expand Down Expand Up @@ -560,7 +609,7 @@ async def invoke_multi_raw(
system_fee: int = 0,
append_network_fee: int = 0,
append_system_fee: int = 0,
) -> Sequence:
) -> InvokeReceipt[Sequence]:
"""
Call all contract methods (concatenated) in one go and persist results on the chain. Costs GAS.
Do not wait for tx to be included in a block. Do not post process the execution results according to
Expand Down Expand Up @@ -1288,7 +1337,6 @@ def process(res: noderpc.ExecutionResult, _: int = 0) -> list[types.UInt160]:

return ContractMethodResult(sb.to_array(), process)


def total_owned_by(
self, owner: types.UInt160 | NeoAddress, token_id: bytes
) -> ContractMethodResult[int]:
Expand All @@ -1304,7 +1352,6 @@ def total_owned_by(
)
return ContractMethodResult(sb.to_array(), unwrap.as_int)


def total_owned_by_friendly(
self, owner: types.UInt160 | NeoAddress, token_id: bytes
) -> ContractMethodResult[float]:
Expand Down
Loading