iwa 0.0.10__py3-none-any.whl → 0.0.12__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/chain/interface.py +21 -7
- iwa/core/cli.py +8 -0
- iwa/core/services/transaction.py +196 -3
- iwa/core/utils.py +38 -0
- iwa/core/wallet.py +2 -1
- iwa/plugins/olas/contracts/abis/service_registry_token_utility.json +926 -0
- iwa/plugins/olas/contracts/service.py +50 -0
- iwa/plugins/olas/service_manager/lifecycle.py +275 -39
- iwa/plugins/olas/service_manager/staking.py +152 -63
- iwa/plugins/olas/tests/test_olas_contracts.py +6 -2
- iwa/plugins/olas/tests/test_service_lifecycle.py +1 -4
- iwa/plugins/olas/tests/test_service_manager.py +59 -89
- iwa/plugins/olas/tests/test_service_manager_errors.py +1 -2
- iwa/plugins/olas/tests/test_service_manager_flows.py +5 -15
- iwa/web/routers/olas/services.py +52 -19
- iwa/web/routers/olas/staking.py +8 -2
- {iwa-0.0.10.dist-info → iwa-0.0.12.dist-info}/METADATA +1 -1
- {iwa-0.0.10.dist-info → iwa-0.0.12.dist-info}/RECORD +23 -22
- tests/test_chain.py +12 -7
- {iwa-0.0.10.dist-info → iwa-0.0.12.dist-info}/WHEEL +0 -0
- {iwa-0.0.10.dist-info → iwa-0.0.12.dist-info}/entry_points.txt +0 -0
- {iwa-0.0.10.dist-info → iwa-0.0.12.dist-info}/licenses/LICENSE +0 -0
- {iwa-0.0.10.dist-info → iwa-0.0.12.dist-info}/top_level.txt +0 -0
|
@@ -213,3 +213,53 @@ class ServiceManagerContract(ContractInstance):
|
|
|
213
213
|
tx_params={"from": from_address},
|
|
214
214
|
)
|
|
215
215
|
return tx
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class ServiceRegistryTokenUtilityContract(ContractInstance):
|
|
219
|
+
"""Class to interact with the service registry token utility contract.
|
|
220
|
+
|
|
221
|
+
This contract manages token-bonded services, tracking agent bonds and
|
|
222
|
+
security deposits for services that use ERC20 tokens (like OLAS) instead
|
|
223
|
+
of native currency.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
name = "service_registry_token_utility"
|
|
227
|
+
abi_path = OLAS_ABI_PATH / "service_registry_token_utility.json"
|
|
228
|
+
|
|
229
|
+
def get_agent_bond(self, service_id: int, agent_id: int) -> int:
|
|
230
|
+
"""Get the agent bond for a specific agent in a service.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
service_id: The service ID.
|
|
234
|
+
agent_id: The agent ID within the service.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
The bond amount in wei.
|
|
238
|
+
|
|
239
|
+
"""
|
|
240
|
+
return self.call("getAgentBond", service_id, agent_id)
|
|
241
|
+
|
|
242
|
+
def get_operator_balance(self, operator: str, service_id: int) -> int:
|
|
243
|
+
"""Get the operator balance for a service.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
operator: The operator address.
|
|
247
|
+
service_id: The service ID.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
The balance amount in wei.
|
|
251
|
+
|
|
252
|
+
"""
|
|
253
|
+
return self.call("getOperatorBalance", operator, service_id)
|
|
254
|
+
|
|
255
|
+
def get_service_token_deposit(self, service_id: int) -> tuple:
|
|
256
|
+
"""Get the token deposit info for a service.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
service_id: The service ID.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Tuple of (token_address, security_deposit).
|
|
263
|
+
|
|
264
|
+
"""
|
|
265
|
+
return self.call("mapServiceIdTokenDeposit", service_id)
|
|
@@ -1,4 +1,61 @@
|
|
|
1
|
-
"""Lifecycle manager mixin.
|
|
1
|
+
"""Lifecycle manager mixin for OLAS service lifecycle operations.
|
|
2
|
+
|
|
3
|
+
OLAS Service Lifecycle & Token Flow
|
|
4
|
+
====================================
|
|
5
|
+
|
|
6
|
+
This module handles the first 4 steps of the service lifecycle (staking is in staking.py).
|
|
7
|
+
Each step involves specific token movements as detailed below.
|
|
8
|
+
|
|
9
|
+
STEP 1: CREATE SERVICE
|
|
10
|
+
- What happens:
|
|
11
|
+
* Service is registered on-chain with metadata (config hash, agent IDs)
|
|
12
|
+
* Service ownership NFT (ERC-721) is minted to service owner
|
|
13
|
+
* Bond parameters are recorded but NO tokens move yet
|
|
14
|
+
- Token Movement: None
|
|
15
|
+
- Approval: Service Owner → Token Utility (for 2 × bond_amount OLAS)
|
|
16
|
+
- Next State: PRE_REGISTRATION
|
|
17
|
+
|
|
18
|
+
STEP 2: ACTIVATE REGISTRATION
|
|
19
|
+
- What happens:
|
|
20
|
+
* Service Owner signals readiness to accept agent registrations
|
|
21
|
+
* Token Utility pulls min_staking_deposit OLAS from owner via transferFrom()
|
|
22
|
+
- Token Movement:
|
|
23
|
+
* 5,000 OLAS: Service Owner → Token Utility (for 10k contract)
|
|
24
|
+
- Native value sent: 1 wei (not 5k OLAS!)
|
|
25
|
+
* This is MIN_AGENT_BOND, a placeholder for native-bonded services
|
|
26
|
+
* For OLAS-bonded services, tokens move via Token Utility, not via msg.value
|
|
27
|
+
- Next State: ACTIVE_REGISTRATION
|
|
28
|
+
|
|
29
|
+
STEP 3: REGISTER AGENT
|
|
30
|
+
- What happens:
|
|
31
|
+
* Agent instance address is registered to the service
|
|
32
|
+
* Token Utility pulls agent_bond OLAS from owner via transferFrom()
|
|
33
|
+
- Token Movement:
|
|
34
|
+
* 5,000 OLAS: Service Owner → Token Utility (for 10k contract)
|
|
35
|
+
- Native value sent: 1 wei per agent
|
|
36
|
+
* Same logic as activation - tokens move via Token Utility
|
|
37
|
+
- Next State: FINISHED_REGISTRATION
|
|
38
|
+
|
|
39
|
+
STEP 4: DEPLOY
|
|
40
|
+
- What happens:
|
|
41
|
+
* Safe multisig is created with agent instances as owners
|
|
42
|
+
* Service transitions to operational state
|
|
43
|
+
- Token Movement: None
|
|
44
|
+
- Next State: DEPLOYED
|
|
45
|
+
|
|
46
|
+
After DEPLOYED, see staking.py for STEP 5: STAKE
|
|
47
|
+
|
|
48
|
+
Token Utility Contract:
|
|
49
|
+
The Token Utility (0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8 on Gnosis) is the
|
|
50
|
+
intermediary that holds OLAS deposits. When you call activateRegistration() or
|
|
51
|
+
registerAgents() on the Service Manager, it internally calls Token Utility's
|
|
52
|
+
transferFrom() to move OLAS from the service owner.
|
|
53
|
+
|
|
54
|
+
This is why:
|
|
55
|
+
1. We approve Token Utility BEFORE activation/registration
|
|
56
|
+
2. We send 1 wei native value (not the OLAS amount) in TX
|
|
57
|
+
3. The actual OLAS moves via transferFrom(), not msg.value
|
|
58
|
+
"""
|
|
2
59
|
|
|
3
60
|
from typing import List, Optional, Union
|
|
4
61
|
|
|
@@ -20,7 +77,26 @@ from iwa.plugins.olas.models import Service
|
|
|
20
77
|
|
|
21
78
|
|
|
22
79
|
class LifecycleManagerMixin:
|
|
23
|
-
"""Mixin for service lifecycle operations.
|
|
80
|
+
"""Mixin for OLAS service lifecycle operations.
|
|
81
|
+
|
|
82
|
+
Handles the CREATE → ACTIVATE → REGISTER → DEPLOY flow for OLAS services.
|
|
83
|
+
Each method transitions the service to the next state.
|
|
84
|
+
|
|
85
|
+
Token Movement Summary:
|
|
86
|
+
┌───────────────┬────────────────────────────────────────────────────┐
|
|
87
|
+
│ Step │ OLAS Movement │
|
|
88
|
+
├───────────────┼────────────────────────────────────────────────────┤
|
|
89
|
+
│ create() │ None (just approval to Token Utility) │
|
|
90
|
+
│ activate() │ 5k OLAS → Token Utility (via transferFrom) │
|
|
91
|
+
│ register() │ 5k OLAS → Token Utility (via transferFrom) │
|
|
92
|
+
│ deploy() │ None (just creates Safe multisig) │
|
|
93
|
+
└───────────────┴────────────────────────────────────────────────────┘
|
|
94
|
+
|
|
95
|
+
Usage:
|
|
96
|
+
manager = ServiceManager(wallet)
|
|
97
|
+
service_id = manager.create(chain_name="gnosis", token_address_or_tag="OLAS")
|
|
98
|
+
manager.spin_up(bond_amount_wei=5000e18, staking_contract=staking)
|
|
99
|
+
"""
|
|
24
100
|
|
|
25
101
|
def create(
|
|
26
102
|
self,
|
|
@@ -192,7 +268,22 @@ class LifecycleManagerMixin:
|
|
|
192
268
|
service_owner_account,
|
|
193
269
|
bond_amount_wei: Wei,
|
|
194
270
|
) -> None:
|
|
195
|
-
"""Approve
|
|
271
|
+
"""Approve Token Utility to spend OLAS tokens (called during create).
|
|
272
|
+
|
|
273
|
+
Why 2× bond amount?
|
|
274
|
+
- Activation requires min_staking_deposit (= bond_amount)
|
|
275
|
+
- Registration requires agent_bond (= bond_amount)
|
|
276
|
+
- Total = 2 × bond_amount
|
|
277
|
+
|
|
278
|
+
Token Movement: None (this is just an approval, not a transfer)
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
token_address: OLAS token address (or None for native).
|
|
282
|
+
chain_name: Chain to operate on.
|
|
283
|
+
service_owner_account: Account that owns the OLAS tokens.
|
|
284
|
+
bond_amount_wei: Bond amount per agent in wei.
|
|
285
|
+
|
|
286
|
+
"""
|
|
196
287
|
if not token_address:
|
|
197
288
|
return
|
|
198
289
|
|
|
@@ -204,7 +295,7 @@ class LifecycleManagerMixin:
|
|
|
204
295
|
logger.error(f"OLAS Service Registry Token Utility not found for chain: {chain_name}")
|
|
205
296
|
return
|
|
206
297
|
|
|
207
|
-
# Approve the token utility to move tokens (2 * bond amount
|
|
298
|
+
# Approve the token utility to move tokens (2 * bond amount: activation + registration)
|
|
208
299
|
logger.info(f"Approving Token Utility {utility_address} for {2 * bond_amount_wei} tokens")
|
|
209
300
|
approve_success = self.transfer_service.approve_erc20(
|
|
210
301
|
owner_address_or_tag=service_owner_account.address,
|
|
@@ -218,7 +309,28 @@ class LifecycleManagerMixin:
|
|
|
218
309
|
logger.error("Failed to approve Token Utility")
|
|
219
310
|
|
|
220
311
|
def activate_registration(self) -> bool:
|
|
221
|
-
"""Activate registration for the service.
|
|
312
|
+
"""Activate registration for the service (Step 2 of lifecycle).
|
|
313
|
+
|
|
314
|
+
What This Does:
|
|
315
|
+
Transitions service from PRE_REGISTRATION → ACTIVE_REGISTRATION.
|
|
316
|
+
Signals that the service owner is ready to accept agent registrations.
|
|
317
|
+
|
|
318
|
+
Token Movement:
|
|
319
|
+
5,000 OLAS (for 10k contract): Service Owner → Token Utility
|
|
320
|
+
- Moved internally by Token Utility via transferFrom()
|
|
321
|
+
- NOT sent as msg.value (that's just 1 wei)
|
|
322
|
+
|
|
323
|
+
Native Value Sent:
|
|
324
|
+
1 wei (MIN_AGENT_BOND placeholder, not the actual deposit)
|
|
325
|
+
|
|
326
|
+
Prerequisites:
|
|
327
|
+
- Service must be in PRE_REGISTRATION state
|
|
328
|
+
- Token Utility must be approved to spend owner's OLAS
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
True if activation succeeded, False otherwise.
|
|
332
|
+
|
|
333
|
+
"""
|
|
222
334
|
service_id = self.service.service_id
|
|
223
335
|
logger.info(f"[ACTIVATE] Starting activation for service {service_id}")
|
|
224
336
|
|
|
@@ -266,29 +378,50 @@ class LifecycleManagerMixin:
|
|
|
266
378
|
def _ensure_token_approval_for_activation(
|
|
267
379
|
self, token_address: str, security_deposit: Wei
|
|
268
380
|
) -> bool:
|
|
269
|
-
"""Ensure token approval for activation if not native token.
|
|
381
|
+
"""Ensure token approval for activation if not native token.
|
|
382
|
+
|
|
383
|
+
For token-bonded services (e.g., OLAS), we need to approve the
|
|
384
|
+
ServiceRegistryTokenUtility contract to spend the security deposit
|
|
385
|
+
(agent bond) on our behalf.
|
|
386
|
+
|
|
387
|
+
IMPORTANT: We query the exact bond amount from the Token Utility contract
|
|
388
|
+
rather than approving a fixed amount, to match the official OLAS middleware.
|
|
389
|
+
"""
|
|
270
390
|
is_native = str(token_address).lower() == str(ZERO_ADDRESS).lower()
|
|
271
391
|
if is_native:
|
|
272
392
|
return True
|
|
273
393
|
|
|
274
394
|
try:
|
|
275
|
-
#
|
|
395
|
+
# Get the exact agent bond from Token Utility contract
|
|
396
|
+
bond_amount = self._get_agent_bond_from_token_utility()
|
|
397
|
+
if bond_amount is None or bond_amount == 0:
|
|
398
|
+
logger.warning(
|
|
399
|
+
"[ACTIVATE] Could not get agent bond from Token Utility, using security_deposit"
|
|
400
|
+
)
|
|
401
|
+
bond_amount = security_deposit
|
|
402
|
+
|
|
403
|
+
logger.info(f"[ACTIVATE] Agent bond from Token Utility: {bond_amount} wei")
|
|
404
|
+
|
|
405
|
+
# Check owner balance
|
|
276
406
|
balance = self.wallet.balance_service.get_erc20_balance_wei(
|
|
277
407
|
account_address_or_tag=self.service.service_owner_address,
|
|
278
408
|
token_address_or_name=token_address,
|
|
279
409
|
chain_name=self.chain_name,
|
|
280
410
|
)
|
|
281
411
|
|
|
282
|
-
if balance <
|
|
412
|
+
if balance < bond_amount:
|
|
283
413
|
logger.error(
|
|
284
|
-
f"[ACTIVATE] FAIL: Owner balance {balance} < required {
|
|
414
|
+
f"[ACTIVATE] FAIL: Owner balance {balance} < required {bond_amount}"
|
|
285
415
|
)
|
|
416
|
+
return False
|
|
286
417
|
|
|
287
418
|
protocol_contracts = OLAS_CONTRACTS.get(self.chain_name.lower(), {})
|
|
288
419
|
utility_address = protocol_contracts.get("OLAS_SERVICE_REGISTRY_TOKEN_UTILITY")
|
|
289
420
|
|
|
290
421
|
if utility_address:
|
|
291
|
-
|
|
422
|
+
# Approve exactly the bond amount (not 1000 OLAS fixed!)
|
|
423
|
+
# This matches the official OLAS middleware behavior
|
|
424
|
+
required_approval = bond_amount
|
|
292
425
|
|
|
293
426
|
# Check current allowance
|
|
294
427
|
allowance = self.wallet.transfer_service.get_erc20_allowance(
|
|
@@ -298,9 +431,10 @@ class LifecycleManagerMixin:
|
|
|
298
431
|
chain_name=self.chain_name,
|
|
299
432
|
)
|
|
300
433
|
|
|
301
|
-
if allowance <
|
|
434
|
+
if allowance < required_approval:
|
|
302
435
|
logger.info(
|
|
303
|
-
f"
|
|
436
|
+
f"[ACTIVATE] Allowance ({allowance}) < required ({required_approval}). "
|
|
437
|
+
f"Approving Token Utility {utility_address}"
|
|
304
438
|
)
|
|
305
439
|
success_approve = self.wallet.transfer_service.approve_erc20(
|
|
306
440
|
owner_address_or_tag=self.service.service_owner_address,
|
|
@@ -310,27 +444,87 @@ class LifecycleManagerMixin:
|
|
|
310
444
|
chain_name=self.chain_name,
|
|
311
445
|
)
|
|
312
446
|
if not success_approve:
|
|
313
|
-
logger.
|
|
447
|
+
logger.error("[ACTIVATE] Token approval failed")
|
|
314
448
|
return False
|
|
449
|
+
logger.info(f"[ACTIVATE] Approved {required_approval} wei to Token Utility")
|
|
450
|
+
else:
|
|
451
|
+
logger.debug(
|
|
452
|
+
f"[ACTIVATE] Sufficient allowance ({allowance} >= {required_approval})"
|
|
453
|
+
)
|
|
315
454
|
return True
|
|
316
455
|
except Exception as e:
|
|
317
|
-
logger.
|
|
318
|
-
return False
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
456
|
+
logger.error(f"[ACTIVATE] Failed to check/approve tokens: {e}")
|
|
457
|
+
return False
|
|
458
|
+
|
|
459
|
+
def _get_agent_bond_from_token_utility(self) -> Optional[int]:
|
|
460
|
+
"""Get the agent bond from the ServiceRegistryTokenUtility contract.
|
|
461
|
+
|
|
462
|
+
This queries the on-chain Token Utility contract to get the exact bond
|
|
463
|
+
amount required for the service, matching the official OLAS middleware.
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
The agent bond in wei, or None if the query fails.
|
|
467
|
+
|
|
468
|
+
"""
|
|
469
|
+
from iwa.plugins.olas.contracts.service import ServiceRegistryTokenUtilityContract
|
|
470
|
+
|
|
471
|
+
try:
|
|
472
|
+
protocol_contracts = OLAS_CONTRACTS.get(self.chain_name.lower(), {})
|
|
473
|
+
utility_address = protocol_contracts.get("OLAS_SERVICE_REGISTRY_TOKEN_UTILITY")
|
|
474
|
+
|
|
475
|
+
if not utility_address:
|
|
476
|
+
logger.warning("[ACTIVATE] Token Utility address not found for chain")
|
|
477
|
+
return None
|
|
478
|
+
|
|
479
|
+
# Get agent ID (first agent in the service)
|
|
480
|
+
service_info = self.registry.get_service(self.service.service_id)
|
|
481
|
+
agent_ids = service_info.get("agent_ids", [])
|
|
482
|
+
if not agent_ids:
|
|
483
|
+
logger.warning("[ACTIVATE] No agent IDs found for service")
|
|
484
|
+
return None
|
|
485
|
+
agent_id = agent_ids[0]
|
|
486
|
+
|
|
487
|
+
# Use the ServiceRegistryTokenUtilityContract with official ABI
|
|
488
|
+
token_utility = ServiceRegistryTokenUtilityContract(
|
|
489
|
+
address=str(utility_address),
|
|
490
|
+
chain_name=self.chain_name,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
bond = token_utility.get_agent_bond(self.service.service_id, agent_id)
|
|
494
|
+
|
|
495
|
+
logger.debug(
|
|
496
|
+
f"[ACTIVATE] Token Utility getAgentBond({self.service.service_id}, {agent_id}) = {bond}"
|
|
497
|
+
)
|
|
498
|
+
return bond
|
|
499
|
+
|
|
500
|
+
except Exception as e:
|
|
501
|
+
logger.warning(f"[ACTIVATE] Failed to get agent bond from Token Utility: {e}")
|
|
502
|
+
return None
|
|
326
503
|
|
|
327
504
|
def _send_activation_transaction(self, service_id: int, security_deposit: Wei) -> bool:
|
|
328
|
-
"""Send the activation transaction.
|
|
329
|
-
|
|
505
|
+
"""Send the activation transaction.
|
|
506
|
+
|
|
507
|
+
For token-bonded services (e.g., OLAS), we pass MIN_AGENT_BOND (1 wei) as native value.
|
|
508
|
+
The Token Utility handles OLAS transfers internally based on the service configuration.
|
|
509
|
+
For native currency services, we pass the full security_deposit.
|
|
510
|
+
"""
|
|
511
|
+
# Determine if this is a token-bonded service
|
|
512
|
+
token_address = self._get_service_token(service_id)
|
|
513
|
+
is_native = str(token_address).lower() == str(ZERO_ADDRESS).lower()
|
|
514
|
+
|
|
515
|
+
# For token services, use MIN_AGENT_BOND (1 wei) as native value
|
|
516
|
+
# The OLAS token approval was done in _ensure_token_approval_for_activation
|
|
517
|
+
activation_value = security_deposit if is_native else 1
|
|
518
|
+
logger.info(
|
|
519
|
+
f"[ACTIVATE] Token={token_address}, is_native={is_native}, "
|
|
520
|
+
f"activation_value={activation_value} wei"
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
logger.debug(f"[ACTIVATE] Preparing tx: service_id={service_id}, value={activation_value}")
|
|
330
524
|
activate_tx = self.manager.prepare_activate_registration_tx(
|
|
331
525
|
from_address=self.wallet.master_account.address,
|
|
332
526
|
service_id=service_id,
|
|
333
|
-
value=
|
|
527
|
+
value=activation_value,
|
|
334
528
|
)
|
|
335
529
|
logger.debug(f"[ACTIVATE] TX prepared: to={activate_tx.get('to')}")
|
|
336
530
|
|
|
@@ -441,7 +635,12 @@ class LifecycleManagerMixin:
|
|
|
441
635
|
def _ensure_agent_token_approval(
|
|
442
636
|
self, agent_account_address: str, bond_amount_wei: Optional[Wei]
|
|
443
637
|
) -> bool:
|
|
444
|
-
"""Ensure token approval for agent registration if needed.
|
|
638
|
+
"""Ensure token approval for agent registration if needed.
|
|
639
|
+
|
|
640
|
+
For token-bonded services, the service owner must approve the Token Utility
|
|
641
|
+
contract to transfer the agent bond. We query the exact bond from the
|
|
642
|
+
Token Utility contract to match the official OLAS middleware.
|
|
643
|
+
"""
|
|
445
644
|
service_id = self.service.service_id
|
|
446
645
|
token_address = self._get_service_token(service_id)
|
|
447
646
|
is_native = str(token_address) == str(ZERO_ADDRESS)
|
|
@@ -449,38 +648,75 @@ class LifecycleManagerMixin:
|
|
|
449
648
|
if is_native:
|
|
450
649
|
return True
|
|
451
650
|
|
|
651
|
+
# Get exact bond from Token Utility if not explicitly provided
|
|
452
652
|
if not bond_amount_wei:
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
653
|
+
bond_amount_wei = self._get_agent_bond_from_token_utility()
|
|
654
|
+
if not bond_amount_wei:
|
|
655
|
+
logger.warning(
|
|
656
|
+
"[REGISTER] Could not get bond from Token Utility, skipping approval"
|
|
657
|
+
)
|
|
658
|
+
return True
|
|
457
659
|
|
|
458
|
-
|
|
459
|
-
# The service owner (operator) pays the bond, not the agent.
|
|
460
|
-
logger.info(f"Service Owner approving Token Utility for bond: {bond_amount_wei} wei")
|
|
660
|
+
logger.info(f"[REGISTER] Service Owner approving Token Utility for bond: {bond_amount_wei} wei")
|
|
461
661
|
|
|
462
662
|
utility_address = str(
|
|
463
663
|
OLAS_CONTRACTS[self.chain_name]["OLAS_SERVICE_REGISTRY_TOKEN_UTILITY"]
|
|
464
664
|
)
|
|
465
665
|
|
|
666
|
+
# Check current allowance first
|
|
667
|
+
allowance = self.wallet.transfer_service.get_erc20_allowance(
|
|
668
|
+
owner_address_or_tag=self.service.service_owner_address,
|
|
669
|
+
spender_address=utility_address,
|
|
670
|
+
token_address_or_name=token_address,
|
|
671
|
+
chain_name=self.chain_name,
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
if allowance >= bond_amount_wei:
|
|
675
|
+
logger.debug(
|
|
676
|
+
f"[REGISTER] Sufficient allowance ({allowance} >= {bond_amount_wei})"
|
|
677
|
+
)
|
|
678
|
+
return True
|
|
679
|
+
|
|
680
|
+
# Use service owner which holds the OLAS tokens (not necessarily master)
|
|
466
681
|
approve_success = self.wallet.transfer_service.approve_erc20(
|
|
467
682
|
token_address_or_name=token_address,
|
|
468
683
|
spender_address_or_tag=utility_address,
|
|
469
684
|
amount_wei=bond_amount_wei,
|
|
470
|
-
owner_address_or_tag=
|
|
685
|
+
owner_address_or_tag=self.service.service_owner_address,
|
|
471
686
|
chain_name=self.chain_name,
|
|
472
687
|
)
|
|
473
688
|
if not approve_success:
|
|
474
|
-
logger.error("Failed to approve token for agent registration")
|
|
689
|
+
logger.error("[REGISTER] Failed to approve token for agent registration")
|
|
475
690
|
return False
|
|
691
|
+
|
|
692
|
+
logger.info(f"[REGISTER] Approved {bond_amount_wei} wei to Token Utility")
|
|
476
693
|
return True
|
|
477
694
|
|
|
478
695
|
def _send_register_agent_transaction(self, agent_account_address: str) -> bool:
|
|
479
|
-
"""Send the register agent transaction.
|
|
696
|
+
"""Send the register agent transaction.
|
|
697
|
+
|
|
698
|
+
For token-bonded services (e.g., OLAS), we pass MIN_AGENT_BOND (1 wei) as native value.
|
|
699
|
+
The Token Utility handles OLAS transfers internally via transferFrom.
|
|
700
|
+
For native currency services, we pass the full security_deposit * num_agents.
|
|
701
|
+
"""
|
|
480
702
|
service_id = self.service.service_id
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
703
|
+
token_address = self._get_service_token(service_id)
|
|
704
|
+
is_native = str(token_address).lower() == str(ZERO_ADDRESS).lower()
|
|
705
|
+
|
|
706
|
+
# For token services, use MIN_AGENT_BOND (1 wei) per agent
|
|
707
|
+
# For native services, use security_deposit * num_agents
|
|
708
|
+
if is_native:
|
|
709
|
+
service_info = self.registry.get_service(service_id)
|
|
710
|
+
security_deposit = service_info["security_deposit"]
|
|
711
|
+
total_value = security_deposit * len(self.service.agent_ids)
|
|
712
|
+
else:
|
|
713
|
+
# MIN_AGENT_BOND = 1 wei per agent
|
|
714
|
+
total_value = 1 * len(self.service.agent_ids)
|
|
715
|
+
|
|
716
|
+
logger.info(
|
|
717
|
+
f"[REGISTER] Token={token_address}, is_native={is_native}, "
|
|
718
|
+
f"total_value={total_value} wei"
|
|
719
|
+
)
|
|
484
720
|
|
|
485
721
|
logger.debug(
|
|
486
722
|
f"[REGISTER] Preparing tx: agent={agent_account_address}, "
|