Skip to content
Open
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
cbb0660
Problem: Ledger wallet users cannot use Aleph to send transactions.
Sep 10, 2025
4e8c9b6
Fix: Solve code quality issues.
Sep 10, 2025
f4c0f2a
Fix: Solve issue loading the good configuration.
Sep 10, 2025
c52f9dc
Fix: Solved definitively the wallet selection issue and also solved a…
Sep 10, 2025
b994c79
Fix: Solved code-quality issue.
Sep 10, 2025
cb798b5
Feature: cli load_account to handle account selections
1yam Oct 31, 2025
9c347b5
Fix: using load_account instead of _load_account
1yam Oct 31, 2025
f770d0f
fix: unit test mock
1yam Oct 31, 2025
677799d
Feature: aleph account init to create based config for new user using…
1yam Oct 31, 2025
725e042
fix: aleph account list now handle ledger device
1yam Oct 31, 2025
051b0e4
fix: aleph account address now handle ledger device
1yam Oct 31, 2025
55308d6
fix: aleph account export-private-key handle ledger case (can't expor…
1yam Oct 31, 2025
d433012
Feature: missing unit test for ledger
1yam Nov 3, 2025
f61e497
fix: Update aleph.im & twentysix to aleph.cloud (#414)
RezaRahemtola Nov 3, 2025
def2144
Fix: handle common error using ledger (OsError / LedgerError)
1yam Nov 4, 2025
46bbbdf
Fix: handle change from account on sdk side
1yam Nov 7, 2025
96ee9cd
Fix: remove init commands and ensure that config file/folder and subf…
1yam Nov 7, 2025
edd5f07
Fix: AccountLike renamed to AccountTypes
1yam Nov 7, 2025
ffc634e
fix: AlephAccount should bez AccountTypes
1yam Nov 7, 2025
5e66ce2
fix: account init commands unit test should be removed since not usef…
1yam Nov 7, 2025
bb5e3be
Fix: use arguments instead of get_closest_tier (#416)
1yam Nov 13, 2025
25b31ef
Fix: instance create crn is none when when giving --crn-hash or --crn…
1yam Nov 13, 2025
681b59d
Feature: utils functions for ledger
1yam Nov 19, 2025
47e5128
Fix: ensure ledger is connected before loading ledger account
1yam Nov 19, 2025
41b7da6
Fix: avoid connecting to ledger when not needed
1yam Nov 19, 2025
3bdc3b3
Fix: use BaseEthAccount instead of EthAccount in instance create and …
1yam Nov 19, 2025
9116552
fix: refactor aleph account configure and list to handle ledger
1yam Nov 19, 2025
f884163
Fix: call_program_crn_list can now filter node when fetching if they …
1yam Nov 19, 2025
8db0817
Fix: unit test
1yam Nov 19, 2025
c79a795
Feature: --no args for aleph account configure
1yam Nov 19, 2025
703cb47
fix: linting issue
1yam Nov 19, 2025
2a41932
Feature: load acount unit test
1yam Nov 19, 2025
5386882
Feature: ledger can be load from derivation path
1yam Nov 20, 2025
c96ed54
Unit: test_aggregate.py for ledger
1yam Nov 20, 2025
263021a
Unit: new tests for utils func around ledger (wait_for_ledger_connect…
1yam Nov 20, 2025
9ee1acb
Unit: new tests for non interactive account config
1yam Nov 20, 2025
b864806
Problem: Ledger wallet users cannot use Aleph to send transactions.
Sep 10, 2025
3db1512
Fix: Solve code quality issues.
Sep 10, 2025
dd9f469
Fix: Solve issue loading the good configuration.
Sep 10, 2025
5640671
Fix: Solved definitively the wallet selection issue and also solved a…
Sep 10, 2025
dea3db7
Fix: Solved code-quality issue.
Sep 10, 2025
679ea56
Feature: cli load_account to handle account selections
1yam Oct 31, 2025
14805ff
Fix: using load_account instead of _load_account
1yam Oct 31, 2025
a140f15
fix: unit test mock
1yam Oct 31, 2025
7d58847
Feature: aleph account init to create based config for new user using…
1yam Oct 31, 2025
a918403
fix: aleph account list now handle ledger device
1yam Oct 31, 2025
f57a4d3
fix: aleph account address now handle ledger device
1yam Oct 31, 2025
9900347
fix: aleph account export-private-key handle ledger case (can't expor…
1yam Oct 31, 2025
47d2d9f
Feature: missing unit test for ledger
1yam Nov 3, 2025
18f0940
Fix: handle common error using ledger (OsError / LedgerError)
1yam Nov 4, 2025
764a5f6
Fix: handle change from account on sdk side
1yam Nov 7, 2025
8b9ad59
Fix: remove init commands and ensure that config file/folder and subf…
1yam Nov 7, 2025
332f2cc
Fix: AccountLike renamed to AccountTypes
1yam Nov 7, 2025
51a2cac
fix: AlephAccount should bez AccountTypes
1yam Nov 7, 2025
2e9c044
fix: account init commands unit test should be removed since not usef…
1yam Nov 7, 2025
a14a73e
Feature: utils functions for ledger
1yam Nov 19, 2025
1b9a7ed
Fix: ensure ledger is connected before loading ledger account
1yam Nov 19, 2025
db0a9e8
Fix: avoid connecting to ledger when not needed
1yam Nov 19, 2025
bdfd6f2
Fix: use BaseEthAccount instead of EthAccount in instance create and …
1yam Nov 19, 2025
96edd86
fix: refactor aleph account configure and list to handle ledger
1yam Nov 19, 2025
7170a85
Fix: call_program_crn_list can now filter node when fetching if they …
1yam Nov 19, 2025
285723c
Fix: unit test
1yam Nov 19, 2025
94c626a
Feature: --no args for aleph account configure
1yam Nov 19, 2025
e96afe1
fix: linting issue
1yam Nov 19, 2025
bc68f0c
Feature: load acount unit test
1yam Nov 19, 2025
3203b21
Feature: ledger can be load from derivation path
1yam Nov 20, 2025
4527f17
Unit: test_aggregate.py for ledger
1yam Nov 20, 2025
e3e84be
Unit: new tests for utils func around ledger (wait_for_ledger_connect…
1yam Nov 20, 2025
d466231
Unit: new tests for non interactive account config
1yam Nov 20, 2025
896cec5
Merge remote-tracking branch 'origin/andres-feature-implement_ledger'…
1yam Nov 20, 2025
6802a2b
fix: allow user to specify how many ledger account they want to load …
1yam Nov 20, 2025
0ede706
fix: use the already existing args chain to load_account
1yam Nov 20, 2025
1588839
fix: lint issue
1yam Nov 20, 2025
d608925
Refactor: remove code duplication
1yam Nov 20, 2025
a5a83b6
Fix: aggregate unit test
1yam Nov 20, 2025
225474d
Fix: non-evm account couldn't be used using --private-key or --privat…
1yam Nov 21, 2025
fbb442a
fix: rename --no of configure commands to --non-it
1yam Nov 21, 2025
21caad6
fix: non interactive args for aleph account config is --non-it not --no
1yam Nov 21, 2025
5686f45
Fix: allow user to increase number of ledger account fetch using alep…
1yam Nov 21, 2025
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
13 changes: 8 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,20 @@ dependencies = [
"aiodns==3.2",
"aiohttp==3.11.13",
"aleph-message>=1.0.5",
"aleph-sdk-python>=2.1",
"base58==2.1.1", # Needed now as default with _load_account changement
#"aleph-sdk-python>=2.1",
"aleph-sdk-python @ git+https://github.com/aleph-im/aleph-sdk-python@andres-feature-implement_ledger_wallet",
"base58==2.1.1", # Needed now as default with _load_account changement
"click<8.2",
"py-sr25519-bindings==0.2", # Needed for DOT signatures
"ledgerblue>=0.1.48",
"ledgereth>=0.10",
"py-sr25519-bindings==0.2", # Needed for DOT signatures
"pydantic>=2",
"pygments==2.19.1",
"pynacl==1.5", # Needed now as default with _load_account changement
"pynacl==1.5", # Needed now as default with _load_account changement
"python-magic==0.4.27",
"rich==13.9.*",
"setuptools>=65.5",
"substrate-interface==1.7.11", # Needed for DOT signatures
"substrate-interface==1.7.11", # Needed for DOT signatures
"textual==0.73",
"typer==0.15.2",
]
Expand Down
348 changes: 294 additions & 54 deletions src/aleph_client/commands/account.py

Large diffs are not rendered by default.

58 changes: 41 additions & 17 deletions src/aleph_client/commands/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@

import typer
from aiohttp import ClientResponseError, ClientSession
from aleph.sdk.account import _load_account
from aleph.sdk.client import AuthenticatedAlephHttpClient
from aleph.sdk.conf import settings
from aleph.sdk.types import AccountFromPrivateKey
from aleph.sdk.client import AlephHttpClient, AuthenticatedAlephHttpClient
from aleph.sdk.conf import AccountType, load_main_configuration, settings
from aleph.sdk.utils import extended_json_encoder
from aleph_message.models import Chain, MessageType
from aleph_message.status import MessageStatus
Expand All @@ -21,7 +19,7 @@

from aleph_client.commands import help_strings
from aleph_client.commands.utils import setup_logging
from aleph_client.utils import AsyncTyper, sanitize_url
from aleph_client.utils import AccountTypes, AsyncTyper, load_account, sanitize_url

logger = logging.getLogger(__name__)
app = AsyncTyper(no_args_is_help=True)
Expand Down Expand Up @@ -59,7 +57,7 @@ async def forget(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)
address = account.get_address() if address is None else address

if key == "security" and not is_same_context():
Expand Down Expand Up @@ -132,7 +130,7 @@ async def post(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)
address = account.get_address() if address is None else address

if key == "security" and not is_same_context():
Expand Down Expand Up @@ -194,10 +192,19 @@ async def get(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
address = account.get_address() if address is None else address
config_file_path = Path(settings.CONFIG_FILE)
config = load_main_configuration(config_file_path)
account_type = config.type if config else None

async with AuthenticatedAlephHttpClient(account=account, api_server=settings.API_HOST) as client:
# Avoid connecting to ledger
if not account_type or account_type == AccountType.IMPORTED:
account = load_account(private_key, private_key_file)
if account and not address:
address = account.get_address()
elif not address and config and config.address:
address = config.address

async with AlephHttpClient(api_server=settings.API_HOST) as client:
aggregates = None
try:
aggregates = await client.fetch_aggregate(address=address, key=key)
Expand Down Expand Up @@ -229,9 +236,17 @@ async def list_aggregates(
"""Display all aggregates associated to an account"""

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
address = account.get_address() if address is None else address
config_file_path = Path(settings.CONFIG_FILE)
config = load_main_configuration(config_file_path)
account_type = config.type if config else None

# Avoid connecting to ledger
if not account_type or account_type == AccountType.IMPORTED:
account = load_account(private_key, private_key_file)
if account and not address:
address = account.get_address()
elif not address and config and config.address:
address = config.address

aggr_link = f"{sanitize_url(settings.API_HOST)}/api/v0/aggregates/{address}.json"
async with ClientSession() as session:
Expand Down Expand Up @@ -304,7 +319,7 @@ async def authorize(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)

data = await get(
key="security",
Expand Down Expand Up @@ -378,7 +393,7 @@ async def revoke(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)

data = await get(
key="security",
Expand Down Expand Up @@ -433,8 +448,17 @@ async def permissions(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
address = account.get_address() if address is None else address
config_file_path = Path(settings.CONFIG_FILE)
config = load_main_configuration(config_file_path)
account_type = config.type if config else None

# Avoid connecting to ledger
if not account_type or account_type == AccountType.IMPORTED:
account = load_account(private_key, private_key_file)
if account and not address:
address = account.get_address()
elif not address and config and config.address:
address = config.address

data = await get(
key="security",
Expand Down
34 changes: 23 additions & 11 deletions src/aleph_client/commands/credit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
import typer
from aiohttp import ClientResponseError
from aleph.sdk import AlephHttpClient
from aleph.sdk.account import _load_account
from aleph.sdk.conf import settings
from aleph.sdk.types import AccountFromPrivateKey
from aleph.sdk.conf import AccountType, load_main_configuration, settings
from aleph.sdk.utils import displayable_amount
from rich import box
from rich.console import Console
Expand All @@ -17,7 +15,7 @@

from aleph_client.commands import help_strings
from aleph_client.commands.utils import setup_logging
from aleph_client.utils import AsyncTyper
from aleph_client.utils import AsyncTyper, load_account

logger = logging.getLogger(__name__)
app = AsyncTyper(no_args_is_help=True)
Expand All @@ -41,10 +39,17 @@ async def show(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
config_file_path = Path(settings.CONFIG_FILE)
config = load_main_configuration(config_file_path)
account_type = config.type if config else None

if account and not address:
address = account.get_address()
# Avoid connecting to ledger
if not account_type or account_type == AccountType.IMPORTED:
account = load_account(private_key, private_key_file)
if account and not address:
address = account.get_address()
elif not address and config and config.address:
address = config.address

if address:
async with AlephHttpClient(api_server=settings.API_HOST) as client:
Expand Down Expand Up @@ -87,10 +92,17 @@ async def history(
):
setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)

if account and not address:
address = account.get_address()
config_file_path = Path(settings.CONFIG_FILE)
config = load_main_configuration(config_file_path)
account_type = config.type if config else None

# Avoid connecting to ledger
if not account_type or account_type == AccountType.IMPORTED:
account = load_account(private_key, private_key_file)
if account and not address:
address = account.get_address()
elif not address and config and config.address:
address = config.address

try:
# Comment the original API call for testing
Expand Down
16 changes: 7 additions & 9 deletions src/aleph_client/commands/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from typing import Annotated, Optional, cast

import typer
from aleph.sdk.account import _load_account
from aleph.sdk.client import AlephHttpClient, AuthenticatedAlephHttpClient
from aleph.sdk.conf import settings
from aleph.sdk.domain import (
Expand All @@ -18,7 +17,6 @@
)
from aleph.sdk.exceptions import DomainConfigurationError
from aleph.sdk.query.filters import MessageFilter
from aleph.sdk.types import AccountFromPrivateKey
from aleph_message.models import AggregateMessage
from aleph_message.models.base import MessageType
from rich.console import Console
Expand All @@ -27,7 +25,7 @@

from aleph_client.commands import help_strings
from aleph_client.commands.utils import is_environment_interactive
from aleph_client.utils import AsyncTyper
from aleph_client.utils import AccountTypes, AsyncTyper, load_account

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -65,7 +63,7 @@ async def check_domain_records(fqdn, target, owner):


async def attach_resource(
account: AccountFromPrivateKey,
account,
fqdn: Hostname,
item_hash: Optional[str] = None,
catch_all_path: Optional[str] = None,
Expand Down Expand Up @@ -137,7 +135,7 @@ async def attach_resource(
)


async def detach_resource(account: AccountFromPrivateKey, fqdn: Hostname, interactive: Optional[bool] = None):
async def detach_resource(account: AccountTypes, fqdn: Hostname, interactive: Optional[bool] = None):
domain_info = await get_aggregate_domain_info(account, fqdn)
interactive = is_environment_interactive() if interactive is None else interactive

Expand Down Expand Up @@ -187,7 +185,7 @@ async def add(
] = settings.PRIVATE_KEY_FILE,
):
"""Add and link a Custom Domain."""
account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)
interactive = False if (not ask) else is_environment_interactive()

console = Console()
Expand Down Expand Up @@ -272,7 +270,7 @@ async def attach(
] = settings.PRIVATE_KEY_FILE,
):
"""Attach resource to a Custom Domain."""
account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)

await attach_resource(
account,
Expand All @@ -294,7 +292,7 @@ async def detach(
] = settings.PRIVATE_KEY_FILE,
):
"""Unlink Custom Domain."""
account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)

await detach_resource(account, Hostname(fqdn), interactive=False if (not ask) else None)
raise typer.Exit()
Expand All @@ -309,7 +307,7 @@ async def info(
] = settings.PRIVATE_KEY_FILE,
):
"""Show Custom Domain Details."""
account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)

console = Console()
domain_validator = DomainValidator()
Expand Down
28 changes: 17 additions & 11 deletions src/aleph_client/commands/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
import typer
from aiohttp import ClientResponseError
from aleph.sdk import AlephHttpClient, AuthenticatedAlephHttpClient
from aleph.sdk.account import _load_account
from aleph.sdk.conf import settings
from aleph.sdk.types import AccountFromPrivateKey, StorageEnum, StoredContent
from aleph.sdk.conf import AccountType, load_main_configuration, settings
from aleph.sdk.types import StorageEnum, StoredContent
from aleph.sdk.utils import safe_getattr
from aleph_message.models import ItemHash, StoreMessage
from aleph_message.status import MessageStatus
Expand All @@ -23,7 +22,7 @@

from aleph_client.commands import help_strings
from aleph_client.commands.utils import setup_logging
from aleph_client.utils import AsyncTyper
from aleph_client.utils import AccountTypes, AsyncTyper, load_account

logger = logging.getLogger(__name__)
app = AsyncTyper(no_args_is_help=True)
Expand All @@ -44,7 +43,7 @@ async def pin(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)

async with AuthenticatedAlephHttpClient(account=account, api_server=settings.API_HOST) as client:
result: StoreMessage
Expand Down Expand Up @@ -75,7 +74,7 @@ async def upload(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)

async with AuthenticatedAlephHttpClient(account=account, api_server=settings.API_HOST) as client:
if not path.is_file():
Expand Down Expand Up @@ -181,7 +180,7 @@ async def forget(

setup_logging(debug)

account: AccountFromPrivateKey = _load_account(private_key, private_key_file)
account: AccountTypes = load_account(private_key, private_key_file)

hashes = [ItemHash(item_hash) for item_hash in item_hash.split(",")]

Expand Down Expand Up @@ -270,10 +269,17 @@ async def list_files(
json: Annotated[bool, typer.Option(help="Print as json instead of rich table")] = False,
):
"""List all files for a given address"""
account: AccountFromPrivateKey = _load_account(private_key, private_key_file)

if account and not address:
address = account.get_address()
config_file_path = Path(settings.CONFIG_FILE)
config = load_main_configuration(config_file_path)
account_type = config.type if config else None

# Avoid connecting to ledger
if not account_type or account_type == AccountType.IMPORTED:
account = load_account(private_key, private_key_file)
if account and not address:
address = account.get_address()
elif not address and config and config.address:
address = config.address

if address:
# Build the query parameters
Expand Down
Loading
Loading