@@ -117,8 +117,8 @@ WORKDIR /app
117117# Copy your ElizaOS project (created with `elizaos create ...`).
118118ADD . /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.
124124ENTRYPOINT ["/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
198367After ` oasis rofl deploy ` , use the CLI to check machine status and view logs.
0 commit comments