Session Keys
What are session keys?
Section titled “What are session keys?”Session keys are ephemeral signing keys that can perform a limited set of operations on behalf of a root wallet. They are registered on-chain via the SessionKeyRegistry contract, which stores permission grants as time-limited authorizations.
This solves a common problem in dApps: without session keys, every storage operation (creating a dataset, uploading pieces, scheduling deletions) requires the user to approve a wallet popup. With session keys, the root wallet authorizes a temporary key once, and that key handles subsequent signing silently until it expires.
Key concepts
Section titled “Key concepts”- Root wallet - The user’s primary wallet (e.g., MetaMask). Owns the identity, funds, and datasets. Used to authorize session keys via
login(). - Session key - An ephemeral key pair that signs operations on behalf of the root wallet. Has no funds or on-chain identity of its own.
- Permissions - Each authorization grants specific operation types until an expiry timestamp. Permissions are identified by
bytes32hashes (by convention, EIP-712 type hashes). - Expiry - Authorizations are time-limited. The SDK defaults to 1 hour; the contract stores whatever expiry is provided.
Permissions
Section titled “Permissions”The SessionKeyRegistry stores arbitrary bytes32 hashes as permissions and is agnostic to what they represent. By convention, the SDK uses EIP-712 type hashes to identify operations:
| Constant | Operation |
|---|---|
CreateDataSetPermission | Create new datasets |
AddPiecesPermission | Add pieces to datasets |
SchedulePieceRemovalsPermission | Schedule piece removals |
DeleteDataSetPermission | Delete datasets |
These are the constants currently supported by FWSS. The Permission type also accepts any Hex value, allowing registration of custom permission hashes for non-FWSS operations (e.g., authenticated Curio HTTP endpoints).
Quick start
Section titled “Quick start”A complete session key lifecycle from creation through to use:
import * as import SessionKey
SessionKey from '@filoz/synapse-core/session-key'import { const calibration: Chain
calibration } from '@filoz/synapse-core/chains'import { function createWalletClient<transport extends Transport, chain extends Chain | undefined = undefined, accountOrAddress extends Account | Address | undefined = undefined, rpcSchema extends RpcSchema | undefined = undefined>(parameters: WalletClientConfig<transport, chain, accountOrAddress, rpcSchema>): WalletClient<transport, chain, ParseAccount<accountOrAddress>, rpcSchema>
Creates a Wallet Client with a given Transport configured for a Chain.
A Wallet Client is an interface to interact with Ethereum Account(s) and provides the ability to retrieve accounts, execute transactions, sign messages, etc. through Wallet Actions.
The Wallet Client supports signing over:
- JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc).
- Local Accounts (e.g. private key/mnemonic wallets).
createWalletClient, function http<rpcSchema extends RpcSchema | undefined = undefined, raw extends boolean = false>(url?: string | undefined, config?: HttpTransportConfig<rpcSchema, raw>): HttpTransport<rpcSchema, raw>
http, type type Hex = `0x${string}`
Hex } from 'viem'import { function privateKeyToAccount(privateKey: Hex, options?: PrivateKeyToAccountOptions): PrivateKeyAccount
privateKeyToAccount, function generatePrivateKey(): Hex
generatePrivateKey } from 'viem/accounts'
// The root wallet (the user's primary wallet)const const rootAccount: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local";}
rootAccount = function privateKeyToAccount(privateKey: Hex, options?: PrivateKeyToAccountOptions): PrivateKeyAccount
privateKeyToAccount('0x<root-private-key>' as type Hex = `0x${string}`
Hex)const const rootClient: { account: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local"; }; ... 41 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>;}
rootClient = createWalletClient<HttpTransport<undefined, false>, Chain, { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; ... 4 more ...; type: "local";}, undefined>(parameters: { ...;}): { ...;}
Creates a Wallet Client with a given Transport configured for a Chain.
A Wallet Client is an interface to interact with Ethereum Account(s) and provides the ability to retrieve accounts, execute transactions, sign messages, etc. through Wallet Actions.
The Wallet Client supports signing over:
- JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc).
- Local Accounts (e.g. private key/mnemonic wallets).
createWalletClient({ account?: `0x${string}` | { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local";} | Account | undefined
The Account to use for the Client. This will be used for Actions that require an account as an argument.
account: const rootAccount: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local";}
rootAccount, chain?: Chain | Chain | undefined
Chain for the client.
chain: const calibration: Chain
calibration, transport: HttpTransport<undefined, false>
The RPC transport
transport: http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>
http(),})
// Create an ephemeral session keyconst const privateKey: `0x${string}`
privateKey = function generatePrivateKey(): Hex
generatePrivateKey()const const sessionKey: Secp256k1SessionKey
sessionKey = import SessionKey
SessionKey.function fromSecp256k1(options: SessionKey.FromSecp256k1Options): Secp256k1SessionKeyexport fromSecp256k1
fromSecp256k1({ FromSecp256k1Options.privateKey: `0x${string}`
privateKey, FromSecp256k1Options.root: `0x${string}` | Account
root: const rootAccount: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local";}
rootAccount, // Account or Address FromSecp256k1Options.chain: Chain
chain: const calibration: Chain
calibration,})
// Authorize the session key on-chain (root wallet signs this tx)const { const event: Log<bigint, number, false, undefined, true, readonly [{ readonly type: "function"; readonly inputs: readonly [{ readonly name: "user"; readonly internalType: "address"; readonly type: "address"; }, { readonly name: "signer"; readonly internalType: "address"; readonly type: "address"; }, { readonly name: "permission"; readonly internalType: "bytes32"; readonly type: "bytes32"; }]; readonly name: "authorizationExpiry"; readonly outputs: readonly [{ readonly name: ""; readonly internalType: "uint256"; readonly type: "uint256"; }]; readonly stateMutability: "view";}, { ...;}, { ...;}, { ...;}, { ...;}], "AuthorizationsUpdated">
event } = await import SessionKey
SessionKey.function loginSync(client: Client<Transport, Chain, Account>, options: SessionKey.loginSync.OptionsType): Promise<SessionKey.loginSync.OutputType>
loginSync(const rootClient: { account: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local"; }; ... 41 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>;}
rootClient, { address: `0x${string}`
address: const sessionKey: Secp256k1SessionKey
sessionKey.Secp256k1SessionKey.address: `0x${string}`
address, onHash?: ((hash: Hash) => void) | undefined
onHash(hash: `0x${string}`
hash) { var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log('Tx submitted:', hash: `0x${string}`
hash) },})
// Sync expirations from the chain so hasPermission() works locallyawait const sessionKey: Secp256k1SessionKey
sessionKey.Secp256k1SessionKey.syncExpirations(permissions?: SessionKey.Permission[]): Promise<void>
syncExpirations()
// Now use sessionKey.client in place of rootClient for SDK operations.// sessionKey.client signs with the session key; sessionKey.rootAddress// identifies the root wallet as the payer/identity.Detailed usage
Section titled “Detailed usage”Create a session key
Section titled “Create a session key”fromSecp256k1() creates a session key from a secp256k1 private key. It returns a SessionKey<'Secp256k1'> instance.
import * as import SessionKey
SessionKey from '@filoz/synapse-core/session-key'import { const calibration: Chain
calibration } from '@filoz/synapse-core/chains'import type { type Hex = `0x${string}`
Hex } from 'viem'import { function privateKeyToAccount(privateKey: Hex, options?: PrivateKeyToAccountOptions): PrivateKeyAccount
privateKeyToAccount } from 'viem/accounts'
const const rootAccount: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local";}
rootAccount = function privateKeyToAccount(privateKey: Hex, options?: PrivateKeyToAccountOptions): PrivateKeyAccount
privateKeyToAccount('0x<root-private-key>' as type Hex = `0x${string}`
Hex)
const const sessionKey: Secp256k1SessionKey
sessionKey = import SessionKey
SessionKey.function fromSecp256k1(options: SessionKey.FromSecp256k1Options): Secp256k1SessionKeyexport fromSecp256k1
fromSecp256k1({ FromSecp256k1Options.privateKey: `0x${string}`
privateKey: '0x...' as type Hex = `0x${string}`
Hex, // secp256k1 private key for the session key FromSecp256k1Options.root: `0x${string}` | Account
root: const rootAccount: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local";}
rootAccount, // Account or Address of the authorizing wallet FromSecp256k1Options.chain: Chain
chain: const calibration: Chain
calibration, // chain definition (calibration or mainnet) // transport: http(customRpc), // optional, defaults to http() // expirations: { ... }, // optional, pre-populate known expirations})The session key is inert until authorized. It holds a viem Client internally (sessionKey.client) that uses the session key for signing and carries sessionKey.rootAddress as the identity.
Authorize the session key (login)
Section titled “Authorize the session key (login)”The root wallet authorizes the session key on-chain. login() and loginSync() both require a viem WalletClient (a Client with an Account):
// Fire-and-forget (returns tx hash: Hex)const const hash: `0x${string}`
hash = await import SessionKey
SessionKey.function login(client: Client<Transport, Chain, Account>, options: SessionKey.login.OptionsType): Promise<SessionKey.login.OutputType>
login(const rootClient: { account: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local"; }; ... 41 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>;}
rootClient, { address: `0x${string}`
address: const sessionKey: Secp256k1SessionKey
sessionKey.Secp256k1SessionKey.address: `0x${string}`
address,})
// Or wait for confirmation (returns { receipt, event })const { const receipt: TransactionReceipt
receipt, const event: Log<bigint, number, false, undefined, true, readonly [{ readonly type: "function"; readonly inputs: readonly [{ readonly name: "user"; readonly internalType: "address"; readonly type: "address"; }, { readonly name: "signer"; readonly internalType: "address"; readonly type: "address"; }, { readonly name: "permission"; readonly internalType: "bytes32"; readonly type: "bytes32"; }]; readonly name: "authorizationExpiry"; readonly outputs: readonly [{ readonly name: ""; readonly internalType: "uint256"; readonly type: "uint256"; }]; readonly stateMutability: "view";}, { ...;}, { ...;}, { ...;}, { ...;}], "AuthorizationsUpdated">
event } = await import SessionKey
SessionKey.function loginSync(client: Client<Transport, Chain, Account>, options: SessionKey.loginSync.OptionsType): Promise<SessionKey.loginSync.OutputType>
loginSync(const rootClient: { account: { address: Address; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: Hash; }) => Promise<Hex>; signAuthorization: (parameters: AuthorizationRequest) => Promise<SignAuthorizationReturnType>; signMessage: ({ message }: { message: SignableMessage; }) => Promise<Hex>; signTransaction: <serializer extends SerializeTransactionFn<TransactionSerializable> = SerializeTransactionFn<TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: { serializer?: serializer | undefined; } | undefined) => Promise<Hex>; signTypedData: <const typedData extends TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<Hex>; publicKey: Hex; source: "privateKey"; type: "local"; }; ... 41 more ...; extend: <const client extends { ...; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>;}
rootClient, { address: `0x${string}`
address: const sessionKey: Secp256k1SessionKey
sessionKey.Secp256k1SessionKey.address: `0x${string}`
address, expiresAt?: bigint | undefined
expiresAt: var BigInt: BigIntConstructor(value: bigint | boolean | number | string) => bigint
BigInt(var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math.Math.floor(x: number): number
Returns the greatest integer less than or equal to its numeric argument.
floor(var Date: DateConstructor
Enables basic storage and retrieval of dates and times.
Date.DateConstructor.now(): number
Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).
now() / 1000) + 7200), // 2 hours onHash?: ((hash: Hash) => void) | undefined
onHash(hash: `0x${string}`
hash) { var console: Console
console.Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
log('Tx submitted:', hash: `0x${string}`
hash) },})// event is the AuthorizationsUpdated log with args: { identity, permissions, expiry }By default, login() grants all four FWSS permissions (DefaultFwssPermissions) with a 1-hour expiry. Both permissions and expiresAt are configurable.
To grant only specific permissions:
await SessionKey.login(rootClient, { address: sessionKey.address, permissions: [ SessionKey.AddPiecesPermission, SessionKey.SchedulePieceRemovalsPermission, ],})To grant a custom (non-FWSS) permission:
await SessionKey.login(rootClient, { address: sessionKey.address, permissions: [ '0xabcdef...' as Hex, // any bytes32 hash ],})Use the session key for operations
Section titled “Use the session key for operations”Pass sessionKey.client to SDK operations. The session key signs the EIP-712 typed data while sessionKey.rootAddress is used as the payer/identity:
import { createDataSet, waitForCreateDataSet } from '@filoz/synapse-core/sp'
const result = await createDataSet(sessionKey.client, { payee: providerAddress, // Address: the SP's address payer: sessionKey.rootAddress, // Address: the root wallet paying for storage serviceURL: 'https://provider.example.com',})
const dataset = await waitForCreateDataSet(result)Use with the Synapse class
Section titled “Use with the Synapse class”The Synapse class accepts a sessionKey option (SessionKey<'Secp256k1'>) and uses it automatically for eligible operations (dataset creation, piece uploads, piece deletions):
import { Synapse } from '@filoz/synapse-sdk'
const synapse = Synapse.create({ account: rootAccount, chain: calibration, transport: http(rpcUrl), sessionKey: sessionKey,})Synapse.create() validates that the session key has all four FWSS permissions (DefaultFwssPermissions) and that none are expired. This means the session key’s expirations must be populated before construction, either by passing expirations to fromSecp256k1(), or by calling sessionKey.syncExpirations() after login.
Revoke the session key
Section titled “Revoke the session key”When done, the root wallet can revoke permissions:
// Fire-and-forget (returns tx hash: Hex)const hash = await SessionKey.revoke(rootClient, { address: sessionKey.address,})
// Or wait for confirmation (returns { receipt, event })await SessionKey.revokeSync(rootClient, { address: sessionKey.address, onHash(hash) { console.log('Revoking:', hash) },})Both default to revoking all FWSS permissions. Pass permissions to revoke selectively.
Expirations and refresh
Section titled “Expirations and refresh”Session key permissions have a fixed expiry set during login(). When a permission expires, any operation signed with that session key will revert on-chain.
The SDK does not automatically track or refresh expirations. For short-lived sessions (login, perform operations, done), this is not a concern. For long-lived sessions, the developer should:
- Check
sessionKey.hasPermission(permission)before operations if expirations are populated - Call
sessionKey.syncExpirations()periodically to refresh cached state from the chain - Call
login()again from the root wallet when permissions are near expiry
Errors from expired session keys will surface as contract reverts. The SDK does not currently distinguish these from other revert causes.
Checking permissions
Section titled “Checking permissions”hasPermission() and hasPermissions() are local checks against cached expiration timestamps. They return true if the permission’s expiry is in the future:
// Check a single permission (returns boolean)if (sessionKey.hasPermission(SessionKey.CreateDataSetPermission)) { // safe to create dataset}
// Check all FWSS permissions at once (returns boolean)if (sessionKey.hasPermissions(SessionKey.DefaultFwssPermissions)) { // all FWSS permissions are valid}These require that expirations have been populated via one of:
fromSecp256k1({ expirations: ... })at creation timesessionKey.syncExpirations()(fetches from chain via multicall)sessionKey.watch()(syncs and subscribes to live updates)
Real-time tracking (optional)
Section titled “Real-time tracking (optional)”For dApps that need live permission state (e.g., to update UI when permissions expire or are revoked):
const unwatch = await sessionKey.watch()
sessionKey.on('expirationsUpdated', (e: CustomEvent<Expirations>) => { console.log('Permissions changed:', e.detail)})
sessionKey.on('error', (e: CustomEvent<Error>) => { console.error('Watch error:', e.detail)})
// When done, clean up the subscriptionunwatch()// or: sessionKey.unwatch()watch() syncs expirations from the chain, starts a watchContractEvent subscription for AuthorizationsUpdated events, and returns a cleanup function. You can also call sessionKey.unwatch() directly. This is primarily useful for dApp UI; server-side code can use syncExpirations() directly.
Custom permissions
Section titled “Custom permissions”The four FWSS constants are SDK conveniences, not an exhaustive set. Any bytes32 hash can be registered as a permission. To work with custom permissions:
import * as SessionKey from '@filoz/synapse-core/session-key'import { createPublicClient, http, type Hex } from 'viem'import { calibration } from '@filoz/synapse-core/chains'
const publicClient = createPublicClient({ chain: calibration, transport: http(),})
const myPermission = '0x...' as Hex
// Grant (requires root wallet client)await SessionKey.login(rootClient, { address: sessionKey.address, permissions: [myPermission],})
// Check single expiry (returns bigint, 0n if no authorization exists)const expiry = await SessionKey.authorizationExpiry(publicClient, { address: rootAddress, // Address: the root wallet sessionKeyAddress: sessionKey.address, permission: myPermission,})
// Batch check (returns Record<Permission, bigint>)const expirations = await SessionKey.getExpirations(publicClient, { address: rootAddress, sessionKeyAddress: sessionKey.address, permissions: [myPermission, SessionKey.AddPiecesPermission],})Next Steps
Section titled “Next Steps”- Session Keys API: Reference documentation