use crate::serialization;
use krusty_kms_common::Result;
use krusty_kms_sdk::operations::{
FundProof, RagequitProof, RolloverProof, TransferProof, WithdrawProof,
};
use starknet_rust::core::types::Call;
use starknet_rust::core::utils::get_selector_from_name;
use starknet_types_core::curve::ProjectivePoint;
use starknet_types_core::felt::Felt as CoreFelt;
type StarknetRsFelt = starknet_rust::core::types::Felt;
#[must_use]
fn core_felt_to_rs(felt: CoreFelt) -> StarknetRsFelt {
StarknetRsFelt::from_bytes_be(&felt.to_bytes_be())
}
pub fn build_fund_calls(
tongo_address: CoreFelt,
erc20_address: CoreFelt,
rate: u128,
proof: &FundProof,
hint_ciphertext: &[u8; 64],
hint_nonce: &[u8; 24],
) -> Result<(Call, Call)> {
let approve_amount = proof.amount * rate;
let approve_call = build_erc20_approve(erc20_address, tongo_address, approve_amount)?;
let mut calldata = Vec::new();
let (to_x, to_y) = serialization::serialize_projective_point(&proof.y)?;
calldata.push(core_felt_to_rs(to_x));
calldata.push(core_felt_to_rs(to_y));
calldata.push(core_felt_to_rs(CoreFelt::from(proof.amount)));
let hint_felts = serialization::serialize_ae_balance(hint_ciphertext, hint_nonce)?;
for felt in hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let proof_felts = serialization::serialize_poe_proof(&proof.proof)?;
for felt in proof_felts {
calldata.push(core_felt_to_rs(felt));
}
if let Some(ref audit) = proof.audit {
calldata.push(core_felt_to_rs(CoreFelt::ZERO));
let balance_felts = serialization::serialize_cipher_balance(&audit.audited_balance)?;
for felt in balance_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_hint_felts =
serialization::serialize_ae_balance(&audit.hint_ciphertext, &audit.hint_nonce)?;
for felt in audit_hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_proof_felts = serialization::serialize_audit_proof(&audit.proof)?;
for felt in audit_proof_felts {
calldata.push(core_felt_to_rs(felt));
}
} else {
calldata.push(core_felt_to_rs(CoreFelt::ONE)); }
let fund_call = Call {
to: core_felt_to_rs(tongo_address),
selector: get_selector_from_name("fund")
.map_err(|e| krusty_kms_common::KmsError::CryptoError(e.to_string()))?,
calldata,
};
Ok((approve_call, fund_call))
}
pub fn build_erc20_approve(
erc20_address: CoreFelt,
spender: CoreFelt,
amount: u128,
) -> Result<Call> {
let (low, high) = serialization::u128_to_u256(amount);
Ok(Call {
to: core_felt_to_rs(erc20_address),
selector: get_selector_from_name("approve")
.map_err(|e| krusty_kms_common::KmsError::CryptoError(e.to_string()))?,
calldata: vec![
core_felt_to_rs(spender),
core_felt_to_rs(low),
core_felt_to_rs(high),
],
})
}
pub fn build_rollover_call(
tongo_address: CoreFelt,
proof: &RolloverProof,
hint_ciphertext: &[u8; 64],
hint_nonce: &[u8; 24],
) -> Result<Call> {
let mut calldata = Vec::new();
let (to_x, to_y) = serialization::serialize_projective_point(&proof.y)?;
calldata.push(core_felt_to_rs(to_x));
calldata.push(core_felt_to_rs(to_y));
let hint_felts = serialization::serialize_ae_balance(hint_ciphertext, hint_nonce)?;
for felt in hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let proof_felts = serialization::serialize_poe_proof(&proof.proof)?;
for felt in proof_felts {
calldata.push(core_felt_to_rs(felt));
}
Ok(Call {
to: core_felt_to_rs(tongo_address),
selector: get_selector_from_name("rollover")
.map_err(|e| krusty_kms_common::KmsError::CryptoError(e.to_string()))?,
calldata,
})
}
pub fn build_withdraw_call(
tongo_address: CoreFelt,
proof: &WithdrawProof,
hint_ciphertext: &[u8; 64],
hint_nonce: &[u8; 24],
) -> Result<Call> {
let mut calldata = Vec::new();
let (from_x, from_y) = serialization::serialize_projective_point(&proof.y)?;
calldata.push(core_felt_to_rs(from_x));
calldata.push(core_felt_to_rs(from_y));
calldata.push(core_felt_to_rs(proof.recipient));
calldata.push(core_felt_to_rs(CoreFelt::from(proof.amount)));
let hint_felts = serialization::serialize_ae_balance(hint_ciphertext, hint_nonce)?;
for felt in hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let ac_felts = serialization::serialize_cipher_balance(&proof.auxiliar_cipher)?;
for felt in ac_felts {
calldata.push(core_felt_to_rs(felt));
}
let (ax_x, ax_y) = serialization::serialize_projective_point(&proof.a_x)?;
calldata.push(core_felt_to_rs(ax_x));
calldata.push(core_felt_to_rs(ax_y));
let (ar_x, ar_y) = serialization::serialize_projective_point(&proof.a_r)?;
calldata.push(core_felt_to_rs(ar_x));
calldata.push(core_felt_to_rs(ar_y));
let (a_x, a_y) = serialization::serialize_projective_point(&proof.a)?;
calldata.push(core_felt_to_rs(a_x));
calldata.push(core_felt_to_rs(a_y));
let (av_x, av_y) = serialization::serialize_projective_point(&proof.a_v)?;
calldata.push(core_felt_to_rs(av_x));
calldata.push(core_felt_to_rs(av_y));
calldata.push(core_felt_to_rs(proof.sx));
calldata.push(core_felt_to_rs(proof.sb));
calldata.push(core_felt_to_rs(proof.sr));
let range_felts = serialization::serialize_range(&proof.range)?;
for felt in range_felts {
calldata.push(core_felt_to_rs(felt));
}
if let Some(ref audit) = proof.audit {
calldata.push(core_felt_to_rs(CoreFelt::ZERO));
let balance_felts = serialization::serialize_cipher_balance(&audit.audited_balance)?;
for felt in balance_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_hint_felts =
serialization::serialize_ae_balance(&audit.hint_ciphertext, &audit.hint_nonce)?;
for felt in audit_hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_proof_felts = serialization::serialize_audit_proof(&audit.proof)?;
for felt in audit_proof_felts {
calldata.push(core_felt_to_rs(felt));
}
} else {
calldata.push(core_felt_to_rs(CoreFelt::ONE)); }
Ok(Call {
to: core_felt_to_rs(tongo_address),
selector: get_selector_from_name("withdraw")
.map_err(|e| krusty_kms_common::KmsError::CryptoError(e.to_string()))?,
calldata,
})
}
#[allow(clippy::too_many_arguments)]
pub fn build_transfer_call(
tongo_address: CoreFelt,
from: &starknet_types_core::curve::ProjectivePoint,
to: &starknet_types_core::curve::ProjectivePoint,
proof: &TransferProof,
hint_transfer_ciphertext: &[u8; 64],
hint_transfer_nonce: &[u8; 24],
hint_leftover_ciphertext: &[u8; 64],
hint_leftover_nonce: &[u8; 24],
) -> Result<Call> {
let mut calldata = Vec::new();
let (from_x, from_y) = serialization::serialize_projective_point(from)?;
calldata.push(core_felt_to_rs(from_x));
calldata.push(core_felt_to_rs(from_y));
let (to_x, to_y) = serialization::serialize_projective_point(to)?;
calldata.push(core_felt_to_rs(to_x));
calldata.push(core_felt_to_rs(to_y));
let (tb_l_x, tb_l_y) = serialization::serialize_projective_point(&proof.transfer_balance_l)?;
let (tb_r_x, tb_r_y) = serialization::serialize_projective_point(&proof.transfer_balance_r)?;
calldata.push(core_felt_to_rs(tb_l_x));
calldata.push(core_felt_to_rs(tb_l_y));
calldata.push(core_felt_to_rs(tb_r_x));
calldata.push(core_felt_to_rs(tb_r_y));
let (tbs_l_x, tbs_l_y) =
serialization::serialize_projective_point(&proof.transfer_balance_self_l)?;
let (tbs_r_x, tbs_r_y) =
serialization::serialize_projective_point(&proof.transfer_balance_self_r)?;
calldata.push(core_felt_to_rs(tbs_l_x));
calldata.push(core_felt_to_rs(tbs_l_y));
calldata.push(core_felt_to_rs(tbs_r_x));
calldata.push(core_felt_to_rs(tbs_r_y));
let hint_transfer =
serialization::serialize_ae_balance(hint_transfer_ciphertext, hint_transfer_nonce)?;
for felt in hint_transfer {
calldata.push(core_felt_to_rs(felt));
}
let hint_leftover =
serialization::serialize_ae_balance(hint_leftover_ciphertext, hint_leftover_nonce)?;
for felt in hint_leftover {
calldata.push(core_felt_to_rs(felt));
}
let ac_felts = serialization::serialize_cipher_balance(&proof.auxiliar_cipher)?;
for felt in ac_felts {
calldata.push(core_felt_to_rs(felt));
}
let ac2_felts = serialization::serialize_cipher_balance(&proof.auxiliar_cipher2)?;
for felt in ac2_felts {
calldata.push(core_felt_to_rs(felt));
}
let proof_felts = serialization::serialize_proof_of_transfer(&proof.proof)?;
for felt in proof_felts {
calldata.push(core_felt_to_rs(felt));
}
if let Some(ref audit) = proof.audit_balance {
calldata.push(core_felt_to_rs(CoreFelt::ZERO));
let balance_felts = serialization::serialize_cipher_balance(&audit.audited_balance)?;
for felt in balance_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_hint_felts =
serialization::serialize_ae_balance(&audit.hint_ciphertext, &audit.hint_nonce)?;
for felt in audit_hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_proof_felts = serialization::serialize_audit_proof(&audit.proof)?;
for felt in audit_proof_felts {
calldata.push(core_felt_to_rs(felt));
}
} else {
calldata.push(core_felt_to_rs(CoreFelt::ONE));
}
if let Some(ref audit) = proof.audit_transfer {
calldata.push(core_felt_to_rs(CoreFelt::ZERO));
let balance_felts = serialization::serialize_cipher_balance(&audit.audited_balance)?;
for felt in balance_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_hint_felts =
serialization::serialize_ae_balance(&audit.hint_ciphertext, &audit.hint_nonce)?;
for felt in audit_hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_proof_felts = serialization::serialize_audit_proof(&audit.proof)?;
for felt in audit_proof_felts {
calldata.push(core_felt_to_rs(felt));
}
} else {
calldata.push(core_felt_to_rs(CoreFelt::ONE));
}
Ok(Call {
to: core_felt_to_rs(tongo_address),
selector: get_selector_from_name("transfer")
.map_err(|e| krusty_kms_common::KmsError::CryptoError(e.to_string()))?,
calldata,
})
}
pub fn build_ragequit_call(
tongo_address: CoreFelt,
proof: &RagequitProof,
hint_ciphertext: &[u8; 64],
hint_nonce: &[u8; 24],
) -> Result<Call> {
let mut calldata = Vec::new();
let (from_x, from_y) = serialization::serialize_projective_point(&proof.y)?;
calldata.push(core_felt_to_rs(from_x));
calldata.push(core_felt_to_rs(from_y));
calldata.push(core_felt_to_rs(proof.recipient));
calldata.push(core_felt_to_rs(CoreFelt::from(proof.amount)));
let hint_felts = serialization::serialize_ae_balance(hint_ciphertext, hint_nonce)?;
for felt in hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let (ax_x, ax_y) = serialization::serialize_projective_point(&proof.a_x)?;
calldata.push(core_felt_to_rs(ax_x));
calldata.push(core_felt_to_rs(ax_y));
let (ar_x, ar_y) = serialization::serialize_projective_point(&proof.a_r)?;
calldata.push(core_felt_to_rs(ar_x));
calldata.push(core_felt_to_rs(ar_y));
calldata.push(core_felt_to_rs(proof.sx));
if let Some(ref audit) = proof.audit {
calldata.push(core_felt_to_rs(CoreFelt::ZERO));
let balance_felts = serialization::serialize_cipher_balance(&audit.audited_balance)?;
for felt in balance_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_hint_felts =
serialization::serialize_ae_balance(&audit.hint_ciphertext, &audit.hint_nonce)?;
for felt in audit_hint_felts {
calldata.push(core_felt_to_rs(felt));
}
let audit_proof_felts = serialization::serialize_audit_proof(&audit.proof)?;
for felt in audit_proof_felts {
calldata.push(core_felt_to_rs(felt));
}
} else {
calldata.push(core_felt_to_rs(CoreFelt::ONE));
}
Ok(Call {
to: core_felt_to_rs(tongo_address),
selector: get_selector_from_name("ragequit")
.map_err(|e| krusty_kms_common::KmsError::CryptoError(e.to_string()))?,
calldata,
})
}
pub fn build_outside_fund_calls(
tongo_address: CoreFelt,
erc20_address: CoreFelt,
to: &ProjectivePoint,
amount: u128,
rate: u128,
) -> Result<(Call, Call)> {
let approve_amount = amount * rate;
let approve_call = build_erc20_approve(erc20_address, tongo_address, approve_amount)?;
let (to_x, to_y) = serialization::serialize_projective_point(to)?;
let calldata = vec![
core_felt_to_rs(to_x),
core_felt_to_rs(to_y),
core_felt_to_rs(CoreFelt::from(amount)),
];
let outside_fund_call = Call {
to: core_felt_to_rs(tongo_address),
selector: get_selector_from_name("outside_fund")
.map_err(|e| krusty_kms_common::KmsError::CryptoError(e.to_string()))?,
calldata,
};
Ok((approve_call, outside_fund_call))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_erc20_approve() {
let erc20 = CoreFelt::from(0x123u64);
let spender = CoreFelt::from(0x456u64);
let amount = 1000u128;
let call = build_erc20_approve(erc20, spender, amount).unwrap();
assert_eq!(call.calldata.len(), 3);
}
#[test]
fn test_build_outside_fund_calls() {
use starknet_types_core::curve::ProjectivePoint;
use starknet_types_core::felt::Felt;
let tongo = CoreFelt::from(0x111u64);
let erc20 = CoreFelt::from(0x222u64);
let amount = 500u128;
let rate = 10u128;
let g_x =
Felt::from_hex("0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca")
.unwrap();
let g_y =
Felt::from_hex("0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f")
.unwrap();
let to = ProjectivePoint::from_affine(g_x, g_y).unwrap();
let (approve_call, fund_call) =
build_outside_fund_calls(tongo, erc20, &to, amount, rate).unwrap();
assert_eq!(approve_call.calldata.len(), 3);
assert_eq!(fund_call.calldata.len(), 3);
}
}