use super::abi::selector;
use crate::encoding::bytes_to_hex;
const WORD: usize = 32;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FacetCut {
pub facet: [u8; 20],
pub action: u8,
pub selectors: Vec<[u8; 4]>,
}
fn word_usize(value: usize) -> [u8; 32] {
let mut out = [0u8; 32];
out[24..].copy_from_slice(&(value as u64).to_be_bytes());
out
}
fn word_address(addr: &[u8; 20]) -> [u8; 32] {
let mut out = [0u8; 32];
out[12..].copy_from_slice(addr);
out
}
fn encode_facet_cut(cut: &FacetCut) -> Vec<u8> {
let mut buf = Vec::with_capacity(WORD * (3 + 1 + cut.selectors.len()));
buf.extend_from_slice(&word_address(&cut.facet));
buf.extend_from_slice(&word_usize(cut.action as usize));
buf.extend_from_slice(&word_usize(3 * WORD)); buf.extend_from_slice(&word_usize(cut.selectors.len()));
for sel in &cut.selectors {
let mut w = [0u8; 32];
w[..4].copy_from_slice(sel); buf.extend_from_slice(&w);
}
buf
}
fn encode_facet_cut_array(cuts: &[FacetCut]) -> Vec<u8> {
let elems: Vec<Vec<u8>> = cuts.iter().map(encode_facet_cut).collect();
let table_bytes = cuts.len() * WORD;
let mut offsets = Vec::with_capacity(cuts.len());
let mut running = table_bytes;
for e in &elems {
offsets.push(running);
running += e.len();
}
let mut buf = Vec::with_capacity(WORD + table_bytes + running);
buf.extend_from_slice(&word_usize(cuts.len())); for off in offsets {
buf.extend_from_slice(&word_usize(off));
}
for e in elems {
buf.extend_from_slice(&e);
}
buf
}
fn encode_bytes(data: &[u8]) -> Vec<u8> {
let padded = data.len().div_ceil(WORD) * WORD;
let mut buf = Vec::with_capacity(WORD + padded);
buf.extend_from_slice(&word_usize(data.len()));
buf.extend_from_slice(data);
buf.resize(WORD + padded, 0);
buf
}
pub fn encode_diamond_cut(cuts: &[FacetCut], init: &[u8; 20], init_calldata: &[u8]) -> Vec<u8> {
let sel = selector("diamondCut((address,uint8,bytes4[])[],address,bytes)");
let cuts_blob = encode_facet_cut_array(cuts);
let calldata_blob = encode_bytes(init_calldata);
const HEAD: usize = 3 * WORD; let cuts_offset = HEAD;
let calldata_offset = HEAD + cuts_blob.len();
let mut buf = Vec::with_capacity(4 + HEAD + cuts_blob.len() + calldata_blob.len());
buf.extend_from_slice(&sel);
buf.extend_from_slice(&word_usize(cuts_offset)); buf.extend_from_slice(&word_address(init)); buf.extend_from_slice(&word_usize(calldata_offset)); buf.extend_from_slice(&cuts_blob);
buf.extend_from_slice(&calldata_blob);
buf
}
pub fn encode_diamond_cut_hex(cuts: &[FacetCut], init: &[u8; 20], init_calldata: &[u8]) -> String {
format!("0x{}", bytes_to_hex(&encode_diamond_cut(cuts, init, init_calldata)))
}
pub fn encode_diamond_constructor_args(owner: &[u8; 20], cuts: &[FacetCut]) -> Vec<u8> {
let cuts_blob = encode_facet_cut_array(cuts);
const HEAD: usize = 2 * WORD; let mut buf = Vec::with_capacity(HEAD + cuts_blob.len());
buf.extend_from_slice(&word_address(owner)); buf.extend_from_slice(&word_usize(HEAD)); buf.extend_from_slice(&cuts_blob);
buf
}
pub fn encode_diamond_constructor_args_hex(owner: &[u8; 20], cuts: &[FacetCut]) -> String {
format!("0x{}", bytes_to_hex(&encode_diamond_constructor_args(owner, cuts)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn selector_matches_cast() {
let sel = selector("diamondCut((address,uint8,bytes4[])[],address,bytes)");
assert_eq!(bytes_to_hex(&sel), "1f931c1c");
}
#[test]
fn golden_add_two_selectors() {
let expected = concat!(
"0x1f931c1c",
"0000000000000000000000000000000000000000000000000000000000000060",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000160",
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000000000000000000000000000000000000000000000000020",
"0000000000000000000000001111111111111111111111111111111111111111",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000060",
"0000000000000000000000000000000000000000000000000000000000000002",
"aabbccdd00000000000000000000000000000000000000000000000000000000",
"1122334400000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
);
let cut = FacetCut {
facet: [0x11; 20],
action: 0,
selectors: vec![[0xaa, 0xbb, 0xcc, 0xdd], [0x11, 0x22, 0x33, 0x44]],
};
let got = encode_diamond_cut_hex(&[cut], &[0u8; 20], &[]);
assert_eq!(got, expected);
}
#[test]
fn golden_remove_nonzero_init_short_calldata() {
let expected = concat!(
"0x1f931c1c",
"0000000000000000000000000000000000000000000000000000000000000060",
"000000000000000000000000000000000000000000000000000000000000beef",
"0000000000000000000000000000000000000000000000000000000000000140",
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000000000000000000000000000000000000000000000000020",
"0000000000000000000000002222222222222222222222222222222222222222",
"0000000000000000000000000000000000000000000000000000000000000002",
"0000000000000000000000000000000000000000000000000000000000000060",
"0000000000000000000000000000000000000000000000000000000000000001",
"deadbeef00000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000002",
"cafe000000000000000000000000000000000000000000000000000000000000",
);
let mut init = [0u8; 20];
init[18] = 0xbe;
init[19] = 0xef;
let cut = FacetCut {
facet: [0x22; 20],
action: 2,
selectors: vec![[0xde, 0xad, 0xbe, 0xef]],
};
let got = encode_diamond_cut_hex(&[cut], &init, &[0xca, 0xfe]);
assert_eq!(got, expected);
}
#[test]
fn raw_and_hex_agree_and_word_aligned() {
let cut = FacetCut {
facet: [0x11; 20],
action: 0,
selectors: vec![[0xaa, 0xbb, 0xcc, 0xdd]],
};
let raw = encode_diamond_cut(std::slice::from_ref(&cut), &[0u8; 20], &[]);
let hex = encode_diamond_cut_hex(&[cut], &[0u8; 20], &[]);
assert_eq!(hex, format!("0x{}", bytes_to_hex(&raw)));
assert_eq!((raw.len() - 4) % WORD, 0);
}
#[test]
fn empty_cuts_list() {
let got = encode_diamond_cut_hex(&[], &[0u8; 20], &[]);
let expected = concat!(
"0x1f931c1c",
"0000000000000000000000000000000000000000000000000000000000000060",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000080",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000",
);
assert_eq!(got, expected);
}
#[test]
fn golden_constructor_args_one_cut() {
let expected = concat!(
"0x",
"0000000000000000000000001111111111111111111111111111111111111111",
"0000000000000000000000000000000000000000000000000000000000000040",
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000000000000000000000000000000000000000000000000020",
"0000000000000000000000002222222222222222222222222222222222222222",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000060",
"0000000000000000000000000000000000000000000000000000000000000002",
"aabbccdd00000000000000000000000000000000000000000000000000000000",
"1122334400000000000000000000000000000000000000000000000000000000",
);
let cut = FacetCut {
facet: [0x22; 20],
action: 0,
selectors: vec![[0xaa, 0xbb, 0xcc, 0xdd], [0x11, 0x22, 0x33, 0x44]],
};
let got = encode_diamond_constructor_args_hex(&[0x11; 20], std::slice::from_ref(&cut));
assert_eq!(got, expected);
let raw = encode_diamond_constructor_args(&[0x11; 20], std::slice::from_ref(&cut));
assert_eq!(got, format!("0x{}", bytes_to_hex(&raw)));
assert_eq!(raw.len() % WORD, 0);
}
#[test]
fn golden_constructor_args_two_cuts_remove() {
let expected = concat!(
"0x",
"0000000000000000000000003333333333333333333333333333333333333333",
"0000000000000000000000000000000000000000000000000000000000000040",
"0000000000000000000000000000000000000000000000000000000000000002",
"0000000000000000000000000000000000000000000000000000000000000040",
"00000000000000000000000000000000000000000000000000000000000000e0",
"0000000000000000000000004444444444444444444444444444444444444444",
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000060",
"0000000000000000000000000000000000000000000000000000000000000001",
"deadbeef00000000000000000000000000000000000000000000000000000000",
"0000000000000000000000005555555555555555555555555555555555555555",
"0000000000000000000000000000000000000000000000000000000000000002",
"0000000000000000000000000000000000000000000000000000000000000060",
"0000000000000000000000000000000000000000000000000000000000000002",
"cafebabe00000000000000000000000000000000000000000000000000000000",
"feedface00000000000000000000000000000000000000000000000000000000",
);
let cuts = [
FacetCut {
facet: [0x44; 20],
action: 0,
selectors: vec![[0xde, 0xad, 0xbe, 0xef]],
},
FacetCut {
facet: [0x55; 20],
action: 2,
selectors: vec![[0xca, 0xfe, 0xba, 0xbe], [0xfe, 0xed, 0xfa, 0xce]],
},
];
let got = encode_diamond_constructor_args_hex(&[0x33; 20], &cuts);
assert_eq!(got, expected);
}
}