Skip to content
Closed
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
77 changes: 77 additions & 0 deletions contracts/evm/deployments/EXTRACT_ORIGINAL_INITCODE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Extracting Original Init Code

## Problem

The current build environment produces different init code hashes than those used to mine the original vanity salts:

| Contract | Current Build Hash | Vanity Mining Hash | Expected Address |
|----------|-------------------|-------------------|------------------|
| x402ExactPermit2Proxy | `0x84571af86df...` | `Unknown` | `0x402085c248eea27d92e8b30b2c58ed07f9e20001` |
| x402UptoPermit2Proxy | `TBD` | `Unknown` | `0x402039b3d6e6bec5a02c2c9fd937ac17a6940002` |

## Solution Approach

To extract the original init code, we need to reverse-engineer it from existing deployments:

### Method 1: From Live Deployments

If the contracts are already deployed on any EVM chain:

```bash
# Find a chain where the contracts are deployed
# Extract the deployment transaction
cast tx <deployment_tx_hash> --rpc-url <rpc_url>

# The 'input' field contains: salt + init code
# Extract init code by removing the first 32 bytes (salt)
```

### Method 2: From Original Build Environment

If we can access the original build environment:
- Use the exact same compiler version, settings, and file paths
- Run the `ExtractInitCode.s.sol` script
- Verify the generated addresses match the expected vanity addresses

### Method 3: Vanity Salt Mining Records

If vanity mining logs are available:
- The mining process should have recorded the init code hash used
- Use that hash to reconstruct or verify the init code

## Current Status

**NEEDED**: Someone with access to the original build environment or deployment transaction data to provide the correct init code files.

## Files to Generate

Once the original init code is obtained:

```
deployments/
├── x402ExactPermit2Proxy.initcode # Raw hex (no 0x prefix)
├── x402ExactPermit2Proxy.salt # 32-byte hex (no 0x prefix)
├── x402UptoPermit2Proxy.initcode # Raw hex (no 0x prefix)
├── x402UptoPermit2Proxy.salt # 32-byte hex (no 0x prefix)
└── README.md # Deployment instructions
```

## Verification

After obtaining the init code files, verify they produce the correct addresses:

```solidity
// In ExtractInitCode.s.sol or a separate verification script
bytes memory initCode = hex"<initcode_from_file>";
bytes32 salt = hex"<salt_from_file>";
bytes32 initCodeHash = keccak256(initCode);
address expectedAddress = address(uint160(uint256(keccak256(
abi.encodePacked(
bytes1(0xff),
CREATE2_DEPLOYER,
salt,
initCodeHash
)
))));
// Should match vanity addresses
```
146 changes: 146 additions & 0 deletions contracts/evm/deployments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# x402 Permit2 Proxy Deterministic Deployments

This directory contains the raw creation bytecode (init code) and salts needed to deploy x402 Permit2 Proxies to their canonical deterministic addresses on any EVM chain.

## Problem Statement

The x402 Permit2 Proxy contracts use CREATE2 with vanity-mined salts to deploy to deterministic addresses across all EVM chains:

- **x402ExactPermit2Proxy**: `0x402085c248eea27d92e8b30b2c58ed07f9e20001`
- **x402UptoPermit2Proxy**: `0x402039b3d6e6bec5a02c2c9fd937ac17a6940002`

However, reproducing these deployments requires the **exact init code hash** that was used during vanity address mining. Solidity's CBOR metadata (appended to creation bytecode) includes compiler environment details that vary between builds, making local compilation produce different init code hashes.

## Solution

This directory provides the raw creation bytecode that produces the correct init code hashes for the canonical vanity addresses.

## Files

| File | Purpose |
|------|---------|
| `x402ExactPermit2Proxy.initcode` | Raw init code (creation bytecode + constructor args) for x402ExactPermit2Proxy |
| `x402ExactPermit2Proxy.salt` | Vanity-mined salt for x402ExactPermit2Proxy |
| `x402UptoPermit2Proxy.initcode` | Raw init code (creation bytecode + constructor args) for x402UptoPermit2Proxy |
| `x402UptoPermit2Proxy.salt` | Vanity-mined salt for x402UptoPermit2Proxy |

## Deployment Instructions

### Prerequisites

1. **CREATE2 Deployer**: Arachnid's deterministic deployer must be available at `0x4e59b44847b379578588920cA78FbF26c0B4956C`
2. **Permit2**: Canonical Permit2 must be deployed at `0x000000000022D473030F116dDEE9F6B43aC78BA3`

### Deploy x402ExactPermit2Proxy

```bash
# Using cast (from foundry)
cast send 0x4e59b44847b379578588920cA78FbF26c0B4956C \
"$(cat deployments/x402ExactPermit2Proxy.salt)$(cat deployments/x402ExactPermit2Proxy.initcode)" \
--rpc-url $RPC_URL \
--private-key $PRIVATE_KEY

# Expected deployment address: 0x402085c248eea27d92e8b30b2c58ed07f9e20001
```

