From cde1e2f8de3b2e9f3e236656f8019de490171e4a Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Thu, 31 Oct 2024 23:43:24 +0100 Subject: [PATCH 01/10] Feature: Implemented new EVM chains. --- pyproject.toml | 6 ++-- src/aleph/sdk/account.py | 18 ++++++++++- src/aleph/sdk/chains/evm.py | 47 +++++++++++++++++++++++++++++ src/aleph/sdk/conf.py | 60 ++++++++++++++++++++++++++++++++++--- src/aleph/sdk/evm_utils.py | 8 ++++- src/aleph/sdk/types.py | 2 +- 6 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 src/aleph/sdk/chains/evm.py diff --git a/pyproject.toml b/pyproject.toml index 409694cf..8a5d1a76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,15 +30,15 @@ dynamic = [ "version" ] dependencies = [ "aiohttp>=3.8.3", "aioresponses>=0.7.6", - "aleph-message>=0.4.9", + "aleph-message @ git+https://github.com/aleph-im/aleph-message.git@andres-feature-add_new_evm_chains", "aleph-superfluid>=0.2.1", - "base58==2.1.1", # Needed now as default with _load_account changement + "base58==2.1.1", # Needed now as default with _load_account changement "coincurve; python_version<'3.11'", "coincurve>=19; python_version>='3.11'", "eth-abi>=4; python_version>='3.11'", "eth-typing==4.3.1", "jwcrypto==1.5.6", - "pynacl==1.5", # Needed now as default with _load_account changement + "pynacl==1.5", # Needed now as default with _load_account changement "python-magic", "typing-extensions", "web3==6.3", diff --git a/src/aleph/sdk/account.py b/src/aleph/sdk/account.py index 9bfafcd3..edfe83cf 100644 --- a/src/aleph/sdk/account.py +++ b/src/aleph/sdk/account.py @@ -7,8 +7,10 @@ from aleph.sdk.chains.common import get_fallback_private_key from aleph.sdk.chains.ethereum import ETHAccount +from aleph.sdk.chains.evm import EVMAccount from aleph.sdk.chains.remote import RemoteAccount from aleph.sdk.chains.solana import SOLAccount +from aleph.sdk.chains.substrate import DOTAccount from aleph.sdk.conf import load_main_configuration, settings from aleph.sdk.evm_utils import get_chains_with_super_token from aleph.sdk.types import AccountFromPrivateKey @@ -18,10 +20,24 @@ T = TypeVar("T", bound=AccountFromPrivateKey) chain_account_map: Dict[Chain, Type[T]] = { # type: ignore - Chain.ETH: ETHAccount, + Chain.ARBITRUM: EVMAccount, Chain.AVAX: ETHAccount, Chain.BASE: ETHAccount, + Chain.BLAST: EVMAccount, + Chain.BOB: EVMAccount, + Chain.CYBER: EVMAccount, + Chain.DOT: DOTAccount, + Chain.ETH: ETHAccount, + Chain.FRAXTAL: EVMAccount, + Chain.LINEA: EVMAccount, + Chain.LISK: EVMAccount, + Chain.METIS: EVMAccount, + Chain.MODE: EVMAccount, + Chain.OPTIMISM: EVMAccount, + Chain.POL: EVMAccount, Chain.SOL: SOLAccount, + Chain.WORLDCHAIN: EVMAccount, + Chain.ZORA: EVMAccount, } diff --git a/src/aleph/sdk/chains/evm.py b/src/aleph/sdk/chains/evm.py new file mode 100644 index 00000000..b3e94f37 --- /dev/null +++ b/src/aleph/sdk/chains/evm.py @@ -0,0 +1,47 @@ +from decimal import Decimal +from pathlib import Path +from typing import Awaitable, Optional + +from aleph_message.models import Chain +from eth_account import Account # type: ignore + +from .common import get_fallback_private_key +from .ethereum import ETHAccount + + +class EVMAccount(ETHAccount): + def __init__(self, private_key: bytes, chain: Optional[Chain] = None): + super().__init__(private_key, chain) + if chain: + self.CHAIN = chain.value + + @staticmethod + def from_mnemonic(mnemonic: str, chain: Optional[Chain] = None) -> "EVMAccount": + Account.enable_unaudited_hdwallet_features() + return EVMAccount( + private_key=Account.from_mnemonic(mnemonic=mnemonic).key, chain=chain + ) + + def get_token_balance(self) -> Decimal: + raise ValueError(f"Token not implemented for this chain {self.CHAIN}") + + def get_super_token_balance(self) -> Decimal: + raise ValueError(f"Super token not implemented for this chain {self.CHAIN}") + + def create_flow(self, receiver: str, flow: Decimal) -> Awaitable[str]: + raise ValueError(f"Flow creation not implemented for this chain {self.CHAIN}") + + def get_flow(self, receiver: str): + raise ValueError(f"Get flow not implemented for this chain {self.CHAIN}") + + def update_flow(self, receiver: str, flow: Decimal) -> Awaitable[str]: + raise ValueError(f"Flow update not implemented for this chain {self.CHAIN}") + + def delete_flow(self, receiver: str) -> Awaitable[str]: + raise ValueError(f"Flow deletion not implemented for this chain {self.CHAIN}") + + +def get_fallback_account( + path: Optional[Path] = None, chain: Optional[Chain] = None +) -> ETHAccount: + return ETHAccount(private_key=get_fallback_private_key(path=path), chain=chain) diff --git a/src/aleph/sdk/conf.py b/src/aleph/sdk/conf.py index 5fe4cd4b..883430f0 100644 --- a/src/aleph/sdk/conf.py +++ b/src/aleph/sdk/conf.py @@ -99,10 +99,9 @@ class Settings(BaseSettings): active=False, ), # MAINNETS - Chain.ETH: ChainInfo( - chain_id=1, - rpc="https://eth-mainnet.public.blastapi.io", - token="0x27702a26126e0B3702af63Ee09aC4d1A084EF628", + Chain.ARBITRUM: ChainInfo( + chain_id=42161, + rpc="https://arbitrum-one.publicnode.com", ), Chain.AVAX: ChainInfo( chain_id=43114, @@ -116,12 +115,65 @@ class Settings(BaseSettings): token="0xc0Fbc4967259786C743361a5885ef49380473dCF", super_token="0xc0Fbc4967259786C743361a5885ef49380473dCF", ), + Chain.BLAST: ChainInfo( + chain_id=81457, + rpc="https://blastl2-mainnet.public.blastapi.io", + ), + Chain.BOB: ChainInfo( + chain_id=60808, + rpc="https://bob-mainnet.public.blastapi.io", + ), Chain.BSC: ChainInfo( chain_id=56, rpc="https://binance.llamarpc.com", token="0x82D2f8E02Afb160Dd5A480a617692e62de9038C4", active=False, ), + Chain.CYBER: ChainInfo( + chain_id=7560, + rpc="https://rpc.cyber.co", + ), + Chain.ETH: ChainInfo( + chain_id=1, + rpc="https://eth-mainnet.public.blastapi.io", + token="0x27702a26126e0B3702af63Ee09aC4d1A084EF628", + ), + Chain.FRAXTAL: ChainInfo( + chain_id=252, + rpc="https://rpc.frax.com", + ), + Chain.LINEA: ChainInfo( + chain_id=59144, + rpc="https://linea-rpc.publicnode.com", + ), + Chain.LISK: ChainInfo( + chain_id=1135, + rpc="https://rpc.api.lisk.com", + ), + Chain.METIS: ChainInfo( + chain_id=1088, + rpc="https://metis.drpc.org", + ), + Chain.MODE: ChainInfo( + chain_id=34443, + rpc="https://mode.drpc.org", + ), + Chain.OPTIMISM: ChainInfo( + chain_id=10, + rpc="https://optimism-rpc.publicnode.com", + ), + Chain.POL: ChainInfo( + chain_id=137, + rpc="https://polygon.gateway.tenderly.co", + ), + Chain.WORLDCHAIN: ChainInfo( + chain_id=480, + rpc="https://worldchain-mainnet.gateway.tenderly.co", + ), + Chain.ZORA: ChainInfo( + chain_id=7777777, + rpc="https://rpc.zora.energy/", + ), } # Add all placeholders to allow easy dynamic setup of CHAINS CHAINS_SEPOLIA_ACTIVE: Optional[bool] diff --git a/src/aleph/sdk/evm_utils.py b/src/aleph/sdk/evm_utils.py index c7166cec..4d2026ef 100644 --- a/src/aleph/sdk/evm_utils.py +++ b/src/aleph/sdk/evm_utils.py @@ -79,10 +79,16 @@ def get_super_token_address( return None -def get_chains_with_holding() -> List[Union[Chain, str]]: +def get_compatible_chains() -> List[Union[Chain, str]]: return [chain for chain, info in settings.CHAINS.items() if info.active] +def get_chains_with_holding() -> List[Union[Chain, str]]: + return [ + chain for chain, info in settings.CHAINS.items() if info.active and info.token + ] + + def get_chains_with_super_token() -> List[Union[Chain, str]]: return [ chain diff --git a/src/aleph/sdk/types.py b/src/aleph/sdk/types.py index dab90379..4dbe78b3 100644 --- a/src/aleph/sdk/types.py +++ b/src/aleph/sdk/types.py @@ -77,6 +77,6 @@ class ChainInfo(BaseModel): chain_id: int rpc: str - token: str + token: Optional[str] = None super_token: Optional[str] = None active: bool = True From 745df74b133941d1974c240dda619c3050d5ac7e Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Fri, 1 Nov 2024 12:25:07 +0100 Subject: [PATCH 02/10] Fix: Added chain argument on initialization. --- src/aleph/sdk/account.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aleph/sdk/account.py b/src/aleph/sdk/account.py index edfe83cf..68ee658f 100644 --- a/src/aleph/sdk/account.py +++ b/src/aleph/sdk/account.py @@ -78,7 +78,7 @@ def account_from_file( return account_type(private_key) # type: ignore account_type = load_chain_account_type(chain) - account = account_type(private_key) + account = account_type(private_key, chain) if chain in get_chains_with_super_token(): account.switch_chain(chain) return account @@ -101,7 +101,7 @@ def _load_account( f"Detected {config.chain} account for path {settings.CONFIG_FILE}" ) else: - account_type = account_type = load_chain_account_type( + account_type = load_chain_account_type( Chain.ETH ) # Defaults to ETHAccount logger.warning( From 51df98ad907fa59b262c981a3311ce76de8a27a1 Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Fri, 1 Nov 2024 12:27:12 +0100 Subject: [PATCH 03/10] Fix: Remove venv folder to keep on track. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f18f4bd6..5ab655ca 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ MANIFEST # Per-project virtualenvs .venv*/ +venv/* **/device.key # environment variables From d675694dc7acfaf6011a6fcdf370d5bc40f2ea2d Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Fri, 1 Nov 2024 12:33:20 +0100 Subject: [PATCH 04/10] Fix: Added chain auto-loading if it's not defined. --- src/aleph/sdk/account.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/aleph/sdk/account.py b/src/aleph/sdk/account.py index 68ee658f..8c62ea8c 100644 --- a/src/aleph/sdk/account.py +++ b/src/aleph/sdk/account.py @@ -93,7 +93,7 @@ def _load_account( """Load an account from a private key string or file, or from the configuration file.""" # Loads configuration if no account_type is specified - if not account_type: + if not account_type or not chain: config = load_main_configuration(settings.CONFIG_FILE) if config and hasattr(config, "chain"): account_type = load_chain_account_type(config.chain) @@ -101,9 +101,7 @@ def _load_account( f"Detected {config.chain} account for path {settings.CONFIG_FILE}" ) else: - account_type = load_chain_account_type( - Chain.ETH - ) # Defaults to ETHAccount + account_type = load_chain_account_type(Chain.ETH) # Defaults to ETHAccount logger.warning( f"No main configuration data found in {settings.CONFIG_FILE}, defaulting to {account_type and account_type.__name__}" ) From d97a1e153e0212f580c41afb23d87ea7e67dad38 Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Fri, 1 Nov 2024 12:50:55 +0100 Subject: [PATCH 05/10] Fix: Added chain auto-loading by default configuration if the user don't request it explicitly. --- src/aleph/sdk/account.py | 29 ++++++++++++++++------------- src/aleph/sdk/conf.py | 2 ++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/aleph/sdk/account.py b/src/aleph/sdk/account.py index 8c62ea8c..0bf54201 100644 --- a/src/aleph/sdk/account.py +++ b/src/aleph/sdk/account.py @@ -59,7 +59,7 @@ def account_from_hex_string( return account_type(bytes.fromhex(private_key_str)) # type: ignore account_type = load_chain_account_type(chain) - account = account_type(bytes.fromhex(private_key_str)) + account = account_type(bytes.fromhex(private_key_str), chain) if chain in get_chains_with_super_token(): account.switch_chain(chain) return account # type: ignore @@ -92,26 +92,29 @@ def _load_account( ) -> AccountFromPrivateKey: """Load an account from a private key string or file, or from the configuration file.""" - # Loads configuration if no account_type is specified - if not account_type or not chain: - config = load_main_configuration(settings.CONFIG_FILE) + config = load_main_configuration(settings.CONFIG_FILE) + chain_to_use = settings.DEFAULT_CHAIN + + if not chain: if config and hasattr(config, "chain"): - account_type = load_chain_account_type(config.chain) + chain_to_use = config.chain logger.debug( f"Detected {config.chain} account for path {settings.CONFIG_FILE}" ) - else: - account_type = load_chain_account_type(Chain.ETH) # Defaults to ETHAccount - logger.warning( - f"No main configuration data found in {settings.CONFIG_FILE}, defaulting to {account_type and account_type.__name__}" - ) + + # Loads configuration if no account_type is specified + if not account_type: + account_type = load_chain_account_type(chain_to_use) + logger.warning( + f"No main configuration data found in {settings.CONFIG_FILE}, defaulting to {account_type and account_type.__name__}" + ) # Loads private key from a string if private_key_str: - return account_from_hex_string(private_key_str, account_type, chain) + return account_from_hex_string(private_key_str, account_type, chain_to_use) # Loads private key from a file elif private_key_path and private_key_path.is_file(): - return account_from_file(private_key_path, account_type, chain) + return account_from_file(private_key_path, account_type, chain_to_use) # For ledger keys elif settings.REMOTE_CRYPTO_HOST: logger.debug("Using remote account") @@ -126,7 +129,7 @@ def _load_account( else: new_private_key = get_fallback_private_key() account = account_from_hex_string( - bytes.hex(new_private_key), account_type, chain + bytes.hex(new_private_key), account_type, chain_to_use ) logger.info( f"Generated fallback private key with address {account.get_address()}" diff --git a/src/aleph/sdk/conf.py b/src/aleph/sdk/conf.py index 883430f0..846d82ab 100644 --- a/src/aleph/sdk/conf.py +++ b/src/aleph/sdk/conf.py @@ -187,6 +187,8 @@ class Settings(BaseSettings): CHAINS_BASE_RPC: Optional[str] CHAINS_BSC_RPC: Optional[str] + DEFAULT_CHAIN: Chain = Chain.ETH + # Dns resolver DNS_IPFS_DOMAIN = "ipfs.public.aleph.sh" DNS_PROGRAM_DOMAIN = "program.public.aleph.sh" From 12db40207e69b7b541bb72b5ea7ef741a72689cd Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Fri, 1 Nov 2024 12:55:16 +0100 Subject: [PATCH 06/10] Fix: Solve issue with str chain value --- src/aleph/sdk/chains/evm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aleph/sdk/chains/evm.py b/src/aleph/sdk/chains/evm.py index b3e94f37..0af2758d 100644 --- a/src/aleph/sdk/chains/evm.py +++ b/src/aleph/sdk/chains/evm.py @@ -13,7 +13,7 @@ class EVMAccount(ETHAccount): def __init__(self, private_key: bytes, chain: Optional[Chain] = None): super().__init__(private_key, chain) if chain: - self.CHAIN = chain.value + self.CHAIN = chain @staticmethod def from_mnemonic(mnemonic: str, chain: Optional[Chain] = None) -> "EVMAccount": From 189e3b5ca30ac9200cf7a09ba20ba47858248c96 Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Fri, 1 Nov 2024 12:59:50 +0100 Subject: [PATCH 07/10] Fix: Solve typing issue passing the chain argument. --- src/aleph/sdk/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aleph/sdk/types.py b/src/aleph/sdk/types.py index 4dbe78b3..8f70dbd8 100644 --- a/src/aleph/sdk/types.py +++ b/src/aleph/sdk/types.py @@ -6,7 +6,7 @@ __all__ = ("StorageEnum", "Account", "AccountFromPrivateKey", "GenericMessage") -from aleph_message.models import AlephMessage +from aleph_message.models import AlephMessage, Chain class StorageEnum(str, Enum): @@ -35,7 +35,7 @@ def get_public_key(self) -> str: ... class AccountFromPrivateKey(Account, Protocol): """Only accounts that are initialized from a private key string are supported.""" - def __init__(self, private_key: bytes): ... + def __init__(self, private_key: bytes, chain: Chain): ... async def sign_raw(self, buffer: bytes) -> bytes: ... From 5d67f0a3621f40436218555acc81f689609280fb Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Fri, 1 Nov 2024 13:11:38 +0100 Subject: [PATCH 08/10] Fix: Disable temporarily the chain field change to test it deeply. --- src/aleph/sdk/chains/evm.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/aleph/sdk/chains/evm.py b/src/aleph/sdk/chains/evm.py index 0af2758d..5bf66ef1 100644 --- a/src/aleph/sdk/chains/evm.py +++ b/src/aleph/sdk/chains/evm.py @@ -12,8 +12,9 @@ class EVMAccount(ETHAccount): def __init__(self, private_key: bytes, chain: Optional[Chain] = None): super().__init__(private_key, chain) - if chain: - self.CHAIN = chain + # Decide if we have to send also the specified chain value or always use ETH + # if chain: + # self.CHAIN = chain @staticmethod def from_mnemonic(mnemonic: str, chain: Optional[Chain] = None) -> "EVMAccount": From 39c5798abfc7299e0f5b6de1bf978fe6fa07fb0a Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Mon, 4 Nov 2024 16:34:14 +0100 Subject: [PATCH 09/10] Fix: Update to already released aleph_message dependency. --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8a5d1a76..d6644265 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,15 +30,15 @@ dynamic = [ "version" ] dependencies = [ "aiohttp>=3.8.3", "aioresponses>=0.7.6", - "aleph-message @ git+https://github.com/aleph-im/aleph-message.git@andres-feature-add_new_evm_chains", + "aleph-message>=0.5", "aleph-superfluid>=0.2.1", - "base58==2.1.1", # Needed now as default with _load_account changement + "base58==2.1.1", # Needed now as default with _load_account changement "coincurve; python_version<'3.11'", "coincurve>=19; python_version>='3.11'", "eth-abi>=4; python_version>='3.11'", "eth-typing==4.3.1", "jwcrypto==1.5.6", - "pynacl==1.5", # Needed now as default with _load_account changement + "pynacl==1.5", # Needed now as default with _load_account changement "python-magic", "typing-extensions", "web3==6.3", From c8663c749bc33cc16982c1583d50b98fdf8e329f Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Mon, 4 Nov 2024 16:42:17 +0100 Subject: [PATCH 10/10] Fix: Removed build action for macos-12 as it's deprecated on GitHub actions. --- .github/workflows/build-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 4e32a239..61074b57 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-12, macos-13, macos-14, ubuntu-22.04, ubuntu-24.04] + os: [macos-13, macos-14, ubuntu-22.04, ubuntu-24.04] runs-on: ${{ matrix.os }} steps: