Skip to content

Commit

Permalink
refactor: improving tests, adding docs and etc
Browse files Browse the repository at this point in the history
  • Loading branch information
aorumbayev committed Oct 19, 2023
1 parent b52f9db commit b5dc1a2
Show file tree
Hide file tree
Showing 19 changed files with 244 additions and 119 deletions.
34 changes: 17 additions & 17 deletions docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,16 @@
- [VALUE](#value)
- [opt-in](#opt-in)
- [Options](#options-20)
- [-a, --account ](#-a---account-)
- [-n, --network ](#-n---network--1)
- [Arguments](#arguments-6)
- [ACCOUNT](#account)
- [ASSET_IDS](#asset_ids)
- [opt-out](#opt-out)
- [Options](#options-21)
- [-a, --account ](#-a---account--1)
- [--all](#--all)
- [-n, --network ](#-n---network--2)
- [Arguments](#arguments-7)
- [ACCOUNT](#account-1)
- [ASSET_IDS](#asset_ids-1)
- [send](#send)
- [Options](#options-22)
Expand All @@ -141,7 +141,7 @@
- [-n, --network ](#-n---network--3)
- [sign](#sign)
- [Options](#options-23)
- [-a, --account ](#-a---account-)
- [-a, --account ](#-a---account--2)
- [-f, --file ](#-f---file--3)
- [-t, --transaction ](#-t---transaction--1)
- [-o, --output ](#-o---output--3)
Expand Down Expand Up @@ -856,15 +856,19 @@ Required argument

### opt-in

Opt-in to an asset using <ID> <ACCOUNT>. This is required before you can receive an asset. Use -n to specify localnet, testnet, or mainnet.
Opt-in to an asset(s). This is required before you can receive an asset. Use -n to specify localnet, testnet, or mainnet. To supply multiple asset IDs, separate them with a whitespace.

```shell
algokit task opt-in [OPTIONS] ACCOUNT ASSET_IDS
algokit task opt-in [OPTIONS] ASSET_IDS...
```

### Options


### -a, --account <account>
**Required** Address or alias of the signer account.


### -n, --network <network>
Network to use. Refers to localnet by default.

Expand All @@ -877,24 +881,24 @@ Network to use. Refers to localnet by default.
### Arguments


### ACCOUNT
Required argument


### ASSET_IDS
Required argument
Required argument(s)

### opt-out

opt-out of an asset using <ID> <ACCOUNT>. You can only opt out of an asset with a zero balance. Use -n to specify localnet, testnet, or mainnet.
opt-out of an asset(s). You can only opt out of an asset with a zero balance. Use -n to specify localnet, testnet, or mainnet. To supply multiple asset IDs, separate them with a whitespace.

```shell
algokit task opt-out [OPTIONS] ACCOUNT [ASSET_IDS]
algokit task opt-out [OPTIONS] [ASSET_IDS]...
```

### Options


### -a, --account <account>
**Required** Address or alias of the signer account.


### --all
Opt-out of all assets with zero balance.

Expand All @@ -911,12 +915,8 @@ Network to use. Refers to localnet by default.
### Arguments


### ACCOUNT
Required argument


### ASSET_IDS
Optional argument
Optional argument(s)

### send

Expand Down
2 changes: 1 addition & 1 deletion docs/features/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ AlgoKit Tasks are a collection of handy tasks that can be used to perform variou
- [Wallet Aliasing](./tasks/wallet.md) - Manage your Algorand addresses and accounts effortlessly with the AlgoKit Wallet feature. This feature allows you to create short aliases for your addresses and accounts on AlgoKit CLI.
- [Vanity Address Generation](./tasks/vanity.md) - Generate vanity addresses for your Algorand accounts with the AlgoKit Vanity feature. This feature allows you to generate Algorand addresses with a custom prefix of your choice.
- [Transfer Assets or Algos](./tasks/transfer.md) - Transfer Algos or Assets from one account to another with the AlgoKit Transfer feature. This feature allows you to transfer Algos or Assets from one account to another on Algorand blockchain.
- Opt-in or opt-out of Algorand Assets - Coming soon!
- [Opt-(in|out) Assets](./task/opt.md) - Opt-in or opt-out of Algorand Asset(s). Supports single or multiple assets.
- [Signing transactions](./tasks/sign.md) - Sign goal clerk compatible Algorand transactions.
- [Sending transactions](./tasks/send.md) - Send signed goal clerk compatible Algorand transactions.
- [NFD lookups](./tasks/nfd.md) - Perform a lookup via NFD domain or address, returning the associated address or domain respectively using the AlgoKit CLI.
Expand Down
67 changes: 67 additions & 0 deletions docs/features/tasks/opt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# AlgoKit Task Asset opt-(in|out)

AlgoKit Task Asset opt-(in|out) allows you to opt-in or opt-out of Algorand Asset(s). This task supports single or multiple assets.

## Usage

Available commands and possible usage as follows:

### Opt-in

```bash
Usage: algokit task opt-in [OPTIONS] ASSET_IDS...

Opt-in to an asset(s). This is required before you can receive an asset.
Use -n to specify localnet, testnet, or mainnet. To supply multiple asset IDs, separate them with a whitespace.

Options:
--account, -a TEXT Address or alias of the signer account. [required]
-n, --network [localnet|testnet|mainnet]
Network to use. Refers to `localnet` by default.
```

### Opt-out

```bash
Usage: algokit task opt-out [OPTIONS] [ASSET_IDS]...

Opt-out of an asset(s). You can only opt out of an asset with a zero balance.
Use -n to specify localnet, testnet, or mainnet. To supply multiple asset IDs, separate them with a whitespace.

Options:
--account, -a TEXT Address or alias of the signer account. [required]
--all Opt-out of all assets with zero balance.
-n, --network [localnet|testnet|mainnet]
Network to use. Refers to `localnet` by default.
```

## Options

- `ASSET_IDS`: Specifies the asset IDs to opt-in or opt-out. To supply multiple asset IDs, separate them with a whitespace.
- `--account`, `-a` TEXT: Specifies the address or alias of the signer account. This option is required.
- `--all`: Specifies to opt-out of all assets with zero balance.
- `-n`, `--network` [localnet|testnet|mainnet]: Specifies the network to use. Refers to localnet by default.

## Example

Example

To opt-in to an asset(s), you can use the opt-in command as follows:

```bash
$ algokit task opt-in --account {YOUR_ACCOUNT} {ASSET_ID_1} {ASSET_ID_2} {ASSET_ID_3} ...
```

To opt-out of an asset(s), you can use the opt-out command as follows:

```bash
$ algokit task opt-out --account {YOUR_ACCOUNT} {ASSET_ID_1} {ASSET_ID_2} ...
```

To opt-out of all assets with zero balance, you can use the opt-out command with the `--all` flag:

```bash
$ algokit task opt-out --account {YOUR_ACCOUNT} --all
```

> Please note, the account must have sufficient balance to cover the transaction fees.
89 changes: 64 additions & 25 deletions src/algokit/cli/tasks/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import click
from algokit_utils import opt_in, opt_out
from algosdk import error
from algosdk.v2client.algod import AlgodClient

from algokit.cli.tasks.utils import (
ExplorerEntityType,
get_account_info,
get_account_with_private_key,
get_explorer_url,
load_algod_client,
validate_account_balance_to_opt_in,
validate_address,
Expand All @@ -15,13 +18,29 @@
logger = logging.getLogger(__name__)


def _get_zero_balanced_assets(
*, provided_asset_ids: tuple[int], address: str, algod_client: AlgodClient, all_assets: bool = False
) -> list[int]:
asset_ids_list = []
if all_assets:
account_info = get_account_info(algod_client, address)
for asset in account_info.get("assets", []):
if asset.get("amount", 0) == 0:
asset_ids_list.append(int(asset["asset-id"]))
else:
for asset_id in provided_asset_ids:
asset_ids_list.append(asset_id)

return asset_ids_list


@click.command(
name="opt-in",
help="Opt-in to an asset using <ID> <ACCOUNT>. This is required before you can receive an asset. "
"Use -n to specify localnet, testnet, or mainnet.",
help="Opt-in to an asset(s). This is required before you can receive an asset. "
"Use -n to specify localnet, testnet, or mainnet. To supply multiple asset IDs, separate them with a whitespace.",
)
@click.argument("account", type=click.STRING, required=True)
@click.argument("asset_ids", type=click.STRING, required=True)
@click.argument("asset_ids", type=click.INT, required=True, nargs=-1)
@click.option("--account", "-a", type=click.STRING, required=True, help="Address or alias of the signer account.")
@click.option(
"-n",
"--network",
Expand All @@ -30,33 +49,42 @@
required=False,
help="Network to use. Refers to `localnet` by default.",
)
def opt_in_command(asset_ids: str, account: str, network: str) -> None:
asset_ids_list = []
for asset_id in asset_ids.split(","):
asset_ids_list.append(int(asset_id.strip()))
def opt_in_command(asset_ids: tuple[int], account: str, network: str) -> None:
asset_ids_list = list(asset_ids)

opt_in_account = get_account_with_private_key(account)
validate_address(opt_in_account.address)
algod_client = load_algod_client(network)

validate_account_balance_to_opt_in(algod_client, opt_in_account, len(asset_ids_list))
try:
opt_in(algod_client=algod_client, account=opt_in_account, asset_ids=asset_ids_list)
click.echo("Successfully performed opt-in. ")
click.echo("Performing opt-in. This may take a few seconds...")
response = opt_in(algod_client=algod_client, account=opt_in_account, asset_ids=asset_ids_list)
click.echo("Successfully performed opt-in.")
if len(response) > 1:
account_url = get_explorer_url(opt_in_account.address, network, ExplorerEntityType.ADDRESS)
click.echo(f"Check latest transactions on your account at: {account_url}")
else:
for asset_id, txn_id in response.items():
explorer_url = get_explorer_url(txn_id, network, ExplorerEntityType.ASSET)
click.echo(f"Check opt-in status for asset {asset_id} at: {explorer_url}")
except error.AlgodHTTPError as err:
raise click.ClickException(str(err)) from err
except ValueError as err:
logger.debug(err, exc_info=True)
raise click.ClickException(str(err)) from err
except Exception as err:
logger.debug(err, exc_info=True)
raise click.ClickException("Failed to perform opt-in") from err


@click.command(
name="opt-out",
help="opt-out of an asset using <ID> <ACCOUNT>. You can only opt out of an asset with a zero balance. "
"Use -n to specify localnet, testnet, or mainnet.",
help="opt-out of an asset(s). You can only opt out of an asset with a zero balance. "
"Use -n to specify localnet, testnet, or mainnet. To supply multiple asset IDs, separate them with a whitespace.",
)
@click.argument("account", type=click.STRING, required=True)
@click.argument("asset_ids", type=click.STRING, required=False)
@click.argument("asset_ids", type=click.INT, required=False, nargs=-1)
@click.option("--account", "-a", type=click.STRING, required=True, help="Address or alias of the signer account.")
@click.option(
"--all",
"all_assets",
Expand All @@ -72,30 +100,41 @@ def opt_in_command(asset_ids: str, account: str, network: str) -> None:
required=False,
help="Network to use. Refers to `localnet` by default.",
)
def opt_out_command(asset_ids: str, account: str, network: str, all_assets: bool) -> None: # noqa: FBT001
def opt_out_command(asset_ids: tuple[int], account: str, network: str, all_assets: bool) -> None: # noqa: FBT001
if not (all_assets or asset_ids):
raise click.UsageError("asset_ids or --all must be specified")
opt_out_account = get_account_with_private_key(account)
validate_address(opt_out_account.address)
algod_client = load_algod_client(network)
asset_ids_list = []
try:
if all_assets:
account_info = get_account_info(algod_client, opt_out_account.address)
for asset in account_info.get("assets", []):
if asset["amount"] == 0:
asset_ids_list.append(int(asset["asset-id"]))
else:
for asset_id in asset_ids.split(","):
asset_ids_list.append(int(asset_id.strip()))
asset_ids_list = _get_zero_balanced_assets(
provided_asset_ids=asset_ids,
address=opt_out_account.address,
algod_client=algod_client,
all_assets=all_assets,
)

opt_out(algod_client=algod_client, account=opt_out_account, asset_ids=asset_ids_list)
click.echo("Successfully performed opt-out.")
if not asset_ids_list:
raise click.ClickException("No assets to opt-out of.")

click.echo("Performing opt-out. This may take a few seconds...")
response = opt_out(algod_client=algod_client, account=opt_out_account, asset_ids=asset_ids_list)
click.echo("Successfully performed opt-out.")
if len(response) > 1:
account_url = get_explorer_url(opt_out_account.address, network, ExplorerEntityType.ADDRESS)
click.echo(f"Check latest transactions on your account at: {account_url}")
else:
asset_id, txn_id = response.popitem()
transaction_url = get_explorer_url(txn_id, network, ExplorerEntityType.TRANSACTION)
click.echo(f"Check opt-in status for asset {asset_id} at: {transaction_url}")
except error.AlgodHTTPError as err:
raise click.ClickException(str(err)) from err
except ConnectionRefusedError as err:
raise click.ClickException(str(err)) from err
except ValueError as err:
logger.debug(err, exc_info=True)
raise click.ClickException(str(err)) from err
except Exception as err:
logger.debug(err, exc_info=True)
raise click.ClickException("Failed to perform opt-out.") from err
8 changes: 4 additions & 4 deletions src/algokit/cli/tasks/mint.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
from algosdk.util import algos_to_microalgos

from algokit.cli.tasks.utils import (
ExplorerEntityType,
get_account_with_private_key,
get_asset_explorer_url,
get_transaction_explorer_url,
get_explorer_url,
load_algod_client,
validate_balance,
)
Expand Down Expand Up @@ -232,8 +232,8 @@ def mint( # noqa: PLR0913
)

click.echo("\nSuccessfully minted the asset!")
click.echo(f"Browse your asset at: {get_asset_explorer_url(asset_id, network)}")
click.echo(f"Check transaction status at: {get_transaction_explorer_url(txn_id, network)}")
click.echo(f"Browse your asset at: {get_explorer_url(asset_id, network, ExplorerEntityType.ASSET)}")
click.echo(f"Check transaction status at: {get_explorer_url(txn_id, network, ExplorerEntityType.TRANSACTION)}")
except (
Web3StorageBadRequestError,
Web3StorageUnauthorizedError,
Expand Down
15 changes: 12 additions & 3 deletions src/algokit/cli/tasks/send_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
from algosdk.transaction import SignedTransaction, retrieve_from_file

from algokit.cli.common.utils import MutuallyExclusiveOption
from algokit.cli.tasks.utils import get_transaction_explorer_url, load_algod_client, stdin_has_content
from algokit.cli.tasks.utils import (
ExplorerEntityType,
get_explorer_url,
load_algod_client,
stdin_has_content,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -109,13 +114,17 @@ def _send_transactions(network: str, txns: list[SignedTransaction]) -> None:
if any(txn.transaction.group for txn in txns):
txid = algod_client.send_transactions(txns)
click.echo(f"Transaction group successfully sent with txid: {txid}")
click.echo(f"Check transaction group status at: {get_transaction_explorer_url(txid, network)}")
click.echo(
f"Check transaction group status at: {get_explorer_url(txid, network, ExplorerEntityType.TRANSACTION)}"
)
else:
for index, txn in enumerate(txns, start=1):
click.echo(f"\nSending transaction {index}/{len(txns)}")
txid = algod_client.send_transaction(txn)
click.echo(f"Transaction successfully sent with txid: {txid}")
click.echo(f"Check transaction status at: {get_transaction_explorer_url(txid, network)}")
click.echo(
f"Check transaction status at: {get_explorer_url(txid, network, ExplorerEntityType.TRANSACTION)}"
)


@click.command(name="send", help="Send a signed transaction to the given network.")
Expand Down
Loading

0 comments on commit b5dc1a2

Please sign in to comment.