### Deploy x402UptoPermit2Proxy

```bash
# Using cast (from foundry)
cast send 0x4e59b44847b379578588920cA78FbF26c0B4956C \
"$(cat deployments/x402UptoPermit2Proxy.salt)$(cat deployments/x402UptoPermit2Proxy.initcode)" \
--rpc-url $RPC_URL \
--private-key $PRIVATE_KEY

# Expected deployment address: 0x402039b3d6e6bec5a02c2c9fd937ac17a6940002
```

### Verification

After deployment, verify the contracts are deployed to the correct addresses:

```bash
# Check x402ExactPermit2Proxy
cast code 0x402085c248eea27d92e8b30b2c58ed07f9e20001 --rpc-url $RPC_URL

# Check x402UptoPermit2Proxy
cast code 0x402039b3d6e6bec5a02c2c9fd937ac17a6940002 --rpc-url $RPC_URL
```

Both should return non-empty bytecode.

### Verify Contract Functionality

```bash
# Verify Permit2 address is correctly set
cast call 0x402085c248eea27d92e8b30b2c58ed07f9e20001 "PERMIT2()" --rpc-url $RPC_URL
# Should return: 0x000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3

cast call 0x402039b3d6e6bec5a02c2c9fd937ac17a6940002 "PERMIT2()" --rpc-url $RPC_URL
# Should return: 0x000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
```

## Technical Details

### Contract Constructor

Both proxy contracts take a single constructor argument:
- `permit2` (address): The canonical Permit2 address (`0x000000000022D473030F116dDEE9F6B43aC78BA3`)

### CREATE2 Calculation

The deployment address is calculated as:
```
address = keccak256(0xff ++ deployerAddress ++ salt ++ keccak256(initCode))[12:]
```

