Skip to content

Commit c24a3cf

Browse files
nesitorAndres D. Molins
andauthored
Implement new EVM chains (#182)
* Feature: Implemented new EVM chains. * Fix: Added chain argument on initialization. * Fix: Remove venv folder to keep on track. * Fix: Added chain auto-loading if it's not defined. * Fix: Added chain auto-loading by default configuration if the user don't request it explicitly. * Fix: Solve issue with str chain value * Fix: Solve typing issue passing the chain argument. * Fix: Disable temporarily the chain field change to test it deeply. * Fix: Update to already released aleph_message dependency. * Fix: Removed build action for macos-12 as it's deprecated on GitHub actions. --------- Co-authored-by: Andres D. Molins <[email protected]>
1 parent d54e9ac commit c24a3cf

File tree

8 files changed

+153
-27
lines changed

8 files changed

+153
-27
lines changed

.github/workflows/build-wheels.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
fail-fast: false
1515
matrix:
16-
os: [macos-12, macos-13, macos-14, ubuntu-22.04, ubuntu-24.04]
16+
os: [macos-13, macos-14, ubuntu-22.04, ubuntu-24.04]
1717
runs-on: ${{ matrix.os }}
1818

1919
steps:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ MANIFEST
4747

4848
# Per-project virtualenvs
4949
.venv*/
50+
venv/*
5051
**/device.key
5152

5253
# environment variables

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dynamic = [ "version" ]
3030
dependencies = [
3131
"aiohttp>=3.8.3",
3232
"aioresponses>=0.7.6",
33-
"aleph-message>=0.4.9",
33+
"aleph-message>=0.5",
3434
"aleph-superfluid>=0.2.1",
3535
"base58==2.1.1", # Needed now as default with _load_account changement
3636
"coincurve; python_version<'3.11'",

src/aleph/sdk/account.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
from aleph.sdk.chains.common import get_fallback_private_key
99
from aleph.sdk.chains.ethereum import ETHAccount
10+
from aleph.sdk.chains.evm import EVMAccount
1011
from aleph.sdk.chains.remote import RemoteAccount
1112
from aleph.sdk.chains.solana import SOLAccount
13+
from aleph.sdk.chains.substrate import DOTAccount
1214
from aleph.sdk.conf import load_main_configuration, settings
1315
from aleph.sdk.evm_utils import get_chains_with_super_token
1416
from aleph.sdk.types import AccountFromPrivateKey
@@ -18,10 +20,24 @@
1820
T = TypeVar("T", bound=AccountFromPrivateKey)
1921

2022
chain_account_map: Dict[Chain, Type[T]] = { # type: ignore
21-
Chain.ETH: ETHAccount,
23+
Chain.ARBITRUM: EVMAccount,
2224
Chain.AVAX: ETHAccount,
2325
Chain.BASE: ETHAccount,
26+
Chain.BLAST: EVMAccount,
27+
Chain.BOB: EVMAccount,
28+
Chain.CYBER: EVMAccount,
29+
Chain.DOT: DOTAccount,
30+
Chain.ETH: ETHAccount,
31+
Chain.FRAXTAL: EVMAccount,
32+
Chain.LINEA: EVMAccount,
33+
Chain.LISK: EVMAccount,
34+
Chain.METIS: EVMAccount,
35+
Chain.MODE: EVMAccount,
36+
Chain.OPTIMISM: EVMAccount,
37+
Chain.POL: EVMAccount,
2438
Chain.SOL: SOLAccount,
39+
Chain.WORLDCHAIN: EVMAccount,
40+
Chain.ZORA: EVMAccount,
2541
}
2642

2743

@@ -43,7 +59,7 @@ def account_from_hex_string(
4359
return account_type(bytes.fromhex(private_key_str)) # type: ignore
4460

4561
account_type = load_chain_account_type(chain)
46-
account = account_type(bytes.fromhex(private_key_str))
62+
account = account_type(bytes.fromhex(private_key_str), chain)
4763
if chain in get_chains_with_super_token():
4864
account.switch_chain(chain)
4965
return account # type: ignore
@@ -62,7 +78,7 @@ def account_from_file(
6278
return account_type(private_key) # type: ignore
6379

6480
account_type = load_chain_account_type(chain)
65-
account = account_type(private_key)
81+
account = account_type(private_key, chain)
6682
if chain in get_chains_with_super_token():
6783
account.switch_chain(chain)
6884
return account
@@ -76,28 +92,29 @@ def _load_account(
7692
) -> AccountFromPrivateKey:
7793
"""Load an account from a private key string or file, or from the configuration file."""
7894

79-
# Loads configuration if no account_type is specified
80-
if not account_type:
81-
config = load_main_configuration(settings.CONFIG_FILE)
95+
config = load_main_configuration(settings.CONFIG_FILE)
96+
chain_to_use = settings.DEFAULT_CHAIN
97+
98+
if not chain:
8299
if config and hasattr(config, "chain"):
83-
account_type = load_chain_account_type(config.chain)
100+
chain_to_use = config.chain
84101
logger.debug(
85102
f"Detected {config.chain} account for path {settings.CONFIG_FILE}"
86103
)
87-
else:
88-
account_type = account_type = load_chain_account_type(
89-
Chain.ETH
90-
) # Defaults to ETHAccount
91-
logger.warning(
92-
f"No main configuration data found in {settings.CONFIG_FILE}, defaulting to {account_type and account_type.__name__}"
93-
)
104+
105+
# Loads configuration if no account_type is specified
106+
if not account_type:
107+
account_type = load_chain_account_type(chain_to_use)
108+
logger.warning(
109+
f"No main configuration data found in {settings.CONFIG_FILE}, defaulting to {account_type and account_type.__name__}"
110+
)
94111

95112
# Loads private key from a string
96113
if private_key_str:
97-
return account_from_hex_string(private_key_str, account_type, chain)
114+
return account_from_hex_string(private_key_str, account_type, chain_to_use)
98115
# Loads private key from a file
99116
elif private_key_path and private_key_path.is_file():
100-
return account_from_file(private_key_path, account_type, chain)
117+
return account_from_file(private_key_path, account_type, chain_to_use)
101118
# For ledger keys
102119
elif settings.REMOTE_CRYPTO_HOST:
103120
logger.debug("Using remote account")
@@ -112,7 +129,7 @@ def _load_account(
112129
else:
113130
new_private_key = get_fallback_private_key()
114131
account = account_from_hex_string(
115-
bytes.hex(new_private_key), account_type, chain
132+
bytes.hex(new_private_key), account_type, chain_to_use
116133
)
117134
logger.info(
118135
f"Generated fallback private key with address {account.get_address()}"

src/aleph/sdk/chains/evm.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from decimal import Decimal
2+
from pathlib import Path
3+
from typing import Awaitable, Optional
4+
5+
from aleph_message.models import Chain
6+
from eth_account import Account # type: ignore
7+
8+
from .common import get_fallback_private_key
9+
from .ethereum import ETHAccount
10+
11+
12+
class EVMAccount(ETHAccount):
13+
def __init__(self, private_key: bytes, chain: Optional[Chain] = None):
14+
super().__init__(private_key, chain)
15+
# Decide if we have to send also the specified chain value or always use ETH
16+
# if chain:
17+
# self.CHAIN = chain
18+
19+
@staticmethod
20+
def from_mnemonic(mnemonic: str, chain: Optional[Chain] = None) -> "EVMAccount":
21+
Account.enable_unaudited_hdwallet_features()
22+
return EVMAccount(
23+
private_key=Account.from_mnemonic(mnemonic=mnemonic).key, chain=chain
24+
)
25+
26+
def get_token_balance(self) -> Decimal:
27+
raise ValueError(f"Token not implemented for this chain {self.CHAIN}")
28+
29+
def get_super_token_balance(self) -> Decimal:
30+
raise ValueError(f"Super token not implemented for this chain {self.CHAIN}")
31+
32+
def create_flow(self, receiver: str, flow: Decimal) -> Awaitable[str]:
33+
raise ValueError(f"Flow creation not implemented for this chain {self.CHAIN}")
34+
35+
def get_flow(self, receiver: str):
36+
raise ValueError(f"Get flow not implemented for this chain {self.CHAIN}")
37+
38+
def update_flow(self, receiver: str, flow: Decimal) -> Awaitable[str]:
39+
raise ValueError(f"Flow update not implemented for this chain {self.CHAIN}")
40+
41+
def delete_flow(self, receiver: str) -> Awaitable[str]:
42+
raise ValueError(f"Flow deletion not implemented for this chain {self.CHAIN}")
43+
44+
45+
def get_fallback_account(
46+
path: Optional[Path] = None, chain: Optional[Chain] = None
47+
) -> ETHAccount:
48+
return ETHAccount(private_key=get_fallback_private_key(path=path), chain=chain)

src/aleph/sdk/conf.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,9 @@ class Settings(BaseSettings):
9999
active=False,
100100
),
101101
# MAINNETS
102-
Chain.ETH: ChainInfo(
103-
chain_id=1,
104-
rpc="https://eth-mainnet.public.blastapi.io",
105-
token="0x27702a26126e0B3702af63Ee09aC4d1A084EF628",
102+
Chain.ARBITRUM: ChainInfo(
103+
chain_id=42161,
104+
rpc="https://arbitrum-one.publicnode.com",
106105
),
107106
Chain.AVAX: ChainInfo(
108107
chain_id=43114,
@@ -116,12 +115,65 @@ class Settings(BaseSettings):
116115
token="0xc0Fbc4967259786C743361a5885ef49380473dCF",
117116
super_token="0xc0Fbc4967259786C743361a5885ef49380473dCF",
118117
),
118+
Chain.BLAST: ChainInfo(
119+
chain_id=81457,
120+
rpc="https://blastl2-mainnet.public.blastapi.io",
121+
),
122+
Chain.BOB: ChainInfo(
123+
chain_id=60808,
124+
rpc="https://bob-mainnet.public.blastapi.io",
125+
),
119126
Chain.BSC: ChainInfo(
120127
chain_id=56,
121128
rpc="https://binance.llamarpc.com",
122129
token="0x82D2f8E02Afb160Dd5A480a617692e62de9038C4",
123130
active=False,
124131
),
132+
Chain.CYBER: ChainInfo(
133+
chain_id=7560,
134+
rpc="https://rpc.cyber.co",
135+
),
136+
Chain.ETH: ChainInfo(
137+
chain_id=1,
138+
rpc="https://eth-mainnet.public.blastapi.io",
139+
token="0x27702a26126e0B3702af63Ee09aC4d1A084EF628",
140+
),
141+
Chain.FRAXTAL: ChainInfo(
142+
chain_id=252,
143+
rpc="https://rpc.frax.com",
144+
),
145+
Chain.LINEA: ChainInfo(
146+
chain_id=59144,
147+
rpc="https://linea-rpc.publicnode.com",
148+
),
149+
Chain.LISK: ChainInfo(
150+
chain_id=1135,
151+
rpc="https://rpc.api.lisk.com",
152+
),
153+
Chain.METIS: ChainInfo(
154+
chain_id=1088,
155+
rpc="https://metis.drpc.org",
156+
),
157+
Chain.MODE: ChainInfo(
158+
chain_id=34443,
159+
rpc="https://mode.drpc.org",
160+
),
161+
Chain.OPTIMISM: ChainInfo(
162+
chain_id=10,
163+
rpc="https://optimism-rpc.publicnode.com",
164+
),
165+
Chain.POL: ChainInfo(
166+
chain_id=137,
167+
rpc="https://polygon.gateway.tenderly.co",
168+
),
169+
Chain.WORLDCHAIN: ChainInfo(
170+
chain_id=480,
171+
rpc="https://worldchain-mainnet.gateway.tenderly.co",
172+
),
173+
Chain.ZORA: ChainInfo(
174+
chain_id=7777777,
175+
rpc="https://rpc.zora.energy/",
176+
),
125177
}
126178
# Add all placeholders to allow easy dynamic setup of CHAINS
127179
CHAINS_SEPOLIA_ACTIVE: Optional[bool]
@@ -135,6 +187,8 @@ class Settings(BaseSettings):
135187
CHAINS_BASE_RPC: Optional[str]
136188
CHAINS_BSC_RPC: Optional[str]
137189

190+
DEFAULT_CHAIN: Chain = Chain.ETH
191+
138192
# Dns resolver
139193
DNS_IPFS_DOMAIN = "ipfs.public.aleph.sh"
140194
DNS_PROGRAM_DOMAIN = "program.public.aleph.sh"

src/aleph/sdk/evm_utils.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,16 @@ def get_super_token_address(
7979
return None
8080

8181

82-
def get_chains_with_holding() -> List[Union[Chain, str]]:
82+
def get_compatible_chains() -> List[Union[Chain, str]]:
8383
return [chain for chain, info in settings.CHAINS.items() if info.active]
8484

8585

86+
def get_chains_with_holding() -> List[Union[Chain, str]]:
87+
return [
88+
chain for chain, info in settings.CHAINS.items() if info.active and info.token
89+
]
90+
91+
8692
def get_chains_with_super_token() -> List[Union[Chain, str]]:
8793
return [
8894
chain

src/aleph/sdk/types.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
__all__ = ("StorageEnum", "Account", "AccountFromPrivateKey", "GenericMessage")
88

9-
from aleph_message.models import AlephMessage
9+
from aleph_message.models import AlephMessage, Chain
1010

1111

1212
class StorageEnum(str, Enum):
@@ -35,7 +35,7 @@ def get_public_key(self) -> str: ...
3535
class AccountFromPrivateKey(Account, Protocol):
3636
"""Only accounts that are initialized from a private key string are supported."""
3737

38-
def __init__(self, private_key: bytes): ...
38+
def __init__(self, private_key: bytes, chain: Chain): ...
3939

4040
async def sign_raw(self, buffer: bytes) -> bytes: ...
4141

@@ -77,6 +77,6 @@ class ChainInfo(BaseModel):
7777

7878
chain_id: int
7979
rpc: str
80-
token: str
80+
token: Optional[str] = None
8181
super_token: Optional[str] = None
8282
active: bool = True

0 commit comments

Comments
 (0)