iwa 0.0.31__py3-none-any.whl → 0.0.32__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iwa/core/services/transaction.py +19 -1
- iwa/core/services/transfer/erc20.py +24 -3
- iwa/plugins/olas/importer.py +2 -0
- iwa/plugins/olas/service_manager/lifecycle.py +27 -9
- iwa/plugins/olas/service_manager/staking.py +30 -5
- iwa/plugins/olas/tests/test_service_manager.py +4 -0
- iwa/tools/reset_env.py +26 -5
- iwa/web/routers/olas/services.py +74 -33
- iwa/web/static/app.js +16 -7
- {iwa-0.0.31.dist-info → iwa-0.0.32.dist-info}/METADATA +1 -1
- {iwa-0.0.31.dist-info → iwa-0.0.32.dist-info}/RECORD +15 -15
- {iwa-0.0.31.dist-info → iwa-0.0.32.dist-info}/WHEEL +0 -0
- {iwa-0.0.31.dist-info → iwa-0.0.32.dist-info}/entry_points.txt +0 -0
- {iwa-0.0.31.dist-info → iwa-0.0.32.dist-info}/licenses/LICENSE +0 -0
- {iwa-0.0.31.dist-info → iwa-0.0.32.dist-info}/top_level.txt +0 -0
iwa/core/services/transaction.py
CHANGED
|
@@ -214,6 +214,24 @@ class TransactionService:
|
|
|
214
214
|
self.account_service = account_service
|
|
215
215
|
self.safe_service = safe_service
|
|
216
216
|
|
|
217
|
+
def _resolve_label(self, address: str, chain_name: str = "gnosis") -> str:
|
|
218
|
+
"""Resolve address to human-readable label."""
|
|
219
|
+
if not address:
|
|
220
|
+
return "None"
|
|
221
|
+
# Try account/safe tags
|
|
222
|
+
tag = self.account_service.get_tag_by_address(address)
|
|
223
|
+
if tag:
|
|
224
|
+
return tag
|
|
225
|
+
# Try token/contract names
|
|
226
|
+
try:
|
|
227
|
+
chain_interface = ChainInterfaces().get(chain_name)
|
|
228
|
+
name = chain_interface.chain.get_token_name(address)
|
|
229
|
+
if name:
|
|
230
|
+
return name
|
|
231
|
+
except Exception:
|
|
232
|
+
pass
|
|
233
|
+
return address
|
|
234
|
+
|
|
217
235
|
def sign_and_send( # noqa: C901
|
|
218
236
|
self,
|
|
219
237
|
transaction: dict,
|
|
@@ -388,7 +406,7 @@ class TransactionService:
|
|
|
388
406
|
tags: List[str] = None
|
|
389
407
|
) -> Tuple[bool, Dict]:
|
|
390
408
|
"""Execute transaction via SafeService."""
|
|
391
|
-
logger.info(f"Routing transaction via Safe {signer_account.address}...")
|
|
409
|
+
logger.info(f"Routing transaction via Safe {self._resolve_label(signer_account.address, chain_name)}...")
|
|
392
410
|
|
|
393
411
|
try:
|
|
394
412
|
# Extract basic params
|
|
@@ -14,9 +14,28 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from iwa.core.services.transfer import TransferService
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
class ERC20TransferMixin:
|
|
18
19
|
"""Mixin for ERC20 token transfers and approvals."""
|
|
19
20
|
|
|
21
|
+
def _resolve_label(self, address: str, chain_name: str = "gnosis") -> str:
|
|
22
|
+
"""Resolve address to human-readable label."""
|
|
23
|
+
if not address:
|
|
24
|
+
return "None"
|
|
25
|
+
try:
|
|
26
|
+
# Try account/safe tags
|
|
27
|
+
tag = self.account_service.get_tag_by_address(address)
|
|
28
|
+
if tag:
|
|
29
|
+
return tag
|
|
30
|
+
# Try token/contract names
|
|
31
|
+
chain_interface = ChainInterfaces().get(chain_name)
|
|
32
|
+
name = chain_interface.chain.get_token_name(address)
|
|
33
|
+
if name:
|
|
34
|
+
return name
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
return address
|
|
38
|
+
|
|
20
39
|
def _send_erc20_via_safe(
|
|
21
40
|
self: "TransferService",
|
|
22
41
|
from_account: StoredSafeAccount,
|
|
@@ -181,9 +200,11 @@ class ERC20TransferMixin:
|
|
|
181
200
|
is_safe = getattr(owner_account, "threshold", None) is not None
|
|
182
201
|
amount_eth = float(chain_interface.web3.from_wei(amount_wei, "ether"))
|
|
183
202
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
203
|
+
if is_safe:
|
|
204
|
+
logger.info(
|
|
205
|
+
f"Approving {self._resolve_label(spender_address, chain_name)} to spend {amount_eth:.4f} "
|
|
206
|
+
f"{self._resolve_label(token_address, chain_name)} from {self._resolve_label(owner_account.address, chain_name)}"
|
|
207
|
+
)
|
|
187
208
|
|
|
188
209
|
if is_safe:
|
|
189
210
|
tx_limit = self.safe_service.execute_safe_transaction(
|
iwa/plugins/olas/importer.py
CHANGED
|
@@ -889,9 +889,11 @@ class OlasServiceImporter:
|
|
|
889
889
|
"""Generate a unique tag for an imported key.
|
|
890
890
|
|
|
891
891
|
Tags follow the pattern: {service_name}_{role}[_eoa]
|
|
892
|
+
|
|
892
893
|
Examples:
|
|
893
894
|
- trader_alpha_agent
|
|
894
895
|
- trader_alpha_owner_eoa (EOA keys for owner role)
|
|
896
|
+
|
|
895
897
|
"""
|
|
896
898
|
# Use service name as prefix, or 'imported' as fallback
|
|
897
899
|
prefix = service_name or "imported"
|
|
@@ -99,6 +99,24 @@ class LifecycleManagerMixin:
|
|
|
99
99
|
manager.spin_up(bond_amount_wei=5000e18, staking_contract=staking)
|
|
100
100
|
"""
|
|
101
101
|
|
|
102
|
+
def _get_label(self, address: str) -> str:
|
|
103
|
+
"""Resolve address to a human-readable label."""
|
|
104
|
+
if not address:
|
|
105
|
+
return "None"
|
|
106
|
+
|
|
107
|
+
# Try account service tags first (wallets, safes)
|
|
108
|
+
tag = self.wallet.account_service.get_tag_by_address(address)
|
|
109
|
+
if tag:
|
|
110
|
+
return tag
|
|
111
|
+
|
|
112
|
+
# Try token names
|
|
113
|
+
chain_interface = ChainInterfaces().get(self.chain_name)
|
|
114
|
+
token_name = chain_interface.chain.get_token_name(address)
|
|
115
|
+
if token_name:
|
|
116
|
+
return token_name
|
|
117
|
+
|
|
118
|
+
return address
|
|
119
|
+
|
|
102
120
|
def create(
|
|
103
121
|
self,
|
|
104
122
|
chain_name: str = "gnosis",
|
|
@@ -141,8 +159,8 @@ class LifecycleManagerMixin:
|
|
|
141
159
|
agent_params = self._prepare_agent_params(agent_id_values, bond_amount_wei)
|
|
142
160
|
|
|
143
161
|
logger.info(
|
|
144
|
-
f"Preparing create tx: owner={service_owner_account.address}, "
|
|
145
|
-
f"token={token_address}, agent_ids={agent_id_values}, agent_params={agent_params}"
|
|
162
|
+
f"Preparing create tx: owner={self._get_label(service_owner_account.address)}, "
|
|
163
|
+
f"token={self._get_label(token_address)}, agent_ids={agent_id_values}, agent_params={agent_params}"
|
|
146
164
|
)
|
|
147
165
|
|
|
148
166
|
receipt = self._send_create_transaction(
|
|
@@ -339,7 +357,7 @@ class LifecycleManagerMixin:
|
|
|
339
357
|
return False
|
|
340
358
|
|
|
341
359
|
token_address = self._get_service_token(service_id)
|
|
342
|
-
logger.debug(f"[ACTIVATE] Token address: {token_address}")
|
|
360
|
+
logger.debug(f"[ACTIVATE] Token address: {self._get_label(token_address)}")
|
|
343
361
|
|
|
344
362
|
service_info = self.registry.get_service(service_id)
|
|
345
363
|
security_deposit = service_info["security_deposit"]
|
|
@@ -518,7 +536,7 @@ class LifecycleManagerMixin:
|
|
|
518
536
|
# The OLAS token approval was done in _ensure_token_approval_for_activation
|
|
519
537
|
activation_value = security_deposit if is_native else 1
|
|
520
538
|
logger.info(
|
|
521
|
-
f"[ACTIVATE] Token={token_address}, is_native={is_native}, "
|
|
539
|
+
f"[ACTIVATE] Token={self._get_label(token_address)}, is_native={is_native}, "
|
|
522
540
|
f"activation_value={activation_value} wei"
|
|
523
541
|
)
|
|
524
542
|
|
|
@@ -526,7 +544,7 @@ class LifecycleManagerMixin:
|
|
|
526
544
|
owner_address = self.service.service_owner_address or self.wallet.master_account.address
|
|
527
545
|
|
|
528
546
|
logger.debug(
|
|
529
|
-
f"[ACTIVATE] Preparing tx from {owner_address}: service_id={service_id}, value={activation_value}"
|
|
547
|
+
f"[ACTIVATE] Preparing tx from {self._get_label(owner_address)}: service_id={service_id}, value={activation_value}"
|
|
530
548
|
)
|
|
531
549
|
activate_tx = self.manager.prepare_activate_registration_tx(
|
|
532
550
|
from_address=owner_address,
|
|
@@ -583,7 +601,7 @@ class LifecycleManagerMixin:
|
|
|
583
601
|
if not agent_account_address:
|
|
584
602
|
logger.error("[REGISTER] Failed to get/create agent account")
|
|
585
603
|
return False
|
|
586
|
-
logger.info(f"[REGISTER] Agent address: {agent_account_address}")
|
|
604
|
+
logger.info(f"[REGISTER] Agent address: {self._get_label(agent_account_address)}")
|
|
587
605
|
|
|
588
606
|
if not self._ensure_agent_token_approval(agent_account_address, bond_amount_wei):
|
|
589
607
|
logger.error("[REGISTER] Token approval failed")
|
|
@@ -721,7 +739,7 @@ class LifecycleManagerMixin:
|
|
|
721
739
|
total_value = 1 * len(self.service.agent_ids)
|
|
722
740
|
|
|
723
741
|
logger.info(
|
|
724
|
-
f"[REGISTER] Token={token_address}, is_native={is_native}, "
|
|
742
|
+
f"[REGISTER] Token={self._get_label(token_address)}, is_native={is_native}, "
|
|
725
743
|
f"total_value={total_value} wei"
|
|
726
744
|
)
|
|
727
745
|
|
|
@@ -729,7 +747,7 @@ class LifecycleManagerMixin:
|
|
|
729
747
|
owner_address = self.service.service_owner_address or self.wallet.master_account.address
|
|
730
748
|
|
|
731
749
|
logger.debug(
|
|
732
|
-
f"[REGISTER] Preparing tx from {owner_address}: agent={agent_account_address}, "
|
|
750
|
+
f"[REGISTER] Preparing tx from {self._get_label(owner_address)}: agent={self._get_label(agent_account_address)}, "
|
|
733
751
|
f"agent_ids={self.service.agent_ids}, value={total_value}"
|
|
734
752
|
)
|
|
735
753
|
|
|
@@ -782,7 +800,7 @@ class LifecycleManagerMixin:
|
|
|
782
800
|
)
|
|
783
801
|
return False
|
|
784
802
|
|
|
785
|
-
logger.debug(f"[DEPLOY] Preparing deploy tx for owner {self.service.service_owner_address}")
|
|
803
|
+
logger.debug(f"[DEPLOY] Preparing deploy tx for owner {self._get_label(self.service.service_owner_address)}")
|
|
786
804
|
deploy_tx = self.manager.prepare_deploy_tx(
|
|
787
805
|
from_address=self.service.service_owner_address,
|
|
788
806
|
service_id=self.service.service_id,
|
|
@@ -74,6 +74,31 @@ class StakingManagerMixin:
|
|
|
74
74
|
- Service token must match staking contract's required token
|
|
75
75
|
"""
|
|
76
76
|
|
|
77
|
+
def _get_label(self, address: str) -> str:
|
|
78
|
+
"""Resolve address to a human-readable label."""
|
|
79
|
+
if not address:
|
|
80
|
+
return "None"
|
|
81
|
+
|
|
82
|
+
# Try account service tags first (wallets, safes)
|
|
83
|
+
try:
|
|
84
|
+
tag = self.wallet.account_service.get_tag_by_address(address)
|
|
85
|
+
if tag:
|
|
86
|
+
return tag
|
|
87
|
+
except AttributeError:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
# Try token/contract names
|
|
91
|
+
try:
|
|
92
|
+
from iwa.core.chain import ChainInterfaces
|
|
93
|
+
chain_interface = ChainInterfaces().get(self.chain_name)
|
|
94
|
+
token_name = chain_interface.chain.get_token_name(address)
|
|
95
|
+
if token_name:
|
|
96
|
+
return token_name
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
return address
|
|
101
|
+
|
|
77
102
|
def get_staking_status(self) -> Optional[StakingStatus]:
|
|
78
103
|
"""Get comprehensive staking status for the active service.
|
|
79
104
|
|
|
@@ -246,7 +271,7 @@ class StakingManagerMixin:
|
|
|
246
271
|
"""
|
|
247
272
|
logger.info("=" * 50)
|
|
248
273
|
logger.info(f"[STAKE] Starting staking for service {self.service.service_id}")
|
|
249
|
-
logger.info(f"[STAKE] Contract: {staking_contract.address}")
|
|
274
|
+
logger.info(f"[STAKE] Contract: {self._get_label(staking_contract.address)}")
|
|
250
275
|
logger.info("=" * 50)
|
|
251
276
|
|
|
252
277
|
# 1. Validation
|
|
@@ -412,7 +437,7 @@ class StakingManagerMixin:
|
|
|
412
437
|
owner_address = self.service.service_owner_address or self.wallet.master_account.address
|
|
413
438
|
|
|
414
439
|
# Approve service NFT - this is an ERC-721 approval, not ERC-20
|
|
415
|
-
logger.debug(f"[STAKE] Approving service NFT for staking contract from {owner_address}...")
|
|
440
|
+
logger.debug(f"[STAKE] Approving service NFT for staking contract from {self._get_label(owner_address)}...")
|
|
416
441
|
approve_tx = self.registry.prepare_approve_tx(
|
|
417
442
|
from_address=owner_address,
|
|
418
443
|
spender=staking_contract.address,
|
|
@@ -458,7 +483,7 @@ class StakingManagerMixin:
|
|
|
458
483
|
# Use service owner which holds the NFT (not necessarily master)
|
|
459
484
|
owner_address = self.service.service_owner_address or self.wallet.master_account.address
|
|
460
485
|
|
|
461
|
-
logger.debug(f"[STAKE] Preparing stake transaction from {owner_address}...")
|
|
486
|
+
logger.debug(f"[STAKE] Preparing stake transaction from {self._get_label(owner_address)}...")
|
|
462
487
|
stake_tx = staking_contract.prepare_stake_tx(
|
|
463
488
|
from_address=owner_address,
|
|
464
489
|
service_id=self.service.service_id,
|
|
@@ -512,7 +537,7 @@ class StakingManagerMixin:
|
|
|
512
537
|
return False
|
|
513
538
|
|
|
514
539
|
logger.info(
|
|
515
|
-
f"Preparing to unstake service {self.service.service_id} from {staking_contract.address}"
|
|
540
|
+
f"Preparing to unstake service {self.service.service_id} from {self._get_label(staking_contract.address)}"
|
|
516
541
|
)
|
|
517
542
|
|
|
518
543
|
# Check that the service is staked
|
|
@@ -553,7 +578,7 @@ class StakingManagerMixin:
|
|
|
553
578
|
# Unstake the service
|
|
554
579
|
try:
|
|
555
580
|
logger.info(
|
|
556
|
-
f"Preparing unstake transaction for service {self.service.service_id} from {owner_address}"
|
|
581
|
+
f"Preparing unstake transaction for service {self.service.service_id} from {self._get_label(owner_address)}"
|
|
557
582
|
)
|
|
558
583
|
unstake_tx = staking_contract.prepare_unstake_tx(
|
|
559
584
|
from_address=owner_address,
|
|
@@ -69,6 +69,10 @@ def mock_wallet():
|
|
|
69
69
|
new_acc = MagicMock()
|
|
70
70
|
new_acc.address = "0x0987654321098765432109876543210987654321"
|
|
71
71
|
wallet.key_storage.create_account.return_value = new_acc
|
|
72
|
+
wallet.key_storage.create_account.return_value = new_acc
|
|
73
|
+
# Mock account_service
|
|
74
|
+
wallet.account_service = MagicMock()
|
|
75
|
+
wallet.account_service.get_tag_by_address.return_value = "mock_tag"
|
|
72
76
|
# Mock transfer_service
|
|
73
77
|
wallet.transfer_service = MagicMock()
|
|
74
78
|
wallet.transfer_service.approve_erc20.return_value = True
|
iwa/tools/reset_env.py
CHANGED
|
@@ -17,11 +17,18 @@ from iwa.core.models import Config
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _reset_tenderly(profile: int) -> None:
|
|
20
|
-
"""Reset Tenderly networks using
|
|
21
|
-
cmd = ["
|
|
20
|
+
"""Reset Tenderly networks using reset_tenderly.py script."""
|
|
21
|
+
cmd = ["uv", "run", "src/iwa/tools/reset_tenderly.py", "--profile", str(profile)]
|
|
22
22
|
print(f"Running: {' '.join(cmd)}")
|
|
23
23
|
try:
|
|
24
|
-
|
|
24
|
+
# Ensure PYTHONPATH is set to include src
|
|
25
|
+
env = {"PYTHONPATH": "src"}
|
|
26
|
+
# Merge with current env to keep PATH etc.
|
|
27
|
+
import os
|
|
28
|
+
full_env = os.environ.copy()
|
|
29
|
+
full_env.update(env)
|
|
30
|
+
|
|
31
|
+
subprocess.check_call(cmd, env=full_env) # nosec B603
|
|
25
32
|
except subprocess.CalledProcessError as e:
|
|
26
33
|
print(f"Error running reset-tenderly: {e}")
|
|
27
34
|
sys.exit(1)
|
|
@@ -97,12 +104,26 @@ def _clean_wallet_accounts() -> None:
|
|
|
97
104
|
|
|
98
105
|
def main():
|
|
99
106
|
"""Reset the environment by clearing networks, services, and accounts."""
|
|
107
|
+
import argparse
|
|
108
|
+
|
|
109
|
+
parser = argparse.ArgumentParser(description="Reset environment.")
|
|
110
|
+
parser.add_argument(
|
|
111
|
+
"--keep-data",
|
|
112
|
+
action="store_true",
|
|
113
|
+
help="Reset only Tenderly, keeping config.yaml and wallet.json intact."
|
|
114
|
+
)
|
|
115
|
+
args = parser.parse_args()
|
|
116
|
+
|
|
100
117
|
profile = Config().core.tenderly_profile
|
|
101
118
|
print(f"Detected Tenderly profile: {profile}")
|
|
102
119
|
|
|
103
120
|
_reset_tenderly(profile)
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
|
|
122
|
+
if args.keep_data:
|
|
123
|
+
print("Skipping Olas services and wallet cleanup (--keep-data used).")
|
|
124
|
+
else:
|
|
125
|
+
_clean_olas_services()
|
|
126
|
+
_clean_wallet_accounts()
|
|
106
127
|
|
|
107
128
|
print("Environment reset complete.")
|
|
108
129
|
|
iwa/web/routers/olas/services.py
CHANGED
|
@@ -41,7 +41,8 @@ def _determine_bond_amount(req: CreateServiceRequest) -> int:
|
|
|
41
41
|
|
|
42
42
|
if req.token_address and req.staking_contract:
|
|
43
43
|
# If a contract is specified, we MUST use its requirements
|
|
44
|
-
|
|
44
|
+
staking_name = wallet.account_service.get_tag_by_address(req.staking_contract) or req.staking_contract
|
|
45
|
+
logger.info(f"Fetching requirements from {staking_name}...")
|
|
45
46
|
staking_contract = StakingContract(req.staking_contract, req.chain)
|
|
46
47
|
reqs = staking_contract.get_requirements()
|
|
47
48
|
bond_amount = reqs["required_agent_bond"]
|
|
@@ -195,7 +196,8 @@ def deploy_service(
|
|
|
195
196
|
if staking_contract:
|
|
196
197
|
try:
|
|
197
198
|
staking_obj = StakingContract(staking_contract, service.chain_name)
|
|
198
|
-
|
|
199
|
+
staking_name = wallet.account_service.get_tag_by_address(staking_contract) or staking_contract
|
|
200
|
+
logger.info(f"Will stake in {staking_name} after deployment")
|
|
199
201
|
except Exception as e:
|
|
200
202
|
logger.warning(f"Could not set up staking contract: {e}")
|
|
201
203
|
|
|
@@ -228,6 +230,74 @@ def deploy_service(
|
|
|
228
230
|
raise HTTPException(status_code=400, detail=str(e)) from None
|
|
229
231
|
|
|
230
232
|
|
|
233
|
+
|
|
234
|
+
def _resolve_service_accounts(service) -> dict:
|
|
235
|
+
"""Resolve basic accounts info including owner_signer if applicable."""
|
|
236
|
+
accounts = {}
|
|
237
|
+
for role, addr in [
|
|
238
|
+
("agent", service.agent_address),
|
|
239
|
+
("safe", str(service.multisig_address) if service.multisig_address else None),
|
|
240
|
+
("owner", service.service_owner_address),
|
|
241
|
+
]:
|
|
242
|
+
if addr:
|
|
243
|
+
stored = wallet.key_storage.find_stored_account(addr)
|
|
244
|
+
# If this role is 'owner' and it's a Safe, add owner_signer role
|
|
245
|
+
if role == "owner" and stored and hasattr(stored, "signers") and stored.signers:
|
|
246
|
+
signer_addr = stored.signers[0]
|
|
247
|
+
signer_stored = wallet.key_storage.find_stored_account(signer_addr)
|
|
248
|
+
accounts["owner_signer"] = {
|
|
249
|
+
"address": signer_addr,
|
|
250
|
+
"tag": signer_stored.tag if signer_stored else None,
|
|
251
|
+
"native": None,
|
|
252
|
+
"olas": None,
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
accounts[role] = {
|
|
256
|
+
"address": addr,
|
|
257
|
+
"tag": stored.tag if stored else None,
|
|
258
|
+
"native": None,
|
|
259
|
+
"olas": None,
|
|
260
|
+
}
|
|
261
|
+
return accounts
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _resolve_service_balances(service, chain: str) -> dict:
|
|
265
|
+
"""Resolve detailed balances including owner_signer."""
|
|
266
|
+
balances = {}
|
|
267
|
+
for role, addr in [
|
|
268
|
+
("agent", service.agent_address),
|
|
269
|
+
("safe", str(service.multisig_address) if service.multisig_address else None),
|
|
270
|
+
("owner", service.service_owner_address),
|
|
271
|
+
]:
|
|
272
|
+
if addr:
|
|
273
|
+
native_bal = wallet.get_native_balance_eth(addr, chain)
|
|
274
|
+
olas_bal = wallet.balance_service.get_erc20_balance_wei(addr, "OLAS", chain)
|
|
275
|
+
olas_bal_eth = float(olas_bal) / 1e18 if olas_bal else 0
|
|
276
|
+
stored = wallet.key_storage.find_stored_account(addr)
|
|
277
|
+
|
|
278
|
+
# If this role is 'owner' and it's a Safe, resolve owner_signer
|
|
279
|
+
if role == "owner" and stored and hasattr(stored, "signers") and stored.signers:
|
|
280
|
+
signer_addr = stored.signers[0]
|
|
281
|
+
s_native = wallet.get_native_balance_eth(signer_addr, chain)
|
|
282
|
+
s_olas_wei = wallet.balance_service.get_erc20_balance_wei(signer_addr, "OLAS", chain)
|
|
283
|
+
s_olas = float(s_olas_wei) / 1e18 if s_olas_wei else 0
|
|
284
|
+
s_stored = wallet.key_storage.find_stored_account(signer_addr)
|
|
285
|
+
balances["owner_signer"] = {
|
|
286
|
+
"address": signer_addr,
|
|
287
|
+
"tag": s_stored.tag if s_stored else None,
|
|
288
|
+
"native": f"{s_native:.2f}" if s_native else "0.00",
|
|
289
|
+
"olas": f"{s_olas:.2f}",
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
balances[role] = {
|
|
293
|
+
"address": addr,
|
|
294
|
+
"tag": stored.tag if stored else None,
|
|
295
|
+
"native": f"{native_bal:.2f}" if native_bal else "0.00",
|
|
296
|
+
"olas": f"{olas_bal_eth:.2f}",
|
|
297
|
+
}
|
|
298
|
+
return balances
|
|
299
|
+
|
|
300
|
+
|
|
231
301
|
@router.get(
|
|
232
302
|
"/services/basic",
|
|
233
303
|
summary="Get Basic Services",
|
|
@@ -262,20 +332,7 @@ def get_olas_services_basic(chain: str = "gnosis", auth: bool = Depends(verify_a
|
|
|
262
332
|
logger.warning(f"Could not get state for {service_key}: {e}")
|
|
263
333
|
|
|
264
334
|
# Get tags from wallet storage (fast, local lookup)
|
|
265
|
-
accounts =
|
|
266
|
-
for role, addr in [
|
|
267
|
-
("agent", service.agent_address),
|
|
268
|
-
("safe", str(service.multisig_address) if service.multisig_address else None),
|
|
269
|
-
("owner", service.service_owner_address),
|
|
270
|
-
]:
|
|
271
|
-
if addr:
|
|
272
|
-
stored = wallet.key_storage.find_stored_account(addr)
|
|
273
|
-
accounts[role] = {
|
|
274
|
-
"address": addr,
|
|
275
|
-
"tag": stored.tag if stored else None,
|
|
276
|
-
"native": None, # Will be filled by details endpoint
|
|
277
|
-
"olas": None,
|
|
278
|
-
}
|
|
335
|
+
accounts = _resolve_service_accounts(service)
|
|
279
336
|
|
|
280
337
|
result.append(
|
|
281
338
|
{
|
|
@@ -327,23 +384,7 @@ def get_olas_service_details(service_key: str, auth: bool = Depends(verify_auth)
|
|
|
327
384
|
service_state = manager.get_service_state()
|
|
328
385
|
|
|
329
386
|
# Get balances
|
|
330
|
-
balances =
|
|
331
|
-
for role, addr in [
|
|
332
|
-
("agent", service.agent_address),
|
|
333
|
-
("safe", str(service.multisig_address) if service.multisig_address else None),
|
|
334
|
-
("owner", service.service_owner_address),
|
|
335
|
-
]:
|
|
336
|
-
if addr:
|
|
337
|
-
native_bal = wallet.get_native_balance_eth(addr, chain)
|
|
338
|
-
olas_bal = wallet.balance_service.get_erc20_balance_wei(addr, "OLAS", chain)
|
|
339
|
-
olas_bal_eth = float(olas_bal) / 1e18 if olas_bal else 0
|
|
340
|
-
stored = wallet.key_storage.find_stored_account(addr)
|
|
341
|
-
balances[role] = {
|
|
342
|
-
"address": addr,
|
|
343
|
-
"tag": stored.tag if stored else None,
|
|
344
|
-
"native": f"{native_bal:.2f}" if native_bal else "0.00",
|
|
345
|
-
"olas": f"{olas_bal_eth:.2f}",
|
|
346
|
-
}
|
|
387
|
+
balances = _resolve_service_balances(service, chain)
|
|
347
388
|
|
|
348
389
|
staking = None
|
|
349
390
|
if staking_status:
|
iwa/web/static/app.js
CHANGED
|
@@ -1966,25 +1966,30 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
1966
1966
|
}
|
|
1967
1967
|
|
|
1968
1968
|
// Build accounts table
|
|
1969
|
-
const roles = ["agent", "safe", "owner"];
|
|
1969
|
+
const roles = ["agent", "safe", "owner", "owner_signer"];
|
|
1970
1970
|
const accountsHtml = roles
|
|
1971
1971
|
.map((role) => {
|
|
1972
1972
|
const acc = service.accounts[role];
|
|
1973
1973
|
if (!acc || !acc.address) {
|
|
1974
1974
|
if (role === "owner") return "";
|
|
1975
|
+
// For Owner Signer (EOA owner case) or other missing roles
|
|
1976
|
+
const label =
|
|
1977
|
+
role === "owner_signer"
|
|
1978
|
+
? "Owner Signer"
|
|
1979
|
+
: role.charAt(0).toUpperCase() + role.slice(1);
|
|
1980
|
+
const addrText = role === "owner_signer" ? "-" : "Not deployed";
|
|
1975
1981
|
return `
|
|
1976
1982
|
<tr>
|
|
1977
|
-
<td>${escapeHtml(
|
|
1978
|
-
<td class="address-cell text-muted"
|
|
1983
|
+
<td>${escapeHtml(label)}</td>
|
|
1984
|
+
<td class="address-cell text-muted">${escapeHtml(addrText)}</td>
|
|
1979
1985
|
<td class="val">-</td>
|
|
1980
1986
|
<td class="val">-</td>
|
|
1981
1987
|
</tr>
|
|
1982
1988
|
`;
|
|
1983
1989
|
}
|
|
1984
1990
|
|
|
1985
|
-
// Requirement:
|
|
1986
|
-
const displayText =
|
|
1987
|
-
role === "owner" && acc.tag ? acc.tag : shortenAddr(acc.address);
|
|
1991
|
+
// Requirement: Prefer TAG if available, otherwise shorten address
|
|
1992
|
+
const displayText = acc.tag ? acc.tag : shortenAddr(acc.address);
|
|
1988
1993
|
const explorerUrl = getExplorerUrl(acc.address, service.chain);
|
|
1989
1994
|
|
|
1990
1995
|
// Show spinner if loading, otherwise show balance
|
|
@@ -1997,9 +2002,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
1997
2002
|
? '<span class="cell-spinner"></span>'
|
|
1998
2003
|
: escapeHtml(formatBalance(acc.olas));
|
|
1999
2004
|
|
|
2005
|
+
const label =
|
|
2006
|
+
role === "owner_signer"
|
|
2007
|
+
? "Owner Signer"
|
|
2008
|
+
: role.charAt(0).toUpperCase() + role.slice(1);
|
|
2000
2009
|
return `
|
|
2001
2010
|
<tr>
|
|
2002
|
-
<td>${escapeHtml(
|
|
2011
|
+
<td>${escapeHtml(label)}</td>
|
|
2003
2012
|
<td class="address-cell">
|
|
2004
2013
|
<a href="${explorerUrl}" target="_blank" class="explorer-link" title="${escapeHtml(acc.address)}">
|
|
2005
2014
|
${escapeHtml(displayText)}
|
|
@@ -40,10 +40,10 @@ iwa/core/services/account.py,sha256=01MoEvl6FJlMnMB4fGwsPtnGa4kgA-d5hJeKu_ACg7Y,
|
|
|
40
40
|
iwa/core/services/balance.py,sha256=mPE12CuOFfCaJXaQXWOcQM1O03ZF3ghpy_-oOjNk_GE,4104
|
|
41
41
|
iwa/core/services/plugin.py,sha256=GNNlbtELyHl7MNVChrypF76GYphxXduxDog4kx1MLi8,3277
|
|
42
42
|
iwa/core/services/safe.py,sha256=ZmgVwbQhYlH5r3qhlY5uP8nCPtkkvV3sNnYG7_UCWUQ,14831
|
|
43
|
-
iwa/core/services/transaction.py,sha256=
|
|
43
|
+
iwa/core/services/transaction.py,sha256=Z5H5dXEXEMYT6YJWt5q04d_eciv8bKUuHQfWKZRYz1U,19106
|
|
44
44
|
iwa/core/services/transfer/__init__.py,sha256=ZJfshFxJRsp8rkOqfVvd1cqEzIJ9tqBJh8pc0l90GLk,5576
|
|
45
45
|
iwa/core/services/transfer/base.py,sha256=sohz-Ss2i-pGYGl4x9bD93cnYKcSvsXaXyvyRawvgQs,9043
|
|
46
|
-
iwa/core/services/transfer/erc20.py,sha256=
|
|
46
|
+
iwa/core/services/transfer/erc20.py,sha256=vs7OQR36C7JNScZ_UF2Fc9webQLLgAQjaMbMeqdB3do,9736
|
|
47
47
|
iwa/core/services/transfer/multisend.py,sha256=MuOTjzUQoYg1eSixXKhJGBmB1c0ymLelvk4puHm_VGE,15194
|
|
48
48
|
iwa/core/services/transfer/native.py,sha256=2CiUOP1gHEXAtLG0-8FaykV3u3jclq5y71gXQNEoc3w,10433
|
|
49
49
|
iwa/core/services/transfer/swap.py,sha256=exOJdzwkZaGbrFWfmQT_2JMcZUxnkiehXca8TH-vlF0,12269
|
|
@@ -62,7 +62,7 @@ iwa/plugins/gnosis/tests/test_safe.py,sha256=hQHVHBWQhGnuvzvx4U9fOWEwASJWwql42q6
|
|
|
62
62
|
iwa/plugins/olas/__init__.py,sha256=_NhBczzM61fhGYwGhnWfEeL8Jywyy_730GASe2BxzeQ,106
|
|
63
63
|
iwa/plugins/olas/constants.py,sha256=iTFoO2QW3KbhL5k5sKsJxxyDytl9wVIb_9hAih55KrE,7728
|
|
64
64
|
iwa/plugins/olas/events.py,sha256=SWD3wYdQ-l6dLUJSkfh_WsLmedH4Vsw_EvYXg7QC3yc,5970
|
|
65
|
-
iwa/plugins/olas/importer.py,sha256=
|
|
65
|
+
iwa/plugins/olas/importer.py,sha256=ZnMi5noNUZMJzQ-aEmtPZz-eOK3cvboIJPL625F3mHQ,42042
|
|
66
66
|
iwa/plugins/olas/mech_reference.py,sha256=CaSCpQnQL4F7wOG6Ox6Zdoy-uNEQ78YBwVLILQZKL8Q,5782
|
|
67
67
|
iwa/plugins/olas/models.py,sha256=36N2Wuia7DVfpekJJ2pXPeS0lqhe5ED-Hjdox9YQG2c,5230
|
|
68
68
|
iwa/plugins/olas/plugin.py,sha256=zOPWyoVkSVh6guJ3TZj5enJFuiIbP3fRM8FkziPB-c0,15606
|
|
@@ -88,9 +88,9 @@ iwa/plugins/olas/scripts/test_simple_lifecycle.py,sha256=8T50tOZx3afeECSfCNAb0rA
|
|
|
88
88
|
iwa/plugins/olas/service_manager/__init__.py,sha256=GXiThMEY3nPgHUl1i-DLrF4h96z9jPxxI8Jepo2E1PM,1926
|
|
89
89
|
iwa/plugins/olas/service_manager/base.py,sha256=EBPg0ymqgtAb7ZvVSfTt31QYgv_6gp4UAc6je00NLAg,5009
|
|
90
90
|
iwa/plugins/olas/service_manager/drain.py,sha256=1Ku7axThwLtKxaNTkwhP4j1yjIXbFXAqNFDrCSmgfto,12569
|
|
91
|
-
iwa/plugins/olas/service_manager/lifecycle.py,sha256
|
|
91
|
+
iwa/plugins/olas/service_manager/lifecycle.py,sha256=wvg49bv9_zFH1aR26TqM4VYzH8Ff7J8MJt98-zvkV_Q,49880
|
|
92
92
|
iwa/plugins/olas/service_manager/mech.py,sha256=NVzVbEmyOe3wK92VEzCCOSuy3HDkEP1MSoVt7Av8Psk,27949
|
|
93
|
-
iwa/plugins/olas/service_manager/staking.py,sha256=
|
|
93
|
+
iwa/plugins/olas/service_manager/staking.py,sha256=U_Akma_8PDeymHDEH5lNc64AQZ2xN8uljYo0ER7-wYQ,29554
|
|
94
94
|
iwa/plugins/olas/tests/conftest.py,sha256=4vM7EI00SrTGyeP0hNzsGSQHEj2-iznVgzlNh2_OGfo,739
|
|
95
95
|
iwa/plugins/olas/tests/test_importer.py,sha256=i9LKov7kNRECB3hmRnhKBwcfx3uxtjWe4BB77bOOpeo,4282
|
|
96
96
|
iwa/plugins/olas/tests/test_importer_error_handling.py,sha256=yHpdxfN20gnZ1cHPJqk9xHC_26jyVN8kiziY-sl4FuA,11696
|
|
@@ -104,7 +104,7 @@ iwa/plugins/olas/tests/test_olas_view_modals.py,sha256=8j0PNFjKqFC5V1kBdVFWNLMvq
|
|
|
104
104
|
iwa/plugins/olas/tests/test_plugin.py,sha256=RVgU-Cq6t_3mOh90xFAGwlJOV7ZIgp0VNaK5ZAxisAQ,2565
|
|
105
105
|
iwa/plugins/olas/tests/test_plugin_full.py,sha256=55EBa07JhJLVG3IMi6QKlR_ivWLYCdLQTySP66qbEXo,8584
|
|
106
106
|
iwa/plugins/olas/tests/test_service_lifecycle.py,sha256=sOCtpz8T9s55AZe9AoqP1h3XrXw5NDSjDqwLgYThvU4,5559
|
|
107
|
-
iwa/plugins/olas/tests/test_service_manager.py,sha256=
|
|
107
|
+
iwa/plugins/olas/tests/test_service_manager.py,sha256=g9PREmYEHu2iRx9VzQhNJbxQU000dpXg57KN3ApMCTQ,40851
|
|
108
108
|
iwa/plugins/olas/tests/test_service_manager_errors.py,sha256=udlAsQj_t1F5TwVQuWhroF6jDJ4RmGEXaxPh87tMsuA,8538
|
|
109
109
|
iwa/plugins/olas/tests/test_service_manager_flows.py,sha256=QvRFpJIapZXbagxPApr-995i64aJDapxSFyae8tNSHU,20722
|
|
110
110
|
iwa/plugins/olas/tests/test_service_manager_mech.py,sha256=qG6qu5IPRNypXUsblU2OEkuiuwDJ0TH8RXZbibmTFcQ,4937
|
|
@@ -119,7 +119,7 @@ iwa/tools/__init__.py,sha256=jQyuwDQGRigSe7S9JMb4yK3CXPgZFJNffzt6N2v9PU0,21
|
|
|
119
119
|
iwa/tools/check_profile.py,sha256=0LAv9wx4wMM610mX88-6tIoDi2I5LDzh0W9nkprt42s,2177
|
|
120
120
|
iwa/tools/list_contracts.py,sha256=2w-LYB2RVS-eGil2kLiBIEm3tYYhYzT4lAuGO6VtLJU,4861
|
|
121
121
|
iwa/tools/release.py,sha256=-Z9GG6Y-K6KG32K0VUf_MruiUdJxG6W7ToOMzhyCH7Y,3963
|
|
122
|
-
iwa/tools/reset_env.py,sha256=
|
|
122
|
+
iwa/tools/reset_env.py,sha256=lHWMUatl4n6KNCluX-egA9uSt0xSH9m48gt4Lz8GW28,3831
|
|
123
123
|
iwa/tools/reset_tenderly.py,sha256=usKfOLrQvdCzEncueg-Sz3spqX80vHPQmbh2tIygo8o,11295
|
|
124
124
|
iwa/tools/restore_backup.py,sha256=_LJbmKv9SlekLUQFdjI3aHCvAc6uePobJe3bQEFyatk,2455
|
|
125
125
|
iwa/tools/test_chainlist.py,sha256=9J06sTsKgnEcN7WSn-YgJkCHhfbGDdVS-KNMDBhYllA,1143
|
|
@@ -149,16 +149,16 @@ iwa/web/routers/olas/__init__.py,sha256=Jo6Dm1e8fHafCD800fbxsxeFz3tvuEEKXEf5-9tj
|
|
|
149
149
|
iwa/web/routers/olas/admin.py,sha256=PMRdNelqYgQ1xbqh3floFV5xrVtBRQiwZPd8J9_ffxg,5785
|
|
150
150
|
iwa/web/routers/olas/funding.py,sha256=f8fADNtbZEBFl-vuVKfas6os38Vot6K5tJBTenZmCD0,4832
|
|
151
151
|
iwa/web/routers/olas/general.py,sha256=dPsBQppTGoQY1RztliUhseOHOZGeeCR10lhThD9kyXo,803
|
|
152
|
-
iwa/web/routers/olas/services.py,sha256=
|
|
152
|
+
iwa/web/routers/olas/services.py,sha256=mDQh_Hp118Ek8_WIfhmZA_7HC2slw_aKRUjY6PPx91A,18099
|
|
153
153
|
iwa/web/routers/olas/staking.py,sha256=jktJ2C1Q9X4aC0tWJByN3sHpEXY0EIvr3rr4N0MtXXc,14081
|
|
154
|
-
iwa/web/static/app.js,sha256=
|
|
154
|
+
iwa/web/static/app.js,sha256=hZns7VpUZDRnHaIkOLinit65yGItYAxRj6j20CBSm-o,113839
|
|
155
155
|
iwa/web/static/index.html,sha256=q7s7plnMbN1Nkzr5bRxZgvgOFerUChEGIZW7SpAVtPc,28514
|
|
156
156
|
iwa/web/static/style.css,sha256=aTtE42mmfYV6y7xfo9cUgUhT8x-KyNC1zmPjSdskxIk,24315
|
|
157
157
|
iwa/web/tests/test_web_endpoints.py,sha256=C264MH-CTyDW4GLUrTXBgLJKUk4-89pFAScBddd4Fvk,23995
|
|
158
158
|
iwa/web/tests/test_web_olas.py,sha256=0CVSsrncOeJ3x0ECV7mVLQV_CXZRrOqGiVjgLIi6hZ8,16308
|
|
159
159
|
iwa/web/tests/test_web_swap.py,sha256=7A4gBJFL01kIXPtW1E1J17SCsVc_0DmUn-R8kKrnnVA,2974
|
|
160
160
|
iwa/web/tests/test_web_swap_coverage.py,sha256=zGNrzlhZ_vWDCvWmLcoUwFgqxnrp_ACbo49AtWBS_Kw,5584
|
|
161
|
-
iwa-0.0.
|
|
161
|
+
iwa-0.0.32.dist-info/licenses/LICENSE,sha256=eIubm_IlBHPYRQlLNZKbBNKhJUUP3JH0A2miZUhAVfI,1078
|
|
162
162
|
tests/legacy_cow.py,sha256=oOkZvIxL70ReEoD9oHQbOD5GpjIr6AGNHcOCgfPlerU,8389
|
|
163
163
|
tests/legacy_safe.py,sha256=AssM2g13E74dNGODu_H0Q0y412lgqsrYnEzI97nm_Ts,2972
|
|
164
164
|
tests/legacy_transaction_retry_logic.py,sha256=D9RqZ7DBu61Xr2djBAodU2p9UE939LL-DnQXswX5iQk,1497
|
|
@@ -211,8 +211,8 @@ tests/test_utils.py,sha256=vkP49rYNI8BRzLpWR3WnKdDr8upeZjZcs7Rx0pjbQMo,1292
|
|
|
211
211
|
tests/test_workers.py,sha256=MInwdkFY5LdmFB3o1odIaSD7AQZb3263hNafO1De5PE,2793
|
|
212
212
|
tools/create_and_stake_service.py,sha256=1xwy_bJQI1j9yIQ968Oc9Db_F6mk1659LuuZntTASDE,3742
|
|
213
213
|
tools/verify_drain.py,sha256=PkMjblyOOAuQge88FwfEzRtCYeEtJxXhPBmtQYCoQ-8,6743
|
|
214
|
-
iwa-0.0.
|
|
215
|
-
iwa-0.0.
|
|
216
|
-
iwa-0.0.
|
|
217
|
-
iwa-0.0.
|
|
218
|
-
iwa-0.0.
|
|
214
|
+
iwa-0.0.32.dist-info/METADATA,sha256=FBpZWSn3VFMR2HDEVPHCwFFl0AAsEkTK9ZRuPeUbA-c,7295
|
|
215
|
+
iwa-0.0.32.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
216
|
+
iwa-0.0.32.dist-info/entry_points.txt,sha256=nwB6kscrfA7M00pYmL2j-sBH6eF6h2ga9IK1BZxdiyQ,241
|
|
217
|
+
iwa-0.0.32.dist-info/top_level.txt,sha256=kedS9cRUbm4JE2wYeabIXilhHjN8KCw0IGbqqqsw0Bs,16
|
|
218
|
+
iwa-0.0.32.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|