arc-devkit 0.4.7__tar.gz → 0.8.0__tar.gz

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.
Files changed (130) hide show
  1. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/PKG-INFO +13 -7
  2. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/README.md +2 -0
  3. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/base_agent.py +9 -0
  4. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/coordinator.py +39 -0
  5. arc_devkit-0.8.0/arc_devkit/agents/identity.py +205 -0
  6. arc_devkit-0.8.0/arc_devkit/agents/job_agent.py +88 -0
  7. arc_devkit-0.8.0/arc_devkit/agents/jobs.py +321 -0
  8. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/monitor_agent.py +42 -0
  9. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/payment_agent.py +20 -1
  10. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/analytics/portfolio.py +100 -1
  11. arc_devkit-0.8.0/arc_devkit/api/auth.py +24 -0
  12. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/api/main.py +36 -16
  13. arc_devkit-0.8.0/arc_devkit/api/routes/agent_economy.py +195 -0
  14. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/api/routes/agents.py +10 -2
  15. arc_devkit-0.8.0/arc_devkit/api/routes/bridge.py +93 -0
  16. arc_devkit-0.8.0/arc_devkit/api/routes/fees.py +49 -0
  17. arc_devkit-0.8.0/arc_devkit/bridge/__init__.py +14 -0
  18. arc_devkit-0.8.0/arc_devkit/bridge/cctp.py +354 -0
  19. arc_devkit-0.8.0/arc_devkit/bridge/models.py +78 -0
  20. arc_devkit-0.8.0/arc_devkit/bridge/store.py +47 -0
  21. arc_devkit-0.8.0/arc_devkit/cli/commands/agent.py +542 -0
  22. arc_devkit-0.8.0/arc_devkit/cli/commands/bridge.py +148 -0
  23. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/cli/commands/debug.py +80 -0
  24. arc_devkit-0.8.0/arc_devkit/cli/commands/fees.py +55 -0
  25. arc_devkit-0.8.0/arc_devkit/cli/commands/mcp.py +31 -0
  26. arc_devkit-0.8.0/arc_devkit/cli/commands/network.py +130 -0
  27. arc_devkit-0.8.0/arc_devkit/cli/commands/oracle.py +43 -0
  28. arc_devkit-0.8.0/arc_devkit/cli/commands/privacy.py +56 -0
  29. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/cli/flat.py +4 -1
  30. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/cli/main.py +10 -1
  31. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/config.py +43 -3
  32. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/copilot/agent.py +31 -7
  33. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/copilot/tools.py +168 -0
  34. arc_devkit-0.8.0/arc_devkit/core/gas.py +157 -0
  35. arc_devkit-0.8.0/arc_devkit/core/signer.py +121 -0
  36. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/debugger/tx_analyzer.py +39 -0
  37. arc_devkit-0.8.0/arc_devkit/mcp_server.py +116 -0
  38. arc_devkit-0.8.0/arc_devkit/networks.py +67 -0
  39. arc_devkit-0.8.0/arc_devkit/oracle/__init__.py +5 -0
  40. arc_devkit-0.8.0/arc_devkit/oracle/price_feed.py +86 -0
  41. arc_devkit-0.8.0/arc_devkit/paymaster/__init__.py +16 -0
  42. arc_devkit-0.8.0/arc_devkit/paymaster/detector.py +42 -0
  43. arc_devkit-0.8.0/arc_devkit/paymaster/user_operation.py +92 -0
  44. arc_devkit-0.8.0/arc_devkit/privacy/__init__.py +23 -0
  45. arc_devkit-0.8.0/arc_devkit/privacy/confidential_transfer.py +86 -0
  46. arc_devkit-0.8.0/arc_devkit/privacy/view_key.py +100 -0
  47. arc_devkit-0.8.0/arc_devkit/stablecoins/__init__.py +5 -0
  48. {arc_devkit-0.4.7/arc_devkit/usdc → arc_devkit-0.8.0/arc_devkit/stablecoins}/token.py +49 -24
  49. arc_devkit-0.8.0/arc_devkit/usdc/token.py +18 -0
  50. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit.egg-info/PKG-INFO +13 -7
  51. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit.egg-info/SOURCES.txt +52 -0
  52. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit.egg-info/requires.txt +6 -0
  53. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/pyproject.toml +17 -7
  54. arc_devkit-0.8.0/tests/test_agent_identity.py +167 -0
  55. arc_devkit-0.8.0/tests/test_agent_jobs.py +196 -0
  56. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_agents.py +73 -0
  57. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_analytics.py +73 -1
  58. arc_devkit-0.8.0/tests/test_api_agent_economy.py +168 -0
  59. arc_devkit-0.8.0/tests/test_api_bridge.py +70 -0
  60. arc_devkit-0.8.0/tests/test_api_fees.py +61 -0
  61. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_async_agents.py +37 -0
  62. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_autonomous.py +35 -0
  63. arc_devkit-0.8.0/tests/test_bridge.py +448 -0
  64. arc_devkit-0.8.0/tests/test_cli_agent_economy.py +174 -0
  65. arc_devkit-0.8.0/tests/test_cli_bridge.py +106 -0
  66. arc_devkit-0.8.0/tests/test_cli_debug_advanced.py +79 -0
  67. arc_devkit-0.8.0/tests/test_cli_fees.py +31 -0
  68. arc_devkit-0.8.0/tests/test_cli_network.py +34 -0
  69. arc_devkit-0.8.0/tests/test_cli_sprint6.py +80 -0
  70. arc_devkit-0.8.0/tests/test_config.py +69 -0
  71. arc_devkit-0.8.0/tests/test_connection_regression.py +34 -0
  72. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_copilot_tools.py +97 -0
  73. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_debugger.py +45 -0
  74. arc_devkit-0.8.0/tests/test_gas.py +88 -0
  75. arc_devkit-0.8.0/tests/test_job_agent.py +100 -0
  76. arc_devkit-0.8.0/tests/test_mcp_server.py +119 -0
  77. arc_devkit-0.8.0/tests/test_oracle.py +56 -0
  78. arc_devkit-0.8.0/tests/test_paymaster.py +89 -0
  79. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_payment_security.py +24 -0
  80. arc_devkit-0.8.0/tests/test_privacy.py +118 -0
  81. arc_devkit-0.8.0/tests/test_signer.py +78 -0
  82. arc_devkit-0.8.0/tests/test_stablecoins.py +111 -0
  83. arc_devkit-0.4.7/arc_devkit/cli/commands/agent.py +0 -278
  84. arc_devkit-0.4.7/arc_devkit/core/gas.py +0 -64
  85. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/__init__.py +0 -0
  86. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/__init__.py +0 -0
  87. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/async_base.py +0 -0
  88. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/async_monitor.py +0 -0
  89. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/autonomous.py +0 -0
  90. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/dashboard.py +0 -0
  91. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/event_bus.py +0 -0
  92. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/agents/guardrails.py +0 -0
  93. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/analytics/__init__.py +0 -0
  94. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/api/__init__.py +0 -0
  95. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/api/rate_limit.py +0 -0
  96. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/api/routes/__init__.py +0 -0
  97. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/api/routes/copilot.py +0 -0
  98. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/api/routes/debugger.py +0 -0
  99. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/cli/__init__.py +0 -0
  100. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/cli/commands/__init__.py +0 -0
  101. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/cli/commands/copilot.py +0 -0
  102. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/contracts/__init__.py +0 -0
  103. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/contracts/loader.py +0 -0
  104. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/copilot/__init__.py +0 -0
  105. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/core/__init__.py +0 -0
  106. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/core/connection.py +0 -0
  107. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/core/validation.py +0 -0
  108. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/core/wallet.py +0 -0
  109. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/debugger/__init__.py +0 -0
  110. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/deploy/__init__.py +0 -0
  111. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/deploy/deployer.py +0 -0
  112. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/events/__init__.py +0 -0
  113. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/events/listener.py +0 -0
  114. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit/usdc/__init__.py +0 -0
  115. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit.egg-info/dependency_links.txt +0 -0
  116. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit.egg-info/entry_points.txt +0 -0
  117. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/arc_devkit.egg-info/top_level.txt +0 -0
  118. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/setup.cfg +0 -0
  119. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_api.py +0 -0
  120. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_api_security.py +0 -0
  121. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_cli.py +0 -0
  122. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_connection.py +0 -0
  123. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_contracts.py +0 -0
  124. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_copilot.py +0 -0
  125. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_deploy.py +0 -0
  126. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_events.py +0 -0
  127. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_guardrails.py +0 -0
  128. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_integration.py +0 -0
  129. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_usdc.py +0 -0
  130. {arc_devkit-0.4.7 → arc_devkit-0.8.0}/tests/test_validation.py +0 -0
