Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
aorumbayev committed Dec 7, 2024
1 parent 1e224bd commit 0c04d5c
Show file tree
Hide file tree
Showing 19 changed files with 463 additions and 336 deletions.
13 changes: 10 additions & 3 deletions src/algokit_utils/accounts/account_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def from_mnemonic(self, mnemonic: str) -> Account:
private_key = to_private_key(mnemonic)
account = Account(private_key=private_key)
self._accounts[account.address] = account
self.set_signer(account.address, AccountTransactionSigner(private_key=private_key))
return account

def from_environment(self, name: str, fund_with: AlgoAmount | None = None) -> Account:
Expand All @@ -115,12 +116,14 @@ def from_environment(self, name: str, fund_with: AlgoAmount | None = None) -> Ac
private_key = mnemonic.to_private_key(account_mnemonic)
account = Account(private_key=private_key)
self._accounts[account.address] = account
self.set_signer(account.address, AccountTransactionSigner(private_key=private_key))
return account

if self._client_manager.is_local_net():
kmd_account = self._kmd_account_manager.get_or_create_wallet_account(name, fund_with)
account = Account(private_key=kmd_account.private_key)
self._accounts[account.address] = account
self.set_signer(account.address, AccountTransactionSigner(private_key=kmd_account.private_key))
return account

raise ValueError(f"Missing environment variable {name.upper()}_MNEMONIC when looking for account {name}")
Expand All @@ -134,11 +137,13 @@ def from_kmd(

account = Account(private_key=kmd_account.private_key)
self._accounts[account.address] = account
self.set_signer(account.address, AccountTransactionSigner(private_key=kmd_account.private_key))
return account

def rekeyed(self, sender: str, account: Account) -> Account:
self._accounts[sender] = account
return account
def rekeyed(self, sender: Account | str, account: Account) -> Account:
sender_address = sender.address if isinstance(sender, Account) else sender
self._accounts[sender_address] = account
return Account(address=sender_address, private_key=account.private_key)

def random(self) -> Account:
"""
Expand All @@ -148,12 +153,14 @@ def random(self) -> Account:
"""
account = Account.new_account()
self._accounts[account.address] = account
self.set_signer(account.address, AccountTransactionSigner(private_key=account.private_key))
return account

def localnet_dispenser(self) -> Account:
kmd_account = self._kmd_account_manager.get_localnet_dispenser_account()
account = Account(private_key=kmd_account.private_key)
self._accounts[account.address] = account
self.set_signer(account.address, AccountTransactionSigner(private_key=kmd_account.private_key))
return account

def dispenser_from_environment(self) -> Account:
Expand Down
30 changes: 24 additions & 6 deletions src/algokit_utils/applications/app_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import copy
import json
import os
from dataclasses import dataclass
from dataclasses import dataclass, fields
from typing import TYPE_CHECKING, Any, Protocol, TypeVar

import algosdk
Expand Down Expand Up @@ -572,11 +572,29 @@ def delete(self, params: AppClientMethodCallParams) -> AppDeleteMethodCall:
)
return AppDeleteMethodCall(**input_params)

