use alloy_primitives::{Address, U256, address};
use crate::config::chains::EvmCall;
pub const WEIROLL_CONTRACT_ADDRESS: Address = address!("9585c3062Df1C247d5E373Cfca9167F7dC2b5963");
#[derive(Debug, Clone)]
pub struct WeirollCommand {
pub flags: u8,
pub value: u8,
pub gas: u16,
pub target: Address,
pub selector: [u8; 4],
pub in_out: [u8; 4],
}
impl WeirollCommand {
#[must_use]
pub fn pack(&self) -> [u8; 32] {
let mut word = [0u8; 32];
word[0] = self.flags;
word[1] = self.value;
word[2..4].copy_from_slice(&self.gas.to_be_bytes());
word[4..24].copy_from_slice(self.target.as_slice());
word[24..28].copy_from_slice(&self.selector);
word[28..32].copy_from_slice(&self.in_out);
word
}
#[must_use]
pub const fn flags_ref(&self) -> u8 {
self.flags
}
#[must_use]
pub const fn target_ref(&self) -> &Address {
&self.target
}
#[must_use]
pub const fn selector_ref(&self) -> &[u8; 4] {
&self.selector
}
}
#[derive(Debug, Clone)]
pub struct WeirollScript {
pub commands: Vec<[u8; 32]>,
pub state: Vec<Vec<u8>>,
}
impl WeirollScript {
#[must_use]
pub const fn command_count(&self) -> usize {
self.commands.len()
}
#[must_use]
pub const fn state_slot_count(&self) -> usize {
self.state.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.commands.is_empty()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum WeirollCommandFlags {
DelegateCall = 0x00,
Call = 0x01,
StaticCall = 0x02,
CallWithValue = 0x03,
}
impl WeirollCommandFlags {
pub const CALLTYPE_MASK: u8 = 0x03;
pub const EXTENDED_COMMAND: u8 = 0x40;
pub const TUPLE_RETURN: u8 = 0x80;
}
pub const WEIROLL_ADDRESS: &str = "0x9585c3062Df1C247d5E373Cfca9167F7dC2b5963";
#[derive(Debug, Clone)]
pub struct WeirollContractRef {
pub address: Address,
pub abi: Vec<u8>,
pub command_flags: WeirollCommandFlags,
}
#[must_use]
pub fn create_weiroll_contract(
address: Address,
abi: Vec<u8>,
command_flags: Option<WeirollCommandFlags>,
) -> WeirollContractRef {
WeirollContractRef {
address,
abi,
command_flags: command_flags.map_or(WeirollCommandFlags::Call, |v| v),
}
}
#[must_use]
pub const fn create_weiroll_library(address: Address, abi: Vec<u8>) -> WeirollContractRef {
WeirollContractRef { address, abi, command_flags: WeirollCommandFlags::DelegateCall }
}
#[must_use]
pub fn create_weiroll_delegate_call(
add_to_planner: impl FnOnce(&mut super::WeirollPlanner),
) -> EvmCall {
let mut planner = super::WeirollPlanner::new();
add_to_planner(&mut planner);
let script = planner.plan();
let calldata = abi_encode_execute(&script.commands, &script.state);
EvmCall { to: WEIROLL_CONTRACT_ADDRESS, data: calldata, value: U256::ZERO }
}
fn abi_encode_execute(commands: &[[u8; 32]], state: &[Vec<u8>]) -> Vec<u8> {
let selector: [u8; 4] = [0xde, 0x79, 0x2b, 0xe1];
let commands_offset: usize = 64;
let commands_data_len = 32 + commands.len() * 32; let state_offset: usize = commands_offset + commands_data_len;
let mut buf = Vec::with_capacity(4 + state_offset + 256);
buf.extend_from_slice(&selector);
buf.extend_from_slice(&pad_u256(commands_offset));
buf.extend_from_slice(&pad_u256(state_offset));
buf.extend_from_slice(&pad_u256(commands.len()));
for cmd in commands {
buf.extend_from_slice(cmd);
}
buf.extend_from_slice(&pad_u256(state.len()));
let data_area_start = state.len() * 32;
let mut current_offset = data_area_start;
for slot in state {
buf.extend_from_slice(&pad_u256(current_offset));
current_offset += 32 + slot.len().div_ceil(32) * 32;
}
for slot in state {
buf.extend_from_slice(&pad_u256(slot.len()));
buf.extend_from_slice(slot);
let padding = (32 - (slot.len() % 32)) % 32;
buf.extend(std::iter::repeat_n(0u8, padding));
}
buf
}
fn pad_u256(value: usize) -> [u8; 32] {
let mut word = [0u8; 32];
word[24..32].copy_from_slice(&(value as u64).to_be_bytes());
word
}
#[must_use]
pub fn define_read_only<T>(mut object: T, setter: impl FnOnce(&mut T)) -> T {
setter(&mut object);
object
}
#[must_use]
pub fn get_static<T: Clone>(entries: &[(&str, T)], key: &str) -> Option<T> {
entries.iter().find(|(k, _)| *k == key).map(|(_, v)| v.clone())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pack_encodes_all_fields() {
let cmd = WeirollCommand {
flags: 0x01,
value: 0xFF,
gas: 21_000,
target: Address::ZERO,
selector: [0xde, 0xad, 0xbe, 0xef],
in_out: [0x01, 0x02, 0x03, 0x04],
};
let packed = cmd.pack();
assert_eq!(packed.len(), 32);
assert_eq!(packed[0], 0x01);
assert_eq!(packed[1], 0xFF);
assert_eq!(&packed[2..4], &21_000u16.to_be_bytes());
assert_eq!(&packed[4..24], Address::ZERO.as_slice());
assert_eq!(&packed[24..28], &[0xde, 0xad, 0xbe, 0xef]);
assert_eq!(&packed[28..32], &[0x01, 0x02, 0x03, 0x04]);
}
#[test]
fn flags_ref_returns_flags() {
let cmd = WeirollCommand {
flags: 0x42,
value: 0,
gas: 0,
target: Address::ZERO,
selector: [0; 4],
in_out: [0; 4],
};
assert_eq!(cmd.flags_ref(), 0x42);
}
#[test]
fn target_ref_returns_address() {
let addr: Address = "0x1111111111111111111111111111111111111111".parse().unwrap();
let cmd = WeirollCommand {
flags: 0,
value: 0,
gas: 0,
target: addr,
selector: [0; 4],
in_out: [0; 4],
};
assert_eq!(*cmd.target_ref(), addr);
}
#[test]
fn selector_ref_returns_selector() {
let cmd = WeirollCommand {
flags: 0,
value: 0,
gas: 0,
target: Address::ZERO,
selector: [0xaa, 0xbb, 0xcc, 0xdd],
in_out: [0; 4],
};
assert_eq!(*cmd.selector_ref(), [0xaa, 0xbb, 0xcc, 0xdd]);
}
#[test]
fn empty_script() {
let script = WeirollScript { commands: vec![], state: vec![] };
assert!(script.is_empty());
assert_eq!(script.command_count(), 0);
assert_eq!(script.state_slot_count(), 0);
}
#[test]
fn non_empty_script() {
let script =
WeirollScript { commands: vec![[0u8; 32], [1u8; 32]], state: vec![vec![0xab]] };
assert!(!script.is_empty());
assert_eq!(script.command_count(), 2);
assert_eq!(script.state_slot_count(), 1);
}
#[test]
fn command_flags_values() {
assert_eq!(WeirollCommandFlags::DelegateCall as u8, 0x00);
assert_eq!(WeirollCommandFlags::Call as u8, 0x01);
assert_eq!(WeirollCommandFlags::StaticCall as u8, 0x02);
assert_eq!(WeirollCommandFlags::CallWithValue as u8, 0x03);
}
#[test]
fn calltype_mask_isolates_call_type() {
assert_eq!(
WeirollCommandFlags::Call as u8 & WeirollCommandFlags::CALLTYPE_MASK,
WeirollCommandFlags::Call as u8
);
}
#[test]
fn extended_command_and_tuple_return_bits() {
assert_eq!(WeirollCommandFlags::EXTENDED_COMMAND, 0x40);
assert_eq!(WeirollCommandFlags::TUPLE_RETURN, 0x80);
}
#[test]
fn create_contract_default_flags() {
let c = create_weiroll_contract(Address::ZERO, vec![], None);
assert_eq!(c.command_flags, WeirollCommandFlags::Call);
}
#[test]
fn create_contract_custom_flags() {
let c =
create_weiroll_contract(Address::ZERO, vec![], Some(WeirollCommandFlags::StaticCall));
assert_eq!(c.command_flags, WeirollCommandFlags::StaticCall);
}
#[test]
fn create_library_uses_delegatecall() {
let lib = create_weiroll_library(Address::ZERO, vec![]);
assert_eq!(lib.command_flags, WeirollCommandFlags::DelegateCall);
}
#[test]
fn delegate_call_produces_valid_evm_call() {
let evm_call = create_weiroll_delegate_call(|planner| {
planner.add_command(WeirollCommand {
flags: 0,
value: 0,
gas: 0,
target: Address::ZERO,
selector: [0; 4],
in_out: [0; 4],
});
});
assert_eq!(evm_call.to, WEIROLL_CONTRACT_ADDRESS);
assert_eq!(evm_call.value, U256::ZERO);
assert_eq!(&evm_call.data[..4], &[0xde, 0x79, 0x2b, 0xe1]);
}
#[test]
fn delegate_call_empty_planner() {
let evm_call = create_weiroll_delegate_call(|_| {});
assert_eq!(evm_call.to, WEIROLL_CONTRACT_ADDRESS);
assert!(!evm_call.data.is_empty());
}
#[test]
fn define_read_only_applies_mutation() {
let val = define_read_only(42u32, |v| *v = 100);
assert_eq!(val, 100);
}
#[test]
fn define_read_only_with_struct() {
#[derive(Default)]
struct S {
x: i32,
}
let s = define_read_only(S::default(), |s| s.x = 7);
assert_eq!(s.x, 7);
}
#[test]
fn get_static_found() {
let entries: &[(&str, i32)] = &[("a", 1), ("b", 2)];
assert_eq!(get_static(entries, "b"), Some(2));
}
#[test]
fn get_static_not_found() {
let entries: &[(&str, i32)] = &[("a", 1)];
assert_eq!(get_static(entries, "z"), None);
}
#[test]
fn get_static_empty_entries() {
let entries: &[(&str, i32)] = &[];
assert_eq!(get_static(entries, "a"), None);
}
#[test]
fn weiroll_address_matches_constant() {
let parsed: Address = WEIROLL_ADDRESS.parse().unwrap();
assert_eq!(parsed, WEIROLL_CONTRACT_ADDRESS);
}
}