From e5520b436deb8c118dcdb2ad4dff6a79ebba488c Mon Sep 17 00:00:00 2001 From: Kevin Yang <5478483+k-yang@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:42:53 -0600 Subject: [PATCH 1/4] refactor: make cliff_amount required --- .../airdrop-token-vesting/src/contract.rs | 6 +- contracts/airdrop-token-vesting/src/msg.rs | 10 ++-- .../airdrop-token-vesting/src/testing.rs | 56 +++++++------------ 3 files changed, 28 insertions(+), 44 deletions(-) diff --git a/contracts/airdrop-token-vesting/src/contract.rs b/contracts/airdrop-token-vesting/src/contract.rs index e0e4eb9..9a6d8f4 100644 --- a/contracts/airdrop-token-vesting/src/contract.rs +++ b/contracts/airdrop-token-vesting/src/contract.rs @@ -178,7 +178,7 @@ fn reward_users( .. } => { *vesting_amount = req.vesting_amount; - *cliff_amount = req.cliff_amount.unwrap(); + *cliff_amount = req.cliff_amount; } } @@ -530,7 +530,7 @@ pub mod tests { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(5000u128), - cliff_amount: None, + cliff_amount: Uint128::zero(), }], vesting_schedule: VestingSchedule::LinearVesting { start_time: Uint64::new(100), @@ -577,7 +577,7 @@ pub mod tests { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(5000u128), - cliff_amount: None, + cliff_amount: Uint128::zero(), }], vesting_schedule: VestingSchedule::LinearVesting { start_time: Uint64::new(100), diff --git a/contracts/airdrop-token-vesting/src/msg.rs b/contracts/airdrop-token-vesting/src/msg.rs index 6b019da..ef4ca79 100644 --- a/contracts/airdrop-token-vesting/src/msg.rs +++ b/contracts/airdrop-token-vesting/src/msg.rs @@ -57,7 +57,7 @@ pub enum ExecuteMsg { pub struct RewardUserRequest { pub user_address: String, pub vesting_amount: Uint128, - pub cliff_amount: Option, + pub cliff_amount: Uint128, } impl RewardUserRequest { @@ -71,18 +71,16 @@ impl RewardUserRequest { if let VestingSchedule::LinearVestingWithCliff { .. } = vesting_schedule { - if self.cliff_amount.is_none() - || self.cliff_amount.unwrap().is_zero() - { + if self.cliff_amount.is_zero() { return Err(ContractError::Vesting(VestingError::Cliff( CliffError::ZeroAmount, ))); } - if self.cliff_amount.unwrap() > self.vesting_amount { + if self.cliff_amount > self.vesting_amount { return Err(ContractError::Vesting(VestingError::Cliff( CliffError::ExcessiveAmount { - cliff_amount: self.cliff_amount.unwrap().into(), + cliff_amount: self.cliff_amount.into(), vesting_amount: self.vesting_amount.into(), }, ))); diff --git a/contracts/airdrop-token-vesting/src/testing.rs b/contracts/airdrop-token-vesting/src/testing.rs index 586c8f1..f340bc7 100644 --- a/contracts/airdrop-token-vesting/src/testing.rs +++ b/contracts/airdrop-token-vesting/src/testing.rs @@ -246,14 +246,14 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { let create_msg = |start_time: u64, end_time: u64, vesting_amount: u128, - cliff_amount: Option, + cliff_amount: u128, cliff_time: u64| -> ExecuteMsg { ExecuteMsg::RewardUsers { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(vesting_amount), - cliff_amount: cliff_amount.map(Uint128::new), + cliff_amount: Uint128::new(cliff_amount), }], vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(start_time), @@ -266,7 +266,7 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { }; // unauthorized sender - let msg = create_msg(100, 110, 0, Some(1000), 105); + let msg = create_msg(100, 110, 0, 1000, 105); require_error( &mut deps, &env, @@ -276,7 +276,7 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { ); // zero amount vesting token - let msg = create_msg(100, 110, 0, Some(1000), 105); + let msg = create_msg(100, 110, 0, 1000, 105); require_error( &mut deps, &env, @@ -286,17 +286,7 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { ); // zero amount cliff token - let msg = create_msg(100, 110, 1000, Some(0), 105); - require_error( - &mut deps, - &env, - mock_info("addr0000", &[]), - msg, - ContractError::Vesting(VestingError::Cliff(CliffError::ZeroAmount)), - ); - - // none amount cliff token - let msg = create_msg(100, 110, 1000, None, 105); + let msg = create_msg(100, 110, 1000, 0, 105); require_error( &mut deps, &env, @@ -306,7 +296,7 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { ); // cliff time less than block time - let msg = create_msg(100, 110, 1000, Some(1000), 99); + let msg = create_msg(100, 110, 1000, 500, 99); require_error( &mut deps, &env, @@ -319,7 +309,7 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { ); // end time less than start time - let msg = create_msg(110, 100, 1000, Some(1000), 105); + let msg = create_msg(110, 100, 1000, 1000, 105); require_error( &mut deps, &env, @@ -333,8 +323,7 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { // cliff amount greater than vesting amount let (vesting_amount, cliff_amount, cliff_time) = (1000, 1001, 105); - let msg = - create_msg(100, 110, vesting_amount, Some(cliff_amount), cliff_time); + let msg = create_msg(100, 110, vesting_amount, cliff_amount, cliff_time); require_error( &mut deps, &env, @@ -351,8 +340,7 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { // deposit amount higher than unallocated let (vesting_amount, cliff_amount, cliff_time) = (10000, 250, 105); - let msg = - create_msg(100, 110, vesting_amount, Some(cliff_amount), cliff_time); + let msg = create_msg(100, 110, vesting_amount, cliff_amount, cliff_time); require_error( &mut deps, &env, @@ -363,8 +351,7 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { // valid amount let (vesting_amount, cliff_amount, cliff_time) = (1000, 250, 105); - let msg = - create_msg(100, 110, vesting_amount, Some(cliff_amount), cliff_time); + let msg = create_msg(100, 110, vesting_amount, cliff_amount, cliff_time); let res = execute(deps.as_mut(), env.clone(), mock_info("addr0000", &[]), msg)?; @@ -401,12 +388,12 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { RewardUserRequest { user_address: "addr0002".to_string(), vesting_amount: Uint128::new(vesting_amount), - cliff_amount: Some(Uint128::new(cliff_amount)), + cliff_amount: Uint128::new(cliff_amount), }, RewardUserRequest { user_address: "addr0002".to_string(), vesting_amount: Uint128::new(vesting_amount), - cliff_amount: Some(Uint128::new(cliff_amount)), + cliff_amount: Uint128::new(cliff_amount), }, ], vesting_schedule: VestingSchedule::LinearVestingWithCliff { @@ -465,14 +452,14 @@ fn test_withdraw() -> TestResult { let create_msg = |start_time: u64, end_time: u64, vesting_amount: u128, - cliff_amount: Option, + cliff_amount: u128, cliff_time: u64| -> ExecuteMsg { ExecuteMsg::RewardUsers { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(vesting_amount), - cliff_amount: cliff_amount.map(Uint128::new), + cliff_amount: Uint128::new(cliff_amount), }], vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(start_time), @@ -486,8 +473,7 @@ fn test_withdraw() -> TestResult { // valid amount let (vesting_amount, cliff_amount, cliff_time) = (1000, 250, 105); - let msg = - create_msg(100, 110, vesting_amount, Some(cliff_amount), cliff_time); + let msg = create_msg(100, 110, vesting_amount, cliff_amount, cliff_time); let _res = execute(deps.as_mut(), env.clone(), mock_info("addr0000", &[]), msg)?; @@ -590,7 +576,7 @@ fn register_vesting_account_with_native_token() -> TestResult { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::zero(), - cliff_amount: None, + cliff_amount: Uint128::zero(), }], vesting_schedule: VestingSchedule::LinearVesting { start_time: Uint64::new(100), @@ -612,7 +598,7 @@ fn register_vesting_account_with_native_token() -> TestResult { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(1000001u128), - cliff_amount: None, + cliff_amount: Uint128::zero(), }], vesting_schedule: VestingSchedule::LinearVesting { start_time: Uint64::new(100), @@ -635,12 +621,12 @@ fn register_vesting_account_with_native_token() -> TestResult { RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(1000u128), - cliff_amount: None, + cliff_amount: Uint128::zero(), }, RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(1u128), - cliff_amount: None, + cliff_amount: Uint128::zero(), }, ], vesting_schedule: VestingSchedule::LinearVesting { @@ -663,7 +649,7 @@ fn register_vesting_account_with_native_token() -> TestResult { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(100u128), - cliff_amount: None, + cliff_amount: Uint128::zero(), }], vesting_schedule: VestingSchedule::LinearVesting { start_time: Uint64::new(100), @@ -749,7 +735,7 @@ fn claim_native() -> TestResult { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(1000000u128), - cliff_amount: None, + cliff_amount: Uint128::zero(), }], vesting_schedule: VestingSchedule::LinearVesting { start_time: Uint64::new(100), From d0dd2b2026c5248e0e85ae2694af345a9ce4f79f Mon Sep 17 00:00:00 2001 From: Kevin Yang <5478483+k-yang@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:48:36 -0600 Subject: [PATCH 2/4] refactor: remove VestingSchedule::LinearVesting --- .../airdrop-token-vesting/src/contract.rs | 11 ++-- contracts/airdrop-token-vesting/src/msg.rs | 66 ------------------- .../airdrop-token-vesting/src/testing.rs | 28 ++++++-- 3 files changed, 27 insertions(+), 78 deletions(-) diff --git a/contracts/airdrop-token-vesting/src/contract.rs b/contracts/airdrop-token-vesting/src/contract.rs index 9a6d8f4..f759b67 100644 --- a/contracts/airdrop-token-vesting/src/contract.rs +++ b/contracts/airdrop-token-vesting/src/contract.rs @@ -169,9 +169,6 @@ fn reward_users( // update the vesting schedule to match with the request match &mut vesting_schedule { - VestingSchedule::LinearVesting { vesting_amount, .. } => { - *vesting_amount = req.vesting_amount; - } VestingSchedule::LinearVestingWithCliff { vesting_amount, cliff_amount, @@ -532,10 +529,12 @@ pub mod tests { vesting_amount: Uint128::new(5000u128), cliff_amount: Uint128::zero(), }], - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::new(1000000u128), + cliff_amount: Uint128::zero(), + cliff_time: Uint64::new(105), }, }; @@ -579,10 +578,12 @@ pub mod tests { vesting_amount: Uint128::new(5000u128), cliff_amount: Uint128::zero(), }], - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::new(1000000u128), + cliff_amount: Uint128::zero(), + cliff_time: Uint64::new(105), }, }; diff --git a/contracts/airdrop-token-vesting/src/msg.rs b/contracts/airdrop-token-vesting/src/msg.rs index ef4ca79..7dab074 100644 --- a/contracts/airdrop-token-vesting/src/msg.rs +++ b/contracts/airdrop-token-vesting/src/msg.rs @@ -123,13 +123,6 @@ pub struct VestingData { #[cw_serde] pub enum VestingSchedule { - /// LinearVesting is used to vest tokens linearly during a time period. - /// The total_amount will be vested during this period. - LinearVesting { - start_time: Uint64, // vesting start time in second unit - end_time: Uint64, // vesting end time in second unit - vesting_amount: Uint128, // total vesting amount - }, LinearVestingWithCliff { start_time: Uint64, // vesting start time in second unit end_time: Uint64, // vesting end time in second unit @@ -175,25 +168,6 @@ impl Cliff { impl VestingSchedule { pub fn vested_amount(&self, block_time: u64) -> StdResult { match self { - VestingSchedule::LinearVesting { - start_time, - end_time, - vesting_amount, - } => { - if block_time <= start_time.u64() { - return Ok(Uint128::zero()); - } - - if block_time >= end_time.u64() { - return Ok(*vesting_amount); - } - - let vested_token = vesting_amount - .checked_mul(Uint128::from(block_time - start_time.u64()))? - .checked_div(Uint128::from(end_time - start_time))?; - - Ok(vested_token) - } VestingSchedule::LinearVestingWithCliff { start_time: _start_time, end_time, @@ -238,17 +212,6 @@ impl VestingSchedule { pub fn validate(&self, block_time: Timestamp) -> Result<(), VestingError> { self.validate_time(block_time)?; match &self { - VestingSchedule::LinearVesting { - start_time: _, - end_time: _, - vesting_amount, - } => { - if vesting_amount.is_zero() { - return Err(VestingError::ZeroVestingAmount); - } - Ok(()) - } - VestingSchedule::LinearVestingWithCliff { start_time: _, end_time: _, @@ -282,19 +245,6 @@ impl VestingSchedule { block_time: Timestamp, ) -> Result<(), VestingError> { match self { - VestingSchedule::LinearVesting { - start_time, - end_time, - .. - } => { - if end_time <= start_time { - return Err(VestingError::InvalidTimeRange { - start_time: start_time.u64(), - end_time: end_time.u64(), - }); - } - Ok(()) - } VestingSchedule::LinearVestingWithCliff { start_time, end_time, @@ -323,22 +273,6 @@ pub mod tests { use super::*; use crate::contract::tests::TestResult; - #[test] - fn linear_vesting_vested_amount() -> TestResult { - let schedule = VestingSchedule::LinearVesting { - start_time: Uint64::new(100), - end_time: Uint64::new(110), - vesting_amount: Uint128::new(1000000u128), - }; - - assert_eq!(schedule.vested_amount(100)?, Uint128::zero()); - assert_eq!(schedule.vested_amount(105)?, Uint128::new(500000u128)); - assert_eq!(schedule.vested_amount(110)?, Uint128::new(1000000u128)); - assert_eq!(schedule.vested_amount(115)?, Uint128::new(1000000u128)); - - Ok(()) - } - #[test] fn linear_vesting_with_cliff_vested_amount() -> TestResult { let schedule = VestingSchedule::LinearVestingWithCliff { diff --git a/contracts/airdrop-token-vesting/src/testing.rs b/contracts/airdrop-token-vesting/src/testing.rs index f340bc7..dfd03c6 100644 --- a/contracts/airdrop-token-vesting/src/testing.rs +++ b/contracts/airdrop-token-vesting/src/testing.rs @@ -578,10 +578,12 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_amount: Uint128::zero(), cliff_amount: Uint128::zero(), }], - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::zero(), + cliff_amount: Uint128::zero(), + cliff_time: Uint64::new(105), }, }; @@ -600,10 +602,12 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_amount: Uint128::new(1000001u128), cliff_amount: Uint128::zero(), }], - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::new(1000000u128), + cliff_amount: Uint128::zero(), + cliff_time: Uint64::new(105), }, }; let info = mock_info("addr0000", &[]); @@ -629,10 +633,12 @@ fn register_vesting_account_with_native_token() -> TestResult { cliff_amount: Uint128::zero(), }, ], - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::new(1000000u128), + cliff_amount: Uint128::zero(), + cliff_time: Uint64::new(105), }, }; let info = mock_info("addr0000", &[]); @@ -651,10 +657,12 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_amount: Uint128::new(100u128), cliff_amount: Uint128::zero(), }], - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::new(100u128), + cliff_amount: Uint128::zero(), + cliff_time: Uint64::new(105), }, }; let info = mock_info("addr0000", &[Coin::new(1000u128, "uusd")]); @@ -698,10 +706,12 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_account: crate::state::VestingAccount { address: "addr0001".to_string(), vesting_amount: Uint128::new(100u128), - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::new(100u128), + cliff_amount: Uint128::zero(), + cliff_time: Uint64::new(105), }, claimed_amount: Uint128::zero(), }, @@ -737,10 +747,12 @@ fn claim_native() -> TestResult { vesting_amount: Uint128::new(1000000u128), cliff_amount: Uint128::zero(), }], - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), + cliff_time: Uint64::new(105), end_time: Uint64::new(110), vesting_amount: Uint128::new(1000000u128), + cliff_amount: Uint128::zero(), }, }; @@ -793,10 +805,12 @@ fn claim_native() -> TestResult { vesting_account: crate::state::VestingAccount { address: "addr0001".to_string(), vesting_amount: Uint128::new(1000000), - vesting_schedule: VestingSchedule::LinearVesting { + vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::new(1000000u128), + cliff_amount: Uint128::zero(), + cliff_time: Uint64::new(105), }, claimed_amount: Uint128::new(500000), }, From 5911038925cd145df22e4300272668fde193845c Mon Sep 17 00:00:00 2001 From: Kevin Yang <5478483+k-yang@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:22:23 -0600 Subject: [PATCH 3/4] feat: allow zero cliff --- .../airdrop-token-vesting/src/contract.rs | 25 +++++++++++- contracts/airdrop-token-vesting/src/errors.rs | 3 -- contracts/airdrop-token-vesting/src/msg.rs | 34 +++++----------- .../airdrop-token-vesting/src/testing.rs | 39 ++++++++++++------- 4 files changed, 57 insertions(+), 44 deletions(-) diff --git a/contracts/airdrop-token-vesting/src/contract.rs b/contracts/airdrop-token-vesting/src/contract.rs index f759b67..28f9b1d 100644 --- a/contracts/airdrop-token-vesting/src/contract.rs +++ b/contracts/airdrop-token-vesting/src/contract.rs @@ -165,7 +165,7 @@ fn reward_users( let mut attrs: Vec = vec![]; for req in rewards { // validate amounts and cliff details if there's one - req.validate(vesting_schedule.clone())?; + req.validate()?; // update the vesting schedule to match with the request match &mut vesting_schedule { @@ -538,12 +538,33 @@ pub mod tests { }, }; - execute( + let res = execute( deps.as_mut(), env.clone(), // Use the custom environment with the adjusted block time testing::mock_info("admin-sender", &[coin(1000000, "token")]), register_msg, )?; + assert_eq!( + res.attributes, + vec![ + Attribute { + key: "action".to_string(), + value: "register_vesting_account".to_string() + }, + Attribute { + key: "address".to_string(), + value: "addr0001".to_string() + }, + Attribute { + key: "vesting_amount".to_string(), + value: "5000".to_string() + }, + Attribute { + key: "method".to_string(), + value: "reward_users".to_string() + } + ] + ); // Try to deregister with unauthorized sender let msg = ExecuteMsg::DeregisterVestingAccount { diff --git a/contracts/airdrop-token-vesting/src/errors.rs b/contracts/airdrop-token-vesting/src/errors.rs index be9b992..fef343d 100644 --- a/contracts/airdrop-token-vesting/src/errors.rs +++ b/contracts/airdrop-token-vesting/src/errors.rs @@ -17,9 +17,6 @@ pub enum ContractError { #[derive(thiserror::Error, Debug, PartialEq)] pub enum CliffError { - #[error("cliff_amount is zero but should be greater than 0")] - ZeroAmount, - #[error("cliff_time ({cliff_time}) should be greater than block_time ({block_time})")] InvalidTime { cliff_time: u64, block_time: u64 }, diff --git a/contracts/airdrop-token-vesting/src/msg.rs b/contracts/airdrop-token-vesting/src/msg.rs index 7dab074..c7b049b 100644 --- a/contracts/airdrop-token-vesting/src/msg.rs +++ b/contracts/airdrop-token-vesting/src/msg.rs @@ -61,30 +61,18 @@ pub struct RewardUserRequest { } impl RewardUserRequest { - pub fn validate( - &self, - vesting_schedule: VestingSchedule, - ) -> Result<(), ContractError> { + pub fn validate(&self) -> Result<(), ContractError> { if self.vesting_amount.is_zero() { return Err(ContractError::Vesting(VestingError::ZeroVestingAmount)); } - if let VestingSchedule::LinearVestingWithCliff { .. } = vesting_schedule - { - if self.cliff_amount.is_zero() { - return Err(ContractError::Vesting(VestingError::Cliff( - CliffError::ZeroAmount, - ))); - } - - if self.cliff_amount > self.vesting_amount { - return Err(ContractError::Vesting(VestingError::Cliff( - CliffError::ExcessiveAmount { - cliff_amount: self.cliff_amount.into(), - vesting_amount: self.vesting_amount.into(), - }, - ))); - } + if self.cliff_amount > self.vesting_amount { + return Err(ContractError::Vesting(VestingError::Cliff( + CliffError::ExcessiveAmount { + cliff_amount: self.cliff_amount.into(), + vesting_amount: self.vesting_amount.into(), + }, + ))); } Ok(()) @@ -150,10 +138,6 @@ impl Cliff { } pub fn ok_amount(&self, vesting_amount: Uint128) -> Result<(), CliffError> { - if self.amount.is_zero() { - return Err(CliffError::ZeroAmount); - } - let cliff_amount = self.amount.u128(); if cliff_amount > vesting_amount.u128() { return Err(CliffError::ExcessiveAmount { @@ -211,7 +195,7 @@ impl VestingSchedule { /// pub fn validate(&self, block_time: Timestamp) -> Result<(), VestingError> { self.validate_time(block_time)?; - match &self { + match self { VestingSchedule::LinearVestingWithCliff { start_time: _, end_time: _, diff --git a/contracts/airdrop-token-vesting/src/testing.rs b/contracts/airdrop-token-vesting/src/testing.rs index dfd03c6..60fa74e 100644 --- a/contracts/airdrop-token-vesting/src/testing.rs +++ b/contracts/airdrop-token-vesting/src/testing.rs @@ -285,16 +285,6 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { ContractError::Vesting(VestingError::ZeroVestingAmount), ); - // zero amount cliff token - let msg = create_msg(100, 110, 1000, 0, 105); - require_error( - &mut deps, - &env, - mock_info("addr0000", &[]), - msg, - ContractError::Vesting(VestingError::Cliff(CliffError::ZeroAmount)), - ); - // cliff time less than block time let msg = create_msg(100, 110, 1000, 500, 99); require_error( @@ -745,19 +735,40 @@ fn claim_native() -> TestResult { rewards: vec![RewardUserRequest { user_address: "addr0001".to_string(), vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::zero(), + cliff_amount: Uint128::new(500000u128), }], vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), cliff_time: Uint64::new(105), end_time: Uint64::new(110), vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::zero(), + cliff_amount: Uint128::new(500000u128), }, }; let info = mock_info("addr0000", &[Coin::new(1000000u128, "uusd")]); - let _ = execute(deps.as_mut(), env.clone(), info, msg)?; + let res = execute(deps.as_mut(), env.clone(), info, msg)?; + assert_eq!( + res.attributes, + vec![ + Attribute { + key: "action".to_string(), + value: "register_vesting_account".to_string() + }, + Attribute { + key: "address".to_string(), + value: "addr0001".to_string() + }, + Attribute { + key: "vesting_amount".to_string(), + value: "1000000".to_string() + }, + Attribute { + key: "method".to_string(), + value: "reward_users".to_string() + } + ] + ); // make time to half claimable env.block.time = Timestamp::from_seconds(105); @@ -809,7 +820,7 @@ fn claim_native() -> TestResult { start_time: Uint64::new(100), end_time: Uint64::new(110), vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::zero(), + cliff_amount: Uint128::new(500000u128), cliff_time: Uint64::new(105), }, claimed_amount: Uint128::new(500000), From 15fb12cdab65150191147a9a3bec6fcc2c3de911 Mon Sep 17 00:00:00 2001 From: Kevin Yang <5478483+k-yang@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:03:29 -0600 Subject: [PATCH 4/4] refactor: remove cliff amount and vesting amount from VestingSchedule --- .../airdrop-token-vesting/src/contract.rs | 42 ++----- contracts/airdrop-token-vesting/src/msg.rs | 106 +----------------- contracts/airdrop-token-vesting/src/state.rs | 35 +++++- .../airdrop-token-vesting/src/testing.rs | 22 +--- 4 files changed, 53 insertions(+), 152 deletions(-) diff --git a/contracts/airdrop-token-vesting/src/contract.rs b/contracts/airdrop-token-vesting/src/contract.rs index 28f9b1d..1df66cb 100644 --- a/contracts/airdrop-token-vesting/src/contract.rs +++ b/contracts/airdrop-token-vesting/src/contract.rs @@ -139,7 +139,7 @@ fn reward_users( env: Env, info: MessageInfo, rewards: Vec, - mut vesting_schedule: VestingSchedule, + vesting_schedule: VestingSchedule, ) -> Result { let mut res = vec![]; @@ -160,30 +160,19 @@ fn reward_users( StdError::generic_err("Insufficient funds for all rewards").into() ); } - vesting_schedule.validate_time(env.block.time)?; + vesting_schedule.validate(env.block.time)?; let mut attrs: Vec = vec![]; for req in rewards { // validate amounts and cliff details if there's one req.validate()?; - // update the vesting schedule to match with the request - match &mut vesting_schedule { - VestingSchedule::LinearVestingWithCliff { - vesting_amount, - cliff_amount, - .. - } => { - *vesting_amount = req.vesting_amount; - *cliff_amount = req.cliff_amount; - } - } - let result = register_vesting_account( deps.storage, env.block.time, req.user_address.clone(), req.vesting_amount, + req.cliff_amount, vesting_schedule.clone(), ); @@ -218,7 +207,8 @@ fn register_vesting_account( storage: &mut dyn Storage, block_time: Timestamp, address: String, - deposit_amount: Uint128, + vesting_amount: Uint128, + cliff_amount: Uint128, vesting_schedule: VestingSchedule, ) -> Result { // vesting_account existence check @@ -232,7 +222,8 @@ fn register_vesting_account( address.as_str(), &VestingAccount { address: address.to_string(), - vesting_amount: deposit_amount, + vesting_amount, + cliff_amount, vesting_schedule, claimed_amount: Uint128::zero(), }, @@ -241,7 +232,7 @@ fn register_vesting_account( Ok(Response::new().add_attributes(vec![ ("action", "register_vesting_account"), ("address", address.as_str()), - ("vesting_amount", &deposit_amount.to_string()), + ("vesting_amount", &vesting_amount.to_string()), ])) } @@ -278,9 +269,7 @@ fn deregister_vesting_account( // remove vesting account VESTING_ACCOUNTS.remove(deps.storage, address.as_str()); - let vested_amount = account - .vesting_schedule - .vested_amount(env.block.time.seconds())?; + let vested_amount = account.vested_amount(env.block.time.seconds())?; let claimed_amount = account.claimed_amount; // transfer already vested amount to vested_token_recipient and if @@ -359,9 +348,7 @@ fn claim( } let mut account = account.unwrap(); - let vested_amount = account - .vesting_schedule - .vested_amount(env.block.time.seconds())?; + let vested_amount = account.vested_amount(env.block.time.seconds())?; let claimed_amount = account.claimed_amount; let claimable_amount = vested_amount.checked_sub(claimed_amount)?; @@ -436,9 +423,8 @@ fn vesting_account( match account { None => Err(StdError::not_found("Vesting account not found")), Some(account) => { - let vested_amount = account - .vesting_schedule - .vested_amount(env.block.time.seconds())?; + let vested_amount = + account.vested_amount(env.block.time.seconds())?; let vesting = VestingData { vesting_account: account.clone(), @@ -532,8 +518,6 @@ pub mod tests { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(105), }, }; @@ -602,8 +586,6 @@ pub mod tests { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(105), }, }; diff --git a/contracts/airdrop-token-vesting/src/msg.rs b/contracts/airdrop-token-vesting/src/msg.rs index c7b049b..f3fbfb3 100644 --- a/contracts/airdrop-token-vesting/src/msg.rs +++ b/contracts/airdrop-token-vesting/src/msg.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{StdResult, Timestamp, Uint128, Uint64}; +use cosmwasm_std::{Timestamp, Uint128, Uint64}; use cw20::Denom; use crate::{ @@ -112,11 +112,9 @@ pub struct VestingData { #[cw_serde] pub enum VestingSchedule { LinearVestingWithCliff { - start_time: Uint64, // vesting start time in second unit - end_time: Uint64, // vesting end time in second unit - vesting_amount: Uint128, // total vesting amount - cliff_amount: Uint128, // amount that will be unvested at cliff_time - cliff_time: Uint64, // cliff time in second unit + start_time: Uint64, // vesting start time in second unit + end_time: Uint64, // vesting end time in second unit + cliff_time: Uint64, // cliff time in second unit }, } @@ -150,72 +148,6 @@ impl Cliff { } impl VestingSchedule { - pub fn vested_amount(&self, block_time: u64) -> StdResult { - match self { - VestingSchedule::LinearVestingWithCliff { - start_time: _start_time, - end_time, - vesting_amount, - cliff_amount, - cliff_time, - } => { - if block_time < cliff_time.u64() { - return Ok(Uint128::zero()); - } - - if block_time == cliff_time.u64() { - return Ok(*cliff_amount); - } - - if block_time >= end_time.u64() { - return Ok(*vesting_amount); - } - - let remaining_token = - vesting_amount.checked_sub(*cliff_amount)?; - let vested_token = remaining_token - .checked_mul(Uint128::from(block_time - cliff_time.u64()))? - .checked_div(Uint128::from(end_time - cliff_time))?; - - Ok(vested_token + cliff_amount) - } - } - } - - /// - /// Validates the vesting schedule. - /// - /// - If the VestingSchedule is LinearVesting, it checks that the vesting amount is not zero. - /// - If the VestingSchedule is LinearVestingWithCliff, it checks: - /// - that the vesting amount is not zero. - /// - that the cliff amount is not zero. - /// - that the cliff amount is less than or equal to the vesting amount. - /// - /// Also it calls to validate_time - /// - pub fn validate(&self, block_time: Timestamp) -> Result<(), VestingError> { - self.validate_time(block_time)?; - match self { - VestingSchedule::LinearVestingWithCliff { - start_time: _, - end_time: _, - vesting_amount, - cliff_time, - cliff_amount, - } => { - if vesting_amount.is_zero() { - return Err(VestingError::ZeroVestingAmount); - } - let cliff = Cliff { - amount: *cliff_amount, - time: *cliff_time, - }; - cliff.ok_amount(*vesting_amount)?; - Ok(()) - } - } - } - /// /// validate_time checks that the start_time is less than the end_time. /// additionally, if the vesting schedule is LinearVestingWithCliff, it checks that the cliff_time @@ -224,10 +156,7 @@ impl VestingSchedule { /// Additionally, it the vesting schedule is LinearVestingWithCliff, it checks that the cliff_time /// is bigger or equal to the block_time. /// - pub fn validate_time( - &self, - block_time: Timestamp, - ) -> Result<(), VestingError> { + pub fn validate(&self, block_time: Timestamp) -> Result<(), VestingError> { match self { VestingSchedule::LinearVestingWithCliff { start_time, @@ -251,28 +180,3 @@ impl VestingSchedule { } } } - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::contract::tests::TestResult; - - #[test] - fn linear_vesting_with_cliff_vested_amount() -> TestResult { - let schedule = VestingSchedule::LinearVestingWithCliff { - start_time: Uint64::new(100), - end_time: Uint64::new(110), - vesting_amount: Uint128::new(1_000_000_u128), - cliff_amount: Uint128::new(100_000_u128), - cliff_time: Uint64::new(105), - }; - - assert_eq!(schedule.vested_amount(100)?, Uint128::zero()); - assert_eq!(schedule.vested_amount(105)?, Uint128::new(100000u128)); // cliff time then the cliff amount - assert_eq!(schedule.vested_amount(120)?, Uint128::new(1000000u128)); // complete vesting - assert_eq!(schedule.vested_amount(104)?, Uint128::zero()); // before cliff time - assert_eq!(schedule.vested_amount(109)?, Uint128::new(820_000)); // after cliff time but before end time - - Ok(()) - } -} diff --git a/contracts/airdrop-token-vesting/src/state.rs b/contracts/airdrop-token-vesting/src/state.rs index 536f58c..add1587 100644 --- a/contracts/airdrop-token-vesting/src/state.rs +++ b/contracts/airdrop-token-vesting/src/state.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use cosmwasm_schema::cw_serde; use crate::msg::VestingSchedule; -use cosmwasm_std::Uint128; +use cosmwasm_std::{StdResult, Uint128}; use cw_storage_plus::{Item, Map}; pub const VESTING_ACCOUNTS: Map<&str, VestingAccount> = @@ -34,6 +34,39 @@ impl Whitelist { pub struct VestingAccount { pub address: String, pub vesting_amount: Uint128, + pub cliff_amount: Uint128, pub vesting_schedule: VestingSchedule, pub claimed_amount: Uint128, } + +impl VestingAccount { + pub fn vested_amount(&self, block_time: u64) -> StdResult { + match self.vesting_schedule { + VestingSchedule::LinearVestingWithCliff { + start_time: _start_time, + end_time, + cliff_time, + } => { + if block_time < cliff_time.u64() { + return Ok(Uint128::zero()); + } + + if block_time == cliff_time.u64() { + return Ok(self.cliff_amount); + } + + if block_time >= end_time.u64() { + return Ok(self.vesting_amount); + } + + let remaining_token = + self.vesting_amount.checked_sub(self.cliff_amount)?; + let vested_token = remaining_token + .checked_mul(Uint128::from(block_time - cliff_time.u64()))? + .checked_div(Uint128::from(end_time - cliff_time))?; + + Ok(vested_token + self.cliff_amount) + } + } + } +} diff --git a/contracts/airdrop-token-vesting/src/testing.rs b/contracts/airdrop-token-vesting/src/testing.rs index 60fa74e..6b20848 100644 --- a/contracts/airdrop-token-vesting/src/testing.rs +++ b/contracts/airdrop-token-vesting/src/testing.rs @@ -258,8 +258,6 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(start_time), end_time: Uint64::new(end_time), - vesting_amount: Uint128::zero(), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(cliff_time), }, } @@ -389,8 +387,6 @@ fn register_cliff_vesting_account_with_native_token() -> TestResult { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::zero(), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(cliff_time), }, }; @@ -454,8 +450,6 @@ fn test_withdraw() -> TestResult { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(start_time), end_time: Uint64::new(end_time), - vesting_amount: Uint128::zero(), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(cliff_time), }, } @@ -571,8 +565,6 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::zero(), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(105), }, }; @@ -595,8 +587,6 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(105), }, }; @@ -626,8 +616,6 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(105), }, }; @@ -650,8 +638,6 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::new(100u128), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(105), }, }; @@ -696,11 +682,10 @@ fn register_vesting_account_with_native_token() -> TestResult { vesting_account: crate::state::VestingAccount { address: "addr0001".to_string(), vesting_amount: Uint128::new(100u128), + cliff_amount: Uint128::zero(), vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::new(100u128), - cliff_amount: Uint128::zero(), cliff_time: Uint64::new(105), }, claimed_amount: Uint128::zero(), @@ -741,8 +726,6 @@ fn claim_native() -> TestResult { start_time: Uint64::new(100), cliff_time: Uint64::new(105), end_time: Uint64::new(110), - vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::new(500000u128), }, }; @@ -816,11 +799,10 @@ fn claim_native() -> TestResult { vesting_account: crate::state::VestingAccount { address: "addr0001".to_string(), vesting_amount: Uint128::new(1000000), + cliff_amount: Uint128::new(500000), vesting_schedule: VestingSchedule::LinearVestingWithCliff { start_time: Uint64::new(100), end_time: Uint64::new(110), - vesting_amount: Uint128::new(1000000u128), - cliff_amount: Uint128::new(500000u128), cliff_time: Uint64::new(105), }, claimed_amount: Uint128::new(500000),