Migrateable lockups and misc fixes (#62)

This commit is contained in:
Armani Ferrante 2020-12-15 00:36:49 -08:00 committed by GitHub
parent 911c63b52d
commit 7261556aba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 347 additions and 269 deletions

2
Cargo.lock generated
View File

@ -3331,6 +3331,7 @@ dependencies = [
"serde_json",
"serum-common",
"serum-context",
"serum-meta-entity",
"serum-registry",
"serum-registry-client",
"solana-client",
@ -3349,7 +3350,6 @@ dependencies = [
"lazy_static",
"serum-common",
"serum-meta-entity",
"serum-pool-schema",
"serum-registry",
"solana-client",
"solana-client-gen",

View File

@ -120,11 +120,11 @@ impl Client {
let whitelist = s.whitelist;
let mut accounts = vec![
AccountMeta::new_readonly(beneficiary.pubkey(), true),
AccountMeta::new(vesting, false),
AccountMeta::new_readonly(safe, false),
AccountMeta::new_readonly(whitelist, false),
AccountMeta::new_readonly(whitelist_program, false),
// Below are relay accounts.
AccountMeta::new(vesting, false),
AccountMeta::new(vault, false),
AccountMeta::new_readonly(
self.vault_authority(safe, vesting, beneficiary.pubkey())?,
@ -170,13 +170,13 @@ impl Client {
let whitelist = s.whitelist;
let mut accounts = vec![
AccountMeta::new_readonly(beneficiary.pubkey(), true),
AccountMeta::new(vesting, false),
AccountMeta::new_readonly(safe, false),
AccountMeta::new_readonly(whitelist, false),
AccountMeta::new_readonly(whitelist_program, false),
// Below are relay accounts.
//
// Whitelist relay interface.
AccountMeta::new(vesting, false),
AccountMeta::new(vault, false),
AccountMeta::new_readonly(
self.vault_authority(safe, vesting, beneficiary.pubkey())?,

View File

@ -1,5 +1,4 @@
use serum_lockup::accounts::vault;
use serum_lockup::accounts::Vesting;
use solana_sdk::account_info::AccountInfo;
use solana_sdk::entrypoint::ProgramResult;
use solana_sdk::instruction::Instruction;
@ -11,9 +10,9 @@ pub fn whitelist_cpi(
instruction: Instruction,
safe: &Pubkey,
beneficiary_acc_info: &AccountInfo,
vesting: &Vesting,
vesting_nonce: u8,
accounts: &[AccountInfo],
) -> ProgramResult {
let signer_seeds = vault::signer_seeds(safe, beneficiary_acc_info.key, &vesting.nonce);
let signer_seeds = vault::signer_seeds(safe, beneficiary_acc_info.key, &vesting_nonce);
solana_sdk::program::invoke_signed(&instruction, accounts, &[&signer_seeds])
}

View File

@ -7,7 +7,6 @@ use solana_sdk::account_info::{next_account_info, AccountInfo};
use solana_sdk::instruction::{AccountMeta, Instruction};
use solana_sdk::program_pack::Pack as TokenPack;
use solana_sdk::pubkey::Pubkey;
use std::convert::Into;
use std::iter::Iterator;
pub fn handler(
@ -20,7 +19,6 @@ pub fn handler(
let acc_infos = &mut accounts.iter();
let beneficiary_acc_info = next_account_info(acc_infos)?;
let vesting_acc_info = next_account_info(acc_infos)?;
let safe_acc_info = next_account_info(acc_infos)?;
let wl_acc_info = next_account_info(acc_infos)?;
let wl_prog_acc_info = next_account_info(acc_infos)?;
@ -28,6 +26,7 @@ pub fn handler(
// Below accounts are relayed.
// Whitelist interface.
let vesting_acc_info = next_account_info(acc_infos)?;
let vault_acc_info = next_account_info(acc_infos)?;
let vault_auth_acc_info = next_account_info(acc_infos)?;
let tok_prog_acc_info = next_account_info(acc_infos)?;
@ -37,7 +36,10 @@ pub fn handler(
// Program specific.
let remaining_relay_accs = acc_infos;
access_control(AccessControlRequest {
let AccessControlResponse {
vesting_nonce,
vesting_whitelist_owned,
} = access_control(AccessControlRequest {
program_id,
beneficiary_acc_info,
vesting_acc_info,
@ -49,10 +51,6 @@ pub fn handler(
vault_acc_info,
vault_auth_acc_info,
})?;
Vesting::unpack_unchecked_mut(
&mut vesting_acc_info.try_borrow_mut_data()?,
&mut |vesting: &mut Vesting| {
state_transition(StateTransitionRequest {
accounts,
instruction_data,
@ -63,18 +61,17 @@ pub fn handler(
vault_acc_info,
vault_auth_acc_info,
tok_prog_acc_info,
vesting,
vesting_nonce,
vesting_whitelist_owned,
vesting_acc_info,
beneficiary_acc_info,
remaining_relay_accs,
})
.map_err(Into::into)
},
)?;
})?;
Ok(())
}
fn access_control(req: AccessControlRequest) -> Result<(), LockupError> {
fn access_control(req: AccessControlRequest) -> Result<AccessControlResponse, LockupError> {
msg!("access-control: whitelist_deposit");
let AccessControlRequest {
@ -107,7 +104,7 @@ fn access_control(req: AccessControlRequest) -> Result<(), LockupError> {
safe_acc_info,
program_id,
)?;
let _vesting = access_control::vesting(
let vesting = access_control::vesting(
program_id,
safe_acc_info,
vesting_acc_info,
@ -129,14 +126,19 @@ fn access_control(req: AccessControlRequest) -> Result<(), LockupError> {
return Err(LockupErrorCode::InvalidTokenAccountOwner)?;
}
Ok(())
Ok(AccessControlResponse {
vesting_nonce: vesting.nonce,
vesting_whitelist_owned: vesting.whitelist_owned,
})
}
fn state_transition(req: StateTransitionRequest) -> Result<(), LockupError> {
msg!("state-transition: whitelist_deposit");
let StateTransitionRequest {
vesting,
vesting_nonce,
vesting_whitelist_owned,
vesting_acc_info,
instruction_data,
accounts,
safe_acc_info,
@ -158,6 +160,7 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), LockupError> {
// Invoke relay, signing with the program-derived-address.
{
let mut meta_accounts = vec![
AccountMeta::new_readonly(*vesting_acc_info.key, false),
AccountMeta::new(*vault_acc_info.key, false),
AccountMeta::new_readonly(*vault_auth_acc_info.key, true),
AccountMeta::new_readonly(*tok_prog_acc_info.key, false),
@ -180,7 +183,7 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), LockupError> {
relay_instruction,
safe_acc_info.key,
beneficiary_acc_info,
vesting,
vesting_nonce,
accounts,
)?;
}
@ -197,12 +200,20 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), LockupError> {
return Err(LockupErrorCode::InsufficientDepositAmount)?;
}
// Cannot deposit more than withdrawn.
if deposit_amount > vesting.whitelist_owned {
if deposit_amount > vesting_whitelist_owned {
return Err(LockupErrorCode::DepositOverflow)?;
}
Vesting::unpack_unchecked_mut(
&mut vesting_acc_info.try_borrow_mut_data()?,
&mut |vesting: &mut Vesting| {
// Book keeping.
vesting.whitelist_owned -= deposit_amount;
Ok(())
},
)?;
Ok(())
}
@ -219,6 +230,11 @@ struct AccessControlRequest<'a, 'b> {
vault_auth_acc_info: &'a AccountInfo<'b>,
}
struct AccessControlResponse {
vesting_nonce: u8,
vesting_whitelist_owned: u64,
}
struct StateTransitionRequest<'a, 'b, 'c> {
remaining_relay_accs: &'c mut dyn Iterator<Item = &'a AccountInfo<'b>>,
accounts: &'a [AccountInfo<'b>],
@ -230,6 +246,8 @@ struct StateTransitionRequest<'a, 'b, 'c> {
wl_prog_vault_authority_acc_info: &'a AccountInfo<'b>,
tok_prog_acc_info: &'a AccountInfo<'b>,
beneficiary_acc_info: &'a AccountInfo<'b>,
vesting_acc_info: &'a AccountInfo<'b>,
instruction_data: &'c [u8],
vesting: &'c mut Vesting,
vesting_nonce: u8,
vesting_whitelist_owned: u64,
}

View File

@ -8,7 +8,6 @@ use solana_sdk::instruction::{AccountMeta, Instruction};
use solana_sdk::program_pack::Pack as TokenPack;
use solana_sdk::pubkey::Pubkey;
use spl_token::state::Account as TokenAccount;
use std::convert::Into;
pub fn handler(
program_id: &Pubkey,
@ -21,7 +20,6 @@ pub fn handler(
let acc_infos = &mut accounts.iter();
let beneficiary_acc_info = next_account_info(acc_infos)?;
let vesting_acc_info = next_account_info(acc_infos)?;
let safe_acc_info = next_account_info(acc_infos)?;
let wl_acc_info = next_account_info(acc_infos)?;
let wl_prog_acc_info = next_account_info(acc_infos)?;
@ -29,6 +27,7 @@ pub fn handler(
// Below accounts are relayed.
// Whitelist interface.
let vesting_acc_info = next_account_info(acc_infos)?;
let vault_acc_info = next_account_info(acc_infos)?;
let vault_auth_acc_info = next_account_info(acc_infos)?;
let tok_prog_acc_info = next_account_info(acc_infos)?;
@ -38,7 +37,7 @@ pub fn handler(
// Program specific.
let remaining_relay_accs = acc_infos;
access_control(AccessControlRequest {
let AccessControlResponse { vesting_nonce } = access_control(AccessControlRequest {
program_id,
beneficiary_acc_info,
vesting_acc_info,
@ -51,13 +50,11 @@ pub fn handler(
vault_auth_acc_info,
amount,
})?;
Vesting::unpack_unchecked_mut(
&mut vesting_acc_info.try_borrow_mut_data()?,
&mut |vesting: &mut Vesting| {
state_transition(StateTransitionRequest {
accounts,
amount,
vesting_nonce,
vesting_acc_info,
instruction_data,
safe_acc_info,
wl_prog_acc_info,
@ -66,18 +63,14 @@ pub fn handler(
vault_acc_info,
vault_auth_acc_info,
tok_prog_acc_info,
vesting,
beneficiary_acc_info,
remaining_relay_accs,
})
.map_err(Into::into)
},
)?;
})?;
Ok(())
}
fn access_control(req: AccessControlRequest) -> Result<(), LockupError> {
fn access_control(req: AccessControlRequest) -> Result<AccessControlResponse, LockupError> {
msg!("access-control: whitelist_withdraw");
let AccessControlRequest {
@ -137,14 +130,17 @@ fn access_control(req: AccessControlRequest) -> Result<(), LockupError> {
return Err(LockupErrorCode::InvalidTokenAccountOwner)?;
}
Ok(())
Ok(AccessControlResponse {
vesting_nonce: vesting.nonce,
})
}
fn state_transition(req: StateTransitionRequest) -> Result<(), LockupError> {
msg!("state-transition: whitelist_withdraw");
let StateTransitionRequest {
vesting,
vesting_acc_info,
vesting_nonce,
instruction_data,
beneficiary_acc_info,
accounts,
@ -167,6 +163,7 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), LockupError> {
// Invoke relay.
{
let mut meta_accounts = vec![
AccountMeta::new_readonly(*vesting_acc_info.key, false),
AccountMeta::new(*vault_acc_info.key, false),
AccountMeta::new_readonly(*vault_auth_acc_info.key, true),
AccountMeta::new_readonly(*tok_prog_acc_info.key, false),
@ -189,7 +186,7 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), LockupError> {
relay_instruction,
safe_acc_info.key,
beneficiary_acc_info,
vesting,
vesting_nonce,
accounts,
)?;
}
@ -205,8 +202,14 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), LockupError> {
if amount_transferred > amount {
return Err(LockupErrorCode::InsufficientAmount)?;
}
// Book keeping.
Vesting::unpack_unchecked_mut(
&mut vesting_acc_info.try_borrow_mut_data()?,
&mut |vesting: &mut Vesting| {
vesting.whitelist_owned += amount_transferred;
Ok(())
},
)?;
Ok(())
}
@ -225,6 +228,10 @@ struct AccessControlRequest<'a, 'b> {
amount: u64,
}
struct AccessControlResponse {
vesting_nonce: u8,
}
struct StateTransitionRequest<'a, 'b, 'c> {
remaining_relay_accs: &'c mut dyn Iterator<Item = &'a AccountInfo<'b>>,
accounts: &'a [AccountInfo<'b>],
@ -236,7 +243,8 @@ struct StateTransitionRequest<'a, 'b, 'c> {
vault_auth_acc_info: &'a AccountInfo<'b>,
beneficiary_acc_info: &'a AccountInfo<'b>,
safe_acc_info: &'a AccountInfo<'b>,
vesting_acc_info: &'a AccountInfo<'b>,
instruction_data: &'c [u8],
vesting: &'c mut Vesting,
vesting_nonce: u8,
amount: u64,
}

View File

@ -10,6 +10,7 @@ serum-context = { path = "../../context" }
serum-common = { path = "../../common" }
serum-registry = { path = "../", features = ["client"] }
serum-registry-client = { path = "../client" }
serum-meta-entity = { path = "../meta-entity", features = ["client"] }
solana-client-gen = { path = "../../solana-client-gen" }
spl-token = { version = "2.0.5", default-features = false }
serde = { version = "1.0", features = ["derive"] }

View File

@ -1,7 +1,8 @@
use anyhow::{anyhow, Result};
use anyhow::Result;
use clap::Clap;
use serum_common::client::rpc;
use serum_context::Context;
use serum_meta_entity::accounts::Metadata;
use serum_registry::accounts::{
Entity, LockedRewardVendor, Member, Registrar, UnlockedRewardVendor,
};
@ -20,9 +21,6 @@ pub enum Command {
/// Seoncds until deactivation.
#[clap(short = 't', long, default_value = "10000")]
deactivation_timelock: i64,
/// SRM equivalent amount required for node activation.
#[clap(short, long, default_value = "10_000_000")]
reward_activation_threshold: u64,
#[clap(short, long)]
max_stake_per_entity: u64,
#[clap(short, long)]
@ -30,10 +28,8 @@ pub enum Command {
#[clap(short = 'b', long)]
stake_rate_mega: u64,
},
/// Creates and registers a delegated staked node entity.
/// Creates a node entity, setting the active wallet as leader.
CreateEntity {
#[clap(short, long)]
meta_entity_program_id: Pubkey,
/// Registrar account address.
#[clap(short, long)]
registrar: Pubkey,
@ -42,21 +38,20 @@ pub enum Command {
#[clap(short, long)]
about: String,
#[clap(short, long)]
image_url: String,
image_url: Option<String>,
},
/// Joins an entity, creating an associated member account.
CreateMember {
/// Node entity to join with.
/// Updates an entity. Active wallet must be the node leader.
UpdateEntity {
#[clap(short, long)]
name: Option<String>,
#[clap(short, long)]
about: Option<String>,
#[clap(short, long)]
image_url: Option<String>,
#[clap(short, long)]
entity: Pubkey,
/// Delegate of the member account [optional].
#[clap(short, long)]
delegate: Option<Pubkey>,
/// Registrar account address.
#[clap(short, long)]
registrar: Pubkey,
},
/// Sends all leftover funds from an expired unlocked reward vendor to a given
/// Sends all unclaimed funds from an expired unlocked reward vendor to a given
/// account.
ExpireUnlockedReward {
/// The token account to send the leftover rewards to.
@ -67,7 +62,7 @@ pub enum Command {
#[clap(short, long)]
registrar: Pubkey,
},
/// Sends all leftover funds from an expired locked reward vendor to a given
/// Sends all unclaimed funds from an expired locked reward vendor to a given
/// account.
ExpireLockedReward {
/// The token account to send the leftover rewards to.
@ -96,10 +91,9 @@ pub enum AccountsCommand {
},
/// View a member of a node entity.
Member {
/// Address of the stake account [optional]. If not provided, the
/// first derived stake address will be used for the configured wallet.
/// Address of the member stake account.
#[clap(short, long)]
address: Option<Pubkey>,
address: Pubkey,
},
LockedVendor {
#[clap(short, long)]
@ -115,11 +109,10 @@ pub fn run(ctx: Context, cmd: Command) -> Result<()> {
let registry_pid = ctx.registry_pid;
match cmd {
Command::Accounts(cmd) => account_cmd(&ctx, registry_pid, cmd),
Command::Accounts(cmd) => account_cmd(&ctx, cmd),
Command::Init {
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
stake_rate,
stake_rate_mega,
@ -128,7 +121,6 @@ pub fn run(ctx: Context, cmd: Command) -> Result<()> {
registry_pid,
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
stake_rate,
stake_rate_mega,
@ -138,21 +130,13 @@ pub fn run(ctx: Context, cmd: Command) -> Result<()> {
name,
about,
image_url,
meta_entity_program_id,
} => create_entity_cmd(
&ctx,
registry_pid,
registrar,
} => create_entity_cmd(&ctx, registry_pid, registrar, name, about, image_url),
Command::UpdateEntity {
name,
about,
image_url,
meta_entity_program_id,
),
Command::CreateMember {
entity,
delegate,
registrar,
} => create_member_cmd(&ctx, registry_pid, registrar, entity, delegate),
} => update_entity_cmd(&ctx, name, about, image_url, entity),
Command::ExpireUnlockedReward {
token,
vendor,
@ -184,38 +168,13 @@ pub fn run(ctx: Context, cmd: Command) -> Result<()> {
}
}
fn create_member_cmd(
ctx: &Context,
registry_pid: Pubkey,
registrar: Pubkey,
entity: Pubkey,
delegate: Option<Pubkey>,
) -> Result<()> {
let delegate = delegate.unwrap_or(Pubkey::new_from_array([0; 32]));
let client = ctx.connect::<Client>(registry_pid)?;
let CreateMemberResponse { tx, member } = client.create_member(CreateMemberRequest {
entity,
beneficiary: &ctx.wallet()?,
delegate,
registrar,
})?;
println!("Confirmed transaction: {:?}", tx);
println!("Created node entity member with address: {:?}", member);
Ok(())
}
fn create_entity_cmd(
ctx: &Context,
registry_pid: Pubkey,
registrar: Pubkey,
name: String,
about: String,
image_url: String,
meta_entity_program_id: Pubkey,
image_url: Option<String>,
) -> Result<()> {
let leader_kp = ctx.wallet()?;
@ -226,8 +185,8 @@ fn create_entity_cmd(
metadata: Some(EntityMetadata {
name,
about,
image_url,
meta_entity_program_id,
image_url: image_url.unwrap_or("".to_string()),
meta_entity_program_id: ctx.meta_entity_pid,
}),
})?;
@ -236,7 +195,27 @@ fn create_entity_cmd(
Ok(())
}
fn account_cmd(ctx: &Context, registry_pid: Pubkey, cmd: AccountsCommand) -> Result<()> {
fn update_entity_cmd(
ctx: &Context,
name: Option<String>,
about: Option<String>,
image_url: Option<String>,
entity: Pubkey,
) -> Result<()> {
let client = ctx.connect::<Client>(ctx.registry_pid)?;
let resp = client.update_entity_metadata(UpdateEntityMetadataRequest {
name,
about,
image_url,
entity,
meta_entity_pid: ctx.meta_entity_pid,
})?;
println!("Transaction signature: {}", resp.tx.to_string());
Ok(())
}
fn account_cmd(ctx: &Context, cmd: AccountsCommand) -> Result<()> {
let rpc_client = ctx.rpc_client();
match cmd {
@ -246,18 +225,11 @@ fn account_cmd(ctx: &Context, registry_pid: Pubkey, cmd: AccountsCommand) -> Res
}
AccountsCommand::Entity { address } => {
let acc: Entity = rpc::get_account_unchecked(&rpc_client, &address)?;
let m: Metadata = rpc::get_account_unchecked(&rpc_client, &acc.metadata)?;
println!("{:#?}", acc);
println!("{:#?}", m);
}
AccountsCommand::Member { address } => {
let address = match address {
Some(a) => a,
None => Pubkey::create_with_seed(
&ctx.wallet()?.pubkey(),
Client::member_seed(),
&registry_pid,
)
.map_err(|e| anyhow!("unable to derive stake address: {}", e.to_string()))?,
};
let acc: Member = rpc::get_account(&rpc_client, &address)?;
println!("{:#?}", acc);
}
@ -278,7 +250,6 @@ pub fn init(
registry_pid: Pubkey,
withdrawal_timelock: i64,
deactivation_timelock: i64,
reward_activation_threshold: u64,
max_stake_per_entity: u64,
stake_rate: u64,
stake_rate_mega: u64,
@ -297,7 +268,6 @@ pub fn init(
deactivation_timelock,
mint: ctx.srm_mint,
mega_mint: ctx.msrm_mint,
reward_activation_threshold,
max_stake_per_entity,
stake_rate,
stake_rate_mega,

View File

@ -15,6 +15,4 @@ borsh = { git = "https://github.com/project-serum/borsh", branch = "serum" }
serum-registry = { path = "../", features = ["client"] }
solana-client-gen = { path = "../../solana-client-gen" }
serum-common = { path = "../../common" }
# todo: remove
serum-pool-schema = { path = "../../pool/schema" }
serum-meta-entity = { path = "../meta-entity" }
serum-meta-entity = { path = "../meta-entity", features = ["client"] }

View File

@ -1,6 +1,7 @@
use serum_common::client::rpc;
use serum_common::pack::*;
use serum_meta_entity::accounts::mqueue::{MQueue, Ring as MQueueRing};
use serum_meta_entity::client::Client as MetaEntityClient;
use serum_registry::accounts::reward_queue::{RewardEventQueue, Ring};
use serum_registry::accounts::{
self, pending_withdrawal, vault, BalanceSandbox, Entity, LockedRewardVendor, Member,
@ -32,7 +33,6 @@ impl Client {
withdrawal_timelock,
deactivation_timelock,
max_stake_per_entity,
reward_activation_threshold,
mint,
mega_mint,
stake_rate,
@ -113,7 +113,6 @@ impl Client {
nonce,
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
stake_rate,
stake_rate_mega,
@ -269,6 +268,37 @@ impl Client {
Ok(UpdateEntityResponse { tx })
}
pub fn update_entity_metadata(
&self,
req: UpdateEntityMetadataRequest,
) -> Result<UpdateEntityMetadataResponse, ClientError> {
let UpdateEntityMetadataRequest {
name,
about,
image_url,
meta_entity_pid,
entity,
} = req;
let entity = self.entity(&entity)?;
let accounts = [
AccountMeta::new(entity.metadata, false),
AccountMeta::new_readonly(self.payer().pubkey(), true),
];
let client = MetaEntityClient::new(
meta_entity_pid,
Keypair::from_bytes(&self.payer().to_bytes()).expect("invalid payer"),
self.inner.url(),
Some(self.inner.options().clone()),
);
client
.update(&accounts, name, about, image_url, None)
.map(|tx| UpdateEntityMetadataResponse { tx })
.map_err(|err| ClientError::Any(anyhow::anyhow!("{}", err.to_string())))
}
pub fn create_member(
&self,
req: CreateMemberRequest,
@ -529,10 +559,15 @@ impl Client {
amount,
} = req;
// Dummy account to pass into the instruction, since it conforms to the
// lockup program's whitelist withdraw/deposit interface.
let dummy_account_meta = AccountMeta::new_readonly(sysvar::clock::ID, false);
let vault = self.vault_for(&member, &depositor, false)?;
let vault_acc = rpc::get_token_account::<TokenAccount>(self.rpc(), &vault)?;
let accounts = vec![
// Whitelist relay interface,
dummy_account_meta,
AccountMeta::new(depositor, false),
AccountMeta::new(depositor_authority.pubkey(), true),
AccountMeta::new_readonly(spl_token::ID, false),
@ -563,10 +598,16 @@ impl Client {
registrar,
amount,
} = req;
// Dummy account to pass into the instruction, since it conforms to the
// lockup program's whitelist withdraw/deposit interface.
let dummy_account_meta = AccountMeta::new_readonly(sysvar::clock::ID, false);
let vault = self.vault_for(&member, &depositor, false)?;
let vault_acc = rpc::get_token_account::<TokenAccount>(self.rpc(), &vault)?;
let accounts = vec![
// Whitelist relay interface.
dummy_account_meta,
AccountMeta::new(depositor, false),
AccountMeta::new_readonly(beneficiary.pubkey(), true),
AccountMeta::new_readonly(spl_token::ID, false),
@ -846,11 +887,11 @@ impl Client {
AccountMeta::new_readonly(solana_sdk::sysvar::clock::ID, false),
AccountMeta::new_readonly(self.vault_authority(&registrar)?, false),
AccountMeta::new_readonly(m.balances[0].owner, false),
AccountMeta::new_readonly(m.balances[0].vault_stake, false),
AccountMeta::new_readonly(m.balances[0].vault_stake_mega, false),
AccountMeta::new_readonly(m.balances[0].spt, false),
AccountMeta::new_readonly(m.balances[0].spt_mega, false),
AccountMeta::new_readonly(m.balances[1].owner, false),
AccountMeta::new_readonly(m.balances[1].vault_stake, false),
AccountMeta::new_readonly(m.balances[1].vault_stake_mega, false),
AccountMeta::new_readonly(m.balances[1].spt, false),
AccountMeta::new_readonly(m.balances[1].spt_mega, false),
];
let tx = self
.inner
@ -1115,16 +1156,23 @@ fn create_metadata_instructions(
about: String,
image_url: String,
) -> Vec<Instruction> {
let metadata_size = {
// 280 chars max.
let max_name = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
.to_string();
let max_about = max_name.clone();
let max_image_url = max_name.clone();
let md = serum_meta_entity::accounts::Metadata {
initialized: false,
entity: Pubkey::new_from_array([0; 32]),
authority: *payer,
name: name.clone(),
about: about.clone(),
image_url: image_url.clone(),
name: max_name,
about: max_about,
image_url: max_image_url,
chat: Pubkey::new_from_array([0; 32]),
};
let metadata_size = md.size().unwrap();
md.size().unwrap()
};
let lamports = client
.get_minimum_balance_for_rent_exemption(metadata_size as usize)
.unwrap();
@ -1179,7 +1227,6 @@ pub struct InitializeRequest {
pub withdrawal_timelock: i64,
pub deactivation_timelock: i64,
pub max_stake_per_entity: u64,
pub reward_activation_threshold: u64,
pub mint: Pubkey,
pub mega_mint: Pubkey,
pub stake_rate: u64,
@ -1223,6 +1270,18 @@ pub struct UpdateEntityResponse {
pub tx: Signature,
}
pub struct UpdateEntityMetadataRequest {
pub name: Option<String>,
pub about: Option<String>,
pub image_url: Option<String>,
pub meta_entity_pid: Pubkey,
pub entity: Pubkey,
}
pub struct UpdateEntityMetadataResponse {
pub tx: Signature,
}
pub struct CreateMemberRequest<'a> {
pub entity: Pubkey,
pub delegate: Pubkey,

View File

@ -39,7 +39,8 @@ pub fn handler(
let clock_acc_info = next_account_info(acc_infos)?;
let mut spt_acc_infos = vec![];
while acc_infos.len() > 0 {
// 2: Main and locked balances.
for _ in 0..2 {
spt_acc_infos.push(next_account_info(acc_infos)?);
}
@ -205,10 +206,17 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
// 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
.checked_div(vendor.pool_token_supply)
.unwrap()
.checked_mul(vendor.total)
.unwrap()
.checked_div(vendor.pool_token_supply)
.unwrap();
if amount <= 0 {
// Invariant violation.
msg!("Invalid reward calculation.");
return Err(RegistryErrorCode::Unknown)?;
}
// Lockup program requires the timestamp to be <= clock's timestamp.
// So update if the time has already passed.
let end_ts = {
@ -218,7 +226,7 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
vendor.end_ts
}
};
if amount > 0 {
let ix = {
let instr = LockupInstruction::CreateVesting {
beneficiary: member.beneficiary,
@ -264,7 +272,6 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
],
&[signer_seeds],
)?;
}
Ok(())
}

View File

@ -32,7 +32,8 @@ pub fn handler(
let clock_acc_info = next_account_info(acc_infos)?;
let mut spt_acc_infos = vec![];
while acc_infos.len() > 0 {
// 2: Main and locked balances.
for _ in 0..2 {
spt_acc_infos.push(next_account_info(acc_infos)?);
}
@ -122,11 +123,11 @@ fn access_control(req: AccessControlRequest) -> Result<AccessControlResponse, Re
.enumerate()
.map(|(idx, spt_acc_info)| {
if is_mega {
if &member.balances[idx].spt != spt_acc_info.key {
if &member.balances[idx].spt_mega != spt_acc_info.key {
return Err(RegistryErrorCode::InvalidSpt)?;
}
} else {
if &member.balances[idx].spt_mega != spt_acc_info.key {
if &member.balances[idx].spt != spt_acc_info.key {
return Err(RegistryErrorCode::InvalidSpt)?;
}
}
@ -187,11 +188,17 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
// 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
.checked_div(vendor.pool_token_supply)
.unwrap()
.checked_mul(vendor.total)
.unwrap()
.checked_div(vendor.pool_token_supply)
.unwrap();
if amount <= 0 {
// Invariant violation.
msg!("Invalid reward calculation.");
return Err(RegistryErrorCode::Unknown)?;
}
let signer_seeds = &[
registrar_acc_info.key.as_ref(),
vendor_acc_info.key.as_ref(),

View File

@ -16,6 +16,7 @@ pub fn handler(
let acc_infos = &mut accounts.iter();
// Lockup whitelist relay interface.
let _vesting_acc_info = next_account_info(acc_infos)?;
let depositor_acc_info = next_account_info(acc_infos)?;
let depositor_authority_acc_info = next_account_info(acc_infos)?;
let token_program_acc_info = next_account_info(acc_infos)?;

View File

@ -18,7 +18,6 @@ pub fn handler(
nonce: u8,
withdrawal_timelock: i64,
deactivation_timelock: i64,
reward_activation_threshold: u64,
max_stake_per_entity: u64,
stake_rate: u64,
stake_rate_mega: u64,
@ -56,7 +55,6 @@ pub fn handler(
withdrawal_timelock,
nonce,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
reward_event_q_acc_info,
registrar_acc_info,
@ -137,7 +135,6 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
withdrawal_timelock,
nonce,
deactivation_timelock,
reward_activation_threshold,
pool_mint_acc_info,
pool_mint_mega_acc_info,
max_stake_per_entity,
@ -155,7 +152,6 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
registrar.deactivation_timelock = deactivation_timelock;
registrar.max_stake_per_entity = max_stake_per_entity;
registrar.nonce = nonce;
registrar.reward_activation_threshold = reward_activation_threshold;
registrar.pool_mint = *pool_mint_acc_info.key;
registrar.pool_mint_mega = *pool_mint_mega_acc_info.key;
registrar.reward_event_q = *reward_event_q_acc_info.key;
@ -187,7 +183,6 @@ struct StateTransitionRequest<'a, 'b, 'c> {
pool_mint_mega_acc_info: &'a AccountInfo<'b>,
registrar: &'c mut Registrar,
authority: Pubkey,
reward_activation_threshold: u64,
deactivation_timelock: i64,
withdrawal_timelock: i64,
max_stake_per_entity: u64,

View File

@ -40,7 +40,6 @@ fn entry(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8])
nonce,
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
stake_rate,
stake_rate_mega,
@ -53,7 +52,6 @@ fn entry(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8])
nonce,
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
stake_rate,
stake_rate_mega,
@ -62,7 +60,6 @@ fn entry(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8])
new_authority,
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
} => update_registrar::handler(
program_id,
@ -70,7 +67,6 @@ fn entry(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8])
new_authority,
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
),
RegistryInstruction::CreateEntity { metadata } => {

View File

@ -26,9 +26,9 @@ pub fn handler(program_id: &Pubkey, accounts: &[AccountInfo]) -> Result<(), Regi
while acc_infos.len() > 0 {
asset_acc_infos.push(AssetAccInfos {
owner_acc_info: next_account_info(acc_infos)?,
vault_stake_acc_info: next_account_info(acc_infos)?,
vault_stake_mega_acc_info: next_account_info(acc_infos)?,
})
spt_acc_info: next_account_info(acc_infos)?,
spt_mega_acc_info: next_account_info(acc_infos)?,
});
}
let AccessControlResponse {
@ -121,9 +121,9 @@ fn access_control(req: AccessControlRequest) -> Result<AccessControlResponse, Re
// BPF exploads when mapping so use a for loop.
let mut assets = vec![];
for a in &asset_acc_infos {
let (vault_stake, is_mega) = access_control::member_vault_stake(
let (spt, is_mega) = access_control::member_spt(
&member,
a.vault_stake_acc_info,
a.spt_acc_info,
vault_authority_acc_info,
registrar_acc_info,
&registrar,
@ -131,9 +131,9 @@ fn access_control(req: AccessControlRequest) -> Result<AccessControlResponse, Re
a.owner_acc_info.key,
)?;
assert!(!is_mega);
let (vault_stake_mega, is_mega) = access_control::member_vault_stake(
let (spt_mega, is_mega) = access_control::member_spt(
&member,
a.vault_stake_mega_acc_info,
a.spt_mega_acc_info,
vault_authority_acc_info,
registrar_acc_info,
&registrar,
@ -141,10 +141,7 @@ fn access_control(req: AccessControlRequest) -> Result<AccessControlResponse, Re
a.owner_acc_info.key,
)?;
assert!(is_mega);
assets.push(Assets {
vault_stake,
vault_stake_mega,
})
assets.push(Assets { spt, spt_mega });
}
Ok(AccessControlResponse {
@ -182,11 +179,11 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
// untouched.
for a in assets {
// Remove.
curr_entity.balances.spt_amount -= a.vault_stake.amount;
curr_entity.balances.spt_mega_amount -= a.vault_stake_mega.amount;
curr_entity.balances.spt_amount -= a.spt.amount;
curr_entity.balances.spt_mega_amount -= a.spt_mega.amount;
// Add.
new_entity.balances.spt_amount += a.vault_stake.amount;
new_entity.balances.spt_mega_amount += a.vault_stake_mega.amount;
new_entity.balances.spt_amount += a.spt.amount;
new_entity.balances.spt_mega_amount += a.spt_mega.amount;
}
member.entity = *new_entity_acc_info.key;
@ -227,12 +224,12 @@ struct StateTransitionRequest<'a, 'b, 'c> {
}
struct Assets {
vault_stake: TokenAccount,
vault_stake_mega: TokenAccount,
spt: TokenAccount,
spt_mega: TokenAccount,
}
struct AssetAccInfos<'a, 'b> {
owner_acc_info: &'a AccountInfo<'b>,
vault_stake_acc_info: &'a AccountInfo<'b>,
vault_stake_mega_acc_info: &'a AccountInfo<'b>,
spt_acc_info: &'a AccountInfo<'b>,
spt_mega_acc_info: &'a AccountInfo<'b>,
}

View File

@ -13,7 +13,6 @@ pub fn handler(
new_authority: Option<Pubkey>,
withdrawal_timelock: Option<i64>,
deactivation_timelock: Option<i64>,
reward_activation_threshold: Option<u64>,
max_stake_per_entity: Option<u64>,
) -> Result<(), RegistryError> {
msg!("handler: initialize");
@ -37,7 +36,6 @@ pub fn handler(
new_authority,
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
})
.map_err(Into::into)
@ -71,7 +69,6 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
new_authority,
withdrawal_timelock,
deactivation_timelock,
reward_activation_threshold,
max_stake_per_entity,
} = req;
@ -87,10 +84,6 @@ fn state_transition(req: StateTransitionRequest) -> Result<(), RegistryError> {
registrar.deactivation_timelock = deactivation_timelock;
}
if let Some(reward_activation_threshold) = reward_activation_threshold {
registrar.reward_activation_threshold = reward_activation_threshold;
}
if let Some(max_stake_per_entity) = max_stake_per_entity {
registrar.max_stake_per_entity = max_stake_per_entity;
}
@ -109,6 +102,5 @@ struct StateTransitionRequest<'a> {
new_authority: Option<Pubkey>,
withdrawal_timelock: Option<i64>,
deactivation_timelock: Option<i64>,
reward_activation_threshold: Option<u64>,
max_stake_per_entity: Option<u64>,
}

View File

@ -17,6 +17,7 @@ pub fn handler(
let acc_infos = &mut accounts.iter();
// Lockup whitelist relay interface.
let _vesting_acc_info = next_account_info(acc_infos)?;
let depositor_acc_info = next_account_info(acc_infos)?;
let depositor_authority_acc_info = next_account_info(acc_infos)?;
let token_program_acc_info = next_account_info(acc_infos)?;

View File

@ -43,7 +43,6 @@ fn lifecycle() -> Result<()> {
max_stake_per_entity: 1_000_000_000_000_000,
stake_rate: 1,
stake_rate_mega: 1,
reward_activation_threshold: 1,
})?;
// Create entity--and subsequently activate it so that it can receive

View File

@ -242,6 +242,41 @@ pub fn member_vault_stake(
Ok((member_vault, is_mega))
}
pub fn member_spt(
member: &Member,
member_spt_acc_info: &AccountInfo,
member_vault_authority_acc_info: &AccountInfo,
registrar_acc_info: &AccountInfo,
registrar: &Registrar,
program_id: &Pubkey,
balance_id: &Pubkey,
) -> Result<(TokenAccount, bool), RegistryError> {
let member_spt = vault_authenticated(
member_spt_acc_info,
member_vault_authority_acc_info,
registrar_acc_info,
&registrar,
program_id,
)?;
let b = member
.balances
.iter()
.filter(|b| &b.owner == balance_id)
.collect::<Vec<&BalanceSandbox>>();
let balances = b.first().ok_or(RegistryErrorCode::InvalidBalanceSandbox)?;
let is_mega = {
if member_spt_acc_info.key != &balances.spt && member_spt_acc_info.key != &balances.spt_mega
{
return Err(RegistryErrorCode::InvalidSpt)?;
}
member_spt_acc_info.key == &balances.spt_mega
};
Ok((member_spt, is_mega))
}
pub fn member_vault_pending_withdrawal(
member: &Member,
member_vault_acc_info: &AccountInfo,

View File

@ -18,9 +18,6 @@ pub struct Registrar {
pub authority: Pubkey,
/// Nonce to derive the program-derived address owning the vaults.
pub nonce: u8,
/// The amount of tokens that must be deposited to be eligible for rewards,
/// denominated in SRM.
pub reward_activation_threshold: u64,
/// The maximum stake per node entity, denominated in SRM.
pub max_stake_per_entity: u64,
/// Number of seconds that must pass for a withdrawal to complete.

View File

@ -30,7 +30,6 @@ pub mod instruction {
nonce: u8,
withdrawal_timelock: i64,
deactivation_timelock: i64,
reward_activation_threshold: u64,
max_stake_per_entity: u64,
stake_rate: u64,
stake_rate_mega: u64,
@ -43,7 +42,6 @@ pub mod instruction {
new_authority: Option<Pubkey>,
withdrawal_timelock: Option<i64>,
deactivation_timelock: Option<i64>,
reward_activation_threshold: Option<u64>,
max_stake_per_entity: Option<u64>,
},
/// Accounts:

View File

@ -33,7 +33,6 @@ fn lifecycle() {
// Initialize the registrar.
let withdrawal_timelock = 10;
let deactivation_timelock = 10;
let reward_activation_threshold = 10;
let max_stake_per_entity = 100_000_000_000_000;
let registrar_authority = Keypair::generate(&mut OsRng);
@ -46,7 +45,6 @@ fn lifecycle() {
deactivation_timelock,
mint: srm_mint,
mega_mint: msrm_mint,
reward_activation_threshold,
max_stake_per_entity,
stake_rate: 1,
stake_rate_mega: 1,

View File

@ -12,13 +12,18 @@ CLUSTER=l
#CLUSTER=devnet
DEACTIVATION_TIMELOCK=60
WITHDRAWAL_TIMELOCK=60
#
# 100_000_000 million SRM (6 decimals)
#
MAX_STAKE_PER_ENTITY=100000000000000
#
# 1 SRM (6 decimals) to stake.
#
STAKE_RATE=1000000
#
# 1 MSRM (0 decimals) to stake.
#
STAKE_RATE_MEGA=1
REWARD_ACTIVATION_THRESHOLD=1
CONFIG_FILE=~/.config/serum/cli/dev.yaml
serum=$(pwd)/target/debug/serum
@ -90,7 +95,6 @@ EOM
local rInit=$($serum --config $CONFIG_FILE \
registry init \
--deactivation-timelock $DEACTIVATION_TIMELOCK \
--reward-activation-threshold $REWARD_ACTIVATION_THRESHOLD \
--withdrawal-timelock $WITHDRAWAL_TIMELOCK \
--max-stake-per-entity $MAX_STAKE_PER_ENTITY \
--stake-rate $STAKE_RATE \
@ -101,8 +105,7 @@ EOM
local reward_q=$(echo $rInit | jq .rewardEventQueue -r)
local lInit=$($serum --config $CONFIG_FILE \
lockup \
initialize)
lockup initialize)
local safe=$(echo $lInit | jq .safe -r)
@ -115,8 +118,7 @@ EOM
--registrar $registrar \
--about "This the default entity all new members join." \
--image-url " " \
--name "Default" \
--meta-entity-program-id $meta_entity_pid)
--name "Default" )
local entity=$(echo $createEntity | jq .entity -r)