Skip to content

Commit 7344544

Browse files
authored
chore: algokit utils v3 migration (#611)
* chore: algokit utils v3 migration * chore: temporarily set portability test algokit init against pr branch * chore: tmp disable audit * chore: restore tests against main branch
1 parent ba91e82 commit 7344544

File tree

15 files changed

+326
-242
lines changed

15 files changed

+326
-242
lines changed

.github/actions/setup-poetry/action.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ runs:
99
- if: ${{ runner.os == 'macOS' && runner.arch == 'ARM64' }}
1010
run: |
1111
pip install poetry
12+
pip install poetry-plugin-export
1213
shell: bash
1314

1415
- if: ${{ runner.os != 'macOS' || runner.arch != 'ARM64' }}
1516
run: |
1617
pip install --user pipx
1718
pipx ensurepath
1819
pipx install poetry ${{ runner.os == 'macOS' && '--python "$Python_ROOT_DIR/bin/python"' || '' }}
20+
pipx inject poetry poetry-plugin-export
1921
shell: bash
2022

2123
- name: Get full Python version

.github/workflows/check-python.yaml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ jobs:
2424
- name: Audit with pip-audit
2525
run: |
2626
# audit non dev dependencies, no exclusions
27-
poetry export --without=dev > requirements.txt && poetry run pip-audit -r requirements.txt
28-
27+
poetry export --without=dev > requirements.txt
28+
poetry run pip-audit -r requirements.txt --ignore-vuln 'GHSA-79v4-65xg-pq4g'
2929
# audit all dependencies, with exclusions.
3030
# If a vulnerability is found in a dev dependency without an available fix,
3131
# it can be temporarily ignored by adding --ignore-vuln e.g.
32-
poetry run pip-audit
32+
# TODO: decide on `GHSA-79v4-65xg-pq4g`, see https://osv.dev/vulnerability/GHSA-79v4-65xg-pq4g
33+
# The vulnerability is not applicable to the cli case, the only abstraciton leveraged is `RSAPublicKey` in
34+
# vendored src/algokit/core/_vendor/auth0/authentication/token_verifier.py that was added to remove dependency
35+
# on auth0 package that caused many adhoc transitive dependency errors in cli. As a result, consequent cryptography
36+
# vulnerabilities need to be a) verified for applicability to cli case and ignored if not applicable or b) fixed by
37+
# updating the vendored file to use the latest version of `cryptography` that has the fix.
3338
3439
- name: Check formatting with Ruff
3540
run: |

poetry.lock

Lines changed: 73 additions & 136 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ tomli = { version = "^2.0.1", python = "<3.11" }
1818
python-dotenv = "^1.0.0"
1919
mslex = "^1.1.0"
2020
keyring = "25.2.1"
21-
pyjwt = "^2.8.0"
22-
cryptography = "^43.0.1" # pyjwt has a weak dependency on cryptography
23-
algokit-utils = "^2.3.0"
21+
# pyjwt is locked to version ^2.8.0 because its explicitly
22+
# vendored from auth0 repo, to reduce depedency on auth0 package that caused many adhoc transitive dependency errors in cli
23+
# see header in src/algokit/core/_vendor/auth0/authentication/token_verifier.py
24+
pyjwt = "^2.8.0"
25+
cryptography = "^43.0.1" # pyjwt has a weak dependency on cryptography and explicitly requires it in the vendored file, hence the lock
26+
algokit-utils = "^3.0.0"
2427
multiformats = "0.3.1"
2528
multiformats_config = "0.3.1" # pinned this to be in lockstep with multiformats
2629
jsondiff = "^2.0.0"
@@ -44,6 +47,7 @@ sphinxnotes-markdown-builder = "^0.5.6"
4447
poethepoet = ">=0.17.1,<0.27.0"
4548
gfm-toc = "^0.0.7"
4649
pytest-xdist = "^3.4.0"
50+
pytest-sugar = "^1.0.0"
4751

4852
[build-system]
4953
requires = ["poetry-core"]

src/algokit/__main__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22

33
from algokit.cli import algokit
44

5-
# Required to support full feature parity when running in binary execution mode
65
freeze_support()
76
algokit()

src/algokit/cli/tasks/assets.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import logging
22

33
import click
4-
from algokit_utils import opt_in, opt_out
54
from algosdk import error
65
from algosdk.v2client.algod import AlgodClient
76

@@ -14,6 +13,7 @@
1413
validate_account_balance_to_opt_in,
1514
validate_address,
1615
)
16+
from algokit.core.utils import get_algorand_client_for_network
1717

1818
logger = logging.getLogger(__name__)
1919

