-
Notifications
You must be signed in to change notification settings - Fork 8
feat(ev-deployer): part 3 - add Permit2 contract support #182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
89eccaa
0c8f54e
2ba2b80
a540858
b9e2670
18ed817
f7d0e71
46ea9a6
e5f4eb9
be1b241
7e19222
e9fa70e
946026d
9dd5011
217be0c
67151c3
56548ec
fa8a428
bfa5a23
4da01ea
3faa629
eb413d1
aeffc0d
a2d194e
e8a39f8
089ef22
6b85563
93b3eaa
70111fd
fa0e71f
1acd3c8
cb838e8
ee68354
ef5ac9e
65bbf9e
08c9eb4
46b75bf
e365cfe
7f9e238
b1e5ae3
5d2be71
c32b633
04beb6b
c6e679b
cae3723
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| [submodule "contracts/lib/forge-std"] | ||
| path = contracts/lib/forge-std | ||
| url = https://github.com/foundry-rs/forge-std | ||
| [submodule "contracts/lib/permit2"] | ||
| path = contracts/lib/permit2 | ||
| url = https://github.com/Uniswap/permit2 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,8 @@ pub(crate) struct ChainConfig { | |
| pub(crate) struct ContractsConfig { | ||
| /// `AdminProxy` contract config (optional). | ||
| pub admin_proxy: Option<AdminProxyConfig>, | ||
| /// `Permit2` contract config (optional). | ||
| pub permit2: Option<Permit2Config>, | ||
| } | ||
|
|
||
| /// `AdminProxy` configuration. | ||
|
|
@@ -39,6 +41,13 @@ pub(crate) struct AdminProxyConfig { | |
| pub owner: Address, | ||
| } | ||
|
|
||
| /// `Permit2` configuration (Uniswap token approval manager). | ||
| #[derive(Debug, Deserialize)] | ||
| pub(crate) struct Permit2Config { | ||
| /// Address to deploy at. | ||
| pub address: Address, | ||
| } | ||
|
Comment on lines
+44
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate Right now a zero Permit2 address is accepted, which can produce an invalid deployment target without early feedback. Suggested fix fn validate(&self) -> eyre::Result<()> {
if let Some(ref ap) = self.contracts.admin_proxy {
eyre::ensure!(
!ap.owner.is_zero(),
"admin_proxy.owner must not be the zero address"
);
}
+
+ if let Some(ref p2) = self.contracts.permit2 {
+ eyre::ensure!(
+ !p2.address.is_zero(),
+ "permit2.address must not be the zero address"
+ );
+ }
Ok(())
}🤖 Prompt for AI Agents |
||
|
|
||
| impl DeployConfig { | ||
| /// Load and validate config from a TOML file. | ||
| pub(crate) fn load(path: &Path) -> eyre::Result<Self> { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,76 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! Bytecode patching for Solidity immutable variables. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! Solidity `immutable` values are embedded in the **runtime bytecode** by the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! compiler, not in storage. When compiling with placeholder values (e.g. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! `address(0)`, `uint32(0)`), the compiler leaves zero-filled regions at known | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! byte offsets. This module replaces those regions with the actual values from | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //! the deploy config at genesis-generation time. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use alloy_primitives::{B256, U256}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// A single immutable reference inside a bytecode blob. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug, Clone, Copy)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub(crate) struct ImmutableRef { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Byte offset into the **runtime** bytecode. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub start: usize, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Number of bytes (always 32 for EVM words). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub length: usize, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Patch a mutable bytecode slice, writing `value` at every listed offset. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// # Panics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Panics if any reference extends past the end of `bytecode`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub(crate) fn patch_bytes(bytecode: &mut [u8], refs: &[ImmutableRef], value: &[u8; 32]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for r in refs { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| r.start + r.length <= bytecode.len(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "immutable ref out of bounds: start={} length={} bytecode_len={}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| r.start, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| r.length, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bytecode.len() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bytecode[r.start..r.start + r.length].copy_from_slice(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enforce immutable word-size invariant explicitly.
Suggested fix pub(crate) fn patch_bytes(bytecode: &mut [u8], refs: &[ImmutableRef], value: &[u8; 32]) {
for r in refs {
+ assert!(
+ r.length == value.len(),
+ "immutable ref length mismatch: expected={} got={} start={}",
+ value.len(),
+ r.length,
+ r.start
+ );
assert!(
r.start + r.length <= bytecode.len(),
"immutable ref out of bounds: start={} length={} bytecode_len={}",
r.start,
r.length,
bytecode.len()
);
bytecode[r.start..r.start + r.length].copy_from_slice(value);
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Convenience: patch with an ABI-encoded `uint256`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub(crate) fn patch_u256(bytecode: &mut [u8], refs: &[ImmutableRef], val: U256) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let word = B256::from(val); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| patch_bytes(bytecode, refs, &word.0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mod tests { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use super::*; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn patch_single_ref() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut bytecode = vec![0u8; 64]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let refs = [ImmutableRef { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start: 10, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| length: 32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let value = B256::from(U256::from(42u64)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| patch_bytes(&mut bytecode, &refs, &value.0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(bytecode[41], 42); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // bytes before are untouched | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(bytecode[9], 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // bytes after are untouched | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(bytecode[42], 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[should_panic(expected = "immutable ref out of bounds")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn patch_out_of_bounds_panics() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut bytecode = vec![0u8; 16]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let refs = [ImmutableRef { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| length: 32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let value = [0u8; 32]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| patch_bytes(&mut bytecode, &refs, &value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,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); | ||
| } | ||
|
Comment on lines
+20
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prevent silent alloc overwrite when two configured contracts share one address. At Line 20-23, adding Permit2 means two configured contracts can now collide. 🛠️ Proposed fix (fail on duplicate deploy-config addresses)-pub(crate) fn build_alloc(config: &DeployConfig) -> Value {
+pub(crate) fn build_alloc(config: &DeployConfig) -> eyre::Result<Value> {
let mut alloc = Map::new();
if let Some(ref ap_config) = config.contracts.admin_proxy {
let contract = contracts::admin_proxy::build(ap_config);
- insert_contract(&mut alloc, &contract);
+ 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);
+ insert_contract(&mut alloc, &contract)?;
}
- Value::Object(alloc)
+ Ok(Value::Object(alloc))
}
@@
- let alloc = build_alloc(config);
+ let alloc = build_alloc(config)?;
@@
-fn insert_contract(alloc: &mut Map<String, Value>, contract: &GenesisContract) {
+fn insert_contract(alloc: &mut Map<String, Value>, contract: &GenesisContract) -> eyre::Result<()> {
let addr_key = normalize_addr(&format!("{}", contract.address));
@@
- alloc.insert(addr_key, Value::Object(entry));
+ if alloc.insert(addr_key.clone(), Value::Object(entry)).is_some() {
+ eyre::bail!("duplicate deploy address in config: {addr_key}");
+ }
+ Ok(())
}🤖 Prompt for AI Agents |
||
|
|
||
| Value::Object(alloc) | ||
| } | ||
|
|
||
|
|
@@ -102,6 +107,7 @@ mod tests { | |
| address: address!("000000000000000000000000000000000000ad00"), | ||
| owner: address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), | ||
| }), | ||
| permit2: None, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the full canonical Permit2 address in the config reference.
Line 36 truncates the canonical address with
..., which is error-prone for copy/paste in config docs.📝 Proposed doc fix
🤖 Prompt for AI Agents