arc-devkit 0.4.5__tar.gz → 0.4.6__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 (62) hide show
  1. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/PKG-INFO +1 -1
  2. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/agents/async_monitor.py +13 -11
  3. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/agents/monitor_agent.py +11 -5
  4. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/agents/payment_agent.py +22 -14
  5. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/analytics/portfolio.py +5 -7
  6. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/cli/flat.py +8 -10
  7. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/copilot/agent.py +5 -2
  8. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/debugger/tx_analyzer.py +11 -6
  9. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/deploy/deployer.py +1 -2
  10. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/events/listener.py +1 -3
  11. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit.egg-info/PKG-INFO +1 -1
  12. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/pyproject.toml +1 -1
  13. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_agents.py +1 -3
  14. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_analytics.py +10 -6
  15. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_api.py +4 -4
  16. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_async_agents.py +17 -10
  17. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_cli.py +28 -7
  18. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_contracts.py +5 -7
  19. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_debugger.py +1 -3
  20. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_deploy.py +3 -1
  21. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_events.py +26 -5
  22. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/README.md +0 -0
  23. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/__init__.py +0 -0
  24. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/agents/__init__.py +0 -0
  25. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/agents/async_base.py +0 -0
  26. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/agents/base_agent.py +0 -0
  27. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/analytics/__init__.py +0 -0
  28. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/api/__init__.py +0 -0
  29. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/api/main.py +0 -0
  30. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/api/routes/__init__.py +0 -0
  31. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/api/routes/agents.py +0 -0
  32. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/api/routes/copilot.py +0 -0
  33. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/api/routes/debugger.py +0 -0
  34. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/cli/__init__.py +0 -0
  35. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/cli/commands/__init__.py +0 -0
  36. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/cli/commands/agent.py +0 -0
  37. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/cli/commands/copilot.py +0 -0
  38. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/cli/commands/debug.py +0 -0
  39. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/cli/main.py +0 -0
  40. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/config.py +0 -0
  41. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/contracts/__init__.py +0 -0
  42. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/contracts/loader.py +0 -0
  43. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/copilot/__init__.py +0 -0
  44. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/core/__init__.py +0 -0
  45. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/core/connection.py +0 -0
  46. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/core/gas.py +0 -0
  47. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/core/wallet.py +0 -0
  48. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/debugger/__init__.py +0 -0
  49. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/deploy/__init__.py +0 -0
  50. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/events/__init__.py +0 -0
  51. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/usdc/__init__.py +0 -0
  52. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit/usdc/token.py +0 -0
  53. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit.egg-info/SOURCES.txt +0 -0
  54. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit.egg-info/dependency_links.txt +0 -0
  55. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit.egg-info/entry_points.txt +0 -0
  56. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit.egg-info/requires.txt +0 -0
  57. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/arc_devkit.egg-info/top_level.txt +0 -0
  58. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/setup.cfg +0 -0
  59. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_connection.py +0 -0
  60. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_copilot.py +0 -0
  61. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_integration.py +0 -0
  62. {arc_devkit-0.4.5 → arc_devkit-0.4.6}/tests/test_usdc.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arc-devkit
3
- Version: 0.4.5
3
+ Version: 0.4.6
4
4
  Summary: Plataforma open source de ferramentas para desenvolvedores da Arc blockchain (Circle)
5
5
  Author-email: Jeielsantosdev <jeielsantos.ti@gmail.com>
6
6
  License-Expression: MIT
@@ -99,10 +99,12 @@ class AsyncMonitorAgent(AsyncBaseAgent):
99
99
  if self._state_file:
100
100
  try:
101
101
  self._state_file.write_text(
102
- json.dumps({
103
- "balances": {k: str(v) for k, v in self._last_balances.items()},
104
- "last_erc20_block": self._last_erc20_block,
105
- })
102
+ json.dumps(
103
+ {
104
+ "balances": {k: str(v) for k, v in self._last_balances.items()},
105
+ "last_erc20_block": self._last_erc20_block,
106
+ }
107
+ )
106
108
  )
107
109
  except Exception as exc:
108
110
  logger.warning("Failed to save state: %s", exc)
@@ -159,7 +161,11 @@ class AsyncMonitorAgent(AsyncBaseAgent):
159
161
 
160
162
  direction = "credit" if recipient in self._watched_lower else "debit"
161
163
  address = next(
162
- (a for a in self._watched if a.lower() == (recipient if direction == "credit" else sender)),
164
+ (
165
+ a
166
+ for a in self._watched
167
+ if a.lower() == (recipient if direction == "credit" else sender)
168
+ ),
163
169
  "",
164
170
  )
