Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "contracts/lib/hyperlane-monorepo"]
path = contracts/lib/hyperlane-monorepo
url = https://github.com/hyperlane-xyz/hyperlane-monorepo.git
[submodule "contracts/lib/permit2"]
path = contracts/lib/permit2
url = https://github.com/Uniswap/permit2
5 changes: 5 additions & 0 deletions bin/ev-deployer/examples/devnet.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ mailbox = "0x0000000000000000000000000000000000001200"
[contracts.noop_ism]
address = "0x0000000000000000000000000000000000001300"

[contracts.permit2]
# Canonical Uniswap Permit2 address (same on all chains via CREATE2).
# Using it here so frontends, SDKs and routers work out of the box.
address = "0x000000000022D473030F116dDEE9F6B43aC78BA3"

[contracts.protocol_fee]
address = "0x0000000000000000000000000000000000001400"
owner = "0x000000000000000000000000000000000000Ad00"
Expand Down
47 changes: 28 additions & 19 deletions bin/ev-deployer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub(crate) struct ContractsConfig {
pub mailbox: Option<MailboxConfig>,
/// `NoopIsm` contract config (optional).
pub noop_ism: Option<NoopIsmConfig>,
/// `Permit2` contract config (optional).
pub permit2: Option<Permit2Config>,
/// `ProtocolFee` contract config (optional).
pub protocol_fee: Option<ProtocolFeeConfig>,
}
Expand Down Expand Up @@ -90,25 +92,7 @@ pub(crate) struct MerkleTreeHookConfig {
pub mailbox: Address,
}

/// `ProtocolFee` configuration (Hyperlane post-dispatch hook that charges a protocol fee).
#[derive(Debug, Deserialize)]
pub(crate) struct ProtocolFeeConfig {
/// Address to deploy at.
pub address: Address,
/// Owner address.
#[serde(default)]
pub owner: Address,
/// Maximum protocol fee in wei.
pub max_protocol_fee: u64,
/// Protocol fee charged per dispatch in wei.
#[serde(default)]
pub protocol_fee: u64,
/// Beneficiary address that receives collected fees.
#[serde(default)]
pub beneficiary: Address,
}

