use k256::ecdsa::SigningKey;
use super::*;
pub fn invite_code_hash(code: &str) -> [u8; 32] {
keccak_key(code.as_bytes())
}
pub(crate) fn encode_create_invite(code_hash: &[u8; 32], amount_wei: u128, ttl_secs: u64) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + 3 * 32);
out.extend_from_slice(&selector("createInvite(bytes32,uint256,uint64)"));
out.extend_from_slice(code_hash);
out.extend_from_slice(&u256_be(amount_wei));
out.extend_from_slice(&u256_be(ttl_secs as u128));
out
}
pub(crate) fn encode_accept_invite(code: &str) -> Vec<u8> {
let bytes = code.as_bytes();
let len = bytes.len();
let padded_len = len.div_ceil(32) * 32;
let mut out = Vec::with_capacity(4 + 32 + 32 + padded_len);
out.extend_from_slice(&selector("acceptInvite(string)"));
out.extend_from_slice(&u256_be(0x20));
out.extend_from_slice(&u256_be(len as u128));
out.extend_from_slice(bytes);
out.resize(4 + 32 + 32 + padded_len, 0);
out
}
pub(crate) fn encode_reclaim_invite(code_hash: &[u8; 32]) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + 32);
out.extend_from_slice(&selector("reclaimInvite(bytes32)"));
out.extend_from_slice(code_hash);
out
}
#[allow(clippy::too_many_arguments)]
pub async fn create_invite_sponsored(
sender: &SigningKey,
fee_payer: &SigningKey,
code_hash: [u8; 32],
amount_wei: u128,
ttl_secs: u64,
fee_token: &str,
) -> Result<String, String> {
create_invite_sponsored_bridged(sender, fee_payer, code_hash, amount_wei, ttl_secs, fee_token, 0)
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn create_invite_sponsored_bridged(
sender: &SigningKey,
fee_payer: &SigningKey,
code_hash: [u8; 32],
amount_wei: u128,
ttl_secs: u64,
fee_token: &str,
bridge_wei: u128,
) -> Result<String, String> {
sponsored_escrow_diamond_call_bridged(
sender,
fee_payer,
amount_wei,
encode_create_invite(&code_hash, amount_wei, ttl_secs),
fee_token,
2_500_000,
bridge_wei,
)
.await
}
pub async fn accept_invite_sponsored(
sender: &SigningKey,
fee_payer: &SigningKey,
code: &str,
fee_token: &str,
) -> Result<String, String> {
sponsored_diamond_call(sender, fee_payer, encode_accept_invite(code), fee_token, 2_000_000)
.await
}
pub async fn reclaim_invite_sponsored(
sender: &SigningKey,
fee_payer: &SigningKey,
code_hash: [u8; 32],
fee_token: &str,
) -> Result<String, String> {
sponsored_diamond_call(
sender,
fee_payer,
encode_reclaim_invite(&code_hash),
fee_token,
600_000,
)
.await
}
pub async fn escrowed_of(account_hex: &str) -> Result<u128, String> {
let account = parse_eth_address(account_hex)?;
let result = read_view(selector("escrowedOf(address)"), &[addr_word(&account)]).await?;
decode_u256_as_u128(&result)
}
pub async fn get_invite(code_hash: [u8; 32]) -> Result<(String, u128, u64, u8), String> {
let result = read_view(selector("getInvite(bytes32)"), &[code_hash]).await?;
let bytes = hex_to_bytes(&result)?;
if bytes.len() < 4 * 32 {
return Err(format!("getInvite: short response {} bytes", bytes.len()));
}
let word = |i: usize| &bytes[i * 32..(i + 1) * 32];
let funder = format!("0x{}", bytes_to_hex(&word(0)[12..32])); let amount = u128_low(word(1)); let expiry = u64_low(word(2)); let status = word(3)[31]; Ok((funder, amount, expiry, status))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn invite_code_hash_matches_keccak256_of_code_bytes() {
let h_empty = invite_code_hash("");
let hex_empty: String = h_empty.iter().map(|b| format!("{b:02x}")).collect();
assert_eq!(
hex_empty,
"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
);
let code = "inv-100-A8kZqM2pQr";
assert_eq!(invite_code_hash(code), keccak_key(code.as_bytes()));
assert_ne!(invite_code_hash("inv-10-aaaa"), invite_code_hash("inv-10-aaab"));
assert_eq!(invite_code_hash(code), invite_code_hash(code));
}
#[test]
fn encode_create_invite_layout() {
let code_hash = invite_code_hash("inv-100-deadbeef01");
let amount: u128 = 100 * 1_000_000_000_000_000_000; let ttl: u64 = 7 * 24 * 3600; let cd = encode_create_invite(&code_hash, amount, ttl);
assert_eq!(cd.len(), 4 + 3 * 32);
assert_eq!(&cd[..4], &selector("createInvite(bytes32,uint256,uint64)"));
assert_eq!(&cd[4..36], &code_hash[..]);
assert_eq!(&cd[36..68], &u256_be(amount)[..]);
assert_eq!(&cd[68..100], &u256_be(ttl as u128)[..]);
}
#[test]
fn encode_accept_invite_dynamic_string_layout() {
let code = "inv-1000-Qm2pZ8kXaa"; let cd = encode_accept_invite(code);
assert_eq!(&cd[..4], &selector("acceptInvite(string)"));
assert_eq!(&cd[4..36], &u256_be(0x20)[..]);
assert_eq!(&cd[36..68], &u256_be(code.len() as u128)[..]);
assert_eq!(&cd[68..68 + code.len()], code.as_bytes());
let padded = code.len().div_ceil(32) * 32;
assert_eq!(cd.len(), 4 + 32 + 32 + padded);
assert!(cd[68 + code.len()..].iter().all(|&b| b == 0));
assert_eq!(&cd[4..36], &encode_redeem(code)[4..36]);
assert_eq!(&cd[36..], &encode_redeem(code)[36..]);
}
#[test]
fn encode_reclaim_invite_layout() {
let code_hash = invite_code_hash("inv-10-cafef00d11");
let cd = encode_reclaim_invite(&code_hash);
assert_eq!(cd.len(), 4 + 32);
assert_eq!(&cd[..4], &selector("reclaimInvite(bytes32)"));
assert_eq!(&cd[4..36], &code_hash[..]);
}
}