@@ -1,16 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arc-devkit
3
- Version: 0.4.7
3
+ Version: 0.8.0
4
4
  Summary: Plataforma open source de ferramentas para desenvolvedores da Arc blockchain (Circle)
5
- Author: Arc DevKit Contributors
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/Jeielsantosdev/arc_dev_kit
8
- Project-URL: Repository, https://github.com/Jeielsantosdev/arc_dev_kit
9
- Project-URL: Issues, https://github.com/Jeielsantosdev/arc_dev_kit/issues
5
+ Author-email: Jeielsantosdev <jeielsantos.ti@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Jeielsantosdev/arc-devkit
8
+ Project-URL: Repository, https://github.com/Jeielsantosdev/arc-devkit
9
+ Project-URL: Bug Tracker, https://github.com/Jeielsantosdev/arc-devkit/issues
10
10
  Keywords: arc,blockchain,circle,usdc,web3,ai,anthropic
11
11
  Classifier: Development Status :: 3 - Alpha
12
12
  Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Programming Language :: Python :: 3
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
@@ -39,8 +38,13 @@ Requires-Dist: mypy>=1.8; extra == "dev"
39
38
  Requires-Dist: bandit>=1.7; extra == "dev"
40
39
  Requires-Dist: pip-audit>=2.7; extra == "dev"
