Skip to content

Commit

Permalink
feat: validating time deltas (#7)
Browse files Browse the repository at this point in the history
Only a set of possible time-deltas should be allowed (e.g.) 0 (for
unlocked with withdrawal period), 1 month, 3 months, and so on... The
locking method should revert if the user tries to lock for a different
amount of time.

This PR replaces the concept of Dates when creating a lockup, since the
user will be inputting a TimeDelta, and internally we should calculate
the Date, to keep the same infra as it was.
  • Loading branch information
wei3erHase authored Sep 25, 2024
1 parent f387724 commit ed2f662
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 13 deletions.
10 changes: 5 additions & 5 deletions program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Arbitrary for VestingInstruction {
#[derive(Clone, Debug, PartialEq)]
pub struct Schedule {
// Schedule release time in unix timestamp
pub release_time: u64,
pub time_delta: u64,
pub amount: u64,
}

Expand Down Expand Up @@ -131,7 +131,7 @@ impl VestingInstruction {
.map(Pubkey::new_from_array)
.ok_or(InvalidInstruction)?;
let offset = 64;
let release_time = rest
let time_delta = rest
.get(offset..offset + 8)
.and_then(|slice| slice.try_into().ok())
.map(u64::from_le_bytes)
Expand All @@ -142,7 +142,7 @@ impl VestingInstruction {
.map(u64::from_le_bytes)
.ok_or(InvalidInstruction)?;
let schedule = Schedule {
release_time,
time_delta,
amount,
};
Self::Create {
Expand Down Expand Up @@ -185,7 +185,7 @@ impl VestingInstruction {
buf.push(1);
buf.extend_from_slice(seeds);
buf.extend_from_slice(&mint_address.to_bytes());
buf.extend_from_slice(&schedule.release_time.to_le_bytes());
buf.extend_from_slice(&schedule.time_delta.to_le_bytes());
buf.extend_from_slice(&schedule.amount.to_le_bytes());
}
&Self::Unlock { seeds } => {
Expand Down Expand Up @@ -321,7 +321,7 @@ mod test {
seeds: [50u8; 32],
schedule: Schedule {
amount: 42,
release_time: 250,
time_delta: 250,
},
mint_address: mint_address.clone(),
};
Expand Down
21 changes: 20 additions & 1 deletion program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,27 @@ impl Processor {

let mut total_amount: u64 = 0;

// NOTE: validate time delta to be 0 (unlocked), or a set of predefined values (1 month, 3 months, ...)
let release_time;
match schedule.time_delta {
0 => {
release_time = 0;
},
1 | 100 | 300 => { // TODO: replace for validated values
// Valid time_delta values, do nothing
// TODO: make test advance in time between initialize and unlock
// let clock = Clock::from_account_info(&clock_sysvar_account)?;
// release_time = clock.unix_timestamp as u64 + schedule.time_delta; // TODO: uncomment
release_time = schedule.time_delta; // NOTE: For testing purposes, keeping previous behavior
}
_ => {
msg!("Unsupported time delta");
return Err(ProgramError::InvalidInstructionData);
}
}

let state_schedule = VestingSchedule {
release_time: schedule.release_time,
release_time: release_time,
amount: schedule.amount,
};
state_schedule.pack_into_slice(&mut data[VestingScheduleHeader::LEN..]);
Expand Down
19 changes: 12 additions & 7 deletions program/tests/functional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
use std::str::FromStr;

use solana_program::{hash::Hash, pubkey::Pubkey, rent::Rent, system_program, sysvar};
use solana_test_framework::ProgramTestContextExtension;
use solana_program_test::{processor, ProgramTest, ProgramTestContext};
use solana_sdk::{account::Account, signature::Keypair, signature::Signer, system_instruction, transaction::Transaction};
use solana_sdk::{account::Account, signature::Keypair, signature::Signer, system_instruction,transaction::Transaction};
use solana_test_framework::ProgramTestContextExtension;
use spl_token::{self,instruction::{initialize_account, initialize_mint, mint_to}};
use token_vesting::instruction::{create, init, initialize_unlock, unlock};
use token_vesting::{entrypoint::process_instruction, instruction::Schedule};
use token_vesting::instruction::{init, unlock, create, initialize_unlock};
use spl_token::{self, instruction::{initialize_mint, initialize_account, mint_to}};

#[tokio::test]
async fn test_token_vesting() {
Expand Down Expand Up @@ -94,7 +94,7 @@ async fn test_token_vesting() {

let schedule = Schedule {
amount: 100,
release_time: 1,
time_delta: 1,
};

let test_instructions = [
Expand Down Expand Up @@ -134,7 +134,12 @@ async fn test_token_vesting() {
Transaction::new_with_payer(&test_instructions, Some(&payer.pubkey()));
test_transaction.partial_sign(&[&payer, &source_account], recent_blockhash);

banks_client.process_transaction(test_transaction).await.unwrap();
// NOTE: we're NOT doing `now() + time_delta` in the program (but we should), that's why this test passes
// TODO: add warp_to_timestamp to correctly test the behaviour
banks_client
.process_transaction(test_transaction)
.await
.unwrap();
}

#[tokio::test]
Expand Down Expand Up @@ -238,7 +243,7 @@ async fn test_token_unlocking() {

let schedule = Schedule {
amount: 100,
release_time: 0,
time_delta: 0,
};

let test_instructions = [
Expand Down

0 comments on commit ed2f662

Please sign in to comment.