diff --git a/SPEC.md b/SPEC.md index 732c91a..e9f3cc7 100644 --- a/SPEC.md +++ b/SPEC.md @@ -307,6 +307,7 @@ Lazy vaults have their `USER_VAULTS` index populated on first access via `initia #### `create_vault_full(owner, amount, start_time, end_time) → u64` - Admin-only. +- Requires `(end_time - start_time) ≤ MAX_DURATION` where `MAX_DURATION = 315,360,000` seconds (10 years). Panics otherwise. - Deducts `amount` from `ADMIN_BALANCE`. Panics if insufficient. - Writes full vault struct with `is_initialized = true`. - Updates `USER_VAULTS[owner]`. @@ -315,6 +316,7 @@ Lazy vaults have their `USER_VAULTS` index populated on first access via `initia #### `create_vault_lazy(owner, amount, start_time, end_time) → u64` - Admin-only. +- Requires `(end_time - start_time) ≤ MAX_DURATION` where `MAX_DURATION = 315,360,000` seconds (10 years). Panics otherwise. - Same as above but sets `is_initialized = false` and skips `USER_VAULTS` write. - Lower storage cost at creation time. @@ -341,6 +343,7 @@ Lazy vaults have their `USER_VAULTS` index populated on first access via `initia #### `batch_create_vaults_lazy(batch_data) → Vec` - Admin-only. - Validates total batch amount against `ADMIN_BALANCE` in a single check upfront. +- Requires each vault’s `(end_time - start_time) ≤ MAX_DURATION` where `MAX_DURATION = 315,360,000` seconds (10 years). Panics otherwise. - Creates all vaults lazily in a loop. Updates `VAULT_COUNT` once at the end. #### `batch_create_vaults_full(batch_data) → Vec` diff --git a/contracts/grant_contracts/src/lib.rs b/contracts/grant_contracts/src/lib.rs index 680e063..9c912aa 100644 --- a/contracts/grant_contracts/src/lib.rs +++ b/contracts/grant_contracts/src/lib.rs @@ -10,6 +10,9 @@ const END_TIME: Symbol = symbol_short!("END"); const RECIPIENT: Symbol = symbol_short!("RECIPIENT"); const CLAIMED: Symbol = symbol_short!("CLAIMED"); +// 10 years in seconds (Issue #44) +const MAX_DURATION: u64 = 315_360_000; + #[contractimpl] impl GrantContract { pub fn initialize_grant( @@ -18,35 +21,47 @@ impl GrantContract { total_amount: U256, duration_seconds: u64, ) -> u64 { + assert!( + duration_seconds <= MAX_DURATION, + "duration exceeds MAX_DURATION" + ); let start_time = env.ledger().timestamp(); let end_time = start_time + duration_seconds; - + env.storage().instance().set(&TOTAL_AMOUNT, &total_amount); env.storage().instance().set(&START_TIME, &start_time); env.storage().instance().set(&END_TIME, &end_time); env.storage().instance().set(&RECIPIENT, &recipient); - env.storage().instance().set(&CLAIMED, &U256::from_u32(&env, 0)); - + env.storage() + .instance() + .set(&CLAIMED, &U256::from_u32(&env, 0)); end_time } - + pub fn claimable_balance(env: Env) -> U256 { let current_time = env.ledger().timestamp(); let start_time = env.storage().instance().get(&START_TIME).unwrap_or(0); let end_time = env.storage().instance().get(&END_TIME).unwrap_or(0); - let total_amount = env.storage().instance().get(&TOTAL_AMOUNT).unwrap_or(U256::from_u32(&env, 0)); - let claimed = env.storage().instance().get(&CLAIMED).unwrap_or(U256::from_u32(&env, 0)); - + let total_amount = env + .storage() + .instance() + .get(&TOTAL_AMOUNT) + .unwrap_or(U256::from_u32(&env, 0)); + let claimed = env + .storage() + .instance() + .get(&CLAIMED) + .unwrap_or(U256::from_u32(&env, 0)); if current_time <= start_time { return U256::from_u32(&env, 0); } - + let elapsed = if current_time >= end_time { end_time - start_time } else { current_time - start_time }; - + let total_duration = end_time - start_time; let vested = if total_duration > 0 { let elapsed_u256 = U256::from_u32(&env, elapsed as u32); @@ -55,36 +70,47 @@ impl GrantContract { } else { U256::from_u32(&env, 0) }; - + if vested > claimed { vested.sub(&claimed) } else { U256::from_u32(&env, 0) } } - + pub fn claim(env: Env, recipient: Address) -> U256 { recipient.require_auth(); - + let stored_recipient = env.storage().instance().get(&RECIPIENT).unwrap(); assert_eq!(recipient, stored_recipient, "Unauthorized recipient"); - + let claimable = Self::claimable_balance(env.clone()); assert!(claimable > U256::from_u32(&env, 0), "No tokens to claim"); - - let claimed = env.storage().instance().get(&CLAIMED).unwrap_or(U256::from_u32(&env, 0)); + + let claimed = env + .storage() + .instance() + .get(&CLAIMED) + .unwrap_or(U256::from_u32(&env, 0)); let new_claimed = claimed.add(&claimable); env.storage().instance().set(&CLAIMED, &new_claimed); - + claimable } - + pub fn get_grant_info(env: Env) -> (U256, u64, u64, U256) { - let total_amount = env.storage().instance().get(&TOTAL_AMOUNT).unwrap_or(U256::from_u32(&env, 0)); + let total_amount = env + .storage() + .instance() + .get(&TOTAL_AMOUNT) + .unwrap_or(U256::from_u32(&env, 0)); let start_time = env.storage().instance().get(&START_TIME).unwrap_or(0); let end_time = env.storage().instance().get(&END_TIME).unwrap_or(0); - let claimed = env.storage().instance().get(&CLAIMED).unwrap_or(U256::from_u32(&env, 0)); - + let claimed = env + .storage() + .instance() + .get(&CLAIMED) + .unwrap_or(U256::from_u32(&env, 0)); (total_amount, start_time, end_time, claimed) } } diff --git a/contracts/grant_contracts/src/test.rs b/contracts/grant_contracts/src/test.rs index 5e44068..148d692 100644 --- a/contracts/grant_contracts/src/test.rs +++ b/contracts/grant_contracts/src/test.rs @@ -1,7 +1,7 @@ #![cfg(test)] use super::*; -use soroban_sdk::{testutils::{Address as _, Ledger}, Address, Env, U256}; +use soroban_sdk::{testutils::Address as _, Address, Env, U256}; #[test] fn test_basic_grant() { @@ -15,7 +15,22 @@ fn test_basic_grant() { let duration = 100u64; client.initialize_grant(&recipient, &total_amount, &duration); - + let claimable = client.claimable_balance(); assert_eq!(claimable, U256::from_u32(&env, 0)); } + +#[test] +#[should_panic(expected = "duration exceeds MAX_DURATION")] +fn test_initialize_rejects_duration_over_max() { + let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register(GrantContract, ()); + let client = GrantContractClient::new(&env, &contract_id); + + let recipient = Address::generate(&env); + let total_amount = U256::from_u32(&env, 1000); + let duration = super::MAX_DURATION + 1; + + client.initialize_grant(&recipient, &total_amount, &duration); +} diff --git a/contracts/vesting_contracts/src/lib.rs b/contracts/vesting_contracts/src/lib.rs index b7ee757..a163c9a 100644 --- a/contracts/vesting_contracts/src/lib.rs +++ b/contracts/vesting_contracts/src/lib.rs @@ -6,6 +6,9 @@ use soroban_sdk::{ Vec, }; +// 10 years in seconds (Issue #44) +pub const MAX_DURATION: u64 = 315_360_000; + // DataKey for whitelisted tokens #[contracttype] pub enum WhitelistDataKey { @@ -51,6 +54,12 @@ pub struct Vault { pub released_amount: i128, pub start_time: u64, pub end_time: u64, + pub creation_time: u64, // Timestamp of creation for clawback grace period + pub step_duration: u64, // Duration of each vesting step in seconds (0 = linear) + + pub is_initialized: bool, // Lazy initialization flag + pub is_irrevocable: bool, // Security flag to prevent admin withdrawal + pub is_transferable: bool, // Can the beneficiary transfer this vault? pub keeper_fee: i128, // Fee paid to anyone who triggers auto_claim pub title: String, // Short human-readable title (max 32 chars) pub is_initialized: bool, // Lazy initialization flag @@ -115,6 +124,26 @@ pub struct VestingContract; #[contractimpl] #[allow(deprecated)] impl VestingContract { + fn require_not_deprecated(env: &Env) { + let deprecated: bool = env + .storage() + .instance() + .get(&DataKey::IsDeprecated) + .unwrap_or(false); + if deprecated { + panic!("Contract is deprecated"); + } + } + + fn require_valid_duration(start_time: u64, end_time: u64) { + let duration = end_time + .checked_sub(start_time) + .unwrap_or_else(|| panic!("end_time must be >= start_time")); + if duration > MAX_DURATION { + panic!("duration exceeds MAX_DURATION"); + } + } + // Admin-only: Add token to whitelist pub fn add_to_whitelist(env: Env, token: Address) { Self::require_admin(&env); @@ -424,6 +453,7 @@ impl VestingContract { ) -> u64 { Self::require_admin(&env); + Self::require_valid_duration(start_time, end_time); let mut vault_count: u64 = env .storage() @@ -528,6 +558,7 @@ impl VestingContract { step_duration: u64, ) -> u64 { Self::require_admin(&env); + Self::require_valid_duration(start_time, end_time); let mut vault_count: u64 = env .storage() @@ -673,24 +704,6 @@ impl VestingContract { elapsed }; - if vault.step_duration > 0 { - // Periodic vesting: calculate vested = (elapsed / step_duration) * rate * step_duration - // Rate is total_amount / duration, so: vested = (elapsed / step_duration) * (total_amount / duration) * step_duration - // This simplifies to: vested = (elapsed / step_duration) * total_amount * step_duration / duration - let completed_steps = elapsed / vault.step_duration; - let rate_per_second = vault.total_amount / duration as i128; - let vested = completed_steps as i128 * rate_per_second * vault.step_duration as i128; - - // Ensure we don't exceed total amount - if vested > vault.total_amount { - vault.total_amount - } else { - vested - } - } else { - // Linear vesting - (vault.total_amount * elapsed as i128) / duration as i128 - } (vault.total_amount * effective_elapsed as i128) / duration as i128 } @@ -1157,6 +1170,9 @@ impl VestingContract { let now = env.ledger().timestamp(); for i in 0..batch_data.recipients.len() { let vault_id = initial_count + i as u64 + 1; + let start_time: u64 = batch_data.start_times.get(i).unwrap(); + let end_time: u64 = batch_data.end_times.get(i).unwrap(); + Self::require_valid_duration(start_time, end_time); let vault = Vault { title: String::from_slice(&env, ""), @@ -1164,12 +1180,9 @@ impl VestingContract { delegate: None, total_amount: batch_data.amounts.get(i).unwrap(), released_amount: 0, - start_time: batch_data.start_times.get(i).unwrap(), - end_time: batch_data.end_times.get(i).unwrap(), + start_time, + end_time, keeper_fee: batch_data.keeper_fees.get(i).unwrap(), - title: String::from_slice(&env, ""), - is_initialized: false, // Lazy initialization - is_irrevocable: false, // Default to revocable for batch operations is_initialized: false, is_irrevocable: false, creation_time: now, @@ -1184,7 +1197,6 @@ impl VestingContract { .set(&DataKey::VaultData(vault_id), &vault); vault_ids.push_back(vault_id); - let start_time = batch_data.start_times.get(i).unwrap(); let cliff_duration = start_time.saturating_sub(now); let vault_created = VaultCreated { vault_id, @@ -1244,6 +1256,9 @@ impl VestingContract { let now = env.ledger().timestamp(); for i in 0..batch_data.recipients.len() { let vault_id = initial_count + i as u64 + 1; + let start_time: u64 = batch_data.start_times.get(i).unwrap(); + let end_time: u64 = batch_data.end_times.get(i).unwrap(); + Self::require_valid_duration(start_time, end_time); let vault = Vault { title: String::from_slice(&env, ""), @@ -1251,8 +1266,8 @@ impl VestingContract { delegate: None, total_amount: batch_data.amounts.get(i).unwrap(), released_amount: 0, - start_time: batch_data.start_times.get(i).unwrap(), - end_time: batch_data.end_times.get(i).unwrap(), + start_time, + end_time, keeper_fee: batch_data.keeper_fees.get(i).unwrap(), title: String::from_slice(&env, ""), is_initialized: true, @@ -1280,7 +1295,6 @@ impl VestingContract { vault_ids.push_back(vault_id); - let start_time = batch_data.start_times.get(i).unwrap(); let cliff_duration = start_time.saturating_sub(now); let vault_created = VaultCreated { vault_id, @@ -1434,6 +1448,12 @@ impl VestingContract { pub fn revoke_partial(env: Env, vault_id: u64, amount: i128) -> i128 { Self::require_admin(&env); + let mut vault: Vault = env + .storage() + .instance() + .get(&DataKey::VaultData(vault_id)) + .unwrap_or_else(|| panic!("Vault not found")); + let returned = Self::internal_revoke_partial(&env, vault_id, amount); // Single admin balance update for this call @@ -1593,6 +1613,10 @@ impl VestingContract { if now > vault.creation_time + grace_period { panic!("Grace period expired"); } + if vault.released_amount > 0 { + panic!("Tokens already claimed"); + } + if vault.released_amount > 0 { panic!("Tokens already claimed"); @@ -1908,7 +1932,6 @@ impl VestingContract { // Calculate currently claimable tokens based on linear vesting pub fn get_claimable_amount(env: Env, vault_id: u64) -> i128 { - let vault: Vault = env.storage().instance() let vault: Vault = env .storage() .instance() @@ -1927,7 +1950,6 @@ impl VestingContract { // Auto-claim function that anyone can call. // Tokens go to beneficiary, but keeper earns a fee. pub fn auto_claim(env: Env, vault_id: u64, keeper: Address) { - let mut vault: Vault = env.storage().instance() if Self::is_paused(env.clone()) { panic!("Contract is paused - all withdrawals are disabled"); } @@ -2105,4 +2127,7 @@ impl VestingContract { } } +// Unit tests for this contract are kept as integration tests under +// `contracts/vesting_contracts/tests/` to avoid `no_std` test-harness friction. +// mod test; // mod test; // Disabled - tests need refactoring diff --git a/contracts/vesting_contracts/src/test.rs b/contracts/vesting_contracts/src/test.rs index 7dfa62d..0faf08d 100644 --- a/contracts/vesting_contracts/src/test.rs +++ b/contracts/vesting_contracts/src/test.rs @@ -1,8 +1,6 @@ #[cfg(test)] mod tests { - use crate::{ - BatchCreateData, Milestone, VestingContract, VestingContractClient, - }; + use crate::{BatchCreateData, Milestone, VestingContract, VestingContractClient}; use soroban_sdk::{ contract, contractimpl, testutils::{Address as _, Ledger}, @@ -13,6 +11,13 @@ mod tests { // Helper: spin up a fresh contract // ------------------------------------------------------------------------- + fn setup() -> ( + Env, + Address, + VestingContractClient<'static>, + Address, + Address, + ) { fn setup() -> (Env, Address, VestingContractClient<'static>, Address) { let env = Env::default(); env.mock_all_auths(); @@ -63,6 +68,24 @@ mod tests { let now = env.ledger().timestamp(); let id1 = client.create_vault_full( + &beneficiary, + &1_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, + ); + let id2 = client.create_vault_full( + &beneficiary, + &500i128, + &(now + 10), + &(now + 2_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &1_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -81,6 +104,14 @@ mod tests { let now = env.ledger().timestamp(); let id = client.create_vault_lazy( + &beneficiary, + &1_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &1_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -91,6 +122,66 @@ mod tests { // Batch vault creation // ------------------------------------------------------------------------- + #[test] + #[should_panic(expected = "duration exceeds MAX_DURATION")] + fn test_create_vault_full_rejects_duration_over_max() { + let (env, _cid, client, _admin, _token) = setup(); + let beneficiary = Address::generate(&env); + let start = env.ledger().timestamp(); + let end = start + crate::MAX_DURATION + 1; + + client.create_vault_full( + &beneficiary, + &1_000i128, + &start, + &end, + &0i128, + &true, + &false, + &0u64, + ); + } + + #[test] + #[should_panic(expected = "duration exceeds MAX_DURATION")] + fn test_create_vault_lazy_rejects_duration_over_max() { + let (env, _cid, client, _admin, _token) = setup(); + let beneficiary = Address::generate(&env); + let start = env.ledger().timestamp(); + let end = start + crate::MAX_DURATION + 1; + + client.create_vault_lazy( + &beneficiary, + &1_000i128, + &start, + &end, + &0i128, + &true, + &false, + &0u64, + ); + } + + #[test] + #[should_panic(expected = "duration exceeds MAX_DURATION")] + fn test_batch_create_vaults_rejects_duration_over_max() { + let (env, _cid, client, _admin, _token) = setup(); + let r1 = Address::generate(&env); + let start = 100u64; + let end = start + crate::MAX_DURATION + 1; + + let batch = BatchCreateData { + recipients: vec![&env, r1.clone()], + amounts: vec![&env, 1_000i128], + start_times: vec![&env, start], + end_times: vec![&env, end], + keeper_fees: vec![&env, 0i128], + step_durations: vec![&env, 0u64], + }; + + client.batch_create_vaults_lazy(&batch); + } + #[test] fn test_batch_create_vaults_lazy() { let (env, _cid, client, _admin) = setup(); @@ -145,6 +236,14 @@ mod tests { let total = 1_009i128; let vault_id = client.create_vault_full( + &beneficiary, + &total, + &start, + &end, + &0i128, + &true, + &true, + &step, &beneficiary, &total, &start, &end, &0i128, &true, &true, &step, ); @@ -166,6 +265,14 @@ mod tests { let total = 100_000i128; let vault_id = client.create_vault_full( + &beneficiary, + &total, + &now, + &(now + duration), + &0i128, + &true, + &false, + &duration, &beneficiary, &total, &now, &(now + duration), &0i128, &true, &false, &duration, ); @@ -184,6 +291,14 @@ mod tests { let duration = 1_000u64; let vault_id = client.create_vault_full( + &beneficiary, + &100_000i128, + &now, + &(now + duration), + &0i128, + &true, + &false, + &duration, &beneficiary, &100_000i128, &now, &(now + duration), &0i128, &true, &false, &duration, ); @@ -191,403 +306,455 @@ mod tests { env.ledger().with_mut(|l| l.timestamp = now + duration - 1); client.claim_tokens(&vault_id, &1i128); } - + #[test] fn test_admin_access_control() { let (env, contract_id, client, admin) = setup(); let new_admin = Address::generate(&env); - + // Test: Admin can propose new admin client.propose_new_admin(&new_admin); assert_eq!(client.get_proposed_admin(), Some(new_admin.clone())); - + // Test: Proposed admin can accept ownership new_admin.require_auth(); client.accept_ownership(); - + // Verify admin transfer completed assert_eq!(client.get_admin(), new_admin); assert_eq!(client.get_proposed_admin(), None); } -#[test] -fn test_periodic_vesting_monthly_steps() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); - - // Create addresses for testing - let admin = Address::generate(&env); - let beneficiary = Address::generate(&env); - - // Initialize contract - let initial_supply = 1000000i128; - env.mock_all_auths(); - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); - - // Create vault with monthly vesting (30 days = 2,592,000 seconds) - let amount = 1200000i128; // 1,200,000 tokens over 12 months = 100,000 per month - let start_time = 1000000u64; - let end_time = start_time + (365 * 24 * 60 * 60); // 1 year - let step_duration = 30 * 24 * 60 * 60; // 30 days in seconds - let keeper_fee = 1000i128; - - let vault_id = client.create_vault_full( - &beneficiary, - &amount, - &start_time, - &end_time, - &keeper_fee, - &false, // revocable - &true, // transferable - &step_duration, - ); - - // Test 1: Before start time - no vesting - env.ledger().set_timestamp(start_time - 1000); - let claimable = client.get_claimable_amount(&vault_id); - assert_eq!(claimable, 0, "Should have no claimable tokens before start time"); - - // Test 2: After 15 days (less than one step) - still no vesting (rounds down) - env.ledger().set_timestamp(start_time + (15 * 24 * 60 * 60)); - let claimable = client.get_claimable_amount(&vault_id); - assert_eq!(claimable, 0, "Should have no claimable tokens before first step completes"); - - // Test 3: After exactly 30 days - one step completed - env.ledger().set_timestamp(start_time + step_duration); - let claimable = client.get_claimable_amount(&vault_id); - let expected_monthly = amount / 12; // 100,000 tokens per month - assert_eq!(claimable, expected_monthly, "Should have exactly one month of tokens after 30 days"); - - // Test 4: After 45 days - still only one step (rounds down) - env.ledger().set_timestamp(start_time + (45 * 24 * 60 * 60)); - let claimable = client.get_claimable_amount(&vault_id); - assert_eq!(claimable, expected_monthly, "Should still have only one month of tokens after 45 days"); - - // Test 5: After 60 days - two steps completed - env.ledger().set_timestamp(start_time + (2 * step_duration)); - let claimable = client.get_claimable_amount(&vault_id); - assert_eq!(claimable, 2 * expected_monthly, "Should have two months of tokens after 60 days"); - - // Test 6: After 6 months - 6 steps completed - env.ledger().set_timestamp(start_time + (6 * step_duration)); - let claimable = client.get_claimable_amount(&vault_id); - assert_eq!(claimable, 6 * expected_monthly, "Should have six months of tokens after 6 months"); - - // Test 7: After end time - all tokens vested - env.ledger().set_timestamp(end_time + 1000); - let claimable = client.get_claimable_amount(&vault_id); - assert_eq!(claimable, amount, "Should have all tokens vested after end time"); -} + #[test] + fn test_periodic_vesting_monthly_steps() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); -#[test] -fn test_periodic_vesting_weekly_steps() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); - - // Create addresses for testing - let admin = Address::generate(&env); - let beneficiary = Address::generate(&env); - - // Initialize contract - let initial_supply = 1000000i128; - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); - - // Set admin as caller - - // Create vault with weekly vesting (7 days = 604,800 seconds) - let amount = 520000i128; // 520,000 tokens over 52 weeks = 10,000 per week - let start_time = 1000000u64; - let end_time = start_time + (365 * 24 * 60 * 60); // 1 year - let step_duration = 7 * 24 * 60 * 60; // 7 days in seconds - let keeper_fee = 100i128; - - let vault_id = client.create_vault_full( - &beneficiary, - &amount, - &start_time, - &end_time, - &keeper_fee, - &false, // revocable - &true, // transferable - &step_duration, - ); - - // Test: After 3 weeks - 3 steps completed - env.ledger().set_timestamp(start_time + (3 * step_duration)); - let claimable = client.get_claimable_amount(&vault_id); - let expected_weekly = 10000i128; // 10,000 tokens per week - assert_eq!(claimable, 3 * expected_weekly, "Should have three weeks of tokens after 3 weeks"); - - // Test: After 10 weeks - 10 steps completed - env.ledger().set_timestamp(start_time + (10 * step_duration)); - let claimable = client.get_claimable_amount(&vault_id); - assert_eq!(claimable, 10 * expected_weekly, "Should have ten weeks of tokens after 10 weeks"); -} + // Create addresses for testing + let admin = Address::generate(&env); + let beneficiary = Address::generate(&env); -#[test] -fn test_linear_vesting_step_duration_zero() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); - - // Create addresses for testing - let admin = Address::generate(&env); - let beneficiary = Address::generate(&env); - - // Initialize contract - let initial_supply = 1000000i128; - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); - - // Set admin as caller - - // Create vault with linear vesting (step_duration = 0) - let amount = 1200000i128; - let start_time = 1000000u64; - let end_time = start_time + (365 * 24 * 60 * 60); // 1 year - let step_duration = 0u64; // Linear vesting - let keeper_fee = 1000i128; - - let vault_id = client.create_vault_full( - &beneficiary, - &amount, - &start_time, - &end_time, - &keeper_fee, - &false, // revocable - &true, // transferable - &step_duration, - ); - - // Test: After 6 months (half the duration) - should have 50% vested - env.ledger().set_timestamp(start_time + (182 * 24 * 60 * 60)); // ~6 months - let claimable = client.get_claimable_amount(&vault_id); - let expected_half = amount / 2; // 50% of tokens - assert_eq!(claimable, expected_half, "Should have 50% of tokens after half the time for linear vesting"); - - // Test: After 3 months (quarter of the duration) - should have 25% vested - env.ledger().set_timestamp(start_time + (91 * 24 * 60 * 60)); // ~3 months - let claimable = client.get_claimable_amount(&vault_id); - let expected_quarter = amount / 4; // 25% of tokens - assert_eq!(claimable, expected_quarter, "Should have 25% of tokens after quarter of the time for linear vesting"); -} + // Initialize contract + let initial_supply = 1000000i128; + env.mock_all_auths(); + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); -#[test] -fn test_periodic_vesting_claim_partial() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); - - // Create addresses for testing - let admin = Address::generate(&env); - let beneficiary = Address::generate(&env); - - // Initialize contract - let initial_supply = 1000000i128; - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); - - // Set beneficiary as caller for claiming - - // Create vault with monthly vesting - let amount = 120000i128; // 120,000 tokens over 12 months = 10,000 per month - let start_time = 1000000u64; - let end_time = start_time + (365 * 24 * 60 * 60); // 1 year - let step_duration = 30 * 24 * 60 * 60; // 30 days - let keeper_fee = 100i128; - - let vault_id = client.create_vault_full( - &beneficiary, - &amount, - &start_time, - &end_time, - &keeper_fee, - &false, // revocable - &true, // transferable - &step_duration, - ); - - // Move time to 3 months - env.ledger().set_timestamp(start_time + (3 * step_duration)); - - // Claim partial amount - let claim_amount = 15000i128; // Less than the 30,000 available - let claimed = client.claim_tokens(&vault_id, &claim_amount); - assert_eq!(claimed, claim_amount, "Should claim the requested amount"); - - // Check remaining claimable - let remaining_claimable = client.get_claimable_amount(&vault_id); - assert_eq!(remaining_claimable, 15000i128, "Should have 15,000 tokens remaining claimable"); - - // Claim the rest - let final_claim = client.claim_tokens(&vault_id, &remaining_claimable); - assert_eq!(final_claim, remaining_claimable, "Should claim remaining tokens"); - - // Check no more tokens available - let no_more_claimable = client.get_claimable_amount(&vault_id); - assert_eq!(no_more_claimable, 0, "Should have no more claimable tokens"); -} + // Create vault with monthly vesting (30 days = 2,592,000 seconds) + let amount = 1200000i128; // 1,200,000 tokens over 12 months = 100,000 per month + let start_time = 1000000u64; + let end_time = start_time + (365 * 24 * 60 * 60); // 1 year + let step_duration = 30 * 24 * 60 * 60; // 30 days in seconds + let keeper_fee = 1000i128; -#[test] -fn test_vault_creation_access_control() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); - - // Create addresses for testing - let admin = Address::generate(&env); - let vault_owner = Address::generate(&env); - - // Initialize contract with admin - let initial_supply = 1000000i128; - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); - - // Test: Admin can create vaults - let vault_id = client.create_vault_full( - &vault_owner, - &1000i128, - &100u64, - &200u64, - &0i128, - &false, - &true, - &0u64, - ); - assert_eq!(vault_id, 1); -} + let vault_id = client.create_vault_full( + &beneficiary, + &amount, + &start_time, + &end_time, + &keeper_fee, + &false, // revocable + &true, // transferable + &step_duration, + ); -#[test] -fn test_batch_operations_admin_control() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); - - // Create addresses for testing - let admin = Address::generate(&env); - let unauthorized_user = Address::generate(&env); - let recipient1 = Address::generate(&env); - let recipient2 = Address::generate(&env); - - // Initialize contract with admin - let initial_supply = 1000000i128; - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); - - // Create batch data - let batch_data = BatchCreateData { - recipients: vec![&env, recipient1.clone(), recipient2.clone()], - amounts: vec![&env, 1000i128, 2000i128], - start_times: vec![&env, 100u64, 150u64], - end_times: vec![&env, 200u64, 250u64], - keeper_fees: vec![&env, 0i128, 0i128], - step_durations: vec![&env, 0u64, 0u64], - }; - -#[test] -fn test_batch_vault_creation() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); - - let admin = Address::generate(&env); - let initial_supply = 1000000i128; - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); - - let recipient1 = Address::generate(&env); - let recipient2 = Address::generate(&env); - - let batch_data = BatchCreateData { - recipients: vec![&env, recipient1, recipient2], - amounts: vec![&env, 1000i128, 2000i128], - start_times: vec![&env, 100u64, 100u64], - end_times: vec![&env, 200u64, 200u64], - keeper_fees: vec![&env, 10i128, 20i128], - step_durations: vec![&env, 0u64, 0u64], - }; - - // Test: Admin can create batch vaults - let vault_ids = client.batch_create_vaults_lazy(&batch_data); - assert_eq!(vault_ids.len(), 2); - assert_eq!(vault_ids.get(0), Some(1)); - assert_eq!(vault_ids.get(1), Some(2)); -} + // Test 1: Before start time - no vesting + env.ledger().set_timestamp(start_time - 1000); + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, 0, + "Should have no claimable tokens before start time" + ); -#[test] -fn test_milestone_unlocking_and_claim_limits() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); + // Test 2: After 15 days (less than one step) - still no vesting (rounds down) + env.ledger().set_timestamp(start_time + (15 * 24 * 60 * 60)); + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, 0, + "Should have no claimable tokens before first step completes" + ); - let admin = Address::generate(&env); - let initial_supply = 1000000i128; - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); -} + // Test 3: After exactly 30 days - one step completed + env.ledger().set_timestamp(start_time + step_duration); + let claimable = client.get_claimable_amount(&vault_id); + let expected_monthly = amount / 12; // 100,000 tokens per month + assert_eq!( + claimable, expected_monthly, + "Should have exactly one month of tokens after 30 days" + ); -#[test] -fn test_step_vesting_fuzz() { - let env = Env::default(); - let contract_id = env.register(VestingContract, ()); - let client = VestingContractClient::new(&env, &contract_id); - - let admin = Address::generate(&env); - let beneficiary = Address::generate(&env); - - let initial_supply = 1_000_000_000_000i128; - env.mock_all_auths(); - client.initialize(&admin, &initial_supply); - - // Fuzz testing with prime numbers to check for truncation errors - // Primes: 1009 (amount), 17 (step), 101 (duration) - let total_amount = 1009i128; - let start_time = 1000u64; - let duration = 101u64; // Prime duration - let end_time = start_time + duration; - let step_duration = 17u64; // Prime step - - let vault_id = client.create_vault_full( - &beneficiary, - &total_amount, - &start_time, - &end_time, - &0i128, - &true, - &true, - &step_duration, - ); - - // Advance time to end - env.ledger().with_mut(|li| { - li.timestamp = end_time + 1; - }); - - // Claim all - let claimed = client.claim_tokens(&vault_id, &total_amount); - - // Assert full amount is claimed - assert_eq!(claimed, total_amount); - - // Verify vault state - let vault = client.get_vault(&vault_id); - assert_eq!(vault.released_amount, total_amount); -} + // Test 4: After 45 days - still only one step (rounds down) + env.ledger().set_timestamp(start_time + (45 * 24 * 60 * 60)); + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, expected_monthly, + "Should still have only one month of tokens after 45 days" + ); -// Mock Staking Contract for testing cross-contract calls -#[contract] -pub struct MockStakingContract; + // Test 5: After 60 days - two steps completed + env.ledger().set_timestamp(start_time + (2 * step_duration)); + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, + 2 * expected_monthly, + "Should have two months of tokens after 60 days" + ); + + // Test 6: After 6 months - 6 steps completed + env.ledger().set_timestamp(start_time + (6 * step_duration)); + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, + 6 * expected_monthly, + "Should have six months of tokens after 6 months" + ); -#[contractimpl] -impl MockStakingContract { - pub fn stake(env: Env, vault_id: u64, amount: i128, _validator: Address) { - env.events().publish((Symbol::new(&env, "stake"), vault_id), amount); + // Test 7: After end time - all tokens vested + env.ledger().set_timestamp(end_time + 1000); + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, amount, + "Should have all tokens vested after end time" + ); + } + + #[test] + fn test_periodic_vesting_weekly_steps() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + // Create addresses for testing + let admin = Address::generate(&env); + let beneficiary = Address::generate(&env); + + // Initialize contract + let initial_supply = 1000000i128; + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); + + // Set admin as caller + + // Create vault with weekly vesting (7 days = 604,800 seconds) + let amount = 520000i128; // 520,000 tokens over 52 weeks = 10,000 per week + let start_time = 1000000u64; + let end_time = start_time + (365 * 24 * 60 * 60); // 1 year + let step_duration = 7 * 24 * 60 * 60; // 7 days in seconds + let keeper_fee = 100i128; + + let vault_id = client.create_vault_full( + &beneficiary, + &amount, + &start_time, + &end_time, + &keeper_fee, + &false, // revocable + &true, // transferable + &step_duration, + ); + + // Test: After 3 weeks - 3 steps completed + env.ledger().set_timestamp(start_time + (3 * step_duration)); + let claimable = client.get_claimable_amount(&vault_id); + let expected_weekly = 10000i128; // 10,000 tokens per week + assert_eq!( + claimable, + 3 * expected_weekly, + "Should have three weeks of tokens after 3 weeks" + ); + + // Test: After 10 weeks - 10 steps completed + env.ledger() + .set_timestamp(start_time + (10 * step_duration)); + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, + 10 * expected_weekly, + "Should have ten weeks of tokens after 10 weeks" + ); + } + + #[test] + fn test_linear_vesting_step_duration_zero() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + // Create addresses for testing + let admin = Address::generate(&env); + let beneficiary = Address::generate(&env); + + // Initialize contract + let initial_supply = 1000000i128; + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); + + // Set admin as caller + + // Create vault with linear vesting (step_duration = 0) + let amount = 1200000i128; + let start_time = 1000000u64; + let end_time = start_time + (365 * 24 * 60 * 60); // 1 year + let step_duration = 0u64; // Linear vesting + let keeper_fee = 1000i128; + + let vault_id = client.create_vault_full( + &beneficiary, + &amount, + &start_time, + &end_time, + &keeper_fee, + &false, // revocable + &true, // transferable + &step_duration, + ); + + // Test: After 6 months (half the duration) - should have 50% vested + env.ledger() + .set_timestamp(start_time + (182 * 24 * 60 * 60)); // ~6 months + let claimable = client.get_claimable_amount(&vault_id); + let expected_half = amount / 2; // 50% of tokens + assert_eq!( + claimable, expected_half, + "Should have 50% of tokens after half the time for linear vesting" + ); + + // Test: After 3 months (quarter of the duration) - should have 25% vested + env.ledger().set_timestamp(start_time + (91 * 24 * 60 * 60)); // ~3 months + let claimable = client.get_claimable_amount(&vault_id); + let expected_quarter = amount / 4; // 25% of tokens + assert_eq!( + claimable, expected_quarter, + "Should have 25% of tokens after quarter of the time for linear vesting" + ); + } + + #[test] + fn test_periodic_vesting_claim_partial() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + // Create addresses for testing + let admin = Address::generate(&env); + let beneficiary = Address::generate(&env); + + // Initialize contract + let initial_supply = 1000000i128; + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); + + // Set beneficiary as caller for claiming + + // Create vault with monthly vesting + let amount = 120000i128; // 120,000 tokens over 12 months = 10,000 per month + let start_time = 1000000u64; + let end_time = start_time + (365 * 24 * 60 * 60); // 1 year + let step_duration = 30 * 24 * 60 * 60; // 30 days + let keeper_fee = 100i128; + + let vault_id = client.create_vault_full( + &beneficiary, + &amount, + &start_time, + &end_time, + &keeper_fee, + &false, // revocable + &true, // transferable + &step_duration, + ); + + // Move time to 3 months + env.ledger().set_timestamp(start_time + (3 * step_duration)); + + // Claim partial amount + let claim_amount = 15000i128; // Less than the 30,000 available + let claimed = client.claim_tokens(&vault_id, &claim_amount); + assert_eq!(claimed, claim_amount, "Should claim the requested amount"); + + // Check remaining claimable + let remaining_claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + remaining_claimable, 15000i128, + "Should have 15,000 tokens remaining claimable" + ); + + // Claim the rest + let final_claim = client.claim_tokens(&vault_id, &remaining_claimable); + assert_eq!( + final_claim, remaining_claimable, + "Should claim remaining tokens" + ); + + // Check no more tokens available + let no_more_claimable = client.get_claimable_amount(&vault_id); + assert_eq!(no_more_claimable, 0, "Should have no more claimable tokens"); + } + + #[test] + fn test_vault_creation_access_control() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + // Create addresses for testing + let admin = Address::generate(&env); + let vault_owner = Address::generate(&env); + + // Initialize contract with admin + let initial_supply = 1000000i128; + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); + + // Test: Admin can create vaults + let vault_id = client.create_vault_full( + &vault_owner, + &1000i128, + &100u64, + &200u64, + &0i128, + &false, + &true, + &0u64, + ); + assert_eq!(vault_id, 1); + } + + #[test] + fn test_batch_operations_admin_control() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + // Create addresses for testing + let admin = Address::generate(&env); + let unauthorized_user = Address::generate(&env); + let recipient1 = Address::generate(&env); + let recipient2 = Address::generate(&env); + + // Initialize contract with admin + let initial_supply = 1000000i128; + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); + + // Create batch data + let batch_data = BatchCreateData { + recipients: vec![&env, recipient1.clone(), recipient2.clone()], + amounts: vec![&env, 1000i128, 2000i128], + start_times: vec![&env, 100u64, 150u64], + end_times: vec![&env, 200u64, 250u64], + keeper_fees: vec![&env, 0i128, 0i128], + step_durations: vec![&env, 0u64, 0u64], + }; + + #[test] + fn test_batch_vault_creation() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let initial_supply = 1000000i128; + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); + + let recipient1 = Address::generate(&env); + let recipient2 = Address::generate(&env); + + let batch_data = BatchCreateData { + recipients: vec![&env, recipient1, recipient2], + amounts: vec![&env, 1000i128, 2000i128], + start_times: vec![&env, 100u64, 100u64], + end_times: vec![&env, 200u64, 200u64], + keeper_fees: vec![&env, 10i128, 20i128], + step_durations: vec![&env, 0u64, 0u64], + }; + + // Test: Admin can create batch vaults + let vault_ids = client.batch_create_vaults_lazy(&batch_data); + assert_eq!(vault_ids.len(), 2); + assert_eq!(vault_ids.get(0), Some(1)); + assert_eq!(vault_ids.get(1), Some(2)); + } + + #[test] + fn test_milestone_unlocking_and_claim_limits() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let initial_supply = 1000000i128; + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); + } + + #[test] + fn test_step_vesting_fuzz() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let beneficiary = Address::generate(&env); + + let initial_supply = 1_000_000_000_000i128; + env.mock_all_auths(); + client.initialize(&admin, &initial_supply); + + // Fuzz testing with prime numbers to check for truncation errors + // Primes: 1009 (amount), 17 (step), 101 (duration) + let total_amount = 1009i128; + let start_time = 1000u64; + let duration = 101u64; // Prime duration + let end_time = start_time + duration; + let step_duration = 17u64; // Prime step + + let vault_id = client.create_vault_full( + &beneficiary, + &total_amount, + &start_time, + &end_time, + &0i128, + &true, + &true, + &step_duration, + ); + + // Advance time to end + env.ledger().with_mut(|li| { + li.timestamp = end_time + 1; + }); + + // Claim all + let claimed = client.claim_tokens(&vault_id, &total_amount); + + // Assert full amount is claimed + assert_eq!(claimed, total_amount); + + // Verify vault state + let vault = client.get_vault(&vault_id); + assert_eq!(vault.released_amount, total_amount); + } + + // Mock Staking Contract for testing cross-contract calls + #[contract] + pub struct MockStakingContract; + + #[contractimpl] + impl MockStakingContract { + pub fn stake(env: Env, vault_id: u64, amount: i128, _validator: Address) { + env.events() + .publish((Symbol::new(&env, "stake"), vault_id), amount); + } + } + + pub fn unstake(env: Env, vault_id: u64, amount: i128) { + env.events() + .publish((Symbol::new(&env, "unstake"), vault_id), amount); + } } -} // ------------------------------------------------------------------------- // Irrevocable vault @@ -600,6 +767,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &1_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &1_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -617,6 +792,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &1_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &1_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -636,6 +819,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &5_000i128, + &(now + 100), + &(now + 10_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &5_000i128, &(now + 100), &(now + 10_000), &0i128, &true, &false, &0u64, ); @@ -653,6 +844,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &5_000i128, + &(now + 100), + &(now + 10_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &5_000i128, &(now + 100), &(now + 10_000), &0i128, &true, &false, &0u64, ); @@ -672,12 +871,30 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &1_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &1_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); let milestones = vec![ &env, + Milestone { + id: 1, + percentage: 50, + is_unlocked: false, + }, + Milestone { + id: 2, + percentage: 50, + is_unlocked: false, + }, Milestone { id: 1, percentage: 50, is_unlocked: false }, Milestone { id: 2, percentage: 50, is_unlocked: false }, ]; @@ -700,12 +917,25 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &1_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &1_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); let milestones = vec![ &env, + Milestone { + id: 1, + percentage: 100, + is_unlocked: false, + }, Milestone { id: 1, percentage: 100, is_unlocked: false }, ]; client.set_milestones(&vault_id, &milestones); @@ -724,6 +954,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &1_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &1_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -747,6 +985,17 @@ impl MockStakingContract { let initial_supply = 1_000_000i128; let vault_amount = 10_000i128; + let vault_id = client.create_vault_full( + &beneficiary, + &10_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, + ); + assert!(client.check_invariant()); // After vault creation: admin_balance = 990_000, vault locked = 10_000 let vault_id = client.create_vault_full( &beneficiary, &vault_amount, &now, &(now + 1_000), @@ -804,6 +1053,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); client.create_vault_full( + &beneficiary, + &3_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &3_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -829,6 +1086,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &4_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &4_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -859,6 +1124,14 @@ impl MockStakingContract { for _ in 0..3 { let b = Address::generate(&env); client.create_vault_full( + &b, + &2_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &b, &2_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -885,6 +1158,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &2_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &2_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -914,6 +1195,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); let vault_id = client.create_vault_full( + &beneficiary, + &3_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &3_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -960,6 +1249,14 @@ impl MockStakingContract { let now = env.ledger().timestamp(); client.create_vault_full( + &beneficiary, + &3_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, &beneficiary, &3_000i128, &now, &(now + 1_000), &0i128, &true, &false, &0u64, ); @@ -990,6 +1287,401 @@ impl MockStakingContract { client.rescue_unallocated_tokens(&token_addr); // must panic } + + // ========================================================================= + // Yield demonstration tests + // ========================================================================= + + #[test] + fn test_yield_is_distributed_on_claim() { + let (env, contract_id, client, _admin, token) = setup(); + let beneficiary = Address::generate(&env); + let now = env.ledger().timestamp(); + + let vault_id = client.create_vault_full( + &beneficiary, + &10_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, + ); + + // Simulate yield + let stellar = token::StellarAssetClient::new(&env, &token); + stellar.mint(&contract_id, &2_000i128); + + env.ledger().with_mut(|l| l.timestamp = now + 1_001); + let claimed = client.claim_tokens(&vault_id, &10_000i128); + + assert_eq!(claimed, 12_000i128); // principal + all yield + } + + #[test] + fn test_yield_on_partial_claim() { + let (env, contract_id, client, _admin, token) = setup(); + let beneficiary = Address::generate(&env); + let now = env.ledger().timestamp(); + + let vault_id = client.create_vault_full( + &beneficiary, + &10_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, + ); + + let stellar = token::StellarAssetClient::new(&env, &token); + stellar.mint(&contract_id, &2_000i128); + + env.ledger().with_mut(|l| l.timestamp = now + 500); + let claimed = client.claim_tokens(&vault_id, &5_000i128); + + assert_eq!(claimed, 6_000i128); // 5k principal + 1k yield + } + + #[test] + fn test_yield_proportional_with_multiple_vaults() { + let (env, contract_id, client, _admin, token) = setup(); + let now = env.ledger().timestamp(); + + let v1 = client.create_vault_full( + &Address::generate(&env), + &10_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, + ); + let v2 = client.create_vault_full( + &Address::generate(&env), + &20_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, + ); + + let stellar = token::StellarAssetClient::new(&env, &token); + stellar.mint(&contract_id, &6_000i128); + + env.ledger().with_mut(|l| l.timestamp = now + 1_001); + + let claimed1 = client.claim_tokens(&v1, &10_000i128); + let claimed2 = client.claim_tokens(&v2, &20_000i128); + + assert_eq!(claimed1, 12_000i128); + assert_eq!(claimed2, 24_000i128); + } + + #[test] + #[should_panic(expected = "Cannot rescue yield-bearing token")] + fn test_rescue_yield_token_panics() { + let (env, _cid, client, _admin, token) = setup(); + client.rescue_unallocated_tokens(&token); + } + + // ------------------------------------------------------------------------- + // Zero-duration vault fuzz tests (Issue #41) + // ------------------------------------------------------------------------- + + #[test] + fn test_zero_duration_vault_immediate_unlock() { + let (env, _cid, client, _admin) = setup(); + let beneficiary = Address::generate(&env); + let now = env.ledger().timestamp(); + + let vault_id = client.create_vault_full( + &beneficiary, + &5_000i128, + &now, + &now, + &0i128, + &true, + &false, + &0u64, + ); + + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, 5_000i128, + "zero-duration vault should unlock 100% immediately" + ); + } + + #[test] + fn test_zero_duration_vault_claim_full() { + let (env, _cid, client, _admin) = setup(); + let beneficiary = Address::generate(&env); + let now = env.ledger().timestamp(); + + let vault_id = client.create_vault_full( + &beneficiary, + &10_000i128, + &now, + &now, + &0i128, + &true, + &false, + &0u64, + ); + + let claimed = client.claim_tokens(&vault_id, &10_000i128); + assert_eq!( + claimed, 10_000i128, + "should claim full amount from zero-duration vault" + ); + + let vault = client.get_vault(&vault_id); + assert_eq!(vault.released_amount, 10_000i128); + } + + #[test] + fn test_zero_duration_vault_before_start() { + let (env, _cid, client, _admin) = setup(); + let beneficiary = Address::generate(&env); + let future = env.ledger().timestamp() + 1_000; + + let vault_id = client.create_vault_full( + &beneficiary, + &5_000i128, + &future, + &future, + &0i128, + &true, + &false, + &0u64, + ); + + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, 0, + "zero-duration vault should not unlock before start_time" + ); + } + + #[test] + fn test_zero_cliff_vault_vests_immediately() { + let (env, _cid, client, _admin) = setup(); + let beneficiary = Address::generate(&env); + let now = env.ledger().timestamp(); + + let vault_id = client.create_vault_full( + &beneficiary, + &10_000i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, + ); + + env.ledger().with_mut(|l| l.timestamp = now + 500); + let claimable = client.get_claimable_amount(&vault_id); + assert!( + claimable > 0, + "zero-cliff vault should vest from start_time" + ); + } + + #[test] + fn test_zero_amount_vault_no_claimable() { + let (env, _cid, client, _admin) = setup(); + let beneficiary = Address::generate(&env); + let now = env.ledger().timestamp(); + + let vault_id = client.create_vault_full( + &beneficiary, + &0i128, + &now, + &(now + 1_000), + &0i128, + &true, + &false, + &0u64, + ); + + env.ledger().with_mut(|l| l.timestamp = now + 1_001); + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, 0, + "zero-amount vault should have nothing claimable" + ); + } + + #[test] + fn test_zero_duration_zero_amount_vault() { + let (env, _cid, client, _admin) = setup(); + let beneficiary = Address::generate(&env); + let now = env.ledger().timestamp(); + + let vault_id = client.create_vault_full( + &beneficiary, + &0i128, + &now, + &now, + &0i128, + &true, + &false, + &0u64, + ); + + let claimable = client.get_claimable_amount(&vault_id); + assert_eq!( + claimable, 0, + "zero-duration + zero-amount vault should have nothing claimable" + ); + } + + #[test] + fn test_vault_start_time_immutable() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let initial_supply = 1000000i128; + client.initialize(&admin, &initial_supply); + + // Create a vault + let owner = Address::generate(&env); + let amount = 1000i128; + let start_time = 123456789u64; + let end_time = start_time + 10000; + let keeper_fee = 10i128; + let is_revocable = false; + let is_transferable = false; + let step_duration = 0u64; + let vault_id = client.create_vault( + &owner, + &amount, + &start_time, + &end_time, + &keeper_fee, + &is_revocable, + &is_transferable, + &step_duration, + ); + + // Try to change start_time or cliff_duration (should not be possible) + let vault = client.get_vault(&vault_id); + let original_start_time = vault.start_time; + let original_cliff_duration = vault.cliff_duration; + + // Attempt to update vault via admin functions (should not affect start_time/cliff_duration) + client.mark_irrevocable(&vault_id); + client.transfer_beneficiary(&vault_id, &Address::generate(&env)); + client.set_delegate(&vault_id, &Some(Address::generate(&env))); + + let updated_vault = client.get_vault(&vault_id); + assert_eq!(updated_vault.start_time, original_start_time); + assert_eq!(updated_vault.cliff_duration, original_cliff_duration); + } + + #[test] + fn test_global_pause_functionality() { + let env = Env::default(); + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(&env, &contract_id); + + // Create addresses for testing + let admin = Address::generate(&env); + let beneficiary = Address::generate(&env); + let unauthorized_user = Address::generate(&env); + + // Initialize contract with admin + let initial_supply = 1000000i128; + client.initialize(&admin, &initial_supply); + + // Verify initial state is unpaused + assert_eq!(client.is_paused(), false); + + // Test: Unauthorized user cannot toggle pause + env.as_contract(&contract_id, || { + env.current_contract_address().set(&unauthorized_user); + }); + + let result = std::panic::catch_unwind(|| { + client.toggle_pause(); + }); + assert!(result.is_err()); + assert_eq!(client.is_paused(), false); // Should still be unpaused + + // Test: Admin can pause the contract + env.as_contract(&contract_id, || { + env.current_contract_address().set(&admin); + }); + + client.toggle_pause(); + assert_eq!(client.is_paused(), true); // Should now be paused + + // Create a vault for testing claims + let now = env.ledger().timestamp(); + let vault_id = client.create_vault_full( + &beneficiary, + &1000i128, + &now, + &(now + 1000), + &0i128, + &false, + &true, + &0u64, + ); + + // Move time to make tokens claimable + env.ledger().set_timestamp(now + 1001); + + // Set beneficiary as caller + env.as_contract(&contract_id, || { + env.current_contract_address().set(&beneficiary); + }); + + // Test: Claims should fail when paused + let result = std::panic::catch_unwind(|| { + client.claim_tokens(&vault_id, &100i128); + }); + assert!(result.is_err()); + + // Test: Delegate claims should also fail when paused + let delegate = Address::generate(&env); + client.set_delegate(&vault_id, &Some(delegate.clone())); + + env.as_contract(&contract_id, || { + env.current_contract_address().set(&delegate); + }); + + let result = std::panic::catch_unwind(|| { + client.claim_as_delegate(&vault_id, &100i128); + }); + assert!(result.is_err()); + + // Test: Admin can unpause the contract + env.as_contract(&contract_id, || { + env.current_contract_address().set(&admin); + }); + + client.toggle_pause(); + assert_eq!(client.is_paused(), false); // Should be unpaused + + // Test: Claims should work after unpausing + env.as_contract(&contract_id, || { + env.current_contract_address().set(&beneficiary); + }); + + let claimed = client.claim_tokens(&vault_id, &100i128); + assert_eq!(claimed, 100i128); // Should succeed + } +} } } diff --git a/contracts/vesting_contracts/tests/max_duration.rs b/contracts/vesting_contracts/tests/max_duration.rs new file mode 100644 index 0000000..0a45e6e --- /dev/null +++ b/contracts/vesting_contracts/tests/max_duration.rs @@ -0,0 +1,102 @@ +use soroban_sdk::{testutils::Address as _, vec, Address, Env}; + +use vesting_contracts::{BatchCreateData, VestingContract, VestingContractClient, MAX_DURATION}; + +fn setup(env: &Env) -> (VestingContractClient<'static>, Address) { + env.mock_all_auths(); + + let contract_id = env.register(VestingContract, ()); + let client = VestingContractClient::new(env, &contract_id); + + let admin = Address::generate(env); + client.initialize(&admin, &1_000_000i128); + + (client, admin) +} + +#[test] +fn create_vault_full_allows_max_duration() { + let env = Env::default(); + let (client, _admin) = setup(&env); + + let beneficiary = Address::generate(&env); + let start = env.ledger().timestamp(); + let end = start + MAX_DURATION; + + client.create_vault_full( + &beneficiary, + &1_000i128, + &start, + &end, + &0i128, + &true, + &false, + &0u64, + ); +} + +#[test] +#[should_panic(expected = "duration exceeds MAX_DURATION")] +fn create_vault_full_rejects_over_max_duration() { + let env = Env::default(); + let (client, _admin) = setup(&env); + + let beneficiary = Address::generate(&env); + let start = env.ledger().timestamp(); + let end = start + MAX_DURATION + 1; + + client.create_vault_full( + &beneficiary, + &1_000i128, + &start, + &end, + &0i128, + &true, + &false, + &0u64, + ); +} + +#[test] +#[should_panic(expected = "duration exceeds MAX_DURATION")] +fn create_vault_lazy_rejects_over_max_duration() { + let env = Env::default(); + let (client, _admin) = setup(&env); + + let beneficiary = Address::generate(&env); + let start = env.ledger().timestamp(); + let end = start + MAX_DURATION + 1; + + client.create_vault_lazy( + &beneficiary, + &1_000i128, + &start, + &end, + &0i128, + &true, + &false, + &0u64, + ); +} + +#[test] +#[should_panic(expected = "duration exceeds MAX_DURATION")] +fn batch_create_vaults_rejects_over_max_duration() { + let env = Env::default(); + let (client, _admin) = setup(&env); + + let recipient = Address::generate(&env); + let start = 100u64; + let end = start + MAX_DURATION + 1; + + let batch = BatchCreateData { + recipients: vec![&env, recipient], + amounts: vec![&env, 1_000i128], + start_times: vec![&env, start], + end_times: vec![&env, end], + keeper_fees: vec![&env, 0i128], + step_durations: vec![&env, 0u64], + }; + + client.batch_create_vaults_lazy(&batch); +} diff --git a/contracts/vesting_curves/Cargo.lock b/contracts/vesting_curves/Cargo.lock new file mode 100644 index 0000000..c832c29 --- /dev/null +++ b/contracts/vesting_curves/Cargo.lock @@ -0,0 +1,1691 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytes-lit" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adabf37211a5276e46335feabcbb1530c95eb3fdf85f324c7db942770aa025d" +dependencies = [ + "num-bigint", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "cc" +version = "1.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.114", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.114", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.114", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.0", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2e42bf80fcdefb3aae6ff3c7101a62cf942e95320ed5b518a1705bc11c6b2f" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "soroban-env-common" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "027cd856171bfd6ad2c0ffb3b7dfe55ad7080fb3050c36ad20970f80da634472" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a07dda1ae5220d975979b19ad4fd56bc86ec7ec1b4b25bc1c5d403f934e592e" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e8b03a4191d485eab03f066336112b2a50541a7553179553dc838b986b94dd" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "22.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00eff744764ade3bc480e4909e3a581a240091f3d262acdce80b41f7069b2bd9" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.114", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "22.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801272da800bd000bd44c4d9d99f73e36e756b5f646a355c062efc33425c7119" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "22.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062ec196246f0ad2429ae256b9387efc6c84dfc26ab409c20f8c8d4b7da3cd1c" +dependencies = [ + "arbitrary", + "bytes-lit", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey", +] + +[[package]] +name = "soroban-sdk-macros" +version = "22.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562372e2806f3d4fe450c7d1a8d8b79140c60494969812089bb6e36f66050ffe" +dependencies = [ + "crate-git-revision", + "darling 0.20.11", + "itertools", + "proc-macro2", + "quote", + "rustc_version", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.114", +] + +[[package]] +name = "soroban-spec" +version = "22.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14311180c5678a5bf1b7b13c414784ceb4551b1caf70c1293a720910bd1df81b" +dependencies = [ + "base64 0.13.1", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "22.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a8a990c2d455cf5ab65f8156ff0145c9dbb4b0f5ba38c3da1fd69ee5abc2f7" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.114", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stellar-strkey" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3aa3ed00e70082cb43febc1c2afa5056b9bb3e348bbb43d0cd0aa88a611144" +dependencies = [ + "crate-git-revision", + "data-encoding", + "thiserror", +] + +[[package]] +name = "stellar-xdr" +version = "22.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce69db907e64d1e70a3dce8d4824655d154749426a6132b25395c49136013e4" +dependencies = [ + "arbitrary", + "base64 0.13.1", + "crate-git-revision", + "escape-bytes", + "hex", + "serde", + "serde_with", + "stellar-strkey", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "time" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vesting-vault" +version = "0.0.0" +dependencies = [ + "soroban-sdk", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.114", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.13.0", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dafd85c832c1b68bbb4ec0c72c7f6f4fc5179627d2bc7c26b30e4c0cc11e76cc" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cb7e4e8436d9db52fbd6625dbf2f45243ab84994a72882ec8227b99e72b439a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "zmij" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" diff --git a/contracts/vesting_curves/Cargo.toml b/contracts/vesting_curves/Cargo.toml index c11052a..a6cdf46 100644 --- a/contracts/vesting_curves/Cargo.toml +++ b/contracts/vesting_curves/Cargo.toml @@ -5,6 +5,9 @@ members = ["contracts/vesting-vault"] [workspace.dependencies] soroban-sdk = "22.0.0" +[patch.crates-io] +keccak = { path = "vendor/keccak-0.1.5" } + [profile.release] opt-level = "z" overflow-checks = true @@ -17,4 +20,4 @@ lto = true [profile.release-with-logs] inherits = "release" -debug-assertions = true \ No newline at end of file +debug-assertions = true diff --git a/contracts/vesting_curves/contracts/vesting-vault/Cargo.toml b/contracts/vesting_curves/contracts/vesting-vault/Cargo.toml new file mode 100644 index 0000000..f94be8b --- /dev/null +++ b/contracts/vesting_curves/contracts/vesting-vault/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "vesting-vault" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["lib", "cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/contracts/vesting_curves/contracts/vesting-vault/Makefile b/contracts/vesting_curves/contracts/vesting-vault/Makefile new file mode 100644 index 0000000..b971934 --- /dev/null +++ b/contracts/vesting_curves/contracts/vesting-vault/Makefile @@ -0,0 +1,16 @@ +default: build + +all: test + +test: build + cargo test + +build: + stellar contract build + @ls -l target/wasm32v1-none/release/*.wasm + +fmt: + cargo fmt --all + +clean: + cargo clean diff --git a/contracts/vesting_curves/src/lib.rs b/contracts/vesting_curves/contracts/vesting-vault/src/lib.rs similarity index 96% rename from contracts/vesting_curves/src/lib.rs rename to contracts/vesting_curves/contracts/vesting-vault/src/lib.rs index 6d6ae65..08350a2 100644 --- a/contracts/vesting_curves/src/lib.rs +++ b/contracts/vesting_curves/contracts/vesting-vault/src/lib.rs @@ -1,4 +1,3 @@ - #![no_std] use soroban_sdk::{ @@ -17,6 +16,9 @@ const START: Symbol = symbol_short!("START"); const DURATION: Symbol = symbol_short!("DURATION"); const CURVE: Symbol = symbol_short!("CURVE"); +// 10 years in seconds (Issue #44) +const MAX_DURATION: u64 = 315_360_000; + #[contracttype] #[derive(Clone, Debug, PartialEq)] pub enum VestingCurve { @@ -54,6 +56,7 @@ impl VestingVault { assert!(total_amount > 0, "total_amount must be positive"); assert!(duration > 0, "duration must be positive"); + assert!(duration <= MAX_DURATION, "duration exceeds MAX_DURATION"); admin.require_auth(); @@ -98,12 +101,7 @@ impl VestingVault { } match curve { - - VestingCurve::Linear => { - - (total * elapsed as i128) / duration as i128 - } - + VestingCurve::Linear => (total * elapsed as i128) / duration as i128, VestingCurve::Exponential => { let elapsed_u128 = elapsed as u128; let duration_u128 = duration as u128; @@ -181,4 +179,4 @@ impl VestingVault { // --------------------------------------------------------------------------- #[cfg(test)] -mod test; \ No newline at end of file +mod test; diff --git a/contracts/vesting_curves/src/test.rs b/contracts/vesting_curves/contracts/vesting-vault/src/test.rs similarity index 84% rename from contracts/vesting_curves/src/test.rs rename to contracts/vesting_curves/contracts/vesting-vault/src/test.rs index 3daf30f..e3d7809 100644 --- a/contracts/vesting_curves/src/test.rs +++ b/contracts/vesting_curves/contracts/vesting-vault/src/test.rs @@ -1,4 +1,3 @@ - #![cfg(test)] extern crate std; @@ -23,7 +22,6 @@ struct Setup { env: Env, vault: VestingVaultClient<'static>, token: Address, - admin: Address, beneficiary: Address, } @@ -61,7 +59,7 @@ fn create_setup(curve: VestingCurve) -> Setup { &curve, ); - Setup { env, vault, token, admin, beneficiary } + Setup { env, vault, token, beneficiary } } // --------------------------------------------------------------------------- @@ -276,7 +274,6 @@ fn z1_zero_duration_panics() { let vault_id = env.register(crate::VestingVault, ()); let vault = VestingVaultClient::new(&env, &vault_id); TokenClient::new(&env, &token).transfer(&admin, &vault_id, &TOTAL); - env.ledger().with_mut(|l| l.timestamp = START); vault.initialize( @@ -338,7 +335,6 @@ fn z3_zero_duration_exponential_panics() { TokenClient::new(&env, &token).transfer(&admin, &vault_id, &TOTAL); env.ledger().with_mut(|l| l.timestamp = START); - vault.initialize( &admin, &beneficiary, @@ -348,4 +344,71 @@ fn z3_zero_duration_exponential_panics() { &0u64, &VestingCurve::Exponential, ); -} \ No newline at end of file +} + +// ── Duration cap (Issue #44) ──────────────────────────────────────────────── + +#[test] +fn i7_initialize_allows_max_duration() { + let env = Env::default(); + env.mock_all_auths(); + + let admin = Address::generate(&env); + let beneficiary = Address::generate(&env); + let token_admin = Address::generate(&env); + + let token_id = env.register_stellar_asset_contract_v2(token_admin.clone()); + let token = token_id.address(); + StellarAssetClient::new(&env, &token).mint(&admin, &TOTAL); + + let vault_id = env.register(crate::VestingVault, ()); + let vault = VestingVaultClient::new(&env, &vault_id); + + TokenClient::new(&env, &token).transfer(&admin, &vault_id, &TOTAL); + + env.ledger().with_mut(|l| l.timestamp = START); + + vault.initialize( + &admin, + &beneficiary, + &token, + &TOTAL, + &START, + &crate::MAX_DURATION, + &VestingCurve::Linear, + ); + assert_eq!(vault.get_curve(), VestingCurve::Linear); +} + +#[test] +#[should_panic(expected = "duration exceeds MAX_DURATION")] +fn i8_initialize_rejects_duration_over_max() { + let env = Env::default(); + env.mock_all_auths(); + + let admin = Address::generate(&env); + let beneficiary = Address::generate(&env); + let token_admin = Address::generate(&env); + + let token_id = env.register_stellar_asset_contract_v2(token_admin.clone()); + let token = token_id.address(); + StellarAssetClient::new(&env, &token).mint(&admin, &TOTAL); + + let vault_id = env.register(crate::VestingVault, ()); + let vault = VestingVaultClient::new(&env, &vault_id); + + TokenClient::new(&env, &token).transfer(&admin, &vault_id, &TOTAL); + env.ledger().with_mut(|l| l.timestamp = START); + + let too_long = crate::MAX_DURATION + 1; + + vault.initialize( + &admin, + &beneficiary, + &token, + &TOTAL, + &START, + &too_long, + &VestingCurve::Linear, + ); +} diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/.cargo-ok b/contracts/vesting_curves/vendor/keccak-0.1.5/.cargo-ok new file mode 100644 index 0000000..5f8b795 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/.cargo_vcs_info.json b/contracts/vesting_curves/vendor/keccak-0.1.5/.cargo_vcs_info.json new file mode 100644 index 0000000..4a71c96 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "64a5bebb67c943933157e77a6cc62eac36b87d12" + }, + "path_in_vcs": "keccak" +} \ No newline at end of file diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/CHANGELOG.md b/contracts/vesting_curves/vendor/keccak-0.1.5/CHANGELOG.md new file mode 100644 index 0000000..01a8a68 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/CHANGELOG.md @@ -0,0 +1,45 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.5 (2024-01-12) +### Changed +- Enable ARMv8 ASM backend for `p1600` ([#68]) + +[#68]: https://github.com/RustCrypto/sponges/pull/68 + +## 0.1.4 (2023-05-04) +### Added +- `keccak_p` fns for `[200, 400, 800, 1600]` ([#55]) + +### Changed +- 2018 edition upgrade ([#32]) + +[#32]: https://github.com/RustCrypto/sponges/pull/32 +[#55]: https://github.com/RustCrypto/sponges/pull/55 + +## 0.1.3 (2022-11-14) +### Added +- ARMv8 SHA3 ASM intrinsics implementation for `keccak_f1600` ([#23]) + +[#23]: https://github.com/RustCrypto/sponges/pull/23 + +## 0.1.2 (2022-05-24) +### Changed +- Implement `simd` feature with `portable_simd` instead of deprecated `packed_simd` ([#16]) + +[#16]: https://github.com/RustCrypto/sponges/pull/16 + +## 0.1.1 (2022-05-24) +### Added +- Generic keccak-p and keccak-f {200, 400, 800} ([#7]) +- f1600x{2, 4, 8} ([#8]) + +[#7]: https://github.com/RustCrypto/sponges/pull/7 +[#8]: https://github.com/RustCrypto/sponges/pull/8 + +## 0.1.0 (2018-03-27) +- Initial release diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/Cargo.toml b/contracts/vesting_curves/vendor/keccak-0.1.5/Cargo.toml new file mode 100644 index 0000000..6d7ae31 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/Cargo.toml @@ -0,0 +1,43 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "keccak" +version = "0.1.5" +authors = ["RustCrypto Developers"] +description = """ +Pure Rust implementation of the Keccak sponge function including the keccak-f +and keccak-p variants +""" +documentation = "https://docs.rs/keccak" +readme = "README.md" +keywords = [ + "crypto", + "sponge", + "keccak", + "keccak-f", + "keccak-p", +] +categories = [ + "cryptography", + "no-std", +] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/RustCrypto/sponges/tree/master/keccak" + +[features] +asm = [] +no_unroll = [] +simd = [] + +[target."cfg(target_arch = \"aarch64\")".dependencies.cpufeatures] +version = "0.2" diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/Cargo.toml.orig b/contracts/vesting_curves/vendor/keccak-0.1.5/Cargo.toml.orig new file mode 100644 index 0000000..8a87110 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/Cargo.toml.orig @@ -0,0 +1,23 @@ +[package] +name = "keccak" +version = "0.1.5" +description = """ +Pure Rust implementation of the Keccak sponge function including the keccak-f +and keccak-p variants +""" +authors = ["RustCrypto Developers"] +license = "Apache-2.0 OR MIT" +documentation = "https://docs.rs/keccak" +repository = "https://github.com/RustCrypto/sponges/tree/master/keccak" +keywords = ["crypto", "sponge", "keccak", "keccak-f", "keccak-p"] +categories = ["cryptography", "no-std"] +readme = "README.md" +edition = "2018" + +[features] +asm = [] # Use optimized assembly when available (currently only ARMv8) +no_unroll = [] # Do no unroll loops for binary size reduction +simd = [] # Use core::simd (nightly-only) + +[target.'cfg(target_arch = "aarch64")'.dependencies] +cpufeatures = "0.2" diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/LICENSE-APACHE b/contracts/vesting_curves/vendor/keccak-0.1.5/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/LICENSE-MIT b/contracts/vesting_curves/vendor/keccak-0.1.5/LICENSE-MIT new file mode 100644 index 0000000..81a3d57 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018-2022 RustCrypto Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/README.md b/contracts/vesting_curves/vendor/keccak-0.1.5/README.md new file mode 100644 index 0000000..989c311 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/README.md @@ -0,0 +1,66 @@ +# RustCrypto: Keccak Sponge Function + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +[![Build Status][build-image]][build-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] + +Pure Rust implementation of the [Keccak Sponge Function][1] including the keccak-f +and keccak-p variants. + +[Documentation][docs-link] + +## About + +This crate implements the core Keccak sponge function, upon which many other +cryptographic functions are built. + +For the SHA-3 family including the SHAKE XOFs, see the [`sha3`] crate, which +is built on this crate. + +## Minimum Supported Rust Version + +Rust **1.41** or higher by default, or **1.59** with the `asm` feature enabled. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/keccak.svg +[crate-link]: https://crates.io/crates/keccak +[docs-image]: https://docs.rs/keccak/badge.svg +[docs-link]: https://docs.rs/keccak/ +[build-image]: https://github.com/RustCrypto/sponges/actions/workflows/keccak.yml/badge.svg +[build-link]: https://github.com/RustCrypto/sponges/actions/workflows/keccak.yml +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/369879-sponges + +[//]: # (general links) + +[1]: https://keccak.team/keccak.html +[`sha3`]: https://github.com/RustCrypto/hashes/tree/master/sha3 diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/benches/mod.rs b/contracts/vesting_curves/vendor/keccak-0.1.5/benches/mod.rs new file mode 100644 index 0000000..960c40a --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/benches/mod.rs @@ -0,0 +1,43 @@ +#![feature(test)] +#![cfg_attr(feature = "simd", feature(portable_simd))] + +extern crate keccak; +extern crate test; + +use keccak::{f1600, f200, f400, f800, p1600}; + +macro_rules! impl_bench { + ($name:ident, $fn:ident, $type:expr) => { + #[bench] + fn $name(b: &mut test::Bencher) { + let mut data = [$type; 25]; + b.iter(|| $fn(&mut data)); + } + }; +} + +impl_bench!(b_f200, f200, 0u8); +impl_bench!(b_f400, f400, 0u16); +impl_bench!(b_f800, f800, 0u32); +impl_bench!(b_f1600, f1600, 0u64); + +#[bench] +fn b_p1600_24(b: &mut test::Bencher) { + let mut data = [0u64; 25]; + b.iter(|| p1600(&mut data, 24)); +} + +#[bench] +fn b_p1600_16(b: &mut test::Bencher) { + let mut data = [0u64; 25]; + b.iter(|| p1600(&mut data, 16)); +} + +#[cfg(feature = "simd")] +mod simd { + use keccak::simd::{f1600x2, f1600x4, f1600x8, u64x2, u64x4, u64x8}; + + impl_bench!(b_f1600x2, f1600x2, u64x2::splat(0)); + impl_bench!(b_f1600x4, f1600x4, u64x4::splat(0)); + impl_bench!(b_f1600x8, f1600x8, u64x8::splat(0)); +} diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/src/armv8.rs b/contracts/vesting_curves/vendor/keccak-0.1.5/src/armv8.rs new file mode 100644 index 0000000..698c8a1 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/src/armv8.rs @@ -0,0 +1,192 @@ +/// Keccak-p1600 on ARMv8.4-A with FEAT_SHA3. +/// +/// See p. K12.2.2 p. 11,749 of the ARM Reference manual. +/// Adapted from the Keccak-f1600 implementation in the XKCP/K12. +/// see +#[target_feature(enable = "sha3")] +pub unsafe fn p1600_armv8_sha3_asm(state: &mut [u64; 25], round_count: usize) { + core::arch::asm!(" + // Read state + ld1.1d {{ v0- v3}}, [x0], #32 + ld1.1d {{ v4- v7}}, [x0], #32 + ld1.1d {{ v8-v11}}, [x0], #32 + ld1.1d {{v12-v15}}, [x0], #32 + ld1.1d {{v16-v19}}, [x0], #32 + ld1.1d {{v20-v23}}, [x0], #32 + ld1.1d {{v24}}, [x0] + sub x0, x0, #192 + + // NOTE: This loop actually computes two f1600 functions in + // parallel, in both the lower and the upper 64-bit of the + // 128-bit registers v0-v24. + 0: sub x8, x8, #1 + + // Theta Calculations + eor3.16b v25, v20, v15, v10 + eor3.16b v26, v21, v16, v11 + eor3.16b v27, v22, v17, v12 + eor3.16b v28, v23, v18, v13 + eor3.16b v29, v24, v19, v14 + eor3.16b v25, v25, v5, v0 + eor3.16b v26, v26, v6, v1 + eor3.16b v27, v27, v7, v2 + eor3.16b v28, v28, v8, v3 + eor3.16b v29, v29, v9, v4 + rax1.2d v30, v25, v27 + rax1.2d v31, v26, v28 + rax1.2d v27, v27, v29 + rax1.2d v28, v28, v25 + rax1.2d v29, v29, v26 + + // Rho and Phi + eor.16b v0, v0, v29 + xar.2d v25, v1, v30, #64 - 1 + xar.2d v1, v6, v30, #64 - 44 + xar.2d v6, v9, v28, #64 - 20 + xar.2d v9, v22, v31, #64 - 61 + xar.2d v22, v14, v28, #64 - 39 + xar.2d v14, v20, v29, #64 - 18 + xar.2d v26, v2, v31, #64 - 62 + xar.2d v2, v12, v31, #64 - 43 + xar.2d v12, v13, v27, #64 - 25 + xar.2d v13, v19, v28, #64 - 8 + xar.2d v19, v23, v27, #64 - 56 + xar.2d v23, v15, v29, #64 - 41 + xar.2d v15, v4, v28, #64 - 27 + xar.2d v28, v24, v28, #64 - 14 + xar.2d v24, v21, v30, #64 - 2 + xar.2d v8, v8, v27, #64 - 55 + xar.2d v4, v16, v30, #64 - 45 + xar.2d v16, v5, v29, #64 - 36 + xar.2d v5, v3, v27, #64 - 28 + xar.2d v27, v18, v27, #64 - 21 + xar.2d v3, v17, v31, #64 - 15 + xar.2d v30, v11, v30, #64 - 10 + xar.2d v31, v7, v31, #64 - 6 + xar.2d v29, v10, v29, #64 - 3 + + // Chi and Iota + bcax.16b v20, v26, v22, v8 + bcax.16b v21, v8, v23, v22 + bcax.16b v22, v22, v24, v23 + bcax.16b v23, v23, v26, v24 + bcax.16b v24, v24, v8, v26 + + ld1r.2d {{v26}}, [x1], #8 + + bcax.16b v17, v30, v19, v3 + bcax.16b v18, v3, v15, v19 + bcax.16b v19, v19, v16, v15 + bcax.16b v15, v15, v30, v16 + bcax.16b v16, v16, v3, v30 + + bcax.16b v10, v25, v12, v31 + bcax.16b v11, v31, v13, v12 + bcax.16b v12, v12, v14, v13 + bcax.16b v13, v13, v25, v14 + bcax.16b v14, v14, v31, v25 + + bcax.16b v7, v29, v9, v4 + bcax.16b v8, v4, v5, v9 + bcax.16b v9, v9, v6, v5 + bcax.16b v5, v5, v29, v6 + bcax.16b v6, v6, v4, v29 + + bcax.16b v3, v27, v0, v28 + bcax.16b v4, v28, v1, v0 + bcax.16b v0, v0, v2, v1 + bcax.16b v1, v1, v27, v2 + bcax.16b v2, v2, v28, v27 + + eor.16b v0,v0,v26 + + // Rounds loop + cbnz w8, 0b + + // Write state + st1.1d {{ v0- v3}}, [x0], #32 + st1.1d {{ v4- v7}}, [x0], #32 + st1.1d {{ v8-v11}}, [x0], #32 + st1.1d {{v12-v15}}, [x0], #32 + st1.1d {{v16-v19}}, [x0], #32 + st1.1d {{v20-v23}}, [x0], #32 + st1.1d {{v24}}, [x0] + ", + in("x0") state.as_mut_ptr(), + in("x1") crate::RC[24-round_count..].as_ptr(), + in("x8") round_count, + clobber_abi("C"), + options(nostack) + ); +} + +#[cfg(all(test, target_feature = "sha3"))] +mod tests { + use super::*; + + #[test] + fn test_keccak_f1600() { + // Test vectors are copied from XKCP (eXtended Keccak Code Package) + // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt + let state_first = [ + 0xF1258F7940E1DDE7, + 0x84D5CCF933C0478A, + 0xD598261EA65AA9EE, + 0xBD1547306F80494D, + 0x8B284E056253D057, + 0xFF97A42D7F8E6FD4, + 0x90FEE5A0A44647C4, + 0x8C5BDA0CD6192E76, + 0xAD30A6F71B19059C, + 0x30935AB7D08FFC64, + 0xEB5AA93F2317D635, + 0xA9A6E6260D712103, + 0x81A57C16DBCF555F, + 0x43B831CD0347C826, + 0x01F22F1A11A5569F, + 0x05E5635A21D9AE61, + 0x64BEFEF28CC970F2, + 0x613670957BC46611, + 0xB87C5A554FD00ECB, + 0x8C3EE88A1CCF32C8, + 0x940C7922AE3A2614, + 0x1841F924A2C509E4, + 0x16F53526E70465C2, + 0x75F644E97F30A13B, + 0xEAF1FF7B5CECA249, + ]; + let state_second = [ + 0x2D5C954DF96ECB3C, + 0x6A332CD07057B56D, + 0x093D8D1270D76B6C, + 0x8A20D9B25569D094, + 0x4F9C4F99E5E7F156, + 0xF957B9A2DA65FB38, + 0x85773DAE1275AF0D, + 0xFAF4F247C3D810F7, + 0x1F1B9EE6F79A8759, + 0xE4FECC0FEE98B425, + 0x68CE61B6B9CE68A1, + 0xDEEA66C4BA8F974F, + 0x33C43D836EAFB1F5, + 0xE00654042719DBD9, + 0x7CF8A9F009831265, + 0xFD5449A6BF174743, + 0x97DDAD33D8994B40, + 0x48EAD5FC5D0BE774, + 0xE3B8C8EE55B7B03C, + 0x91A0226E649E42E9, + 0x900E3129E7BADD7B, + 0x202A9EC5FAA3CCE8, + 0x5B3402464E1C3DB6, + 0x609F4E62A44C1059, + 0x20D06CD26A8FBF5C, + ]; + + let mut state = [0u64; 25]; + unsafe { p1600_armv8_sha3_asm(&mut state, 24) }; + assert_eq!(state, state_first); + unsafe { p1600_armv8_sha3_asm(&mut state, 24) }; + assert_eq!(state, state_second); + } +} diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/src/lib.rs b/contracts/vesting_curves/vendor/keccak-0.1.5/src/lib.rs new file mode 100644 index 0000000..dc18ea3 --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/src/lib.rs @@ -0,0 +1,498 @@ +//! Keccak [sponge function](https://en.wikipedia.org/wiki/Sponge_function). +//! +//! If you are looking for SHA-3 hash functions take a look at [`sha3`][1] and +//! [`tiny-keccak`][2] crates. +//! +//! To disable loop unrolling (e.g. for constraint targets) use `no_unroll` +//! feature. +//! +//! ``` +//! // Test vectors are from KeccakCodePackage +//! let mut data = [0u64; 25]; +//! +//! keccak::f1600(&mut data); +//! assert_eq!(data, [ +//! 0xF1258F7940E1DDE7, 0x84D5CCF933C0478A, 0xD598261EA65AA9EE, 0xBD1547306F80494D, +//! 0x8B284E056253D057, 0xFF97A42D7F8E6FD4, 0x90FEE5A0A44647C4, 0x8C5BDA0CD6192E76, +//! 0xAD30A6F71B19059C, 0x30935AB7D08FFC64, 0xEB5AA93F2317D635, 0xA9A6E6260D712103, +//! 0x81A57C16DBCF555F, 0x43B831CD0347C826, 0x01F22F1A11A5569F, 0x05E5635A21D9AE61, +//! 0x64BEFEF28CC970F2, 0x613670957BC46611, 0xB87C5A554FD00ECB, 0x8C3EE88A1CCF32C8, +//! 0x940C7922AE3A2614, 0x1841F924A2C509E4, 0x16F53526E70465C2, 0x75F644E97F30A13B, +//! 0xEAF1FF7B5CECA249, +//! ]); +//! +//! keccak::f1600(&mut data); +//! assert_eq!(data, [ +//! 0x2D5C954DF96ECB3C, 0x6A332CD07057B56D, 0x093D8D1270D76B6C, 0x8A20D9B25569D094, +//! 0x4F9C4F99E5E7F156, 0xF957B9A2DA65FB38, 0x85773DAE1275AF0D, 0xFAF4F247C3D810F7, +//! 0x1F1B9EE6F79A8759, 0xE4FECC0FEE98B425, 0x68CE61B6B9CE68A1, 0xDEEA66C4BA8F974F, +//! 0x33C43D836EAFB1F5, 0xE00654042719DBD9, 0x7CF8A9F009831265, 0xFD5449A6BF174743, +//! 0x97DDAD33D8994B40, 0x48EAD5FC5D0BE774, 0xE3B8C8EE55B7B03C, 0x91A0226E649E42E9, +//! 0x900E3129E7BADD7B, 0x202A9EC5FAA3CCE8, 0x5B3402464E1C3DB6, 0x609F4E62A44C1059, +//! 0x20D06CD26A8FBF5C, +//! ]); +//! ``` +//! +//! [1]: https://docs.rs/sha3 +//! [2]: https://docs.rs/tiny-keccak + +#![no_std] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![cfg_attr(feature = "simd", feature(portable_simd))] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" +)] +#![allow(non_upper_case_globals)] +#![warn( + clippy::mod_module_files, + clippy::unwrap_used, + missing_docs, + rust_2018_idioms, + unused_lifetimes, + unused_qualifications +)] + +use core::{ + convert::TryInto, + fmt::Debug, + mem::size_of, + ops::{BitAnd, BitAndAssign, BitXor, BitXorAssign, Not}, +}; + +#[rustfmt::skip] +mod unroll; + +#[cfg(all(target_arch = "aarch64", feature = "asm"))] +mod armv8; + +#[cfg(all(target_arch = "aarch64", feature = "asm"))] +cpufeatures::new!(armv8_sha3_intrinsics, "sha3"); + +const PLEN: usize = 25; + +const RHO: [u32; 24] = [ + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, +]; + +const PI: [usize; 24] = [ + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, +]; + +const RC: [u64; 24] = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808a, + 0x8000000080008000, + 0x000000000000808b, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008a, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000a, + 0x000000008000808b, + 0x800000000000008b, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800a, + 0x800000008000000a, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +]; + +/// Keccak is a permutation over an array of lanes which comprise the sponge +/// construction. +pub trait LaneSize: + Copy + + Clone + + Debug + + Default + + PartialEq + + BitAndAssign + + BitAnd + + BitXorAssign + + BitXor + + Not +{ + /// Number of rounds of the Keccak-f permutation. + const KECCAK_F_ROUND_COUNT: usize; + + /// Truncate function. + fn truncate_rc(rc: u64) -> Self; + + /// Rotate left function. + fn rotate_left(self, n: u32) -> Self; +} + +macro_rules! impl_lanesize { + ($type:ty, $round:expr, $truncate:expr) => { + impl LaneSize for $type { + const KECCAK_F_ROUND_COUNT: usize = $round; + + fn truncate_rc(rc: u64) -> Self { + $truncate(rc) + } + + fn rotate_left(self, n: u32) -> Self { + self.rotate_left(n) + } + } + }; +} + +impl_lanesize!(u8, 18, |rc: u64| { rc.to_le_bytes()[0] }); +impl_lanesize!(u16, 20, |rc: u64| { + let tmp = rc.to_le_bytes(); + #[allow(clippy::unwrap_used)] + Self::from_le_bytes(tmp[..size_of::()].try_into().unwrap()) +}); +impl_lanesize!(u32, 22, |rc: u64| { + let tmp = rc.to_le_bytes(); + #[allow(clippy::unwrap_used)] + Self::from_le_bytes(tmp[..size_of::()].try_into().unwrap()) +}); +impl_lanesize!(u64, 24, |rc: u64| { rc }); + +macro_rules! impl_keccak { + ($pname:ident, $fname:ident, $type:ty) => { + /// Keccak-p sponge function + pub fn $pname(state: &mut [$type; PLEN], round_count: usize) { + keccak_p(state, round_count); + } + + /// Keccak-f sponge function + pub fn $fname(state: &mut [$type; PLEN]) { + keccak_p(state, <$type>::KECCAK_F_ROUND_COUNT); + } + }; +} + +impl_keccak!(p200, f200, u8); +impl_keccak!(p400, f400, u16); +impl_keccak!(p800, f800, u32); + +#[cfg(not(all(target_arch = "aarch64", feature = "asm")))] +impl_keccak!(p1600, f1600, u64); + +/// Keccak-p[1600, rc] permutation. +#[cfg(all(target_arch = "aarch64", feature = "asm"))] +pub fn p1600(state: &mut [u64; PLEN], round_count: usize) { + if armv8_sha3_intrinsics::get() { + unsafe { armv8::p1600_armv8_sha3_asm(state, round_count) } + } else { + keccak_p(state, round_count); + } +} + +/// Keccak-f[1600] permutation. +#[cfg(all(target_arch = "aarch64", feature = "asm"))] +pub fn f1600(state: &mut [u64; PLEN]) { + if armv8_sha3_intrinsics::get() { + unsafe { armv8::p1600_armv8_sha3_asm(state, 24) } + } else { + keccak_p(state, u64::KECCAK_F_ROUND_COUNT); + } +} + +#[cfg(feature = "simd")] +/// SIMD implementations for Keccak-f1600 sponge function +pub mod simd { + use crate::{keccak_p, LaneSize, PLEN}; + pub use core::simd::{u64x2, u64x4, u64x8}; + + macro_rules! impl_lanesize_simd_u64xn { + ($type:ty) => { + impl LaneSize for $type { + const KECCAK_F_ROUND_COUNT: usize = 24; + + fn truncate_rc(rc: u64) -> Self { + Self::splat(rc) + } + + fn rotate_left(self, n: u32) -> Self { + self << Self::splat(n.into()) | self >> Self::splat((64 - n).into()) + } + } + }; + } + + impl_lanesize_simd_u64xn!(u64x2); + impl_lanesize_simd_u64xn!(u64x4); + impl_lanesize_simd_u64xn!(u64x8); + + impl_keccak!(p1600x2, f1600x2, u64x2); + impl_keccak!(p1600x4, f1600x4, u64x4); + impl_keccak!(p1600x8, f1600x8, u64x8); +} + +#[allow(unused_assignments)] +/// Generic Keccak-p sponge function +pub fn keccak_p(state: &mut [L; PLEN], round_count: usize) { + if round_count > L::KECCAK_F_ROUND_COUNT { + panic!("A round_count greater than KECCAK_F_ROUND_COUNT is not supported!"); + } + + // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=25 + // "the rounds of KECCAK-p[b, nr] match the last rounds of KECCAK-f[b]" + let round_consts = &RC[(L::KECCAK_F_ROUND_COUNT - round_count)..L::KECCAK_F_ROUND_COUNT]; + + // not unrolling this loop results in a much smaller function, plus + // it positively influences performance due to the smaller load on I-cache + for &rc in round_consts { + let mut array = [L::default(); 5]; + + // Theta + unroll5!(x, { + unroll5!(y, { + array[x] ^= state[5 * y + x]; + }); + }); + + unroll5!(x, { + unroll5!(y, { + let t1 = array[(x + 4) % 5]; + let t2 = array[(x + 1) % 5].rotate_left(1); + state[5 * y + x] ^= t1 ^ t2; + }); + }); + + // Rho and pi + let mut last = state[1]; + unroll24!(x, { + array[0] = state[PI[x]]; + state[PI[x]] = last.rotate_left(RHO[x]); + last = array[0]; + }); + + // Chi + unroll5!(y_step, { + let y = 5 * y_step; + + unroll5!(x, { + array[x] = state[y + x]; + }); + + unroll5!(x, { + let t1 = !array[(x + 1) % 5]; + let t2 = array[(x + 2) % 5]; + state[y + x] = array[x] ^ (t1 & t2); + }); + }); + + // Iota + state[0] ^= L::truncate_rc(rc); + } +} + +#[cfg(test)] +mod tests { + use crate::{keccak_p, LaneSize, PLEN}; + + fn keccak_f(state_first: [L; PLEN], state_second: [L; PLEN]) { + let mut state = [L::default(); PLEN]; + + keccak_p(&mut state, L::KECCAK_F_ROUND_COUNT); + assert_eq!(state, state_first); + + keccak_p(&mut state, L::KECCAK_F_ROUND_COUNT); + assert_eq!(state, state_second); + } + + #[test] + fn keccak_f200() { + // Test vectors are copied from XKCP (eXtended Keccak Code Package) + // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-200-IntermediateValues.txt + let state_first = [ + 0x3C, 0x28, 0x26, 0x84, 0x1C, 0xB3, 0x5C, 0x17, 0x1E, 0xAA, 0xE9, 0xB8, 0x11, 0x13, + 0x4C, 0xEA, 0xA3, 0x85, 0x2C, 0x69, 0xD2, 0xC5, 0xAB, 0xAF, 0xEA, + ]; + let state_second = [ + 0x1B, 0xEF, 0x68, 0x94, 0x92, 0xA8, 0xA5, 0x43, 0xA5, 0x99, 0x9F, 0xDB, 0x83, 0x4E, + 0x31, 0x66, 0xA1, 0x4B, 0xE8, 0x27, 0xD9, 0x50, 0x40, 0x47, 0x9E, + ]; + + keccak_f::(state_first, state_second); + } + + #[test] + fn keccak_f400() { + // Test vectors are copied from XKCP (eXtended Keccak Code Package) + // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-400-IntermediateValues.txt + let state_first = [ + 0x09F5, 0x40AC, 0x0FA9, 0x14F5, 0xE89F, 0xECA0, 0x5BD1, 0x7870, 0xEFF0, 0xBF8F, 0x0337, + 0x6052, 0xDC75, 0x0EC9, 0xE776, 0x5246, 0x59A1, 0x5D81, 0x6D95, 0x6E14, 0x633E, 0x58EE, + 0x71FF, 0x714C, 0xB38E, + ]; + let state_second = [ + 0xE537, 0xD5D6, 0xDBE7, 0xAAF3, 0x9BC7, 0xCA7D, 0x86B2, 0xFDEC, 0x692C, 0x4E5B, 0x67B1, + 0x15AD, 0xA7F7, 0xA66F, 0x67FF, 0x3F8A, 0x2F99, 0xE2C2, 0x656B, 0x5F31, 0x5BA6, 0xCA29, + 0xC224, 0xB85C, 0x097C, + ]; + + keccak_f::(state_first, state_second); + } + + #[test] + fn keccak_f800() { + // Test vectors are copied from XKCP (eXtended Keccak Code Package) + // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-800-IntermediateValues.txt + let state_first = [ + 0xE531D45D, 0xF404C6FB, 0x23A0BF99, 0xF1F8452F, 0x51FFD042, 0xE539F578, 0xF00B80A7, + 0xAF973664, 0xBF5AF34C, 0x227A2424, 0x88172715, 0x9F685884, 0xB15CD054, 0x1BF4FC0E, + 0x6166FA91, 0x1A9E599A, 0xA3970A1F, 0xAB659687, 0xAFAB8D68, 0xE74B1015, 0x34001A98, + 0x4119EFF3, 0x930A0E76, 0x87B28070, 0x11EFE996, + ]; + let state_second = [ + 0x75BF2D0D, 0x9B610E89, 0xC826AF40, 0x64CD84AB, 0xF905BDD6, 0xBC832835, 0x5F8001B9, + 0x15662CCE, 0x8E38C95E, 0x701FE543, 0x1B544380, 0x89ACDEFF, 0x51EDB5DE, 0x0E9702D9, + 0x6C19AA16, 0xA2913EEE, 0x60754E9A, 0x9819063C, 0xF4709254, 0xD09F9084, 0x772DA259, + 0x1DB35DF7, 0x5AA60162, 0x358825D5, 0xB3783BAB, + ]; + + keccak_f::(state_first, state_second); + } + + #[test] + fn keccak_f1600() { + // Test vectors are copied from XKCP (eXtended Keccak Code Package) + // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt + let state_first = [ + 0xF1258F7940E1DDE7, + 0x84D5CCF933C0478A, + 0xD598261EA65AA9EE, + 0xBD1547306F80494D, + 0x8B284E056253D057, + 0xFF97A42D7F8E6FD4, + 0x90FEE5A0A44647C4, + 0x8C5BDA0CD6192E76, + 0xAD30A6F71B19059C, + 0x30935AB7D08FFC64, + 0xEB5AA93F2317D635, + 0xA9A6E6260D712103, + 0x81A57C16DBCF555F, + 0x43B831CD0347C826, + 0x01F22F1A11A5569F, + 0x05E5635A21D9AE61, + 0x64BEFEF28CC970F2, + 0x613670957BC46611, + 0xB87C5A554FD00ECB, + 0x8C3EE88A1CCF32C8, + 0x940C7922AE3A2614, + 0x1841F924A2C509E4, + 0x16F53526E70465C2, + 0x75F644E97F30A13B, + 0xEAF1FF7B5CECA249, + ]; + let state_second = [ + 0x2D5C954DF96ECB3C, + 0x6A332CD07057B56D, + 0x093D8D1270D76B6C, + 0x8A20D9B25569D094, + 0x4F9C4F99E5E7F156, + 0xF957B9A2DA65FB38, + 0x85773DAE1275AF0D, + 0xFAF4F247C3D810F7, + 0x1F1B9EE6F79A8759, + 0xE4FECC0FEE98B425, + 0x68CE61B6B9CE68A1, + 0xDEEA66C4BA8F974F, + 0x33C43D836EAFB1F5, + 0xE00654042719DBD9, + 0x7CF8A9F009831265, + 0xFD5449A6BF174743, + 0x97DDAD33D8994B40, + 0x48EAD5FC5D0BE774, + 0xE3B8C8EE55B7B03C, + 0x91A0226E649E42E9, + 0x900E3129E7BADD7B, + 0x202A9EC5FAA3CCE8, + 0x5B3402464E1C3DB6, + 0x609F4E62A44C1059, + 0x20D06CD26A8FBF5C, + ]; + + keccak_f::(state_first, state_second); + } + + #[cfg(feature = "simd")] + mod simd { + use super::keccak_f; + use core::simd::{u64x2, u64x4, u64x8}; + + macro_rules! impl_keccak_f1600xn { + ($name:ident, $type:ty) => { + #[test] + fn $name() { + // Test vectors are copied from XKCP (eXtended Keccak Code Package) + // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt + let state_first = [ + <$type>::splat(0xF1258F7940E1DDE7), + <$type>::splat(0x84D5CCF933C0478A), + <$type>::splat(0xD598261EA65AA9EE), + <$type>::splat(0xBD1547306F80494D), + <$type>::splat(0x8B284E056253D057), + <$type>::splat(0xFF97A42D7F8E6FD4), + <$type>::splat(0x90FEE5A0A44647C4), + <$type>::splat(0x8C5BDA0CD6192E76), + <$type>::splat(0xAD30A6F71B19059C), + <$type>::splat(0x30935AB7D08FFC64), + <$type>::splat(0xEB5AA93F2317D635), + <$type>::splat(0xA9A6E6260D712103), + <$type>::splat(0x81A57C16DBCF555F), + <$type>::splat(0x43B831CD0347C826), + <$type>::splat(0x01F22F1A11A5569F), + <$type>::splat(0x05E5635A21D9AE61), + <$type>::splat(0x64BEFEF28CC970F2), + <$type>::splat(0x613670957BC46611), + <$type>::splat(0xB87C5A554FD00ECB), + <$type>::splat(0x8C3EE88A1CCF32C8), + <$type>::splat(0x940C7922AE3A2614), + <$type>::splat(0x1841F924A2C509E4), + <$type>::splat(0x16F53526E70465C2), + <$type>::splat(0x75F644E97F30A13B), + <$type>::splat(0xEAF1FF7B5CECA249), + ]; + let state_second = [ + <$type>::splat(0x2D5C954DF96ECB3C), + <$type>::splat(0x6A332CD07057B56D), + <$type>::splat(0x093D8D1270D76B6C), + <$type>::splat(0x8A20D9B25569D094), + <$type>::splat(0x4F9C4F99E5E7F156), + <$type>::splat(0xF957B9A2DA65FB38), + <$type>::splat(0x85773DAE1275AF0D), + <$type>::splat(0xFAF4F247C3D810F7), + <$type>::splat(0x1F1B9EE6F79A8759), + <$type>::splat(0xE4FECC0FEE98B425), + <$type>::splat(0x68CE61B6B9CE68A1), + <$type>::splat(0xDEEA66C4BA8F974F), + <$type>::splat(0x33C43D836EAFB1F5), + <$type>::splat(0xE00654042719DBD9), + <$type>::splat(0x7CF8A9F009831265), + <$type>::splat(0xFD5449A6BF174743), + <$type>::splat(0x97DDAD33D8994B40), + <$type>::splat(0x48EAD5FC5D0BE774), + <$type>::splat(0xE3B8C8EE55B7B03C), + <$type>::splat(0x91A0226E649E42E9), + <$type>::splat(0x900E3129E7BADD7B), + <$type>::splat(0x202A9EC5FAA3CCE8), + <$type>::splat(0x5B3402464E1C3DB6), + <$type>::splat(0x609F4E62A44C1059), + <$type>::splat(0x20D06CD26A8FBF5C), + ]; + + keccak_f::<$type>(state_first, state_second); + } + }; + } + + impl_keccak_f1600xn!(keccak_f1600x2, u64x2); + impl_keccak_f1600xn!(keccak_f1600x4, u64x4); + impl_keccak_f1600xn!(keccak_f1600x8, u64x8); + } +} diff --git a/contracts/vesting_curves/vendor/keccak-0.1.5/src/unroll.rs b/contracts/vesting_curves/vendor/keccak-0.1.5/src/unroll.rs new file mode 100644 index 0000000..eab745b --- /dev/null +++ b/contracts/vesting_curves/vendor/keccak-0.1.5/src/unroll.rs @@ -0,0 +1,62 @@ +/// unroll5 +#[cfg(not(feature = "no_unroll"))] +#[macro_export] +macro_rules! unroll5 { + ($var:ident, $body:block) => { + { const $var: usize = 0; $body; } + { const $var: usize = 1; $body; } + { const $var: usize = 2; $body; } + { const $var: usize = 3; $body; } + { const $var: usize = 4; $body; } + }; +} + +/// unroll5 +#[cfg(feature = "no_unroll")] +#[macro_export] +macro_rules! unroll5 { + ($var:ident, $body:block) => { + for $var in 0..5 $body + } +} + +/// unroll24 +#[cfg(not(feature = "no_unroll"))] +#[macro_export] +macro_rules! unroll24 { + ($var: ident, $body: block) => { + { const $var: usize = 0; $body; } + { const $var: usize = 1; $body; } + { const $var: usize = 2; $body; } + { const $var: usize = 3; $body; } + { const $var: usize = 4; $body; } + { const $var: usize = 5; $body; } + { const $var: usize = 6; $body; } + { const $var: usize = 7; $body; } + { const $var: usize = 8; $body; } + { const $var: usize = 9; $body; } + { const $var: usize = 10; $body; } + { const $var: usize = 11; $body; } + { const $var: usize = 12; $body; } + { const $var: usize = 13; $body; } + { const $var: usize = 14; $body; } + { const $var: usize = 15; $body; } + { const $var: usize = 16; $body; } + { const $var: usize = 17; $body; } + { const $var: usize = 18; $body; } + { const $var: usize = 19; $body; } + { const $var: usize = 20; $body; } + { const $var: usize = 21; $body; } + { const $var: usize = 22; $body; } + { const $var: usize = 23; $body; } + }; +} + +/// unroll24 +#[cfg(feature = "no_unroll")] +#[macro_export] +macro_rules! unroll24 { + ($var:ident, $body:block) => { + for $var in 0..24 $body + } +}