Skip to content

Commit de0b0a5

Browse files
committed
refactor: explicit service owner eoa/multisig support
1 parent caa2081 commit de0b0a5

3 files changed

Lines changed: 58 additions & 24 deletions

File tree

src/iwa/plugins/olas/importer.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,13 @@ class DiscoveredService:
8282
service_name: Optional[str] = None
8383
# New fields for full service import
8484
staking_contract_address: Optional[str] = None
85-
service_owner_address: Optional[str] = None
86-
service_owner_safe: Optional[str] = None # To store the Safe Owner address if it exists
85+
service_owner_eoa_address: Optional[str] = None
86+
service_owner_multisig_address: Optional[str] = None
87+
88+
@property
89+
def service_owner_address(self) -> Optional[str]:
90+
"""Backward compatibility: effective owner address."""
91+
return self.service_owner_multisig_address or self.service_owner_eoa_address
8792

8893
@property
8994
def agent_key(self) -> Optional[DiscoveredKey]:
@@ -551,14 +556,14 @@ def _extract_owner_address(self, service: DiscoveredService, operate_folder: Pat
551556

552557
if safe_owner_address:
553558
# CASE: Owner is Safe
554-
service.service_owner_address = safe_owner_address # The Safe is the official owner
555-
service.service_owner_safe = safe_owner_address
556-
# We also need to ensure the EOA is imported. 'check_wallet' usually handles the EOA.
557-
# But we need to link them.
559+
service.service_owner_multisig_address = safe_owner_address
560+
service.service_owner_eoa_address = eoa_address # The EOA is the signer/controller
561+
558562
logger.debug(f"Extracted Safe owner address: {safe_owner_address} (Signer: {eoa_address})")
559563
elif eoa_address:
560564
# CASE: Owner is EOA
561-
service.service_owner_address = eoa_address
565+
service.service_owner_eoa_address = eoa_address
566+
service.service_owner_multisig_address = None
562567
logger.debug(f"Extracted EOA owner address: {eoa_address}")
563568

564569
except (json.JSONDecodeError, IOError) as e:
@@ -573,14 +578,14 @@ def _merge_unique_keys(self, service: DiscoveredService, new_keys: List[Discover
573578
existing_addrs.add(key.address.lower())
574579

575580
def _infer_owner_address(self, service: DiscoveredService) -> None:
576-
"""Infer service_owner_address from keys with role='owner' if not already set."""
577-
if service.service_owner_address:
581+
"""Infer service_owner_eoa_address from keys with role='owner' if not already set."""
582+
if service.service_owner_eoa_address:
578583
return # Already set
579584

580585
for key in service.keys:
581586
if key.role == "owner" and key.address:
582-
service.service_owner_address = key.address
583-
logger.debug(f"Inferred owner address from key: {key.address}")
587+
service.service_owner_eoa_address = key.address
588+
logger.debug(f"Inferred owner EOA address from key: {key.address}")
584589
return
585590

586591
def _parse_keystore_file(
@@ -774,19 +779,19 @@ def _import_discovered_safes(self, service: DiscoveredService, result: ImportRes
774779
result.errors.append(f"Safe {service.safe_address}: {safe_result[1]}")
775780

776781
# 2. Import Owner Safe (if it exists and is different)
777-
if service.service_owner_safe and service.service_owner_safe != service.safe_address:
782+
if service.service_owner_multisig_address and service.service_owner_multisig_address != service.safe_address:
778783
# Signer for Owner Safe is the EOA owner key
779784
owner_signers = self._get_owner_signers(service)
780785

781786
safe_result = self._import_safe(
782-
address=service.service_owner_safe,
787+
address=service.service_owner_multisig_address,
783788
signers=owner_signers,
784789
tag_suffix="owner_safe", # e.g. trader_zeta_owner_safe
785790
service_name=service.service_name
786791
)
787792
if safe_result[0]:
788-
result.imported_safes.append(service.service_owner_safe)
789-
logger.info(f"Imported Owner Safe {service.service_owner_safe}")
793+
result.imported_safes.append(service.service_owner_multisig_address)
794+
logger.info(f"Imported Owner Safe {service.service_owner_multisig_address}")
790795

791796
def _get_agent_signers(self, service: DiscoveredService) -> List[str]:
792797
"""Get list of signers for the agent safe."""
@@ -971,7 +976,8 @@ def _import_service_config(self, service: DiscoveredService) -> Tuple[bool, str]
971976
service_id=service.service_id,
972977
agent_ids=[25], # Trader agents always use agent ID 25
973978
multisig_address=service.safe_address,
974-
service_owner_address=service.service_owner_address,
979+
service_owner_eoa_address=service.service_owner_eoa_address,
980+
service_owner_multisig_address=service.service_owner_multisig_address,
975981
staking_contract_address=service.staking_contract_address,
976982
token_address=str(OLAS_TOKEN_ADDRESS_GNOSIS),
977983
)

src/iwa/plugins/olas/models.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from typing import Dict, List, Optional
44

5-
from pydantic import BaseModel, Field
5+
from pydantic import BaseModel, Field, root_validator
66

77
from iwa.core.models import EthereumAddress
88

@@ -14,12 +14,40 @@ class Service(BaseModel):
1414
chain_name: str
1515
service_id: int # Unique per chain
1616
agent_ids: List[int] = Field(default_factory=list) # List of agent type IDs
17-
service_owner_address: Optional[EthereumAddress] = None
17+
18+
# New explicit owner fields
19+
service_owner_eoa_address: Optional[EthereumAddress] = None
20+
service_owner_multisig_address: Optional[EthereumAddress] = None
21+
22+
# Deprecated fields (kept for migration, removed from physical model via aliasing/validation)
23+
# Actually, we keep it optional but not used, or use migration logic.
24+
# Let's remove it from fields and rely on before validator to map it to eoa.
25+
1826
agent_address: Optional[EthereumAddress] = None
1927
multisig_address: Optional[EthereumAddress] = None
2028
staking_contract_address: Optional[EthereumAddress] = None
2129
token_address: Optional[EthereumAddress] = None
2230

31+
@root_validator(pre=True)
32+
def migrate_owner_fields(cls, values):
33+
"""Migrate legacy service_owner_address to service_owner_eoa_address."""
34+
# Check for legacy 'service_owner_address'
35+
if "service_owner_address" in values and values["service_owner_address"]:
36+
legacy_addr = values["service_owner_address"]
37+
38+
# If service_owner_eoa_address is missing, use legacy
39+
if "service_owner_eoa_address" not in values or not values["service_owner_eoa_address"]:
40+
values["service_owner_eoa_address"] = legacy_addr
41+
42+
# Remove legacy field from values so it doesn't cause extra field errors if we removed it from model
43+
# Or if strict.
44+
return values
45+
46+
@property
47+
def service_owner_address(self) -> Optional[EthereumAddress]:
48+
"""Backward compatibility property: Returns effective owner (Safe if present, else EOA)."""
49+
return self.service_owner_multisig_address or self.service_owner_eoa_address
50+
2351
@property
2452
def key(self) -> str:
2553
"""Unique key for this service (chain_name:service_id)."""

src/iwa/plugins/olas/plugin.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,19 +192,19 @@ def _add_owner_info(self, table, service) -> None:
192192
val = f"[red]{val}[/red]"
193193
status = "🔒 encrypted" if owner_key.is_encrypted else "🔓 plaintext"
194194
table.add_row("Owner (EOA)", f"{val} {status}")
195-
elif service.service_owner_address and service.service_owner_address != service.service_owner_safe:
196-
# Fallback if we have an address but no key object, and it's not the safe
197-
table.add_row("Owner (EOA)", service.service_owner_address)
195+
elif service.service_owner_eoa_address:
196+
# Fallback if we have an address but no key object
197+
table.add_row("Owner (EOA)", service.service_owner_eoa_address)
198198
else:
199199
table.add_row("Owner (EOA)", "[yellow]N/A[/yellow]")
200200

201201
# 2. Display Safe Owner
202-
if service.service_owner_safe:
202+
if service.service_owner_multisig_address:
203203
# Check on-chain existence if possible (using same helper as agent safe)
204204
on_chain_signers, safe_exists = self._get_safe_signers(
205-
service.service_owner_safe, service.chain_name
205+
service.service_owner_multisig_address, service.chain_name
206206
)
207-
val = service.service_owner_safe
207+
val = service.service_owner_multisig_address
208208
if safe_exists:
209209
val += " [green]✓[/green]"
210210

0 commit comments

Comments
 (0)