@oracle-agent/oracle 0.1.0 → 0.2.1
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/README.md +37 -10
- package/SECURITY.md +11 -11
- package/SETUP.md +76 -16
- package/bin/desk-server.mjs +3 -1
- package/docs/architecture.md +10 -9
- package/package.json +13 -7
- package/protocols/templates/safe-erc20/SECURITY.md +16 -0
- package/protocols/templates/safe-erc20/foundry.toml +12 -0
- package/protocols/templates/safe-erc20/remappings.txt +2 -0
- package/protocols/templates/safe-erc20/src/SafeERC20.sol +53 -0
- package/protocols/templates/safe-erc20/test/SafeERC20.t.sol +72 -0
- package/public/oracle-splash/index.html +648 -598
- package/public/oracle-splash/mesh-graph.png +0 -0
- package/public/oracle-splash/mesh-graph.svg +216 -0
- package/scripts/protocol-template-gate.mjs +15 -0
- package/skills/oracle-protocol-builder/SKILL.md +45 -24
- package/src/data/catalog.mjs +9 -0
- package/src/data/desk-data.mjs +24 -0
- package/src/data/providers/bitcoin-esplora.mjs +2 -16
- package/src/data/providers/cowswap.mjs +9 -96
- package/src/data/providers/evm-rpc.mjs +19 -0
- package/src/data/providers/hl-assets.mjs +165 -0
- package/src/data/providers/hl-info.mjs +7 -1
- package/src/data/providers/hl-outcome.mjs +61 -0
- package/src/data/providers/hl-perps.mjs +40 -35
- package/src/data/providers/magiceden-sol.mjs +7 -6
- package/src/data/providers/poly-clob.mjs +460 -0
- package/src/data/providers/satflow.mjs +5 -4
- package/src/data/providers/solana-rpc.mjs +9 -0
- package/src/data/providers/uniswap-v3.mjs +3 -2
- package/src/index.mjs +1 -0
- package/src/onboarding/agent-keys.mjs +4 -0
- package/src/prepare-envelope.mjs +108 -0
- package/src/protocol-templates/gate.mjs +179 -0
- package/src/protocol-templates/prepare-deploy.mjs +81 -0
- package/src/public-control/bundler-client.mjs +3 -18
|
Binary file
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 900" width="1400" height="900">
|
|
2
|
+
<defs>
|
|
3
|
+
<style>
|
|
4
|
+
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400;1,500&family=Outfit:wght@300;400;500&family=IBM+Plex+Mono:wght@400;500&display=swap');
|
|
5
|
+
</style>
|
|
6
|
+
<radialGradient id="bg" cx="50%" cy="35%" r="70%">
|
|
7
|
+
<stop offset="0%" stop-color="#111925"/>
|
|
8
|
+
<stop offset="45%" stop-color="#0B1018"/>
|
|
9
|
+
<stop offset="100%" stop-color="#070b10"/>
|
|
10
|
+
</radialGradient>
|
|
11
|
+
<radialGradient id="orb1" cx="50%" cy="50%" r="50%">
|
|
12
|
+
<stop offset="0%" stop-color="#7CC4FF" stop-opacity="0.18"/>
|
|
13
|
+
<stop offset="55%" stop-color="#7CC4FF" stop-opacity="0.04"/>
|
|
14
|
+
<stop offset="100%" stop-color="#7CC4FF" stop-opacity="0"/>
|
|
15
|
+
</radialGradient>
|
|
16
|
+
<radialGradient id="orb2" cx="50%" cy="50%" r="50%">
|
|
17
|
+
<stop offset="0%" stop-color="#50D2C1" stop-opacity="0.10"/>
|
|
18
|
+
<stop offset="100%" stop-color="#50D2C1" stop-opacity="0"/>
|
|
19
|
+
</radialGradient>
|
|
20
|
+
<pattern id="grid" width="48" height="48" patternUnits="userSpaceOnUse">
|
|
21
|
+
<path d="M 48 0 L 0 0 0 48" fill="none" stroke="rgba(124,196,255,0.06)" stroke-width="1"/>
|
|
22
|
+
</pattern>
|
|
23
|
+
<linearGradient id="panelStroke" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
24
|
+
<stop offset="0%" stop-color="rgba(124,196,255,0.28)"/>
|
|
25
|
+
<stop offset="100%" stop-color="rgba(124,196,255,0.08)"/>
|
|
26
|
+
</linearGradient>
|
|
27
|
+
<linearGradient id="cardFill" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
28
|
+
<stop offset="0%" stop-color="#152031"/>
|
|
29
|
+
<stop offset="100%" stop-color="#111925"/>
|
|
30
|
+
</linearGradient>
|
|
31
|
+
<filter id="softGlow" x="-50%" y="-50%" width="200%" height="200%">
|
|
32
|
+
<feGaussianBlur stdDeviation="8" result="b"/>
|
|
33
|
+
<feMerge>
|
|
34
|
+
<feMergeNode in="b"/>
|
|
35
|
+
<feMergeNode in="SourceGraphic"/>
|
|
36
|
+
</feMerge>
|
|
37
|
+
</filter>
|
|
38
|
+
<filter id="nodeGlow" x="-40%" y="-40%" width="180%" height="180%">
|
|
39
|
+
<feGaussianBlur stdDeviation="4" result="b"/>
|
|
40
|
+
<feMerge>
|
|
41
|
+
<feMergeNode in="b"/>
|
|
42
|
+
<feMergeNode in="SourceGraphic"/>
|
|
43
|
+
</feMerge>
|
|
44
|
+
</filter>
|
|
45
|
+
<marker id="ah-blue" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
|
|
46
|
+
<path d="M0,0 L8,4 L0,8 Z" fill="#7CC4FF"/>
|
|
47
|
+
</marker>
|
|
48
|
+
<marker id="ah-poly" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
|
|
49
|
+
<path d="M0,0 L8,4 L0,8 Z" fill="#3B82F6"/>
|
|
50
|
+
</marker>
|
|
51
|
+
<marker id="ah-hl" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
|
|
52
|
+
<path d="M0,0 L8,4 L0,8 Z" fill="#50D2C1"/>
|
|
53
|
+
</marker>
|
|
54
|
+
<marker id="ah-rh" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
|
|
55
|
+
<path d="M0,0 L8,4 L0,8 Z" fill="#d7ff4d"/>
|
|
56
|
+
</marker>
|
|
57
|
+
<marker id="ah-sol" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
|
|
58
|
+
<path d="M0,0 L8,4 L0,8 Z" fill="#9945FF"/>
|
|
59
|
+
</marker>
|
|
60
|
+
<marker id="ah-btc" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
|
|
61
|
+
<path d="M0,0 L8,4 L0,8 Z" fill="#F0B90B"/>
|
|
62
|
+
</marker>
|
|
63
|
+
<marker id="ah-mute" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto">
|
|
64
|
+
<path d="M0,0 L8,4 L0,8 Z" fill="#5B738F"/>
|
|
65
|
+
</marker>
|
|
66
|
+
</defs>
|
|
67
|
+
|
|
68
|
+
<!-- page bg -->
|
|
69
|
+
<rect width="1400" height="900" fill="url(#bg)"/>
|
|
70
|
+
<rect width="1400" height="900" fill="url(#grid)"/>
|
|
71
|
+
<ellipse cx="700" cy="420" rx="520" ry="280" fill="url(#orb1)"/>
|
|
72
|
+
<ellipse cx="980" cy="620" rx="280" ry="180" fill="url(#orb2)"/>
|
|
73
|
+
|
|
74
|
+
<!-- ========== brand top-left (oracle) ========== -->
|
|
75
|
+
<g id="brand">
|
|
76
|
+
<circle cx="52" cy="48" r="7" fill="#7CC4FF" filter="url(#softGlow)"/>
|
|
77
|
+
<circle cx="52" cy="48" r="3.2" fill="#0B1018"/>
|
|
78
|
+
<text x="70" y="54" fill="#E8F1FA" font-family="Outfit, system-ui, sans-serif" font-size="26" font-weight="400" letter-spacing="-0.3">oracle</text>
|
|
79
|
+
<text x="52" y="78" fill="#5B738F" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" letter-spacing="1.8">MULTI-PROFILE MESH · POWERED BY HERMES</text>
|
|
80
|
+
</g>
|
|
81
|
+
|
|
82
|
+
<!-- right meta -->
|
|
83
|
+
<text x="1350" y="48" fill="#5B738F" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" text-anchor="end" letter-spacing="1.5">FOR USERS</text>
|
|
84
|
+
<text x="1350" y="68" fill="#9BB6D1" font-family="Outfit, system-ui, sans-serif" font-size="13" text-anchor="end">graph · customizable · add agents</text>
|
|
85
|
+
|
|
86
|
+
<!-- ========== main glass panel ========== -->
|
|
87
|
+
<rect x="48" y="110" width="1304" height="730" rx="22" fill="#111925" fill-opacity="0.72" stroke="url(#panelStroke)" stroke-width="1.2"/>
|
|
88
|
+
<!-- panel header bar -->
|
|
89
|
+
<rect x="48" y="110" width="1304" height="52" rx="22" fill="#152031" fill-opacity="0.9"/>
|
|
90
|
+
<rect x="48" y="140" width="1304" height="22" fill="#152031" fill-opacity="0.9"/>
|
|
91
|
+
<text x="72" y="142" fill="#7CC4FF" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="11" letter-spacing="1.6">FOR USERS</text>
|
|
92
|
+
<text x="700" y="142" fill="#9BB6D1" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="11" text-anchor="middle" letter-spacing="1.6">MULTI-PROFILE // CUSTOMIZABLE</text>
|
|
93
|
+
<text x="1328" y="142" fill="#5B738F" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="11" text-anchor="end" letter-spacing="1.4">POWERED BY HERMES</text>
|
|
94
|
+
|
|
95
|
+
<!-- pills -->
|
|
96
|
+
<g font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" letter-spacing="1.2">
|
|
97
|
+
<rect x="72" y="182" width="128" height="28" rx="14" fill="none" stroke="rgba(124,196,255,0.28)" stroke-width="1"/>
|
|
98
|
+
<text x="136" y="200" fill="#A7D8FF" text-anchor="middle">MULTI PROFILES</text>
|
|
99
|
+
<rect x="210" y="182" width="128" height="28" rx="14" fill="none" stroke="rgba(124,196,255,0.28)" stroke-width="1"/>
|
|
100
|
+
<text x="274" y="200" fill="#A7D8FF" text-anchor="middle">CUSTOMIZABLE</text>
|
|
101
|
+
<rect x="348" y="182" width="112" height="28" rx="14" fill="none" stroke="rgba(124,196,255,0.28)" stroke-width="1"/>
|
|
102
|
+
<text x="404" y="200" fill="#A7D8FF" text-anchor="middle">ADD AGENTS</text>
|
|
103
|
+
<rect x="470" y="182" width="118" height="28" rx="14" fill="none" stroke="rgba(124,196,255,0.28)" stroke-width="1"/>
|
|
104
|
+
<text x="529" y="200" fill="#A7D8FF" text-anchor="middle">MORE CHAINS</text>
|
|
105
|
+
<rect x="598" y="182" width="148" height="28" rx="14" fill="none" stroke="rgba(124,196,255,0.28)" stroke-width="1"/>
|
|
106
|
+
<text x="672" y="200" fill="#A7D8FF" text-anchor="middle">POWERED BY HERMES</text>
|
|
107
|
+
</g>
|
|
108
|
+
|
|
109
|
+
<!-- ========== connectors (behind nodes) ========== -->
|
|
110
|
+
<!-- input -> oracle -->
|
|
111
|
+
<line x1="700" y1="310" x2="700" y2="360" stroke="#7CC4FF" stroke-width="2.5" marker-end="url(#ah-blue)" filter="url(#nodeGlow)"/>
|
|
112
|
+
<line x1="700" y1="310" x2="700" y2="360" stroke="#7CC4FF" stroke-width="10" stroke-opacity="0.12"/>
|
|
113
|
+
|
|
114
|
+
<!-- oracle hub fan-out to profiles -->
|
|
115
|
+
<!-- poly left -->
|
|
116
|
+
<path d="M620 450 C560 450 420 450 280 520" fill="none" stroke="#3B82F6" stroke-width="2" marker-end="url(#ah-poly)"/>
|
|
117
|
+
<path d="M620 450 C560 450 420 450 280 520" fill="none" stroke="#3B82F6" stroke-width="8" stroke-opacity="0.10"/>
|
|
118
|
+
<!-- hl center-left -->
|
|
119
|
+
<path d="M660 470 C640 500 560 520 500 560" fill="none" stroke="#50D2C1" stroke-width="2" marker-end="url(#ah-hl)"/>
|
|
120
|
+
<path d="M660 470 C640 500 560 520 500 560" fill="none" stroke="#50D2C1" stroke-width="8" stroke-opacity="0.10"/>
|
|
121
|
+
<!-- rh center -->
|
|
122
|
+
<line x1="700" y1="470" x2="700" y2="560" stroke="#d7ff4d" stroke-width="2" marker-end="url(#ah-rh)"/>
|
|
123
|
+
<line x1="700" y1="470" x2="700" y2="560" stroke="#d7ff4d" stroke-width="8" stroke-opacity="0.10"/>
|
|
124
|
+
<!-- sol center-right -->
|
|
125
|
+
<path d="M740 470 C760 500 840 520 900 560" fill="none" stroke="#9945FF" stroke-width="2" marker-end="url(#ah-sol)"/>
|
|
126
|
+
<path d="M740 470 C760 500 840 520 900 560" fill="none" stroke="#9945FF" stroke-width="8" stroke-opacity="0.10"/>
|
|
127
|
+
<!-- btc right -->
|
|
128
|
+
<path d="M780 450 C860 450 1000 450 1120 520" fill="none" stroke="#F0B90B" stroke-width="2" marker-end="url(#ah-btc)"/>
|
|
129
|
+
<path d="M780 450 C860 450 1000 450 1120 520" fill="none" stroke="#F0B90B" stroke-width="8" stroke-opacity="0.10"/>
|
|
130
|
+
<!-- builder bottom -->
|
|
131
|
+
<path d="M700 470 C700 510 700 640 700 680" fill="none" stroke="#5B738F" stroke-width="1.5" stroke-dasharray="4 4" marker-end="url(#ah-mute)"/>
|
|
132
|
+
|
|
133
|
+
<!-- ========== 01 INPUT / COMMAND ========== -->
|
|
134
|
+
<g filter="url(#nodeGlow)">
|
|
135
|
+
<rect x="575" y="230" width="250" height="80" rx="14" fill="url(#cardFill)" stroke="rgba(124,196,255,0.35)" stroke-width="1.2"/>
|
|
136
|
+
<text x="700" y="258" fill="#5B738F" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" text-anchor="middle" letter-spacing="1.6">YOU</text>
|
|
137
|
+
<text x="700" y="282" fill="#E8F1FA" font-family="Outfit, system-ui, sans-serif" font-size="18" font-weight="500" text-anchor="middle">Input / Command</text>
|
|
138
|
+
<text x="700" y="300" fill="#9BB6D1" font-family="Outfit, system-ui, sans-serif" font-size="12" font-weight="300" text-anchor="middle">what you type or ask</text>
|
|
139
|
+
</g>
|
|
140
|
+
|
|
141
|
+
<!-- ========== 02 CONTROL PLANE · oracle ========== -->
|
|
142
|
+
<g filter="url(#nodeGlow)">
|
|
143
|
+
<rect x="520" y="360" width="360" height="110" rx="16" fill="url(#cardFill)" stroke="rgba(124,196,255,0.45)" stroke-width="1.4"/>
|
|
144
|
+
<rect x="520" y="360" width="360" height="110" rx="16" fill="rgba(124,196,255,0.04)"/>
|
|
145
|
+
<text x="700" y="392" fill="#7CC4FF" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" text-anchor="middle" letter-spacing="1.8">CONTROL PLANE</text>
|
|
146
|
+
<text x="700" y="424" fill="#E8F1FA" font-family="Cormorant Garamond, Georgia, serif" font-size="34" font-weight="500" font-style="italic" text-anchor="middle">oracle</text>
|
|
147
|
+
<text x="700" y="450" fill="#9BB6D1" font-family="Outfit, system-ui, sans-serif" font-size="12" font-weight="300" text-anchor="middle">route · bound · evolve</text>
|
|
148
|
+
</g>
|
|
149
|
+
|
|
150
|
+
<!-- ========== 03 PROFILE CARDS ========== -->
|
|
151
|
+
<!-- polymarket -->
|
|
152
|
+
<g>
|
|
153
|
+
<rect x="80" y="520" width="260" height="130" rx="16" fill="#111925" stroke="rgba(59,130,246,0.45)" stroke-width="1.2"/>
|
|
154
|
+
<rect x="80" y="520" width="260" height="130" rx="16" fill="rgba(59,130,246,0.06)"/>
|
|
155
|
+
<text x="100" y="548" fill="#3B82F6" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" letter-spacing="1.6">PROFILE</text>
|
|
156
|
+
<text x="100" y="578" fill="#A7D8FF" font-family="Outfit, system-ui, sans-serif" font-size="18" font-weight="500">polymarket agent</text>
|
|
157
|
+
<text x="100" y="600" fill="#9BB6D1" font-family="Outfit, system-ui, sans-serif" font-size="12" font-weight="300">events · arb · markets</text>
|
|
158
|
+
<rect x="100" y="618" width="100" height="20" rx="10" fill="none" stroke="rgba(59,130,246,0.35)"/>
|
|
159
|
+
<text x="150" y="632" fill="#7CC4FF" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="9" text-anchor="middle">Polymarket</text>
|
|
160
|
+
</g>
|
|
161
|
+
|
|
162
|
+
<!-- hyperliquid -->
|
|
163
|
+
<g>
|
|
164
|
+
<rect x="370" y="560" width="260" height="130" rx="16" fill="#111925" stroke="rgba(80,210,193,0.45)" stroke-width="1.2"/>
|
|
165
|
+
<rect x="370" y="560" width="260" height="130" rx="16" fill="rgba(80,210,193,0.06)"/>
|
|
166
|
+
<text x="390" y="588" fill="#50D2C1" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" letter-spacing="1.6">PROFILE</text>
|
|
167
|
+
<text x="390" y="618" fill="#A8F0E6" font-family="Outfit, system-ui, sans-serif" font-size="18" font-weight="500">hyperliquid agent</text>
|
|
168
|
+
<text x="390" y="640" fill="#9BB6D1" font-family="Outfit, system-ui, sans-serif" font-size="12" font-weight="300">perps · hip-3/4 · ta</text>
|
|
169
|
+
<rect x="390" y="658" width="170" height="20" rx="10" fill="none" stroke="rgba(80,210,193,0.35)"/>
|
|
170
|
+
<text x="475" y="672" fill="#50D2C1" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="9" text-anchor="middle">Hyperliquid · HyperEVM 999</text>
|
|
171
|
+
</g>
|
|
172
|
+
|
|
173
|
+
<!-- robinhood -->
|
|
174
|
+
<g>
|
|
175
|
+
<rect x="660" y="560" width="260" height="130" rx="16" fill="#111925" stroke="rgba(215,255,77,0.40)" stroke-width="1.2"/>
|
|
176
|
+
<rect x="660" y="560" width="260" height="130" rx="16" fill="rgba(215,255,77,0.05)"/>
|
|
177
|
+
<text x="680" y="588" fill="#d7ff4d" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" letter-spacing="1.6">PROFILE</text>
|
|
178
|
+
<text x="680" y="618" fill="#EAF7B0" font-family="Outfit, system-ui, sans-serif" font-size="18" font-weight="500">robinhood agent</text>
|
|
179
|
+
<text x="680" y="640" fill="#9BB6D1" font-family="Outfit, system-ui, sans-serif" font-size="12" font-weight="300">markets · nfts · names</text>
|
|
180
|
+
<rect x="680" y="658" width="150" height="20" rx="10" fill="none" stroke="rgba(215,255,77,0.35)"/>
|
|
181
|
+
<text x="755" y="672" fill="#d7ff4d" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="9" text-anchor="middle">Robinhood Chain 4663</text>
|
|
182
|
+
</g>
|
|
183
|
+
|
|
184
|
+
<!-- solana -->
|
|
185
|
+
<g>
|
|
186
|
+
<rect x="950" y="560" width="200" height="130" rx="16" fill="#111925" stroke="rgba(153,69,255,0.45)" stroke-width="1.2"/>
|
|
187
|
+
<rect x="950" y="560" width="200" height="130" rx="16" fill="rgba(153,69,255,0.06)"/>
|
|
188
|
+
<text x="970" y="588" fill="#9945FF" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" letter-spacing="1.6">PROFILE</text>
|
|
189
|
+
<text x="970" y="618" fill="#D4B3FF" font-family="Outfit, system-ui, sans-serif" font-size="18" font-weight="500">solana agent</text>
|
|
190
|
+
<text x="970" y="640" fill="#9BB6D1" font-family="Outfit, system-ui, sans-serif" font-size="12" font-weight="300">jup · nfts · dexs</text>
|
|
191
|
+
<rect x="970" y="658" width="70" height="20" rx="10" fill="none" stroke="rgba(153,69,255,0.35)"/>
|
|
192
|
+
<text x="1005" y="672" fill="#c4a0ff" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="9" text-anchor="middle">Solana</text>
|
|
193
|
+
</g>
|
|
194
|
+
|
|
195
|
+
<!-- bitcoin (top-right of profile row) -->
|
|
196
|
+
<g>
|
|
197
|
+
<rect x="1160" y="520" width="160" height="130" rx="16" fill="#111925" stroke="rgba(240,185,11,0.40)" stroke-width="1.2"/>
|
|
198
|
+
<rect x="1160" y="520" width="160" height="130" rx="16" fill="rgba(240,185,11,0.05)"/>
|
|
199
|
+
<text x="1180" y="548" fill="#F0B90B" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" letter-spacing="1.6">PROFILE</text>
|
|
200
|
+
<text x="1180" y="578" fill="#F8E29A" font-family="Outfit, system-ui, sans-serif" font-size="18" font-weight="500">bitcoin</text>
|
|
201
|
+
<text x="1180" y="600" fill="#9BB6D1" font-family="Outfit, system-ui, sans-serif" font-size="12" font-weight="300">ord · runes</text>
|
|
202
|
+
<rect x="1180" y="618" width="50" height="20" rx="10" fill="none" stroke="rgba(240,185,11,0.35)"/>
|
|
203
|
+
<text x="1205" y="632" fill="#F0B90B" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="9" text-anchor="middle">BTC</text>
|
|
204
|
+
</g>
|
|
205
|
+
|
|
206
|
+
<!-- protocol builder -->
|
|
207
|
+
<g>
|
|
208
|
+
<rect x="520" y="710" width="360" height="72" rx="14" fill="#111925" stroke="rgba(124,196,255,0.22)" stroke-width="1" stroke-dasharray="5 4"/>
|
|
209
|
+
<text x="700" y="738" fill="#5B738F" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" text-anchor="middle" letter-spacing="1.6">BUILDER</text>
|
|
210
|
+
<text x="700" y="762" fill="#E8F1FA" font-family="Outfit, system-ui, sans-serif" font-size="16" font-weight="400" text-anchor="middle">protocol builder · gated templates · user-signed deploy</text>
|
|
211
|
+
</g>
|
|
212
|
+
|
|
213
|
+
<!-- footer -->
|
|
214
|
+
<text x="72" y="870" fill="#5B738F" font-family="Outfit, system-ui, sans-serif" font-size="12" font-weight="300">oracle routes input/command across a live multi-profile desk. profiles evolve. chains get added. not one bot.</text>
|
|
215
|
+
<text x="1328" y="870" fill="#3a6f99" font-family="IBM Plex Mono, ui-monospace, monospace" font-size="10" text-anchor="end">self-custody · prepare → local sign</text>
|
|
216
|
+
</svg>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { listProtocolTemplates, runProtocolTemplateGate } from "../src/protocol-templates/gate.mjs";
|
|
3
|
+
|
|
4
|
+
const id = process.argv[2];
|
|
5
|
+
if (!id || id === "list") {
|
|
6
|
+
console.log(JSON.stringify(listProtocolTemplates(), null, 2));
|
|
7
|
+
process.exit(0);
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const r = runProtocolTemplateGate(id, { allowSkipStatic: process.env.REQUIRE_SLITHER !== "1" });
|
|
11
|
+
console.log(JSON.stringify(r, null, 2));
|
|
12
|
+
} catch (e) {
|
|
13
|
+
console.error(String(e.message || e));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
@@ -1,38 +1,59 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oracle-protocol-builder
|
|
3
|
-
description: Use when the user wants Oracle to
|
|
3
|
+
description: Use when the user wants Oracle to scaffold/deploy protocol templates. Gated Foundry templates; prepare-only deploy; not a firm audit.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Protocol builder
|
|
7
7
|
|
|
8
|
-
Oracle
|
|
9
|
-
it writes and prepares; the user signs and deploys.
|
|
8
|
+
Oracle scaffolds and **prepares unsigned deploys**. The user signs. Custody boundary unchanged.
|
|
10
9
|
|
|
11
|
-
##
|
|
10
|
+
## Security gate (v1)
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
- scaffold mint/gacha/claim pages
|
|
15
|
-
- build DEX/pool/router integrations
|
|
16
|
-
- write deploy and verify scripts
|
|
17
|
-
- produce unsigned deploy or admin transactions
|
|
18
|
-
- create launch runbooks and monitoring dashboards
|
|
19
|
-
- audit authority, upgradeability, fees, and emergency controls
|
|
12
|
+
Before any template deploy prepare:
|
|
20
13
|
|
|
21
|
-
|
|
14
|
+
1. `forge test` must pass on the template
|
|
15
|
+
2. Slither runs when installed (optional skip if missing; set `REQUIRE_SLITHER=1` to hard-require)
|
|
16
|
+
3. JS `runProtocolTemplateGate` / `prepareTemplateDeploy` refuse otherwise
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
```js
|
|
19
|
+
// list
|
|
20
|
+
data.call("protocol-templates", "list")
|
|
21
|
+
// gate
|
|
22
|
+
data.call("protocol-templates", "gate", { templateId: "safe-erc20" })
|
|
23
|
+
// unsigned deploy prepare (stamped)
|
|
24
|
+
data.call("protocol-templates", "prepareDeploy", {
|
|
25
|
+
templateId: "safe-erc20",
|
|
26
|
+
chainId: 8453,
|
|
27
|
+
args: [name, symbol, supply, initialHolder, initialOwner],
|
|
28
|
+
})
|
|
29
|
+
```
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
CLI: `npm run protocol:gate -- safe-erc20`
|
|
32
|
+
|
|
33
|
+
## Shipped template: `safe-erc20`
|
|
34
|
+
|
|
35
|
+
- Fixed supply, mint once in constructor (no hidden mint)
|
|
36
|
+
- Ownable2Step + pause
|
|
37
|
+
- No tax / blacklist / max-tx / upgrade proxy
|
|
38
|
+
- Foundry tests included
|
|
39
|
+
|
|
40
|
+
## Honesty
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
fake TVL, or any contract whose main purpose is deceiving buyers.
|
|
42
|
+
**Reviewed templates + automated tests ≠ paid Solidity audit.**
|
|
34
43
|
|
|
35
|
-
|
|
44
|
+
Docs and prepare envelopes always set `firmAudit: false` and carry the disclaimer.
|
|
45
|
+
Mainnet TVL → independent firm audit.
|
|
46
|
+
|
|
47
|
+
## Required order (custom work)
|
|
48
|
+
|
|
49
|
+
1. Authority model
|
|
50
|
+
2. Threat model
|
|
51
|
+
3. Tests (Foundry)
|
|
52
|
+
4. Static analysis when available
|
|
53
|
+
5. Gate green
|
|
54
|
+
6. Prepare unsigned only
|
|
55
|
+
7. User signs + verify source
|
|
56
|
+
|
|
57
|
+
## Refusal line
|
|
36
58
|
|
|
37
|
-
|
|
38
|
-
artifact. End with the exact tests/simulations run and what remains unverified.
|
|
59
|
+
Refuse hidden drains, honeypots, wash-trading systems, undisclosed tax switches, fake TVL, or any contract whose main purpose is deceiving buyers.
|
package/src/data/catalog.mjs
CHANGED
|
@@ -59,6 +59,15 @@ registerProvider({
|
|
|
59
59
|
description: "Hyperliquid public /info",
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
registerProvider({
|
|
63
|
+
id: "protocol-templates",
|
|
64
|
+
venue: "protocol-builder",
|
|
65
|
+
chainIds: [],
|
|
66
|
+
auth: "none",
|
|
67
|
+
ops: ["health", "list", "gate", "prepareDeploy"],
|
|
68
|
+
description: "Gated Safe* Solidity templates: forge test required before unsigned deploy prepare. NOT a firm audit.",
|
|
69
|
+
});
|
|
70
|
+
|
|
62
71
|
registerProvider({
|
|
63
72
|
id: "poly-public",
|
|
64
73
|
venue: "poly",
|
package/src/data/desk-data.mjs
CHANGED
|
@@ -27,6 +27,10 @@ import * as hevm from "./providers/hyperevm-dex.mjs";
|
|
|
27
27
|
import * as mesol from "./providers/magiceden-sol.mjs";
|
|
28
28
|
import * as hlstake from "./providers/hl-staking.mjs";
|
|
29
29
|
import * as hlmarkets from "./providers/hl-markets.mjs";
|
|
30
|
+
import * as hloutcome from "./providers/hl-outcome.mjs";
|
|
31
|
+
import * as polyclob from "./providers/poly-clob.mjs";
|
|
32
|
+
import * as protoTpl from "../protocol-templates/prepare-deploy.mjs";
|
|
33
|
+
import { listProtocolTemplates, runProtocolTemplateGate } from "../protocol-templates/gate.mjs";
|
|
30
34
|
import * as hlperps from "./providers/hl-perps.mjs";
|
|
31
35
|
import * as gecko from "./providers/geckoterminal.mjs";
|
|
32
36
|
import * as curve from "./providers/curve.mjs";
|
|
@@ -211,6 +215,23 @@ const OPS = {
|
|
|
211
215
|
floor: (o, a = {}) => osnft.openseaFloor(a.slug, o),
|
|
212
216
|
},
|
|
213
217
|
|
|
218
|
+
"hl-outcome": {
|
|
219
|
+
list: (o) => hloutcome.hlOutcomeList(o),
|
|
220
|
+
prepareOrder: (o, a = {}) => hloutcome.hlPrepareOutcomeOrder(a, o),
|
|
221
|
+
prepareCancel: (o, a = {}) => hloutcome.hlPrepareOutcomeCancel(a, o),
|
|
222
|
+
},
|
|
223
|
+
"protocol-templates": {
|
|
224
|
+
health: () => ({ ok: true, templates: listProtocolTemplates().map((t) => t.id), firmAudit: false }),
|
|
225
|
+
list: () => listProtocolTemplates(),
|
|
226
|
+
gate: (o, a = {}) => runProtocolTemplateGate(a.templateId || a.id, o),
|
|
227
|
+
prepareDeploy: (o, a = {}) => protoTpl.prepareTemplateDeploy(a, o),
|
|
228
|
+
},
|
|
229
|
+
"poly-clob": {
|
|
230
|
+
prepareOrder: (o, a = {}) => {
|
|
231
|
+
// prepare is sync; wrap for dataCall
|
|
232
|
+
return polyclob.polyPrepareOrder(a || {});
|
|
233
|
+
},
|
|
234
|
+
},
|
|
214
235
|
"hl-perps": {
|
|
215
236
|
health: (o) => hlmarkets.hlMarketsHealth(o),
|
|
216
237
|
assetInfo: (o, a = {}) => hlperps.hlPerpAssetInfo(a, o),
|
|
@@ -529,6 +550,9 @@ export const data = {
|
|
|
529
550
|
price: (tokenId, side, o) => dataCall("poly-public", "price", { tokenId, side }, o),
|
|
530
551
|
events: (a, o) => dataCall("poly-public", "events", a || {}, o),
|
|
531
552
|
},
|
|
553
|
+
polyClob: {
|
|
554
|
+
prepareOrder: (a, o) => dataCall("poly-clob", "prepareOrder", a || {}, o),
|
|
555
|
+
},
|
|
532
556
|
rh: {
|
|
533
557
|
health: (o) => dataCall("rh-agent", "health", {}, o),
|
|
534
558
|
policy: (o) => dataCall("rh-agent", "policy", {}, o),
|
|
@@ -208,22 +208,8 @@ export async function btcDecodeTxHex(args = {}, opts = {}) {
|
|
|
208
208
|
* broadcast, so nothing goes out as a side effect of a read path. Oracle never
|
|
209
209
|
* signs here; the caller supplies the bytes.
|
|
210
210
|
*/
|
|
211
|
-
export async function btcBroadcast(
|
|
212
|
-
|
|
213
|
-
throw new Error("bitcoin-esplora: broadcast requires execute:true — refusing an implicit send");
|
|
214
|
-
}
|
|
215
|
-
const txHex = btcTxHex(args.txHex || args.hex || args.signedTransaction, "txHex");
|
|
216
|
-
const txid = await postText("/tx", txHex, opts);
|
|
217
|
-
if (!/^[0-9a-f]{64}$/i.test(txid)) {
|
|
218
|
-
throw new Error(`bitcoin-esplora: unexpected broadcast response ${String(txid).slice(0, 80)}`);
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
provider: "bitcoin-esplora",
|
|
222
|
-
network: network(opts),
|
|
223
|
-
sent: true,
|
|
224
|
-
txid: txid.toLowerCase(),
|
|
225
|
-
signedTransactionHex: txHex,
|
|
226
|
-
};
|
|
211
|
+
export async function btcBroadcast(_a = {}, _o = {}) {
|
|
212
|
+
throw new Error("bitcoin-esplora.btcBroadcast refused: @oracle-agent/oracle is prepare-only. Broadcast from the user wallet or a local operator process.");
|
|
227
213
|
}
|
|
228
214
|
|
|
229
215
|
// Keep httpJson import usable for Core RPC optional path later without circular deps.
|
|
@@ -331,72 +331,12 @@ export function cowOrderUid(intent = {}) {
|
|
|
331
331
|
return toCowOrderUid({ digest, owner, validTo: message.validTo });
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
export async function cowSignOrder(
|
|
335
|
-
|
|
336
|
-
const typedData = intent.typedData;
|
|
337
|
-
if (!typedData?.domain || !typedData?.types || !typedData?.message) throw new Error("cowswap: typedData order intent required");
|
|
338
|
-
if (typedData.primaryType && typedData.primaryType !== "Order") throw new Error("cowswap: primaryType must be Order");
|
|
339
|
-
const owner = addr(intent.owner || opts.signerAddress, "owner");
|
|
340
|
-
assertCowOrderGuard(intent, typedData, owner, opts);
|
|
341
|
-
if (!opts.signTypedData || typeof opts.signTypedData !== "function") throw new Error("cowswap: signTypedData callback required");
|
|
342
|
-
const signature = await opts.signTypedData(typedData);
|
|
343
|
-
const recovered = addr(verifyTypedData(typedData.domain, typedData.types, typedData.message, signature), "recovered signer");
|
|
344
|
-
if (recovered !== owner) throw new Error("cowswap: recovered signer mismatch");
|
|
345
|
-
const uid = cowOrderUid({ ...intent, owner });
|
|
346
|
-
return {
|
|
347
|
-
provider: "cowswap",
|
|
348
|
-
chainId: Number(intent.chainId ?? typedData.domain.chainId),
|
|
349
|
-
owner,
|
|
350
|
-
signingScheme: "eip712",
|
|
351
|
-
signature,
|
|
352
|
-
uid,
|
|
353
|
-
quoteId: intent.quoteId ?? intent.cowGuard?.quoteId ?? null,
|
|
354
|
-
order: typedData.message,
|
|
355
|
-
typedData,
|
|
356
|
-
cowGuard: intent.cowGuard || null,
|
|
357
|
-
executableArtifact: true,
|
|
358
|
-
submitted: false,
|
|
359
|
-
};
|
|
334
|
+
export async function cowSignOrder(_a = {}, _o = {}) {
|
|
335
|
+
throw new Error("cowswap.cowSignOrder refused: @oracle-agent/oracle is prepare-only. Sign with the user wallet; do not pass private keys here.");
|
|
360
336
|
}
|
|
361
337
|
|
|
362
|
-
export async function cowSubmitSignedOrder(
|
|
363
|
-
|
|
364
|
-
const chainId = Number(signed.chainId ?? signed.typedData?.domain?.chainId ?? 1);
|
|
365
|
-
const order = signed.order || signed.typedData?.message;
|
|
366
|
-
if (!order) throw new Error("cowswap: signed order message required");
|
|
367
|
-
const owner = addr(signed.owner || signed.from, "owner");
|
|
368
|
-
const signature = String(signed.signature || "").trim();
|
|
369
|
-
if (!/^0x[0-9a-fA-F]+$/.test(signature)) throw new Error("cowswap: signature hex required");
|
|
370
|
-
const typedData = signed.typedData || { domain: orderDomain(chainId), types: COW_ORDER_TYPES, message: order };
|
|
371
|
-
assertCowOrderGuard({ ...signed, chainId, typedData, cowGuard: signed.cowGuard }, typedData, owner, opts);
|
|
372
|
-
const recovered = addr(verifyTypedData(typedData.domain, typedData.types, typedData.message, signature), "recovered signer");
|
|
373
|
-
if (recovered !== owner) throw new Error("cowswap: recovered signer mismatch");
|
|
374
|
-
const uid = orderUid(signed.uid || cowOrderUid({ chainId, owner, typedData }));
|
|
375
|
-
const body = {
|
|
376
|
-
...order,
|
|
377
|
-
signature,
|
|
378
|
-
signingScheme: signed.signingScheme || "eip712",
|
|
379
|
-
from: owner,
|
|
380
|
-
...(signed.quoteId != null ? { quoteId: signed.quoteId } : {}),
|
|
381
|
-
};
|
|
382
|
-
const acceptedUid = await httpJson(`${host(chainId, opts)}/orders`, {
|
|
383
|
-
method: "POST",
|
|
384
|
-
body,
|
|
385
|
-
fetchImpl: opts.fetchImpl,
|
|
386
|
-
timeoutMs: opts.timeoutMs ?? 15_000,
|
|
387
|
-
});
|
|
388
|
-
const accepted = orderUid(typeof acceptedUid === "string" ? acceptedUid : acceptedUid?.uid || uid);
|
|
389
|
-
if (accepted !== uid) throw new Error("cowswap: accepted UID mismatch from orderbook");
|
|
390
|
-
return {
|
|
391
|
-
ok: true,
|
|
392
|
-
provider: "cowswap",
|
|
393
|
-
chainId,
|
|
394
|
-
uid: accepted,
|
|
395
|
-
expectedUid: uid,
|
|
396
|
-
owner,
|
|
397
|
-
submitted: true,
|
|
398
|
-
response: acceptedUid,
|
|
399
|
-
};
|
|
338
|
+
export async function cowSubmitSignedOrder(_a = {}, _o = {}) {
|
|
339
|
+
throw new Error("cowswap.cowSubmitSignedOrder refused: @oracle-agent/oracle is prepare-only. Submit signed orders from the user wallet or local operator.");
|
|
400
340
|
}
|
|
401
341
|
|
|
402
342
|
export function cowPrepareCancel(q = {}) {
|
|
@@ -418,42 +358,15 @@ export function cowPrepareCancel(q = {}) {
|
|
|
418
358
|
};
|
|
419
359
|
}
|
|
420
360
|
|
|
421
|
-
export async function cowSignCancel(
|
|
422
|
-
|
|
423
|
-
const typedData = cancel.typedData;
|
|
424
|
-
if (!typedData?.domain || !typedData?.types || !typedData?.message) throw new Error("cowswap: cancellation typedData required");
|
|
425
|
-
if (typedData.primaryType && typedData.primaryType !== "OrderCancellations") throw new Error("cowswap: primaryType must be OrderCancellations");
|
|
426
|
-
const owner = addr(cancel.owner || opts.signerAddress, "owner");
|
|
427
|
-
if (!opts.signTypedData || typeof opts.signTypedData !== "function") throw new Error("cowswap: signTypedData callback required");
|
|
428
|
-
const signature = await opts.signTypedData(typedData);
|
|
429
|
-
const recovered = addr(verifyTypedData(typedData.domain, typedData.types, typedData.message, signature), "recovered signer");
|
|
430
|
-
if (recovered !== owner) throw new Error("cowswap: recovered signer mismatch");
|
|
431
|
-
return {
|
|
432
|
-
...cancel,
|
|
433
|
-
owner,
|
|
434
|
-
signingScheme: "eip712",
|
|
435
|
-
signature,
|
|
436
|
-
executableArtifact: true,
|
|
437
|
-
};
|
|
361
|
+
export async function cowSignCancel(_a = {}, _o = {}) {
|
|
362
|
+
throw new Error("cowswap.cowSignCancel refused: @oracle-agent/oracle is prepare-only. Cancel-sign with the user wallet.");
|
|
438
363
|
}
|
|
439
364
|
|
|
440
|
-
export async function cowCancelOrders(
|
|
441
|
-
|
|
442
|
-
const chainId = Number(cancel.chainId ?? cancel.typedData?.domain?.chainId ?? 1);
|
|
443
|
-
const orderUids = (cancel.orderUids || cancel.typedData?.message?.orderUids || []).map((u, i) => orderUid(u, `orderUids[${i}]`));
|
|
444
|
-
if (!orderUids.length) throw new Error("cowswap: orderUids required");
|
|
445
|
-
const signature = String(cancel.signature || "").trim();
|
|
446
|
-
if (!/^0x[0-9a-fA-F]+$/.test(signature)) throw new Error("cowswap: cancellation signature hex required");
|
|
447
|
-
const body = { orderUids, signature, signingScheme: cancel.signingScheme || "eip712" };
|
|
448
|
-
const response = await httpJson(`${host(chainId, opts)}/orders`, {
|
|
449
|
-
method: "DELETE",
|
|
450
|
-
body,
|
|
451
|
-
fetchImpl: opts.fetchImpl,
|
|
452
|
-
timeoutMs: opts.timeoutMs ?? 15_000,
|
|
453
|
-
});
|
|
454
|
-
return { ok: true, provider: "cowswap", chainId, orderUids, cancelled: true, response };
|
|
365
|
+
export async function cowCancelOrders(_a = {}, _o = {}) {
|
|
366
|
+
throw new Error("cowswap.cowCancelOrders refused: @oracle-agent/oracle is prepare-only. Cancel from the user wallet or local operator.");
|
|
455
367
|
}
|
|
456
368
|
|
|
369
|
+
|
|
457
370
|
function tradeBuyAmount(trade = {}) {
|
|
458
371
|
return uintString(trade.buyAmount ?? trade.executedBuyAmount ?? "0", "trade.buyAmount");
|
|
459
372
|
}
|
|
@@ -60,7 +60,26 @@ export function resolveRpcUrl(chainId, { rpcUrl, env = process.env } = {}) {
|
|
|
60
60
|
* @param {string} method
|
|
61
61
|
* @param {any[]} [params]
|
|
62
62
|
*/
|
|
63
|
+
|
|
64
|
+
/** Prepare-only: write/settle JSON-RPC methods are hard-refused even on deep import. */
|
|
65
|
+
const EVM_WRITE_RPC = new Set([
|
|
66
|
+
"eth_sendTransaction",
|
|
67
|
+
"eth_sendRawTransaction",
|
|
68
|
+
"eth_sign",
|
|
69
|
+
"eth_signTransaction",
|
|
70
|
+
"eth_signTypedData",
|
|
71
|
+
"eth_signTypedData_v4",
|
|
72
|
+
"personal_sign",
|
|
73
|
+
]);
|
|
74
|
+
|
|
63
75
|
export async function rpcCall(chainId, method, params = [], opts = {}) {
|
|
76
|
+
const methodName = String(method || "");
|
|
77
|
+
if (EVM_WRITE_RPC.has(methodName) || /send|sign/i.test(methodName) && !/^eth_get|^eth_call|^eth_block|^eth_chain|^eth_fee|^eth_gas|^eth_max|^eth_sync|^net_|^web3_/.test(methodName)) {
|
|
78
|
+
// allowlist is positive for common reads; anything send/sign-shaped dies.
|
|
79
|
+
if (EVM_WRITE_RPC.has(methodName) || methodName.startsWith("eth_send") || methodName.includes("sign")) {
|
|
80
|
+
throw new Error(`evm-rpc: ${methodName} refused — @oracle-agent/oracle is prepare-only (no broadcast/sign)`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
64
83
|
const url = resolveRpcUrl(chainId, opts);
|
|
65
84
|
if (!url) {
|
|
66
85
|
throw new Error(
|