diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5a77ae8..e2963b2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,5 +41,5 @@ jobs: - name: Run Forge tests run: | - forge test -vvv + forge test -vvv --no-match-path 'test/integration/**' id: test diff --git a/README.md b/README.md index 9ff447a..b360114 100644 --- a/README.md +++ b/README.md @@ -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. + * [Immutable X Asset Migration Contracts](#immutable-x-asset-migration-contracts) * [Overview](#overview) @@ -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:** @@ -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) @@ -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) diff --git a/docs/finalise-pending-withdrawals.md b/docs/finalise-pending-withdrawals.md new file mode 100644 index 0000000..f2f62d8 --- /dev/null +++ b/docs/finalise-pending-withdrawals.md @@ -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. \ No newline at end of file