Skip to content

Commit

Permalink
Merge pull request #9 from algorandfoundation/rename-sandbox
Browse files Browse the repository at this point in the history
fix: rename sandbox to localnet
  • Loading branch information
robdmoore authored Mar 29, 2023
2 parents 05fcedd + ea43db2 commit c73cd62
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 32 deletions.
9 changes: 4 additions & 5 deletions src/algokit_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
get_account_from_mnemonic,
get_dispenser_account,
get_kmd_wallet_account,
get_localnet_default_account,
get_or_create_kmd_wallet_account,
get_sandbox_default_account,
)
from algokit_utils.application_client import (
ABICallArgs,
Expand Down Expand Up @@ -62,14 +62,14 @@
get_algod_client,
get_indexer_client,
get_kmd_client_from_algod_client,
is_sandbox,
is_localnet,
)

__all__ = [
"create_kmd_wallet_account",
"get_account_from_mnemonic",
"get_or_create_kmd_wallet_account",
"get_sandbox_default_account",
"get_localnet_default_account",
"get_dispenser_account",
"get_kmd_wallet_account",
"get_account",
Expand All @@ -87,7 +87,6 @@
"ABICallArgsDict",
"ABICreateCallArgs",
"ABICreateCallArgsDict",
"ApplicationClient",
"CommonCallParameters",
"CommonCallParametersDict",
"CreateCallParameters",
Expand Down Expand Up @@ -120,7 +119,7 @@
"get_algod_client",
"get_indexer_client",
"get_kmd_client_from_algod_client",
"is_sandbox",
"is_localnet",
"TransferParameters",
"transfer",
]
37 changes: 21 additions & 16 deletions src/algokit_utils/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

from algokit_utils._transfer import TransferParameters, transfer
from algokit_utils.models import Account
from algokit_utils.network_clients import get_kmd_client_from_algod_client, is_sandbox
from algokit_utils.network_clients import get_kmd_client_from_algod_client, is_localnet

