Open a session
The user signs one EIP-712 message. After that, a generated key signs. The wallet is not prompted again.
A session is a grant: an EIP-712 message the user signs with their wallet. The message names a freshly generated secp256k1 key, an expiry (one hour by default), the hub’s current session epoch, and the selectors that key may call. The app verifies the signature on every call and resolves _actor() to the granter, so your contract sees the user, never the key.
const session = await interlude.openSession({
wallet, // the user's wallet. Prompted exactly once.
scope: ["play"], // First hour. The demo uses join / move / …
});
session.granter; // the user. What the app's _actor() returns.
session.sessionKey; // the generated key. Holds no funds.
session.expiresAt; // one hour by default.What the wallet prompt says
A grant with no scope authorises nothing. That is deliberate: the prompt should say only join and move, not "this site can do anything". Pass anyFunction: true only when you mean a blanket grant. Overloads take the full signature.
The SDK fills in the parts that are easy to get wrong. chainId is Monad’s, not the node’s. verifyingContract is the app. The epoch is hub.sessionEpochOf(granter) as of signing: a grant that names an old epoch is stale on arrival, which is how a revoke actually stops the key.
Refresh does not prompt again
openSession restores from sessionStorage before it opens the wallet. A refresh mid-game costs the user nothing, as long as the grant has not expired and they have not revoked. The cost of that convenience is on the revoke page.