/// `Mailbox` configuration (Hyperlane core messaging hub).
/// `MailboxConfig` configuration (Hyperlane core messaging hub).
#[derive(Debug, Deserialize)]
pub(crate) struct MailboxConfig {
/// Address to deploy at.
Expand All @@ -134,6 +118,31 @@ pub(crate) struct NoopIsmConfig {
pub address: Address,
}

/// `Permit2` configuration (Uniswap token approval manager).
#[derive(Debug, Deserialize)]
pub(crate) struct Permit2Config {
/// Address to deploy at.
pub address: Address,
}

/// `ProtocolFee` configuration (Hyperlane post-dispatch hook that charges a protocol fee).
#[derive(Debug, Deserialize)]
pub(crate) struct ProtocolFeeConfig {
/// Address to deploy at.
pub address: Address,
/// Owner address.
#[serde(default)]
pub owner: Address,
/// Maximum protocol fee in wei.
pub max_protocol_fee: u64,
/// Protocol fee charged per dispatch in wei.
#[serde(default)]
pub protocol_fee: u64,
/// Beneficiary address that receives collected fees.
#[serde(default)]
pub beneficiary: Address,
}

impl DeployConfig {
/// Load and validate config from a TOML file.
pub(crate) fn load(path: &Path) -> eyre::Result<Self> {
Expand Down
1 change: 1 addition & 0 deletions bin/ev-deployer/src/contracts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub(crate) mod immutables;
pub(crate) mod mailbox;
pub(crate) mod merkle_tree_hook;
pub(crate) mod noop_ism;
pub(crate) mod permit2;
pub(crate) mod protocol_fee;

use alloy_primitives::{Address, Bytes, B256};
Expand Down
233 changes: 233 additions & 0 deletions bin/ev-deployer/src/contracts/permit2.rs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions bin/ev-deployer/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ pub(crate) fn build_alloc(config: &DeployConfig) -> Value {
insert_contract(&mut alloc, &contract);
}

if let Some(ref p2_config) = config.contracts.permit2 {
let contract = contracts::permit2::build(p2_config, config.chain.chain_id);
insert_contract(&mut alloc, &contract);
}

if let Some(ref pf_config) = config.contracts.protocol_fee {
let contract = contracts::protocol_fee::build(pf_config);
insert_contract(&mut alloc, &contract);
Expand Down Expand Up @@ -123,6 +128,7 @@ mod tests {
merkle_tree_hook: None,
mailbox: None,
noop_ism: None,
permit2: None,
protocol_fee: None,
},
}
Expand Down
6 changes: 6 additions & 0 deletions bin/ev-deployer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ fn main() -> eyre::Result<()> {
.as_ref()
.map(|c| c.address)
.ok_or_else(|| eyre::eyre!("noop_ism not configured"))?,
"permit2" => cfg
.contracts
.permit2
.as_ref()
.map(|c| c.address)
.ok_or_else(|| eyre::eyre!("permit2 not configured"))?,
"protocol_fee" => cfg
.contracts
.protocol_fee
Expand Down
7 changes: 7 additions & 0 deletions bin/ev-deployer/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ pub(crate) fn build_manifest(config: &DeployConfig) -> Value {
);
}

if let Some(ref p2) = config.contracts.permit2 {
manifest.insert(
"permit2".to_string(),
Value::String(format!("{}", p2.address)),
);
}

if let Some(ref pf) = config.contracts.protocol_fee {
manifest.insert(
"protocol_fee".to_string(),
Expand Down
24 changes: 23 additions & 1 deletion bin/ev-deployer/tests/e2e_genesis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ grep -q "000000000000000000000000000000000000FE00" "$GENESIS" \
|| fail "FeeVault address not found in genesis"
grep -q "0000000000000000000000000000000000001100" "$GENESIS" \
|| fail "MerkleTreeHook address not found in genesis"
grep -q "000000000022D473030F116dDEE9F6B43aC78BA3" "$GENESIS" \
|| fail "Permit2 address not found in genesis"

pass "genesis contains all three contract addresses"
pass "genesis contains all contract addresses"

# ── Step 3: Start ev-reth ────────────────────────────────

Expand Down Expand Up @@ -239,6 +241,26 @@ check_immutable "localDomain" 644 "$domain_word"
deployed_block_word="0000000000000000000000000000000000000000000000000000000000000000"
check_immutable "deployedBlock" 578 "$deployed_block_word"

# ── Step 7: Verify Permit2 ─────────────────────────────

PERMIT2="0x000000000022D473030F116dDEE9F6B43aC78BA3"

echo "=== Verifying Permit2 at $PERMIT2 ==="

# Check code is present
p2_code=$(rpc_call "eth_getCode" "[\"$PERMIT2\", \"latest\"]")
[[ "$p2_code" != "0x" && "$p2_code" != "0x0" && ${#p2_code} -gt 10 ]] \
|| fail "Permit2 has no bytecode (got: $p2_code)"
pass "Permit2 has bytecode (${#p2_code} hex chars)"

# Call DOMAIN_SEPARATOR() — selector 0x3644e515
# Should return the cached domain separator matching chain_id=1234 and the contract address
p2_domain_sep=$(rpc_call "eth_call" "[{\"to\":\"$PERMIT2\",\"data\":\"0x3644e515\"}, \"latest\"]")
expected_domain_sep="0x6cda538cafce36292a6ef27740629597f85f6716f5694d26d5c59fc1d07cfd95"
[[ "$(echo "$p2_domain_sep" | tr '[:upper:]' '[:lower:]')" == "$(echo "$expected_domain_sep" | tr '[:upper:]' '[:lower:]')" ]] \
|| fail "Permit2 DOMAIN_SEPARATOR() mismatch: got $p2_domain_sep, expected $expected_domain_sep"
pass "Permit2 DOMAIN_SEPARATOR() correct for chain_id=1234"

# ── Done ─────────────────────────────────────────────────

echo ""
Expand Down
1 change: 1 addition & 0 deletions contracts/lib/permit2
Submodule permit2 added at cc56ad
Loading