use crate::{
word_to_u256, Address, CollateralParams, Market, MarketSnapshot, Offer, Position, Sig,
SimMarket, Word, U256,
};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum DecodeError {
#[error("input too short: needed {needed} bytes, have {have}")]
TooShort {
needed: usize,
have: usize,
},
#[error("bad offset: {0}")]
BadOffset(usize),
#[error("bad length: {0}")]
BadLength(usize),
#[error("integer overflow: value does not fit its target type")]
IntegerOverflow,
#[error("invalid bool encoding")]
InvalidBool,
#[error("unexpected function selector: {0:02x?}")]
BadSelector([u8; 4]),
}
fn word_at(bytes: &[u8], off: usize) -> Result<Word, DecodeError> {
let end = off.checked_add(32).ok_or(DecodeError::BadOffset(off))?;
if end > bytes.len() {
return Err(DecodeError::TooShort {
needed: end,
have: bytes.len(),
});
}
let mut w = [0u8; 32];
w.copy_from_slice(&bytes[off..end]);
Ok(w)
}
fn head_word(bytes: &[u8], base: usize, k: usize) -> Result<Word, DecodeError> {
let rel = k.checked_mul(32).ok_or(DecodeError::BadOffset(k))?;
let off = base.checked_add(rel).ok_or(DecodeError::BadOffset(base))?;
word_at(bytes, off)
}
fn word_to_usize(w: &Word) -> Result<usize, DecodeError> {
if w[..24].iter().any(|&b| b != 0) {
return Err(DecodeError::IntegerOverflow);
}
let mut b = [0u8; 8];
b.copy_from_slice(&w[24..]);
usize::try_from(u64::from_be_bytes(b)).map_err(|_| DecodeError::IntegerOverflow)
}
fn word_to_address(w: &Word) -> Address {
let mut a = [0u8; 20];
a.copy_from_slice(&w[12..]);
a
}
fn word_to_bool(w: &Word) -> Result<bool, DecodeError> {
if w[..31].iter().any(|&b| b != 0) {
return Err(DecodeError::InvalidBool);
}
match w[31] {
0 => Ok(false),
1 => Ok(true),
_ => Err(DecodeError::InvalidBool),
}
}
fn word_to_u128(w: &Word) -> Result<u128, DecodeError> {
crate::word_to_u128(w).ok_or(DecodeError::IntegerOverflow)
}
fn word_to_small(w: &Word, n: usize) -> Result<u64, DecodeError> {
if w[..32 - n].iter().any(|&b| b != 0) {
return Err(DecodeError::IntegerOverflow);
}
let mut b = [0u8; 8];
b[8 - n..].copy_from_slice(&w[32 - n..]);
Ok(u64::from_be_bytes(b))
}
fn resolve(base: usize, offset: usize) -> Result<usize, DecodeError> {
base.checked_add(offset)
.ok_or(DecodeError::BadOffset(offset))
}
pub fn decode_offer(bytes: &[u8]) -> Result<Offer, DecodeError> {
let tuple_off = word_to_usize(&word_at(bytes, 0)?)?;
decode_offer_tuple(bytes, tuple_off)
}
fn decode_offer_tuple(bytes: &[u8], base: usize) -> Result<Offer, DecodeError> {
let market_off = word_to_usize(&head_word(bytes, base, 0)?)?;
let buy = word_to_bool(&head_word(bytes, base, 1)?)?;
let maker = word_to_address(&head_word(bytes, base, 2)?);
let start = head_word(bytes, base, 3)?;
let expiry = head_word(bytes, base, 4)?;
let tick = head_word(bytes, base, 5)?;
let group = head_word(bytes, base, 6)?;
let callback = word_to_address(&head_word(bytes, base, 7)?);
let callback_data_off = word_to_usize(&head_word(bytes, base, 8)?)?;
let receiver_if_maker_is_seller = word_to_address(&head_word(bytes, base, 9)?);
let ratifier = word_to_address(&head_word(bytes, base, 10)?);
let reduce_only = word_to_bool(&head_word(bytes, base, 11)?)?;
let max_units = word_to_u128(&head_word(bytes, base, 12)?)?;
let max_assets = word_to_u128(&head_word(bytes, base, 13)?)?;
let continuous_fee_cap = head_word(bytes, base, 14)?;
let market = decode_market_tuple(bytes, resolve(base, market_off)?)?;
let callback_data = decode_bytes(bytes, resolve(base, callback_data_off)?)?;
Ok(Offer {
market,
buy,
maker,
start,
expiry,
tick,
group,
callback,
callback_data,
receiver_if_maker_is_seller,
ratifier,
reduce_only,
max_units,
max_assets,
continuous_fee_cap,
})
}
fn decode_market_tuple(bytes: &[u8], base: usize) -> Result<Market, DecodeError> {
let chain_id = head_word(bytes, base, 0)?;
let midnight = word_to_address(&head_word(bytes, base, 1)?);
let loan_token = word_to_address(&head_word(bytes, base, 2)?);
let cp_off = word_to_usize(&head_word(bytes, base, 3)?)?;
let maturity = head_word(bytes, base, 4)?;
let rcf_threshold = head_word(bytes, base, 5)?;
let enter_gate = word_to_address(&head_word(bytes, base, 6)?);
let liquidator_gate = word_to_address(&head_word(bytes, base, 7)?);
let collateral_params = decode_collateral_params(bytes, resolve(base, cp_off)?)?;
Ok(Market {
chain_id,
midnight,
loan_token,
collateral_params,
maturity,
rcf_threshold,
enter_gate,
liquidator_gate,
})
}
fn decode_collateral_params(
bytes: &[u8],
base: usize,
) -> Result<Vec<CollateralParams>, DecodeError> {
let len = word_to_usize(&word_at(bytes, base)?)?;
let elems_base = resolve(base, 32)?;
let span = len.checked_mul(128).ok_or(DecodeError::BadLength(len))?;
let end = elems_base
.checked_add(span)
.ok_or(DecodeError::BadLength(len))?;
if end > bytes.len() {
return Err(DecodeError::TooShort {
needed: end,
have: bytes.len(),
});
}
let mut out = Vec::with_capacity(len);
for i in 0..len {
let cp_base = resolve(
elems_base,
i.checked_mul(128).ok_or(DecodeError::BadLength(len))?,
)?;
out.push(CollateralParams {
token: word_to_address(&head_word(bytes, cp_base, 0)?),
lltv: head_word(bytes, cp_base, 1)?,
liquidation_cursor: head_word(bytes, cp_base, 2)?,
oracle: word_to_address(&head_word(bytes, cp_base, 3)?),
});
}
Ok(out)
}
fn decode_bytes(bytes: &[u8], base: usize) -> Result<Vec<u8>, DecodeError> {
let len = word_to_usize(&word_at(bytes, base)?)?;
let start = resolve(base, 32)?;
let end = start.checked_add(len).ok_or(DecodeError::BadLength(len))?;
if end > bytes.len() {
return Err(DecodeError::TooShort {
needed: end,
have: bytes.len(),
});
}
Ok(bytes[start..end].to_vec())
}
fn decode_word_array(bytes: &[u8], base: usize) -> Result<Vec<Word>, DecodeError> {
let len = word_to_usize(&word_at(bytes, base)?)?;
let elems_base = resolve(base, 32)?;
let span = len.checked_mul(32).ok_or(DecodeError::BadLength(len))?;
let end = elems_base
.checked_add(span)
.ok_or(DecodeError::BadLength(len))?;
if end > bytes.len() {
return Err(DecodeError::TooShort {
needed: end,
have: bytes.len(),
});
}
let mut out = Vec::with_capacity(len);
for i in 0..len {
let off = resolve(
elems_base,
i.checked_mul(32).ok_or(DecodeError::BadLength(len))?,
)?;
out.push(word_at(bytes, off)?);
}
Ok(out)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MarketStateView {
pub total_units: u128,
pub loss_factor: u128,
pub withdrawable: u128,
pub continuous_fee_credit: u128,
pub settlement_fee_cbp: [u16; 7],
pub continuous_fee: u32,
pub tick_spacing: u8,
}
impl MarketStateView {
fn loss_factor_maxed(&self) -> bool {
self.loss_factor == u128::MAX
}
pub fn to_sim_market(&self) -> SimMarket {
SimMarket {
tick_spacing: self.tick_spacing,
continuous_fee: self.continuous_fee as u128,
settlement_fee_cbp: self.settlement_fee_cbp,
loss_factor_maxed: self.loss_factor_maxed(),
}
}
pub fn to_market_snapshot(&self) -> MarketSnapshot {
MarketSnapshot {
tick_spacing: self.tick_spacing,
loss_factor_maxed: self.loss_factor_maxed(),
continuous_fee: self.continuous_fee as u128,
}
}
}
pub fn decode_market_state(bytes: &[u8]) -> Result<MarketStateView, DecodeError> {
let total_units = word_to_u128(&head_word(bytes, 0, 0)?)?;
let loss_factor = word_to_u128(&head_word(bytes, 0, 1)?)?;
let withdrawable = word_to_u128(&head_word(bytes, 0, 2)?)?;
let continuous_fee_credit = word_to_u128(&head_word(bytes, 0, 3)?)?;
let mut settlement_fee_cbp = [0u16; 7];
for (i, slot) in settlement_fee_cbp.iter_mut().enumerate() {
*slot = word_to_small(&head_word(bytes, 0, 4 + i)?, 2)? as u16;
}
let continuous_fee = word_to_small(&head_word(bytes, 0, 11)?, 4)? as u32;
let tick_spacing = word_to_small(&head_word(bytes, 0, 12)?, 1)? as u8;
Ok(MarketStateView {
total_units,
loss_factor,
withdrawable,
continuous_fee_credit,
settlement_fee_cbp,
continuous_fee,
tick_spacing,
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PositionView {
pub credit: u128,
pub pending_fee: u128,
pub last_loss_factor: u128,
pub last_accrual: u128,
pub debt: u128,
pub collateral_bitmap: u128,
}
impl PositionView {
pub fn to_sim_position(&self) -> Position {
Position {
credit: self.credit,
debt: self.debt,
pending_fee: self.pending_fee,
}
}
}
pub fn decode_position(bytes: &[u8]) -> Result<PositionView, DecodeError> {
Ok(PositionView {
credit: word_to_u128(&head_word(bytes, 0, 0)?)?,
pending_fee: word_to_u128(&head_word(bytes, 0, 1)?)?,
last_loss_factor: word_to_u128(&head_word(bytes, 0, 2)?)?,
last_accrual: word_to_u128(&head_word(bytes, 0, 3)?)?,
debt: word_to_u128(&head_word(bytes, 0, 4)?)?,
collateral_bitmap: word_to_u128(&head_word(bytes, 0, 5)?)?,
})
}
pub fn decode_consumed(bytes: &[u8]) -> Result<u128, DecodeError> {
word_to_u128(&word_at(bytes, 0)?)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RatifierData {
pub sig: Sig,
pub root: Word,
pub leaf_index: usize,
pub proof: Vec<Word>,
}
pub fn decode_ratifier_data(bytes: &[u8]) -> Result<RatifierData, DecodeError> {
let v = word_to_small(&word_at(bytes, 0)?, 1)? as u8;
let r = word_at(bytes, 32)?;
let s = word_at(bytes, 64)?;
let root = word_at(bytes, 96)?;
let leaf_index = word_to_usize(&word_at(bytes, 128)?)?;
let proof_off = word_to_usize(&word_at(bytes, 160)?)?;
let proof = decode_word_array(bytes, proof_off)?;
Ok(RatifierData {
sig: Sig { r, s, v },
root,
leaf_index,
proof,
})
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SetterRatifierData {
pub root: Word,
pub leaf_index: usize,
pub proof: Vec<Word>,
}
pub fn decode_setter_ratifier_data(bytes: &[u8]) -> Result<SetterRatifierData, DecodeError> {
let root = word_at(bytes, 0)?;
let leaf_index = word_to_usize(&word_at(bytes, 32)?)?;
let proof_off = word_to_usize(&word_at(bytes, 64)?)?;
let proof = decode_word_array(bytes, proof_off)?;
Ok(SetterRatifierData {
root,
leaf_index,
proof,
})
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RatifierPayload {
Ecrecover(RatifierData),
Setter(SetterRatifierData),
}
impl RatifierPayload {
pub fn root(&self) -> &Word {
match self {
Self::Ecrecover(rd) => &rd.root,
Self::Setter(rd) => &rd.root,
}
}
pub fn leaf_index(&self) -> usize {
match self {
Self::Ecrecover(rd) => rd.leaf_index,
Self::Setter(rd) => rd.leaf_index,
}
}
pub fn proof(&self) -> &[Word] {
match self {
Self::Ecrecover(rd) => &rd.proof,
Self::Setter(rd) => &rd.proof,
}
}
pub fn sig(&self) -> Option<&Sig> {
match self {
Self::Ecrecover(rd) => Some(&rd.sig),
Self::Setter(_) => None,
}
}
}
pub fn decode_any_ratifier_data(bytes: &[u8]) -> Result<RatifierPayload, DecodeError> {
let first = word_at(bytes, 0)?;
if first[..31].iter().all(|&b| b == 0) {
decode_ratifier_data(bytes).map(RatifierPayload::Ecrecover)
} else {
decode_setter_ratifier_data(bytes).map(RatifierPayload::Setter)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TakeCall {
pub offer: Offer,
pub ratifier_data_raw: Vec<u8>,
pub ratifier_data: RatifierPayload,
pub units: U256,
pub taker: Address,
pub receiver_if_taker_is_seller: Address,
pub taker_callback: Address,
pub taker_callback_data: Vec<u8>,
}
pub fn decode_take_calldata(bytes: &[u8]) -> Result<TakeCall, DecodeError> {
let args = strip_selector(bytes, crate::TAKE_SELECTOR)?;
let offer_off = word_to_usize(&head_word(args, 0, 0)?)?;
let ratifier_off = word_to_usize(&head_word(args, 0, 1)?)?;
let units = word_to_u256(&head_word(args, 0, 2)?);
let taker = word_to_address(&head_word(args, 0, 3)?);
let receiver_if_taker_is_seller = word_to_address(&head_word(args, 0, 4)?);
let taker_callback = word_to_address(&head_word(args, 0, 5)?);
let callback_data_off = word_to_usize(&head_word(args, 0, 6)?)?;
let offer = decode_offer_tuple(args, offer_off)?;
let ratifier_data_raw = decode_bytes(args, ratifier_off)?;
let ratifier_data = decode_any_ratifier_data(&ratifier_data_raw)?;
let taker_callback_data = decode_bytes(args, callback_data_off)?;
Ok(TakeCall {
offer,
ratifier_data_raw,
ratifier_data,
units,
taker,
receiver_if_taker_is_seller,
taker_callback,
taker_callback_data,
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BundleKind {
BuyWithUnitsTarget,
SellWithUnitsTarget,
BuyWithAssetsTarget,
SellWithAssetsTarget,
}
impl BundleKind {
pub fn from_selector(sel: [u8; 4]) -> Option<Self> {
match sel {
crate::BUNDLE_BUY_UNITS_SELECTOR => Some(Self::BuyWithUnitsTarget),
crate::BUNDLE_SELL_UNITS_SELECTOR => Some(Self::SellWithUnitsTarget),
crate::BUNDLE_BUY_ASSETS_SELECTOR => Some(Self::BuyWithAssetsTarget),
crate::BUNDLE_SELL_ASSETS_SELECTOR => Some(Self::SellWithAssetsTarget),
_ => None,
}
}
pub fn selector(&self) -> [u8; 4] {
match self {
Self::BuyWithUnitsTarget => crate::BUNDLE_BUY_UNITS_SELECTOR,
Self::SellWithUnitsTarget => crate::BUNDLE_SELL_UNITS_SELECTOR,
Self::BuyWithAssetsTarget => crate::BUNDLE_BUY_ASSETS_SELECTOR,
Self::SellWithAssetsTarget => crate::BUNDLE_SELL_ASSETS_SELECTOR,
}
}
pub fn function_name(&self) -> &'static str {
match self {
Self::BuyWithUnitsTarget => "midnightBundlesV1BuyWithUnitsTargetAndWithdrawCollateral",
Self::SellWithUnitsTarget => "midnightBundlesV1SupplyCollateralAndSellWithUnitsTarget",
Self::BuyWithAssetsTarget => {
"midnightBundlesV1BuyWithAssetsTargetAndWithdrawCollateral"
}
Self::SellWithAssetsTarget => {
"midnightBundlesV1SupplyCollateralAndSellWithAssetsTarget"
}
}
}
pub fn target_label(&self) -> &'static str {
match self {
Self::BuyWithUnitsTarget | Self::SellWithUnitsTarget => "target units",
Self::BuyWithAssetsTarget => "target buyer assets",
Self::SellWithAssetsTarget => "target seller assets",
}
}
pub fn limit_label(&self) -> &'static str {
match self {
Self::BuyWithUnitsTarget => "max buyer assets",
Self::SellWithUnitsTarget => "min seller assets",
Self::BuyWithAssetsTarget => "min units",
Self::SellWithAssetsTarget => "max units",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TokenPermit {
pub kind: u8,
pub data: Vec<u8>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CollateralWithdrawal {
pub collateral_index: U256,
pub assets: U256,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CollateralSupply {
pub collateral_index: U256,
pub assets: U256,
pub permit: TokenPermit,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OfferFill {
pub offer: Offer,
pub ratifier_data_raw: Vec<u8>,
pub ratifier_data: RatifierPayload,
pub units: U256,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BundleSide {
Buy {
loan_token_permit: TokenPermit,
collateral_withdrawals: Vec<CollateralWithdrawal>,
collateral_receiver: Address,
},
Sell {
receiver: Address,
collateral_supplies: Vec<CollateralSupply>,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BundleCall {
pub kind: BundleKind,
pub target: U256,
pub limit: U256,
pub taker: Address,
pub reduce_only: bool,
pub side: BundleSide,
pub fills: Vec<OfferFill>,
pub referral_fee_pct: U256,
pub referral_fee_recipient: Address,
pub max_continuous_fee: U256,
pub deadline: U256,
}
pub fn decode_bundle_calldata(bytes: &[u8]) -> Result<BundleCall, DecodeError> {
if bytes.len() < 4 {
return Err(DecodeError::TooShort {
needed: 4,
have: bytes.len(),
});
}
let sel = [bytes[0], bytes[1], bytes[2], bytes[3]];
let kind = BundleKind::from_selector(sel).ok_or(DecodeError::BadSelector(sel))?;
let args = &bytes[4..];
let target = word_to_u256(&head_word(args, 0, 0)?);
let limit = word_to_u256(&head_word(args, 0, 1)?);
let taker = word_to_address(&head_word(args, 0, 2)?);
let reduce_only = word_to_bool(&head_word(args, 0, 3)?)?;
let (side, fills, tail_slot) = match kind {
BundleKind::BuyWithUnitsTarget | BundleKind::BuyWithAssetsTarget => {
let permit_off = word_to_usize(&head_word(args, 0, 4)?)?;
let fills_off = word_to_usize(&head_word(args, 0, 5)?)?;
let withdrawals_off = word_to_usize(&head_word(args, 0, 6)?)?;
let collateral_receiver = word_to_address(&head_word(args, 0, 7)?);
let side = BundleSide::Buy {
loan_token_permit: decode_token_permit(args, permit_off)?,
collateral_withdrawals: decode_collateral_withdrawals(args, withdrawals_off)?,
collateral_receiver,
};
(side, decode_offer_fills(args, fills_off)?, 8)
}
BundleKind::SellWithUnitsTarget | BundleKind::SellWithAssetsTarget => {
let receiver = word_to_address(&head_word(args, 0, 4)?);
let supplies_off = word_to_usize(&head_word(args, 0, 5)?)?;
let fills_off = word_to_usize(&head_word(args, 0, 6)?)?;
let side = BundleSide::Sell {
receiver,
collateral_supplies: decode_collateral_supplies(args, supplies_off)?,
};
(side, decode_offer_fills(args, fills_off)?, 7)
}
};
Ok(BundleCall {
kind,
target,
limit,
taker,
reduce_only,
side,
fills,
referral_fee_pct: word_to_u256(&head_word(args, 0, tail_slot)?),
referral_fee_recipient: word_to_address(&head_word(args, 0, tail_slot + 1)?),
max_continuous_fee: word_to_u256(&head_word(args, 0, tail_slot + 2)?),
deadline: word_to_u256(&head_word(args, 0, tail_slot + 3)?),
})
}
fn decode_token_permit(bytes: &[u8], base: usize) -> Result<TokenPermit, DecodeError> {
let kind = word_to_small(&head_word(bytes, base, 0)?, 1)? as u8;
let data_off = word_to_usize(&head_word(bytes, base, 1)?)?;
let data = decode_bytes(bytes, resolve(base, data_off)?)?;
Ok(TokenPermit { kind, data })
}
fn decode_collateral_withdrawals(
bytes: &[u8],
base: usize,
) -> Result<Vec<CollateralWithdrawal>, DecodeError> {
let len = word_to_usize(&word_at(bytes, base)?)?;
let elems_base = resolve(base, 32)?;
let span = len.checked_mul(64).ok_or(DecodeError::BadLength(len))?;
let end = elems_base
.checked_add(span)
.ok_or(DecodeError::BadLength(len))?;
if end > bytes.len() {
return Err(DecodeError::TooShort {
needed: end,
have: bytes.len(),
});
}
let mut out = Vec::with_capacity(len);
for i in 0..len {
let e_base = resolve(elems_base, i * 64)?;
out.push(CollateralWithdrawal {
collateral_index: word_to_u256(&head_word(bytes, e_base, 0)?),
assets: word_to_u256(&head_word(bytes, e_base, 1)?),
});
}
Ok(out)
}
fn decode_collateral_supplies(
bytes: &[u8],
base: usize,
) -> Result<Vec<CollateralSupply>, DecodeError> {
let (len, elems_base) = dynamic_array_head(bytes, base)?;
let mut out = Vec::with_capacity(len);
for i in 0..len {
let off = word_to_usize(&head_word(bytes, elems_base, i)?)?;
let e_base = resolve(elems_base, off)?;
let permit_off = word_to_usize(&head_word(bytes, e_base, 2)?)?;
out.push(CollateralSupply {
collateral_index: word_to_u256(&head_word(bytes, e_base, 0)?),
assets: word_to_u256(&head_word(bytes, e_base, 1)?),
permit: decode_token_permit(bytes, resolve(e_base, permit_off)?)?,
});
}
Ok(out)
}
fn decode_offer_fills(bytes: &[u8], base: usize) -> Result<Vec<OfferFill>, DecodeError> {
let (len, elems_base) = dynamic_array_head(bytes, base)?;
let mut out = Vec::with_capacity(len);
for i in 0..len {
let off = word_to_usize(&head_word(bytes, elems_base, i)?)?;
let fill_base = resolve(elems_base, off)?;
let offer_off = word_to_usize(&head_word(bytes, fill_base, 0)?)?;
let rd_off = word_to_usize(&head_word(bytes, fill_base, 1)?)?;
let units = word_to_u256(&head_word(bytes, fill_base, 2)?);
let offer = decode_offer_tuple(bytes, resolve(fill_base, offer_off)?)?;
let ratifier_data_raw = decode_bytes(bytes, resolve(fill_base, rd_off)?)?;
let ratifier_data = decode_any_ratifier_data(&ratifier_data_raw)?;
out.push(OfferFill {
offer,
ratifier_data_raw,
ratifier_data,
units,
});
}
Ok(out)
}
fn dynamic_array_head(bytes: &[u8], base: usize) -> Result<(usize, usize), DecodeError> {
let len = word_to_usize(&word_at(bytes, base)?)?;
let elems_base = resolve(base, 32)?;
let span = len.checked_mul(32).ok_or(DecodeError::BadLength(len))?;
let end = elems_base
.checked_add(span)
.ok_or(DecodeError::BadLength(len))?;
if end > bytes.len() {
return Err(DecodeError::TooShort {
needed: end,
have: bytes.len(),
});
}
Ok((len, elems_base))
}
pub fn decode_cancel_root_calldata(bytes: &[u8]) -> Result<(Address, Word), DecodeError> {
let args = strip_selector(bytes, crate::CANCEL_ROOT_SELECTOR)?;
let maker = word_to_address(&head_word(args, 0, 0)?);
let root = head_word(args, 0, 1)?;
Ok((maker, root))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RatifyCall {
pub maker: Address,
pub root: Word,
pub ratified: bool,
}
pub fn decode_set_is_root_ratified_calldata(bytes: &[u8]) -> Result<RatifyCall, DecodeError> {
let args = strip_selector(bytes, crate::SET_IS_ROOT_RATIFIED_SELECTOR)?;
Ok(RatifyCall {
maker: word_to_address(&head_word(args, 0, 0)?),
root: head_word(args, 0, 1)?,
ratified: word_to_bool(&head_word(args, 0, 2)?)?,
})
}
fn strip_selector(bytes: &[u8], expected: [u8; 4]) -> Result<&[u8], DecodeError> {
if bytes.len() < 4 {
return Err(DecodeError::TooShort {
needed: 4,
have: bytes.len(),
});
}
let selector = [bytes[0], bytes[1], bytes[2], bytes[3]];
if selector != expected {
return Err(DecodeError::BadSelector(selector));
}
Ok(&bytes[4..])
}