41
40
  Requires-Dist: httpx>=0.27; extra == "dev"
41
+ Requires-Dist: vcrpy>=6.0; extra == "dev"
42
+ Requires-Dist: mcp>=1.2; extra == "dev"
43
+ Requires-Dist: locust>=2.29; extra == "dev"
42
44
  Provides-Extra: security
43
45
  Requires-Dist: keyring>=24; extra == "security"
46
+ Provides-Extra: mcp
47
+ Requires-Dist: mcp>=1.2; extra == "mcp"
44
48
 
45
49
  # Arc DevKit
46
50
 
@@ -53,6 +57,8 @@ Requires-Dist: keyring>=24; extra == "security"
53
57
 
54
58
  **Arc DevKit** is a complete Python SDK for developers building on the **Arc blockchain** — Circle's EVM-compatible Layer 1 with USDC as the gas token and sub-second finality.
55
59
 
60
+ **link to project** https://arc-dev-kit-uxun.vercel.app/
61
+
56
62
  It solves the practical friction of building on Arc: USDC gas accounting, PoA middleware, ERC-20 monitoring, async agents, WebSocket streaming, and AI-assisted debugging — all packaged and ready to use.
57
63
 
58
64
  ---
@@ -9,6 +9,8 @@
9
9
 
10
10
  **Arc DevKit** is a complete Python SDK for developers building on the **Arc blockchain** — Circle's EVM-compatible Layer 1 with USDC as the gas token and sub-second finality.
11
11
 
