Method.tempo.session
Sessions server method
Choose a signing account
Create a server-only wallet.ts module, then import account wherever an example creates a local signing account.
import { privateKeyToAccount } from 'viem/accounts'
export const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)Creates a Tempo Sessions server method for voucher verification, channel accounting, top-ups, and settlement.
Usage
import { , , } from 'mppx/server'
import { } from 'viem/accounts'
const = ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
const = .({
: [
.({
,
: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
: .(),
}),
],
})With charge and session
Use the tempo() convenience function to register both one-time charges and Sessions.
import { , , } from 'mppx/server'
import { } from 'viem/accounts'
const = ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
const = .({
: [
({
,
: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
: .(),
}),
],
})With Legacy Sessions
Use tempo.sessionLegacy() only for clients that still send Legacy Sessions Credentials.
import { , , } from 'mppx/server'
import { } from 'viem/accounts'
const = ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
const = .({
: [
.({
,
: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
: .(),
}),
],
})Migrate from Legacy Sessions
Register tempo.session() beside tempo.sessionLegacy() during migration so existing clients keep working while new clients use Sessions.
| Server methods | Compatible client methods |
|---|---|
tempo.session() only | tempo.session() or tempo() |
tempo.sessionLegacy() only | tempo.sessionLegacy() or tempo.sessionLegacy.method() |
tempo.session() and tempo.sessionLegacy() | tempo.session() plus tempo.sessionLegacy.method() when clients may see both Challenge types |
Current Sessions Challenges include sessionProtocol: "v2" and use TIP-1034 reserve channels. Legacy Sessions use the contract-backed Sessions v1 flow. Channel state is not portable between them, so close or settle Legacy channels with tempo.sessionLegacy and open new channels with tempo.session.
import { , , } from 'mppx/server'
import { } from 'viem/accounts'
const = ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
const = .({
: [
.({
,
: 4217,
: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
: .(),
}),
.({
,
: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
: .(),
}),
],
})Use tempo.sessionLegacy() only while clients still send Legacy Sessions Credentials.
With same-route bootstrap
Set bootstrap: true and provide resolveChannelId when clients should recover a previous channel before opening a new one. Bootstrap uses the protected route itself: the client sends HEAD, answers a $0 Tempo charge Challenge, and the server uses the verified source plus request metadata to look up an existing channel.
import { , , } from 'mppx/server'
import { } from 'viem/accounts'
const = ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef')
const = .()
const = new <string, string>()
const = .({
: [
.({
,
: true,
: '0x20c0000000000000000000000000000000000000', // pathUSD on Tempo
,
({ , }) {
return ( ? .() : ) ?? ?..('Payment-Session') ??
},
}),
],
})If the resolver returns a known channel ID, the server responds with Payment-Session and Payment-Session-Snapshot. If it returns undefined, the server returns 204 without a snapshot and the client falls back to opening a new channel.
Return type
import type { Method } from 'mppx'
type ReturnType = Method.ServerParameters
account
- Type:
Account
Account used for server-driven settlement and close transactions. The account address is also used as the default recipient.
amount (optional)
- Type:
string
Default amount to charge per unit.
bootstrap (optional)
- Type:
boolean
Enables same-route HEAD bootstrap using a zero-amount identity proof. Clients can use this to recover a session snapshot before opening a new channel.
chainId (optional)
- Type:
number
Tempo chain ID used for Sessions Challenges. Use 4217 for mainnet and 42431 for Moderato testnet.
channelStateTtl (optional)
- Type:
number - Default:
5000
TTL in milliseconds for cached on-chain channel state. After this duration, the server re-queries on-chain state during voucher handling to detect forced close requests.
currency (optional)
- Type:
Address
Default TIP-20 token address for the payment currency.
decimals (optional)
- Type:
number - Default:
6
Decimal places for amount parsing.
escrowContract (optional)
- Type:
Address
Reserve precompile address override.
feePayer (optional)
- Type:
Account | string | true
Account or URL for sponsoring open, top-up, settlement, and close transaction fees. Pass a viem Account to co-sign locally, a URL string to delegate to a remote fee payer service, or true when the account parameter doubles as the fee payer.
feePayerPolicy (optional)
- Type:
Partial<{ maxFeePerGas: bigint; maxGas: bigint; maxPriorityFeePerGas: bigint; maxTotalFee: bigint; maxValidityWindowSeconds: number }>
Override the local fee-sponsor policy used for sponsored open and top-up transactions and server-driven close transactions.
feeToken (optional)
- Type:
Address
Optional fee token used for server-driven close transactions.
getClient (optional)
- Type:
(parameters: { chainId?: number }) => MaybePromise<Client>
Function that returns a viem client for the given chain ID.
minVoucherDelta (optional)
- Type:
string - Default:
"0"
Minimum voucher delta to accept as a numeric string. Rejects vouchers where the increment over the previous highest voucher is below this threshold.
operator (optional)
- Type:
Address
Payee-side operator authorized for channel operations.
recipient (optional)
- Type:
Address
Default recipient address for payments. Defaults to the account address.
resolveChannelId (optional)
- Type:
ResolveSessionChannelId
Function that resolves a reusable channel ID from request identity when no Credential or request channel ID is present.
type ResolveSessionChannelId = (
parameters: ResolveSessionChannelIdParameters,
) => Promise<string | null | undefined> | string | null | undefined
type ResolveSessionChannelIdParameters = {
request?: {
readonly headers: Headers
readonly hasBody?: boolean
readonly method: string
readonly url?: URL
}
credential: Credential | null | undefined
source?: string
paymentRequest: SessionPaymentRequestInput
store: ChannelStore
}Use source for identity bootstrap. It is only set after the server verifies the client's zero-amount proof. Use request.headers for app session cookies or explicit Payment-Session hints. The returned channel ID is treated as a hint; the server still verifies that the channel exists and matches the current payment request.
settlementSchedule (optional)
- Type:
{ amount?: string | bigint; intervalMs?: number; units?: number }
Server-owned automatic settlement cadence. Clients don't receive or control this schedule.
sse (optional)
- Type:
boolean | { poll?: boolean; pollingInterval?: number }
Enable SSE streaming. Pass true to enable with defaults, or pass an options object to configure SSE.
When poll is enabled, the stream checks the store at pollingInterval instead of waiting on store notifications. Use this for runtimes where promises cannot be resolved across request contexts.
store (optional)
- Type:
Store.AtomicStore - Default:
Store.memory()
Atomic store backend for channel state. Session mutations must be linearizable across instances so voucher, top-up, spend, and close updates cannot race.
import { , } from 'mppx/server'
import { } from 'viem/accounts'
const = .({
: ('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'),
: .(),
})Use Store.memory() for local development and tests. For multi-instance deployments, use Store.redis(), Store.upstash(), or Store.cloudflare().
suggestedDeposit (optional)
- Type:
string
Suggested deposit amount communicated to clients in the Challenge.
unitType (optional)
- Type:
string
Unit type label, such as "token", "byte", or "request".
Related
Snapshot helpers
Use these helpers when you need to read or write the Payment-Session-Snapshot header yourself.
import { tempo } from 'mppx/server'
const header = tempo.session.serializeSnapshot(snapshot)
const snapshot = tempo.session.deserializeSnapshot(header)type SessionSnapshot = {
acceptedCumulative: string
chainId: number
channelId: Hex
closeRequestedAt?: string
deposit: string
descriptor: ChannelDescriptor
escrow: Address
requiredCumulative: string
settled: string
spent: string
units?: number
}tempo.session.charge
Charges against a precompile-backed channel's stored balance outside an HTTP handler.
import { tempo } from 'mppx/server'
const state = await tempo.session.charge(store, channelId, 10_000n)This returns the updated channel state and throws if the channel is missing, closed, or does not have enough unspent deposit.
tempo.session.settle
Settles a single precompile-backed channel on-chain.
import { tempo } from 'mppx/server'
const txHash = await tempo.session.settle(store, client, channelId)The sender must be the channel payee or a nonzero operator. You can pass an account or fee-payer options as the fourth argument.
tempo.session.settleBatch
Settles multiple precompile-backed channels on-chain.
import { tempo } from 'mppx/server'
const txHashes = await tempo.session.settleBatch(store, client, channelIds)settleBatch applies the same validation as settle to each channel and returns the settlement transaction hashes in order.