Skip to content
Open
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
84 changes: 84 additions & 0 deletions .swapproxy-deploy/README.md
Original file line number Diff line number Diff line change
@@ -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 <RPC> --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.
1 change: 1 addition & 0 deletions .swapproxy-deploy/canonical-initcode.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0x608080604052346015576103ed908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f905f3560e01c632894adf914610027575f80fd5b346103175760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103175760043573ffffffffffffffffffffffffffffffffffffffff81168091036103175760243573ffffffffffffffffffffffffffffffffffffffff8116809103610317576064359067ffffffffffffffff821161031757366023830112156103175781600401359367ffffffffffffffff8511610317573660248685010111610317576084359267ffffffffffffffff841161031757366023850112156103175783600401359267ffffffffffffffff8411610317578360051b923660248588010111610317576064815f6020947f23b872dd000000000000000000000000000000000000000000000000000000008295523360048401528a602484015260443560448401525af13d15601f3d1160015f51141617161561031b57843b15610317579492906101b96040519687957f3593564c000000000000000000000000000000000000000000000000000000008752606060048801526024606488019201610379565b927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8585030160248601528084526020808501928501019360248401935f917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbd82360301905b848410610297578b895f818d8183818f60a435604483015203925af1801561028c57610249575080f35b905067ffffffffffffffff811161025f57604052005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040513d5f823e3d90fd5b91939597509193957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828203018752873583811215610317578401906044602483013592019167ffffffffffffffff8111610317578036038313610317576103056020928392600195610379565b9901970194019189979695939161021f565b5f80fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f858286010152011601019056fea26469706673582212208ac0c35628927f6bcae2e9d562e95e23928185675564d5ae5f0d72393ee3c27e64736f6c634300081a0033
183 changes: 183 additions & 0 deletions .swapproxy-deploy/create2.json
Original file line number Diff line number Diff line change
@@ -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
}
}
}
7 changes: 7 additions & 0 deletions deployments/json/1.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@
"initcodeHash": "bb25c343f9777ab8fd717ab2e0ec094d596e6ad8d9b28e17ffe1d190507a1177",
"timestamp": 1779987077802,
"commitHash": "f684acb"
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0xc5a5d5d0d8d88c2bf201e910adf85481693d97a2a4e9b36eba8a18aa3a359f7e",
"timestamp": 1780511567000,
"commitHash": "484b405"
}
},
"history": [
Expand Down
7 changes: 7 additions & 0 deletions deployments/json/10.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@
"deploymentTxn": "0x029e91802d7ff5802ee09179c89f6f8ad98c1204405bd51e84fa1185b7239af0",
"timestamp": 1756327634000,
"commitHash": "320811c"
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0x8700849e19ff6f7cfa640428de6127e0c6ce5e072ed56f0ba5243064c3fdfe2b",
"timestamp": 1780512131000,
"commitHash": "484b405"
}
},
"history": [
Expand Down
7 changes: 7 additions & 0 deletions deployments/json/10143.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@
"deploymentTxn": "0xf6e9a23b80dfe53fa52ba304fc83aeee76e8ca9cda20738be89a57440c4bd1c9",
"timestamp": 1736642980000,
"commitHash": "cba26cd"
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0xa4483028c6d4cdf0b2d4cc0d96a63290bc4724d21f5d72b1a7b349d89a43efb4",
"timestamp": 1780520073000,
"commitHash": "2899307"
}
},
"history": [
Expand Down
7 changes: 7 additions & 0 deletions deployments/json/11155111.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@
"initcodeHash": "e3a961ecd088faffe75dfa50cfa48ff2ed98085ec5e3be185c337f2a65d6af54",
"timestamp": 1779989940749,
"commitHash": "f684acb"
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0x4e523ef440e28ef0d0dd1f3f1dc825bd65bdf2903aab8fdb1e25fa57dd760477",
"timestamp": 1780512348000,
"commitHash": "484b405"
}
},
"history": [
Expand Down
7 changes: 7 additions & 0 deletions deployments/json/130.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@
"proxy": false,
"deploymentTxn": "GENESIS_1f98400000000000000000000000000000000004",
"timestamp": 1730748360000
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0x1964ed422c15332c63ea00933b9ebb2471664bf7a32eb973e4be6c6ad4cd1631",
"timestamp": 1780512150000,
"commitHash": "484b405"
}
},
"history": [
Expand Down
7 changes: 7 additions & 0 deletions deployments/json/1301.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@
"deploymentTxn": "0x79223e459e374a931b5cf32f82860ac3980306e7e23a4d3004ad974a3aee1ab4",
"timestamp": 1756328710000,
"commitHash": "320811c"
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0x38d91a2a6fb78b9d48c19cf2f07e7455b30815a46a8561496a0c2bd757b11260",
"timestamp": 1780512166000,
"commitHash": "484b405"
}
},
"history": [
Expand Down
7 changes: 7 additions & 0 deletions deployments/json/137.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@
"proxy": false,
"deploymentTxn": "0x45e77864249b0a388df4f98fc04203d6cd11ab6f1f4746cbf600d6055b376276",
"timestamp": 1640021988000
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0x1416754b1c37296bdcff1ba4f7ba42114c6270211ff951938c8f3e2a422a4444",
"timestamp": 1780520011000,
"commitHash": "2899307"
}
},
"history": [
Expand Down
7 changes: 7 additions & 0 deletions deployments/json/143.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@
"deploymentTxn": "0xf7d33e2d187de2fb75db98198c1e840d5ac7394cd3a756524699710bf3575e06",
"timestamp": 1763074970811,
"commitHash": "efab318"
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0x67236b1ce4125229595d30424e08b9c63885894be4f9a5a9516c69cf1b61df63",
"timestamp": 1780520023000,
"commitHash": "2899307"
}
},
"history": [
Expand Down
7 changes: 7 additions & 0 deletions deployments/json/1868.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@
"deploymentTxn": "0xd6fc8edbc48342932e18b28b8b62970648e5614bff5ba4b13d4d1d72fe689f0c",
"timestamp": 1738081525000,
"commitHash": "f9a2783"
},
"SwapProxy": {
"address": "0x0000000085E102724e78eCd2F45DC9cA239Affad",
"proxy": false,
"deploymentTxn": "0x57cad6b16c08166ea230cffb5e71f92da5252ded6629a83fb5c010b9d1944398",
"timestamp": 1780512185000,
"commitHash": "484b405"
}
},
"history": [
Expand Down
Loading
Loading