Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new EVM chains #182

Merged
merged 10 commits into from
Nov 4, 2024
2 changes: 1 addition & 1 deletion .github/workflows/build-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ MANIFEST

# Per-project virtualenvs
.venv*/
venv/*
**/device.key

# environment variables
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dynamic = [ "version" ]
dependencies = [
"aiohttp>=3.8.3",
"aioresponses>=0.7.6",
"aleph-message>=0.4.9",
"aleph-message>=0.5",
"aleph-superfluid>=0.2.1",
"base58==2.1.1", # Needed now as default with _load_account changement
"coincurve; python_version<'3.11'",
Expand Down
51 changes: 34 additions & 17 deletions src/aleph/sdk/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
}


Expand All @@ -43,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
Expand All @@ -62,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
Expand All @@ -76,28 +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:
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 = 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")
Expand All @@ -112,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()}"
Expand Down
48 changes: 48 additions & 0 deletions src/aleph/sdk/chains/evm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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)
# Decide if we have to send also the specified chain value or always use ETH
# if chain:
# self.CHAIN = chain
nesitor marked this conversation as resolved.
Show resolved Hide resolved

@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)
62 changes: 58 additions & 4 deletions src/aleph/sdk/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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]
Expand All @@ -135,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"
Expand Down
8 changes: 7 additions & 1 deletion src/aleph/sdk/evm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/aleph/sdk/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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: ...

Expand Down Expand Up @@ -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
Loading