Mandate.Finance
Founding Product Engineer & Technical Co-founder · Mar 2026 – present
Visita il sitoProblema
Costruire una piattaforma DeFi dove agent AI autonomi gestiscono fondi reali on-chain, mantenendo self-custody dell'utente, gasless UX e oversight in tempo reale. Servono guardrail forti perché l'irreversibilità on-chain non perdona.
Architettura
Piattaforma multi-repo distribuita su 11 workspace — 6 servizi runtime su GCP Cloud Run (`backend`, `agent-executor`, `signing-service`, `mcp-gateway`, `jobs`, `stats-api`), una suite di smart contract Foundry (ERC-7821 `KairosBatchExecutor` + `KairosBillingTreasury` UUPS, deployati allo stesso indirizzo su Base, Arbitrum e Ethereum mainnet via CREATE2), un Postgres 16 privato con 59 migration SQL versionate, due frontend Next.js 15 (`ui` e `landing`), un test harness end-to-end basato su SSE e il sito pubblico di docs. Agent layer: un singolo LLM loop in `agent-executor` parametrizzato per 7 job type (deploy / scan / trade / rebalance / report / exit / chat) e ~8 strategy template conservati su Postgres — tool whitelist per task, SELL GUARD a livello di codice e per-vault prompt override su tre tier. L'MCP gateway (Python + FastAPI) intermedia tutte le letture DeFi via JSON-RPC su stdio con request-id correlation per evitare cross-tenant pollution; il signing-service è l'unico custode delle EOA per-agent (keystore AES-256-GCM in Postgres) e l'unica via per le scritture on-chain — ogni scrittura passa da una transazione EIP-7702 Type-4 sponsorata da un singolo hot wallet, con counter atomico per i nonce in Redis così che lo scale-out multi-instance di Cloud Run sia sicuro.
Decisioni chiave
Single agent loop + 7 job type + 8 strategy template
Un singolo LLM loop (`runAgentLoop` in `agent-executor/src/llm/loop.ts`), una sola fonte di verità per i prompt (tabella `strategy_templates` in Postgres), tool whitelist per job type (deploy / scan / trade / rebalance / report / exit / chat) e per-vault prompt override a tre tier. Aggiungere una nuova strategia è una riga SQL, non un servizio. Sostituisce il framing 'multi-agent (evaluator/researcher/manager/executor)': la realtà è più interessante — parametrizzazione runtime, non topologia di ruoli.
Tre layer di difesa contro le hallucination dello step deploy
Quando i modelli più piccoli hallucinavano 'Deployment Complete' senza chiamare `register_vault`, la piattaforma deployava un secondo vault on-chain e bloccava i fondi. Fix a tre layer: (1) auto-register dentro la branch `deploy_vault` dal topic `VaultCreated` parsato dalla receipt del signing-service, (2) `registerVaultInDb` idempotente che short-circuita su duplicate-active, (3) post-loop validator che sovrascrive la decision dichiarata dall'LLM in `'error'` quando `getVaultStatus(vaultId)` mostra pending/null.
SELL GUARD a livello di codice non bypassabile dall'LLM
Sui vault trader (`category.endsWith('-trader')`), quando `factor_swap_openocean` venderebbe il token in trading per il denominator, l'executor chiama `simulate_exit` server-side, calcola il cost basis dai `vault_trades` e blocca lo swap se il PnL supera la soglia di stop-loss. L'LLM non può rilanciare o discutere — vede solo `SELL_BLOCKED`. Origine: deepseek aveva bypassato tre volte il prompt-level mandate → enforcement spostato nel codice.
Custodia EOA per-agent + sponsor paga il gas
Il backend non vede mai chiavi in chiaro: ogni EOA dell'agent è generata e cifrata AES-256-GCM dentro `signing-service/src/keystore.ts`, persistita in `signing_keystores`, decifrata in una cache TTL di 5 minuti. Gli utenti firmano authorization EIP-7702 che delegano a un singolo batch executor allo stesso indirizzo CREATE2 su tre chain; l'hot wallet della piattaforma paga il gas. Risultato: UX gasless self-custody con zero relayer wallet trust nel mezzo.
Sponsor nonce con Redis EVAL Lua atomico
Il `SponsorMutex` in-process serializza solo una replica; lo scaling multi-instance di Cloud Run può andare in race su `getTransactionCount(sponsor, 'pending')`. Fix: counter atomico Redis-backed `sponsor-nonce:{chainId}` driven da uno script `EVAL` Lua (`GET → SET(n+1) → return n` atomico), con cold-start path che legge il nonce dalla chain solo se la key non esiste. Fallback su `Map` in-memory se Redis non è disponibile, con warn log per ops.
MCP-over-stdio multi-tenant con request-id correlation
JSON-RPC naive su singolo pipe stdin/stdout non è sicuro per un gateway multi-tenant: un singolo `console.log` di una qualsiasi dep transitiva polluiva lo stream e produceva data scrambling cross-vault (osservato in produzione il 2026-04-15 — agent A vedeva il `factor_vault_analytics` di agent B). Il gateway loopa su `stdout.readline()` e scarta ogni frame il cui `id` non matcha la richiesta in flight — unica difesa durevole per questa classe di bug multi-tenant.
Leverage watchdog in-process (HF<1.2 emergency exit)
`agent-executor/src/leverage-watchdog.ts`: cron `setInterval` da 60s avviato post-`app.listen`, fermato su SIGTERM. Ogni tick: (1) carica i vault con `mode: leverage`, (2) legge il `min(healthFactor)` su Aave + Morpho via `factor_vault_analytics`, (3) su `HF < config.minHealthFactor` inserisce una row `custom_jobs` con il prompt di exit-leverage schedulato `* * * * *`, (4) sotto la soglia hard 1.2 emette un prompt EMERGENCY-prefixato e `log.error`. Cloud Run a singola replica + guard `inFlight` in-process previene tick concorrenti.
UX gasless con self-custody
Privy + EIP-7702 Type-4 sponsored transactions + ERC-7821 batch executor: l'utente non vede mai una private key e non paga gas, mantenendo self-custody. Cross-chain via LiFi percepito come operazione singola.
Multi-provider LLM cost-driven
Astrazione multi-provider in produzione (DeepInfra come default, OpenRouter come backup). Anthropic resta come strumento di review/research, non nel router LLM in produzione. La migrazione da Ollama Cloud a DeepInfra ha tagliato ~50% del costo. Pattern cost-driven, non capability-driven.