Skip to content

Commit

Permalink
feat: accounts.resolve_address() method for resolving input into ad…
Browse files Browse the repository at this point in the history
…dress types (#2521)
  • Loading branch information
antazoey authored Feb 19, 2025
1 parent 37173cc commit 32623e3
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 13 deletions.
54 changes: 53 additions & 1 deletion src/ape/managers/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from collections.abc import Generator, Iterator
from contextlib import AbstractContextManager as ContextManager
from functools import cached_property, singledispatchmethod
from typing import Optional, Union
from typing import TYPE_CHECKING, Optional, Union

from eth_utils import is_hex

Expand All @@ -19,6 +19,9 @@
from ape.utils.basemodel import ManagerAccessMixin
from ape.utils.misc import log_instead_of_fail

if TYPE_CHECKING:
from ape.api.address import BaseAddress

_DEFAULT_SENDERS: list[AccountAPI] = []


Expand Down Expand Up @@ -465,3 +468,52 @@ def init_test_account(
self, index: int, address: AddressType, private_key: str
) -> "TestAccountAPI":
return self.test_accounts.init_test_account(index, address, private_key)

def resolve_address(
self, account_id: Union["BaseAddress", AddressType, str, int, bytes]
) -> Optional[AddressType]:
"""
Resolve the given input to an address.
Args:
account_id (:class:~ape.api.address.BaseAddress, str, int, bytes): The input to resolve.
It handles anything that converts to an AddressType like an ENS or a BaseAddress.
It also handles account aliases Ape is aware of, or int or bytes address values.
Returns:
:class:`~ape.types.AddressType` | None
"""
if isinstance(account_id, str) and account_id.startswith("0x"):
# Was given a hex-address string.
if provider := self.network_manager.active_provider:
return provider.network.ecosystem.decode_address(account_id)
else:
# Assume Ethereum-like.
return self.network_manager.ether.decode_address(account_id)

elif not isinstance(account_id, str):
# Was given either an integer, bytes, or a BaseAddress (account or contract).
return self.conversion_manager.convert(account_id, AddressType)

elif isinstance(account_id, str) and account_id in self.aliases:
# Was given an account alias.
account = self.load(account_id)
return account.address

elif (
isinstance(account_id, str)
and account_id.startswith("TEST::")
and account_id[-1].isdigit()
):
# Test account "alias".
account_idx = int(account_id[-1])
return self.test_accounts[account_idx]

elif isinstance(account_id, str) and not is_hex(account_id):
# Was maybe given an ENS name.
try:
return self.conversion_manager.convert(account_id, AddressType)
except ConversionError:
return None

return None
8 changes: 2 additions & 6 deletions src/ape/managers/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
APINotImplementedError,
BlockNotFoundError,
ChainError,
ConversionError,
ProviderNotConnectedError,
QueryEngineError,
TransactionNotFoundError,
Expand Down Expand Up @@ -955,11 +954,8 @@ def get_balance(
if (isinstance(address, str) and not address.startswith("0x")) or not isinstance(
address, str
):
try:
address = self.conversion_manager.convert(address, AddressType)
except ConversionError:
# Try to get the balance anyway; maybe the provider can handle it.
address = address
# Handles accounts, ENS, integers, aliases, everything.
address = self.account_manager.resolve_address(address)

return self.provider.get_balance(address, block_id=block_id)

Expand Down
12 changes: 6 additions & 6 deletions src/ape_console/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import click
from click.testing import CliRunner
from eth_utils import is_hex
from IPython import get_ipython
from IPython.core.magic import Magics, line_magic, magics_class
from rich import print as rich_print
Expand All @@ -14,7 +13,6 @@
from ape.exceptions import Abort, ApeException, handle_ape_exception
from ape.logging import logger
from ape.managers.project import LocalProject
from ape.types.address import AddressType
from ape.utils.basemodel import ManagerAccessMixin
from ape.utils.os import clean_path

Expand Down Expand Up @@ -67,11 +65,13 @@ def bal(self, line: str = ""):
provider = ape.networks.provider
ecosystem = provider.network.ecosystem
result = eval(line, self.ipython.user_global_ns, self.ipython.user_ns)
if isinstance(result, str) and not is_hex(result):
# Check if is an account alias.
address = ape.accounts.load(result).address

if isinstance(result, str) and result.startswith("0x"):
address = result

else:
address = ape.convert(result, AddressType)
# Handles accounts, ENS, integers, BaseAddress, and aliases.
address = ManagerAccessMixin.account_manager.resolve_address(result) or f"{result}"

decimals = ecosystem.fee_token_decimals
symbol = ecosystem.fee_token_symbol
Expand Down
30 changes: 30 additions & 0 deletions tests/functional/test_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -1004,3 +1004,33 @@ def test_call_sign_false(owner, vyper_contract_instance):
tx = vyper_contract_instance.setNumber.as_transaction(5991)
with pytest.raises(SignatureError):
owner.call(tx, sign=False)


def test_resolve_address(owner, keyfile_account, account_manager, vyper_contract_instance):
# Test test-account alias input.
actual = account_manager.resolve_address(owner.alias)
assert actual == owner.address

# Test keyfile-account alias input.
actual = account_manager.resolve_address(keyfile_account.alias)
assert actual == keyfile_account.address

# Test address input.
actual = account_manager.resolve_address(owner.address)
assert actual == owner.address

# Test account input.
actual = account_manager.resolve_address(owner)
assert actual == owner.address

# Test contract input.
actual = account_manager.resolve_address(vyper_contract_instance)
assert actual == vyper_contract_instance.address

# Test int input.
actual = account_manager.resolve_address(int(owner.address, 16))
assert actual == owner.address

# Test int input.
actual = account_manager.resolve_address(HexBytes(owner.address))
assert actual == owner.address

0 comments on commit 32623e3

Please sign in to comment.