diff --git a/.swapproxy-deploy/README.md b/.swapproxy-deploy/README.md new file mode 100644 index 00000000..683b18f9 --- /dev/null +++ b/.swapproxy-deploy/README.md @@ -0,0 +1,84 @@ +# SwapProxy — deterministic CREATE2 deployment + +This directory is the **source of truth** for deploying `SwapProxy` to one deterministic +address on every chain via CREATE2, so any funded key (not a specific privileged EOA) can +deploy it. + +## Canonical values + +| Field | Value | +| ------------------------- | --------------------------------------------------------------------------------------------- | +| CREATE2 factory | `0x4e59b44847b379578588920cA78FbF26c0B4956C` (Arachnid, same address on all supported chains) | +| Salt | `0xd00319ff7795e528cc1ccd28bd6e08e46a42130d69ab4992d656773ba5fb323c` (vanity-mined) | +| Initcode hash | `0x342d83f4d7400dd603e2a6829db9cb032e7f62a2dda94746f2acd4e7e881bb2f` | +| **Deterministic address** | **`0x0000000085E102724e78eCd2F45DC9cA239Affad`** | + +The salt is vanity-mined for a 4-leading-zero-byte address, matching the Uniswap singleton +convention (Permit2 `0x000000000022…`, CaliburEntry `0x000000009b1d…`). Since the Arachnid +factory uses the salt raw (no `msg.sender` mixing), this address is identical on every chain +from any deployer key. + +The full record is in [`create2.json`](./create2.json); the raw initcode is in +[`canonical-initcode.hex`](./canonical-initcode.hex). + +## Why we pin the contract, not the Universal Router + +The CREATE2 address is `keccak256(0xff ++ factory ++ salt ++ keccak256(initcode))[12:]`. It +depends only on the factory, the salt, and the **initcode** — never on the deployer key or its +nonce. + +`SwapProxy`'s entire compile closure is five files: + +``` +SwapProxy.sol +├─ interfaces/ISwapProxy.sol → imports IUniversalRouter.sol +├─ interfaces/IUniversalRouter.sol → imports nothing (standalone interface) +├─ solmate/src/tokens/ERC20.sol +└─ solmate/src/utils/SafeTransferLib.sol +``` + +It calls the router only through the `IUniversalRouter` **interface**, which fixes the +`execute(bytes,bytes[],uint256)` selector at compile time and embeds **zero** router bytecode. +So the Universal Router implementation can keep shipping new versions and this address never +moves. We therefore deploy the **frozen initcode** in this directory rather than recompiling +from the live `src/pkgs/universal-router` submodule. + +The only things that can change the address: + +1. changing the `execute` selector in `IUniversalRouter.sol`, +2. editing the solmate files in the closure, or +3. changing compiler settings. + +## Initcode provenance + +`canonical-initcode.hex` is the frozen creation bytecode of the mainnet-verified, 1005-byte +`SwapProxy` runtime, built with: + +``` +solc 0.8.26+commit.8a97fa7a, evmVersion cancun, optimizer on, runs 4444, viaIR true, bytecodeHash ipfs +``` + +Note this differs from the repo's `universal-router` foundry profile (`optimizer_runs = 1`, +`bytecode_hash = "none"`), which produces a different 964-byte build. Deploying the frozen +initcode makes every chain converge on the same canonical bytecode. + +To reproduce or re-verify, use the standard-JSON input fetched from any chain where the +canonical SwapProxy is already verified (see `reference_swapproxy_canonical_deploy`). + +## Deploy + +```bash +forge script script/DeploySwapProxyCreate2.s.sol \ + --rpc-url --broadcast +``` + +The script is **idempotent** (no-op if SwapProxy already exists at the canonical address) and +self-checking: it reverts if the frozen initcode hash or the predicted address does not match +the pinned constants. + +## Relationship to the legacy address + +The pre-CREATE2 deployments live at `0x02e5be68d46dac0b524905bff209cf47ee6db2a9` (plain CREATE +at nonce 0 of `0xb259…aC87`). `SwapProxy` is immutable and ownerless, so those instances stay +on-chain; consumers migrate to the CREATE2 address above. There is no salt/initcode that +reproduces the legacy address via CREATE2 — the two opcodes use different address formulas. diff --git a/.swapproxy-deploy/canonical-initcode.hex b/.swapproxy-deploy/canonical-initcode.hex new file mode 100644 index 00000000..4606a7d7 --- /dev/null +++ b/.swapproxy-deploy/canonical-initcode.hex @@ -0,0 +1 @@ +0x608080604052346015576103ed908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c632894adf914610027575f80fd5b346103175760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103175760043573ffffffffffffffffffffffffffffffffffffffff81168091036103175760243573ffffffffffffffffffffffffffffffffffffffff8116809103610317576064359067ffffffffffffffff821161031757366023830112156103175781600401359367ffffffffffffffff8511610317573660248685010111610317576084359267ffffffffffffffff841161031757366023850112156103175783600401359267ffffffffffffffff8411610317578360051b923660248588010111610317576064815f6020947f23b872dd000000000000000000000000000000000000000000000000000000008295523360048401528a602484015260443560448401525af13d15601f3d1160015f51141617161561031b57843b15610317579492906101b96040519687957f3593564c000000000000000000000000000000000000000000000000000000008752606060048801526024606488019201610379565b927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8585030160248601528084526020808501928501019360248401935f917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbd82360301905b848410610297578b895f818d8183818f60a435604483015203925af1801561028c57610249575080f35b905067ffffffffffffffff811161025f57604052005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040513d5f823e3d90fd5b91939597509193957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828203018752873583811215610317578401906044602483013592019167ffffffffffffffff8111610317578036038313610317576103056020928392600195610379565b9901970194019189979695939161021f565b5f80fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f858286010152011601019056fea26469706673582212208ac0c35628927f6bcae2e9d562e95e23928185675564d5ae5f0d72393ee3c27e64736f6c634300081a0033 \ No newline at end of file diff --git a/.swapproxy-deploy/create2.json b/.swapproxy-deploy/create2.json new file mode 100644 index 00000000..fca0a6cc --- /dev/null +++ b/.swapproxy-deploy/create2.json @@ -0,0 +1,183 @@ +{ + "contract": "SwapProxy", + "description": "Deterministic CREATE2 deployment of the canonical SwapProxy. Address depends only on {create2Factory, salt, initcodeHash}; any funded key can deploy it.", + "create2Factory": "0x4e59b44847b379578588920cA78FbF26c0B4956C", + "salt": "0xd00319ff7795e528cc1ccd28bd6e08e46a42130d69ab4992d656773ba5fb323c", + "saltNote": "Vanity-mined for a 4-leading-zero-byte address (Uniswap singleton convention, cf. Permit2/CaliburEntry).", + "initcodeHash": "0x342d83f4d7400dd603e2a6829db9cb032e7f62a2dda94746f2acd4e7e881bb2f", + "initcodeFile": ".swapproxy-deploy/canonical-initcode.hex", + "initcodeBytes": 1031, + "runtimeBytes": 1005, + "predictedAddress": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "source": { + "repo": "https://github.com/Uniswap/universal-router", + "vendoredAt": "src/pkgs/universal-router/contracts/SwapProxy.sol", + "note": "The universal-router repo is the origin of the source, but it is NOT a live dependency. The frozen initcode below is the source of truth. The contract only references the IUniversalRouter interface, so UR router upgrades never change this address.", + "compileClosure": [ + "contracts/SwapProxy.sol", + "contracts/interfaces/ISwapProxy.sol", + "contracts/interfaces/IUniversalRouter.sol", + "lib/solmate/src/tokens/ERC20.sol", + "lib/solmate/src/utils/SafeTransferLib.sol" + ] + }, + "build": { + "solc": "0.8.26+commit.8a97fa7a", + "evmVersion": "cancun", + "optimizer": true, + "optimizerRuns": 4444, + "viaIR": true, + "bytecodeHash": "ipfs", + "note": "Matches the mainnet-verified 1005-byte SwapProxy runtime. This differs from the repo's universal-router foundry profile (optimizer_runs=1, bytecode_hash=none, 964-byte build), which is why we deploy the frozen initcode instead of recompiling." + }, + "legacy": { + "address": "0x02e5be68d46dac0b524905bff209cf47ee6db2a9", + "mechanism": "plain CREATE at nonce 0 of deployer 0xb259f71F8Fa2B0165c4d71061C4bf6c77444aC87", + "note": "Pre-CREATE2 deployments. Immutable and ownerless, so they remain on-chain; consumers migrate to the CREATE2 address above." + }, + "deploymentsNote": "All 22 chains deployed at predictedAddress with byte-identical 1005-byte runtime (keccak 0x24a203c24b85e0994ae6aecccc2bc0c1df4e22f1b3d63f33d18bec0245867aaf). Verified on each block explorer except X Layer (196), whose only explorer is OKLink (verification pending an OKLink API key).", + "deployments": { + "1": { + "chain": "ethereum", + "txHash": "0xc5a5d5d0d8d88c2bf201e910adf85481693d97a2a4e9b36eba8a18aa3a359f7e", + "explorer": "etherscan.io", + "verified": true + }, + "10": { + "chain": "optimism", + "txHash": "0x8700849e19ff6f7cfa640428de6127e0c6ce5e072ed56f0ba5243064c3fdfe2b", + "explorer": "optimistic.etherscan.io", + "verified": true + }, + "130": { + "chain": "unichain", + "txHash": "0x1964ed422c15332c63ea00933b9ebb2471664bf7a32eb973e4be6c6ad4cd1631", + "explorer": "uniscan.xyz", + "verified": true + }, + "1301": { + "chain": "unichain-sepolia", + "txHash": "0x38d91a2a6fb78b9d48c19cf2f07e7455b30815a46a8561496a0c2bd757b11260", + "explorer": "sepolia.uniscan.xyz", + "verified": true + }, + "1868": { + "chain": "soneium", + "txHash": "0x57cad6b16c08166ea230cffb5e71f92da5252ded6629a83fb5c010b9d1944398", + "explorer": "soneium.blockscout.com", + "verified": true + }, + "4326": { + "chain": "megaeth", + "txHash": "0x7602a721745f189bbcccb5e15dda8ee089a4409446d7ab906260bd574fca677d", + "explorer": "megaeth.blockscout.com", + "verified": true, + "note": "needed --gas-limit ~13M; chain estimates ~10.8M gas for the create" + }, + "4663": { + "chain": "robinhood", + "txHash": "0x8368c4fdc9bdf5669a005f7d4432f7b4ef973f12a8253648d08fbec21b131673", + "explorer": "8crv4vmq6tiu1yqr.blockscout.com", + "verified": true, + "note": "deployed via cast mktx -> sequencer (legacy, gas-price 0.2 gwei)" + }, + "5042": { + "chain": "arc", + "txHash": "0x3f11adba44661435d1e8d2b8664d18c8a156e349a73b3d964e08ac8a9d4aff63", + "explorer": "explorer.arc.io", + "verified": true, + "note": "needed --legacy --gas-estimate-multiplier 130" + }, + "7777777": { + "chain": "zora", + "txHash": "0xcc97b54913f501ba14bdfef6dc6b98f91e4062583787910e427339ec3982a8fe", + "explorer": "explorer.zora.energy", + "verified": true + }, + "8453": { + "chain": "base", + "txHash": "0xbb4502710f732b0ec1de99ff381b5e3a315ae22c5ef9190827064c6fbbd0c2f7", + "explorer": "basescan.org", + "verified": true + }, + "11155111": { + "chain": "sepolia", + "txHash": "0x4e523ef440e28ef0d0dd1f3f1dc825bd65bdf2903aab8fdb1e25fa57dd760477", + "explorer": "sepolia.etherscan.io", + "verified": true + }, + "42161": { + "chain": "arbitrum", + "txHash": "0x9a66e8f05fc5936775984321f66e78c39b0807d8cfbf3a483c02935fdc825390", + "explorer": "arbiscan.io", + "verified": true + }, + "42220": { + "chain": "celo", + "txHash": "0x8352219a027b54390877bcf1e9b6a8acec51972afc60de8bac957b17769ca1ad", + "explorer": "celoscan.io", + "verified": true + }, + "56": { + "chain": "bnb", + "txHash": "0x534b33f5da2a892662f639426d3f2ce3a6f4e93c08cf8ad5f1949c4e81fdb5be", + "explorer": "bscscan.org", + "verified": true + }, + "137": { + "chain": "polygon", + "txHash": "0x1416754b1c37296bdcff1ba4f7ba42114c6270211ff951938c8f3e2a422a4444", + "explorer": "polygonscan.com", + "verified": true + }, + "143": { + "chain": "monad", + "txHash": "0x67236b1ce4125229595d30424e08b9c63885894be4f9a5a9516c69cf1b61df63", + "explorer": "monadscan.com", + "verified": true + }, + "196": { + "chain": "xlayer", + "txHash": "0x1a3805b1bc91f52922e15eefc480b3eed18789dee1ce2724aae28ee9239f574f", + "explorer": "oklink.com/x-layer", + "verified": false, + "note": "explorer is OKLink; verification needs an OKX/OKLink API key (pending)" + }, + "480": { + "chain": "worldchain", + "txHash": "0x4f516e2d7a4b76ee9b390a62ba4bb80b8acf319bb954e240cd879889bb8a4324", + "explorer": "worldscan.org", + "verified": true + }, + "10143": { + "chain": "monad-testnet", + "txHash": "0xa4483028c6d4cdf0b2d4cc0d96a63290bc4724d21f5d72b1a7b349d89a43efb4", + "explorer": "testnet.monadscan.com", + "verified": true + }, + "43114": { + "chain": "avalanche", + "txHash": "0x0b003e8d3ebc8a0f747a4f6a2447c9ef61838eeb3dcadc5faed8b2544b9e0e0e", + "explorer": "snowscan.xyz", + "verified": true + }, + "59144": { + "chain": "linea", + "txHash": "0xf6f92613b47e423eb42013836be96a2e6146490944de28dbc5c38f248064617c", + "explorer": "lineascan.build", + "verified": true + }, + "81457": { + "chain": "blast", + "txHash": "0x074da378b5f893ffe87ff0a4cb1e9a0be523cb67a8978629df5b5b455ef63459", + "explorer": "blastscan.io", + "verified": true + }, + "57073": { + "chain": "ink", + "txHash": "0x7bcc6b9d9fc51cbb318b0e0398e1ac407dc811c137020fc06775ee2cc2f9d345", + "explorer": "explorer.inkonchain.com", + "verified": true + } + } +} diff --git a/deployments/json/1.json b/deployments/json/1.json index d77320d8..b9e3f859 100644 --- a/deployments/json/1.json +++ b/deployments/json/1.json @@ -234,6 +234,13 @@ "initcodeHash": "bb25c343f9777ab8fd717ab2e0ec094d596e6ad8d9b28e17ffe1d190507a1177", "timestamp": 1779987077802, "commitHash": "f684acb" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0xc5a5d5d0d8d88c2bf201e910adf85481693d97a2a4e9b36eba8a18aa3a359f7e", + "timestamp": 1780511567000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/10.json b/deployments/json/10.json index 36849cf9..5a64064e 100644 --- a/deployments/json/10.json +++ b/deployments/json/10.json @@ -140,6 +140,13 @@ "deploymentTxn": "0x029e91802d7ff5802ee09179c89f6f8ad98c1204405bd51e84fa1185b7239af0", "timestamp": 1756327634000, "commitHash": "320811c" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x8700849e19ff6f7cfa640428de6127e0c6ce5e072ed56f0ba5243064c3fdfe2b", + "timestamp": 1780512131000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/10143.json b/deployments/json/10143.json index 0e2a9adf..b8ac899c 100644 --- a/deployments/json/10143.json +++ b/deployments/json/10143.json @@ -98,6 +98,13 @@ "deploymentTxn": "0xf6e9a23b80dfe53fa52ba304fc83aeee76e8ca9cda20738be89a57440c4bd1c9", "timestamp": 1736642980000, "commitHash": "cba26cd" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0xa4483028c6d4cdf0b2d4cc0d96a63290bc4724d21f5d72b1a7b349d89a43efb4", + "timestamp": 1780520073000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/11155111.json b/deployments/json/11155111.json index de48e977..06918ddb 100644 --- a/deployments/json/11155111.json +++ b/deployments/json/11155111.json @@ -135,6 +135,13 @@ "initcodeHash": "e3a961ecd088faffe75dfa50cfa48ff2ed98085ec5e3be185c337f2a65d6af54", "timestamp": 1779989940749, "commitHash": "f684acb" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x4e523ef440e28ef0d0dd1f3f1dc825bd65bdf2903aab8fdb1e25fa57dd760477", + "timestamp": 1780512348000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/130.json b/deployments/json/130.json index 7a5b00b1..01c19caf 100644 --- a/deployments/json/130.json +++ b/deployments/json/130.json @@ -145,6 +145,13 @@ "proxy": false, "deploymentTxn": "GENESIS_1f98400000000000000000000000000000000004", "timestamp": 1730748360000 + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x1964ed422c15332c63ea00933b9ebb2471664bf7a32eb973e4be6c6ad4cd1631", + "timestamp": 1780512150000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/1301.json b/deployments/json/1301.json index 7d2c411b..95cc06c3 100644 --- a/deployments/json/1301.json +++ b/deployments/json/1301.json @@ -130,6 +130,13 @@ "deploymentTxn": "0x79223e459e374a931b5cf32f82860ac3980306e7e23a4d3004ad974a3aee1ab4", "timestamp": 1756328710000, "commitHash": "320811c" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x38d91a2a6fb78b9d48c19cf2f07e7455b30815a46a8561496a0c2bd757b11260", + "timestamp": 1780512166000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/137.json b/deployments/json/137.json index 877ca4eb..e249a00c 100644 --- a/deployments/json/137.json +++ b/deployments/json/137.json @@ -150,6 +150,13 @@ "proxy": false, "deploymentTxn": "0x45e77864249b0a388df4f98fc04203d6cd11ab6f1f4746cbf600d6055b376276", "timestamp": 1640021988000 + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x1416754b1c37296bdcff1ba4f7ba42114c6270211ff951938c8f3e2a422a4444", + "timestamp": 1780520011000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/143.json b/deployments/json/143.json index ae850477..47d2ede8 100644 --- a/deployments/json/143.json +++ b/deployments/json/143.json @@ -164,6 +164,13 @@ "deploymentTxn": "0xf7d33e2d187de2fb75db98198c1e840d5ac7394cd3a756524699710bf3575e06", "timestamp": 1763074970811, "commitHash": "efab318" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x67236b1ce4125229595d30424e08b9c63885894be4f9a5a9516c69cf1b61df63", + "timestamp": 1780520023000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/1868.json b/deployments/json/1868.json index a09860ef..f7b912ed 100644 --- a/deployments/json/1868.json +++ b/deployments/json/1868.json @@ -124,6 +124,13 @@ "deploymentTxn": "0xd6fc8edbc48342932e18b28b8b62970648e5614bff5ba4b13d4d1d72fe689f0c", "timestamp": 1738081525000, "commitHash": "f9a2783" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x57cad6b16c08166ea230cffb5e71f92da5252ded6629a83fb5c010b9d1944398", + "timestamp": 1780512185000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/196.json b/deployments/json/196.json index e58a7941..82edd804 100644 --- a/deployments/json/196.json +++ b/deployments/json/196.json @@ -171,6 +171,13 @@ "deploymentTxn": "0x955cd44ebeeffae2c133e28ab977bf6573f250bb3fce8c3b42c4f81372bbe776", "timestamp": 1764932489402, "commitHash": "cdb2417" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x1a3805b1bc91f52922e15eefc480b3eed18789dee1ce2724aae28ee9239f574f", + "timestamp": 1780520043000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/42161.json b/deployments/json/42161.json index b3b40b6b..6fddb240 100644 --- a/deployments/json/42161.json +++ b/deployments/json/42161.json @@ -156,6 +156,13 @@ "deploymentTxn": "0x7b50a77ad5545257fa9f21ac84185039fb1941d7915ec9b004d8de65fd7d22fd", "timestamp": 1755634518000, "commitHash": "0a52cd5" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x9a66e8f05fc5936775984321f66e78c39b0807d8cfbf3a483c02935fdc825390", + "timestamp": 1780512200000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/42220.json b/deployments/json/42220.json index 9dedd2b1..4198699c 100644 --- a/deployments/json/42220.json +++ b/deployments/json/42220.json @@ -134,6 +134,13 @@ "deploymentTxn": "0x00b870a8790b751759c2c968ef624ab7ed0f2599c7defe4e2bae35c21db53f67", "timestamp": 1762992985411, "commitHash": "a87a9d9" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x8352219a027b54390877bcf1e9b6a8acec51972afc60de8bac957b17769ca1ad", + "timestamp": 1780512224000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/43114.json b/deployments/json/43114.json index 7851be41..14d67551 100644 --- a/deployments/json/43114.json +++ b/deployments/json/43114.json @@ -61,6 +61,13 @@ "proxy": false, "deploymentTxn": "0xe800494c82f8d26226091df3c0b5317e1990bda57e90b7a5305e102f55267b6d", "timestamp": 1679632828000 + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x0b003e8d3ebc8a0f747a4f6a2447c9ef61838eeb3dcadc5faed8b2544b9e0e0e", + "timestamp": 1780520106000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/4326.json b/deployments/json/4326.json index 03321734..5345813b 100644 --- a/deployments/json/4326.json +++ b/deployments/json/4326.json @@ -162,6 +162,13 @@ "initcodeHash": "11d1503669e1cd407acefce7a995826cbbf60bea1019a80d611a875371026b79", "timestamp": 1769806741503, "commitHash": "1868754" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x7602a721745f189bbcccb5e15dda8ee089a4409446d7ab906260bd574fca677d", + "timestamp": 1780513063000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/4663.json b/deployments/json/4663.json index 12cf3749..005dc8b2 100644 --- a/deployments/json/4663.json +++ b/deployments/json/4663.json @@ -176,6 +176,13 @@ } } } + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x8368c4fdc9bdf5669a005f7d4432f7b4ef973f12a8253648d08fbec21b131673", + "timestamp": 1780513158000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/480.json b/deployments/json/480.json index 3a6ee7d5..21b286a9 100644 --- a/deployments/json/480.json +++ b/deployments/json/480.json @@ -75,6 +75,13 @@ "proxy": false, "deploymentTxn": "0x08308660d6b69fbde0e86fb740ae1feedd5a38c109dcc5824ec1bc98c0fff7ab", "timestamp": 1722542371000 + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x4f516e2d7a4b76ee9b390a62ba4bb80b8acf319bb954e240cd879889bb8a4324", + "timestamp": 1780520057000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/5042.json b/deployments/json/5042.json index ce02ab43..4192358d 100644 --- a/deployments/json/5042.json +++ b/deployments/json/5042.json @@ -162,6 +162,13 @@ "initcodeHash": "0dd2931a3fa4f9d62cfa0c23ec94d56019086a08952ae677c3e2e79d056e770f", "timestamp": 1779919102535, "commitHash": "e5b4eed" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x3f11adba44661435d1e8d2b8664d18c8a156e349a73b3d964e08ac8a9d4aff63", + "timestamp": 1780512707000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/56.json b/deployments/json/56.json index c6eb478d..6d34a190 100644 --- a/deployments/json/56.json +++ b/deployments/json/56.json @@ -143,6 +143,13 @@ "deploymentTxn": "0xb08db69ef8fb6d9a4fb0641d0e9bc3b3b343ac640268a9e71976f2b1675378a8", "timestamp": 1748029118000, "commitHash": "0a52cd5" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x534b33f5da2a892662f639426d3f2ce3a6f4e93c08cf8ad5f1949c4e81fdb5be", + "timestamp": 1780519994000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/59144.json b/deployments/json/59144.json index 45a6c34c..6f872071 100644 --- a/deployments/json/59144.json +++ b/deployments/json/59144.json @@ -200,6 +200,13 @@ "initcodeHash": "0dd2931a3fa4f9d62cfa0c23ec94d56019086a08952ae677c3e2e79d056e770f", "timestamp": 1771464102868, "commitHash": "4afdfb9" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0xf6f92613b47e423eb42013836be96a2e6146490944de28dbc5c38f248064617c", + "timestamp": 1780520123000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/7777777.json b/deployments/json/7777777.json index a5fb2789..a40f4000 100644 --- a/deployments/json/7777777.json +++ b/deployments/json/7777777.json @@ -58,6 +58,13 @@ "deploymentTxn": "0x37297d700c225f308fba2b7477cf78c993e27f9912e20469e4d34d60e87d1e4e", "timestamp": 1748029232000, "commitHash": "0a52cd5" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0xcc97b54913f501ba14bdfef6dc6b98f91e4062583787910e427339ec3982a8fe", + "timestamp": 1780512323000, + "commitHash": "484b405" } }, "history": [ diff --git a/deployments/json/81457.json b/deployments/json/81457.json index 3d737fab..a0b1b103 100644 --- a/deployments/json/81457.json +++ b/deployments/json/81457.json @@ -98,6 +98,13 @@ "deploymentTxn": "0x7075aad6676517bfbe235b27534e98c7817e1cae5916a9f1f1d04fc17c76c2fa", "timestamp": 1737564586000, "commitHash": "417770f" + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0x074da378b5f893ffe87ff0a4cb1e9a0be523cb67a8978629df5b5b455ef63459", + "timestamp": 1780520141000, + "commitHash": "2899307" } }, "history": [ diff --git a/deployments/json/8453.json b/deployments/json/8453.json index 048a8e7d..7138052c 100644 --- a/deployments/json/8453.json +++ b/deployments/json/8453.json @@ -195,6 +195,13 @@ "proxy": false, "deploymentTxn": "0xbd1d9b1a36c659e0bb69e51cc6f7941c34fa9cce4fbfde0f7881e4d3b1cbf105", "timestamp": 1722874175000 + }, + "SwapProxy": { + "address": "0x0000000085E102724e78eCd2F45DC9cA239Affad", + "proxy": false, + "deploymentTxn": "0xbb4502710f732b0ec1de99ff381b5e3a315ae22c5ef9190827064c6fbbd0c2f7", + "timestamp": 1780511199000, + "commitHash": "484b405" } }, "history": [ diff --git a/foundry.toml b/foundry.toml index c5e3bd4a..d60de9ad 100644 --- a/foundry.toml +++ b/foundry.toml @@ -12,7 +12,8 @@ fs_permissions = [ { access = "read", path = "script/" }, { access = "read-write", path = "script/deploy/tasks" }, { access = "read", path = "out/" }, - { access = "read", path = "deployments/" } + { access = "read", path = "deployments/" }, + { access = "read", path = ".swapproxy-deploy/" } ] skip = [ diff --git a/script/DeploySwapProxyCreate2.s.sol b/script/DeploySwapProxyCreate2.s.sol new file mode 100644 index 00000000..5e90f92b --- /dev/null +++ b/script/DeploySwapProxyCreate2.s.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {Script, console2} from 'forge-std/Script.sol'; + +/// @title DeploySwapProxyCreate2 +/// @notice Deploys SwapProxy to a single deterministic address on every chain via the canonical +/// CREATE2 factory, using a FROZEN initcode artifact rather than a fresh local compile. +/// @dev Source-of-truth model (see .swapproxy-deploy/README.md): +/// - The address is determined ONLY by {factory, salt, initcode}, never by the deployer key. +/// - The initcode is the frozen canonical SwapProxy build (matches the mainnet-verified 1005-byte +/// runtime). We deploy these exact bytes so every chain converges on identical bytecode. +/// - We intentionally do NOT recompile SwapProxy from the universal-router submodule here: the +/// repo's `universal-router` profile (optimizer_runs=1, bytecode_hash=none) yields a different +/// 964-byte build, and the UR router implementation is irrelevant to SwapProxy's bytecode +/// anyway (SwapProxy only references the IUniversalRouter interface). +contract DeploySwapProxyCreate2 is Script { + // NOTE: CREATE2_FACTORY (0x4e59...4956C, the Arachnid deterministic deployment proxy) is + // inherited from forge-std's CommonBase and is the same address on every supported chain. + + /// @notice Salt for the canonical SwapProxy deployment. Vanity-mined for a leading-zero address + /// (Uniswap singleton convention, cf. Permit2 / CaliburEntry). + bytes32 internal constant SALT = 0xd00319ff7795e528cc1ccd28bd6e08e46a42130d69ab4992d656773ba5fb323c; + + /// @notice keccak256 of the frozen initcode. Guards against artifact corruption. + bytes32 internal constant INITCODE_HASH = 0x342d83f4d7400dd603e2a6829db9cb032e7f62a2dda94746f2acd4e7e881bb2f; + + /// @notice Deterministic SwapProxy address for {CREATE2_FACTORY, SALT, INITCODE_HASH}. + address internal constant EXPECTED_ADDRESS = 0x0000000085E102724e78eCd2F45DC9cA239Affad; + + /// @notice Path to the frozen canonical initcode (hex, 0x-prefixed, no trailing whitespace). + string internal constant INITCODE_PATH = '.swapproxy-deploy/canonical-initcode.hex'; + + error InitcodeHashMismatch(bytes32 expected, bytes32 actual); + error AddressMismatch(address expected, address actual); + error FactoryMissing(address factory); + error DeployFailed(); + + function run() external { + bytes memory initcode = vm.parseBytes(vm.readFile(INITCODE_PATH)); + + // The frozen artifact must be byte-identical to what the constants describe. + bytes32 initcodeHash = keccak256(initcode); + if (initcodeHash != INITCODE_HASH) revert InitcodeHashMismatch(INITCODE_HASH, initcodeHash); + + address predicted = _create2Address(SALT, initcodeHash); + if (predicted != EXPECTED_ADDRESS) revert AddressMismatch(EXPECTED_ADDRESS, predicted); + + // Idempotent: if SwapProxy already lives at the canonical address on this chain, do nothing. + if (EXPECTED_ADDRESS.code.length != 0) { + console2.log('SwapProxy already deployed at', EXPECTED_ADDRESS); + return; + } + + if (CREATE2_FACTORY.code.length == 0) revert FactoryMissing(CREATE2_FACTORY); + + vm.startBroadcast(); + // Arachnid factory calldata is `salt (32 bytes) ++ initcode`. + (bool ok,) = CREATE2_FACTORY.call(abi.encodePacked(SALT, initcode)); + vm.stopBroadcast(); + + if (!ok || EXPECTED_ADDRESS.code.length == 0) revert DeployFailed(); + console2.log('SwapProxy deployed at', EXPECTED_ADDRESS); + } + + /// @notice Computes the CREATE2 address: keccak256(0xff ++ factory ++ salt ++ initcodeHash)[12:]. + function _create2Address(bytes32 salt, bytes32 initcodeHash) internal pure returns (address) { + return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), CREATE2_FACTORY, salt, initcodeHash))))); + } +}