RFPG Proposal: The Rip Van Winkle (An Aztec migration SDK)
Summary
An app-centric migration SDK for Aztec rollup upgrades. Users self migrate state (public + private): Rollup 1 nullifies state and sends a commitment through L1→L2 messaging; user claims on Rollup 2 with that commitment, which contains their state data.
Apps implement prepare_for_migration on R1 (how to make state unspendable) and adapt_state on R2 (handle storage changes). The SDK handles L1 messaging, portal token transfers, and frontend tooling.
Trustless, generalizable across any app type, and supports the “Rip Van Winkle” case where a user shows up after some rollup upgrades allowing them to migrate via forced execution even after R1 has no sequencers.
General Design Principles
- Escape Hatch Safety: R1 state must be nullified to prevent double-spend via forced withdrawals
- Rip Van Winkle Support: Users can migrate from R1 to R5 using escape hatch for forced tx execution even if R1 has no sequencers
- App Developer Burden: Apps define their own migration policy without compromising user privacy. Apps provide StateAdapter for storage structure changes between rollup versions.
- User specifies R2 claim address at migration time; no wallet/key correlation required. Though a neater solution uses the master keys.
- Public + Private State: Single transaction handles both state types.
Start and End Date
- Start: January 27, 2026
- Prototype: February 27, 2026
- Final delivery: March 6, 2026
Team
Mohammed Mahmoud
github: mohammed7s (Mohammed) · GitHub
Technical Architecture
(Onchain Category A)
Components
- Generic MigrationSource contract on R1 : Single contract where apps register; users batch migrate all apps in one tx
- L1 Migration Registry : Relays commitments, handles portal token backing transfers to new portals
- MigrationTarget contract on R2 : Verifies commitments, coordinates claiming
- User-facing SDK : Frontend tooling for wallet integration and transaction building
1. R1 MigrationSource contract
Apps register and specify how to make state unspendable (e.g., tokens = burn, DeFi position = withdraw from pool) to avoid double spending through the escap hatch:
prepare_for_migration(owner) → MigrationData
- Nullify owner’s private state
- Zero owner’s public state
- Return state_data summarizing migrated state
App Registration (App developer):
- App registers the prepare_for_migration(owner) function for their app
- App calls MigrationSource.register(app_address)
- MigrationSource stores app in registry
User Batch Migration (or for a single app):
- User calls migrate_all(recipient_r2, [token_app, nft_app, defi_app, …])
- For each app, migrate(recipient_r2, app)
- Calls app.prepare_for_migration(msg_sender)
- Creates commitment for each: hash(recipient_r2, app_address, state_data)
- Sends single batched L1 message to Migration Registry with app specific commitment
2. L1 Migration Registry
- Query Rollup Registry for R1 outbox and R2 inbox addresses
- Consume and verify batched commitments from R1 outbox
- Move portal token backing: withdraw from R1 portal, deposit to R2 portal (if applicable)
- Forward batched commitments to R2 inbox targeting MigrationTarget contract
3. R2 MigrationTarget Contract
Responsibilities:
- Reconstruct and verify commitment from recipient and state_data
- Consume L1→L2 message proving legitimate relay
- Prevent double-claim via public storage tracking
- Call app’s adapt_state to mint new state
adapt_state(recipient, state_data)
- Interpret state_data from R1
- Handle storage slot mapping if R2 structure differs
App Dev Requirements
- Define how to nullify private/public state
- Define commitment generation on R1
- Define storage mappings for R2 (optional, only if changed)
Flow
- User connects wallet to R1 migration app or specific app that incorprated the migration sdk
- App shows migratable state across all registered apps (private + public balances)
- User enters their R2 address
- User clicks “Migrate All” → single R1 tx nullifies all state, sends batched commitment
- Relayer (or user) calls L1 Registry to relay commitments to R2
- User connects to R2 app with R2 wallet
- App queries for pending claims matching user’s R2 address
- User clicks “Claim All” → R2 tx mints state across all apps
Public state analysis
| Category | Strategy |
|---|---|
| Config/Admin (admin, minters, name, symbol) | Deploy fresh on R2 |
| Global (total_supply, interest_accumulator) | Recalculate from migrations |
| User-Specific (public_balances, collateral) | User migrates with private state |
A weird paradox is that naively copying public state variables that reprsesent globals is not fully accurate if only a portion of users have migrated. And since some of it will be in pending state. On other hand, it should still be the same app and same balances etc.
Our approach: For global protocol recalculate with every migration this way its always accurate for R2. And for user specific state, we can attach it to the user’s migration since they are self migrating anyways.
The R2 Address Authorization Problem
Aztec addresses include contract-specific data (contract_class_id, salt, initialization_hash), so R1 and R2 addresses differ even for the same user. How does R2 verify the claimant owns the migrated state?
| Approach | Description |
|---|---|
| Secret/Receipt | Tornado-style secret generated on R1, used to claim on R2 |
| Target Address Binding | User specifies R2 address during R1 migration |
| Public Keys Proof | Prove same public_keys_hash (same seed → same master keys) |
Our Approach: Target Address Binding for simplicity. User specifies R2 address when migrating. The neater solution is to use the keys approach, this SDK can be extended to support any work done on that, since the scope here is just to deal with the app specific state regardless how the user is validates on R2.
Deliverables:
| Deliverable | Type | Description |
|---|---|---|
| MigrationSource.nr | Contract | R1 batch migration |
| MigrationTarget.nr | Contract | R2 claim verification |
| MigrationRelay.sol | Contract | L1 relay + portal handling |
| @aztec/migration-sdk | TypeScript | Frontend integration |
| Token migration | Example | Reference implementation |
| NFT migration | Example | Reference implementation |
| Docs | Markdown | Integration + user guides |
Milestones:
- R1 MigrationSource contract with app registration and batch migration (Week 1-2)
- R2 MigrationTarget contract with claim verification and double-claim prevention (Week 2-3)
- L1 MigrationRelay with portal token support (Week 3-4)
- Example Token and NFT migrations with end-to-end devnet testing (Week 4-5)
- TypeScript SDK and documentation (Week 5-6)
Grant Amount Requested
$20,000