arc-devkit 0.2.0__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.
- arc_devkit/__init__.py +3 -0
- arc_devkit/agents/__init__.py +7 -0
- arc_devkit/agents/base_agent.py +87 -0
- arc_devkit/agents/monitor_agent.py +104 -0
- arc_devkit/agents/payment_agent.py +97 -0
- arc_devkit/api/__init__.py +1 -0
- arc_devkit/api/main.py +42 -0
- arc_devkit/api/routes/__init__.py +1 -0
- arc_devkit/api/routes/agents.py +126 -0
- arc_devkit/api/routes/copilot.py +37 -0
- arc_devkit/api/routes/debugger.py +55 -0
- arc_devkit/cli/__init__.py +1 -0
- arc_devkit/cli/commands/__init__.py +1 -0
- arc_devkit/cli/commands/agent.py +176 -0
- arc_devkit/cli/commands/copilot.py +40 -0
- arc_devkit/cli/commands/debug.py +110 -0
- arc_devkit/cli/main.py +69 -0
- arc_devkit/config.py +73 -0
- arc_devkit/copilot/__init__.py +5 -0
- arc_devkit/copilot/agent.py +74 -0
- arc_devkit/core/__init__.py +1 -0
- arc_devkit/core/connection.py +50 -0
- arc_devkit/core/gas.py +62 -0
- arc_devkit/core/wallet.py +72 -0
- arc_devkit/debugger/__init__.py +5 -0
- arc_devkit/debugger/tx_analyzer.py +107 -0
- arc_devkit-0.2.0.dist-info/METADATA +238 -0
- arc_devkit-0.2.0.dist-info/RECORD +31 -0
- arc_devkit-0.2.0.dist-info/WHEEL +5 -0
- arc_devkit-0.2.0.dist-info/entry_points.txt +2 -0
- arc_devkit-0.2.0.dist-info/top_level.txt +1 -0
arc_devkit/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Módulo Agent Starter Kit — templates de agentes econômicos Arc."""
|
|
2
|
+
|
|
3
|
+
from arc_devkit.agents.base_agent import BaseAgent
|
|
4
|
+
from arc_devkit.agents.payment_agent import PaymentAgent
|
|
5
|
+
from arc_devkit.agents.monitor_agent import MonitorAgent
|
|
6
|
+
|
|
7
|
+
__all__ = ["BaseAgent", "PaymentAgent", "MonitorAgent"]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Classe base abstrata para todos os agentes econômicos Arc."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
|
|
6
|
+
from eth_account import Account
|
|
7
|
+
from web3 import Web3
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BaseAgent(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Fundação para todos os agentes econômicos Arc.
|
|
15
|
+
|
|
16
|
+
Gerencia a conexão com a blockchain, a identidade da carteira
|
|
17
|
+
e fornece helpers de logging. Subclasses implementam get_balance()
|
|
18
|
+
e execute() com a lógica específica de cada tipo de agente.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
private_key: str | None = None,
|
|
24
|
+
rpc_url: str | None = None,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Inicializa o agente com carteira e conexão RPC.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
private_key: Chave privada hex (opcional). Se omitida, usa
|
|
31
|
+
ARC_PRIVATE_KEY do ambiente. Sem chave = modo leitura.
|
|
32
|
+
rpc_url: URL do nó RPC (opcional). Se omitida, usa ARC_RPC_URL.
|
|
33
|
+
"""
|
|
34
|
+
from arc_devkit.config import settings
|
|
35
|
+
from arc_devkit.core.connection import get_web3
|
|
36
|
+
|
|
37
|
+
# Configurar conexão com a Arc
|
|
38
|
+
self._w3: Web3 = get_web3()
|
|
39
|
+
|
|
40
|
+
# Resolver chave privada: argumento > variável de ambiente > None
|
|
41
|
+
_chave = private_key or settings.arc_private_key
|
|
42
|
+
|
|
43
|
+
if _chave:
|
|
44
|
+
account = Account.from_key(_chave)
|
|
45
|
+
self._address: str | None = account.address
|
|
46
|
+
self._private_key: str | None = _chave
|
|
47
|
+
logger.info(
|
|
48
|
+
"[%s] Inicializado com carteira %s",
|
|
49
|
+
self.__class__.__name__,
|
|
50
|
+
self._address,
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
self._address = None
|
|
54
|
+
self._private_key = None
|
|
55
|
+
logger.warning(
|
|
56
|
+
"[%s] Sem chave privada — modo somente leitura.",
|
|
57
|
+
self.__class__.__name__,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def wallet_address(self) -> str | None:
|
|
62
|
+
"""Endereço checksummed da carteira do agente."""
|
|
63
|
+
return self._address
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def get_balance(self) -> dict:
|
|
67
|
+
"""
|
|
68
|
+
Retorna o saldo atual da carteira do agente.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Dict com pelo menos 'address' e 'balance_usdc'.
|
|
72
|
+
"""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def execute(self, **kwargs) -> dict:
|
|
77
|
+
"""
|
|
78
|
+
Executa a ação principal do agente.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dict com o resultado da execução, incluindo 'status'.
|
|
82
|
+
"""
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
def log(self, msg: str) -> None:
|
|
86
|
+
"""Helper de log padronizado: prefixo com nome da classe."""
|
|
87
|
+
logger.info("[%s] %s", self.__class__.__name__, msg)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Agente de monitoramento — detecta mudanças de saldo em carteiras Arc."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from web3 import Web3
|
|
9
|
+
|
|
10
|
+
from arc_devkit.agents.base_agent import BaseAgent
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MonitorAgent(BaseAgent):
|
|
16
|
+
"""
|
|
17
|
+
Monitora uma carteira Arc e chama callbacks ao detectar mudanças de saldo.
|
|
18
|
+
|
|
19
|
+
Executa em loop de polling — adequado para MVP. Versões futuras
|
|
20
|
+
utilizarão eth_subscribe para eventos em tempo real via WebSocket.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
watched_address: str,
|
|
26
|
+
interval_seconds: int = 15,
|
|
27
|
+
**kwargs: Any,
|
|
28
|
+
) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Inicializa o agente de monitoramento.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
watched_address: Endereço EVM a monitorar.
|
|
34
|
+
interval_seconds: Intervalo de polling em segundos (padrão: 15).
|
|
35
|
+
**kwargs: Repassado ao BaseAgent (private_key, rpc_url).
|
|
36
|
+
"""
|
|
37
|
+
super().__init__(**kwargs)
|
|
38
|
+
self._watched = Web3.to_checksum_address(watched_address)
|
|
39
|
+
self._interval = interval_seconds
|
|
40
|
+
self._last_balance: int | None = None
|
|
41
|
+
self._running = False
|
|
42
|
+
|
|
43
|
+
def get_balance(self) -> dict:
|
|
44
|
+
"""Retorna o saldo atual da carteira monitorada."""
|
|
45
|
+
wei = self._w3.eth.get_balance(self._watched)
|
|
46
|
+
return {
|
|
47
|
+
"address": self._watched,
|
|
48
|
+
"balance_wei": str(wei),
|
|
49
|
+
"balance_eth": str(self._w3.from_wei(wei, "ether")),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def execute(
|
|
53
|
+
self,
|
|
54
|
+
callback: Callable[[dict], None] | None = None,
|
|
55
|
+
max_iterations: int = 0,
|
|
56
|
+
) -> dict:
|
|
57
|
+
"""
|
|
58
|
+
Inicia o loop de monitoramento da carteira.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
callback: Função chamada com um dict de evento ao detectar mudança.
|
|
62
|
+
O dict contém: address, saldo_anterior, saldo_atual, diferenca_wei.
|
|
63
|
+
max_iterations: Número máximo de iterações (0 = loop infinito).
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Dict com status e total de iterações executadas.
|
|
67
|
+
"""
|
|
68
|
+
self._running = True
|
|
69
|
+
self._last_balance = self._w3.eth.get_balance(self._watched)
|
|
70
|
+
iteracoes = 0
|
|
71
|
+
|
|
72
|
+
self.log(f"Monitorando {self._watched} a cada {self._interval}s")
|
|
73
|
+
|
|
74
|
+
while self._running:
|
|
75
|
+
saldo_atual = self._w3.eth.get_balance(self._watched)
|
|
76
|
+
|
|
77
|
+
if saldo_atual != self._last_balance:
|
|
78
|
+
diferenca = saldo_atual - self._last_balance
|
|
79
|
+
evento = {
|
|
80
|
+
"address": self._watched,
|
|
81
|
+
"saldo_anterior_wei": str(self._last_balance),
|
|
82
|
+
"saldo_atual_wei": str(saldo_atual),
|
|
83
|
+
"diferenca_wei": str(diferenca),
|
|
84
|
+
"tipo": "credito" if diferenca > 0 else "debito",
|
|
85
|
+
}
|
|
86
|
+
self.log(f"Mudança detectada: {diferenca:+d} wei ({evento['tipo']})")
|
|
87
|
+
|
|
88
|
+
if callback:
|
|
89
|
+
callback(evento)
|
|
90
|
+
|
|
91
|
+
self._last_balance = saldo_atual
|
|
92
|
+
|
|
93
|
+
iteracoes += 1
|
|
94
|
+
if max_iterations and iteracoes >= max_iterations:
|
|
95
|
+
break
|
|
96
|
+
|
|
97
|
+
time.sleep(self._interval)
|
|
98
|
+
|
|
99
|
+
return {"status": "finalizado", "iteracoes": iteracoes}
|
|
100
|
+
|
|
101
|
+
def stop(self) -> None:
|
|
102
|
+
"""Interrompe o loop de monitoramento na próxima iteração."""
|
|
103
|
+
self._running = False
|
|
104
|
+
self.log("Monitoramento encerrado.")
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Agente de pagamento Arc — monta e assina transações de transferência."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
|
|
6
|
+
from web3 import Web3
|
|
7
|
+
|
|
8
|
+
from arc_devkit.agents.base_agent import BaseAgent
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PaymentAgent(BaseAgent):
|
|
14
|
+
"""
|
|
15
|
+
Agente responsável por executar pagamentos na rede Arc.
|
|
16
|
+
|
|
17
|
+
Monta, assina e (opcionalmente) envia transações de transferência.
|
|
18
|
+
Projetado para ser reutilizado em fluxos de pagamento recorrente.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def get_balance(self) -> dict:
|
|
22
|
+
"""
|
|
23
|
+
Retorna o saldo nativo da carteira do agente.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Dict com address, balance_wei e balance_usdc (Decimal).
|
|
27
|
+
"""
|
|
28
|
+
if not self._address:
|
|
29
|
+
return {"error": "Nenhuma chave privada configurada — modo somente leitura."}
|
|
30
|
+
|
|
31
|
+
wei = self._w3.eth.get_balance(self._address)
|
|
32
|
+
balance = Decimal(str(self._w3.from_wei(wei, "ether")))
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
"address": self._address,
|
|
36
|
+
"balance_wei": str(wei),
|
|
37
|
+
"balance_usdc": balance,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
def execute(self, to: str, amount_usdc: float, enviar: bool = False) -> dict:
|
|
41
|
+
"""
|
|
42
|
+
Monta e assina uma transação de pagamento.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
to: Endereço EVM do destinatário.
|
|
46
|
+
amount_usdc: Valor a transferir (em USDC).
|
|
47
|
+
enviar: Se True, envia a transação para a rede (requer chave privada).
|
|
48
|
+
Se False (padrão), retorna a transação assinada sem enviar.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Dict com status, dados da transação e hash (se enviada).
|
|
52
|
+
"""
|
|
53
|
+
if not self._private_key:
|
|
54
|
+
return {"status": "erro", "error": "Chave privada necessária para assinar transações."}
|
|
55
|
+
|
|
56
|
+
destinatario = Web3.to_checksum_address(to)
|
|
57
|
+
self.log(f"Preparando pagamento de {amount_usdc} USDC → {destinatario}")
|
|
58
|
+
|
|
59
|
+
# Montar transação
|
|
60
|
+
nonce = self._w3.eth.get_transaction_count(self._address)
|
|
61
|
+
tx = {
|
|
62
|
+
"from": self._address,
|
|
63
|
+
"to": destinatario,
|
|
64
|
+
"value": self._w3.to_wei(amount_usdc, "ether"), # placeholder: USDC nativo
|
|
65
|
+
"gas": 21_000,
|
|
66
|
+
"gasPrice": self._w3.eth.gas_price,
|
|
67
|
+
"nonce": nonce,
|
|
68
|
+
"chainId": self._w3.eth.chain_id,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Assinar a transação com a chave privada
|
|
72
|
+
signed = self._w3.eth.account.sign_transaction(tx, self._private_key)
|
|
73
|
+
self.log("Transação assinada com sucesso.")
|
|
74
|
+
|
|
75
|
+
if enviar:
|
|
76
|
+
# Enviar para a rede Arc
|
|
77
|
+
tx_hash = self._w3.eth.send_raw_transaction(signed.raw_transaction)
|
|
78
|
+
tx_hash_hex = tx_hash.hex()
|
|
79
|
+
self.log(f"Transação enviada: {tx_hash_hex}")
|
|
80
|
+
return {
|
|
81
|
+
"status": "enviada",
|
|
82
|
+
"from": self._address,
|
|
83
|
+
"to": destinatario,
|
|
84
|
+
"amount_usdc": amount_usdc,
|
|
85
|
+
"tx_hash": tx_hash_hex,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Modo padrão: retornar transação assinada sem enviar
|
|
89
|
+
# TODO: remova enviar=False e passe enviar=True para envio real
|
|
90
|
+
return {
|
|
91
|
+
"status": "assinada",
|
|
92
|
+
"from": self._address,
|
|
93
|
+
"to": destinatario,
|
|
94
|
+
"amount_usdc": amount_usdc,
|
|
95
|
+
"raw_transaction": signed.raw_transaction.hex(),
|
|
96
|
+
"nota": "Transação assinada. Passe enviar=True para enviar à rede.",
|
|
97
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Módulo API — interface REST FastAPI do Arc DevKit."""
|
arc_devkit/api/main.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""API REST do Arc DevKit — FastAPI."""
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI
|
|
4
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
5
|
+
|
|
6
|
+
from arc_devkit import __version__
|
|
7
|
+
from arc_devkit.api.routes import agents, copilot, debugger
|
|
8
|
+
|
|
9
|
+
app = FastAPI(
|
|
10
|
+
title="Arc DevKit API",
|
|
11
|
+
description=(
|
|
12
|
+
"API REST para ferramentas de desenvolvimento na Arc blockchain. "
|
|
13
|
+
"Expõe os módulos Dev Copilot, Agent Kit e Tx Debugger via HTTP."
|
|
14
|
+
),
|
|
15
|
+
version=__version__,
|
|
16
|
+
docs_url="/docs",
|
|
17
|
+
redoc_url="/redoc",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# CORS para desenvolvimento local (ajuste allow_origins em produção)
|
|
21
|
+
app.add_middleware(
|
|
22
|
+
CORSMiddleware,
|
|
23
|
+
allow_origins=[
|
|
24
|
+
"http://localhost:3000", # React CRA
|
|
25
|
+
"http://localhost:5173", # Vite
|
|
26
|
+
"http://localhost:8080",
|
|
27
|
+
],
|
|
28
|
+
allow_credentials=True,
|
|
29
|
+
allow_methods=["*"],
|
|
30
|
+
allow_headers=["*"],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Registrar routers
|
|
34
|
+
app.include_router(copilot.router, prefix="/copilot", tags=["Dev Copilot"])
|
|
35
|
+
app.include_router(agents.router, prefix="/agents", tags=["Agents"])
|
|
36
|
+
app.include_router(debugger.router, prefix="/debug", tags=["Tx Debugger"])
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@app.get("/health", tags=["Infra"])
|
|
40
|
+
def health() -> dict:
|
|
41
|
+
"""Verifica se a API está respondendo."""
|
|
42
|
+
return {"status": "ok", "version": __version__}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Rotas da API Arc DevKit."""
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Rotas API para operações de carteira e agentes Arc."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, HTTPException
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
router = APIRouter()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class WalletResponse(BaseModel):
|
|
10
|
+
"""Dados de uma carteira criada ou consultada."""
|
|
11
|
+
|
|
12
|
+
address: str
|
|
13
|
+
balance_wei: str | None = None
|
|
14
|
+
balance_usdc: str | None = None
|
|
15
|
+
private_key: str | None = None # presente apenas na criação
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PaymentRequest(BaseModel):
|
|
19
|
+
"""Corpo da requisição de pagamento."""
|
|
20
|
+
|
|
21
|
+
to: str = Field(..., description="Endereço EVM de destino.")
|
|
22
|
+
amount_usdc: float = Field(..., gt=0, description="Valor a transferir (em USDC).")
|
|
23
|
+
private_key: str = Field(..., description="Chave privada do remetente (hex).")
|
|
24
|
+
enviar: bool = Field(False, description="Se True, envia à rede; caso contrário retorna tx assinada.")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PaymentResponse(BaseModel):
|
|
28
|
+
"""Resultado de um pagamento."""
|
|
29
|
+
|
|
30
|
+
status: str
|
|
31
|
+
from_address: str | None = Field(None, alias="from")
|
|
32
|
+
to: str | None = None
|
|
33
|
+
amount_usdc: float | None = None
|
|
34
|
+
tx_hash: str | None = None
|
|
35
|
+
raw_transaction: str | None = None
|
|
36
|
+
nota: str | None = None
|
|
37
|
+
|
|
38
|
+
model_config = {"populate_by_name": True}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class BlockResponse(BaseModel):
|
|
42
|
+
"""Número do bloco atual na Arc."""
|
|
43
|
+
|
|
44
|
+
block_number: int
|
|
45
|
+
chain_id: int
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@router.post("/wallet", response_model=WalletResponse, summary="Criar nova carteira")
|
|
49
|
+
async def create_wallet() -> WalletResponse:
|
|
50
|
+
"""
|
|
51
|
+
Cria uma nova carteira EVM para uso na Arc.
|
|
52
|
+
|
|
53
|
+
A chave privada retornada é gerada localmente — armazene com segurança.
|
|
54
|
+
Este endpoint não armazena a chave privada.
|
|
55
|
+
"""
|
|
56
|
+
from arc_devkit.core.wallet import create_wallet
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
carteira = create_wallet()
|
|
60
|
+
return WalletResponse(**carteira)
|
|
61
|
+
except Exception as exc:
|
|
62
|
+
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@router.get(
|
|
66
|
+
"/balance/{address}",
|
|
67
|
+
response_model=WalletResponse,
|
|
68
|
+
summary="Consultar saldo de carteira",
|
|
69
|
+
)
|
|
70
|
+
async def get_balance(address: str) -> WalletResponse:
|
|
71
|
+
"""Retorna o saldo nativo de um endereço Arc."""
|
|
72
|
+
from arc_devkit.core.wallet import get_balance
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
resultado = get_balance(address)
|
|
76
|
+
return WalletResponse(
|
|
77
|
+
address=resultado["address"],
|
|
78
|
+
balance_wei=resultado["balance_wei"],
|
|
79
|
+
balance_usdc=str(resultado["balance_usdc"]),
|
|
80
|
+
)
|
|
81
|
+
except Exception as exc:
|
|
82
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@router.post("/payment", response_model=PaymentResponse, summary="Executar pagamento")
|
|
86
|
+
async def payment(body: PaymentRequest) -> PaymentResponse:
|
|
87
|
+
"""
|
|
88
|
+
Prepara e (opcionalmente) envia um pagamento na Arc.
|
|
89
|
+
|
|
90
|
+
Com `enviar=false` (padrão), retorna a transação assinada sem enviá-la.
|
|
91
|
+
Com `enviar=true`, transmite à rede e retorna o tx_hash.
|
|
92
|
+
"""
|
|
93
|
+
from arc_devkit.agents.payment_agent import PaymentAgent
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
agente = PaymentAgent(private_key=body.private_key)
|
|
97
|
+
resultado = agente.execute(to=body.to, amount_usdc=body.amount_usdc, enviar=body.enviar)
|
|
98
|
+
|
|
99
|
+
if resultado.get("status") == "erro":
|
|
100
|
+
raise HTTPException(status_code=400, detail=resultado.get("error"))
|
|
101
|
+
|
|
102
|
+
return PaymentResponse(
|
|
103
|
+
status=resultado["status"],
|
|
104
|
+
**{"from": resultado.get("from")},
|
|
105
|
+
to=resultado.get("to"),
|
|
106
|
+
amount_usdc=resultado.get("amount_usdc"),
|
|
107
|
+
tx_hash=resultado.get("tx_hash"),
|
|
108
|
+
raw_transaction=resultado.get("raw_transaction"),
|
|
109
|
+
nota=resultado.get("nota"),
|
|
110
|
+
)
|
|
111
|
+
except HTTPException:
|
|
112
|
+
raise
|
|
113
|
+
except Exception as exc:
|
|
114
|
+
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@router.get("/block", response_model=BlockResponse, summary="Bloco atual da Arc")
|
|
118
|
+
async def get_block() -> BlockResponse:
|
|
119
|
+
"""Retorna o número do bloco mais recente e o chain ID da Arc."""
|
|
120
|
+
from arc_devkit.core.connection import get_web3
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
w3 = get_web3()
|
|
124
|
+
return BlockResponse(block_number=w3.eth.block_number, chain_id=w3.eth.chain_id)
|
|
125
|
+
except Exception as exc:
|
|
126
|
+
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Rotas API para o Dev Copilot."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, HTTPException
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
router = APIRouter()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AskRequest(BaseModel):
|
|
10
|
+
"""Corpo da requisição para o endpoint /copilot/ask."""
|
|
11
|
+
|
|
12
|
+
prompt: str = Field(..., min_length=3, description="Pergunta ou instrução.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AskResponse(BaseModel):
|
|
16
|
+
"""Resposta do Dev Copilot."""
|
|
17
|
+
|
|
18
|
+
response: str = Field(..., description="Resposta gerada pelo modelo.")
|
|
19
|
+
model: str = Field(..., description="Identificador do modelo usado.")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@router.post("/ask", response_model=AskResponse, summary="Perguntar ao Dev Copilot")
|
|
23
|
+
async def ask(body: AskRequest) -> AskResponse:
|
|
24
|
+
"""
|
|
25
|
+
Envia uma pergunta ao Dev Copilot e retorna a resposta.
|
|
26
|
+
|
|
27
|
+
O Dev Copilot é especializado em desenvolvimento na Arc blockchain:
|
|
28
|
+
Solidity, web3.py, USDC, Circle Agent Stack e agentes econômicos.
|
|
29
|
+
"""
|
|
30
|
+
from arc_devkit.copilot.agent import DevCopilot
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
copilot = DevCopilot()
|
|
34
|
+
resposta = copilot.ask(body.prompt)
|
|
35
|
+
return AskResponse(response=resposta, model=DevCopilot.MODEL)
|
|
36
|
+
except Exception as exc:
|
|
37
|
+
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Rotas API para o Tx Debugger."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, HTTPException, Query
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
router = APIRouter()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GasEstimateResponse(BaseModel):
|
|
10
|
+
"""Estimativa de custo de gás para uma transferência."""
|
|
11
|
+
|
|
12
|
+
gas_limit: int
|
|
13
|
+
gas_price_gwei: str
|
|
14
|
+
gas_price_wei: str
|
|
15
|
+
custo_usdc: str
|
|
16
|
+
custo_wei: str
|
|
17
|
+
amount_usdc: float
|
|
18
|
+
to: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@router.get("/estimate", response_model=GasEstimateResponse, summary="Estimar custo de gás")
|
|
22
|
+
async def estimate_gas(
|
|
23
|
+
to: str = Query(..., description="Endereço EVM de destino."),
|
|
24
|
+
amount: float = Query(..., gt=0, description="Valor a transferir (em USDC)."),
|
|
25
|
+
from_address: str = Query("", description="Endereço remetente (opcional)."),
|
|
26
|
+
) -> GasEstimateResponse:
|
|
27
|
+
"""
|
|
28
|
+
Estima o custo de gás para uma transferência nativa na Arc.
|
|
29
|
+
|
|
30
|
+
Útil para exibir o custo ao usuário antes de confirmar uma transação.
|
|
31
|
+
"""
|
|
32
|
+
from arc_devkit.core.gas import estimate_transfer
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
est = estimate_transfer(to, amount, from_address or None)
|
|
36
|
+
return GasEstimateResponse(**est)
|
|
37
|
+
except Exception as exc:
|
|
38
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@router.get("/{tx_hash}", summary="Analisar transação")
|
|
42
|
+
async def analyze(tx_hash: str) -> dict:
|
|
43
|
+
"""
|
|
44
|
+
Analisa uma transação Arc e retorna diagnóstico completo.
|
|
45
|
+
|
|
46
|
+
Combina dados do RPC (receipt + trace) com análise do Dev Copilot
|
|
47
|
+
para gerar um relatório em linguagem natural em português.
|
|
48
|
+
"""
|
|
49
|
+
from arc_devkit.debugger.tx_analyzer import TxAnalyzer
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
analyzer = TxAnalyzer()
|
|
53
|
+
return analyzer.analyze(tx_hash)
|
|
54
|
+
except Exception as exc:
|
|
55
|
+
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Módulo CLI — interface de linha de comando do Arc DevKit."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Comandos da CLI Arc DevKit."""
|