12
+ **link to project** https://arc-dev-kit-uxun.vercel.app/
13
+
12
14
  It solves the practical friction of building on Arc: USDC gas accounting, PoA middleware, ERC-20 monitoring, async agents, WebSocket streaming, and AI-assisted debugging — all packaged and ready to use.
13
15
 
14
16
  ---
@@ -74,6 +74,14 @@ class BaseAgent(ABC):
74
74
  account = Account.from_key(_key)
75
75
  self._address: str | None = account.address
76
76
  self._private_key: str | None = _key
77
+
78
+ from arc_devkit.core.signer import LocalKeySigner
79
+
80
+ # Prepares the pluggable Signer interface (core/signer.py) for
81
+ # future hardware-wallet/post-quantum backends — subclasses still
82
+ # sign via self._private_key today; this is additive, not a
83
+ # behavior change.
84
+ self._signer: LocalKeySigner | None = LocalKeySigner(_key)
77
85
  logger.info(
78
86
  "[%s] Initialized with wallet %s",
79
87
  self.__class__.__name__,
@@ -82,6 +90,7 @@ class BaseAgent(ABC):
82
90
  else:
83
91
  self._address = None
84
92
  self._private_key = None
93
+ self._signer = None
85
94
  logger.warning(
86
95
  "[%s] No private key — read-only mode.",
87
96
  self.__class__.__name__,
@@ -10,6 +10,7 @@ import logging
10
10
 
11
11
  from arc_devkit.agents.event_bus import EventBus
12
12
  from arc_devkit.agents.guardrails import Guardrails
13
+ from arc_devkit.agents.jobs import Job, JobRegistry
13
14
  from arc_devkit.agents.monitor_agent import MonitorAgent
14
15
  from arc_devkit.agents.payment_agent import PaymentAgent
15
16
 
@@ -93,6 +94,44 @@ class CoordinatorAgent:
93
94
  self.bus.publish_sync("coordinator.transfer", {"to": to, "result": result})
94
95
  return result
95
96
 
97
+ def hire_agent(
98
+ self,
99
+ job_registry: JobRegistry,
100
+ agent_address: str,
101
+ amount_usdc: float,
102
+ spec: str,
103
+ ) -> Job:
104
+ """
105
+ Create a job (with USDC escrow) hiring another agent — planning a
106
+ workflow that contracts other agents via ERC-8183 jobs.
107
+
108
+ Requires a PaymentAgent registered with a private key (its wallet
109
+ signs the job-creation tx and funds the escrow). The caller must have
110
+ already approved `job_registry`'s contract to spend `amount_usdc` USDC.
111
+ """
112
+ from decimal import Decimal
113
+
114
+ if self._payment_agent is None or self._payment_agent._private_key is None:
115
+ return Job(
116
+ job_id=0,
117
+ requester="",
118
+ agent=agent_address,
119
+ amount_usdc=Decimal(str(amount_usdc)),
120
+ spec=spec,
121
+ error="No PaymentAgent with a private key registered.",
122
+ )
123
+
124
+ job = job_registry.create_job(
125
+ agent_address,
126
+ Decimal(str(amount_usdc)),
127
+ spec,
128
+ self._payment_agent._private_key,
129
+ )
130
+ self.bus.publish_sync(
131
+ "coordinator.hire", {"agent": agent_address, "job_id": job.job_id, "spec": spec}
132
+ )
133
+ return job
134
+
96
135
  def maintain_balance(
97
136
  self,
98
137
  address: str,
@@ -0,0 +1,205 @@
1
+ """ERC-8004 (Trustless Agents) identity + reputation registry client.
2
+
3
+ ERC-8004 is a very recent EIP (2025) for on-chain agent identity and
4
+ reputation, still evolving — no canonical registry deployment address is
5
+ published for Arc (or confirmed cross-chain) yet, so `AgentRegistry` always
6
+ requires an explicit `identity_registry_address` (and, for reputation,
7
+ `reputation_registry_address`). The default ABIs below are this SDK's
8
+ best-effort reconstruction of the registries described in the spec
9
+ (`registerAgent`/`resolveAgent`, `giveFeedback`/`getReputation`) — pass
10
+ `abi=`/`reputation_abi=` to override once a reference ABI is published.
11
+ """
12
+
13
+ import logging
14
+ from dataclasses import dataclass
15
+ from decimal import Decimal
16
+
17
+ from web3 import Web3
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Best-effort default ABIs — see module docstring.
22
+ _DEFAULT_IDENTITY_ABI = [
23
+ {
24
+ "inputs": [
25
+ {"name": "domain", "type": "string"},
26
+ {"name": "agentAddress", "type": "address"},
27
+ ],
28
+ "name": "registerAgent",
29
+ "outputs": [{"name": "agentId", "type": "uint256"}],
30
+ "stateMutability": "nonpayable",
31
+ "type": "function",
32
+ },
33
+ {
34
+ "inputs": [{"name": "agentId", "type": "uint256"}],
35
+ "name": "resolveAgent",
36
+ "outputs": [
37
+ {"name": "domain", "type": "string"},
38
+ {"name": "agentAddress", "type": "address"},
39
+ ],
40
+ "stateMutability": "view",
41
+ "type": "function",
42
+ },
43
+ {
44
+ "anonymous": False,
45
+ "inputs": [
46
+ {"indexed": True, "name": "agentId", "type": "uint256"},
47
+ {"indexed": False, "name": "domain", "type": "string"},
48
+ {"indexed": True, "name": "agentAddress", "type": "address"},
49
+ ],
50
+ "name": "AgentRegistered",
51
+ "type": "event",
52
+ },
53
+ ]
54
+
55
+ _DEFAULT_REPUTATION_ABI = [
56
+ {
57
+ "inputs": [
58
+ {"name": "agentId", "type": "uint256"},
59
+ {"name": "score", "type": "uint8"},
60
+ {"name": "tag", "type": "string"},
61
+ ],
62
+ "name": "giveFeedback",
63
+ "outputs": [],
64
+ "stateMutability": "nonpayable",
65
+ "type": "function",
66
+ },
67
+ {
68
+ "inputs": [{"name": "agentId", "type": "uint256"}],
69
+ "name": "getReputation",
70
+ "outputs": [
71
+ {"name": "totalScore", "type": "uint256"},
72
+ {"name": "feedbackCount", "type": "uint256"},
73
+ ],
74
+ "stateMutability": "view",
75
+ "type": "function",
76
+ },
77
+ ]
78
+
79
+
80
+ @dataclass
81
+ class AgentIdentity:
82
+ """An agent's on-chain identity (ERC-8004 Identity Registry)."""
83
+
84
+ agent_id: int
85
+ domain: str
86
+ agent_address: str
87
+ tx_hash: str | None = None
88
+
89
+
90
+ @dataclass
91
+ class ReputationScore:
92
+ """Aggregated feedback for an agent (ERC-8004 Reputation Registry)."""
93
+
94
+ agent_id: int
95
+ total_score: int
96
+ feedback_count: int
97
+
98
+ @property
99
+ def average(self) -> Decimal:
100
+ if self.feedback_count == 0:
101
+ return Decimal("0")
102
+ return Decimal(self.total_score) / Decimal(self.feedback_count)
103
+
104
+
105
+ class AgentRegistry:
106
+ """Client for the ERC-8004 Identity + Reputation registries."""
107
+
108
+ def __init__(
109
+ self,
110
+ w3: Web3,
111
+ identity_registry_address: str,
112
+ reputation_registry_address: str | None = None,
113
+ abi: list[dict] | None = None,
114
+ reputation_abi: list[dict] | None = None,
115
+ ) -> None:
116
+ from arc_devkit.core.validation import validate_address
117
+
118
+ self._w3 = w3
119
+ self._identity_abi = abi or _DEFAULT_IDENTITY_ABI
120
+ self._identity = w3.eth.contract(
121
+ address=validate_address(identity_registry_address),
122
+ abi=self._identity_abi,
123
+ )
124
+ self._reputation_abi = reputation_abi or _DEFAULT_REPUTATION_ABI
125
+ self._reputation = None
126
+ if reputation_registry_address:
127
+ self._reputation = w3.eth.contract(
128
+ address=validate_address(reputation_registry_address),
129
+ abi=self._reputation_abi,
130
+ )
131
+
132
+ def register(self, domain: str, private_key: str) -> AgentIdentity:
133
+ """Register the caller's wallet as an agent under `domain`."""
134
+ from eth_account import Account
135
+
136
+ sender = Account.from_key(private_key).address
137
+ tx = self._identity.functions.registerAgent(domain, sender).build_transaction(
138
+ {
139
+ "from": sender,
140
+ "nonce": self._w3.eth.get_transaction_count(sender),
141
+ "gas": 200_000,
142
+ "gasPrice": self._w3.eth.gas_price,
143
+ "chainId": self._w3.eth.chain_id,
144
+ }
145
+ )
146
+ signed = self._w3.eth.account.sign_transaction(tx, private_key)
147
+ tx_hash = self._w3.eth.send_raw_transaction(signed.raw_transaction)
148
+ tx_hash_hex = tx_hash.hex()
149
+
150
+ receipt = self._w3.eth.wait_for_transaction_receipt(tx_hash)
151
+ agent_id = 0
152
+ logs = self._identity.events.AgentRegistered().process_receipt(receipt)
153
+ if logs:
154
+ agent_id = logs[0]["args"]["agentId"]
155
+
156
+ logger.info("Agent registered: domain=%s address=%s id=%s", domain, sender, agent_id)
157
+ return AgentIdentity(
158
+ agent_id=agent_id, domain=domain, agent_address=sender, tx_hash=tx_hash_hex
159
+ )
160
+
161
+ def resolve(self, agent_id: int) -> AgentIdentity | None:
162
+ """Look up an agent's domain and address by id."""
163
+ try:
164
+ domain, agent_address = self._identity.functions.resolveAgent(agent_id).call()
165
+ except Exception as exc:
166
+ logger.warning("resolveAgent(%d) failed: %s", agent_id, exc)
167
+ return None
168
+ return AgentIdentity(agent_id=agent_id, domain=domain, agent_address=agent_address)
169
+
170
+ def get_reputation(self, agent_id: int) -> ReputationScore | None:
171
+ """Return aggregated feedback for an agent. None if unavailable."""
172
+ if self._reputation is None:
173
+ logger.warning("No reputation_registry_address configured.")
174
+ return None
175
+ try:
176
+ total_score, feedback_count = self._reputation.functions.getReputation(agent_id).call()
177
+ except Exception as exc:
178
+ logger.warning("getReputation(%d) failed: %s", agent_id, exc)
179
+ return None
180
+ return ReputationScore(
181
+ agent_id=agent_id, total_score=total_score, feedback_count=feedback_count
182
+ )
183
+
184
+ def give_feedback(self, agent_id: int, score: int, private_key: str, tag: str = "") -> str:
185
+ """Submit feedback (0-100) for an agent. Returns the tx hash."""
186
+ if not 0 <= score <= 100:
187
+ raise ValueError("score must be between 0 and 100.")
188
+ if self._reputation is None:
189
+ raise ValueError("No reputation_registry_address configured.")
190
+
191
+ from eth_account import Account
192
+
193
+ sender = Account.from_key(private_key).address
194
+ tx = self._reputation.functions.giveFeedback(agent_id, score, tag).build_transaction(
195
+ {
196
+ "from": sender,
197
+ "nonce": self._w3.eth.get_transaction_count(sender),
198
+ "gas": 150_000,
199
+ "gasPrice": self._w3.eth.gas_price,
200
+ "chainId": self._w3.eth.chain_id,
201
+ }
202
+ )
203
+ signed = self._w3.eth.account.sign_transaction(tx, private_key)
204
+ tx_hash = self._w3.eth.send_raw_transaction(signed.raw_transaction)
205
+ return tx_hash.hex()
@@ -0,0 +1,88 @@
1
+ """JobAgent — accepts and executes ERC-8183 jobs autonomously, within guardrails."""
2
+
3
+ import logging
4
+ from collections.abc import Callable
5
+
6
+ from arc_devkit.agents.base_agent import BaseAgent
7
+ from arc_devkit.agents.guardrails import Guardrails, GuardrailViolation
8
+ from arc_devkit.agents.jobs import Job, JobRegistry
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class JobAgent(BaseAgent):
14
+ """
15
+ Autonomously accepts an ERC-8183 job, runs a handler to produce the
16
+ deliverable, and submits it — every step gated by the kill switch.
17
+
18
+ Example:
19
+ agent = JobAgent(registry_address="0x...", private_key=key, guardrails=guardrails)
20
+ result = agent.execute(job_id=42, handler=lambda job: "ipfs://Qm...")
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ registry_address: str,
26
+ job_abi: list[dict] | None = None,
27
+ guardrails: Guardrails | None = None,
28
+ **kwargs: object,
29
+ ) -> None:
30
+ super().__init__(**kwargs)
31
+ self._guardrails = guardrails
32
+ self._registry = JobRegistry(self._w3, registry_address, abi=job_abi, guardrails=guardrails)
33
+
34
+ def get_balance(self) -> dict:
35
+ if not self._address:
36
+ return {"error": "No private key configured — read-only mode."}
37
+ wei = self._w3.eth.get_balance(self._address)
38
+ return {"address": self._address, "balance_wei": str(wei)}
39
+
40
+ def execute(self, job_id: int, handler: Callable[[Job], str]) -> dict: # type: ignore[override]
41
+ """
42
+ Accept `job_id`, run `handler(job)` to produce a deliverable URI/string,
43
+ and deliver it.
44
+
45
+ Args:
46
+ job_id: The job to accept.
47
+ handler: Called with the accepted Job; must return the deliverable
48
+ (e.g. an IPFS URI or a result string) to submit on-chain.
49
+
50
+ Returns:
51
+ Dict with status ("delivered"/"blocked"/"error") and the Job state.
52
+ """
53
+ if not self._private_key:
54
+ return {"status": "error", "error": "Private key required."}
55
+
56
+ if self._guardrails:
57
+ try:
58
+ self._guardrails.check_kill_switch()
59
+ except GuardrailViolation as exc:
60
+ return {"status": "blocked", "error": str(exc)}
61
+
62
+ job = self._registry.accept_job(job_id, self._private_key)
63
+ if job.error:
64
+ self.log(f"Failed to accept job {job_id}: {job.error}")
65
+ return {"status": "error", "error": job.error, "job": job}
66
+
67
+ self.log(f"Accepted job {job_id} — running handler...")
68
+ try:
69
+ deliverable = handler(job)
70
+ except Exception as exc:
71
+ self.log(f"Handler failed for job {job_id}: {exc}")
72
+ return {"status": "error", "error": str(exc), "job": job}
73
+
74
+ delivered = self._registry.deliver_job(job_id, deliverable, self._private_key)
75
+ if delivered.error:
76
+ return {"status": "error", "error": delivered.error, "job": delivered}
77
+
78
+ if self._guardrails:
79
+ self._guardrails.audit(
80
+ agent="JobAgent",
81
+ trigger="manual",
82
+ action="job_deliver",
83
+ job_id=job_id,
84
+ deliverable_uri=deliverable,
85
+ )
86
+
87
+ self.log(f"Delivered job {job_id}.")
88
+ return {"status": "delivered", "job": delivered}