Home

Awesome

æREPL-web

Description

æREPL-web is a websocket interface to æREPL which allows executing æternity Sophia Smart Contract Language interactively through Elixir channels.

Setup

git clone https://github.com/aeternity/aerepl-web
cd aerepl-web && mix deps.get
export ERL_LIBS=$ERL_LIBS:$(pwd)/deps/aerepl/_build/prod/lib
cd assets && npm install && cd ..
mix phx.server

Now you can visit localhost:4000 from your browser.

Connection

To communicate with the server you will need to use channels provided by Phoenix Framework. The connection is bound to a session on the server side where the REPL's state is kept. See the example in assets/js/socket.js.

Example setup

Import the necessary dependency:

import {Socket} from "phoenix";

To open the channel you will need to join the repl_session lobby:

let channel = socket.channel("repl_session:lobby", {});

Then register the callback for incoming messages:

channel.on(/*reponse type*/, payload => {
    ...
});

It may be good to register several additional handlers:

channel.onError( () => console.log("Error") );
channel.onClose( () => console.log("Successful close") );


channel.join()
    .receive("ok", resp => { console.log("Joined successfully"); })
    .receive("error", resp => { console.log("Unable to join", resp); });

To push a message to a channel use the push method:

channel.push(/*message type*/, /*payload*/);

Protocol

Opening a channel

socket.channel("repl_session:lobby", DATA);

Fields in DATA:

Response fields:

Standard responses

Most calls have the same response format. The fields are as follows:

"call_str"

CLI-style interface to the REPL.

Input fields:

Response fields standard.

"call" (unstable)

Raw interface to the underlying Erlang gen_server. Don't use unless you really want to hack.

Input fields:

"cast" (unstable)

Same as in call, but does not return anything.

"app_version"

Input fields: (none)

Response: application version as string.

"banner"

Returns a nice show-offy banner wit the REPL logo and version information.

Input fields:

Response: banner as string.

"prompt"

Returns a suggestion for the user prompt, which indicates the REPL state.

Input fields:

Response fields standard. Possible values of msg:

"reset"

Reset the REPL state.

Input fields:

Response fields standard.

"type"

Typechecks a Sophia expression

Input fields:

Response fields standard.

"state"

Sets the in-REPL contract store (the value of state in Sophia)

Input fields:

Response fields standard.

"eval"

Evaluates a Sophia expression.

Input fields:

Response fields standard.

"load"

Loads files into the REPL context. Only the first one will be explicitly included. Note that the files have to be first uploaded using update_filesystem_cache. It also does not deploy contracts, but only makes them visible in the REPL context.

Input fields:

Response fields standard.

"reload"

Reloads already files that have been loaded. Note that the files have to be first uploaded using update_filesystem_cache.

Input fields:

Response fields standard.

"update_filesystem_cache"

Uploads files to the REPL filesystem cache to make them accessible by the load and reload commands. Note that this does not deploy any contracts, but only makes them visible in the REPL context.

Input fields:

Response is empty.

"set"

Adjusts REPL's behavior. See :help set or aerepl documentation for more details.

Input fields:

Response fields standard.

"help"

Returns information about commands. Note that it's mostly relevant for CLI-style users.

Input fields:

Response fields standard.

"disas"

Presents FATE assembly of a thing.

Input fields:

Response fields standard.

"break"

Sets a breakpoint.

Input fields:

Response fields standard.

"delete_break"

Removes breakpoint by id.

Input fields:

Response fields standard.

"delete_break_loc"

Removes breakpoint by location.

Input fields:

Response fields standard.

"continue"

Resumes paused execution.

Input fields:

Response fields standard.

"stepover"

Moves paused execution by one line in the source code, skipping function calls.

Input fields:

Response fields standard.

"stepin"

Moves paused execution by one line in the source code, entering function calls.

Input fields:

Response fields standard.

"stepout"

Moves paused execution until return of the currently visited function.

Input fields:

Response fields standard.

"location"

Shows location in code of currently paused execution.

Input fields:

Response fields standard.

"print_var"

Prints value of a local variable in the currently paused execution.

Input fields:

Response fields standard.

"print_vars"

Prints values of all local variables in the currently paused execution.

Input fields:

Response fields standard.

"stacktrace"

Prints the stacktrace.

Input fields:

Response fields standard.

"version"

Returns version information about the REPL itself, FATE, Sophia and aeternity node.

Input fields:

Response fields standard.

Examples

What's the type of Oracle.query?

Two ways to do it:

Then the result can be accessed like r.receive("ok", resp => console.log(resp.msg)).

Deploy a contract

First, obtain the contract source

let src = "contract MyContract = entrypoint f() = 123\n";

Upload the source to the REPL file cache. The contract is not deployed, nor even typechecked yet. If you are working with multiple files, include them all in the files field.

let name = "MyContract.aes";
let fs = [{filename: name, content: src}];

channel.push("update_filesystem_cache", {files: fs, user_session: session});

Load the contract in the REPL context. This step is necessary for REPL to know which files to consider included directly while performing calls.

let result = channel.push("load", {files: [name], user_session: session});

Now, the result variable contains information about whether the contract was successfully loaded. This may not be the case for example when there are type errors.

result.receive("ok", (resp) => console.log(resp.msg));

There is no direct way to deploy contracts in the REPL, because Sophia/FATE can do it perfectly fine. To create an instance of the contract, run:

let deploy_cmd = "let my_contract = Chain.create() : MyContract";
channel.push("eval", {expr: cmd, user_session: session});

Finally, call the contract using the remote call notation from Sophia:

let call_cmd = "my_contract.f()";
channel.push("eval", {expr: cmd, user_session: session})
    .receive("ok", (resp) => console.log(resp.msg));