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.
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 WWhat 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 in the browser. Next to the machine it is a fraction of a millisecond. Behind a user’s 30 ms of internet it is still the call, not the block.