165
171
 
@@ -182,9 +188,7 @@ class AsyncMonitorAgent(AsyncBaseAgent):
182
188
  async def get_balance(self) -> dict: # type: ignore[override]
183
189
  resultado = {}
184
190
  for addr in self._watched:
185
- wei = await self._acall_rpc(
186
- self._w3.eth.get_balance, cast(ChecksumAddress, addr)
187
- )
191
+ wei = await self._acall_rpc(self._w3.eth.get_balance, cast(ChecksumAddress, addr))
188
192
  resultado[addr] = {
189
193
  "address": addr,
190
194
  "balance_wei": str(wei),
@@ -215,9 +219,7 @@ class AsyncMonitorAgent(AsyncBaseAgent):
215
219
 
216
220
  if self._usdc_contract and self._last_erc20_block == 0:
217
221
  try:
218
- self._last_erc20_block = await self._acall_rpc(
219
- lambda: self._w3.eth.block_number
220
- )
222
+ self._last_erc20_block = await self._acall_rpc(lambda: self._w3.eth.block_number)
221
223
  except Exception:
222
224
  pass
223
225
 
@@ -111,10 +111,12 @@ class MonitorAgent(BaseAgent):
111
111
  if self._state_file:
112
112
  try:
113
113
  self._state_file.write_text(
114
- json.dumps({
115
- "balances": {k: str(v) for k, v in self._last_balances.items()},
116
- "last_erc20_block": self._last_erc20_block,
117
- })
114
+ json.dumps(
115
+ {
116
+ "balances": {k: str(v) for k, v in self._last_balances.items()},
117
+ "last_erc20_block": self._last_erc20_block,
118
+ }
119
+ )
118
120
  )
119
121
  except Exception as exc:
120
122
  logger.warning("Failed to save state: %s", exc)
@@ -170,7 +172,11 @@ class MonitorAgent(BaseAgent):
170
172
 
171
173
  direction = "credit" if recipient in self._watched_lower else "debit"
172
174
  address = next(
173
- (a for a in self._watched if a.lower() == (recipient if direction == "credit" else sender)),
175
+ (
176
+ a
177
+ for a in self._watched
178
+ if a.lower() == (recipient if direction == "credit" else sender)
179
+ ),
174
180
  "",
175
181
  )
176
182
 
@@ -85,12 +85,15 @@ class PaymentAgent(BaseAgent):
85
85
  nonce = self._w3.eth.get_transaction_count(cast(ChecksumAddress, self._address))
86
86
 
87
87
  tx = contract.functions.transfer(to, atomic).build_transaction(
88
- cast(TxParams, {
89
- "from": self._address,
90
- "nonce": nonce,
91
- "gasPrice": self._w3.eth.gas_price,
92
- "chainId": self._w3.eth.chain_id,
93
- })
88
+ cast(
89
+ TxParams,
90
+ {
91
+ "from": self._address,
92
+ "nonce": nonce,
93
+ "gasPrice": self._w3.eth.gas_price,
94
+ "chainId": self._w3.eth.chain_id,
95
+ },
96
+ )
94
97
  )
95
98
  gas_limit = self._estimate_gas(cast(TxParams, tx))
96
99
  tx["gas"] = gas_limit
@@ -137,15 +140,20 @@ class PaymentAgent(BaseAgent):
137
140
  else:
138
141
  value_wei = self._w3.to_wei(amount_usdc, "ether")
139
142
  nonce = self._w3.eth.get_transaction_count(cast(ChecksumAddress, self._address))
140
- tx_base = cast(TxParams, {
141
- "from": self._address,
142
- "to": destinatario,
143
- "value": value_wei,
144
- "nonce": nonce,
145
- "chainId": self._w3.eth.chain_id,
146
- })
143
+ tx_base = cast(
144
+ TxParams,
145
+ {
146
+ "from": self._address,
147
+ "to": destinatario,
148
+ "value": value_wei,
149
+ "nonce": nonce,
150
+ "chainId": self._w3.eth.chain_id,
151
+ },
152
+ )
147
153
  gas_limit = self._estimate_gas(tx_base)
148
- tx = cast(TxParams, {**tx_base, "gas": gas_limit, "gasPrice": self._w3.eth.gas_price})
154
+ tx = cast(
155
+ TxParams, {**tx_base, "gas": gas_limit, "gasPrice": self._w3.eth.gas_price}
156
+ )
149
157
  signed = self._w3.eth.account.sign_transaction(tx, self._private_key)
150
158
  self.log("Transaction signed successfully.")
151
159
 
@@ -34,11 +34,11 @@ class PortfolioSnapshot:
34
34
  """Point-in-time view of a wallet's portfolio on Arc."""
35
35
 
36
36
  address: str
37
- native_balance: Decimal # ARC (18 decimals, converted to ether)
38
- usdc_balance: Decimal | None # USDC (6 decimals); None if contract unavailable
39
- nonce: int # total txs ever sent from this address
37
+ native_balance: Decimal # ARC (18 decimals, converted to ether)
38
+ usdc_balance: Decimal | None # USDC (6 decimals); None if contract unavailable
39
+ nonce: int # total txs ever sent from this address
40
40
  recent_txs: list[TransactionSummary]
41
- blocks_scanned: int # actual number of blocks inspected
41
+ blocks_scanned: int # actual number of blocks inspected
42
42
  blocks_from: int
43
43
  blocks_to: int
44
44
  activity_score: ActivityLevel
@@ -289,9 +289,7 @@ class PortfolioAnalyzer:
289
289
  except Exception:
290
290
  pass
291
291
 
292
- value_arc = Decimal(
293
- str(self._w3.from_wei(tx.get("value", 0), "ether"))
294
- )
292
+ value_arc = Decimal(str(self._w3.from_wei(tx.get("value", 0), "ether")))
295
293
 
296
294
  results.append(
297
295
  TransactionSummary(
@@ -193,6 +193,7 @@ def balance(
193
193
  checksum = _validate_address(address)
194
194
  w3 = get_web3()
195
195
  from eth_typing import ChecksumAddress as _CA
196
+
196
197
  saldo = Decimal(str(w3.from_wei(w3.eth.get_balance(cast(_CA, checksum)), "ether")))
197
198
  nonce = w3.eth.get_transaction_count(cast(_CA, checksum))
198
199
 
@@ -397,9 +398,7 @@ def debug_batch(
397
398
  console.print(summary_table)
398
399
  ok = sum(1 for r in results if r.get("status") == "success")
399
400
  fail = len(results) - ok
400
- console.print(
401
- f"\n[green]✓ {ok} succeeded[/green] [red]✗ {fail} failed[/red]"
402
- )
401
+ console.print(f"\n[green]✓ {ok} succeeded[/green] [red]✗ {fail} failed[/red]")
403
402
 
404
403
 
405
404
  @app.command()
@@ -426,9 +425,12 @@ def codegen(
426
425
  )
427
426
 
428
427
  with console.status("DevCopilot generating code...", spinner="dots"):
429
- resposta = DevCopilot().ask(prompt)
428
+ resposta = DevCopilot(max_tokens=8000).ask(prompt)
430
429
 
431
430
  match = re.search(r"```python\s*(.*?)```", resposta, re.DOTALL)
431
+ if not match:
432
+ # Fallback: grab everything after ```python even if closing fence is missing
433
+ match = re.search(r"```python\s*(.*)", resposta, re.DOTALL)
432
434
  codigo = match.group(1).strip() if match else None
433
435
  explicacao = re.sub(r"```python.*?```", "", resposta, flags=re.DOTALL).strip()
434
436
 
@@ -970,9 +972,7 @@ def portfolio_report(
970
972
  except SystemExit:
971
973
  continue
972
974
 
973
- with console.status(
974
- f"Analyzing [cyan]{checksum[:10]}...[/cyan]", spinner="dots"
975
- ):
975
+ with console.status(f"Analyzing [cyan]{checksum[:10]}...[/cyan]", spinner="dots"):
976
976
  snapshot = analyzer.analyze(checksum, scan_blocks=blocks)
977
977
 
978
978
  data = analyzer.to_dict(snapshot)
@@ -981,9 +981,7 @@ def portfolio_report(
981
981
 
982
982
  score_color = _SCORE_COLOR[snapshot.activity_score]
983
983
  score_icon = _SCORE_ICON[snapshot.activity_score]
984
- usdc_str = (
985
- f"{snapshot.usdc_balance:.4f}" if snapshot.usdc_balance is not None else "N/A"
986
- )
984
+ usdc_str = f"{snapshot.usdc_balance:.4f}" if snapshot.usdc_balance is not None else "N/A"
987
985
 
988
986
  report_table.add_row(
989
987
  label or "—",
@@ -71,6 +71,7 @@ class DevCopilot:
71
71
  extra_context: str | None = None,
72
72
  model: str | None = None,
73
73
  offline: bool = False,
74
+ max_tokens: int | None = None,
74
75
  ) -> None:
75
76
  """
76
77
  Args:
@@ -79,10 +80,12 @@ class DevCopilot:
79
80
  model: Model override (default: ANTHROPIC_MODEL from .env).
80
81
  offline: When True, return a mock response without calling the API.
81
82
  Useful for local tests and CI environments without an API key.
83
+ max_tokens: Override the default MAX_TOKENS limit for this instance.
82
84
  """
83
85
  self._offline = offline
84
86
  self._client = anthropic.Anthropic(api_key=settings.anthropic_api_key)
85
87
  self.model = model or settings.anthropic_model
88
+ self._max_tokens = max_tokens if max_tokens is not None else self.MAX_TOKENS
86
89
  self._history: list[dict] = []
87
90
  self._cache: dict[str, tuple[str, float]] = {} # key → (response, timestamp)
88
91
 
@@ -147,7 +150,7 @@ class DevCopilot:
147
150
 
148
151
  message = self._client.messages.create(
149
152
  model=self.model,
150
- max_tokens=self.MAX_TOKENS,
153
+ max_tokens=self._max_tokens,
151
154
  system=self._system,
152
155
  messages=cast(list[MessageParam], list(self._history)),
153
156
  )
@@ -197,7 +200,7 @@ class DevCopilot:
197
200
 
198
201
  with self._client.messages.stream(
199
202
  model=self.model,
200
- max_tokens=self.MAX_TOKENS,
203
+ max_tokens=self._max_tokens,
201
204
  system=self._system,
202
205
  messages=cast(list[MessageParam], self._history),
203
206
  ) as stream:
@@ -13,8 +13,8 @@ from arc_devkit.core.connection import get_web3
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
  # Solidity revert selectors
16
- _REVERT_SELECTOR = bytes.fromhex("08c379a0") # Error(string)
17
- _PANIC_SELECTOR = bytes.fromhex("4e487b71") # Panic(uint256)
16
+ _REVERT_SELECTOR = bytes.fromhex("08c379a0") # Error(string)
17
+ _PANIC_SELECTOR = bytes.fromhex("4e487b71") # Panic(uint256)
18
18
 
19
19
  _PANIC_CODES: dict[int, str] = {
20
20
  0x00: "Generic compiler-inserted panic",
@@ -164,6 +164,7 @@ class TxAnalyzer:
164
164
  self._w3 = w3
165
165
  elif rpc_url:
166
166
  from web3.middleware import ExtraDataToPOAMiddleware
167
+
167
168
  _w3 = Web3(Web3.HTTPProvider(rpc_url, request_kwargs={"timeout": 10}))
168
169
  _w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
169
170
  self._w3 = _w3
@@ -252,9 +253,7 @@ class TxAnalyzer:
252
253
  summary = copilot.ask(prompt)
253
254
  except Exception as exc:
254
255
  logger.warning("AI analysis unavailable: %s", exc)
255
- summary = (
256
- f"Status: {status_str} | Gas used: {gas_used} | Cost: {cost_decimal} USDC"
257
- )
256
+ summary = f"Status: {status_str} | Gas used: {gas_used} | Cost: {cost_decimal} USDC"
258
257
  if revert_reason:
259
258
  summary += f" | Revert: {revert_reason}"
260
259
 
@@ -364,7 +363,13 @@ class TxAnalyzer:
364
363
  normalized[k] = str(v)
365
364
  elif isinstance(v, (list, tuple)):
366
365
  normalized[k] = [
367
- ("0x" + x.hex() if isinstance(x, bytes) else str(x) if isinstance(x, int) else x)
366
+ (
367
+ "0x" + x.hex()
368
+ if isinstance(x, bytes)
369
+ else str(x)
370
+ if isinstance(x, int)
371
+ else x
372
+ )
368
373
  for x in v
369
374
  ]
370
375
  else:
@@ -214,8 +214,7 @@ class ContractDeployer:
214
214
  if key is None:
215
215
  available = [k.split(":")[-1] for k in compiled]
216
216
  raise ValueError(
217
- f"Contract '{contract_name}' not found. "
218
- f"Available: {', '.join(available)}"
217
+ f"Contract '{contract_name}' not found. Available: {', '.join(available)}"
219
218
  )
220
219
 
221
220
  abi = compiled[key]["abi"]
@@ -50,9 +50,7 @@ class EventListener:
50
50
 
51
51
  self._contract = None
52
52
  if self._contract_address and self._abi:
53
- self._contract = self._w3.eth.contract(
54
- address=self._contract_address, abi=self._abi
55
- )
53
+ self._contract = self._w3.eth.contract(address=self._contract_address, abi=self._abi)
56
54
 
57
55
  # ------------------------------------------------------------------
58
56
  # Registration
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arc-devkit
3
- Version: 0.4.5
3
+ Version: 0.4.6
4
4
  Summary: Plataforma open source de ferramentas para desenvolvedores da Arc blockchain (Circle)
5
5
  Author-email: Jeielsantosdev <jeielsantos.ti@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "arc-devkit"
7
- version = "0.4.5"
7
+ version = "0.4.6"
8
8
  description = "Plataforma open source de ferramentas para desenvolvedores da Arc blockchain (Circle)"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -234,9 +234,7 @@ class TestPaymentAgent:
234
234
  from arc_devkit.agents.payment_agent import PaymentAgent
235
235
 
236
236
  agent = PaymentAgent(private_key="0x" + "a" * 64)
237
- with patch("time.sleep") as mock_sleep, patch(
238
- "time.time", side_effect=[0.0, 1.0, 2.0]
239
- ):
237
+ with patch("time.sleep") as mock_sleep, patch("time.time", side_effect=[0.0, 1.0, 2.0]):
240
238
  result = agent._wait_for_receipt(b"\xde\xad", timeout=10)
241
239
  assert result == receipt
242
240
  mock_sleep.assert_called_once()
@@ -439,11 +439,11 @@ class TestBalanceHistory:
439
439
 
440
440
  from arc_devkit.cli.flat import app
441
441
 
442
- monkeypatch.setattr(
443
- "arc_devkit.analytics.portfolio._HISTORY_DIR", tmp_path / "history"
444
- )
442
+ monkeypatch.setattr("arc_devkit.analytics.portfolio._HISTORY_DIR", tmp_path / "history")
445
443
  runner = CliRunner()
446
- with patch("arc_devkit.analytics.portfolio.PortfolioAnalyzer.load_history", return_value=[]):
444
+ with patch(
445
+ "arc_devkit.analytics.portfolio.PortfolioAnalyzer.load_history", return_value=[]
446
+ ):
447
447
  result = runner.invoke(app, ["portfolio", "history", "0x" + "a" * 40])
448
448
  assert result.exit_code == 0
449
449
  assert "No history" in result.output or "history" in result.output.lower()
@@ -454,9 +454,13 @@ class TestBalanceHistory:
454
454
 
455
455
  from arc_devkit.cli.flat import app
456
456
 
457
- records = [{"native_balance": "1.5", "timestamp": "2026-01-01T00:00:00+00:00", "tx_count": 2}]
457
+ records = [
458
+ {"native_balance": "1.5", "timestamp": "2026-01-01T00:00:00+00:00", "tx_count": 2}
459
+ ]
458
460
  runner = CliRunner()
459
- with patch("arc_devkit.analytics.portfolio.PortfolioAnalyzer.load_history", return_value=records):
461
+ with patch(
462
+ "arc_devkit.analytics.portfolio.PortfolioAnalyzer.load_history", return_value=records
463
+ ):
460
464
  result = runner.invoke(app, ["portfolio", "history", "0x" + "a" * 40, "--json"])
461
465
  assert result.exit_code == 0
462
466
  data = json.loads(result.output)
@@ -130,9 +130,7 @@ def test_estimate_gas_sem_to_retorna_422(client):
130
130
 
131
131
  def test_estimate_gas_bad_address_returns_400(client):
132
132
  with patch("arc_devkit.core.gas.estimate_transfer", side_effect=Exception("bad addr")):
133
- resp = client.get(
134
- "/debug/estimate", params={"to": "bad", "amount": 1.0}
135
- )
133
+ resp = client.get("/debug/estimate", params={"to": "bad", "amount": 1.0})
136
134
  assert resp.status_code == 400
137
135
 
138
136
 
@@ -244,7 +242,9 @@ def test_payment_execute_error_returns_400(client):
244
242
 
245
243
 
246
244
  def test_payment_500_on_unexpected_exception(client, mock_web3):
247
- with patch("arc_devkit.agents.payment_agent.PaymentAgent.execute", side_effect=Exception("boom")):
245
+ with patch(
246
+ "arc_devkit.agents.payment_agent.PaymentAgent.execute", side_effect=Exception("boom")
247
+ ):
248
248
  resp = client.post(
249
249
  "/agents/payment",
250
250
  json={
@@ -144,8 +144,8 @@ async def test_async_monitor_callback_fired_on_change():
144
144
  w3.from_wei.return_value = "1.0"
145
145
  # First call seeds the initial balance, second detects a change
146
146
  w3.eth.get_balance.side_effect = [
147
- _MOCK_BALANCE, # initial seed in execute()
148
- _MOCK_BALANCE * 2, # change detected on first iteration
147
+ _MOCK_BALANCE, # initial seed in execute()
148
+ _MOCK_BALANCE * 2, # change detected on first iteration
149
149
  ]
150
150
  monitor._w3 = w3
151
151
 
@@ -237,8 +237,12 @@ def test_websocket_monitor_accepts_connection():
237
237
 
238
238
  from arc_devkit.api.main import app
239
239
 
240
- with patch("arc_devkit.core.connection.get_web3", return_value=_mock_w3()), \
241
- patch("arc_devkit.agents.async_monitor.AsyncMonitorAgent.execute", new_callable=AsyncMock) as mock_exec:
240
+ with (
241
+ patch("arc_devkit.core.connection.get_web3", return_value=_mock_w3()),
242
+ patch(
243
+ "arc_devkit.agents.async_monitor.AsyncMonitorAgent.execute", new_callable=AsyncMock
244
+ ) as mock_exec,
245
+ ):
242
246
  mock_exec.return_value = {"status": "done", "iterations": 0}
243
247
 
244
248
  client = TestClient(app)
@@ -257,10 +261,14 @@ def test_async_monitor_state_file_loaded(tmp_path):
257
261
  import json
258
262
 
259
263
  state = tmp_path / "state.json"
260
- state.write_text(json.dumps({
261
- "balances": {_ADDRESS: "5000000000000000000"},
262
- "last_erc20_block": 42,
263
- }))
264
+ state.write_text(
265
+ json.dumps(
266
+ {
267
+ "balances": {_ADDRESS: "5000000000000000000"},
268
+ "last_erc20_block": 42,
269
+ }
270
+ )
271
+ )
264
272
 
265
273
  with patch("arc_devkit.core.connection.get_web3", return_value=_mock_w3()):
266
274
  from arc_devkit.agents.async_monitor import AsyncMonitorAgent
@@ -330,8 +338,7 @@ async def test_async_monitor_webhook_called(tmp_path, respx_mock=None):
330
338
  resp.raise_for_status = MagicMock()
331
339
  return resp
332
340
 
333
- with patch("httpx.AsyncClient.post", fake_post), \
334
- patch("asyncio.sleep", new_callable=AsyncMock):
341
+ with patch("httpx.AsyncClient.post", fake_post), patch("asyncio.sleep", new_callable=AsyncMock):
335
342
  await monitor.execute(max_iterations=1)
336
343
 
337
344
  assert len(events_sent) == 1
@@ -220,7 +220,9 @@ class TestHistory:
220
220
  def test_history_json_output(self, tmp_path, monkeypatch):
221
221
  history_file = tmp_path / "history.json"
222
222
  history_file.write_text(
223
- json.dumps([{"type": "debug", "tx_hash": "0x" + "a" * 64, "timestamp": "2026-06-21T10:00:00"}])
223
+ json.dumps(
224
+ [{"type": "debug", "tx_hash": "0x" + "a" * 64, "timestamp": "2026-06-21T10:00:00"}]
225
+ )
224
226
  )
225
227
  monkeypatch.setattr("arc_devkit.cli.flat._HISTORY_FILE", history_file)
226
228
  result = runner.invoke(app, ["history", "--json"])
@@ -232,7 +234,15 @@ class TestHistory:
232
234
  def test_history_table_output(self, tmp_path, monkeypatch):
233
235
  history_file = tmp_path / "history.json"
234
236
  history_file.write_text(
235
- json.dumps([{"type": "portfolio", "address": "0x" + "a" * 40, "timestamp": "2026-06-21T10:00:00"}])
237
+ json.dumps(
238
+ [
239
+ {
240
+ "type": "portfolio",
241
+ "address": "0x" + "a" * 40,
242
+ "timestamp": "2026-06-21T10:00:00",
243
+ }
244
+ ]
245
+ )
236
246
  )
237
247
  monkeypatch.setattr("arc_devkit.cli.flat._HISTORY_FILE", history_file)
238
248
  result = runner.invoke(app, ["history"])
@@ -273,7 +283,10 @@ class TestDebugCommand:
273
283
  "summary": "OK",
274
284
  "custo_usdc": "0.0001",
275
285
  "revert_reason": None,
276
- "decoded_input": {"function": "transfer", "args": {"to": "0xf...", "amount": "100"}},
286
+ "decoded_input": {
287
+ "function": "transfer",
288
+ "args": {"to": "0xf...", "amount": "100"},
289
+ },
277
290
  "error": None,
278
291
  "suggestion": "",
279
292
  "raw_data": {},
@@ -349,7 +362,9 @@ class TestSendCommand:
349
362
  "raw_transaction": "0xdeadbeef",
350
363
  "nota": "Transaction signed.",
351
364
  }
352
- with patch("arc_devkit.agents.payment_agent.PaymentAgent.execute", return_value=signed_result):
365
+ with patch(
366
+ "arc_devkit.agents.payment_agent.PaymentAgent.execute", return_value=signed_result
367
+ ):
353
368
  result = runner.invoke(
354
369
  app,
355
370
  ["send", "0x" + "b" * 40, "1.0", "--token", "native", "--json"],
@@ -373,7 +388,9 @@ class TestSendCommand:
373
388
  "raw_transaction": "0xcafebabe",
374
389
  "nota": "USDC transfer signed.",
375
390
  }
376
- with patch("arc_devkit.agents.payment_agent.PaymentAgent.execute", return_value=signed_result):
391
+ with patch(
392
+ "arc_devkit.agents.payment_agent.PaymentAgent.execute", return_value=signed_result
393
+ ):
377
394
  result = runner.invoke(
378
395
  app,
379
396
  ["send", "0x" + "b" * 40, "5.0", "--token", "usdc", "--json"],
@@ -398,7 +415,9 @@ class TestSendCommand:
398
415
  "raw_transaction": "0xdeadbeef01020304050607080910111213",
399
416
  "nota": "Transaction signed.",
400
417
  }
401
- with patch("arc_devkit.agents.payment_agent.PaymentAgent.execute", return_value=signed_result):
418
+ with patch(
419
+ "arc_devkit.agents.payment_agent.PaymentAgent.execute", return_value=signed_result
420
+ ):
402
421
  result = runner.invoke(app, ["send", "0x" + "b" * 40, "2.0"])
403
422
 
404
423
  assert result.exit_code == 0
@@ -417,7 +436,9 @@ class TestSendCommand:
417
436
  "tx_hash": "0x" + "de" * 32,
418
437
  "aviso": "Timeout waiting for receipt.",
419
438
  }
420
- with patch("arc_devkit.agents.payment_agent.PaymentAgent.execute", return_value=sent_result):
439
+ with patch(
440
+ "arc_devkit.agents.payment_agent.PaymentAgent.execute", return_value=sent_result
441
+ ):
421
442
  result = runner.invoke(app, ["send", "0x" + "b" * 40, "3.0", "--token", "usdc"])
422
443
 
423
444
  assert result.exit_code == 0
@@ -14,9 +14,7 @@ import pytest
14
14
 
15
15
  class TestLoadAbi:
16
16
  def _write_json(self, data: object, suffix: str = ".json") -> Path:
17
- f = tempfile.NamedTemporaryFile(
18
- mode="w", suffix=suffix, delete=False, encoding="utf-8"
19
- )
17
+ f = tempfile.NamedTemporaryFile(mode="w", suffix=suffix, delete=False, encoding="utf-8")
20
18
  json.dump(data, f)
21
19
  f.flush()
22
20
  return Path(f.name)
@@ -153,7 +151,9 @@ class TestDecodeEvents:
153
151
 
154
152
  w3 = MagicMock()
155
153
  decoded_log = MagicMock()
156
- decoded_log.__getitem__ = lambda self, k: {"args": {"from": "0xa", "to": "0xb", "value": 100}}[k]
154
+ decoded_log.__getitem__ = lambda self, k: {
155
+ "args": {"from": "0xa", "to": "0xb", "value": 100}
156
+ }[k]
157
157
  w3.eth.contract.return_value.events.__getitem__.return_value.return_value.process_receipt.return_value = [
158
158
  {"args": {"from": "0xa", "to": "0xb", "value": 100}}
159
159
  ]
@@ -180,8 +180,6 @@ class TestDecodeEvents:
180
180
  from arc_devkit.contracts.loader import decode_events
181
181
 
182
182
  w3 = MagicMock()
183
- w3.eth.contract.return_value.events.__getitem__.return_value.return_value.process_receipt.return_value = (
184
- []
185
- )
183
+ w3.eth.contract.return_value.events.__getitem__.return_value.return_value.process_receipt.return_value = []
186
184
  result = decode_events({}, [], "Transfer", "0x" + "a" * 40, w3=w3)
187
185
  assert result == []
@@ -320,9 +320,7 @@ class TestDebugCLI:
320
320
  from arc_devkit.cli.flat import app
321
321
 
322
322
  runner = CliRunner()
323
- result = runner.invoke(
324
- app, ["debug", "0x" + "a" * 64, "--abi", "nonexistent.json"]
325
- )
323
+ result = runner.invoke(app, ["debug", "0x" + "a" * 64, "--abi", "nonexistent.json"])
326
324
  assert result.exit_code != 0
327
325
 
328
326
  def test_debug_batch_from_txt_file(self, mock_anthropic):
@@ -161,7 +161,9 @@ class TestContractDeployer:
161
161
 
162
162
  def test_deploy_from_source_raises_import_error_without_solcx(self, tmp_path):
163
163
  sol_file = tmp_path / "Token.sol"
164
- sol_file.write_text("// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\ncontract Token {}")
164
+ sol_file.write_text(
165
+ "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\ncontract Token {}"
166
+ )
165
167
 
166
168
  d = self._deployer()
167
169
  with patch.dict("sys.modules", {"solcx": None}):
@@ -127,7 +127,14 @@ class TestEventListenerPoll:
127
127
  received: list = []
128
128
 
129
129
  def fake_decode(self_log):
130
- return {"event": "Transfer", "address": "0x...", "args": {}, "block_number": 1, "tx_hash": None, "log_index": 0}
130
+ return {
131
+ "event": "Transfer",
132
+ "address": "0x...",
133
+ "args": {},
134
+ "block_number": 1,
135
+ "tx_hash": None,
136
+ "log_index": 0,
137
+ }
131
138
 
132
139
  with patch.object(EventListener, "_decode_log", side_effect=fake_decode):
133
140
  el.on("Transfer", lambda e: received.append(e))
@@ -182,10 +189,18 @@ class TestEventListenerPoll:
182
189
 
183
190
  received: list = []
184
191
  bad_cb = MagicMock(side_effect=RuntimeError("boom"))
192
+
185
193
  def good_cb(e):
186
194
  received.append(e)
187
195
 
188
- fake_event = {"event": "Transfer", "address": "0x", "args": {}, "block_number": 1, "tx_hash": None, "log_index": 0}
196
+ fake_event = {
197
+ "event": "Transfer",
198
+ "address": "0x",
199
+ "args": {},
200
+ "block_number": 1,
201
+ "tx_hash": None,
202
+ "log_index": 0,
203
+ }
189
204
  with patch.object(EventListener, "_decode_log", return_value=fake_event):
190
205
  el.on("Transfer", bad_cb)
191
206
  el.on("Transfer", good_cb)
@@ -228,7 +243,9 @@ class TestDecodeLog:
228
243
  "transactionHash": bytes.fromhex("ab" * 32),
229
244
  "logIndex": 0,
230
245
  }.get(k, default)
231
- contract_mock.events.__getitem__.return_value.return_value.process_log.return_value = decoded
246
+ contract_mock.events.__getitem__.return_value.return_value.process_log.return_value = (
247
+ decoded
248
+ )
232
249
  w3.eth.contract.return_value = contract_mock
233
250
 
234
251
  el = EventListener(contract_address=addr, abi=abi, w3=w3)
@@ -246,7 +263,9 @@ class TestDecodeLog:
246
263
 
247
264
  w3 = _make_w3()
248
265
  contract_mock = MagicMock()
249
- contract_mock.events.__getitem__.return_value.return_value.process_log.side_effect = Exception("bad log")
266
+ contract_mock.events.__getitem__.return_value.return_value.process_log.side_effect = (
267
+ Exception("bad log")
268
+ )
250
269
  w3.eth.contract.return_value = contract_mock
251
270
 
252
271
  el = EventListener(contract_address=addr, abi=abi, w3=w3)
@@ -276,7 +295,9 @@ class TestDecodeLog:
276
295
  w3 = _make_w3()
277
296
  contract_mock = MagicMock()
278
297
  # Make event decoding fail so we fall back to raw
279
- contract_mock.events.__getitem__.return_value.return_value.process_log.side_effect = Exception("fail")
298
+ contract_mock.events.__getitem__.return_value.return_value.process_log.side_effect = (
299
+ Exception("fail")
300
+ )
280
301
  w3.eth.contract.return_value = contract_mock
281
302
 
282
303
  el = EventListener(contract_address=addr, abi=abi, w3=w3)
File without changes
File without changes