Skip to content

Commit b62b78a

Browse files
committed
Add rofl-client-ts use case with eliza
1 parent ad02e68 commit b62b78a

File tree

1 file changed

+171
-2
lines changed

1 file changed

+171
-2
lines changed

docs/build/use-cases/trustless-agent.mdx

Lines changed: 171 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ WORKDIR /app
117117
# Copy your ElizaOS project (created with `elizaos create ...`).
118118
ADD . /app
119119

120-
# Install ElizaOS CLI and project dependencies.
121-
RUN bun install -g @elizaos/cli && bun install
120+
# Install ElizaOS CLI and project dependencies (npm-based).
121+
RUN npm i -g @elizaos/cli && npm ci
122122

123123
# Minimal entrypoint that launches the agent.
124124
ENTRYPOINT ["/bin/sh", "/app/docker/entry.sh"]
@@ -193,6 +193,175 @@ By default, the Oasis-maintained provider is selected on Testnet.
193193
`appd`. The tx originates from the enclave-bound key of your ROFL app.
194194
- Contracts can require verified ROFL origin to bind calls to your app.
195195

196+
### Eliza + ROFL TypeScript client (inline)
197+
198+
Use the official ROFL TypeScript client directly inside your Eliza agent. Add a
199+
small helper you can import from any Eliza action/tool to derive the enclave key,
200+
expose the wallet address, and sign+submit transactions via appd.
201+
202+
<details>
203+
<summary>src/rofl.ts</summary>
204+
205+
```ts
206+
import { RoflClient, KeyKind } from "@oasisprotocol/rofl-client";
207+
import { Interface, Wallet } from "ethers";
208+
import http from "node:http";
209+
210+
export async function ensureEnclaveWallet(keyId = "agent-identity-key") {
211+
const client = new RoflClient(); // connects to /run/rofl-appd.sock inside ROFL
212+
const key = await client.generateKey(keyId, KeyKind.SECP256K1);
213+
const wallet = new Wallet("0x" + key);
214+
await client.setMetadata({ key_fingerprint: key.slice(0, 16) });
215+
return { wallet, key, client };
216+
}
217+
218+
export async function signSubmitEthTx(params: {
219+
to: string;
220+
abi: any[];
221+
method: string;
222+
args?: any[];
223+
gasLimit?: number;
224+
}) {
225+
const { to, abi, method, args, gasLimit } = params;
226+
const iface = new Interface(abi);
227+
const data = iface.encodeFunctionData(method, args ?? []);
228+
const body = JSON.stringify({
229+
tx: { kind: "eth", data: { gas_limit: gasLimit ?? 200000, to, value: 0, data } },
230+
});
231+
return await new Promise<any>((resolve, reject) => {
232+
const req = http.request(
233+
{
234+
socketPath: "/run/rofl-appd.sock",
235+
path: "/rofl/v1/tx/sign-submit",
236+
method: "POST",
237+
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) },
238+
},
239+
(res) => {
240+
let buf = "";
241+
res.on("data", (c) => (buf += c));
242+
res.on("end", () => {
243+
try {
244+
resolve(JSON.parse(buf || "{}"));
245+
} catch {
246+
resolve({ raw: buf });
247+
}
248+
});
249+
}
250+
);
251+
req.on("error", reject);
252+
req.write(body);
253+
req.end();
254+
});
255+
}
256+
```
257+
</details>
258+
259+
Use the helper from inside your Eliza agent (e.g., in a tool/action handler):
260+
261+
<details>
262+
<summary>Example usage in an Eliza handler</summary>
263+
264+
```ts
265+
import { ensureEnclaveWallet, signSubmitEthTx } from "./rofl";
266+
267+
// During agent startup (once):
268+
const { wallet } = await ensureEnclaveWallet("agent-identity-key");
269+
console.log("ROFL enclave EVM address:", wallet.address);
270+
271+
// Later, on user intent (e.g., tool call):
272+
await signSubmitEthTx({
273+
to: "0xYourContract",
274+
abi: [ /* contract ABI */ ],
275+
method: "yourMethod",
276+
args: [ /* arguments */ ],
277+
gasLimit: 200000,
278+
});
279+
```
280+
</details>
281+
282+
#### Wire into ElizaOS (enable tool and prompting)
283+
284+
Add a small tool wrapper Eliza can call. Register it in your agent config and nudge the model to invoke it via system instructions.
285+
286+
<details>
287+
<summary>tools/rofl.ts</summary>
288+
289+
```ts
290+
import { ensureEnclaveWallet, signSubmitEthTx } from "../src/rofl";
291+
292+
export async function generate_rofl_wallet(params?: { keyId?: string }) {
293+
const { wallet } = await ensureEnclaveWallet(params?.keyId ?? "agent-identity-key");
294+
return { address: wallet.address };
295+
}
296+
297+
export async function submit_rofl_tx(input: {
298+
to: string;
299+
abi: any[];
300+
method: string;
301+
args?: any[];
302+
gasLimit?: number;
303+
}) {
304+
return await signSubmitEthTx(input);
305+
}
306+
```
307+
</details>
308+
309+
Register the tool in your Eliza agent configuration (adjust pathing to your build output):
310+
311+
<details>
312+
<summary>agent.config.json</summary>
313+
314+
```json
315+
{
316+
"name": "rofl-eliza",
317+
"model": "openai:gpt-4o-mini",
318+
"tools": ["./dist/tools/rofl.js"]
319+
}
320+
```
321+
</details>
322+
323+
Guide the model to use the tool via system prompt:
324+
325+
<details>
326+
<summary>system-prompt.txt</summary>
327+
328+
```
329+
You are a ROFL-enabled Eliza agent. When a user asks to create a wallet or perform an on-chain action:
330+
- First call generate_rofl_wallet to ensure an enclave-bound EVM address exists.
331+
- Then call submit_rofl_tx with the target contract (to), ABI, method and args.
332+
Never expose private keys; only share the EVM address.
333+
```
334+
</details>
335+
336+
Ensure the appd UNIX socket is available to your container:
337+
338+
<details>
339+
<summary>compose.yaml (socket mount)</summary>
340+
341+
```yaml
342+
services:
343+
eliza-agent:
344+
# ...
345+
volumes:
346+
- /run/rofl-appd.sock:/run/rofl-appd.sock
347+
```
348+
</details>
349+
350+
Install dependencies and build with your app:
351+
352+
```shell
353+
npm i ethers @oasisprotocol/rofl-client typescript
354+
npx tsc
355+
```
356+
357+
Your `docker/entry.sh` can continue to start just the Eliza agent:
358+
359+
```shell
360+
#!/bin/sh
361+
set -e
362+
exec elizaos start
363+
```
364+
196365
## Testing it out
197366

198367
After `oasis rofl deploy`, use the CLI to check machine status and view logs.

0 commit comments

Comments
 (0)