__all__ = [
"get_account_from_mnemonic",
"get_or_create_kmd_wallet_account",
"get_sandbox_default_account",
"get_localnet_default_account",
"get_dispenser_account",
"get_kmd_wallet_account",
"get_account",
Expand All @@ -26,12 +26,14 @@


def get_account_from_mnemonic(mnemonic: str) -> Account:
"""Convert a mnemonic (25 word passphrase) into an Account"""
private_key = to_private_key(mnemonic) # type: ignore[no-untyped-call]
address = address_from_private_key(private_key) # type: ignore[no-untyped-call]
return Account(private_key, address)


def create_kmd_wallet_account(kmd_client: KMDClient, name: str) -> Account:
"""Creates a wallet with specified name"""
wallet_id = kmd_client.create_wallet(name, "")["id"] # type: ignore[no-untyped-call]
wallet_handle = kmd_client.init_wallet_handle(wallet_id, "") # type: ignore[no-untyped-call]
kmd_client.generate_key(wallet_handle) # type: ignore[no-untyped-call]
Expand All @@ -46,6 +48,7 @@ def create_kmd_wallet_account(kmd_client: KMDClient, name: str) -> Account:
def get_or_create_kmd_wallet_account(
client: AlgodClient, name: str, fund_with_algos: float = 1000, kmd_client: KMDClient | None = None
) -> Account:
"""Returns a wallet with specified name, or creates one if not found"""
kmd_client = kmd_client or get_kmd_client_from_algod_client(client)
account = get_kmd_wallet_account(client, kmd_client, name)

Expand All @@ -54,12 +57,12 @@ def get_or_create_kmd_wallet_account(
assert isinstance(account_info, dict)
if account_info["amount"] > 0:
return account
logger.debug(f"Found existing account in Sandbox with name '{name}', but no funds in the account.")
logger.debug(f"Found existing account in LocalNet with name '{name}', but no funds in the account.")
else:
account = create_kmd_wallet_account(kmd_client, name)

logger.debug(
f"Couldn't find existing account in Sandbox with name '{name}'. "
f"Couldn't find existing account in LocalNet with name '{name}'. "
f"So created account {account.address} with keys stored in KMD."
)

Expand All @@ -82,9 +85,10 @@ def _is_default_account(account: dict[str, Any]) -> bool:
return bool(account["status"] != "Offline" and account["amount"] > 1_000_000_000)


def get_sandbox_default_account(client: AlgodClient) -> Account:
if not is_sandbox(client):
raise Exception("Can't get a default account from non Sandbox network")
def get_localnet_default_account(client: AlgodClient) -> Account:
"""Returns the default Account in a LocalNet instance"""
if not is_localnet(client):
raise Exception("Can't get a default account from non LocalNet network")

account = get_kmd_wallet_account(
client, get_kmd_client_from_algod_client(client), "unencrypted-default-wallet", _is_default_account
Expand All @@ -94,14 +98,16 @@ def get_sandbox_default_account(client: AlgodClient) -> Account:


def get_dispenser_account(client: AlgodClient) -> Account:
if is_sandbox(client):
return get_sandbox_default_account(client)
"""Returns an Account based on DISPENSER_MNENOMIC environment variable or the default account on LocalNet"""
if is_localnet(client):
return get_localnet_default_account(client)
return get_account(client, "DISPENSER")


def get_kmd_wallet_account(
client: AlgodClient, kmd_client: KMDClient, name: str, predicate: Callable[[dict[str, Any]], bool] | None = None
) -> Account | None:
"""Returns wallet matching specified name and predicate or None if not found"""
wallets: list[dict] = kmd_client.list_wallets() # type: ignore[no-untyped-call]

wallet = next((w for w in wallets if w["name"] == name), None)
Expand Down Expand Up @@ -133,9 +139,9 @@ def get_account(
) -> Account:
"""Returns an Algorand account with private key loaded by convention based on the given name identifier.
## Convention:
# Convention
**Non-LocalNet:** will load os.environ['{name}_MNEMONIC'] as a mnemonic secret;
**Non-LocalNet:** will load `os.environ[f"{name}_MNEMONIC"]` as a mnemonic secret
Be careful how the mnemonic is handled, never commit it into source control and ideally load it via a
secret storage service rather than the file system.
Expand All @@ -145,12 +151,11 @@ def get_account(
This allows you to write code that will work seamlessly in production and local development (LocalNet) without
manual config locally (including when you reset the LocalNet).
@example Default
If you have a mnemonic secret loaded into `os.environ['ACCOUNT_MNEMONIC'] then you can call the following to get
# Example
If you have a mnemonic secret loaded into `os.environ["ACCOUNT_MNEMONIC"]` then you can call the following to get
that private key loaded into an account object:
```python
account = await get_account('ACCOUNT', algod)
account = get_account('ACCOUNT', algod)
```
If that code runs against LocalNet then a wallet called 'ACCOUNT' will automatically be created with an account
Expand All @@ -162,7 +167,7 @@ def get_account(
if mnemonic:
return get_account_from_mnemonic(mnemonic)

if is_sandbox(client):
if is_localnet(client):
account = get_or_create_kmd_wallet_account(client, name, fund_with_algos, kmd_client)
os.environ[mnemonic_key] = from_private_key(account.private_key) # type: ignore[no-untyped-call]
return account
Expand Down
20 changes: 14 additions & 6 deletions src/algokit_utils/application_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,22 @@ def deploy(
update_args: ABICallArgs | ABICallArgsDict | None = None,
delete_args: ABICallArgs | ABICallArgsDict | None = None,
) -> au_deploy.DeployResponse:
"""Idempotently deploy (create, update/delete if changed) an app against the given name via the given creator
account, including deploy-time template placeholder substitutions. To understand the architecture decisions
behind this functionality please see
https://github.com/algorandfoundation/algokit-cli/blob/main/docs/architecture-decisions/2023-01-12_smart-contract-deployment.md
Note: if there is a breaking state schema change to an existing app (and `on_schema_break` is set to
"""Deploy an application and update client to reference it.
Idempotently deploy (create, update/delete if changed) an app against the given name via the given creator
account, including deploy-time template placeholder substitutions.
To understand the architecture decisions behind this functionality please see
<https://github.com/algorandfoundation/algokit-cli/blob/main/docs/architecture-decisions/2023-01-12_smart-contract-deployment.md>
```{note}
If there is a breaking state schema change to an existing app (and `on_schema_break` is set to
'ReplaceApp' the existing app will be deleted and re-created.
Note: if there is an update (different TEAL code) to an existing app (and `on_update` is set to 'ReplaceApp')
```
```{note}
If there is an update (different TEAL code) to an existing app (and `on_update` is set to 'ReplaceApp')
the existing app will be deleted and re-created.
```
"""
before = self._approval_program, self._clear_program, self.sender, self.signer, self.app_id
try:
Expand Down
6 changes: 3 additions & 3 deletions src/algokit_utils/network_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"get_algod_client",
"get_indexer_client",
"get_kmd_client_from_algod_client",
"is_sandbox",
"is_localnet",
]

_PURE_STAKE_HOST = "purestake.io"
Expand Down Expand Up @@ -39,7 +39,7 @@ def get_indexer_client(config: AlgoClientConfig | None = None) -> IndexerClient:
return IndexerClient(config.token, config.server, headers) # type: ignore[no-untyped-call]


def is_sandbox(client: AlgodClient) -> bool:
def is_localnet(client: AlgodClient) -> bool:
"""Returns True if client genesis is devnet-v1 or sandnet-v1"""
params = client.suggested_params()
return params.gen in ["devnet-v1", "sandnet-v1"]
Expand All @@ -48,7 +48,7 @@ def is_sandbox(client: AlgodClient) -> bool:
def get_kmd_client_from_algod_client(client: AlgodClient) -> KMDClient:
"""Returns and SDK KMDClient using the same address as provided AlgodClient, but on port specified by
KMD_PORT environment variable, or 4092 by default"""
# We can only use Kmd on the Sandbox otherwise it's not exposed so this makes some assumptions
# We can only use Kmd on the LocalNet otherwise it's not exposed so this makes some assumptions
# (e.g. same token and server as algod and port 4002 by default)
port = os.getenv("KMD_PORT", "4002")
server = _replace_kmd_port(client.algod_address, port)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_deploy_scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
get_account,
get_algod_client,
get_indexer_client,
get_sandbox_default_account,
get_localnet_default_account,
)

from tests.conftest import check_output_stability, get_specs, get_unique_name, read_spec
Expand Down Expand Up @@ -76,7 +76,7 @@ def check_log_stability(self, replacements: dict[str, str] | None = None, suffix
check_output_stability(logs, test_name=self.request.node.name + suffix)

def _normalize_logs(self, logs: str) -> str:
dispenser = get_sandbox_default_account(self.algod_client)
dispenser = get_localnet_default_account(self.algod_client)
logs = logs.replace(self.creator_name, "{creator}")
logs = logs.replace(self.creator.address, "{creator_account}")
logs = logs.replace(dispenser.address, "{dispenser_account}")
Expand Down

1 comment on commit c73cd62

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/algokit_utils
   _transfer.py46296%45–46
   account.py831088%56–60, 91, 104, 128, 131, 175
   application_client.py6308986%64, 190, 206, 230, 245, 250, 270, 287, 298–300, 312, 389, 394, 396, 398, 581, 590, 599, 649, 657, 666, 710, 718, 727, 771, 779, 788, 815, 841, 849, 858, 900, 908, 917, 977, 992, 1010–1013, 1017, 1029–1030, 1034, 1055, 1095, 1103, 1139, 1175–1181, 1185–1190, 1192, 1248, 1255, 1278, 1348, 1407–1414, 1430, 1435–1445, 1451, 1457, 1464–1467, 1491–1496, 1516–1519, 1533
   application_specification.py892572%75, 78–87, 100, 108, 116, 158, 174, 196–205, 209
   deploy.py2522092%124, 158, 162–163, 194, 270, 274, 280–288, 299–302, 320, 399, 443–445
   logic_error.py35197%29
   network_clients.py47394%68, 71–72
TOTAL120915088% 

Tests Skipped Failures Errors Time
96 0 💤 0 ❌ 0 🔥 1m 6s ⏱️

Please sign in to comment.