From 7e11fa3d7b6ac85a2d628a82323b00e568679b5d Mon Sep 17 00:00:00 2001 From: Armani Ferrante Date: Sat, 12 Dec 2020 00:14:57 -0800 Subject: [PATCH] registry: Expire reward drops (#61) --- registry/cli/src/lib.rs | 70 +++++++- registry/client/src/lib.rs | 90 +++++++++- registry/program/src/claim_locked_reward.rs | 11 +- registry/program/src/claim_unlocked_reward.rs | 11 +- registry/program/src/expire_locked_reward.rs | 156 ++++++++++++++++++ .../program/src/expire_unlocked_reward.rs | 156 ++++++++++++++++++ registry/program/src/lib.rs | 8 + registry/src/accounts/locked_reward_vendor.rs | 5 +- .../src/accounts/unlocked_reward_vendor.rs | 1 + registry/src/error.rs | 2 + registry/src/lib.rs | 14 ++ scripts/deploy-staking.sh | 19 ++- 12 files changed, 526 insertions(+), 17 deletions(-) create mode 100644 registry/program/src/expire_locked_reward.rs create mode 100644 registry/program/src/expire_unlocked_reward.rs diff --git a/registry/cli/src/lib.rs b/registry/cli/src/lib.rs index 6c073b2..f922cbb 100644 --- a/registry/cli/src/lib.rs +++ b/registry/cli/src/lib.rs @@ -2,7 +2,9 @@ use anyhow::{anyhow, Result}; use clap::Clap; use serum_common::client::rpc; use serum_context::Context; -use serum_registry::accounts::{Entity, Member, Registrar}; +use serum_registry::accounts::{ + Entity, LockedRewardVendor, Member, Registrar, UnlockedRewardVendor, +}; use serum_registry_client::*; use solana_client_gen::prelude::*; @@ -54,6 +56,28 @@ pub enum Command { #[clap(short, long)] registrar: Pubkey, }, + /// Sends all leftover funds from an expired unlocked reward vendor to a given + /// account. + ExpireUnlockedReward { + /// The token account to send the leftover rewards to. + #[clap(long)] + token: Pubkey, + #[clap(long)] + vendor: Pubkey, + #[clap(short, long)] + registrar: Pubkey, + }, + /// Sends all leftover funds from an expired locked reward vendor to a given + /// account. + ExpireLockedReward { + /// The token account to send the leftover rewards to. + #[clap(long)] + token: Pubkey, + #[clap(long)] + vendor: Pubkey, + #[clap(short, long)] + registrar: Pubkey, + }, } #[derive(Debug, Clap)] @@ -77,6 +101,14 @@ pub enum AccountsCommand { #[clap(short, long)] address: Option, }, + LockedVendor { + #[clap(short, long)] + address: Pubkey, + }, + UnlockedVendor { + #[clap(short, long)] + address: Pubkey, + }, } pub fn run(ctx: Context, cmd: Command) -> Result<()> { @@ -121,6 +153,34 @@ pub fn run(ctx: Context, cmd: Command) -> Result<()> { delegate, registrar, } => create_member_cmd(&ctx, registry_pid, registrar, entity, delegate), + Command::ExpireUnlockedReward { + token, + vendor, + registrar, + } => { + let client = ctx.connect::(registry_pid)?; + let resp = client.expire_unlocked_reward(ExpireUnlockedRewardRequest { + token, + vendor, + registrar, + })?; + println!("Transaction executed: {:?}", resp.tx); + Ok(()) + } + Command::ExpireLockedReward { + token, + vendor, + registrar, + } => { + let client = ctx.connect::(registry_pid)?; + let resp = client.expire_locked_reward(ExpireLockedRewardRequest { + token, + vendor, + registrar, + })?; + println!("Transaction executed: {:?}", resp.tx); + Ok(()) + } } } @@ -201,6 +261,14 @@ fn account_cmd(ctx: &Context, registry_pid: Pubkey, cmd: AccountsCommand) -> Res let acc: Member = rpc::get_account(&rpc_client, &address)?; println!("{:#?}", acc); } + AccountsCommand::LockedVendor { address } => { + let acc: LockedRewardVendor = rpc::get_account(&rpc_client, &address)?; + println!("{:#?}", acc); + } + AccountsCommand::UnlockedVendor { address } => { + let acc: UnlockedRewardVendor = rpc::get_account(&rpc_client, &address)?; + println!("{:#?}", acc); + } }; Ok(()) } diff --git a/registry/client/src/lib.rs b/registry/client/src/lib.rs index 85d23e1..d961b57 100644 --- a/registry/client/src/lib.rs +++ b/registry/client/src/lib.rs @@ -3,7 +3,8 @@ use serum_common::pack::*; use serum_meta_entity::accounts::mqueue::{MQueue, Ring as MQueueRing}; use serum_registry::accounts::reward_queue::{RewardEventQueue, Ring}; use serum_registry::accounts::{ - self, pending_withdrawal, vault, BalanceSandbox, Entity, Member, PendingWithdrawal, Registrar, + self, pending_withdrawal, vault, BalanceSandbox, Entity, LockedRewardVendor, Member, + PendingWithdrawal, Registrar, UnlockedRewardVendor, }; use serum_registry::client::{Client as InnerClient, ClientError as InnerClientError}; use solana_client_gen::prelude::*; @@ -11,6 +12,7 @@ use solana_client_gen::solana_sdk::instruction::AccountMeta; use solana_client_gen::solana_sdk::pubkey::Pubkey; use solana_client_gen::solana_sdk::signature::Signature; use solana_client_gen::solana_sdk::signature::{Keypair, Signer}; +use solana_client_gen::solana_sdk::sysvar; use spl_token::state::Account as TokenAccount; use std::convert::Into; use thiserror::Error; @@ -855,6 +857,64 @@ impl Client { .switch_entity_with_signers(&[self.payer(), beneficiary], &accs)?; Ok(SwitchEntityResponse { tx }) } + + pub fn expire_unlocked_reward( + &self, + req: ExpireUnlockedRewardRequest, + ) -> Result { + let ExpireUnlockedRewardRequest { + token, + vendor, + registrar, + } = req; + let vendor_acc = self.unlocked_vendor(&vendor)?; + let vendor_va = Pubkey::create_program_address( + &[registrar.as_ref(), vendor.as_ref(), &[vendor_acc.nonce]], + self.program(), + ) + .map_err(|_| ClientError::Any(anyhow::anyhow!("invalid vendor vault authority")))?; + let accs = vec![ + AccountMeta::new_readonly(self.payer().pubkey(), true), + AccountMeta::new(token, false), + AccountMeta::new(vendor, false), + AccountMeta::new(vendor_acc.vault, false), + AccountMeta::new_readonly(vendor_va, false), + AccountMeta::new_readonly(registrar, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(sysvar::clock::ID, false), + ]; + let tx = self.inner.expire_unlocked_reward(&accs)?; + Ok(ExpireUnlockedRewardResponse { tx }) + } + + pub fn expire_locked_reward( + &self, + req: ExpireLockedRewardRequest, + ) -> Result { + let ExpireLockedRewardRequest { + token, + vendor, + registrar, + } = req; + let vendor_acc = self.locked_vendor(&vendor)?; + let vendor_va = Pubkey::create_program_address( + &[registrar.as_ref(), vendor.as_ref(), &[vendor_acc.nonce]], + self.program(), + ) + .map_err(|_| ClientError::Any(anyhow::anyhow!("invalid vendor vault authority")))?; + let accs = vec![ + AccountMeta::new_readonly(self.payer().pubkey(), true), + AccountMeta::new(token, false), + AccountMeta::new(vendor, false), + AccountMeta::new(vendor_acc.vault, false), + AccountMeta::new_readonly(vendor_va, false), + AccountMeta::new_readonly(registrar, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(sysvar::clock::ID, false), + ]; + let tx = self.inner.expire_locked_reward(&accs)?; + Ok(ExpireLockedRewardResponse { tx }) + } } // Account accessors. @@ -1008,6 +1068,14 @@ impl Client { pub fn pending_withdrawal(&self, pw: &Pubkey) -> Result { rpc::get_account::(self.rpc(), pw).map_err(Into::into) } + + pub fn unlocked_vendor(&self, v: &Pubkey) -> Result { + rpc::get_account::(self.rpc(), v).map_err(Into::into) + } + + pub fn locked_vendor(&self, v: &Pubkey) -> Result { + rpc::get_account::(self.rpc(), v).map_err(Into::into) + } } pub struct ProgramAccount { @@ -1247,6 +1315,26 @@ pub struct SwitchEntityResponse { pub tx: Signature, } +pub struct ExpireUnlockedRewardRequest { + pub token: Pubkey, + pub vendor: Pubkey, + pub registrar: Pubkey, +} + +pub struct ExpireUnlockedRewardResponse { + pub tx: Signature, +} + +pub struct ExpireLockedRewardRequest { + pub token: Pubkey, + pub vendor: Pubkey, + pub registrar: Pubkey, +} + +pub struct ExpireLockedRewardResponse { + pub tx: Signature, +} + #[derive(Debug, Error)] pub enum ClientError { #[error("Client error {0}")] diff --git a/registry/program/src/claim_locked_reward.rs b/registry/program/src/claim_locked_reward.rs index 70fc5a5..94a42e5 100644 --- a/registry/program/src/claim_locked_reward.rs +++ b/registry/program/src/claim_locked_reward.rs @@ -194,6 +194,14 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> { clock, } = req; + // Move member rewards cursor. + member.rewards_cursor = cursor + 1; + + if vendor.expired { + msg!("Vendor expired. Reward not collected"); + return Ok(()); + } + // Create vesting account with proportion of the reward. let spt_total = spts.iter().map(|a| a.amount).fold(0, |a, b| a + b); let amount = spt_total @@ -258,9 +266,6 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> { )?; } - // Move member rewards cursor. - member.rewards_cursor = cursor + 1; - Ok(()) } diff --git a/registry/program/src/claim_unlocked_reward.rs b/registry/program/src/claim_unlocked_reward.rs index 590e8f7..5dc6efa 100644 --- a/registry/program/src/claim_unlocked_reward.rs +++ b/registry/program/src/claim_unlocked_reward.rs @@ -176,6 +176,14 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> { spts, } = req; + // Move member rewards cursor. + member.rewards_cursor = cursor + 1; + + if vendor.expired { + msg!("Vendor expired. Reward not collected"); + return Ok(()); + } + // Transfer proportion of the reward to the user. let spt_total = spts.iter().map(|a| a.amount).fold(0, |a, b| a + b); let amount = spt_total @@ -198,9 +206,6 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> { amount, )?; - // Move member rewards cursor. - member.rewards_cursor = cursor + 1; - Ok(()) } diff --git a/registry/program/src/expire_locked_reward.rs b/registry/program/src/expire_locked_reward.rs new file mode 100644 index 0000000..4e77536 --- /dev/null +++ b/registry/program/src/expire_locked_reward.rs @@ -0,0 +1,156 @@ +use serum_common::pack::Pack; +use serum_common::program::invoke_token_transfer; +use serum_registry::access_control; +use serum_registry::accounts::LockedRewardVendor; +use serum_registry::error::{RegistryError, RegistryErrorCode}; +use solana_program::msg; +use solana_sdk::account_info::{next_account_info, AccountInfo}; +use solana_sdk::pubkey::Pubkey; +use spl_token::state::Account as TokenAccount; + +#[inline(never)] +pub fn handler(program_id: &Pubkey, accounts: &[AccountInfo]) -> Result<(), RegistryError> { + msg!("handler: expire_locked_reward"); + + let acc_infos = &mut accounts.iter(); + + let expiry_receiver_acc_info = next_account_info(acc_infos)?; + let token_acc_info = next_account_info(acc_infos)?; + let vendor_acc_info = next_account_info(acc_infos)?; + let vault_acc_info = next_account_info(acc_infos)?; + let vault_authority_acc_info = next_account_info(acc_infos)?; + let registrar_acc_info = next_account_info(acc_infos)?; + let token_program_acc_info = next_account_info(acc_infos)?; + let clock_acc_info = next_account_info(acc_infos)?; + + let AccessControlResponse { ref vault } = access_control(AccessControlRequest { + program_id, + registrar_acc_info, + vendor_acc_info, + vault_acc_info, + token_acc_info, + expiry_receiver_acc_info, + clock_acc_info, + })?; + + LockedRewardVendor::unpack_mut( + &mut vendor_acc_info.try_borrow_mut_data()?, + &mut |vendor: &mut LockedRewardVendor| { + state_transition(StateTransitionRequest { + vendor, + vault, + registrar_acc_info, + vendor_acc_info, + vault_acc_info, + vault_authority_acc_info, + token_acc_info, + token_program_acc_info, + }) + .map_err(Into::into) + }, + ) + .map_err(Into::into) +} + +fn access_control(req: AccessControlRequest) -> Result { + msg!("access-control: expire_locked_reward"); + + let AccessControlRequest { + program_id, + expiry_receiver_acc_info, + registrar_acc_info, + vault_acc_info, + vendor_acc_info, + token_acc_info, + clock_acc_info, + } = req; + + // Authorization. + if !expiry_receiver_acc_info.is_signer { + return Err(RegistryErrorCode::Unauthorized)?; + } + + // Account validation. + let _registrar = access_control::registrar(registrar_acc_info, program_id)?; + let vendor = + access_control::locked_reward_vendor(vendor_acc_info, registrar_acc_info, program_id)?; + let vault = access_control::token_account(vault_acc_info)?; + let token = access_control::token_account(token_acc_info)?; + let clock = access_control::clock(clock_acc_info)?; + + if vendor.expired { + return Err(RegistryErrorCode::VendorAlreadyExpired)?; + } + if &vendor.vault != vault_acc_info.key { + return Err(RegistryErrorCode::InvalidVault)?; + } + if &vendor.expiry_receiver != expiry_receiver_acc_info.key { + return Err(RegistryErrorCode::InvalidVault)?; + } + if &token.owner != expiry_receiver_acc_info.key { + return Err(RegistryErrorCode::InvalidAccountOwner)?; + } + if clock.unix_timestamp <= vendor.expiry_ts { + return Err(RegistryErrorCode::VendorNotExpired)?; + } + + Ok(AccessControlResponse { vault }) +} + +fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> { + msg!("state-transition: expire_locked_reward"); + + let StateTransitionRequest { + vendor, + vault, + token_acc_info, + vendor_acc_info, + vault_acc_info, + vault_authority_acc_info, + registrar_acc_info, + token_program_acc_info, + } = req; + + let signer_seeds = &[ + registrar_acc_info.key.as_ref(), + vendor_acc_info.key.as_ref(), + &[vendor.nonce], + ]; + invoke_token_transfer( + vault_acc_info, + token_acc_info, + vault_authority_acc_info, + token_program_acc_info, + &[signer_seeds], + vault.amount, + )?; + + vendor.expired = true; + + Ok(()) +} + +struct AccessControlRequest<'a, 'b> { + program_id: &'a Pubkey, + expiry_receiver_acc_info: &'a AccountInfo<'b>, + registrar_acc_info: &'a AccountInfo<'b>, + vendor_acc_info: &'a AccountInfo<'b>, + vault_acc_info: &'a AccountInfo<'b>, + token_acc_info: &'a AccountInfo<'b>, + clock_acc_info: &'a AccountInfo<'b>, +} + +struct AccessControlResponse { + vault: TokenAccount, +} + +struct StateTransitionRequest<'a, 'b, 'c> { + vendor: &'c mut LockedRewardVendor, + vault: &'c TokenAccount, + registrar_acc_info: &'a AccountInfo<'b>, + vendor_acc_info: &'a AccountInfo<'b>, + vault_authority_acc_info: &'a AccountInfo<'b>, + vault_acc_info: &'a AccountInfo<'b>, + token_program_acc_info: &'a AccountInfo<'b>, + token_acc_info: &'a AccountInfo<'b>, +} diff --git a/registry/program/src/expire_unlocked_reward.rs b/registry/program/src/expire_unlocked_reward.rs new file mode 100644 index 0000000..aa2b9c3 --- /dev/null +++ b/registry/program/src/expire_unlocked_reward.rs @@ -0,0 +1,156 @@ +use serum_common::pack::Pack; +use serum_common::program::invoke_token_transfer; +use serum_registry::access_control; +use serum_registry::accounts::UnlockedRewardVendor; +use serum_registry::error::{RegistryError, RegistryErrorCode}; +use solana_program::msg; +use solana_sdk::account_info::{next_account_info, AccountInfo}; +use solana_sdk::pubkey::Pubkey; +use spl_token::state::Account as TokenAccount; + +#[inline(never)] +pub fn handler(program_id: &Pubkey, accounts: &[AccountInfo]) -> Result<(), RegistryError> { + msg!("handler: expire_unlocked_reward"); + + let acc_infos = &mut accounts.iter(); + + let expiry_receiver_acc_info = next_account_info(acc_infos)?; + let token_acc_info = next_account_info(acc_infos)?; + let vendor_acc_info = next_account_info(acc_infos)?; + let vault_acc_info = next_account_info(acc_infos)?; + let vault_authority_acc_info = next_account_info(acc_infos)?; + let registrar_acc_info = next_account_info(acc_infos)?; + let token_program_acc_info = next_account_info(acc_infos)?; + let clock_acc_info = next_account_info(acc_infos)?; + + let AccessControlResponse { ref vault } = access_control(AccessControlRequest { + program_id, + registrar_acc_info, + vendor_acc_info, + vault_acc_info, + token_acc_info, + expiry_receiver_acc_info, + clock_acc_info, + })?; + + UnlockedRewardVendor::unpack_mut( + &mut vendor_acc_info.try_borrow_mut_data()?, + &mut |vendor: &mut UnlockedRewardVendor| { + state_transition(StateTransitionRequest { + vendor, + vault, + registrar_acc_info, + vendor_acc_info, + vault_acc_info, + vault_authority_acc_info, + token_acc_info, + token_program_acc_info, + }) + .map_err(Into::into) + }, + ) + .map_err(Into::into) +} + +fn access_control(req: AccessControlRequest) -> Result { + msg!("access-control: expire_unlocked_reward"); + + let AccessControlRequest { + program_id, + expiry_receiver_acc_info, + registrar_acc_info, + vault_acc_info, + vendor_acc_info, + token_acc_info, + clock_acc_info, + } = req; + + // Authorization. + if !expiry_receiver_acc_info.is_signer { + return Err(RegistryErrorCode::Unauthorized)?; + } + + // Account validation. + let _registrar = access_control::registrar(registrar_acc_info, program_id)?; + let vendor = + access_control::unlocked_reward_vendor(vendor_acc_info, registrar_acc_info, program_id)?; + let vault = access_control::token_account(vault_acc_info)?; + let token = access_control::token_account(token_acc_info)?; + let clock = access_control::clock(clock_acc_info)?; + + if vendor.expired { + return Err(RegistryErrorCode::VendorAlreadyExpired)?; + } + if &vendor.vault != vault_acc_info.key { + return Err(RegistryErrorCode::InvalidVault)?; + } + if &vendor.expiry_receiver != expiry_receiver_acc_info.key { + return Err(RegistryErrorCode::InvalidVault)?; + } + if &token.owner != expiry_receiver_acc_info.key { + return Err(RegistryErrorCode::InvalidAccountOwner)?; + } + if clock.unix_timestamp <= vendor.expiry_ts { + return Err(RegistryErrorCode::VendorNotExpired)?; + } + + Ok(AccessControlResponse { vault }) +} + +fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> { + msg!("state-transition: expire_unlocked_reward"); + + let StateTransitionRequest { + vendor, + vault, + token_acc_info, + vendor_acc_info, + vault_acc_info, + vault_authority_acc_info, + registrar_acc_info, + token_program_acc_info, + } = req; + + let signer_seeds = &[ + registrar_acc_info.key.as_ref(), + vendor_acc_info.key.as_ref(), + &[vendor.nonce], + ]; + invoke_token_transfer( + vault_acc_info, + token_acc_info, + vault_authority_acc_info, + token_program_acc_info, + &[signer_seeds], + vault.amount, + )?; + + vendor.expired = true; + + Ok(()) +} + +struct AccessControlRequest<'a, 'b> { + program_id: &'a Pubkey, + expiry_receiver_acc_info: &'a AccountInfo<'b>, + registrar_acc_info: &'a AccountInfo<'b>, + vendor_acc_info: &'a AccountInfo<'b>, + vault_acc_info: &'a AccountInfo<'b>, + token_acc_info: &'a AccountInfo<'b>, + clock_acc_info: &'a AccountInfo<'b>, +} + +struct AccessControlResponse { + vault: TokenAccount, +} + +struct StateTransitionRequest<'a, 'b, 'c> { + vendor: &'c mut UnlockedRewardVendor, + vault: &'c TokenAccount, + registrar_acc_info: &'a AccountInfo<'b>, + vendor_acc_info: &'a AccountInfo<'b>, + vault_authority_acc_info: &'a AccountInfo<'b>, + vault_acc_info: &'a AccountInfo<'b>, + token_program_acc_info: &'a AccountInfo<'b>, + token_acc_info: &'a AccountInfo<'b>, +} diff --git a/registry/program/src/lib.rs b/registry/program/src/lib.rs index 9fa14cc..9286698 100644 --- a/registry/program/src/lib.rs +++ b/registry/program/src/lib.rs @@ -16,6 +16,8 @@ mod deposit; mod drop_locked_reward; mod drop_unlocked_reward; mod end_stake_withdrawal; +mod expire_locked_reward; +mod expire_unlocked_reward; mod initialize; mod stake; mod start_stake_withdrawal; @@ -129,6 +131,12 @@ fn entry(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) RegistryInstruction::ClaimUnlockedReward { cursor } => { claim_unlocked_reward::handler(program_id, accounts, cursor) } + RegistryInstruction::ExpireUnlockedReward => { + expire_unlocked_reward::handler(program_id, accounts) + } + RegistryInstruction::ExpireLockedReward => { + expire_locked_reward::handler(program_id, accounts) + } }; result?; diff --git a/registry/src/accounts/locked_reward_vendor.rs b/registry/src/accounts/locked_reward_vendor.rs index 752b844..b3f7311 100644 --- a/registry/src/accounts/locked_reward_vendor.rs +++ b/registry/src/accounts/locked_reward_vendor.rs @@ -12,10 +12,6 @@ pub struct LockedRewardVendor { pub nonce: u8, pub pool: Pubkey, pub pool_token_supply: u64, - // The position of the reward event associated with this vendor. - // Used to perform access control on member accounts attempting - // to claim the reward. Reject any member who's cursor is greater - // than this cursor. pub reward_event_q_cursor: u32, pub start_ts: i64, pub end_ts: i64, @@ -23,6 +19,7 @@ pub struct LockedRewardVendor { pub expiry_receiver: Pubkey, pub total: u64, pub period_count: u64, + pub expired: bool, } impl LockedRewardVendor { diff --git a/registry/src/accounts/unlocked_reward_vendor.rs b/registry/src/accounts/unlocked_reward_vendor.rs index 1c60315..b4170f2 100644 --- a/registry/src/accounts/unlocked_reward_vendor.rs +++ b/registry/src/accounts/unlocked_reward_vendor.rs @@ -17,6 +17,7 @@ pub struct UnlockedRewardVendor { pub expiry_ts: i64, pub expiry_receiver: Pubkey, pub total: u64, + pub expired: bool, } impl UnlockedRewardVendor { diff --git a/registry/src/error.rs b/registry/src/error.rs index 4ae6957..622ae9b 100644 --- a/registry/src/error.rs +++ b/registry/src/error.rs @@ -80,6 +80,8 @@ pub enum RegistryErrorCode { InvalidSpt = 66, InvalidPendingWithdrawalVault = 67, InvalidStakeVault = 68, + VendorAlreadyExpired = 69, + VendorNotExpired = 70, Unknown = 1000, } diff --git a/registry/src/lib.rs b/registry/src/lib.rs index 014cf50..7b49ca4 100644 --- a/registry/src/lib.rs +++ b/registry/src/lib.rs @@ -183,6 +183,20 @@ pub mod instruction { /// /// ClaimUnlockedReward { cursor: u32 }, + /// Accounts: + /// + /// 0. `[signer]` Expiry receiver. + /// 1. `[writable]` Token account to send leftover rewards to. + /// 2. `[writable]` Vendor. + /// 3. `[writable]` Vendor vault. + /// 4. `[]` Vendor vault authority. + /// 5. `[]` Registrar. + /// 6. `[]` Token program. + /// 7. `[]` Clock sysvar. + ExpireUnlockedReward, + /// Same as ExpireUnlockedReward, but with a LockedRewardVendor + /// account. + ExpireLockedReward, } } diff --git a/scripts/deploy-staking.sh b/scripts/deploy-staking.sh index dc63e49..04b2a52 100755 --- a/scripts/deploy-staking.sh +++ b/scripts/deploy-staking.sh @@ -21,8 +21,17 @@ STAKE_RATE_MEGA=1 REWARD_ACTIVATION_THRESHOLD=1 CONFIG_FILE=~/.config/serum/cli/dev.yaml +serum=$(pwd)/target/debug/serum main() { + # + # Check the CLI is built or installed. + # + if ! command -v $serum &> /dev/null + then + echo "Serum CLI not installed" + exit + fi # # Build all programs. # @@ -47,7 +56,7 @@ main() { # # Generate genesis state. # - local genesis=$(cargo run -p serum-cli -- dev init-mint) + local genesis=$($serum dev init-mint) local srm_mint=$(echo $genesis | jq .srmMint -r) local msrm_mint=$(echo $genesis | jq .msrmMint -r) @@ -78,7 +87,7 @@ EOM # # Now intialize all the accounts. # - local rInit=$(cargo run -p serum-cli -- --config $CONFIG_FILE \ + local rInit=$($serum --config $CONFIG_FILE \ registry init \ --deactivation-timelock $DEACTIVATION_TIMELOCK \ --reward-activation-threshold $REWARD_ACTIVATION_THRESHOLD \ @@ -91,7 +100,7 @@ EOM local registrar_nonce=$(echo $rInit | jq .nonce -r) local reward_q=$(echo $rInit | jq .rewardEventQueue -r) - local lInit=$(cargo run -p serum-cli -- --config $CONFIG_FILE \ + local lInit=$($serum --config $CONFIG_FILE \ lockup \ initialize) @@ -101,7 +110,7 @@ EOM # Initialize a node entity. Hack until we separate joining entities # from creating member accounts. # - local createEntity=$(cargo run -p serum-cli -- --config $CONFIG_FILE \ + local createEntity=$($serum --config $CONFIG_FILE \ registry create-entity \ --registrar $registrar \ --about "This the default entity all new members join." \ @@ -114,7 +123,7 @@ EOM # # Add the registry to the lockup program whitelist. # - cargo run -p serum-cli -- --config $CONFIG_FILE \ + $serum --config $CONFIG_FILE \ lockup gov \ --safe $safe \ whitelist-add \