CTRL
CTRL is a Base mainnet automation protocol that compiles natural-language workflow descriptions into on-chain trigger-action vaults. Users sign once via EIP-5792 batch to authorize recurring or event-driven actions (DCA, price-gated swaps, launchpad sniping, whale-following) with pre-set per-swap and daily spend caps, after which a Render-hosted keeper executes autonomously every five seconds. Use it to automate crypto trading strategies without repeated wallet interactions.
git clone --depth 1 https://github.com/aaronjmars/aeon /tmp/ctrl && cp -r /tmp/ctrl/skills/ctrl ~/.claude/skills/ctrlSKILL.md
> **${var}** — Natural-language description of the workflow to build, e.g. `DCA 0.005 ETH into USDC every week`. Required. If empty, log `CTRL_NO_INTENT` and exit cleanly (no notify).
CTRL runs on **Base mainnet** only. It compiles a `trigger → action` graph into a V3 vault, the wallet signs **once** — an EIP-5792 batch deploys the vault and registers spending rules — and a Render-hosted keeper polls every ~5s and executes from there. The security boundary is the user's signature at activate-time, not an API key at create-time, so the create + read REST endpoints are anonymous. Activation requires a real wallet session, which the user provides by opening the CTRL activate landing page in their browser.
Read the last 2 days of `memory/logs/` so a re-run can reference an existing workflow id instead of provisioning a new one.
## Config
- API root = `https://ctrl.build/api/mcp`. No key required for the endpoints used here.
- Chain = Base mainnet (chainId `8453`). Always send `"chain": "base"` and `"network": "mainnet"` — CTRL does NOT support Ethereum, Arbitrum, Solana, or any other chain. If the user asks for another chain, log `CTRL_CHAIN_UNSUPPORTED` and exit before creating anything.
- Sensible defaults the agent should adopt if the user does not state them: per-swap ≤ `0.01 ETH`, per-day ≤ `0.1 ETH`, slippage ≤ `1%` for stable-pair DCA, `15%` only for launchpad sniping. Caps are signed at activate-time, never embedded in the workflow body.
## Steps
### 1. Read the live block catalog
CTRL exposes 24 blocks across four categories (triggers, actions, conditions, utilities). Every key under each node's `data.config` MUST match a `fields[].key` in the catalog — invented keys are silently dropped by the keeper.
```bash
curl -m 10 -s "https://ctrl.build/api/mcp/block-catalog" \
| jq '{
triggers: [.triggers[].id],
actions: [.actions[].id],
conditions: [.conditions[].id],
utilities: [.utilities[].id]
}'
```
Pick the trigger + action ids that match `${var}`. The most common shapes:
- Recurring schedule → `time.interval` (config: `minutes`). The only schedule trigger today — express weekly as `minutes: 10080`, daily as `1440`, hourly as `60`. There is no cron / day-of-week trigger yet.
- Price gate → `price.above` / `price.below` (config: `token`, `threshold`)
- New token launch → `pool.created` (config: `launchpad`, `safetyEnabled: true` for GoPlus honeypot/tax/score gating)
- Swap action → `cypher.swap` (config: `tokenIn`, `tokenOut`, `amount`, `slippage`)
- Telegram alert → `notify.telegram` (config: `message`, `severity`)
If nothing in the catalog matches the intent, log `CTRL_NO_BLOCK_MATCH` and notify the user that the intent is not supported yet.
### 2. Compose the workflow graph
The workflow is a ReactFlow-style `{ nodes, edges }` graph. Each node carries BOTH a top-level `blockType + blockSubtype` (for the create-time validator) AND a `data.blockId + data.subtype` (for the keeper). The two are redundant by design — get them all from the catalog row.
Minimum viable weekly DCA — buy 0.005 ETH of USDC every 10080 minutes, 1% slippage. Write it to `body.json` (step 3 reads that file):
```bash
cat > body.json <<'JSON'
{
"name": "Weekly DCA — ETH to USDC",
"description": "DCA 0.005 ETH into USDC weekly via CTRL",
"chain": "base",
"network": "mainnet",
"workflow_data": {
"nodes": [
{
"id": "t1",
"type": "trigger",
"blockType": "trigger",
"blockSubtype": "interval",
"position": { "x": 200, "y": 200 },
"data": {
"blockId": "time.interval",
"subtype": "interval",
"label": "Every week",
"config": { "minutes": 10080 }
}
},
{
"id": "a1",
"type": "action",
"blockType": "action",
"blockSubtype": "swap",
"position": { "x": 500, "y": 200 },
"data": {
"blockId": "cypher.swap",
"subtype": "swap",
"label": "Buy USDC",
"config": {
"tokenIn": "ETH",
"tokenOut": "USDC",
"amount": 0.005,
"slippage": 1,
"useNativeETH": true
}
}
}
],
"edges": [
{ "id": "e1", "source": "t1", "target": "a1" }
]
}
}
JSON
```
Units the catalog is explicit about:
- `amount` is in **token units** (e.g. `0.005` ETH when `tokenIn` = `ETH`). Beta cap: ≤ 1 ETH-equivalent per swap.
- `slippage` is **percent**, range `0.1–99`. Snipe flows force ≥ 10. For stable-pair DCA pick 0.5–2; for memecoin snipes 10–15.
- The trigger's `minutes` is an integer ≥ 1.
### 3. Create the workflow (draft, anonymous)
```bash
WORKFLOW=$(curl -m 15 -s -X POST "https://ctrl.build/api/mcp/workflows" \
-H "Content-Type: application/json" -d @body.json)
WID=$(printf '%s' "$WORKFLOW" | jq -r '.workflow.id')
[ -n "$WID" ] && [ "$WID" != "null" ] || { echo "CTRL_CREATE_FAILED"; exit 1; }
```
The response is `{ "workflow": { "id", "name", "status", "created_at" } }`. The draft's `user_id` stays NULL until the wallet that signs the activation batch claims it. Drafts that are never activated auto-prune.
### 4. Optional pre-flight — check vault state
`${USER_WALLET}` below is the operator's connected wallet address (the vault owner, a `0x…` address) — read it from `memory/` or ask the user once. Before sending them to sign, surface the vault preview so they know whether a deploy is included and how much ETH to fund:
```bash
USER_WALLET="0x..." # the operator's wallet (vault owner)
curl -s "https://ctrl.build/api/mcp/vault-status?wallet=${USER_WALLET}" | jq '{
vaultExists, vaultAddress, predictedVaultAddress,
ethBalance: .balances.ethDecimal,
wethBalance: .balances.wethDecimal,
ready, warnings
}'
```
If `vaultExists` is `false`, the activate batch will deploy the V3 vault in the same transaction list — fine, but the user must fund it (the user picks `depositEth` on the activate page).
###Mention/keyword sweep on social platforms for [REPLACE: KEYWORDS] — trends, sentiment, top posts
5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates
Curated AI-agent tweets, clustered into narratives with insight summaries
Tracker of AI agent substitution signals — which roles, companies, and industries show real headcount displacement. Named roles + real deployments only.
Competitive-intelligence digest on the AI agent framework space — momentum, releases, breaking changes across a curated watchlist
Cross-domain market pulse from AIXBT's free grounding endpoint — crypto, macro, tradfi, geopolitics. Refreshes taxonomy references (clusters, chains) as a bonus.
Pre-batch API provider health check — detects credit exhaustion or auth failure for every configured provider key before the scheduled batch runs, giving the operator a window to act before skills degrade
List a wallet's live ERC-20 token approvals on Base and flag unlimited / risky spender grants. Keyless via Base RPC (eth_getLogs + eth_call) — no explorer key needed.