arc-devkit 0.4.4__tar.gz → 0.4.5__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.
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/PKG-INFO +1 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/__init__.py +2 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/agents/async_monitor.py +2 -2
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/agents/payment_agent.py +1 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/analytics/portfolio.py +2 -2
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/api/routes/agents.py +1 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/deploy/deployer.py +1 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit.egg-info/PKG-INFO +1 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/pyproject.toml +1 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_agents.py +0 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_analytics.py +2 -5
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_async_agents.py +4 -5
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_cli.py +1 -5
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_contracts.py +3 -3
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_debugger.py +2 -5
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_deploy.py +0 -1
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_events.py +3 -2
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_usdc.py +4 -3
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/README.md +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/agents/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/agents/async_base.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/agents/base_agent.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/agents/monitor_agent.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/analytics/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/api/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/api/main.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/api/routes/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/api/routes/copilot.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/api/routes/debugger.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/cli/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/cli/commands/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/cli/commands/agent.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/cli/commands/copilot.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/cli/commands/debug.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/cli/flat.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/cli/main.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/config.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/contracts/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/contracts/loader.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/copilot/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/copilot/agent.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/core/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/core/connection.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/core/gas.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/core/wallet.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/debugger/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/debugger/tx_analyzer.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/deploy/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/events/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/events/listener.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/usdc/__init__.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit/usdc/token.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit.egg-info/SOURCES.txt +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit.egg-info/dependency_links.txt +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit.egg-info/entry_points.txt +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit.egg-info/requires.txt +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/arc_devkit.egg-info/top_level.txt +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/setup.cfg +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_api.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_connection.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_copilot.py +0 -0
- {arc_devkit-0.4.4 → arc_devkit-0.4.5}/tests/test_integration.py +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Arc DevKit — Developer tools for the Arc blockchain by Circle."""
|
|
2
2
|
|
|
3
|
-
from importlib.metadata import PackageNotFoundError
|
|
3
|
+
from importlib.metadata import PackageNotFoundError
|
|
4
|
+
from importlib.metadata import version as _version
|
|
4
5
|
|
|
5
6
|
try:
|
|
6
7
|
__version__ = _version("arc-devkit")
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
|
-
from collections.abc import AsyncIterator, Callable
|
|
6
|
+
from collections.abc import AsyncIterator, Callable
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import Any, cast
|
|
9
9
|
|
|
@@ -295,7 +295,7 @@ class AsyncMonitorAgent(AsyncBaseAgent):
|
|
|
295
295
|
break
|
|
296
296
|
try:
|
|
297
297
|
event = await asyncio.wait_for(queue.get(), timeout=1.0)
|
|
298
|
-
except
|
|
298
|
+
except TimeoutError:
|
|
299
299
|
continue # re-check task.done() on next iteration
|
|
300
300
|
yield event
|
|
301
301
|
count += 1
|
|
@@ -77,7 +77,7 @@ class PaymentAgent(BaseAgent):
|
|
|
77
77
|
|
|
78
78
|
def _build_usdc_signed_tx(self, to: str, amount: Decimal) -> tuple:
|
|
79
79
|
"""Build and sign a USDC ERC-20 transfer tx; return (signed, gas_limit)."""
|
|
80
|
-
from arc_devkit.usdc.token import _ERC20_ABI,
|
|
80
|
+
from arc_devkit.usdc.token import _ERC20_ABI, USDC_ARC_TESTNET_ADDRESS, USDC_MULTIPLIER
|
|
81
81
|
|
|
82
82
|
usdc_address = Web3.to_checksum_address(USDC_ARC_TESTNET_ADDRESS)
|
|
83
83
|
contract = self._w3.eth.contract(address=usdc_address, abi=_ERC20_ABI)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from datetime import
|
|
6
|
+
from datetime import UTC, datetime
|
|
7
7
|
from decimal import Decimal
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any, Literal
|
|
@@ -151,7 +151,7 @@ class PortfolioAnalyzer:
|
|
|
151
151
|
history_file = store / f"{safe_addr}.jsonl"
|
|
152
152
|
|
|
153
153
|
record = self.to_dict(snapshot)
|
|
154
|
-
record["timestamp"] = datetime.now(tz=
|
|
154
|
+
record["timestamp"] = datetime.now(tz=UTC).isoformat()
|
|
155
155
|
|
|
156
156
|
with history_file.open("a", encoding="utf-8") as f:
|
|
157
157
|
f.write(json.dumps(record) + "\n")
|
|
@@ -180,7 +180,7 @@ async def monitor_ws(
|
|
|
180
180
|
try:
|
|
181
181
|
event = await asyncio.wait_for(queue.get(), timeout=1.0)
|
|
182
182
|
await websocket.send_json(event)
|
|
183
|
-
except
|
|
183
|
+
except TimeoutError:
|
|
184
184
|
# Send a heartbeat ping to detect dead connections
|
|
185
185
|
try:
|
|
186
186
|
await websocket.send_json({"event_type": "ping"})
|
|
@@ -4,9 +4,6 @@ import json
|
|
|
4
4
|
from decimal import Decimal
|
|
5
5
|
from unittest.mock import MagicMock, patch
|
|
6
6
|
|
|
7
|
-
import pytest
|
|
8
|
-
|
|
9
|
-
|
|
10
7
|
# ---------------------------------------------------------------------------
|
|
11
8
|
# Helpers
|
|
12
9
|
# ---------------------------------------------------------------------------
|
|
@@ -358,7 +355,7 @@ class TestPortfolioCLI:
|
|
|
358
355
|
|
|
359
356
|
class TestBalanceHistory:
|
|
360
357
|
def _make_snapshot(self, address="0x" + "a" * 40, tx_count=3):
|
|
361
|
-
from arc_devkit.analytics.portfolio import
|
|
358
|
+
from arc_devkit.analytics.portfolio import PortfolioSnapshot
|
|
362
359
|
|
|
363
360
|
return PortfolioSnapshot(
|
|
364
361
|
address=address,
|
|
@@ -390,7 +387,7 @@ class TestBalanceHistory:
|
|
|
390
387
|
analyzer.save_snapshot(snap, history_dir=tmp_path)
|
|
391
388
|
analyzer.save_snapshot(snap, history_dir=tmp_path)
|
|
392
389
|
path = tmp_path / f"{snap.address.lower()}.jsonl"
|
|
393
|
-
lines = [
|
|
390
|
+
lines = [line for line in path.read_text().splitlines() if line.strip()]
|
|
394
391
|
assert len(lines) == 2
|
|
395
392
|
|
|
396
393
|
def test_save_snapshot_includes_timestamp(self, tmp_path):
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"""Tests for AsyncBaseAgent, AsyncMonitorAgent, and WebSocket endpoint."""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
5
4
|
|
|
6
5
|
import pytest
|
|
7
6
|
|
|
8
|
-
|
|
9
7
|
# ---------------------------------------------------------------------------
|
|
10
8
|
# Helpers
|
|
11
9
|
# ---------------------------------------------------------------------------
|
|
@@ -235,9 +233,10 @@ async def test_async_monitor_event_stream_yields():
|
|
|
235
233
|
|
|
236
234
|
def test_websocket_monitor_accepts_connection():
|
|
237
235
|
"""WebSocket /agents/monitor/{address} must accept and send heartbeat pings."""
|
|
238
|
-
from arc_devkit.api.main import app
|
|
239
236
|
from fastapi.testclient import TestClient
|
|
240
237
|
|
|
238
|
+
from arc_devkit.api.main import app
|
|
239
|
+
|
|
241
240
|
with patch("arc_devkit.core.connection.get_web3", return_value=_mock_w3()), \
|
|
242
241
|
patch("arc_devkit.agents.async_monitor.AsyncMonitorAgent.execute", new_callable=AsyncMock) as mock_exec:
|
|
243
242
|
mock_exec.return_value = {"status": "done", "iterations": 0}
|
|
@@ -345,7 +344,7 @@ async def test_async_monitor_webhook_called(tmp_path, respx_mock=None):
|
|
|
345
344
|
|
|
346
345
|
|
|
347
346
|
def test_copilot_offline_ask_returns_mock(mock_anthropic):
|
|
348
|
-
from arc_devkit.copilot.agent import
|
|
347
|
+
from arc_devkit.copilot.agent import _OFFLINE_RESPONSE, DevCopilot
|
|
349
348
|
|
|
350
349
|
copilot = DevCopilot(offline=True)
|
|
351
350
|
assert copilot.ask("What is Arc?") == _OFFLINE_RESPONSE
|
|
@@ -353,7 +352,7 @@ def test_copilot_offline_ask_returns_mock(mock_anthropic):
|
|
|
353
352
|
|
|
354
353
|
|
|
355
354
|
def test_copilot_offline_stream_returns_mock(mock_anthropic):
|
|
356
|
-
from arc_devkit.copilot.agent import
|
|
355
|
+
from arc_devkit.copilot.agent import _OFFLINE_RESPONSE, DevCopilot
|
|
357
356
|
|
|
358
357
|
copilot = DevCopilot(offline=True)
|
|
359
358
|
result = "".join(copilot.ask_stream("What is Arc?"))
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"""Unit tests for arc_devkit.cli.flat — arc CLI commands."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
import tempfile
|
|
5
4
|
from decimal import Decimal
|
|
6
|
-
from
|
|
7
|
-
from unittest.mock import MagicMock, patch
|
|
5
|
+
from unittest.mock import patch
|
|
8
6
|
|
|
9
|
-
import pytest
|
|
10
7
|
from typer.testing import CliRunner
|
|
11
8
|
|
|
12
9
|
from arc_devkit.cli.flat import app
|
|
@@ -172,7 +169,6 @@ class TestWallet:
|
|
|
172
169
|
|
|
173
170
|
class TestConfig:
|
|
174
171
|
def test_config_set_and_get(self, tmp_path, monkeypatch):
|
|
175
|
-
env_file = tmp_path / ".env"
|
|
176
172
|
monkeypatch.chdir(tmp_path)
|
|
177
173
|
|
|
178
174
|
# set
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import tempfile
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from unittest.mock import MagicMock
|
|
6
|
+
from unittest.mock import MagicMock
|
|
7
7
|
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
# ---------------------------------------------------------------------------
|
|
12
11
|
# load_abi
|
|
13
12
|
# ---------------------------------------------------------------------------
|
|
@@ -79,9 +78,10 @@ class TestCallView:
|
|
|
79
78
|
assert result == "USDC"
|
|
80
79
|
|
|
81
80
|
def test_checksum_applied_to_address(self):
|
|
82
|
-
from arc_devkit.contracts.loader import call_view
|
|
83
81
|
from web3 import Web3
|
|
84
82
|
|
|
83
|
+
from arc_devkit.contracts.loader import call_view
|
|
84
|
+
|
|
85
85
|
w3 = self._w3_with_result(0)
|
|
86
86
|
abi: list = []
|
|
87
87
|
call_view(abi, "0x" + "a" * 40, "balanceOf", w3=w3)
|
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import tempfile
|
|
5
|
-
from decimal import Decimal
|
|
6
5
|
from unittest.mock import MagicMock, patch
|
|
7
6
|
|
|
8
|
-
import pytest
|
|
9
|
-
|
|
10
|
-
|
|
11
7
|
# ---------------------------------------------------------------------------
|
|
12
8
|
# Helpers
|
|
13
9
|
# ---------------------------------------------------------------------------
|
|
@@ -115,9 +111,10 @@ class TestDecodeRevertBytes:
|
|
|
115
111
|
assert "42" in result
|
|
116
112
|
|
|
117
113
|
def test_custom_error_no_args(self):
|
|
118
|
-
from arc_devkit.debugger.tx_analyzer import decode_revert_bytes
|
|
119
114
|
from web3 import Web3
|
|
120
115
|
|
|
116
|
+
from arc_devkit.debugger.tx_analyzer import decode_revert_bytes
|
|
117
|
+
|
|
121
118
|
abi = [{"type": "error", "name": "Unauthorized", "inputs": []}]
|
|
122
119
|
selector = Web3.keccak(text="Unauthorized()")[:4]
|
|
123
120
|
result = decode_revert_bytes(selector, abi=abi)
|
|
@@ -100,7 +100,7 @@ class TestEventListenerPoll:
|
|
|
100
100
|
log = _make_log(block_number=101)
|
|
101
101
|
w3 = _make_w3(current_block=101, logs=[log])
|
|
102
102
|
el = EventListener(w3=w3, from_block=100)
|
|
103
|
-
|
|
103
|
+
el.poll()
|
|
104
104
|
w3.eth.get_logs.assert_called_once()
|
|
105
105
|
call_params = w3.eth.get_logs.call_args[0][0]
|
|
106
106
|
assert call_params["fromBlock"] == 100
|
|
@@ -182,7 +182,8 @@ class TestEventListenerPoll:
|
|
|
182
182
|
|
|
183
183
|
received: list = []
|
|
184
184
|
bad_cb = MagicMock(side_effect=RuntimeError("boom"))
|
|
185
|
-
good_cb
|
|
185
|
+
def good_cb(e):
|
|
186
|
+
received.append(e)
|
|
186
187
|
|
|
187
188
|
fake_event = {"event": "Transfer", "address": "0x", "args": {}, "block_number": 1, "tx_hash": None, "log_index": 0}
|
|
188
189
|
with patch.object(EventListener, "_decode_log", return_value=fake_event):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Unit tests for arc_devkit.usdc.token.USDCToken."""
|
|
2
2
|
|
|
3
3
|
from decimal import Decimal
|
|
4
|
-
from unittest.mock import MagicMock
|
|
4
|
+
from unittest.mock import MagicMock
|
|
5
5
|
|
|
6
6
|
# Standard Hardhat test private key — valid secp256k1 key safe for tests
|
|
7
7
|
_PRIVKEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
|
|
@@ -34,7 +34,7 @@ def _make_contract_w3(balance_atomic: int = 0) -> MagicMock:
|
|
|
34
34
|
|
|
35
35
|
class TestUSDCToken:
|
|
36
36
|
def _token(self, w3=None, balance_atomic: int = 0, contract_address: str | None = None):
|
|
37
|
-
from arc_devkit.usdc.token import
|
|
37
|
+
from arc_devkit.usdc.token import USDCToken
|
|
38
38
|
|
|
39
39
|
addr = contract_address or "0x" + "c" * 40
|
|
40
40
|
w3 = w3 or _make_contract_w3(balance_atomic)
|
|
@@ -128,9 +128,10 @@ class TestUSDCToken:
|
|
|
128
128
|
# --- contract_address property ---
|
|
129
129
|
|
|
130
130
|
def test_contract_address_property(self):
|
|
131
|
-
from arc_devkit.usdc.token import USDCToken
|
|
132
131
|
from web3 import Web3
|
|
133
132
|
|
|
133
|
+
from arc_devkit.usdc.token import USDCToken
|
|
134
|
+
|
|
134
135
|
addr = "0x" + "d" * 40
|
|
135
136
|
token = USDCToken(contract_address=addr, w3=_make_contract_w3())
|
|
136
137
|
assert token.contract_address == Web3.to_checksum_address(addr)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|