1Do App
Session Pay
The wallet signs session boundaries, the session key authorizes only increasing cumulative totals, and any relayer may submit while the chain pays only the delta.
Grant structure
Session Pay has no createSession transaction. The wallet signs SessionGrant offchain, and every settle call resubmits the grant, wallet signature, and session-key signature; the full authorization is not stored onchain.
The EIP-712 domain is Session Pay on 1Do / 1, bound to the current chainId and wallet verifyingContract. The wallet signature is checked through ERC-1271.
- sessionKey and payee must be nonzero, and spendLimit must be greater than zero.
- token fixes the payment asset: address(0) is native currency and any other address is ERC-20.
- spendLimit is the cumulative cap for the entire session, not a per-payment limit.
- sessionExpiresAt is the final submission timestamp. The grant remains valid exactly at that time and fails afterward; zero is immediately expired, not unlimited.
- salt distinguishes independent grants whose other fields are identical.
struct SessionGrant {
address sessionKey;
address payee;
address token; // address(0) = native asset
uint256 spendLimit;
uint256 sessionExpiresAt;
bytes32 salt;
}
struct SettlementAuthorization {
bytes32 sessionId;
uint256 newTotalPaid;
}sessionId and two signatures
sessionId is the EIP-712 struct hash of SessionGrant, not the full domain-separated digest. Because it does not itself include chainId or the wallet, index and database keys should include at least chainId + wallet + sessionId.
The wallet selfSig authorizes the full grant boundary, while sessionSig covers only SettlementAuthorization(sessionId, newTotalPaid). Both signatures are checked on every settlement.
- selfSig is verified through ERC-1271 on the executing wallet, binding the grant to that wallet and chain.
- sessionSig uses standard ECDSA recovery and must recover grant.sessionKey; a contract wallet cannot currently act directly as the session key.
- Any address may relay settle but cannot change payee, token, cap, expiry, or cumulative total.
Cumulative delta settlement
Read onchain progress
The client reads previousTotalPaid and revoked for the wallet-scoped sessionId.
Sign a new cumulative total
The session key signs newTotalPaid strictly above the previous value and no greater than spendLimit.
Submit complete proof
A relayer submits grant, authorization, selfSig, and sessionSig; the app rechecks expiry, revocation, and both signatures.
Pay only the delta
Payment equals newTotalPaid - previousTotalPaid. State advances first, then native currency or ERC-20 goes directly from the wallet to the fixed payee.
The cumulative total replaces a conventional nonce
There is no separate incrementing nonce for a sessionId. Old, equal, or lower totals fail the strict-increase check, so replaying an old authorization cannot pay again.
Revocation and lifecycle
- revokeSession(sessionId) requires a wallet self-call. Revocation has no undo operation, and repeated calls do not emit another event.
- Disabling the app does not clear totalPaid or revoked. Re-enabling the same logic lets an unexpired, unrevoked session continue from its prior total.
- Expiry, cap exhaustion, and revocation are not stored as one status enum; clients derive them from the grant and wallet state.
- An unknown sessionId and a new unpaid session both return totalPaid = 0 and revoked = false, so those values alone do not prove an offchain grant exists.
State reads, events, and SDK
- Read totalPaid(sessionId), revoked(sessionId), and sessionState(sessionId) in the target wallet Runtime context.
- SessionSettled records sessionId, payee, token, paymentAmount, newTotalPaid, and sessionKey; SessionRevoked records sessionId. The wallet is the event emitter.
- sessionIdOf(grant) computes the struct hash. The SDK's technical OneDoSessionPay namespace provides grant/authorization hashes, typed data, and session-key signing helpers.
Funding and failure boundaries
- A grant does not reserve funds or schedule automatic debits. settle is nonpayable, so the wallet must already hold enough native currency or ERC-20 when execution occurs.
- Funds go directly from the wallet to grant.payee, never through the relayer. A token-transfer or native-recipient failure reverts the totalPaid update.
- ERC-20 uses SafeERC20, but there is no net-receipt check for fee-on-transfer or rebasing tokens. paymentAmount is the requested wallet outflow, not a guarantee of identical payee receipt.
- Invalid, expired, or revoked grants; mismatched sessionIds; non-increasing or over-limit totals; and either invalid signature all fail.
- The asset and payee stay fixed for the grant. Changing any field produces a different sessionId and requires a new wallet signature.