SmartSessionEmissary extends SmartSessions to work with The Compact protocol for intent-based cross-chain operations. It combines SmartSessions' policy-based session key management with Compact's claim verification system.
SmartSessionEmissary is built on two foundational systems:
- SmartSessions (v1): ERC-7579 session key management with granular policy control
- VanillaEmissary: Basic emissary functionality for The Compact (ECDSA, WebAuthn, custom validators)
This contract adds claim policies and execution validation to enable session keys to authorize Compact claims within configurable constraints.
-
All SmartSessions v1 features:
- Granular session key control
- Action-specific policies
- ERC-1271 signature validation with ERC-7739 nested EIP-712
- Enable flow for permission requests
- Native ERC-7579 batched execution support
-
All VanillaEmissary features (inherited):
- Built-in ECDSA validation
- Built-in WebAuthn/Passkey validation
- Custom stateless validator support
-
New in SmartSessionEmissary:
- Claim policies for Compact protocol validation
- Execution validation for pre-claim and destination operations
- LockTag-scoped policy isolation
- Multi-mode signature routing
flowchart TB
subgraph User["👤 User"]
SA[Smart Account]
SK[Session Key]
end
subgraph Warp["🔷 Rhinestone Warp"]
ARB[Arbiter]
IE[IntentExecutor]
end
TC[📋 TheCompact]
subgraph Emissary["🔐 SmartSessionEmissary"]
VC[verifyClaim]
VE[verifyExecution]
IS[isValidSignature<br/>ERC-1271]
end
subgraph Policies["📜 Policy Layer"]
CP[Claim Policies]
AP[Action Policies]
SV[Session Validators]
end
SA -->|"Enable Session"| Emissary
SK -->|"Sign Intent"| Warp
ARB -->|"Fill Claim"| TC
TC -->|"Validate Claim"| VC
IE -->|"Execute Ops"| VE
VC --> CP
VC --> SV
VE --> AP
VE --> SV
IS --> SV
SmartSessionEmissary combines VanillaEmissary (inherited) with SmartSession policy enforcement:
| Component | Source | Purpose |
|---|---|---|
verifyClaim() |
New | Validates Compact claims with claim policies |
verifyExecution() |
New | Validates pre-claim/dest executions with action policies |
isValidSignatureWithSender() |
New | ERC-1271 with mode-based routing |
setConfig() / removeConfig() |
SmartSessionMixin | Session lifecycle management |
| ECDSA/Passkey validation | VanillaEmissary | Built-in signature validation |
| Custom validators | VanillaEmissary | External stateless validators |
The first byte of signature data determines the validation path:
flowchart LR
subgraph EmissaryModes["verifyClaim Modes"]
M0["0x00 VANILLA<br/>→ VanillaEmissary"]
M1["0x01 SMART_SESSION<br/>→ Claim Policies"]
end
subgraph SignatureModes["isValidSignature Modes"]
S0["0x00 IS_VALID_1271<br/>→ Direct validation"]
S1["0x01 IS_VALID_7739<br/>→ Nested EIP-712"]
end
When TheCompact calls verifyClaim with mode 0x01:
sequenceDiagram
participant ARB as Arbiter
participant TC as TheCompact
participant SSE as SmartSessionEmissary
participant CP as ClaimPolicy
participant SV as SessionValidator
ARB->>TC: fill(claim, ...)
TC->>SSE: verifyClaim(sponsor, digest, claimHash, emissaryData, lockTag)
Note over SSE: Mode 0x01 → SmartSession path
Note over SSE: Extract permissionId + signature
SSE->>SSE: Verify permissionId enabled for account
SSE->>CP: check1271SignedAction(configId, account, hash, policyData)
Note over CP: Validate: arbiter, expiry,<br/>tokenIn, recipient, tokenOut,<br/>originOps, destOps, qualification
Note over CP: Recompute EIP-712 hash
CP-->>SSE: valid / invalid
alt Policy Valid
SSE->>SV: validateSignatureWithData(digest, sig, initData)
SV-->>SSE: true / false
end
SSE-->>TC: selector or 0xFFFFFFFF
TC-->>ARB: claim filled
When IntentExecutor calls verifyExecution for pre-claim or destination operations:
sequenceDiagram
participant IE as IntentExecutor
participant SSE as SmartSessionEmissary
participant AP as ActionPolicy
participant SV as SessionValidator
IE->>SSE: verifyExecution(sponsor, digest, emissaryData, ops)
Note over SSE: Extract permissionId + signature
SSE->>SSE: Verify permissionId enabled
loop For each execution in ops
SSE->>AP: checkAction(configId, account, target, value, calldata)
Note over AP: Validate target address<br/>Validate function selector<br/>Validate parameters
AP-->>SSE: 0 (success) or revert
end
SSE->>SV: validateSignatureWithData(digest, sig, initData)
SV-->>SSE: true / false
SSE-->>IE: selector or 0xFFFFFFFF
A session defines what a session key can do:
classDiagram
class Session {
+ISessionValidator sessionValidator
+bytes sessionValidatorInitData
+bytes32 salt
+ActionData[] actions
+PolicyData[] claimPolicies
+ERC7739Data erc7739Policies
}
class ActionData {
+bytes4 actionTargetSelector
+address actionTarget
+PolicyData[] actionPolicies
}
class PolicyData {
+address policy
+bytes initData
}
Session "1" *-- "*" ActionData : for verifyExecution
Session "1" *-- "*" PolicyData : claimPolicies for verifyClaim
ActionData "1" *-- "*" PolicyData : actionPolicies
| Policy Type | Scope | Used By |
|---|---|---|
| Claim Policies | Per lockTag | verifyClaim - different allocators have isolated policy sets |
| Action Policies | Per permissionId (global) | verifyExecution - pre-claim and destination ops |
| ERC1271 Policies | Per permissionId (global) | isValidSignature calls |
sequenceDiagram
participant User as User/DApp
participant SSE as SmartSessionEmissary
User->>SSE: setConfig(account, config, enableData)
Note over SSE: Derive lockTag from<br/>allocator + scope + resetPeriod
SSE->>SSE: Verify expires > block.timestamp
SSE->>SSE: Increment nonce (replay protection)
SSE->>SSE: Compute session digest
alt First Enable
SSE->>SSE: Verify user signature only
else Subsequent Enable
SSE->>SSE: Verify user + allocator signatures
end
SSE->>SSE: Enable claim policies (scoped to lockTag)
SSE->>SSE: Enable action policies
SSE->>SSE: Enable ERC7739/1271 policies
SSE->>SSE: Enable session validator
SSE-->>User: emit SmartSessionEmissaryConfigUpdated
Claim policies validate Rhinestone Warp claims against configurable rules. The base policy supports these fields:
| Field | Description |
|---|---|
| Arbiter | Whitelist of allowed arbiters |
| Expiry | Min/max claim expiration bounds |
| TokenIn | Allowed input tokens (+ lockTag for Compact) |
| Recipient | Allowed recipient addresses per chain |
| FillExpiry | Min/max fill deadline bounds per chain |
| TokenOut | Whitelist of output tokens per chain |
| OriginOps | Whether origin operations are required |
| DestOps | Whether destination operations are required |
| Qualification | Parameter validation rules |
| RecipientIsSponsor | Flag to enforce recipient == sponsor (no storage lookup) |
Each field has a 2-bit mode controlling validation behavior:
Mode 00 = SKIP → Don't validate this field
Mode 01 = CHECK_STORAGE → Validate against stored config (exact chainId)
Mode 10 = CHECK_CATCHALL → Validate with chainId=0 fallback (wildcard)
Mode 11 = CHECK_SUBPOLICY → Delegate to external policy contract
All fields support all four modes, except RECIPIENT_IS_SPONSOR which is a simple flag - when mode != SKIP, it enforces recipient == sponsor with no storage lookup required.
- Nonce per (account, lockTag): Each session enable/disable increments a nonce specific to the account and lockTag combination
- Expires timestamp: All enable/disable operations have an expiration time
- Chain ID validation: Multichain sessions explicitly include chainIds to prevent cross-chain replay
| Operation | First Time | Subsequent |
|---|---|---|
| Enable Session | User signature only | User + Allocator signatures |
| Disable Session | User + Allocator signatures | User + Allocator signatures |
The rationale: initial setup creates the config before allocator has committed resources. Updates require allocator approval to protect allocated resources.
- LockTag scoping: Claim policies are isolated per lockTag, preventing cross-allocator policy conflicts
- PermissionId binding: Each session has a unique permissionId derived from
(sessionValidator, initData, salt) - No unsafe fallbacks:
UnsafeFallbackNotAllowederror prevents dangerous wildcard action policies
Validated digests are cached per (account, permissionId) to:
- Prevent double-validation attacks during multi-step flows
- Allow efficient re-verification within the same transaction
┌─────────────────────────────────────────────────────────────┐
│ Trust Boundaries │
├─────────────────────────────────────────────────────────────┤
│ User (Smart Account) │
│ └── Trusts: Session validators they configure │
│ └── Trusts: Policies they enable │
│ └── Controls: Which sessions exist │
├─────────────────────────────────────────────────────────────┤
│ Allocator │
│ └── Controls: Whether sessions can be modified │
│ └── Protects: Resources allocated to the account │
├─────────────────────────────────────────────────────────────┤
│ Session Key Holder │
│ └── Can only: Sign within policy constraints │
│ └── Cannot: Modify session configuration │
│ └── Cannot: Exceed policy limits │
├─────────────────────────────────────────────────────────────┤
│ IntentExecutor │
│ └── Only caller: That can invoke verifyExecution │
│ └── Enforced by: onlyIntentExecutor modifier │
└─────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ [0] Mode byte (0x01 = EMISSARY_SMART_SESSION) │
│ [1:33] PermissionId (bytes32) │
│ [33:65] Policy data offset (uint256) │
│ [65:N] Session validator signature │
│ [N:...] Policy-specific data (for claim policies) │
└────────────────────────────────────────────────────────────┘
For simple ECDSA/Passkey validation without policies:
┌────────────────────────────────────────────────────────────┐
│ [0] Mode byte (0x00 = EMISSARY_VANILLA) │
│ [1:21] Validator address (ECDSA=0x1, Passkey=0x1001) │
│ [21] Config ID (uint8) │
│ [22:...] Signature data │
└────────────────────────────────────────────────────────────┘
- SmartSessions v1 - Base session key management
- The Compact - Cross-chain intent protocol
- compact-utils - Compact protocol utilities
BUSL-1.1