diff --git a/CHANGELOG.md b/CHANGELOG.md index 1559f81..3c124fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` - Runner `resolc` using webkit is no longer supported. ### Fixed +- A missing byte swap for the create2 salt value. ## v0.1.0-dev.12 diff --git a/crates/integration/contracts/AddressPredictor.sol b/crates/integration/contracts/AddressPredictor.sol new file mode 100644 index 0000000..0bc8ae5 --- /dev/null +++ b/crates/integration/contracts/AddressPredictor.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +contract Predicted { + uint public salt; + + constructor(uint _salt) { + salt = _salt; + } +} + +contract AddressPredictor { + constructor(uint _salt, bytes memory _bytecode) payable { + address deployed = address(new Predicted{salt: bytes32(_salt)}(_salt)); + address predicted = predictAddress(_salt, _bytecode); + assert(deployed == predicted); + } + + function predictAddress( + uint _foo, + bytes memory _bytecode + ) public view returns (address predicted) { + bytes32 addr = keccak256( + abi.encodePacked( + bytes1(0xff), + address(this), + bytes32(_foo), + keccak256(abi.encodePacked(_bytecode, abi.encode(_foo))) + ) + ); + predicted = address(uint160(uint(addr))); + } +} diff --git a/crates/integration/src/cases.rs b/crates/integration/src/cases.rs index 41eae48..19afb0d 100644 --- a/crates/integration/src/cases.rs +++ b/crates/integration/src/cases.rs @@ -250,6 +250,17 @@ sol!( ); case!("Storage.sol", Storage, transientCall, storage_transient, value: U256); +sol!( + contract Predicted { + constructor(uint _foo); + } + contract AddressPredictor { + constructor(uint _foo, bytes memory _bytecode) payable; + } +); +case!("AddressPredictor.sol", Predicted, constructorCall, predicted_constructor, salt: U256); +case!("AddressPredictor.sol", AddressPredictor, constructorCall, address_predictor_constructor, salt: U256, bytecode: Bytes); + impl Contract { pub fn build(calldata: Vec, name: &'static str, code: &str) -> Self { Self { diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index f204894..1cede1a 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -486,3 +486,31 @@ fn transfer_denies_reentrancy() { } .run(); } + +#[test] +fn create2_salt() { + let salt = U256::from(777); + let predicted = Contract::predicted_constructor(salt).pvm_runtime; + let predictor = Contract::address_predictor_constructor(salt, predicted.clone().into()); + Specs { + actions: vec![ + Upload { + origin: TestAddress::Alice, + code: Code::Bytes(predicted), + storage_deposit_limit: None, + }, + Instantiate { + origin: TestAddress::Alice, + value: 0, + gas_limit: Some(GAS_LIMIT), + storage_deposit_limit: None, + code: Code::Bytes(predictor.pvm_runtime), + data: predictor.calldata, + salt: OptionalHex::default(), + }, + ], + differential: false, + ..Default::default() + } + .run(); +} diff --git a/crates/llvm-context/src/polkavm/evm/create.rs b/crates/llvm-context/src/polkavm/evm/create.rs index 7d60b7a..26b3255 100644 --- a/crates/llvm-context/src/polkavm/evm/create.rs +++ b/crates/llvm-context/src/polkavm/evm/create.rs @@ -32,6 +32,7 @@ where let salt_pointer = match salt { Some(salt) => { let salt_pointer = context.build_alloca_at_entry(context.word_type(), "salt_pointer"); + let salt = context.build_byte_swap(salt.into())?; context.build_store(salt_pointer, salt)?; salt_pointer } diff --git a/crates/solc-json-interface/src/standard_json/output/source.rs b/crates/solc-json-interface/src/standard_json/output/source.rs index 6472372..f42157f 100644 --- a/crates/solc-json-interface/src/standard_json/output/source.rs +++ b/crates/solc-json-interface/src/standard_json/output/source.rs @@ -196,7 +196,7 @@ impl Source { _ => None, }, ) - .last() + .next_back() .ok_or_else(|| anyhow::anyhow!("The last contract not found in the AST")) } }