Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ jobs:

- name: Run Forge tests
run: |
forge test -vvv
forge test -vvv --no-match-path 'test/integration/**'
id: test
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
[![Solidity](https://img.shields.io/badge/Solidity-0.8.27+-brightgreen.svg)](https://soliditylang.org/)
[![Foundry](https://img.shields.io/badge/Foundry-1.0+-orange.svg)](https://getfoundry.sh/)

> **Migration Complete.** The Immutable X chain was sunset on **11 February 2026**. Automated migration of remaining funds to Immutable zkEVM was carried out between **5–9 March 2026** and has now concluded. This repository contains the contracts that facilitated the migration.
>
> | Milestone | Date |
> |-----------|------|
> | Last day for manual withdrawals from Immutable X | 11 February 2026 |
> | Automated fund migration to Immutable zkEVM | 5–9 March 2026 |

> **Have a pending withdrawal from Immutable X?** If you initiated a withdrawal on Immutable X on or before 11 February 2026 and have not yet finalised it on Ethereum, your funds are still held by the bridge contract. This is separate from the automated migration. See the **[Finalise Manually Initiated Withdrawals Guide](./docs/finalise-pending-withdrawals.md)** for step-by-step instructions on how to claim your funds.

<!-- TOC -->
* [Immutable X Asset Migration Contracts](#immutable-x-asset-migration-contracts)
* [Overview](#overview)
Expand Down Expand Up @@ -35,8 +44,6 @@ To address this, an automated post-sunset migration process will transfer remain

The migration contracts in this repository implement this process while minimising additional trust assumptions. The mechanism anchors on the final StarkEx vault root of the Immutable X chain at shutdown, and strictly constrains all fund disbursements on zkEVM to require valid proofs against this root, ensuring migrated assets are disbursed only to their rightful owners.

---

## Capabilities
### Legacy Bridge Lifecycle
1. **Pending Withdrawal Finalisation:**
Expand Down Expand Up @@ -124,7 +131,7 @@ A high-level description of each component is provided in the [Core Contracts](#

### Mainnet
- **Ethereum**
- [StarkExchangeMigration](https://etherscan.io/address/0x58b5484F489f7858DC83a5a677338074b57de806)
- [StarkExchangeMigration (Proxy)](https://etherscan.io/address/0x5FDCCA53617f4d2b9134B29090C87D01058e27e9) | [Implementation](https://etherscan.io/address/0x58b5484F489f7858DC83a5a677338074b57de806)
- [VaultRootSenderAdapter](https://etherscan.io/address/0x9Fabd9Cc71f15b9Cfd717E117FBb9cfD9fC7cd32)
- **Immutable zkEVM**
- [VaultWithdrawalProcessor](https://explorer.immutable.com/address/0xCeA34C706C4A18E103575832Dd21fD3656026D1E)
Expand All @@ -133,7 +140,7 @@ A high-level description of each component is provided in the [Core Contracts](#

### Testnet
- **Sepolia**
- [StarkExchangeMigration](https://sepolia.etherscan.io/address/0x9F9b4A495A62191A4225759Ae7C00906Cc6417B8)
- [StarkExchangeMigration (Proxy)](https://sepolia.etherscan.io/address/0x2d5c349fd8464da06a3f90b4b0e9195f3d1b7f98) | [Implementation](https://sepolia.etherscan.io/address/0x9F9b4A495A62191A4225759Ae7C00906Cc6417B8)
- [VaultRootSenderAdapter](https://sepolia.etherscan.io/address/0xCeA34C706C4A18E103575832Dd21fD3656026D1E)
- **Immutable zkEVM Testnet**
- [VaultWithdrawalProcessor](https://explorer.testnet.immutable.com/address/0xCeA34C706C4A18E103575832Dd21fD3656026D1E)
Expand Down
151 changes: 151 additions & 0 deletions docs/finalise-pending-withdrawals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Finalise Manually Initiated Withdrawals from Immutable X

> **This guide is unrelated to the automated migration of assets to Immutable zkEVM** (which completed on 5–9 March 2026). It applies only to users who initiated a standard withdrawal from Immutable X on or before **11 February 2026** and have not yet finalised that withdrawal on Ethereum L1.

During normal Immutable X operation, withdrawals were a two-step process: users first initiated a withdrawal on Immutable X, then finalised it on Ethereum to receive their funds. If you completed the first step but never finalised on L1, your funds are still held by the Immutable X bridge contract on Ethereum and can be claimed by calling the `withdraw()` function directly.

---

## Prerequisites

- An Ethereum wallet (e.g. MetaMask) connected to the correct network (Ethereum Mainnet or Sepolia Testnet).
- Your **Stark Key** (see [How to find your Stark Key](#how-to-find-your-stark-key)).
- The **Asset ID** for the token you are withdrawing (see [Asset ID Reference Tables](#asset-id-reference-tables)).

---

## Contract Addresses

| Network | Contract | Explorer Link |
|---------|----------|---------------|
| Ethereum Mainnet | Immutable X Bridge (Proxy) | [0x5FDCCA53617f4d2b9134B29090C87D01058e27e9](https://etherscan.io/address/0x5FDCCA53617f4d2b9134B29090C87D01058e27e9#writeProxyContract) |
| Sepolia Testnet | Immutable X Bridge (Proxy) | [0x2d5c349fd8464da06a3f90b4b0e9195f3d1b7f98](https://sepolia.etherscan.io/address/0x2d5c349fd8464da06a3f90b4b0e9195f3d1b7f98#writeProxyContract) |

---

## Step-by-Step Instructions

### 1. Check your pending withdrawal balance (optional)

Before withdrawing, you can verify that you have a pending withdrawal by calling the read function `getWithdrawalBalance`:

1. Go to the explorer link for the relevant network from the table above.
2. Navigate to **Contract** > **Read as Proxy**.
3. Find the `getWithdrawalBalance` function.
4. Enter:
- `ownerKey`: Your Stark Key (in decimal).
- `assetId`: The Asset ID for the token (the `token_int` value from the [reference tables](#asset-id-reference-tables) below).
5. Click **Query**. A non-zero result confirms you have a pending withdrawal for that asset.

### 2. Finalise the withdrawal

1. Go to the explorer link for the relevant network from the table above.
2. Navigate to **Contract** > **Write as Proxy**.
3. Connect your wallet.
4. Find the `withdraw` function.
5. Enter:
- `ownerKey` (uint256): Your Stark Key (in decimal).
- `assetType` (uint256): The Asset ID for the token you are withdrawing (the `token_int` value from the [reference tables](#asset-id-reference-tables) below).
6. Click **Write** and confirm the transaction in your wallet.

Once the transaction is confirmed, the withdrawn funds will be sent to the Ethereum address associated with your Stark Key.

> **Note:** The `withdraw` function can be called by anyone, not just the owner. The funds are always sent to the Ethereum address registered to the given Stark Key.

---

## How to Find your Stark Key

For **StarkEx v4 standard keys** (which includes the majority of Immutable X users), your Stark Key is simply the **decimal representation of your Ethereum address**.

To convert your Ethereum address to a decimal Stark Key:

1. Take your Ethereum address (e.g. `0x1234...abcd`).
2. Convert the hexadecimal value to decimal. You can use a tool like [RapidTables Hex to Decimal Converter](https://www.rapidtables.com/convert/number/hex-to-decimal.html)

> **Tip:** If you are unsure whether your key follows the standard mapping, you can verify by calling `getEthKey` on the contract with your suspected Stark Key — it should return your Ethereum address.

---

## Asset ID Reference Tables

The **Asset ID** (referred to as `token_int` in the configuration) is the value you need to provide as the `assetType` parameter when calling `withdraw`. Find the token you are withdrawing in the appropriate table below.

### Mainnet (Ethereum)

| Token | Token Address | Asset ID (`token_int`) |
|-------|---------------|------------------------|
| APE | `0x4d224452801aced8b2f0aebe155379bb5d594381` | `1015922532137787982946651776980869339398815931061497279724058213836132190095` |
| BR | `0xe21363bf33620a291a6c354dc3bb99e40cb3086b` | `313505229384936965539156700002111859723492368677210314696313758091297420165` |
| CMT | `0xe910c2a090516fb7a7be07f96a464785f2d5dc18` | `1670413597475604293967066736840650619123375215270437228479873503062842925101` |
| CTA | `0x90685e300a4c4532efcefe91202dfe1dfd572f47` | `990917105548321882863790743692016363780882772911795288852111915173114800720` |
| DEV | `0xa3d59c6d24f428dcfedc09724869e7af4d281fdd` | `1707776506599369959662713172170118390106404453517106730194996143842397299232` |
| EMBER | `0x35f3bad2fcc8053869086885f7898a3d4309db4e` | `127313621446881611467827816653547345717404185327457088545463981166261510791` |
| ETH | (native) | `1103114524755001640548555873671808205895038091681120606634696969331999845790` |
| FGL | `0x19997289163ce4659d447617a0877da36770ab4d` | `427042314744200788989839114132810697143887157126058739219120820878172477593` |
| GODS | `0xccc8cb5229b0ac8069c51fd58367fd1e622afd97` | `636117700839179604858620718832286872417390350654465827892936533551518041557` |
| GOG | `0x9ab7bb7fdc60f4357ecfef43986818a2a3569c62` | `1125223003001074656178455529439603029082973884966680368655489058248875828864` |
| ILV | `0x767fe9edc9e0df98e07454847909b5e959d7ca0e` | `634028667897844324040908450156043846954664548107344752361156161409532472444` |
| IMX | `0xf57e7e7c23978c3caec3c3548e3d615c346e79ff` | `88914301944088089141574999348394996493546404963067902156417732601144566237` |
| LOST | `0x71854072ce51cc8859c8c178e33581034fa75753` | `103129661469142187932697195709967826790010599230686898125218855467795622233` |
| OMI | `0xed35af169af46a02ee13b9d79eb57d6d68c1749e` | `881983512215682697930791077345670677400334790145955708894204513777355240488` |
| OMI (alt) | `0xed35af169af46a02ee13b9d79eb57d6d68c1749e` | `525164948494449116421805041713913612934056483342602240145121349090983728353` |
| SILV2 | `0x7e77dcb127f99ece88230a64db8d595f31f1b068` | `227716095600406046651136589472266806698090667547081713813841569445055346153` |
| USDC | `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` | `1147032829293317481173155891309375254605214077236177772270270553197624560221` |
| VCO | `0x2caa4021e580b07d92adf8a40ec53b33a215d620` | `1485183671027309009439509871835489442660821279230223034298428454062208985878` |
| VCORE | `0x733b5056a0697e7a4357305fe452999a0c409feb` | `1796496801014838733778624354279754962911198551343321171219226048470100654417` |
| WAGMI | `0x3b604747ad1720c01ded0455728b62c0d2f100f0` | `915177440183500088607294558883023442174593163187193385150232248105465289242` |

> **Note:** OMI has two entries with different asset IDs corresponding to different quantum values. If you are unsure which one applies to your withdrawal, try checking `getWithdrawalBalance` with each.

### Testnet (Sepolia)

| Token | Token Address | Asset ID (`token_int`) |
|-------|---------------|------------------------|
| ARG | `0x00e49f72e3ff96f50e98d8533f2eb5a9b69b002f` | `1530984830380564117086548947741263773510366505132298518074070101164820062630` |
| BR | `0xc7bc6619082b8cced2cc936203eff41891edb76b` | `1073653178426466786182623804251028445438158097414716880341181592411393923116` |
| CRYPI | `0x655dfe9d7ec1a74d45841f79ec6b4f6160a4c031` | `469541149215236994106312323274306218924367273999532936226373165480865072166` |
| CRYPI (alt) | `0xf82dbf105e144f7219b089fb2790f3faf19b676e` | `1800070639292759431876200506148529988822384962475997258171744591397001372553` |
| CRYPT | `0x1d30db727c18f6d8c3318deda7d3cd8a689ce935` | `278583067383992383480264268372737502031925467588718654218074253480841633548` |
| CRYPT (alt) | `0x40d3e56ddfcd6b98b41439f9d98eb7a42804249a` | `855696249333702326166027625093829385191505517726423367160505816117313720239` |
| CRYPU | `0xed398bfdde93056c4dd61e2503816587aa80c346` | `45817669857783943078259039040823645909482319712107577790906451611064669921` |
| CRYPU (alt) | `0x8122c8f02f1e586f166dcb6a6de3b5c0fd3e48ca` | `1796911963631684152130514622551639338427318256305213492458648234201659064961` |
| CTA | `0x6da2a4d37259439c7ddcddb55cd0ef2033fe1e8f` | `1205801016002269626459263468621484672253654502893679938994915767141360433198` |
| EST | `0xbe7be8d5f99b16b2f29007d9eb08ed49f73d05d6` | `1697937671430045510303887649398941342205203259071938147663058327235194893000` |
| ETH | (native) | `1103114524755001640548555873671808205895038091681120606634696969331999845790` |
| EVM | `0xf9c16d203b6d67c5943de257fff1ab2e5dc52f73` | `958749024699050137966222344651071737385564981318518993053819450292484749550` |
| GODS | `0x5c9f1680bb6a4b4fc698e0cf702e0cc34aed91b7` | `140045364627528196345084413227318657255889301714016666850545170669121136724` |
| GOG | `0xfe9df9ebe5fbd94b00247613b6cf7629891954e2` | `288497620338891181002256043717214864459537182795830852149172946884830468774` |
| HYPEI | `0x040b46e6f5fb6d21e5e117b2cc6a8490bd13bdd8` | `1782613318986674861730994617630569436938103649781060793390626353255201116359` |
| HYPEI (alt) | `0xb239c8e5c119f231151791fface0bf634670f27a` | `1100051021863410963740714092430936399472019405492900761810892193340061133175` |
| HYPER | `0x17eae1f5d6c6dba5a8fc68789ad8bb6531b1c5ce` | `1730268361872681930365892179602415294747249511559951801222868962857887087074` |
| HYPER (alt) | `0xbd8172ab5f2d4b068f8865ed527c3427cb998678` | `133566068527828825746133918188608556461795710960217173451759027761417430363` |
| HYPEU | `0x64f78605852706da360457c64267a0b7da77f0e9` | `279228093087404724401986225486636769487846954176324340679736210809311495` |
| HYPEU (alt) | `0xb79a155c97a21fb0630b6094d8cd90c3d32790d6` | `278218763421750168916729775629218233399649390895347468260686151792906842745` |
| ILV | `0x886dd14dcc6caed09a3e4d01d489673a84dd8f16` | `1118767138481389624672377790657114412417202583785801293058102967449208440552` |
| ILVI | `0xf444a3355e4624f7b2c532557420e9c01ced499f` | `246687233301300469710108727028426484184902991299048503161524411000024849494` |
| ILVU | `0xc65f3e4d8edda3ee80cf7f98b0b0c9f208c33c9e` | `620001226371989005351249404877383874184100034181679496390579996208319954842` |
| IMX | `0x2fa06c6672ddcc066ab04631192738799231de4a` | `774695052920335475148844746909925970695317951232942764061726388365724651811` |
| MTK | `0xf674d458575c1b659da24a60add8a4f68cd7fb19` | `1079365209654237028653623629764958016172582949813253986541136037171578617505` |
| MZD | `0x174ce99c5a5407b2e7101e5a743f28936f2f0c69` | `889491635621296293428570805285996100526088113454814524032053738967908687010` |
| OMI | `0x02f80da373021e0b6a0a00470f82b55d4830a724` | `1541893324194556794018922586922863773169005377082433218256783405946955321773` |
| OMI (alt 1) | `0x3b1b47bdaeb409a1ed64a13d856bd0ca1f718be5` | `508896894519199837882532718821198929536057284631686405901881975310756808819` |
| OMI (alt 2) | `0x2711f4f362dc6c3da2fce3e021170b033b7df7fc` | `1713273459042363589671903409735439653127748941717893520188565250123836559922` |
| SDK | `0x312674a822a337bc50d4fdf465781079760a6729` | `1007429852875183837711381425904397530529430287830771091622498485479621080033` |
| SILV2 | `0x82475443f1075bc571a552cbdab1f49b24e1c47a` | `1026459113228997553038702658409951763724108387124859616632951167746600061279` |
| SILVI | `0x387798f984091808e266dcb7f3fd306e18d71904` | `224326615647497430209000540151115974094010699057468868354504375901667258695` |
| SILVU | `0x6bdefb9b62444c99999682a2076841b47a4cab9c` | `14406987772269190202869751597715327602721087011555776736463891281797319454` |
| SOLOI | `0xbb864c80cb6e263c34c325f51544e8d0b9e9455c` | `1074694638794775585034847784496210938495869105762602294578446735922582865903` |
| SOLOI (alt) | `0xb13477b46fbcb878b496ca511018801e45cfc846` | `1789880607087655497383974216548231163506506920581499066660700239294810361138` |
| SOLON | `0xedc2e5600ac2ae5c79cb68114e4ca6cb3360ee51` | `1142780287754666633490319950078041001437265973993631920364116058229405481176` |
| SOLON (alt) | `0x3701c15dd3b155d96d02d4eeec6cadc387af767d` | `1304335111848845550036725670378237818351850476608607454929125615580280114750` |
| SOLOU | `0x8db3294ac60ec3462d846a090e1f4348e29d8d8d` | `584818264821865309529860137527490043841291606793888110511892289381889046142` |
| SOLOU (alt) | `0x73d859408b80c395f97232dff990b86f3c51aff6` | `21498777723544896979545581006810717137381075370112586295636297534407605532` |
| TRDS | `0x5b56a1a6e46e799c5ca87b37f9896902f541afc8` | `1694009098181558118158615594769574705969634676578372970032421836083086133628` |
| UNI | `0x1f9840a85d5af5bf1d1762f925bdaddc4201f984` | `540062279147817096126692697840181823459298758504720034739181724881731629294` |
| USDC | `0xca6c31cc71fb8f00dc2c465299f605039e0e1f4b` | `661141420868150794624283883274270269479864861417174829821648462727047715204` |
| VCOIN | `0xec0ed56027f7896fe11309de6dacf95b786924d3` | `1529047011282119779666243264336891129455481470106617051357049340145514211869` |
| VCOIN (alt) | `0xc7d4cd5bc9da8a5f77718f6851f3df6f79a13293` | `937627426884687223015650826300942653066238230804307701105682811511494029416` |
| VCORE | `0x315061da0f44527eb0c534b62df4e5cefc0d5cdb` | `701992353264593934042630901537572119762294448768089561198251820694421559230` |

> **Note:** Some tokens (e.g. OMI, CRYPI, CRYPT, CRYPU, HYPEI, HYPER, HYPEU, SOLOI, SOLON, SOLOU, VCOIN) appear with multiple entries due to different quantum values or token contract addresses. If unsure which applies, check `getWithdrawalBalance` with each Asset ID to find the one with a non-zero balance.
Loading