@@ -55,19 +55,24 @@ def opt_in_command(asset_ids: tuple[int], account: str, network: AlgorandNetwork
5555
opt_in_account = get_account_with_private_key(account)
5656
validate_address(opt_in_account.address)
5757
algod_client = load_algod_client(network)
58+
algorand = get_algorand_client_for_network(network)
5859

5960
validate_account_balance_to_opt_in(algod_client, opt_in_account, len(asset_ids_list))
6061
try:
6162
click.echo("Performing opt-in. This may take a few seconds...")
62-
response = opt_in(algod_client=algod_client, account=opt_in_account, asset_ids=asset_ids_list)
63+
response = algorand.asset.bulk_opt_in(
64+
account=opt_in_account.address,
65+
asset_ids=asset_ids_list,
66+
signer=opt_in_account.signer,
67+
)
6368
click.echo("Successfully performed opt-in.")
6469
if len(response) > 1:
6570
account_url = get_explorer_url(opt_in_account.address, network, ExplorerEntityType.ADDRESS)
6671
click.echo(f"Check latest transactions on your account at: {account_url}")
6772
else:
68-
for asset_id, txn_id in response.items():
69-
explorer_url = get_explorer_url(txn_id, network, ExplorerEntityType.ASSET)
70-
click.echo(f"Check opt-in status for asset {asset_id} at: {explorer_url}")
73+
for asset_opt_int_result in response:
74+
explorer_url = get_explorer_url(asset_opt_int_result.transaction_id, network, ExplorerEntityType.ASSET)
75+
click.echo(f"Check opt-in status for asset {asset_opt_int_result.asset_id} at: {explorer_url}")
7176
except error.AlgodHTTPError as err:
7277
raise click.ClickException(str(err)) from err
7378
except ValueError as err:
@@ -106,6 +111,7 @@ def opt_out_command(*, asset_ids: tuple[int], account: str, network: AlgorandNet
106111
opt_out_account = get_account_with_private_key(account)
107112
validate_address(opt_out_account.address)
108113
algod_client = load_algod_client(network)
114+
algorand = get_algorand_client_for_network(network)
109115
asset_ids_list = []
110116
try:
111117
asset_ids_list = _get_zero_balanced_assets(
@@ -119,15 +125,21 @@ def opt_out_command(*, asset_ids: tuple[int], account: str, network: AlgorandNet
119125
raise click.ClickException("No assets to opt-out of.")
120126

121127
click.echo("Performing opt-out. This may take a few seconds...")
122-
response = opt_out(algod_client=algod_client, account=opt_out_account, asset_ids=asset_ids_list)
128+
response = algorand.asset.bulk_opt_out(
129+
account=opt_out_account.address,
130+
asset_ids=asset_ids_list,
131+
signer=opt_out_account.signer,
132+
)
123133
click.echo("Successfully performed opt-out.")
124134
if len(response) > 1:
125135
account_url = get_explorer_url(opt_out_account.address, network, ExplorerEntityType.ADDRESS)
126136
click.echo(f"Check latest transactions on your account at: {account_url}")
127137
else:
128-
asset_id, txn_id = response.popitem()
129-
transaction_url = get_explorer_url(txn_id, network, ExplorerEntityType.TRANSACTION)
130-
click.echo(f"Check opt-in status for asset {asset_id} at: {transaction_url}")
138+
asset_opt_out_result = response[0]
139+
transaction_url = get_explorer_url(
140+
asset_opt_out_result.transaction_id, network, ExplorerEntityType.TRANSACTION
141+
)
142+
click.echo(f"Check opt-in status for asset {asset_opt_out_result.asset_id} at: {transaction_url}")
131143
except error.AlgodHTTPError as err:
132144
raise click.ClickException(str(err)) from err
133145
except ConnectionRefusedError as err:

src/algokit/cli/tasks/mint.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import json
22
import logging
33
import math
4+
from decimal import Decimal
45
from pathlib import Path
56

67
import click
7-
from algokit_utils import Account
8+
from algokit_utils import AlgoAmount, SigningAccount
89
from algosdk.error import AlgodHTTPError
9-
from algosdk.util import algos_to_microalgos
1010

1111
from algokit.cli.common.constants import AlgorandNetwork, ExplorerEntityType
1212
from algokit.cli.common.utils import get_explorer_url
@@ -31,7 +31,7 @@
3131

3232
MAX_UNIT_NAME_BYTE_LENGTH = 8
3333
MAX_ASSET_NAME_BYTE_LENGTH = 32
34-
ASSET_MINTING_MBR = 0.2 # Algos, 0.1 for base account, 0.1 for asset creation
34+
ASSET_MINTING_MBR = Decimal(0.2) # Algos, 0.1 for base account, 0.1 for asset creation
3535

3636

3737
def _validate_supply(total: int, decimals: int) -> None:
@@ -115,7 +115,7 @@ def _get_and_validate_asset_name(context: click.Context, param: click.Parameter,
115115
)
116116

117117

118-
def _get_creator_account(_: click.Context, __: click.Parameter, value: str) -> Account:
118+
def _get_creator_account(_: click.Context, __: click.Parameter, value: str) -> SigningAccount:
119119
"""
120120
Validate the creator account by checking if it is a valid Algorand address.
121121
@@ -124,7 +124,7 @@ def _get_creator_account(_: click.Context, __: click.Parameter, value: str) -> A
124124
value (str): The value of the parameter.
125125
126126
Returns:
127-
Account: An account object with the address and private key.
127+
SigningAccount: An account object with the address and private key.
128128
"""
129129
try:
130130
return get_account_with_private_key(value)
@@ -284,7 +284,7 @@ def _validate_supply_for_nft(context: click.Context, _: click.Parameter, value:
284284
)
285285
def mint( # noqa: PLR0913
286286
*,
287-
creator: Account,
287+
creator: SigningAccount,
288288
asset_name: str,
289289
unit_name: str,
290290
total: int,
@@ -304,7 +304,7 @@ def mint( # noqa: PLR0913
304304
client,
305305
creator,
306306
0,
307-
algos_to_microalgos(ASSET_MINTING_MBR), # type: ignore[no-untyped-call]
307+
AlgoAmount.from_algo(ASSET_MINTING_MBR).micro_algo,
308308
)
309309

310310
token_metadata = TokenMetadata.from_json_file(token_metadata_path, asset_name, decimals)

src/algokit/cli/tasks/transfer.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
11
import logging
2-
from typing import TYPE_CHECKING
32

43
import click
5-
from algokit_utils import (
6-
TransferAssetParameters,
7-
TransferParameters,
8-
transfer_asset,
9-
)
10-
from algokit_utils import (
11-
transfer as transfer_algos,
12-
)
4+
from algokit_utils import AlgoAmount, AssetTransferParams, PaymentParams, SendAtomicTransactionComposerResults
135

146
from algokit.cli.common.constants import AlgorandNetwork, ExplorerEntityType
157
from algokit.cli.common.utils import get_explorer_url
@@ -21,9 +13,7 @@
2113
validate_address,
2214
validate_balance,
2315
)
24-
25-
if TYPE_CHECKING:
26-
from algosdk.transaction import AssetTransferTxn, PaymentTxn
16+
from algokit.core.utils import get_algorand_client_for_network
2717

2818
logger = logging.getLogger(__name__)
2919

@@ -95,29 +85,39 @@ def transfer( # noqa: PLR0913
9585
validate_balance(algod_client, receiver_address, asset_id)
9686

9787
# Transfer algos or assets depending on asset_id
98-
txn_response: PaymentTxn | AssetTransferTxn | None = None
88+
txn_response: SendAtomicTransactionComposerResults | None = None
89+
algorand = get_algorand_client_for_network(network)
9990
try:
10091
if asset_id == 0:
101-
txn_response = transfer_algos(
102-
algod_client,
103-
TransferParameters(to_address=receiver_address, from_account=sender_account, micro_algos=amount),
92+
txn_response = (
93+
algorand.new_group()
94+
.add_payment(
95+
PaymentParams(
96+
sender=sender_account.address,
97+
receiver=receiver_address,
98+
amount=AlgoAmount(micro_algo=amount),
99+
signer=sender_account.signer,
100+
)
101+
)
102+
.send()
104103
)
105104
else:
106-
txn_response = transfer_asset(
107-
algod_client,
108-
TransferAssetParameters(
109-
from_account=sender_account,
110-
to_address=receiver_address,
111-
amount=amount,
112-
asset_id=asset_id,
113-
),
105+
txn_response = (
106+
algorand.new_group()
107+
.add_asset_transfer(
108+
AssetTransferParams(
109+
sender=sender_account.address,
110+
receiver=receiver_address,
111+
amount=amount,
112+
asset_id=asset_id,
113+
signer=sender_account.signer,
114+
),
115+
)
116+
.send()
114117
)
115118

116-
if not txn_response:
117-
raise click.ClickException("Failed to perform transfer")
118-
119119
txn_url = get_explorer_url(
120-
identifier=txn_response.get_txid(), # type: ignore[no-untyped-call]
120+
identifier=txn_response.tx_ids[0],
121121
network=network,
122122
entity_type=ExplorerEntityType.TRANSACTION,
123123
)

0 commit comments

Comments
 (0)