spendos 0.1.0
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.
- package/.dockerignore +4 -0
- package/.env.example +30 -0
- package/AGENTS.md +212 -0
- package/BOOTSTRAP.md +55 -0
- package/Dockerfile +52 -0
- package/HEARTBEAT.md +7 -0
- package/IDENTITY.md +23 -0
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/SOUL.md +202 -0
- package/SUBMISSION.md +128 -0
- package/TOOLS.md +40 -0
- package/USER.md +17 -0
- package/acp-seller/bin/acp.ts +807 -0
- package/acp-seller/config.json +34 -0
- package/acp-seller/package.json +55 -0
- package/acp-seller/src/commands/agent.ts +328 -0
- package/acp-seller/src/commands/bounty.ts +1189 -0
- package/acp-seller/src/commands/deploy.ts +414 -0
- package/acp-seller/src/commands/job.ts +217 -0
- package/acp-seller/src/commands/profile.ts +71 -0
- package/acp-seller/src/commands/resource.ts +91 -0
- package/acp-seller/src/commands/search.ts +327 -0
- package/acp-seller/src/commands/sell.ts +883 -0
- package/acp-seller/src/commands/serve.ts +258 -0
- package/acp-seller/src/commands/setup.ts +399 -0
- package/acp-seller/src/commands/token.ts +88 -0
- package/acp-seller/src/commands/wallet.ts +123 -0
- package/acp-seller/src/lib/api.ts +118 -0
- package/acp-seller/src/lib/auth.ts +291 -0
- package/acp-seller/src/lib/bounty.ts +257 -0
- package/acp-seller/src/lib/client.ts +42 -0
- package/acp-seller/src/lib/config.ts +240 -0
- package/acp-seller/src/lib/open.ts +41 -0
- package/acp-seller/src/lib/openclawCron.ts +138 -0
- package/acp-seller/src/lib/output.ts +104 -0
- package/acp-seller/src/lib/wallet.ts +81 -0
- package/acp-seller/src/seller/offerings/_shared/preTransactionScan.ts +127 -0
- package/acp-seller/src/seller/offerings/canonical-catalog.ts +221 -0
- package/acp-seller/src/seller/offerings/spendos/spendos_summarize_url/handlers.ts +20 -0
- package/acp-seller/src/seller/offerings/spendos/spendos_summarize_url/offering.json +18 -0
- package/acp-seller/src/seller/offerings/spendos/spendos_translate/handlers.ts +21 -0
- package/acp-seller/src/seller/offerings/spendos/spendos_translate/offering.json +22 -0
- package/acp-seller/src/seller/offerings/spendos/spendos_tweet_gen/handlers.ts +20 -0
- package/acp-seller/src/seller/offerings/spendos/spendos_tweet_gen/offering.json +18 -0
- package/acp-seller/src/seller/runtime/acpSocket.ts +413 -0
- package/acp-seller/src/seller/runtime/logger.ts +36 -0
- package/acp-seller/src/seller/runtime/offeringTypes.ts +52 -0
- package/acp-seller/src/seller/runtime/offerings.ts +277 -0
- package/acp-seller/src/seller/runtime/paymentVerification.test.ts +207 -0
- package/acp-seller/src/seller/runtime/paymentVerification.ts +363 -0
- package/acp-seller/src/seller/runtime/seller.onchain.test.ts +220 -0
- package/acp-seller/src/seller/runtime/seller.test.ts +823 -0
- package/acp-seller/src/seller/runtime/seller.ts +1041 -0
- package/acp-seller/src/seller/runtime/sellerApi.ts +71 -0
- package/acp-seller/src/seller/runtime/startup.ts +270 -0
- package/acp-seller/src/seller/runtime/types.ts +62 -0
- package/acp-seller/tsconfig.json +20 -0
- package/bin/spendos.js +23 -0
- package/contracts/SpendOSAudit.sol +29 -0
- package/dist/mcp-server.mjs +153 -0
- package/jobs/translate.json +7 -0
- package/jobs/tweet-gen.json +7 -0
- package/openclaw.json +41 -0
- package/package.json +49 -0
- package/plugins/spendos-events/index.ts +78 -0
- package/plugins/spendos-events/package.json +14 -0
- package/policies/enforce-bounds.mjs +71 -0
- package/public/index.html +509 -0
- package/public/landing.html +241 -0
- package/railway.json +12 -0
- package/railway.toml +12 -0
- package/scripts/deploy.ts +48 -0
- package/scripts/test-x402-mainnet.ts +30 -0
- package/scripts/xmtp-listener.ts +61 -0
- package/setup.sh +278 -0
- package/skills/spendos/skill.md +26 -0
- package/src/agent.ts +152 -0
- package/src/audit.ts +166 -0
- package/src/governance.ts +367 -0
- package/src/job-registry.ts +306 -0
- package/src/mcp-public.ts +145 -0
- package/src/mcp-server.ts +171 -0
- package/src/opportunity-scanner.ts +138 -0
- package/src/server.ts +870 -0
- package/src/venice-x402.ts +234 -0
- package/src/xmtp.ts +109 -0
- package/src/zerion.ts +58 -0
- package/start.sh +168 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" class="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>SpendOS — Autonomous Agent Economy</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<script>
|
|
9
|
+
tailwind.config = {
|
|
10
|
+
darkMode: 'class',
|
|
11
|
+
theme: { extend: { colors: {
|
|
12
|
+
bg: '#050505', surface: '#0a0a0a', border: '#1a1a1a',
|
|
13
|
+
accent: '#22c55e', danger: '#ef4444', warn: '#f59e0b',
|
|
14
|
+
}}}
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
<style>
|
|
18
|
+
body { font-family: 'SF Mono', 'Fira Code', monospace; }
|
|
19
|
+
.countdown { font-variant-numeric: tabular-nums; }
|
|
20
|
+
@keyframes pulse-glow { 0%,100% { opacity:0.4 } 50% { opacity:1 } }
|
|
21
|
+
.pulse-glow { animation: pulse-glow 3s infinite; }
|
|
22
|
+
.stat-value { font-size: 2.5rem; font-weight: 700; line-height: 1; }
|
|
23
|
+
@keyframes ticker { 0% { opacity: 0; transform: translateY(10px); } 100% { opacity: 1; transform: translateY(0); } }
|
|
24
|
+
.ticker { animation: ticker 0.5s ease-out; }
|
|
25
|
+
</style>
|
|
26
|
+
</head>
|
|
27
|
+
<body class="bg-bg text-gray-300 min-h-screen">
|
|
28
|
+
|
|
29
|
+
<!-- Hero -->
|
|
30
|
+
<header class="text-center py-16 px-6">
|
|
31
|
+
<div class="flex items-center justify-center gap-3 mb-4">
|
|
32
|
+
<div class="w-3 h-3 rounded-full bg-accent pulse-glow"></div>
|
|
33
|
+
<h1 class="text-4xl font-bold text-white tracking-tight">SpendOS</h1>
|
|
34
|
+
</div>
|
|
35
|
+
<p class="text-lg text-gray-400 max-w-2xl mx-auto">
|
|
36
|
+
The first autonomous agent that governs its own spending.
|
|
37
|
+
Earns USDC. Self-funds inference. Proposes investments. All on-chain.
|
|
38
|
+
</p>
|
|
39
|
+
<p class="text-sm text-gray-600 mt-4">Live on Base mainnet · Powered by OWS + Venice + OpenClaw</p>
|
|
40
|
+
</header>
|
|
41
|
+
|
|
42
|
+
<!-- Live Metrics -->
|
|
43
|
+
<main class="max-w-4xl mx-auto px-6 pb-16">
|
|
44
|
+
|
|
45
|
+
<section class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-12">
|
|
46
|
+
<div class="bg-surface border border-border rounded-lg p-6 text-center">
|
|
47
|
+
<div class="text-xs text-gray-500 uppercase tracking-wider mb-2">Revenue</div>
|
|
48
|
+
<div id="stat-earned" class="stat-value text-accent">—</div>
|
|
49
|
+
<div class="text-xs text-gray-600 mt-1">USDC earned</div>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="bg-surface border border-border rounded-lg p-6 text-center">
|
|
52
|
+
<div class="text-xs text-gray-500 uppercase tracking-wider mb-2">Costs</div>
|
|
53
|
+
<div id="stat-spent" class="stat-value text-danger">—</div>
|
|
54
|
+
<div class="text-xs text-gray-600 mt-1">USDC spent</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="bg-surface border border-border rounded-lg p-6 text-center">
|
|
57
|
+
<div class="text-xs text-gray-500 uppercase tracking-wider mb-2">Profit</div>
|
|
58
|
+
<div id="stat-profit" class="stat-value text-white">—</div>
|
|
59
|
+
<div class="text-xs text-gray-600 mt-1">USDC net</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="bg-surface border border-border rounded-lg p-6 text-center">
|
|
62
|
+
<div class="text-xs text-gray-500 uppercase tracking-wider mb-2">Queries</div>
|
|
63
|
+
<div id="stat-queries" class="stat-value text-warn">—</div>
|
|
64
|
+
<div class="text-xs text-gray-600 mt-1">served</div>
|
|
65
|
+
</div>
|
|
66
|
+
</section>
|
|
67
|
+
|
|
68
|
+
<!-- How it works -->
|
|
69
|
+
<section class="bg-surface border border-border rounded-lg p-8 mb-8">
|
|
70
|
+
<h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">How it works</h2>
|
|
71
|
+
<div class="space-y-3 text-sm">
|
|
72
|
+
<div class="flex items-start gap-3">
|
|
73
|
+
<span class="text-accent font-bold">1.</span>
|
|
74
|
+
<span>Agent sells AI compute (summarization, image generation) via <span class="text-white">x402 micropayments</span></span>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="flex items-start gap-3">
|
|
77
|
+
<span class="text-accent font-bold">2.</span>
|
|
78
|
+
<span>Agent self-funds <span class="text-white">Venice AI</span> inference using its own USDC (wallet auth, no API keys)</span>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="flex items-start gap-3">
|
|
81
|
+
<span class="text-accent font-bold">3.</span>
|
|
82
|
+
<span>Agent scans for <span class="text-white">any profitable on-chain opportunity</span> — staking, LPs, swaps, yield — and proposes delegations</span>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="flex items-start gap-3">
|
|
85
|
+
<span class="text-accent font-bold">4.</span>
|
|
86
|
+
<span>Owner approves/rejects via governance dashboard. Every decision <span class="text-white">audited on Base mainnet</span></span>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="flex items-start gap-3">
|
|
89
|
+
<span class="text-accent font-bold">5.</span>
|
|
90
|
+
<span><span class="text-white">Dead man's switch</span> auto-revokes expired delegations. Agent can never overspend.</span>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</section>
|
|
94
|
+
|
|
95
|
+
<!-- Agent Identity -->
|
|
96
|
+
<section class="bg-surface border border-border rounded-lg p-8 mb-8">
|
|
97
|
+
<h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">Agent Identity</h2>
|
|
98
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-xs">
|
|
99
|
+
<div>
|
|
100
|
+
<span class="text-gray-500">OWS Wallet</span>
|
|
101
|
+
<div class="text-white font-mono mt-1" id="stat-wallet">0x68c7...35A1</div>
|
|
102
|
+
</div>
|
|
103
|
+
<div>
|
|
104
|
+
<span class="text-gray-500">Inference</span>
|
|
105
|
+
<div class="text-white mt-1">Venice AI (x402 wallet auth, self-funded)</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div>
|
|
108
|
+
<span class="text-gray-500">Audit Contract</span>
|
|
109
|
+
<div class="text-white font-mono mt-1">
|
|
110
|
+
<a href="https://basescan.org/address/0xF74b481c9f196b5988cAA28Fb1452338597670B6" target="_blank" class="text-blue-400 hover:underline">0xF74b...70B6</a>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div>
|
|
114
|
+
<span class="text-gray-500">Runtime</span>
|
|
115
|
+
<div class="text-white mt-1">OpenClaw + SpendOS on Railway</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</section>
|
|
119
|
+
|
|
120
|
+
<!-- Hackathon Tracks -->
|
|
121
|
+
<section class="bg-surface border border-border rounded-lg p-8 mb-8">
|
|
122
|
+
<h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">OWS Hackathon 2026 Tracks</h2>
|
|
123
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
124
|
+
<div class="flex items-center gap-3 bg-bg border border-accent/30 rounded-lg px-4 py-3">
|
|
125
|
+
<span class="text-accent text-lg">■</span>
|
|
126
|
+
<div>
|
|
127
|
+
<div class="text-white text-sm font-medium">Agent Spend Governance & Identity</div>
|
|
128
|
+
<div class="text-gray-500 text-xs">OWS delegations, session keys, dead man's switch</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="flex items-center gap-3 bg-bg border border-accent/30 rounded-lg px-4 py-3">
|
|
132
|
+
<span class="text-accent text-lg">■</span>
|
|
133
|
+
<div>
|
|
134
|
+
<div class="text-white text-sm font-medium">Pay-Per-Call Services & API Monetization</div>
|
|
135
|
+
<div class="text-gray-500 text-xs">x402 micropayments, dynamic job registry</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="flex items-center gap-3 bg-bg border border-accent/30 rounded-lg px-4 py-3">
|
|
139
|
+
<span class="text-accent text-lg">■</span>
|
|
140
|
+
<div>
|
|
141
|
+
<div class="text-white text-sm font-medium">Multi-Agent Systems & Autonomous Economies</div>
|
|
142
|
+
<div class="text-gray-500 text-xs">Self-funding inference, autonomous revenue streams</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="flex items-center gap-3 bg-bg border border-accent/30 rounded-lg px-4 py-3">
|
|
146
|
+
<span class="text-accent text-lg">■</span>
|
|
147
|
+
<div>
|
|
148
|
+
<div class="text-white text-sm font-medium">Creative / Unhinged</div>
|
|
149
|
+
<div class="text-gray-500 text-xs">An AI that runs a business and can't be stopped</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</section>
|
|
154
|
+
|
|
155
|
+
<!-- Roadmap -->
|
|
156
|
+
<section class="bg-surface border border-border rounded-lg p-8 mb-8">
|
|
157
|
+
<h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">Roadmap</h2>
|
|
158
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
159
|
+
<div class="bg-bg border border-blue-500/20 rounded-lg p-5">
|
|
160
|
+
<div class="flex items-center gap-2 mb-2">
|
|
161
|
+
<svg class="w-5 h-5 text-blue-400" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.64 6.8c-.15 1.58-.8 5.42-1.13 7.19-.14.75-.42 1-.68 1.03-.58.05-1.02-.38-1.58-.75-.88-.58-1.38-.94-2.23-1.5-.99-.65-.35-1.01.22-1.59.15-.15 2.71-2.48 2.76-2.69a.2.2 0 00-.05-.18c-.06-.05-.14-.03-.21-.02-.09.02-1.49.95-4.22 2.79-.4.27-.76.41-1.08.4-.36-.01-1.04-.2-1.55-.37-.63-.2-1.12-.31-1.08-.66.02-.18.27-.36.74-.55 2.92-1.27 4.86-2.11 5.83-2.51 2.78-1.16 3.35-1.36 3.73-1.36.08 0 .27.02.39.12.1.08.13.19.14.27-.01.06.01.24 0 .38z"/></svg>
|
|
162
|
+
<span class="text-white text-sm font-semibold">Telegram</span>
|
|
163
|
+
</div>
|
|
164
|
+
<p class="text-gray-500 text-xs">Talk to your SpendOS agent from Telegram. Approve delegations, check P&L, get alerts -- all from your phone.</p>
|
|
165
|
+
<div class="mt-3 text-[10px] text-blue-400">setup.sh --connect telegram</div>
|
|
166
|
+
</div>
|
|
167
|
+
<div class="bg-bg border border-purple-500/20 rounded-lg p-5">
|
|
168
|
+
<div class="flex items-center gap-2 mb-2">
|
|
169
|
+
<svg class="w-5 h-5 text-purple-400" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03z"/></svg>
|
|
170
|
+
<span class="text-white text-sm font-semibold">Discord</span>
|
|
171
|
+
</div>
|
|
172
|
+
<p class="text-gray-500 text-xs">Run SpendOS as a Discord bot. Your server members can interact with the agent, and you govern its wallet from any channel.</p>
|
|
173
|
+
<div class="mt-3 text-[10px] text-purple-400">setup.sh --connect discord</div>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="bg-bg border border-green-500/20 rounded-lg p-5">
|
|
176
|
+
<div class="flex items-center gap-2 mb-2">
|
|
177
|
+
<svg class="w-5 h-5 text-green-400" viewBox="0 0 24 24" fill="currentColor"><path d="M5.042 15.165a2.528 2.528 0 01-2.52 2.523A2.528 2.528 0 010 15.165a2.527 2.527 0 012.522-2.52h2.52v2.52zm1.271 0a2.527 2.527 0 012.521-2.52 2.527 2.527 0 012.521 2.52v6.313A2.528 2.528 0 018.834 24a2.528 2.528 0 01-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 01-2.521-2.52A2.528 2.528 0 018.834 0a2.528 2.528 0 012.521 2.522v2.52H8.834zm0 1.271a2.528 2.528 0 012.521 2.521 2.528 2.528 0 01-2.521 2.521H2.522A2.528 2.528 0 010 8.834a2.528 2.528 0 012.522-2.521h6.312zm10.122 2.521a2.528 2.528 0 012.522-2.521A2.528 2.528 0 0124 8.834a2.528 2.528 0 01-2.522 2.521h-2.522V8.834zm-1.268 0a2.528 2.528 0 01-2.523 2.521 2.527 2.527 0 01-2.52-2.521V2.522A2.527 2.527 0 0115.165 0a2.528 2.528 0 012.523 2.522v6.312zm-2.523 10.122a2.528 2.528 0 012.523 2.522A2.528 2.528 0 0115.165 24a2.527 2.527 0 01-2.52-2.522v-2.522h2.52zm0-1.268a2.527 2.527 0 01-2.52-2.523 2.526 2.526 0 012.52-2.52h6.313A2.527 2.527 0 0124 15.165a2.528 2.528 0 01-2.522 2.523h-6.313z"/></svg>
|
|
178
|
+
<span class="text-white text-sm font-semibold">Slack</span>
|
|
179
|
+
</div>
|
|
180
|
+
<p class="text-gray-500 text-xs">Deploy SpendOS to your Slack workspace. Manage your agent's economy from threads — delegation approvals via reactions.</p>
|
|
181
|
+
<div class="mt-3 text-[10px] text-green-400">setup.sh --connect slack</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</section>
|
|
185
|
+
|
|
186
|
+
<!-- Partner Tools -->
|
|
187
|
+
<section class="bg-surface border border-border rounded-lg p-8 mb-8">
|
|
188
|
+
<h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">Built With</h2>
|
|
189
|
+
<div class="flex flex-wrap gap-2">
|
|
190
|
+
<span class="px-3 py-1 bg-bg border border-border rounded text-xs">OWS</span>
|
|
191
|
+
<span class="px-3 py-1 bg-bg border border-border rounded text-xs">MoonPay</span>
|
|
192
|
+
<span class="px-3 py-1 bg-bg border border-border rounded text-xs">OpenClaw</span>
|
|
193
|
+
<span class="px-3 py-1 bg-bg border border-border rounded text-xs">Venice AI</span>
|
|
194
|
+
<span class="px-3 py-1 bg-bg border border-border rounded text-xs">x402</span>
|
|
195
|
+
<span class="px-3 py-1 bg-bg border border-border rounded text-xs">Zerion</span>
|
|
196
|
+
<span class="px-3 py-1 bg-bg border border-border rounded text-xs">Base</span>
|
|
197
|
+
<span class="px-3 py-1 bg-bg border border-border rounded text-xs">XMTP</span>
|
|
198
|
+
</div>
|
|
199
|
+
</section>
|
|
200
|
+
|
|
201
|
+
<!-- GitHub -->
|
|
202
|
+
<section class="text-center mb-8">
|
|
203
|
+
<a href="https://github.com/consensus-hq/spendos" target="_blank" class="inline-flex items-center gap-2 px-6 py-3 bg-white text-black rounded-lg font-semibold text-sm hover:bg-gray-200 transition">
|
|
204
|
+
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.44 9.8 8.21 11.39.6.11.79-.26.79-.58v-2.17c-3.34.73-4.04-1.42-4.04-1.42-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.21.08 1.85 1.24 1.85 1.24 1.07 1.84 2.81 1.31 3.5 1 .11-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.12-.3-.54-1.52.12-3.18 0 0 1.01-.32 3.3 1.23a11.5 11.5 0 0 1 6.02 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.66.24 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.63-5.48 5.92.43.37.81 1.1.81 2.22v3.29c0 .32.19.7.8.58A12 12 0 0 0 24 12c0-6.63-5.37-12-12-12z"/></svg>
|
|
205
|
+
Clone & Deploy — 3 commands
|
|
206
|
+
</a>
|
|
207
|
+
</section>
|
|
208
|
+
|
|
209
|
+
<footer class="text-center text-xs text-gray-600 mt-8">
|
|
210
|
+
OWS Hackathon 2026 · Built by Roman Mondello + Claude Opus 4.6
|
|
211
|
+
</footer>
|
|
212
|
+
</main>
|
|
213
|
+
|
|
214
|
+
<script>
|
|
215
|
+
async function loadStats() {
|
|
216
|
+
try {
|
|
217
|
+
const [health, pnl] = await Promise.all([
|
|
218
|
+
fetch('/health').then(r => r.json()).catch(() => null),
|
|
219
|
+
fetch('/api/pnl').then(r => r.ok ? r.json() : null).catch(() => null),
|
|
220
|
+
]);
|
|
221
|
+
|
|
222
|
+
if (pnl) {
|
|
223
|
+
document.getElementById('stat-earned').textContent = '$' + pnl.totalEarned.toFixed(3);
|
|
224
|
+
document.getElementById('stat-spent').textContent = '$' + pnl.totalSpent.toFixed(3);
|
|
225
|
+
document.getElementById('stat-profit').textContent = '$' + pnl.profit.toFixed(3);
|
|
226
|
+
const profitEl = document.getElementById('stat-profit');
|
|
227
|
+
profitEl.className = 'stat-value ' + (pnl.profit >= 0 ? 'text-accent' : 'text-danger');
|
|
228
|
+
document.getElementById('stat-queries').textContent = pnl.queryCount;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (health) {
|
|
232
|
+
document.getElementById('stat-wallet').textContent = health.wallet ? health.wallet.slice(0,6) + '...' + health.wallet.slice(-4) : '—';
|
|
233
|
+
}
|
|
234
|
+
} catch {}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
loadStats();
|
|
238
|
+
setInterval(loadStats, 10000);
|
|
239
|
+
</script>
|
|
240
|
+
</body>
|
|
241
|
+
</html>
|
package/railway.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://railway.app/railway.schema.json",
|
|
3
|
+
"build": {
|
|
4
|
+
"builder": "DOCKERFILE",
|
|
5
|
+
"dockerfilePath": "Dockerfile"
|
|
6
|
+
},
|
|
7
|
+
"deploy": {
|
|
8
|
+
"startCommand": "bash start.sh",
|
|
9
|
+
"healthcheckPath": "/health",
|
|
10
|
+
"restartPolicyType": "ON_FAILURE"
|
|
11
|
+
}
|
|
12
|
+
}
|
package/railway.toml
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createPublicClient, createWalletClient, http } from 'viem';
|
|
2
|
+
import { base } from 'viem/chains';
|
|
3
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
4
|
+
|
|
5
|
+
const DEPLOYER_KEY = process.env.DEPLOYER_PRIVATE_KEY;
|
|
6
|
+
if (!DEPLOYER_KEY) {
|
|
7
|
+
console.error('Set DEPLOYER_PRIVATE_KEY env var');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Compiled bytecode from SpendOSAudit.sol
|
|
12
|
+
// Compile with: solc --bin contracts/SpendOSAudit.sol
|
|
13
|
+
// For now, using a placeholder — replace with actual bytecode after compilation
|
|
14
|
+
const BYTECODE = process.env.SPENDOS_BYTECODE as `0x${string}`;
|
|
15
|
+
if (!BYTECODE) {
|
|
16
|
+
console.error('Set SPENDOS_BYTECODE env var (compile contracts/SpendOSAudit.sol first)');
|
|
17
|
+
console.error('Run: solc --bin --optimize contracts/SpendOSAudit.sol');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function main() {
|
|
22
|
+
const account = privateKeyToAccount(DEPLOYER_KEY as `0x${string}`);
|
|
23
|
+
const client = createWalletClient({
|
|
24
|
+
account,
|
|
25
|
+
chain: base,
|
|
26
|
+
transport: http(),
|
|
27
|
+
});
|
|
28
|
+
const publicClient = createPublicClient({
|
|
29
|
+
chain: base,
|
|
30
|
+
transport: http(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log(`Deploying SpendOSAudit from ${account.address}...`);
|
|
34
|
+
|
|
35
|
+
const hash = await client.deployContract({
|
|
36
|
+
abi: [],
|
|
37
|
+
bytecode: BYTECODE,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log(`Deploy tx: ${hash}`);
|
|
41
|
+
console.log('Waiting for confirmation...');
|
|
42
|
+
|
|
43
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
44
|
+
console.log(`SpendOSAudit deployed at: ${receipt.contractAddress}`);
|
|
45
|
+
console.log(`Set SPENDOS_AUDIT_CONTRACT=${receipt.contractAddress}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test x402 paid request on Base mainnet
|
|
3
|
+
* Usage: EVM_PRIVATE_KEY=0x... npx tsx scripts/test-x402-mainnet.ts
|
|
4
|
+
*/
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import { x402Client, wrapAxiosWithPayment } from '@x402/axios';
|
|
7
|
+
import { registerExactEvmScheme } from '@x402/evm/exact/client';
|
|
8
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
const key = process.env.EVM_PRIVATE_KEY;
|
|
12
|
+
if (!key) { console.error('Set EVM_PRIVATE_KEY'); process.exit(1); }
|
|
13
|
+
|
|
14
|
+
const signer = privateKeyToAccount(key as `0x${string}`);
|
|
15
|
+
console.log('Wallet:', signer.address);
|
|
16
|
+
|
|
17
|
+
const client = new x402Client();
|
|
18
|
+
registerExactEvmScheme(client, { signer });
|
|
19
|
+
const api = wrapAxiosWithPayment(axios.create({ baseURL: 'https://spendos.xyz' }), client);
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
console.log('Calling /api/summarize with x402 payment on Base mainnet...');
|
|
23
|
+
const res = await api.post('/api/summarize', { url: 'https://example.com' });
|
|
24
|
+
console.log('SUCCESS:', JSON.stringify(res.data, null, 2));
|
|
25
|
+
} catch (err: any) {
|
|
26
|
+
console.log('Error:', err.response?.status, err.response?.data ?? err.message);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
main();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* SpendOS XMTP Notification Listener
|
|
4
|
+
*
|
|
5
|
+
* Listens for XMTP messages sent TO the SpendOS treasury wallet.
|
|
6
|
+
* Shows real-time governance notifications from the SpendOS agent.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* NAPI_RS_NATIVE_LIBRARY_PATH=/tmp/xmtp-binding.node npx tsx scripts/xmtp-listener.ts
|
|
10
|
+
*/
|
|
11
|
+
import { Client } from '@xmtp/node-sdk';
|
|
12
|
+
import { mnemonicToAccount } from 'viem/accounts';
|
|
13
|
+
import { toBytes } from 'viem';
|
|
14
|
+
import { getRandomValues } from 'node:crypto';
|
|
15
|
+
|
|
16
|
+
const MNEMONIC = process.env.OWS_IMPORT_MNEMONIC;
|
|
17
|
+
if (!MNEMONIC) { console.error('Set OWS_IMPORT_MNEMONIC env var'); process.exit(1); }
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
const account = mnemonicToAccount(MNEMONIC);
|
|
21
|
+
console.log(`\n SpendOS XMTP Listener`);
|
|
22
|
+
console.log(` Wallet: ${account.address}`);
|
|
23
|
+
console.log(` Waiting for notifications...\n`);
|
|
24
|
+
|
|
25
|
+
const signer = {
|
|
26
|
+
type: 'EOA' as const,
|
|
27
|
+
getIdentifier: () => ({ identifier: account.address, identifierKind: 0 }),
|
|
28
|
+
signMessage: async (message: string) => {
|
|
29
|
+
const sig = await account.signMessage({ message });
|
|
30
|
+
return toBytes(sig);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const dbEncryptionKey = getRandomValues(new Uint8Array(32));
|
|
35
|
+
const client = await Client.create(signer, {
|
|
36
|
+
dbEncryptionKey,
|
|
37
|
+
dbPath: `/tmp/xmtp-spendos-listener-${Date.now()}`,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log(` XMTP client connected\n`);
|
|
41
|
+
|
|
42
|
+
// Sync conversations first
|
|
43
|
+
await client.conversations.sync();
|
|
44
|
+
|
|
45
|
+
// Stream all incoming messages (v6 returns a promise)
|
|
46
|
+
const stream = await client.conversations.streamAllMessages();
|
|
47
|
+
|
|
48
|
+
for await (const message of stream) {
|
|
49
|
+
if (message.senderInboxId === client.inboxId) continue;
|
|
50
|
+
|
|
51
|
+
const time = new Date().toLocaleTimeString();
|
|
52
|
+
console.log(`\n--- ${time} ---`);
|
|
53
|
+
console.log(message.content);
|
|
54
|
+
console.log(`---\n`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
main().catch(err => {
|
|
59
|
+
console.error(`Failed: ${err.message}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
});
|