def update(self, params: AppClientMethodCallParams) -> AppUpdateMethodCall:
input_params = self._get_abi_params(
params.__dict__, on_complete=algosdk.transaction.OnComplete.UpdateApplicationOC
def update(
self, params: AppClientMethodCallParams | AppClientMethodCallWithCompilationAndSendParams
) -> AppUpdateMethodCall:
compile_params = (
self._client.compile(
app_spec=self._client.app_spec,
app_manager=self._algorand.app,
deploy_time_params=params.deploy_time_params,
updatable=params.updatable,
deletable=params.deletable,
).__dict__
if isinstance(params, AppClientMethodCallWithCompilationAndSendParams)
else {}
)
return AppUpdateMethodCall(**input_params)

input_params = {
**self._get_abi_params(params.__dict__, on_complete=algosdk.transaction.OnComplete.UpdateApplicationOC),
**compile_params,
}
# Filter input_params to include only fields valid for AppUpdateMethodCall
app_update_method_call_fields = {field.name for field in fields(AppUpdateMethodCall)}
filtered_input_params = {k: v for k, v in input_params.items() if k in app_update_method_call_fields}
return AppUpdateMethodCall(**filtered_input_params)

def close_out(self, params: AppClientMethodCallParams) -> AppCallMethodCall:
input_params = self._get_abi_params(params.__dict__, on_complete=algosdk.transaction.OnComplete.CloseOutOC)
Expand Down Expand Up @@ -786,7 +804,7 @@ def call(self, params: AppClientMethodCallWithSendParams) -> SendAppTransactionR
confirmations=simulate_response.confirmations,
group_id=simulate_response.group_id or "",
returns=simulate_response.returns,
return_value=simulate_response.returns[-1].return_value,
return_value=simulate_response.returns[-1],
)

