use k256::ecdsa::SigningKey;
use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Validation {
pub validator: String,
pub challenger: String,
pub subject_token_id: u64,
pub work_ref_hex: String,
pub stake_wei: u128,
pub challenge_deadline: u64,
pub resolve_deadline: u64,
pub status: u8,
pub verdict_valid: bool,
}
pub(crate) fn encode_stake_validation(
work_ref: &[u8; 32],
subject_token_id: u64,
valid: bool,
stake_wei: u128,
) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + 4 * 32);
out.extend_from_slice(&selector("stakeValidation(bytes32,uint256,bool,uint256)"));
out.extend_from_slice(work_ref); out.extend_from_slice(&u256_be(subject_token_id as u128)); out.extend_from_slice(&u256_be(valid as u128)); out.extend_from_slice(&u256_be(stake_wei)); out
}
pub async fn stake_validation_sponsored(
validator_signer: &SigningKey,
fee_payer: &SigningKey,
work_ref: [u8; 32],
subject_token_id: u64,
valid: bool,
stake_wei: u128,
fee_token: &str,
) -> Result<String, String> {
sponsored_escrow_diamond_call(
validator_signer,
fee_payer,
stake_wei,
encode_stake_validation(&work_ref, subject_token_id, valid, stake_wei),
fee_token,
3_500_000,
)
.await
}
pub async fn challenge_validation_sponsored(
challenger_signer: &SigningKey,
fee_payer: &SigningKey,
validation_id: u64,
stake_wei: u128,
fee_token: &str,
) -> Result<String, String> {
sponsored_escrow_diamond_call(
challenger_signer,
fee_payer,
stake_wei,
call_uint_bytes("challengeValidation(uint256)", validation_id),
fee_token,
1_500_000,
)
.await
}
pub async fn resolve_validation_sponsored(
resolver_signer: &SigningKey,
fee_payer: &SigningKey,
validation_id: u64,
validator_wins: bool,
fee_token: &str,
) -> Result<String, String> {
let mut input = Vec::with_capacity(4 + 2 * 32);
input.extend_from_slice(&selector("resolveValidation(uint256,bool)"));
input.extend_from_slice(&u256_be(validation_id as u128));
input.extend_from_slice(&u256_be(validator_wins as u128));
sponsored_diamond_call(resolver_signer, fee_payer, input, fee_token, 2_000_000).await
}
pub async fn reclaim_stake_sponsored(
sender: &SigningKey,
fee_payer: &SigningKey,
validation_id: u64,
fee_token: &str,
) -> Result<String, String> {
sponsored_diamond_call(
sender,
fee_payer,
call_uint_bytes("reclaimStake(uint256)", validation_id),
fee_token,
600_000,
)
.await
}
pub async fn reclaim_unresolved_sponsored(
sender: &SigningKey,
fee_payer: &SigningKey,
validation_id: u64,
fee_token: &str,
) -> Result<String, String> {
sponsored_diamond_call(
sender,
fee_payer,
call_uint_bytes("reclaimUnresolved(uint256)", validation_id),
fee_token,
800_000,
)
.await
}
pub async fn get_validation(validation_id: u64) -> Result<Option<Validation>, String> {
let result = read_view(
selector("getValidation(uint256)"),
&[u256_be(validation_id as u128)],
)
.await?;
let bytes = hex_to_bytes(&result)?;
Ok(decode_validation(&bytes))
}
pub(crate) fn decode_validation(bytes: &[u8]) -> Option<Validation> {
if bytes.len() < 9 * 32 {
return None;
}
let word = |i: usize| &bytes[i * 32..(i + 1) * 32];
let validator_bytes = &word(0)[12..32];
if validator_bytes.iter().all(|&b| b == 0) {
return None; }
Some(Validation {
validator: format!("0x{}", bytes_to_hex(validator_bytes)),
challenger: format!("0x{}", bytes_to_hex(&word(1)[12..32])),
subject_token_id: u64_low(word(2)),
work_ref_hex: format!("0x{}", bytes_to_hex(word(3))),
stake_wei: u128_low(word(4)),
challenge_deadline: u64_low(word(5)),
resolve_deadline: u64_low(word(6)),
status: word(7)[31],
verdict_valid: word(8)[31] != 0,
})
}
pub async fn has_validated(
validator_hex: &str,
subject: u64,
work_ref: [u8; 32],
) -> Result<bool, String> {
let validator = parse_eth_address(validator_hex)?;
let result = read_view(
selector("hasValidated(address,uint256,bytes32)"),
&[addr_word(&validator), u256_be(subject as u128), work_ref],
)
.await?;
let bytes = hex_to_bytes(&result)?;
Ok(bytes.last().is_some_and(|&b| b != 0))
}
pub async fn validation_count() -> Result<u64, String> {
let result = read_view(selector("validationCount()"), &[]).await?;
let bytes = hex_to_bytes(&result)?;
if bytes.len() < 32 {
return Ok(0);
}
Ok(u64_low(&bytes[0..32]))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stake_validation_calldata_layout() {
let mut work_ref = [0u8; 32];
work_ref[0] = 0xAB; work_ref[24..32].copy_from_slice(&42u64.to_be_bytes()); let cd = encode_stake_validation(&work_ref, 7, true, 100_000_000_000_000_000_000);
assert_eq!(cd.len(), 4 + 4 * 32);
assert_eq!(
&cd[..4],
&selector("stakeValidation(bytes32,uint256,bool,uint256)")
);
assert_eq!(&cd[4..36], &work_ref[..]);
assert_eq!(cd[4], 0xAB);
assert_eq!(&cd[36..68], &u256_be(7)[..]);
assert!(cd[68..99].iter().all(|&b| b == 0), "bool word must be zero-padded");
assert_eq!(cd[99], 1, "true must occupy the low byte of word 2");
assert_eq!(&cd[100..132], &u256_be(100_000_000_000_000_000_000)[..]);
let cd_false = encode_stake_validation(&work_ref, 7, false, 1);
assert!(cd_false[68..100].iter().all(|&b| b == 0), "false must be an all-zero word");
}
#[test]
fn decode_validation_nine_static_words() {
let word = |hex: &str| -> String {
assert!(hex.len() <= 64);
format!("{:0>64}", hex)
};
let mut hex = String::from("0x");
hex.push_str(&word("1111111111111111111111111111111111111111")); hex.push_str(&word("2222222222222222222222222222222222222222")); hex.push_str(&word("7")); let mut wref = String::from("ab");
wref.push_str(&"0".repeat(60));
wref.push_str("2a");
hex.push_str(&wref); hex.push_str(&word("56bc75e2d63100000")); hex.push_str(&word("f4240")); hex.push_str(&word("f9060")); hex.push_str(&word("1")); hex.push_str(&word("1")); let bytes = hex_to_bytes(&hex).unwrap();
let v = decode_validation(&bytes).expect("decodes");
assert_eq!(v.validator, "0x1111111111111111111111111111111111111111");
assert_eq!(v.challenger, "0x2222222222222222222222222222222222222222");
assert_eq!(v.subject_token_id, 7);
assert!(v.work_ref_hex.starts_with("0xab"));
assert!(v.work_ref_hex.ends_with("2a"));
assert_eq!(v.stake_wei, 100_000_000_000_000_000_000);
assert_eq!(v.challenge_deadline, 1_000_000);
assert_eq!(v.resolve_deadline, 1_020_000);
assert_eq!(v.status, 1);
assert!(v.verdict_valid);
let zeros = vec![0u8; 9 * 32];
assert!(decode_validation(&zeros).is_none());
assert!(decode_validation(&[]).is_none());
assert!(decode_validation(&[0u8; 64]).is_none());
}
}