use alloy_primitives::{address, hex, keccak256, Address, Bytes, Signature, TxKind, B256, U256};
use alloy_sol_types::SolCall;
use mega_evm::{
alloy_consensus::{Signed, TxEip1559, TxLegacy},
revm::context::result::{ExecutionResult, ResultAndState},
sandbox::{
decode_error_result,
tests::{
CREATE2_FACTORY_CODE_HASH, CREATE2_FACTORY_CONTRACT, CREATE2_FACTORY_DEPLOYER,
CREATE2_FACTORY_TX, EIP1820_CODE_HASH, EIP1820_CONTRACT, EIP1820_DEPLOYER, EIP1820_TX,
NON_CONTRACT_CREATION_TX, POST_EIP155_CHAIN_1_TX,
},
KeylessDeployError,
},
test_utils::{transact, BytecodeBuilder, MemoryDatabase},
IKeylessDeploy, MegaSpecId, KEYLESS_DEPLOY_ADDRESS, KEYLESS_DEPLOY_CODE,
};
use revm::bytecode::opcode::{
CALL, CALLDATACOPY, CALLDATASIZE, CODECOPY, CREATE, GAS, ISZERO, JUMPDEST, JUMPI, LOG0, MLOAD,
MSTORE, POP, PUSH0, RETURN, RETURNDATACOPY, RETURNDATASIZE, REVERT, SELFDESTRUCT, SSTORE,
STATICCALL, STOP,
};
const TEST_CALLER: Address = address!("0000000000000000000000000000000000100000");
const LARGE_GAS_LIMIT_OVERRIDE: u64 = 10_000_000_000;
fn call_keyless_deploy(
spec: MegaSpecId,
db: &mut MemoryDatabase,
tx_bytes: Bytes,
gas_limit_override: u64,
value: U256,
) -> ResultAndState<mega_evm::MegaHaltReason> {
let call_data = IKeylessDeploy::keylessDeployCall {
keylessDeploymentTransaction: tx_bytes,
gasLimitOverride: U256::from(gas_limit_override),
}
.abi_encode();
transact(spec, db, TEST_CALLER, Some(KEYLESS_DEPLOY_ADDRESS), call_data.into(), value).unwrap()
}
fn errors_match(a: &KeylessDeployError, b: &KeylessDeployError) -> bool {
match (a, b) {
(
KeylessDeployError::ExecutionHalted { gas_used: a_gas, .. },
KeylessDeployError::ExecutionHalted { gas_used: b_gas, .. },
) => a_gas == b_gas,
_ => a == b,
}
}
fn assert_revert_with_error(
result: &ResultAndState<mega_evm::MegaHaltReason>,
expected: KeylessDeployError,
) {
match &result.result {
ExecutionResult::Revert { output, .. } => {
let error = decode_error_result(output)
.unwrap_or_else(|| panic!("Failed to decode error from output: {:?}", output));
assert!(errors_match(&error, &expected), "Expected {:?}, got {:?}", expected, error);
}
other => panic!("Expected Revert, got {:?}", other),
}
}
fn create_pre_eip155_deploy_tx_with_nonce_and_value(
nonce: u64,
init_code: Bytes,
value: U256,
) -> (Bytes, Address) {
let tx = TxLegacy {
nonce,
gas_price: 100_000_000_000, gas_limit: 1_000_000, to: TxKind::Create,
value,
input: init_code,
chain_id: None, };
let r = U256::from_be_bytes(hex!(
"2222222222222222222222222222222222222222222222222222222222222222"
));
let s = U256::from_be_bytes(hex!(
"2222222222222222222222222222222222222222222222222222222222222222"
));
let sig = Signature::new(r, s, false);
let signed = Signed::new_unchecked(tx, sig, B256::ZERO);
let mut buf = Vec::new();
signed.rlp_encode(&mut buf);
let tx_bytes = Bytes::from(buf);
let signer = signed.recover_signer().expect("should recover signer");
(tx_bytes, signer)
}
fn create_pre_eip155_deploy_tx_with_value(init_code: Bytes, value: U256) -> (Bytes, Address) {
create_pre_eip155_deploy_tx_with_nonce_and_value(0, init_code, value)
}
fn create_pre_eip155_deploy_tx(init_code: Bytes) -> (Bytes, Address) {
create_pre_eip155_deploy_tx_with_nonce_and_value(0, init_code, U256::ZERO)
}
fn calculate_deploy_address_for_tx(tx_bytes: &[u8]) -> Address {
use mega_evm::{
sandbox::{calculate_keyless_deploy_address, decode_keyless_tx, recover_signer},
MegaSpecId,
};
let signed = decode_keyless_tx(tx_bytes, MegaSpecId::REX2).expect("should decode tx");
let signer = recover_signer(&signed).expect("should recover signer");
calculate_keyless_deploy_address(signer)
}
#[test]
fn test_keyless_deploy_eip1820() {
let mut db = MemoryDatabase::default();
db.set_account_balance(EIP1820_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from(EIP1820_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
assert_eq!(ret.deployedAddress, EIP1820_CONTRACT);
assert!(ret.gasUsed > 0, "Expected non-zero gas usage");
let contract = state.get(&EIP1820_CONTRACT).expect("contract should exist in state");
assert!(
contract.info.code.as_ref().map(|c| !c.is_empty()).unwrap_or(false),
"contract should have non-empty code"
);
assert_eq!(contract.info.code_hash, EIP1820_CODE_HASH, "contract code hash should match");
}
#[test]
fn test_keyless_deploy_create2_factory() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
assert_eq!(ret.deployedAddress, CREATE2_FACTORY_CONTRACT);
assert!(ret.gasUsed > 0, "Expected non-zero gas usage");
let contract = state.get(&CREATE2_FACTORY_CONTRACT).expect("contract should exist in state");
assert!(
contract.info.code.as_ref().map(|c| !c.is_empty()).unwrap_or(false),
"contract should have non-empty code"
);
assert_eq!(
contract.info.code_hash, CREATE2_FACTORY_CODE_HASH,
"contract code hash should match"
);
}
#[test]
fn test_keyless_deploy_rejects_ether_transfer() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
db.set_account_balance(TEST_CALLER, U256::from(1_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::from(1), );
assert_revert_with_error(&result, KeylessDeployError::NoEtherTransfer);
}
#[test]
fn test_keyless_deploy_gas_limit_too_low() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let too_low_gas_limit = 99_999;
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
too_low_gas_limit,
U256::ZERO,
);
assert_revert_with_error(
&result,
KeylessDeployError::GasLimitTooLow {
tx_gas_limit: 100_000,
provided_gas_limit: too_low_gas_limit,
},
);
}
#[test]
fn test_keyless_deploy_insufficient_balance_zero() {
let mut db = MemoryDatabase::default();
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::InsufficientBalance);
}
#[test]
fn test_keyless_deploy_insufficient_balance_partial() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::InsufficientBalance);
}
#[test]
fn test_keyless_deploy_contract_already_exists() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
db.set_account_code(CREATE2_FACTORY_CONTRACT, Bytes::from_static(&[0x60, 0x00]));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::ContractAlreadyExists);
}
#[test]
fn test_keyless_deploy_invalid_signature() {
let mut db = MemoryDatabase::default();
let mut corrupted_tx = CREATE2_FACTORY_TX.to_vec();
corrupted_tx[102..134].fill(0xff);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from(corrupted_tx),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::InvalidSignature);
}
#[test]
fn test_keyless_deploy_malformed_encoding() {
let mut db = MemoryDatabase::default();
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(&hex!("deadbeef")),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::MalformedEncoding);
}
#[test]
fn test_keyless_deploy_not_contract_creation() {
let mut db = MemoryDatabase::default();
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(NON_CONTRACT_CREATION_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::NotContractCreation);
}
#[test]
fn test_keyless_deploy_not_pre_eip155() {
let mut db = MemoryDatabase::default();
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(POST_EIP155_CHAIN_1_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::NotPreEIP155);
}
#[test]
fn test_keyless_deploy_non_zero_tx_nonce() {
let mut db = MemoryDatabase::default();
let init_code = Bytes::from_static(&hex!("6000")); let (tx_bytes, signer) =
create_pre_eip155_deploy_tx_with_nonce_and_value(1, init_code, U256::ZERO);
db.set_account_balance(signer, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::NonZeroTxNonce { tx_nonce: 1 });
}
#[test]
fn test_keyless_deploy_rejects_corrupted_signature() {
let mut db = MemoryDatabase::default();
let mut corrupted_tx = CREATE2_FACTORY_TX.to_vec();
corrupted_tx[102..134].fill(0xff);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from(corrupted_tx),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::InvalidSignature);
}
#[test]
fn test_keyless_deploy_modified_s_changes_signer() {
let mut db = MemoryDatabase::default();
let mut modified_tx = CREATE2_FACTORY_TX.to_vec();
modified_tx[135..167].fill(0x33);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from(modified_tx),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::InsufficientBalance);
}
#[test]
fn test_keyless_deploy_rejects_eip2718_typed_envelope() {
let mut db = MemoryDatabase::default();
let eip1559_tx = TxEip1559 {
chain_id: 1,
nonce: 0,
gas_limit: 100_000,
max_fee_per_gas: 100_000_000_000,
max_priority_fee_per_gas: 1_000_000_000,
to: TxKind::Create,
value: U256::ZERO,
input: Bytes::from_static(&[0x60, 0x80, 0x60, 0x40, 0x52]), access_list: Default::default(),
};
let sig = Signature::new(U256::from(0x1234u64), U256::from(0x5678u64), false);
let signed = Signed::new_unchecked(eip1559_tx, sig, B256::ZERO);
let mut rlp_encoded = Vec::new();
signed.rlp_encode(&mut rlp_encoded);
let mut encoded = vec![0x02];
encoded.extend_from_slice(&rlp_encoded);
assert_eq!(encoded[0], 0x02, "EIP-1559 tx should start with type byte 0x02");
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from(encoded),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::MalformedEncoding);
}
#[test]
fn test_keyless_deploy_empty_initcode() {
let mut db = MemoryDatabase::default();
let (tx_bytes, signer) = create_pre_eip155_deploy_tx(Bytes::new());
let initial_balance = U256::from(1_000_000_000_000_000_000_000u128);
db.set_account_balance(signer, initial_balance);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert!(
matches!(result.result, ExecutionResult::Success { .. }),
"Expected Success with EmptyCodeDeployed error, got {:?}",
result.result
);
let output = result.result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output)
.expect("should decode return value");
assert_eq!(ret.deployedAddress, Address::ZERO, "Should return zero address");
assert!(!ret.errorData.is_empty(), "Should have error data");
let error = decode_error_result(&ret.errorData).expect("Should decode error");
assert!(
matches!(error, KeylessDeployError::EmptyCodeDeployed { .. }),
"Expected EmptyCodeDeployed, got {:?}",
error
);
let signer_account = result.state.get(&signer).expect("signer should exist");
assert!(signer_account.info.balance < initial_balance, "Signer should be charged");
}
#[test]
fn test_keyless_deploy_execution_reverted() {
let mut db = MemoryDatabase::default();
let init_code = Bytes::from_static(&hex!("60006000fd"));
let (tx_bytes, signer) = create_pre_eip155_deploy_tx(init_code);
let initial_balance = U256::from(1_000_000_000_000_000_000_000u128);
db.set_account_balance(signer, initial_balance);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert!(
matches!(result.result, ExecutionResult::Success { .. }),
"Expected Success with error data, got {:?}",
result.result
);
let output = result.result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output)
.expect("should decode return value");
assert_eq!(ret.deployedAddress, Address::ZERO, "Failed deploy should return zero address");
assert!(!ret.errorData.is_empty(), "Failed deploy should have error data");
let error = decode_error_result(&ret.errorData)
.unwrap_or_else(|| panic!("Failed to decode error from errorData"));
match error {
KeylessDeployError::ExecutionReverted { gas_used, .. } => {
assert!(gas_used > 0, "Expected non-zero gas usage");
assert_eq!(ret.gasUsed, gas_used, "gasUsed in return should match error");
let signer_account = result.state.get(&signer).expect("signer should exist in state");
let expected_charge = U256::from(gas_used) * U256::from(100_000_000_000u64);
assert_eq!(signer_account.info.balance, initial_balance - expected_charge);
}
other => panic!("Expected ExecutionReverted, got {:?}", other),
}
}
#[test]
fn test_keyless_deploy_execution_halted_invalid_opcode() {
let mut db = MemoryDatabase::default();
let init_code = Bytes::from_static(&hex!("fe"));
let (tx_bytes, signer) = create_pre_eip155_deploy_tx(init_code);
let initial_balance = U256::from(1_000_000_000_000_000_000_000u128);
db.set_account_balance(signer, initial_balance);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert!(
matches!(result.result, ExecutionResult::Success { .. }),
"Expected Success with error data, got {:?}",
result.result
);
let output = result.result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output)
.expect("should decode return value");
assert_eq!(ret.deployedAddress, Address::ZERO, "Failed deploy should return zero address");
assert!(!ret.errorData.is_empty(), "Failed deploy should have error data");
let error = decode_error_result(&ret.errorData)
.unwrap_or_else(|| panic!("Failed to decode error from errorData"));
match error {
KeylessDeployError::ExecutionHalted { gas_used, .. } => {
assert!(gas_used > 0, "Expected non-zero gas usage");
assert_eq!(ret.gasUsed, gas_used, "gasUsed in return should match error");
let signer_account = result.state.get(&signer).expect("signer should exist in state");
let expected_charge = U256::from(gas_used) * U256::from(100_000_000_000u64);
assert_eq!(signer_account.info.balance, initial_balance - expected_charge);
}
other => panic!("Expected ExecutionHalted, got {:?}", other),
}
}
#[test]
fn test_keyless_deploy_gas_limit_exactly_equal() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
100_000, U256::ZERO,
);
if let ExecutionResult::Revert { output, .. } = &result.result {
let error = decode_error_result(output)
.unwrap_or_else(|| panic!("Failed to decode error from output: {:?}", output));
assert!(
!matches!(error, KeylessDeployError::GasLimitTooLow { .. }),
"Should not get GasLimitTooLow with exact gas limit, got: {:?}",
error
);
}
}
#[test]
fn test_keyless_deploy_balance_exactly_sufficient() {
let mut db = MemoryDatabase::default();
let gas_price = 100_000_000_000u64; let exact_balance = U256::from(gas_price) * U256::from(LARGE_GAS_LIMIT_OVERRIDE);
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, exact_balance);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, .. } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success with exact balance, got: {:?}",
result
);
}
#[test]
fn test_keyless_deploy_increments_nonce_from_zero() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let deployer = state.get(&CREATE2_FACTORY_DEPLOYER).expect("deployer should exist in state");
assert_eq!(deployer.info.nonce, 1, "deployer nonce should be incremented from 0 to 1");
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
assert_eq!(ret.deployedAddress, CREATE2_FACTORY_CONTRACT);
}
#[test]
fn test_keyless_deploy_nonce_override_to_zero() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
db.set_account_nonce(CREATE2_FACTORY_DEPLOYER, 1);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
assert_eq!(
ret.deployedAddress, CREATE2_FACTORY_CONTRACT,
"Contract should deploy at expected address (nonce=0)"
);
let contract = state.get(&CREATE2_FACTORY_CONTRACT).expect("contract should exist in state");
assert!(
contract.info.code.as_ref().map(|c| !c.is_empty()).unwrap_or(false),
"contract should have non-empty code"
);
assert_eq!(contract.info.code_hash, CREATE2_FACTORY_CODE_HASH);
let deployer = state.get(&CREATE2_FACTORY_DEPLOYER).expect("deployer should exist in state");
assert_eq!(deployer.info.nonce, 1, "deployer nonce should be left unchanged");
}
#[test]
fn test_keyless_deploy_signer_nonce_too_high() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
db.set_account_nonce(CREATE2_FACTORY_DEPLOYER, 2);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result, KeylessDeployError::SignerNonceTooHigh { signer_nonce: 2 });
}
#[test]
fn test_keyless_deploy_not_available_before_rex2() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX1, &mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success (no-op) in REX1, got: {:?}",
result
);
assert!(
!state.contains_key(&CREATE2_FACTORY_CONTRACT),
"Contract should NOT be deployed in REX1"
);
}
#[test]
fn test_keyless_deploy_not_intercepted_for_inner_calls() {
let mut db = MemoryDatabase::default();
assert!(!KEYLESS_DEPLOY_CODE.is_empty(), "KEYLESS_DEPLOY_CODE should not be empty");
db.set_account_code(KEYLESS_DEPLOY_ADDRESS, KEYLESS_DEPLOY_CODE);
let proxy_code = BytecodeBuilder::default()
.append(CALLDATASIZE)
.append(PUSH0) .append(PUSH0) .append(CALLDATACOPY)
.append(PUSH0) .append(PUSH0) .append(CALLDATASIZE) .append(PUSH0) .append(PUSH0) .push_address(KEYLESS_DEPLOY_ADDRESS)
.append(GAS)
.append(CALL)
.append(ISZERO)
.push_number(0x2b_u8) .append(JUMPI)
.append(RETURNDATASIZE)
.append(PUSH0)
.append(PUSH0)
.append(RETURNDATACOPY)
.append(RETURNDATASIZE)
.append(PUSH0)
.append(RETURN)
.append(JUMPDEST) .append(RETURNDATASIZE)
.append(PUSH0)
.append(PUSH0)
.append(RETURNDATACOPY)
.append(RETURNDATASIZE)
.append(PUSH0)
.append(REVERT)
.build();
let proxy_address = address!("0000000000000000000000000000000000300000");
db.set_account_code(proxy_address, proxy_code);
db.set_account_balance(TEST_CALLER, U256::from(1_000_000_000_000_000_000_000u128));
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let call_data = IKeylessDeploy::keylessDeployCall {
keylessDeploymentTransaction: Bytes::from_static(CREATE2_FACTORY_TX),
gasLimitOverride: U256::from(LARGE_GAS_LIMIT_OVERRIDE),
}
.abi_encode();
let result = transact(
MegaSpecId::REX2,
&mut db,
TEST_CALLER,
Some(proxy_address),
call_data.into(),
U256::ZERO,
)
.unwrap();
assert_revert_with_error(&result, KeylessDeployError::NotIntercepted);
}
#[test]
fn test_keyless_deploy_contract_deployed_on_rex2_activation() {
use alloy_evm::{block::BlockExecutor, Evm, EvmEnv, EvmFactory};
use alloy_hardforks::ForkCondition;
use alloy_op_evm::block::receipt_builder::OpAlloyReceiptBuilder;
use alloy_primitives::B256;
use mega_evm::{
BlockLimits, MegaBlockExecutionCtx, MegaBlockExecutor, MegaEvmFactory, MegaHardfork,
MegaHardforkConfig, TestExternalEnvs, KEYLESS_DEPLOY_CODE_HASH,
};
use revm::{context::BlockEnv, database::State};
let mut db = MemoryDatabase::default();
let mut state = State::builder().with_database(&mut db).build();
let external_envs = TestExternalEnvs::<std::convert::Infallible>::new();
let evm_factory = MegaEvmFactory::new().with_external_env_factory(external_envs);
let mut cfg_env = revm::context::CfgEnv::default();
cfg_env.spec = MegaSpecId::REX2;
let block_env = BlockEnv {
number: revm::primitives::U256::from(1000),
timestamp: revm::primitives::U256::from(1_800_000_000),
gas_limit: 30_000_000,
..Default::default()
};
let evm_env = EvmEnv::new(cfg_env, block_env);
let evm = evm_factory.create_evm(&mut state, evm_env);
let block_ctx = MegaBlockExecutionCtx::new(
B256::ZERO,
Some(B256::ZERO),
Default::default(),
BlockLimits::no_limits(),
);
let chain_spec =
MegaHardforkConfig::default().with(MegaHardfork::Rex2, ForkCondition::Timestamp(0));
let receipt_builder = OpAlloyReceiptBuilder::default();
let mut executor = MegaBlockExecutor::new(evm, block_ctx, chain_spec.clone(), receipt_builder);
executor.apply_pre_execution_changes().expect("Pre-execution changes should succeed");
let db_ref = executor.evm_mut().db_mut();
let cache_acc =
db_ref.load_cache_account(KEYLESS_DEPLOY_ADDRESS).expect("Should be able to load account");
let acc_info = cache_acc.account_info().expect("Keyless deploy contract account should exist");
assert_eq!(
acc_info.code_hash, KEYLESS_DEPLOY_CODE_HASH,
"Keyless deploy contract code hash should match"
);
assert!(acc_info.code.is_some(), "Code should be set on the account");
let deployed_code = acc_info.code.as_ref().unwrap();
assert_eq!(
deployed_code.original_bytes(),
KEYLESS_DEPLOY_CODE,
"Deployed code should match original code"
);
use mega_evm::transact_deploy_keyless_deploy_contract;
let result = transact_deploy_keyless_deploy_contract(&chain_spec, 0, db_ref)
.expect("Should not error")
.unwrap();
assert_eq!(
result.len(),
1,
"Keyless deploy should already be deployed, so function should return state with account marked as read"
);
}
#[test]
fn test_keyless_deploy_address_with_existing_balance() {
let mut db = MemoryDatabase::default();
let deploy_address = calculate_deploy_address_for_tx(CREATE2_FACTORY_TX);
assert_eq!(deploy_address, CREATE2_FACTORY_CONTRACT);
let pre_existing_balance = U256::from(1_000_000_000_000_000_000u128); db.set_account_balance(deploy_address, pre_existing_balance);
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
assert_eq!(ret.deployedAddress, CREATE2_FACTORY_CONTRACT);
let contract = state.get(&CREATE2_FACTORY_CONTRACT).expect("contract should exist in state");
assert!(
contract.info.code.as_ref().map(|c| !c.is_empty()).unwrap_or(false),
"contract should have non-empty code"
);
assert_eq!(
contract.info.code_hash, CREATE2_FACTORY_CODE_HASH,
"contract code hash should match"
);
assert_eq!(contract.info.balance, pre_existing_balance, "balance should be preserved");
}
#[test]
fn test_keyless_deploy_init_code_selfdestructs() {
let mut db = MemoryDatabase::default();
let beneficiary = address!("0000000000000000000000000000000000beeef1");
let contract_value = U256::from(1_000_000_000_000_000_000u128);
let init_code = BytecodeBuilder::default()
.push_address(beneficiary)
.append(SELFDESTRUCT)
.push_number(1_u8) .push_number(0x1a_u8) .push_number(0_u8) .append(CODECOPY)
.push_number(1_u8) .push_number(0_u8) .append(RETURN)
.append(STOP) .build();
let (tx_bytes, signer) = create_pre_eip155_deploy_tx_with_value(init_code, contract_value);
let initial_balance = U256::from(1_001_000_000_000_000_000_000u128);
db.set_account_balance(signer, initial_balance);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success with EmptyCodeDeployed error, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
assert_eq!(ret.deployedAddress, Address::ZERO, "EmptyCodeDeployed should return zero address");
assert!(!ret.errorData.is_empty(), "Should have error data");
let error = decode_error_result(&ret.errorData)
.unwrap_or_else(|| panic!("Failed to decode error from errorData"));
assert!(
matches!(error, KeylessDeployError::EmptyCodeDeployed { .. }),
"Expected EmptyCodeDeployed, got {:?}",
error
);
let beneficiary_acc = state.get(&beneficiary).expect("beneficiary should be in state");
assert_eq!(
beneficiary_acc.info.balance, contract_value,
"beneficiary should receive the contract's ETH via SELFDESTRUCT"
);
let signer_account = state.get(&signer).expect("signer should exist in state");
let gas_cost = U256::from(ret.gasUsed) * U256::from(100_000_000_000u64);
let expected_balance = initial_balance - gas_cost - contract_value;
assert_eq!(
signer_account.info.balance, expected_balance,
"signer should be charged gas + value"
);
}
#[test]
fn test_keyless_deploy_modifies_other_contract_state() {
let mut db = MemoryDatabase::default();
let storage_contract_code = BytecodeBuilder::default()
.push_number(0x20_u8) .push_number(0_u8) .push_number(0_u8) .append(CALLDATACOPY)
.push_number(0_u8) .append(MLOAD)
.push_number(0_u8) .append(SSTORE)
.stop()
.build();
let storage_contract = address!("0000000000000000000000000000000000500000");
db.set_account_code(storage_contract, storage_contract_code);
let test_value = U256::from(0x1234_5678_9abc_def0u64);
let init_code = BytecodeBuilder::default()
.mstore(0, test_value.to_be_bytes_vec())
.push_number(0_u8) .push_number(0_u8) .push_number(0x20_u8) .push_number(0_u8) .push_number(0_u8) .push_address(storage_contract)
.append(GAS)
.append(CALL)
.append(POP)
.push_number(1_u8) .push_number(0x49_u8) .push_number(0_u8) .append(CODECOPY)
.push_number(1_u8) .push_number(0_u8) .append(RETURN)
.append(STOP) .build();
let (tx_bytes, signer) = create_pre_eip155_deploy_tx_with_value(init_code, U256::ZERO);
db.set_account_balance(signer, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
let contract = state.get(&ret.deployedAddress).expect("contract should exist in state");
let code = contract.info.code.as_ref().expect("contract should have code");
assert!(!code.is_empty(), "contract should have non-empty code");
let storage_acc = state.get(&storage_contract).expect("storage contract should be in state");
let stored_value = storage_acc.storage.get(&U256::ZERO).expect("slot 0 should exist");
assert_eq!(stored_value.present_value, test_value, "storage should contain test value");
}
#[test]
fn test_keyless_deploy_creates_child_contract() {
let mut db = MemoryDatabase::default();
let child_runtime_code: &[u8] = &[0x00];
let child_init_code = BytecodeBuilder::default()
.push_number(1_u8) .push_number(0x0c_u8) .push_number(0_u8) .append(CODECOPY)
.push_number(1_u8) .push_number(0_u8) .append(RETURN)
.append_many(child_runtime_code.iter().copied())
.build();
let child_init_len = child_init_code.len();
let mut parent_init = BytecodeBuilder::default();
parent_init = parent_init.mstore(0, &child_init_code);
parent_init = parent_init.push_number(child_init_len as u8); parent_init = parent_init.push_number(0_u8); parent_init = parent_init.push_number(0_u8); parent_init = parent_init.append(CREATE);
parent_init = parent_init.push_number(0_u8); parent_init = parent_init.append(SSTORE);
let current_len = parent_init.len();
let runtime_offset = current_len + 12;
parent_init = parent_init
.push_number(1_u8) .push_number(runtime_offset as u8)
.push_number(0_u8)
.append(CODECOPY)
.push_number(1_u8)
.push_number(0_u8)
.append(RETURN)
.append(STOP);
let init_code = parent_init.build();
let (tx_bytes, signer) = create_pre_eip155_deploy_tx_with_value(init_code, U256::ZERO);
db.set_account_balance(signer, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
let parent_address = ret.deployedAddress;
let runtime_code: &[u8] = &[0x00];
let expected_code_hash = keccak256(runtime_code);
let parent = state.get(&parent_address).expect("parent contract should exist");
assert!(
parent.info.code.as_ref().map(|c| !c.is_empty()).unwrap_or(false),
"parent should have code"
);
assert_eq!(parent.info.code_hash, expected_code_hash, "parent code hash should match");
let child_address_slot = parent.storage.get(&U256::ZERO).expect("slot 0 should exist");
let child_address = Address::from_word(child_address_slot.present_value.into());
let child = state.get(&child_address).expect("child contract should exist in state");
assert!(
child.info.code.as_ref().map(|c| !c.is_empty()).unwrap_or(false),
"child should have code"
);
assert_eq!(child.info.code_hash, expected_code_hash, "child code hash should match");
}
#[test]
fn test_keyless_deploy_with_value_transfer() {
let mut db = MemoryDatabase::default();
let init_code = BytecodeBuilder::default()
.push_number(1_u8) .push_number(0x0c_u8) .push_number(0_u8) .append(CODECOPY)
.push_number(1_u8) .push_number(0_u8) .append(RETURN)
.append(STOP) .build();
let transfer_value = U256::from(500_000_000_000_000_000u128); let (tx_bytes, signer) = create_pre_eip155_deploy_tx_with_value(init_code, transfer_value);
let signer_balance = U256::from(1_000_500_000_000_000_000_000u128);
db.set_account_balance(signer, signer_balance);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
let runtime_code: &[u8] = &[0x00]; let expected_code_hash = keccak256(runtime_code);
let contract = state.get(&ret.deployedAddress).expect("contract should exist");
assert!(
contract.info.code.as_ref().map(|c| !c.is_empty()).unwrap_or(false),
"contract should have non-empty code"
);
assert_eq!(contract.info.code_hash, expected_code_hash, "contract code hash should match");
assert_eq!(contract.info.balance, transfer_value, "contract should have transferred value");
}
#[test]
fn test_keyless_deploy_emits_logs() {
let mut db = MemoryDatabase::default();
let log_data = 0xdeadbeef_u32;
let init_code = BytecodeBuilder::default()
.push_bytes(log_data.to_be_bytes())
.push_number(0_u8)
.append(MSTORE)
.push_number(4_u8) .push_number(0x1c_u8) .append(LOG0)
.push_number(1_u8) .push_number(0x14_u8) .push_number(0_u8) .append(CODECOPY)
.push_number(1_u8) .push_number(0_u8) .append(RETURN)
.append(STOP) .build();
let (tx_bytes, signer) = create_pre_eip155_deploy_tx_with_value(init_code, U256::ZERO);
db.set_account_balance(signer, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state: _ } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let logs = result.logs();
assert_eq!(logs.len(), 1, "Expected exactly one log from sandbox");
let log = &logs[0];
assert_eq!(log.data.data.as_ref(), &log_data.to_be_bytes(), "Log data should match");
assert!(log.data.topics().is_empty(), "LOG0 should have no topics");
}
#[test]
fn test_keyless_deploy_reads_existing_contract() {
let mut db = MemoryDatabase::default();
let return_value = 0x12345678_u32;
let getter_code = BytecodeBuilder::default()
.push_bytes(return_value.to_be_bytes())
.push_number(0_u8)
.append(MSTORE)
.push_number(0x20_u8) .push_number(0_u8)
.append(RETURN)
.build();
let getter_contract = address!("0000000000000000000000000000000000600000");
db.set_account_code(getter_contract, getter_code);
let init_code = BytecodeBuilder::default()
.push_number(0x20_u8) .push_number(0_u8) .push_number(0_u8) .push_number(0_u8) .push_address(getter_contract)
.append(GAS)
.append(STATICCALL)
.append(POP) .push_number(0_u8)
.append(MLOAD)
.push_number(0_u8) .append(SSTORE)
.push_number(1_u8) .push_number(0x2a_u8) .push_number(0_u8) .append(CODECOPY)
.push_number(1_u8) .push_number(0_u8) .append(RETURN)
.append(STOP) .build();
let (tx_bytes, signer) = create_pre_eip155_deploy_tx_with_value(init_code, U256::ZERO);
db.set_account_balance(signer, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"Expected success, got: {:?}",
result
);
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
let contract = state.get(&ret.deployedAddress).expect("contract should exist");
let code = contract.info.code.as_ref().expect("contract should have code");
assert!(!code.is_empty(), "contract should have non-empty code");
let stored = contract.storage.get(&U256::ZERO).expect("slot 0 should exist");
let expected = U256::from(return_value);
assert_eq!(stored.present_value, expected, "storage should have value from getter");
}
#[test]
fn test_keyless_deploy_twice_fails_second_time() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(2_000_000_000_000_000_000_000u128));
let result1 = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result1;
assert!(
matches!(result, ExecutionResult::Success { .. }),
"First deployment should succeed, got: {:?}",
result
);
let contract = state.get(&CREATE2_FACTORY_CONTRACT).expect("contract should exist");
let code = contract.info.code.as_ref().expect("contract should have code");
assert!(!code.is_empty(), "contract should have non-empty code");
assert_eq!(
contract.info.code_hash, CREATE2_FACTORY_CODE_HASH,
"contract code hash should match"
);
db.set_account_code(CREATE2_FACTORY_CONTRACT, code.original_bytes());
let result2 = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
assert_revert_with_error(&result2, KeylessDeployError::ContractAlreadyExists);
}
const DEFAULT_BENEFICIARY: Address = Address::ZERO;
#[test]
fn test_beneficiary_receives_fees_on_success() {
let mut db = MemoryDatabase::default();
db.set_account_balance(CREATE2_FACTORY_DEPLOYER, U256::from(1_000_000_000_000_000_000_000u128));
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
Bytes::from_static(CREATE2_FACTORY_TX),
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(matches!(result, ExecutionResult::Success { .. }));
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
let expected_fee = U256::from(ret.gasUsed) * U256::from(100_000_000_000u64);
let beneficiary_account =
state.get(&DEFAULT_BENEFICIARY).expect("beneficiary should exist in state");
assert_eq!(
beneficiary_account.info.balance, expected_fee,
"beneficiary should receive tx fees from keyless deploy sandbox"
);
}
#[test]
fn test_beneficiary_receives_fees_on_execution_failure() {
let mut db = MemoryDatabase::default();
let init_code = Bytes::from_static(&hex!("60006000fd"));
let (tx_bytes, signer) = create_pre_eip155_deploy_tx(init_code);
let initial_balance = U256::from(1_000_000_000_000_000_000_000u128);
db.set_account_balance(signer, initial_balance);
let result = call_keyless_deploy(
MegaSpecId::REX2,
&mut db,
tx_bytes,
LARGE_GAS_LIMIT_OVERRIDE,
U256::ZERO,
);
let ResultAndState { result, state } = result;
assert!(matches!(result, ExecutionResult::Success { .. }));
let output = result.output().unwrap();
let ret = IKeylessDeploy::keylessDeployCall::abi_decode_returns(output).unwrap();
assert!(!ret.errorData.is_empty(), "Should have ExecutionReverted error");
let expected_fee = U256::from(ret.gasUsed) * U256::from(100_000_000_000u64);
let beneficiary_account =
state.get(&DEFAULT_BENEFICIARY).expect("beneficiary should exist in state");
assert_eq!(
beneficiary_account.info.balance, expected_fee,
"beneficiary should receive fees even when sandbox execution fails"
);
}