Where:
- `deployerAddress`: `0x4e59b44847b379578588920cA78FbF26c0B4956C` (Arachnid's CREATE2 deployer)
- `salt`: The vanity-mined salt from the `.salt` files
- `initCode`: The raw bytecode from the `.initcode` files

### Network Compatibility

This approach works on any EVM chain where:
1. Arachnid's CREATE2 deployer is available
2. Permit2 is deployed at the canonical address
3. The chain ID doesn't affect bytecode generation

## Generating Init Code (Advanced)

If you need to regenerate init code (e.g., for contract modifications):

```bash
# Build contracts
forge build

# Extract init code using the provided script
forge script script/ExtractInitCode.s.sol --ffi
```

Note: The generated init code will likely have different hashes due to compiler metadata, requiring new vanity salt mining for deterministic addresses.

## References

- [CREATE2 Deployer](https://github.com/Arachnid/deterministic-deployment-proxy)
- [Permit2 Documentation](https://github.com/Uniswap/permit2)
- [EIP-1014: Skinny CREATE2](https://eips.ethereum.org/EIPS/eip-1014)

## Troubleshooting

### "CREATE2 deployer not found"
The Arachnid CREATE2 deployer needs to be deployed on the target chain first. See the [deployment instructions](https://github.com/Arachnid/deterministic-deployment-proxy).

### "Permit2 not found"
Permit2 must be deployed before the x402 proxies. Follow the [canonical Permit2 deployment](https://github.com/Uniswap/permit2#deployment-addresses).

### "Deployment failed"
- Check that you have sufficient ETH for gas
- Verify the init code files are complete and correctly formatted
- Ensure the salt files contain exactly 64 hex characters (no 0x prefix)

### "Wrong deployment address"
If the deployed address doesn't match the expected canonical address, it means:
- The init code hash doesn't match what was used for vanity mining
- There may be a discrepancy in the constructor arguments or bytecode
- Verify the files in this directory match the original vanity mining setup
145 changes: 145 additions & 0 deletions contracts/evm/script/ExtractInitCode.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Script, console2} from "forge-std/Script.sol";
import {x402ExactPermit2Proxy} from "../src/x402ExactPermit2Proxy.sol";
import {x402UptoPermit2Proxy} from "../src/x402UptoPermit2Proxy.sol";

/**
* @title ExtractInitCode
* @notice Extracts raw init code (creation bytecode + constructor args) for reproducible deployments
* @dev Run with: forge script script/ExtractInitCode.s.sol
*
* This script generates the raw init code that was used to mine the vanity salts,
* enabling anyone to deploy to the same deterministic addresses on any EVM chain
* without needing to reproduce the exact build environment.
*
* Output files:
* - deployments/x402ExactPermit2Proxy.initcode
* - deployments/x402UptoPermit2Proxy.initcode
* - deployments/x402ExactPermit2Proxy.salt
* - deployments/x402UptoPermit2Proxy.salt
*/
contract ExtractInitCode is Script {
/// @notice Canonical Permit2 address (same on all EVM chains)
address constant CANONICAL_PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

/// @notice Salt for x402ExactPermit2Proxy deterministic deployment
/// @dev Vanity mined for address 0x402085c248eea27d92e8b30b2c58ed07f9e20001
bytes32 constant EXACT_SALT = 0x0000000000000000000000000000000000000000000000003000000007263b0e;

/// @notice Salt for x402UptoPermit2Proxy deterministic deployment
/// @dev Vanity mined for address 0x402039b3d6e6bec5a02c2c9fd937ac17a6940002
bytes32 constant UPTO_SALT = 0x0000000000000000000000000000000000000000000000000000000000edb738;

/// @notice Arachnid's deterministic CREATE2 deployer (same on all EVM chains)
address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C;

function run() public {
console2.log("");
console2.log("============================================================");
console2.log(" x402 Init Code Extraction for Reproducible Deployments");
console2.log("============================================================");
console2.log("");

// Extract init code for both contracts
_extractExactInitCode();
_extractUptoInitCode();

console2.log("");
console2.log("Init code extraction complete!");
console2.log("");
console2.log("Files written:");
console2.log("- deployments/x402ExactPermit2Proxy.initcode");
console2.log("- deployments/x402UptoPermit2Proxy.initcode");
console2.log("- deployments/x402ExactPermit2Proxy.salt");
console2.log("- deployments/x402UptoPermit2Proxy.salt");
console2.log("");
console2.log("Use these files to deploy to deterministic addresses on any EVM chain.");
console2.log("See deployments/README.md for deployment instructions.");
}

function _extractExactInitCode() internal {
console2.log("------------------------------------------------------------");
console2.log(" x402ExactPermit2Proxy Init Code Extraction");
console2.log("------------------------------------------------------------");

// Generate init code: creation bytecode + constructor args
bytes memory initCode = abi.encodePacked(
type(x402ExactPermit2Proxy).creationCode,
abi.encode(CANONICAL_PERMIT2)
);

bytes32 initCodeHash = keccak256(initCode);
address expectedAddress = _computeCreate2Addr(EXACT_SALT, initCodeHash, CREATE2_DEPLOYER);

console2.log("Init code length:", initCode.length);
console2.log("Init code hash:", vm.toString(initCodeHash));
console2.log("Expected address:", expectedAddress);
console2.log("Salt:", vm.toString(EXACT_SALT));

// Write init code to file
string memory initCodeHex = vm.toString(initCode);
vm.writeFile("deployments/x402ExactPermit2Proxy.initcode", initCodeHex);

// Write salt to file (without 0x prefix for easier use with cast)
string memory saltHex = _removeHexPrefix(vm.toString(EXACT_SALT));
vm.writeFile("deployments/x402ExactPermit2Proxy.salt", saltHex);

console2.log("* Init code written to deployments/x402ExactPermit2Proxy.initcode");
console2.log("* Salt written to deployments/x402ExactPermit2Proxy.salt");
console2.log("");
}

function _extractUptoInitCode() internal {
console2.log("------------------------------------------------------------");
console2.log(" x402UptoPermit2Proxy Init Code Extraction");
console2.log("------------------------------------------------------------");

// Generate init code: creation bytecode + constructor args
bytes memory initCode = abi.encodePacked(
type(x402UptoPermit2Proxy).creationCode,
abi.encode(CANONICAL_PERMIT2)
);

bytes32 initCodeHash = keccak256(initCode);
address expectedAddress = _computeCreate2Addr(UPTO_SALT, initCodeHash, CREATE2_DEPLOYER);

console2.log("Init code length:", initCode.length);
console2.log("Init code hash:", vm.toString(initCodeHash));
console2.log("Expected address:", expectedAddress);
console2.log("Salt:", vm.toString(UPTO_SALT));

// Write init code to file
string memory initCodeHex = vm.toString(initCode);
vm.writeFile("deployments/x402UptoPermit2Proxy.initcode", initCodeHex);

// Write salt to file (without 0x prefix for easier use with cast)
string memory saltHex = _removeHexPrefix(vm.toString(UPTO_SALT));
vm.writeFile("deployments/x402UptoPermit2Proxy.salt", saltHex);

console2.log("* Init code written to deployments/x402UptoPermit2Proxy.initcode");
console2.log("* Salt written to deployments/x402UptoPermit2Proxy.salt");
console2.log("");
}

function _computeCreate2Addr(
bytes32 salt,
bytes32 initCodeHash,
address deployer
) internal pure returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, initCodeHash)))));
}

function _removeHexPrefix(string memory hexString) internal pure returns (string memory) {
bytes memory hexBytes = bytes(hexString);
require(hexBytes.length >= 2 && hexBytes[0] == "0" && hexBytes[1] == "x", "Invalid hex string");

bytes memory result = new bytes(hexBytes.length - 2);
for (uint256 i = 2; i < hexBytes.length; i++) {
result[i - 2] = hexBytes[i];
}

return string(result);
}
}
Loading
Loading