return self._client._handle_call_errors(
Expand Down
94 changes: 59 additions & 35 deletions src/algokit_utils/applications/app_deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Literal

import algosdk
from algosdk.atomic_transaction_composer import TransactionSigner
from algosdk.atomic_transaction_composer import ABIResult, TransactionSigner
from algosdk.logic import get_application_address
from algosdk.transaction import OnComplete, Transaction
from algosdk.v2client.indexer import IndexerClient
Expand All @@ -20,7 +20,6 @@
)
from algokit_utils.applications.app_manager import AppManager, BoxReference, TealTemplateParams
from algokit_utils.config import config
from algokit_utils.models.abi import ABIValue
from algokit_utils.transactions.transaction_composer import (
AppCreateMethodCall,
AppCreateParams,
Expand Down Expand Up @@ -113,11 +112,15 @@ class AppDeployResult:
app_id: int | None = None
app_address: str | None = None
transaction: Transaction | None = None
tx_id: str | None = None
transactions: list[Transaction] | None = None
tx_ids: list[str] | None = None
confirmation: algosdk.v2client.algod.AlgodResponseType | None = None
confirmations: list[algosdk.v2client.algod.AlgodResponseType] | None = None
compiled_approval: dict | None = None
compiled_clear: dict | None = None
return_value: ABIValue | None = None
delete_return: ABIValue | None = None
return_value: ABIResult | None = None
delete_return_value: ABIResult | None = None
delete_result: ConfirmedTransactionResult | None = None


Expand Down Expand Up @@ -319,8 +322,12 @@ def _create_app(

return AppDeployResult(
**app_metadata_dict,
tx_id=result.tx_id,
tx_ids=result.tx_ids,
transaction=result.transaction,
transactions=result.transactions,
confirmation=result.confirmation,
confirmations=result.confirmations,
return_value=result.return_value,
)

Expand All @@ -342,7 +349,7 @@ def _handle_schema_break(
return self._create_app(deployment, approval_program, clear_program)

if existing_app.deletable:
return self._create_and_delete_app(deployment, existing_app, approval_program, clear_program)
return self._replace_app(deployment, existing_app, approval_program, clear_program)
else:
raise ValueError("App is not deletable but onSchemaBreak=ReplaceApp, " "cannot delete and recreate app")

Expand All @@ -369,13 +376,13 @@ def _handle_update(

if deployment.on_update in (OnUpdate.ReplaceApp, "replace"):
if existing_app.deletable:
return self._create_and_delete_app(deployment, existing_app, approval_program, clear_program)
return self._replace_app(deployment, existing_app, approval_program, clear_program)
else:
raise ValueError("App is not deletable but onUpdate=ReplaceApp, " "cannot delete and recreate app")

raise ValueError(f"Unsupported onUpdate value: {deployment.on_update}")

def _create_and_delete_app(
def _replace_app(
self,
deployment: AppDeployParams,
existing_app: AppMetaData,
Expand All @@ -386,33 +393,35 @@ def _create_and_delete_app(

# Add create transaction
if isinstance(deployment.create_params, AppCreateMethodCall):
create_params = AppCreateMethodCall(
**{
**deployment.create_params.__dict__,
"approval_program": approval_program,
"clear_state_program": clear_program,
}
composer.add_app_create_method_call(
AppCreateMethodCall(
**{
**deployment.create_params.__dict__,
"approval_program": approval_program,
"clear_state_program": clear_program,
}
)
)
composer.add_app_create_method_call(create_params)
else:
create_params = AppCreateParams(
**{
**deployment.create_params.__dict__,
"approval_program": approval_program,
"clear_state_program": clear_program,
}
composer.add_app_create(
AppCreateParams(
**{
**deployment.create_params.__dict__,
"approval_program": approval_program,
"clear_state_program": clear_program,
}
)
)
composer.add_app_create(create_params)

# Add delete transaction
if isinstance(deployment.delete_params, AppDeleteMethodCall):
delete_params = AppDeleteMethodCall(
delete_call_params = AppDeleteMethodCall(
**{
**deployment.delete_params.__dict__,
"app_id": existing_app.app_id,
}
)
composer.add_app_delete_method_call(delete_params)
composer.add_app_delete_method_call(delete_call_params)
else:
delete_params = AppDeleteParams(
**{
Expand All @@ -424,30 +433,43 @@ def _create_and_delete_app(

result = composer.send()

app_id = int(result.confirmations[0]["application-index"])
app_id = int(result.confirmations[0]["application-index"]) # type: ignore[call-overload]
app_metadata = AppMetaData(
app_id=app_id,
app_address=get_application_address(app_id),
**deployment.metadata.__dict__,
created_metadata=deployment.metadata,
created_round=result.confirmations[0]["confirmed-round"],
updated_round=result.confirmations[0]["confirmed-round"],
created_round=result.confirmations[0]["confirmed-round"], # type: ignore[call-overload]
updated_round=result.confirmations[0]["confirmed-round"], # type: ignore[call-overload]
deleted=False,
)
self._update_app_lookup(deployment.create_params.sender, app_metadata)

app_metadata_dict = app_metadata.__dict__
app_metadata_dict["operation_performed"] = OperationPerformed.Replace
app_metadata_dict["app_id"] = app_id
app_metadata_dict["app_address"] = get_application_address(app_id)

# Extract return_value and delete_return_value from ABIResult
return_value = result.returns[0] if result.returns and isinstance(result.returns[0], ABIResult) else None
delete_return_value = (
result.returns[-1] if len(result.returns) > 1 and isinstance(result.returns[-1], ABIResult) else None
)

return AppDeployResult(
operation_performed="replace",
app_id=app_id,
app_address=get_application_address(app_id),
**app_metadata_dict,
tx_id=result.tx_ids[0],
tx_ids=result.tx_ids,
transaction=result.transactions[0],
transactions=result.transactions,
confirmation=result.confirmations[0],
return_value=result.returns[0] if result.returns else None,
delete_return=result.returns[-1] if len(result.returns) > 1 else None,
delete_result={
"transaction": result.transactions[-1],
"confirmation": result.confirmations[-1],
},
confirmations=result.confirmations,
return_value=return_value,
delete_return_value=delete_return_value,
delete_result=ConfirmedTransactionResult(
transaction=result.transactions[-1],
confirmation=result.confirmations[-1],
),
)

def _update_app(
Expand Down Expand Up @@ -498,7 +520,9 @@ def _update_app(
**app_metadata.__dict__,
operation_performed=OperationPerformed.Update,
transaction=result.transaction,
transactions=result.transactions,
confirmation=result.confirmation,
confirmations=result.confirmations,
return_value=result.return_value,
)

Expand Down
Loading

0 comments on commit 0c04d5c

Please sign in to comment.