FRONTEND

Send a call

session.send(fn, args). No gas, no wallet prompt. The return value comes back in the same round trip.

After openSession, every call is session.send. The wallet is not involved. The generated key signs a wrapper, the node executes, and the app’s return type comes back decoded.

app/play.ts
await session.send("play");
const score = await interlude.read("currentScore");

// Public demo:
// await session.send("join");
// await session.send("move", [1]); // 0 N · 1 E · 2 S · 3 W

What send actually does

It encodes the call from the ABI, wraps it in withSession(grant, signature, call), signs that wrapper with the session key, and unwraps the bytes the wrapper returns back into your function’s own type. Arguments go in the second parameter: session.send("move", [1]).

Nonces are tracked client-side so you can fire calls as fast as the node answers. If they drift (two tabs, a retry), the SDK resynchronises once and retries. You do not manage them.

The path that looks right and is wrong

Do not point a viem wallet at the node with custom(window.ethereum) and call writeContract. That is a real wallet signature on every action, which is the problem this session exists to remove. The node will accept it. Your users will not.

latencyMs is the round trip you just paid, measured where the call was made, signing and queueing included. The SDK sends over a WebSocket it keeps open, so it is one round trip. Next to the node an Exchange order or a Kandle tap takes about 3 ms; the network round trip alone is 38-42 ms p50. The first call of a session costs more, because the node reads that account’s slots from Monad. Either way it is the call, not the block. Figures and methods: Performance.