safe: Change tests to use linear unlock function

This commit is contained in:
armaniferrante 2020-09-24 19:57:37 -07:00 committed by Armani Ferrante
parent 95bf25828a
commit eb131ef2c5
8 changed files with 155 additions and 136 deletions

1
Cargo.lock generated
View File

@ -1839,6 +1839,7 @@ name = "serum-safe"
version = "0.1.0"
dependencies = [
"bytemuck",
"lazy_static",
"num_enum",
"rand",
"serde",

View File

@ -16,7 +16,7 @@ fn burn() {
lsrm,
lsrm_token_acc_owner,
..
} = lifecycle::mint_lsrm(2, vec![10_000, 20_000, 30_000], vec![10, 20, 30]);
} = lifecycle::mint_lsrm(2, 10, 100_000, 1000);
let lsrm1 = &lsrm[0];

View File

@ -10,6 +10,7 @@ pub fn pass_time(client: &RpcClient, slot_num: u64) {
if retries == 0 {
assert!(false);
}
println!("SLEEPING TO PASS TIME {:?}", slot_num);
retries -= 1;
std::thread::sleep(std::time::Duration::from_millis(10));
}

View File

@ -146,16 +146,7 @@ pub struct Initialized {
pub srm_mint: Keypair,
}
pub fn deposit() -> Deposited {
let vesting_slots = vec![11, 12, 13, 14, 15];
let vesting_amounts = vec![1, 2, 3, 4, 5];
deposit_with_schedule(vesting_slots, vesting_amounts)
}
pub fn deposit_with_schedule(
vesting_slot_offsets: Vec<u64>,
vesting_amounts: Vec<u64>,
) -> Deposited {
pub fn deposit_with_schedule(deposit_amount: u64, end_slot: u64, period_count: u64) -> Deposited {
let Initialized {
client,
safe_acc,
@ -167,7 +158,7 @@ pub fn deposit_with_schedule(
..
} = initialize();
let (vesting_acc, vesting_acc_beneficiary, vesting_acc_slots, vesting_acc_amounts) = {
let (vesting_acc, vesting_acc_beneficiary) = {
let deposit_accs = [
AccountMeta::new(depositor.pubkey(), false),
// Authority of the depositing SPL account.
@ -176,42 +167,33 @@ pub fn deposit_with_schedule(
AccountMeta::new(safe_acc.pubkey(), false),
AccountMeta::new(spl_token::ID, false),
AccountMeta::new_readonly(solana_sdk::sysvar::rent::ID, false),
AccountMeta::new_readonly(solana_sdk::sysvar::clock::ID, false),
];
let current_slot = client.rpc().get_slot().unwrap();
let vesting_slots = vesting_slot_offsets
.iter()
.map(|offset| current_slot + offset)
.collect::<Vec<u64>>();
let vesting_acc_beneficiary = Keypair::generate(&mut OsRng);
let vesting_acc_size = Vesting::size_dyn(vesting_slots.len()).unwrap() as usize;
let (_signature, keypair) = client
.create_account_with_size_and_deposit(
vesting_acc_size,
.create_account_and_deposit(
&deposit_accs,
vesting_acc_beneficiary.pubkey(),
vesting_slots.clone(),
vesting_amounts.clone(),
end_slot,
period_count,
deposit_amount,
)
.unwrap();
(
keypair,
vesting_acc_beneficiary,
vesting_slots,
vesting_amounts,
)
(keypair, vesting_acc_beneficiary)
};
Deposited {
client,
vesting_acc_beneficiary,
vesting_acc: vesting_acc.pubkey(),
vesting_acc_slots,
vesting_acc_amounts,
safe_acc: safe_acc.pubkey(),
safe_srm_vault,
safe_srm_vault_authority,
srm_mint,
safe_authority,
end_slot,
period_count,
deposit_amount,
}
}
@ -219,32 +201,32 @@ pub struct Deposited {
pub client: Client,
pub vesting_acc_beneficiary: Keypair,
pub vesting_acc: Pubkey,
pub vesting_acc_slots: Vec<u64>,
pub vesting_acc_amounts: Vec<u64>,
pub safe_acc: Pubkey,
pub safe_srm_vault: Keypair,
pub safe_srm_vault_authority: Pubkey,
pub srm_mint: Keypair,
pub safe_authority: Keypair,
pub end_slot: u64,
pub period_count: u64,
pub deposit_amount: u64,
}
pub fn mint_lsrm(
nft_count: usize,
vesting_slot_offsets: Vec<u64>,
vesting_amounts: Vec<u64>,
deposit_amount: u64,
end_slot: u64,
period_count: u64,
) -> LsrmMinted {
let Deposited {
client,
vesting_acc,
vesting_acc_beneficiary,
vesting_acc_slots,
vesting_acc_amounts,
safe_acc,
safe_srm_vault,
safe_srm_vault_authority,
srm_mint,
..
} = deposit_with_schedule(vesting_slot_offsets, vesting_amounts);
} = deposit_with_schedule(deposit_amount, end_slot, period_count);
// Let the beneficiary be the owner for the NFTs.
let lsrm_token_acc_owner = Keypair::from_bytes(&vesting_acc_beneficiary.to_bytes()).unwrap();
@ -282,13 +264,14 @@ pub fn mint_lsrm(
vesting_acc,
vesting_acc_beneficiary,
srm_mint,
vesting_acc_slots,
vesting_acc_amounts,
safe_acc,
safe_srm_vault,
safe_srm_vault_authority,
lsrm,
lsrm_token_acc_owner,
deposit_amount,
end_slot,
period_count,
}
}
@ -297,12 +280,13 @@ pub struct LsrmMinted {
pub lsrm: Vec<ClientMint>,
pub vesting_acc: Pubkey,
pub vesting_acc_beneficiary: Keypair,
pub vesting_acc_slots: Vec<u64>,
pub vesting_acc_amounts: Vec<u64>,
pub safe_acc: Pubkey,
pub safe_srm_vault: Keypair,
pub safe_srm_vault_authority: Pubkey,
pub srm_mint: Keypair,
// Authority/owner of all the token accounts holding lSRM NFTs.
pub lsrm_token_acc_owner: Keypair,
pub deposit_amount: u64,
pub end_slot: u64,
pub period_count: u64,
}

View File

@ -26,7 +26,13 @@ fn deposit() {
// When.
//
// A depositor performs the vesting account deposit.
let (vesting_acc_kp, expected_beneficiary, expected_slots, expected_amounts) = {
let (
vesting_acc_kp,
expected_beneficiary,
expected_deposit,
expected_end_slot,
expected_period_count,
) = {
let deposit_accs = [
AccountMeta::new(depositor.pubkey(), false),
AccountMeta::new(client.payer().pubkey(), true), // Owner of the depositor SPL account.
@ -34,35 +40,33 @@ fn deposit() {
AccountMeta::new(safe_acc.pubkey(), false),
AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new_readonly(solana_sdk::sysvar::rent::ID, false),
AccountMeta::new_readonly(solana_sdk::sysvar::clock::ID, false),
];
let vesting_acc_beneficiary = Keypair::generate(&mut OsRng);
let vesting_slots = vec![11, 12, 13, 14, 15];
let vesting_amounts = vec![1, 2, 3, 4, 5];
let vesting_acc_size = Vesting::size_dyn(vesting_slots.len()).unwrap() as usize;
println!("EXPECTED_SIZE vesting_acc_size {:?}", vesting_acc_size);
let end_slot = 100_000;
let period_count = 1000;
let deposit_amount = 100;
let (_signature, keypair) = client
.create_account_with_size_and_deposit(
vesting_acc_size,
.create_account_and_deposit(
&deposit_accs,
vesting_acc_beneficiary.pubkey(),
vesting_slots.clone(),
vesting_amounts.clone(),
end_slot,
period_count,
deposit_amount,
)
.unwrap();
(
keypair,
vesting_acc_beneficiary,
vesting_slots,
vesting_amounts,
deposit_amount,
end_slot,
period_count,
)
};
// Then.
//
// Read the state of the program and ensure it's correct.
//
// Check.
//
// The vesting account is setup properly.
{
let vesting_acc = {
@ -77,8 +81,8 @@ fn deposit() {
assert_eq!(vesting_acc.safe, safe_acc.pubkey());
assert_eq!(vesting_acc.beneficiary, expected_beneficiary.pubkey());
assert_eq!(vesting_acc.initialized, true);
assert_eq!(vesting_acc.slots, expected_slots);
assert_eq!(vesting_acc.amounts, expected_amounts);
assert_eq!(vesting_acc.end_slot, expected_end_slot);
assert_eq!(vesting_acc.period_count, expected_period_count);
}
// Then.
//
@ -86,7 +90,7 @@ fn deposit() {
{
let depositor_spl_acc: spl_token::state::Account =
serum_common::client::rpc::account_token_unpacked(client.rpc(), &depositor.pubkey());
let expected_balance = depositor_balance_before - expected_amounts.iter().sum::<u64>();
let expected_balance = depositor_balance_before - expected_deposit;
assert_eq!(depositor_spl_acc.amount, expected_balance);
}
// Then.
@ -98,9 +102,8 @@ fn deposit() {
client.rpc(),
&safe_srm_vault.pubkey(),
);
let expected_balance = expected_amounts.iter().sum::<u64>();
assert_eq!(safe_vault_spl_acc.amount, expected_balance);
assert_eq!(safe_vault_spl_acc.amount, expected_deposit);
// Sanity check the owner of the vault account.
assert_eq!(safe_vault_spl_acc.owner, safe_srm_vault_authority,);
assert_eq!(safe_vault_spl_acc.owner, safe_srm_vault_authority);
}
}

View File

@ -12,7 +12,9 @@ mod common;
fn migrate() {
// Given.
//
// An initialized safe with deposit.
// An initialized safe with deposit (scheduled doesn't matter for
// this test).
let deposit_amount = 100;
let lifecycle::Deposited {
client,
safe_acc,
@ -21,7 +23,7 @@ fn migrate() {
srm_mint,
safe_authority,
..
} = lifecycle::deposit_with_schedule(vec![1, 2, 3, 4, 5], vec![100, 200, 300, 400, 500]);
} = lifecycle::deposit_with_schedule(100, 100_000, 1);
// And.
//
// An SPL account to transfer to.
@ -68,7 +70,7 @@ fn migrate() {
client.rpc(),
&receiver_token_acc.pubkey(),
);
assert_eq!(recipient.amount, 100 + 200 + 300 + 400 + 500);
assert_eq!(recipient.amount, deposit_amount);
}
// Then.

View File

@ -15,16 +15,18 @@ fn mint() {
// Given.
//
// An initialized Serum Safe with deposit.
let client = common::client();
let current_slot = client.rpc().get_slot().unwrap();
let end_slot = current_slot + 1000;
let start_balance = 20;
let period_count = 5;
let common::lifecycle::Deposited {
client,
vesting_acc,
vesting_acc_beneficiary,
vesting_acc_slots,
vesting_acc_amounts,
safe_acc,
safe_srm_vault_authority,
..
} = common::lifecycle::deposit();
} = common::lifecycle::deposit_with_schedule(start_balance, end_slot, period_count);
// When.
//
@ -135,7 +137,14 @@ fn mint() {
vesting_acc_beneficiary.pubkey()
);
assert_eq!(updated_vesting_acc.initialized, true);
assert_eq!(updated_vesting_acc.slots, vesting_acc_slots);
assert_eq!(updated_vesting_acc.amounts, vesting_acc_amounts);
assert_eq!(updated_vesting_acc.start_balance, start_balance);
assert_eq!(updated_vesting_acc.balance, start_balance);
assert_eq!(updated_vesting_acc.end_slot, end_slot);
assert_eq!(updated_vesting_acc.period_count, period_count);
// Time passes from the time we get the slot number executing
// the transaction. So just make sure the start_slot gets set
// somewhere in the middle.
let start_slot = updated_vesting_acc.start_slot;
assert!(current_slot <= start_slot && start_slot < end_slot);
}
}

View File

@ -12,52 +12,61 @@ use spl_token::pack::Pack as TokenPack;
mod common;
// Note: there's no way to programatically control slot time so when testing
// we're stuck with just waiting actual wall clock time. In theory
// this can make the tests brittle and break for unexpected reasons,
// e.g., the transaction gets mined at a slot that's too late and so
// the contract rejects it. Not an issue so far but it is something to be
// aware of.
// Summary.
//
// * Vesting amount of 30 for first period.
// * Vesting amount of 100 for first period.
// * First vesting period passes.
// * Withdraw 30 SRM.
// * Withdraw 10 SRM.
//
// Should receive the SRM.
#[test]
fn withdraw() {
withdraw_test(WithdrawTestParams {
test_type: TestType::Normal,
vesting_slot_offsets: vec![1, 100_000, 200_000],
vesting_amounts: vec![30, 40, 50],
expected_vesting_amounts: vec![0, 40, 50],
expected_withdraw_amount: 30,
expected_spl_balance: 30,
slot_wait_index: Some(0),
deposit_amount: 100,
end_slot_offset: 100,
period_count: 10,
expected_vesting_balance: 90,
withdraw_amount: 10,
expected_spl_balance: 10,
slot_wait_offset: Some(10),
error_code: None,
})
});
}
// Summary.
//
// * Vesting amount of 30.
// * Vesting period passes.
// * Withdraw 31.
// * Vesting amount of 100.
// * First vesting period passes.
// * Withdraw 50.
//
// Should not receive anything.
#[test]
fn withdraw_more_than_vested() {
withdraw_test(WithdrawTestParams {
test_type: TestType::Normal,
vesting_slot_offsets: vec![1, 100_000, 200_000],
vesting_amounts: vec![30, 40, 50],
expected_vesting_amounts: vec![30, 40, 50],
expected_withdraw_amount: 31,
deposit_amount: 100,
end_slot_offset: 100,
period_count: 10,
expected_vesting_balance: 100,
withdraw_amount: 50,
expected_spl_balance: 0,
slot_wait_index: Some(0),
error_code: Some(SafeErrorCode::InsufficientBalance.into()),
slot_wait_offset: Some(10),
error_code: Some(SafeErrorCode::InsufficientWithdrawalBalance.into()),
})
}
// Summary.
//
// * Vesting amount of 30.
// * Vesting period does not pass.
// * Vesting amount of 100.
// * First vesting period does not pass.
// * Withdraw 1.
//
// Should not receive anything.
@ -65,19 +74,20 @@ fn withdraw_more_than_vested() {
fn withdraw_without_vesting() {
withdraw_test(WithdrawTestParams {
test_type: TestType::Normal,
vesting_slot_offsets: vec![99_000, 100_000, 200_000],
vesting_amounts: vec![30, 40, 50],
expected_vesting_amounts: vec![30, 40, 50],
expected_withdraw_amount: 100,
deposit_amount: 100,
end_slot_offset: 100_000,
period_count: 2,
expected_vesting_balance: 100,
withdraw_amount: 1,
expected_spl_balance: 0,
slot_wait_index: None,
error_code: Some(SafeErrorCode::InsufficientBalance.into()),
slot_wait_offset: None,
error_code: Some(SafeErrorCode::InsufficientWithdrawalBalance.into()),
})
}
// Summary.
//
// * Vesting amount of 30.
// * Vesting amount of 100.
// * Mint 2 lSRM.
// * Vesting period passes.
// * Withdraw 20 SRM.
@ -87,49 +97,54 @@ fn withdraw_without_vesting() {
fn withdraw_some_when_locked_outstanding() {
withdraw_test(WithdrawTestParams {
test_type: TestType::LsrmMinted(2),
vesting_slot_offsets: vec![1, 100_000, 200_000],
vesting_amounts: vec![30, 40, 50],
expected_vesting_amounts: vec![10, 40, 50],
expected_withdraw_amount: 20,
deposit_amount: 100,
end_slot_offset: 100,
period_count: 10,
expected_vesting_balance: 80,
withdraw_amount: 20,
expected_spl_balance: 20,
slot_wait_index: Some(0),
slot_wait_offset: Some(20),
error_code: None,
})
}
// Summary.
//
// * Vesting amount of 30.
// * Vesting amount of 100.
// * Mint 2 LSRM.
// * Vesting period passes.
// * Withdraw 30 SRM.
// * Vesting schedule passes entirely.
// * Withdraw 99 SRM.
//
// Should not receive anything.
#[test]
fn withdraw_all_when_locked_outstanding() {
withdraw_test(WithdrawTestParams {
test_type: TestType::LsrmMinted(2),
vesting_slot_offsets: vec![1, 100_000, 200_000],
vesting_amounts: vec![30, 40, 50],
expected_vesting_amounts: vec![30, 40, 50],
expected_withdraw_amount: 30,
deposit_amount: 100,
end_slot_offset: 50,
period_count: 10,
expected_vesting_balance: 100,
withdraw_amount: 99,
expected_spl_balance: 0,
slot_wait_index: Some(0),
error_code: Some(SafeErrorCode::InsufficientBalance.into()),
slot_wait_offset: Some(10),
error_code: Some(SafeErrorCode::InsufficientWithdrawalBalance.into()),
})
}
fn withdraw_test(params: WithdrawTestParams) {
let WithdrawTestParams {
test_type,
vesting_slot_offsets,
vesting_amounts,
expected_vesting_amounts,
expected_withdraw_amount,
deposit_amount,
end_slot_offset,
period_count,
expected_vesting_balance,
withdraw_amount,
expected_spl_balance,
slot_wait_index,
slot_wait_offset,
error_code,
} = params;
let current_slot = common::client().rpc().get_slot().unwrap();
let end_slot = end_slot_offset + current_slot;
// Given.
//
// A vesting account.
@ -137,13 +152,12 @@ fn withdraw_test(params: WithdrawTestParams) {
client,
vesting_acc,
vesting_acc_beneficiary,
vesting_acc_slots,
safe_acc,
safe_srm_vault,
safe_srm_vault_authority,
srm_mint,
..
} = start_state(test_type, vesting_slot_offsets, vesting_amounts);
} = start_state(test_type, deposit_amount, end_slot, period_count);
// And.
//
// An empty SRM SPL token account.
@ -158,8 +172,9 @@ fn withdraw_test(params: WithdrawTestParams) {
// When.
//
// The vesting period passes (or doesn't if set to None).
if let Some(slot_wait_index) = slot_wait_index {
common::blockchain::pass_time(client.rpc(), vesting_acc_slots[slot_wait_index]);
if let Some(slot_wait_offset) = slot_wait_offset {
let slot_wait = current_slot + slot_wait_offset;
common::blockchain::pass_time(client.rpc(), slot_wait);
}
// And.
//
@ -176,7 +191,7 @@ fn withdraw_test(params: WithdrawTestParams) {
AccountMeta::new_readonly(sysvar::clock::ID, false),
];
let signers = [&vesting_acc_beneficiary, client.payer()];
let r = client.withdraw_with_signers(&signers, &accounts, expected_withdraw_amount);
let r = client.withdraw_with_signers(&signers, &accounts, withdraw_amount);
if error_code.is_some() {
match r {
Ok(_) => panic!("expected error code from withdrawal"),
@ -218,7 +233,7 @@ fn withdraw_test(params: WithdrawTestParams) {
.unwrap();
Vesting::unpack(&account.data).unwrap()
};
assert_eq!(vesting_acc.amounts, expected_vesting_amounts);
assert_eq!(vesting_acc.balance, expected_vesting_balance);
}
}
@ -228,40 +243,43 @@ type StartState = lifecycle::LsrmMinted;
fn start_state(
test_type: TestType,
vesting_slot_offsets: Vec<u64>,
vesting_amounts: Vec<u64>,
deposit_amount: u64,
end_slot: u64,
period_count: u64,
) -> StartState {
match test_type {
TestType::LsrmMinted(lsrm_count) => {
lifecycle::mint_lsrm(lsrm_count, vesting_slot_offsets, vesting_amounts)
lifecycle::mint_lsrm(lsrm_count, deposit_amount, end_slot, period_count)
}
TestType::Normal => {
let lifecycle::Deposited {
client,
vesting_acc,
vesting_acc_beneficiary,
vesting_acc_slots,
vesting_acc_amounts,
safe_acc,
safe_srm_vault,
safe_srm_vault_authority,
srm_mint,
deposit_amount,
end_slot,
period_count,
..
} = lifecycle::deposit_with_schedule(vesting_slot_offsets, vesting_amounts);
} = lifecycle::deposit_with_schedule(deposit_amount, end_slot, period_count);
// Dummy keypair to stuff into type. Not used.
let lsrm_token_acc_owner = Keypair::generate(&mut OsRng);
lifecycle::LsrmMinted {
client,
vesting_acc,
vesting_acc_beneficiary,
vesting_acc_slots,
vesting_acc_amounts,
safe_acc,
safe_srm_vault,
safe_srm_vault_authority,
srm_mint,
lsrm: vec![],
lsrm_token_acc_owner,
deposit_amount,
end_slot,
period_count,
}
}
}
@ -269,12 +287,13 @@ fn start_state(
struct WithdrawTestParams {
test_type: TestType,
vesting_slot_offsets: Vec<u64>,
vesting_amounts: Vec<u64>,
expected_vesting_amounts: Vec<u64>,
expected_withdraw_amount: u64,
deposit_amount: u64,
end_slot_offset: u64,
period_count: u64,
expected_vesting_balance: u64,
withdraw_amount: u64,
expected_spl_balance: u64,
slot_wait_index: Option<usize>,
slot_wait_offset: Option<u64>,
error_code: Option<u32>,
}