2021-06-27 13:17:05 -07:00
|
|
|
// WIP. This program has been checkpointed and is not production ready.
|
|
|
|
|
|
|
|
use anchor_lang::prelude::*;
|
|
|
|
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
|
2021-09-11 14:43:12 -07:00
|
|
|
use anchor_spl::dex::{self, Dex};
|
|
|
|
use anchor_spl::token::{self, Mint, Token, TokenAccount};
|
|
|
|
use lockup::program::Lockup;
|
|
|
|
use registry::program::Registry;
|
2021-06-27 13:17:05 -07:00
|
|
|
use registry::{Registrar, RewardVendorKind};
|
2021-09-16 15:49:24 -07:00
|
|
|
use serum_dex::state::OpenOrders;
|
2021-06-27 13:17:05 -07:00
|
|
|
use std::convert::TryInto;
|
2021-09-16 15:49:24 -07:00
|
|
|
use std::mem::size_of;
|
2021-09-11 14:43:12 -07:00
|
|
|
use swap::program::Swap;
|
2021-06-27 13:17:05 -07:00
|
|
|
|
2021-09-07 13:06:15 -07:00
|
|
|
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
|
|
|
|
2021-06-27 13:17:05 -07:00
|
|
|
/// CFO is the program representing the Serum chief financial officer. It is
|
|
|
|
/// the program responsible for collecting and distributing fees from the Serum
|
|
|
|
/// DEX.
|
|
|
|
#[program]
|
|
|
|
pub mod cfo {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
/// Creates a financial officer account associated with a DEX program ID.
|
|
|
|
#[access_control(is_distribution_valid(&d))]
|
|
|
|
pub fn create_officer(
|
|
|
|
ctx: Context<CreateOfficer>,
|
2021-08-15 16:35:53 -07:00
|
|
|
bumps: OfficerBumps,
|
2021-06-27 13:17:05 -07:00
|
|
|
d: Distribution,
|
|
|
|
registrar: Pubkey,
|
|
|
|
msrm_registrar: Pubkey,
|
|
|
|
) -> Result<()> {
|
|
|
|
let officer = &mut ctx.accounts.officer;
|
|
|
|
officer.authority = *ctx.accounts.authority.key;
|
|
|
|
officer.swap_program = *ctx.accounts.swap_program.key;
|
2021-09-11 14:43:12 -07:00
|
|
|
officer.dex_program = ctx.accounts.dex_program.key();
|
2021-06-27 13:17:05 -07:00
|
|
|
officer.distribution = d;
|
|
|
|
officer.registrar = registrar;
|
|
|
|
officer.msrm_registrar = msrm_registrar;
|
|
|
|
officer.stake = *ctx.accounts.stake.to_account_info().key;
|
|
|
|
officer.treasury = *ctx.accounts.treasury.to_account_info().key;
|
|
|
|
officer.srm_vault = *ctx.accounts.srm_vault.to_account_info().key;
|
2021-08-15 16:35:53 -07:00
|
|
|
officer.bumps = bumps;
|
2021-06-27 13:17:05 -07:00
|
|
|
emit!(OfficerDidCreate {
|
|
|
|
pubkey: *officer.to_account_info().key,
|
|
|
|
});
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
/// Creates a market authorization token.
|
|
|
|
pub fn authorize_market(ctx: Context<AuthorizeMarket>, bump: u8) -> Result<()> {
|
|
|
|
ctx.accounts.market_auth.bump = bump;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Revokes a market authorization token.
|
|
|
|
pub fn revoke_market(_ctx: Context<RevokeMarket>) -> Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-06-27 13:17:05 -07:00
|
|
|
/// Creates a deterministic token account owned by the CFO.
|
|
|
|
/// This should be used when a new mint is used for collecting fees.
|
|
|
|
/// Can only be called once per token CFO and token mint.
|
2021-08-15 16:35:53 -07:00
|
|
|
pub fn create_officer_token(_ctx: Context<CreateOfficerToken>, _bump: u8) -> Result<()> {
|
2021-06-27 13:17:05 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
/// Creates an open orders account for the given market.
|
|
|
|
pub fn create_officer_open_orders(
|
|
|
|
ctx: Context<CreateOfficerOpenOrders>,
|
|
|
|
_bump: u8,
|
|
|
|
) -> Result<()> {
|
|
|
|
let seeds = [
|
|
|
|
ctx.accounts.dex_program.key.as_ref(),
|
|
|
|
&[ctx.accounts.officer.bumps.bump],
|
|
|
|
];
|
|
|
|
let cpi_ctx = CpiContext::from(&*ctx.accounts);
|
|
|
|
dex::init_open_orders(cpi_ctx.with_signer(&[&seeds])).map_err(Into::into)
|
|
|
|
}
|
|
|
|
|
2021-06-27 13:17:05 -07:00
|
|
|
/// Updates the cfo's fee distribution.
|
|
|
|
#[access_control(is_distribution_valid(&d))]
|
|
|
|
pub fn set_distribution(ctx: Context<SetDistribution>, d: Distribution) -> Result<()> {
|
|
|
|
ctx.accounts.officer.distribution = d.clone();
|
|
|
|
emit!(DistributionDidChange { distribution: d });
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Transfers fees from the dex to the CFO.
|
|
|
|
pub fn sweep_fees<'info>(ctx: Context<'_, '_, '_, 'info, SweepFees<'info>>) -> Result<()> {
|
2021-09-16 15:49:24 -07:00
|
|
|
let cpi_ctx = CpiContext::from(&*ctx.accounts);
|
2021-08-15 16:35:53 -07:00
|
|
|
let seeds = [
|
|
|
|
ctx.accounts.dex.dex_program.key.as_ref(),
|
|
|
|
&[ctx.accounts.officer.bumps.bump],
|
|
|
|
];
|
2021-09-16 15:49:24 -07:00
|
|
|
dex::sweep_fees(cpi_ctx.with_signer(&[&seeds])).map_err(Into::into)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert the CFO's entire non-SRM token balance into USDC.
|
|
|
|
/// Assumes USDC is the quote currency.
|
|
|
|
#[access_control(is_not_trading(&ctx.accounts.instructions))]
|
|
|
|
pub fn swap_to_usdc<'info>(
|
|
|
|
ctx: Context<'_, '_, '_, 'info, SwapToUsdc<'info>>,
|
|
|
|
min_exchange_rate: ExchangeRate,
|
|
|
|
) -> Result<()> {
|
2021-08-15 16:35:53 -07:00
|
|
|
let seeds = [
|
|
|
|
ctx.accounts.dex_program.key.as_ref(),
|
|
|
|
&[ctx.accounts.officer.bumps.bump],
|
|
|
|
];
|
|
|
|
let cpi_ctx = CpiContext::from(&*ctx.accounts);
|
2021-06-27 13:17:05 -07:00
|
|
|
swap::cpi::swap(
|
2021-09-16 15:49:24 -07:00
|
|
|
cpi_ctx.with_signer(&[&seeds]),
|
|
|
|
swap::Side::Ask,
|
|
|
|
ctx.accounts.from_vault.amount,
|
2021-06-27 13:17:05 -07:00
|
|
|
min_exchange_rate.into(),
|
2021-09-16 15:49:24 -07:00
|
|
|
)
|
|
|
|
.map_err(Into::into)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert the CFO's entire token balance into SRM.
|
|
|
|
/// Assumes SRM is the base currency.
|
|
|
|
#[access_control(is_not_trading(&ctx.accounts.instructions))]
|
|
|
|
pub fn swap_to_srm<'info>(
|
|
|
|
ctx: Context<'_, '_, '_, 'info, SwapToSrm<'info>>,
|
|
|
|
min_exchange_rate: ExchangeRate,
|
|
|
|
) -> Result<()> {
|
2021-08-15 16:35:53 -07:00
|
|
|
let seeds = [
|
|
|
|
ctx.accounts.dex_program.key.as_ref(),
|
|
|
|
&[ctx.accounts.officer.bumps.bump],
|
|
|
|
];
|
2021-09-16 15:49:24 -07:00
|
|
|
let cpi_ctx = CpiContext::from(&*ctx.accounts);
|
2021-06-27 13:17:05 -07:00
|
|
|
swap::cpi::swap(
|
2021-09-16 15:49:24 -07:00
|
|
|
cpi_ctx.with_signer(&[&seeds]),
|
2021-06-27 13:17:05 -07:00
|
|
|
swap::Side::Bid,
|
2021-09-16 15:49:24 -07:00
|
|
|
ctx.accounts.usdc_vault.amount,
|
2021-06-27 13:17:05 -07:00
|
|
|
min_exchange_rate.into(),
|
2021-09-16 15:49:24 -07:00
|
|
|
)
|
|
|
|
.map_err(Into::into)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Distributes srm tokens to the various categories. Before calling this,
|
|
|
|
/// one must convert the fees into SRM via the swap APIs.
|
|
|
|
#[access_control(is_distribution_ready(&ctx.accounts))]
|
|
|
|
pub fn distribute<'info>(ctx: Context<'_, '_, '_, 'info, Distribute<'info>>) -> Result<()> {
|
|
|
|
let total_fees = ctx.accounts.srm_vault.amount;
|
2021-08-15 16:35:53 -07:00
|
|
|
let seeds = [
|
|
|
|
ctx.accounts.dex_program.key.as_ref(),
|
|
|
|
&[ctx.accounts.officer.bumps.bump],
|
|
|
|
];
|
2021-06-27 13:17:05 -07:00
|
|
|
|
|
|
|
// Burn.
|
|
|
|
let burn_amount: u64 = u128::from(total_fees)
|
|
|
|
.checked_mul(ctx.accounts.officer.distribution.burn.into())
|
|
|
|
.unwrap()
|
|
|
|
.checked_div(100)
|
|
|
|
.unwrap()
|
|
|
|
.try_into()
|
|
|
|
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
2021-09-16 15:49:24 -07:00
|
|
|
token::burn(ctx.accounts.into_burn().with_signer(&[&seeds]), burn_amount)?;
|
2021-06-27 13:17:05 -07:00
|
|
|
|
|
|
|
// Stake.
|
|
|
|
let stake_amount: u64 = u128::from(total_fees)
|
|
|
|
.checked_mul(ctx.accounts.officer.distribution.stake.into())
|
|
|
|
.unwrap()
|
|
|
|
.checked_div(100)
|
|
|
|
.unwrap()
|
|
|
|
.try_into()
|
|
|
|
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
|
|
|
token::transfer(
|
2021-09-16 15:49:24 -07:00
|
|
|
ctx.accounts.into_stake_transfer().with_signer(&[&seeds]),
|
2021-06-27 13:17:05 -07:00
|
|
|
stake_amount,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Treasury.
|
|
|
|
let treasury_amount: u64 = u128::from(total_fees)
|
|
|
|
.checked_mul(ctx.accounts.officer.distribution.treasury.into())
|
|
|
|
.unwrap()
|
|
|
|
.checked_div(100)
|
|
|
|
.unwrap()
|
|
|
|
.try_into()
|
|
|
|
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
|
|
|
token::transfer(
|
2021-09-16 15:49:24 -07:00
|
|
|
ctx.accounts.into_treasury_transfer().with_signer(&[&seeds]),
|
2021-06-27 13:17:05 -07:00
|
|
|
treasury_amount,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[access_control(is_stake_reward_ready(&ctx.accounts))]
|
|
|
|
pub fn drop_stake_reward<'info>(
|
|
|
|
ctx: Context<'_, '_, '_, 'info, DropStakeReward<'info>>,
|
|
|
|
) -> Result<()> {
|
|
|
|
// Common reward parameters.
|
|
|
|
let expiry_ts = 1853942400; // 9/30/2028.
|
|
|
|
let expiry_receiver = *ctx.accounts.officer.to_account_info().key;
|
|
|
|
let locked_kind = {
|
|
|
|
let start_ts = 1633017600; // 9/30/2021.
|
|
|
|
let end_ts = 1822320000; // 9/30/2027.
|
|
|
|
let period_count = 2191;
|
|
|
|
RewardVendorKind::Locked {
|
|
|
|
start_ts,
|
|
|
|
end_ts,
|
|
|
|
period_count,
|
|
|
|
}
|
|
|
|
};
|
2021-08-15 16:35:53 -07:00
|
|
|
let seeds = [
|
|
|
|
ctx.accounts.dex_program.key.as_ref(),
|
|
|
|
&[ctx.accounts.officer.bumps.bump],
|
|
|
|
];
|
2021-06-27 13:17:05 -07:00
|
|
|
|
|
|
|
// Total amount staked denominated in SRM (i.e. MSRM is converted to
|
|
|
|
// SRM)
|
|
|
|
let total_pool_value = u128::from(ctx.accounts.srm.pool_mint.supply)
|
|
|
|
.checked_mul(500)
|
|
|
|
.unwrap()
|
|
|
|
.checked_add(
|
|
|
|
u128::from(ctx.accounts.msrm.pool_mint.supply)
|
|
|
|
.checked_mul(1_000_000)
|
|
|
|
.unwrap(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Total reward split between both the SRM and MSRM stake pools.
|
|
|
|
let total_reward_amount = u128::from(ctx.accounts.stake.amount);
|
|
|
|
|
|
|
|
// Proportion of the reward going to the srm pool.
|
|
|
|
//
|
|
|
|
// total_reward_amount * (srm_pool_value / total_pool_value)
|
|
|
|
//
|
|
|
|
let srm_amount: u64 = u128::from(ctx.accounts.srm.pool_mint.supply)
|
|
|
|
.checked_mul(500)
|
|
|
|
.unwrap()
|
|
|
|
.checked_mul(total_reward_amount)
|
|
|
|
.unwrap()
|
|
|
|
.checked_div(total_pool_value)
|
|
|
|
.unwrap()
|
|
|
|
.try_into()
|
|
|
|
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
|
|
|
|
|
|
|
// Proportion of the reward going to the msrm pool.
|
|
|
|
//
|
|
|
|
// total_reward_amount * (msrm_pool_value / total_pool_value)
|
|
|
|
//
|
|
|
|
let msrm_amount = u128::from(ctx.accounts.msrm.pool_mint.supply)
|
|
|
|
.checked_mul(total_reward_amount)
|
|
|
|
.unwrap()
|
|
|
|
.checked_div(total_pool_value)
|
|
|
|
.unwrap()
|
|
|
|
.try_into()
|
|
|
|
.map_err(|_| ErrorCode::U128CannotConvert)?;
|
|
|
|
|
|
|
|
// SRM drop.
|
|
|
|
{
|
|
|
|
// Drop locked reward.
|
|
|
|
let (_, nonce) = Pubkey::find_program_address(
|
|
|
|
&[
|
|
|
|
ctx.accounts.srm.registrar.to_account_info().key.as_ref(),
|
|
|
|
ctx.accounts.srm.vendor.to_account_info().key.as_ref(),
|
|
|
|
],
|
|
|
|
ctx.accounts.token_program.key,
|
|
|
|
);
|
|
|
|
registry::cpi::drop_reward(
|
2021-08-15 16:35:53 -07:00
|
|
|
ctx.accounts.into_srm_reward().with_signer(&[&seeds[..]]),
|
2021-06-27 13:17:05 -07:00
|
|
|
locked_kind.clone(),
|
|
|
|
srm_amount.try_into().unwrap(),
|
|
|
|
expiry_ts,
|
|
|
|
expiry_receiver,
|
|
|
|
nonce,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Drop unlocked reward.
|
|
|
|
registry::cpi::drop_reward(
|
2021-08-15 16:35:53 -07:00
|
|
|
ctx.accounts.into_srm_reward().with_signer(&[&seeds[..]]),
|
2021-06-27 13:17:05 -07:00
|
|
|
RewardVendorKind::Unlocked,
|
|
|
|
srm_amount,
|
|
|
|
expiry_ts,
|
|
|
|
expiry_receiver,
|
|
|
|
nonce,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// MSRM drop.
|
|
|
|
{
|
|
|
|
// Drop locked reward.
|
|
|
|
let (_, nonce) = Pubkey::find_program_address(
|
|
|
|
&[
|
|
|
|
ctx.accounts.msrm.registrar.to_account_info().key.as_ref(),
|
|
|
|
ctx.accounts.msrm.vendor.to_account_info().key.as_ref(),
|
|
|
|
],
|
|
|
|
ctx.accounts.token_program.key,
|
|
|
|
);
|
|
|
|
registry::cpi::drop_reward(
|
2021-08-15 16:35:53 -07:00
|
|
|
ctx.accounts.into_msrm_reward().with_signer(&[&seeds[..]]),
|
2021-06-27 13:17:05 -07:00
|
|
|
locked_kind,
|
|
|
|
msrm_amount,
|
|
|
|
expiry_ts,
|
|
|
|
expiry_receiver,
|
|
|
|
nonce,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Drop unlocked reward.
|
|
|
|
registry::cpi::drop_reward(
|
2021-08-15 16:35:53 -07:00
|
|
|
ctx.accounts.into_msrm_reward().with_signer(&[&seeds[..]]),
|
2021-06-27 13:17:05 -07:00
|
|
|
RewardVendorKind::Unlocked,
|
|
|
|
msrm_amount,
|
|
|
|
expiry_ts,
|
|
|
|
expiry_receiver,
|
|
|
|
nonce,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Context accounts.
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
2021-08-15 16:35:53 -07:00
|
|
|
#[instruction(bumps: OfficerBumps)]
|
2021-06-27 13:17:05 -07:00
|
|
|
pub struct CreateOfficer<'info> {
|
2021-08-15 16:35:53 -07:00
|
|
|
#[account(
|
|
|
|
init,
|
|
|
|
seeds = [dex_program.key.as_ref()],
|
|
|
|
bump = bumps.bump,
|
|
|
|
payer = authority,
|
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
officer: Box<Account<'info, Officer>>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(
|
|
|
|
init,
|
2021-09-16 15:49:24 -07:00
|
|
|
seeds = [b"token", officer.key().as_ref(), srm_mint.key().as_ref()],
|
2021-08-15 16:35:53 -07:00
|
|
|
bump = bumps.srm,
|
|
|
|
payer = authority,
|
2021-09-16 15:49:24 -07:00
|
|
|
token::mint = srm_mint,
|
2021-08-15 00:20:00 -07:00
|
|
|
token::authority = officer,
|
2021-06-27 13:17:05 -07:00
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
srm_vault: Box<Account<'info, TokenAccount>>,
|
2021-09-16 15:49:24 -07:00
|
|
|
#[account(
|
|
|
|
init,
|
|
|
|
seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()],
|
|
|
|
bump = bumps.usdc,
|
|
|
|
payer = authority,
|
|
|
|
token::mint = usdc_mint,
|
|
|
|
token::authority = officer,
|
|
|
|
)]
|
|
|
|
usdc_vault: Box<Account<'info, TokenAccount>>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(
|
|
|
|
init,
|
2021-08-15 16:35:53 -07:00
|
|
|
seeds = [b"stake", officer.key().as_ref()],
|
|
|
|
bump = bumps.stake,
|
|
|
|
payer = authority,
|
2021-09-16 15:49:24 -07:00
|
|
|
token::mint = srm_mint,
|
2021-08-15 00:20:00 -07:00
|
|
|
token::authority = officer,
|
2021-06-27 13:17:05 -07:00
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
stake: Box<Account<'info, TokenAccount>>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(
|
|
|
|
init,
|
2021-08-15 16:35:53 -07:00
|
|
|
seeds = [b"treasury", officer.key().as_ref()],
|
|
|
|
bump = bumps.treasury,
|
|
|
|
payer = authority,
|
2021-09-16 15:49:24 -07:00
|
|
|
token::mint = srm_mint,
|
2021-08-15 00:20:00 -07:00
|
|
|
token::authority = officer,
|
2021-06-27 13:17:05 -07:00
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
treasury: Box<Account<'info, TokenAccount>>,
|
2021-09-16 15:49:24 -07:00
|
|
|
authority: Signer<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[cfg_attr(
|
|
|
|
not(feature = "test"),
|
|
|
|
account(address = mint::SRM),
|
|
|
|
)]
|
2021-09-16 15:49:24 -07:00
|
|
|
srm_mint: Box<Account<'info, Mint>>,
|
|
|
|
#[cfg_attr(
|
|
|
|
not(feature = "test"),
|
|
|
|
account(address = mint::USDC),
|
|
|
|
)]
|
|
|
|
usdc_mint: Box<Account<'info, Mint>>,
|
2021-09-11 14:43:12 -07:00
|
|
|
dex_program: Program<'info, Dex>,
|
|
|
|
swap_program: Program<'info, Swap>,
|
|
|
|
system_program: Program<'info, System>,
|
|
|
|
token_program: Program<'info, Token>,
|
2021-06-27 13:17:05 -07:00
|
|
|
rent: Sysvar<'info, Rent>,
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
#[derive(Accounts)]
|
|
|
|
#[instruction(bump: u8)]
|
|
|
|
pub struct AuthorizeMarket<'info> {
|
|
|
|
#[account(has_one = authority)]
|
|
|
|
officer: Account<'info, Officer>,
|
|
|
|
authority: Signer<'info>,
|
|
|
|
#[account(
|
|
|
|
init,
|
|
|
|
payer = payer,
|
|
|
|
seeds = [b"market-auth", officer.key().as_ref(), market.key.as_ref()],
|
|
|
|
bump = bump,
|
|
|
|
)]
|
|
|
|
market_auth: Account<'info, MarketAuth>,
|
|
|
|
payer: Signer<'info>,
|
|
|
|
// Not read or written to so not validated.
|
|
|
|
market: UncheckedAccount<'info>,
|
|
|
|
system_program: Program<'info, System>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct RevokeMarket<'info> {
|
|
|
|
#[account(has_one = authority)]
|
|
|
|
pub officer: Account<'info, Officer>,
|
|
|
|
pub authority: Signer<'info>,
|
|
|
|
#[account(mut, close = payer)]
|
|
|
|
pub auth: Account<'info, MarketAuth>,
|
|
|
|
pub payer: Signer<'info>,
|
|
|
|
}
|
|
|
|
|
2021-06-27 13:17:05 -07:00
|
|
|
#[derive(Accounts)]
|
2021-08-15 16:35:53 -07:00
|
|
|
#[instruction(bump: u8)]
|
2021-06-27 13:17:05 -07:00
|
|
|
pub struct CreateOfficerToken<'info> {
|
2021-09-07 13:06:15 -07:00
|
|
|
officer: Account<'info, Officer>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(
|
|
|
|
init,
|
2021-09-16 15:49:24 -07:00
|
|
|
seeds = [b"token", officer.key().as_ref(), mint.key().as_ref()],
|
2021-08-15 16:35:53 -07:00
|
|
|
bump = bump,
|
2021-08-14 18:18:28 -07:00
|
|
|
token::mint = mint,
|
2021-08-15 00:20:00 -07:00
|
|
|
token::authority = officer,
|
2021-06-27 13:17:05 -07:00
|
|
|
payer = payer,
|
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
token: Account<'info, TokenAccount>,
|
2021-09-16 15:49:24 -07:00
|
|
|
mint: Account<'info, Mint>,
|
2021-09-11 14:43:12 -07:00
|
|
|
#[account(mut)]
|
|
|
|
payer: Signer<'info>,
|
|
|
|
system_program: Program<'info, System>,
|
|
|
|
token_program: Program<'info, Token>,
|
2021-06-27 13:17:05 -07:00
|
|
|
rent: Sysvar<'info, Rent>,
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
#[derive(Accounts)]
|
|
|
|
#[instruction(bump: u8)]
|
|
|
|
pub struct CreateOfficerOpenOrders<'info> {
|
|
|
|
officer: Account<'info, Officer>,
|
|
|
|
#[account(
|
|
|
|
init,
|
|
|
|
seeds = [b"open-orders", officer.key().as_ref(), market.key.as_ref()],
|
|
|
|
bump = bump,
|
|
|
|
space = 12 + size_of::<OpenOrders>(),
|
|
|
|
payer = payer,
|
|
|
|
owner = dex::ID,
|
|
|
|
)]
|
|
|
|
open_orders: UncheckedAccount<'info>,
|
|
|
|
#[account(mut)]
|
|
|
|
payer: Signer<'info>,
|
|
|
|
dex_program: Program<'info, Dex>,
|
|
|
|
system_program: Program<'info, System>,
|
|
|
|
rent: Sysvar<'info, Rent>,
|
|
|
|
// Used for CPI. Not read or written so not validated.
|
|
|
|
market: UncheckedAccount<'info>,
|
|
|
|
}
|
|
|
|
|
2021-06-27 13:17:05 -07:00
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct SetDistribution<'info> {
|
|
|
|
#[account(has_one = authority)]
|
2021-09-07 13:06:15 -07:00
|
|
|
officer: Account<'info, Officer>,
|
2021-09-11 14:43:12 -07:00
|
|
|
authority: Signer<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct SweepFees<'info> {
|
2021-08-29 14:25:38 -07:00
|
|
|
#[account(
|
|
|
|
seeds = [dex.dex_program.key.as_ref()],
|
|
|
|
bump = officer.bumps.bump,
|
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
officer: Account<'info, Officer>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(
|
|
|
|
mut,
|
2021-09-16 15:49:24 -07:00
|
|
|
seeds = [b"token", officer.key().as_ref(), mint.key().as_ref()],
|
2021-08-15 16:35:53 -07:00
|
|
|
bump,
|
2021-06-27 13:17:05 -07:00
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
sweep_vault: Account<'info, TokenAccount>,
|
2021-09-16 15:49:24 -07:00
|
|
|
mint: Account<'info, Mint>,
|
2021-09-11 14:43:12 -07:00
|
|
|
dex: DexAccounts<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
// DexAccounts are safe because they are used for CPI only.
|
|
|
|
// They are not read or written and so are not validated.
|
2021-06-27 13:17:05 -07:00
|
|
|
#[derive(Accounts)]
|
2021-09-11 14:43:12 -07:00
|
|
|
pub struct DexAccounts<'info> {
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
market: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
pc_vault: UncheckedAccount<'info>,
|
|
|
|
sweep_authority: UncheckedAccount<'info>,
|
|
|
|
vault_signer: UncheckedAccount<'info>,
|
2021-09-11 14:43:12 -07:00
|
|
|
dex_program: Program<'info, Dex>,
|
|
|
|
token_program: Program<'info, Token>,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct SwapToUsdc<'info> {
|
2021-08-29 14:25:38 -07:00
|
|
|
#[account(
|
2021-09-11 14:43:12 -07:00
|
|
|
seeds = [dex_program.key.as_ref()],
|
2021-08-29 14:25:38 -07:00
|
|
|
bump = officer.bumps.bump,
|
|
|
|
)]
|
2021-09-16 15:49:24 -07:00
|
|
|
officer: Box<Account<'info, Officer>>,
|
2021-06-27 13:17:05 -07:00
|
|
|
market: DexMarketAccounts<'info>,
|
|
|
|
#[account(
|
2021-09-16 15:49:24 -07:00
|
|
|
seeds = [b"market-auth", officer.key().as_ref(), market.market.key.as_ref()],
|
|
|
|
bump = market_auth.bump,
|
2021-06-27 13:17:05 -07:00
|
|
|
)]
|
2021-09-16 15:49:24 -07:00
|
|
|
market_auth: Account<'info, MarketAuth>,
|
|
|
|
#[account(
|
|
|
|
mut,
|
|
|
|
constraint = &officer.treasury != &from_vault.key(),
|
|
|
|
constraint = &officer.stake != &from_vault.key(),
|
|
|
|
)]
|
|
|
|
from_vault: Box<Account<'info, TokenAccount>>,
|
|
|
|
#[account(
|
|
|
|
mut,
|
|
|
|
seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()],
|
|
|
|
bump,
|
|
|
|
)]
|
|
|
|
usdc_vault: Box<Account<'info, TokenAccount>>,
|
|
|
|
#[cfg_attr(not(feature = "test"), account(address = mint::USDC))]
|
|
|
|
usdc_mint: Box<Account<'info, Mint>>,
|
2021-09-11 14:43:12 -07:00
|
|
|
swap_program: Program<'info, Swap>,
|
|
|
|
dex_program: Program<'info, Dex>,
|
|
|
|
token_program: Program<'info, Token>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(address = tx_instructions::ID)]
|
2021-09-16 15:49:24 -07:00
|
|
|
instructions: UncheckedAccount<'info>,
|
2021-08-15 16:35:53 -07:00
|
|
|
rent: Sysvar<'info, Rent>,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
2021-09-16 15:49:24 -07:00
|
|
|
#[instruction(bump: u8)]
|
2021-06-27 13:17:05 -07:00
|
|
|
pub struct SwapToSrm<'info> {
|
2021-08-29 14:25:38 -07:00
|
|
|
#[account(
|
2021-09-11 14:43:12 -07:00
|
|
|
seeds = [dex_program.key.as_ref()],
|
2021-08-29 14:25:38 -07:00
|
|
|
bump = officer.bumps.bump,
|
|
|
|
)]
|
2021-09-16 15:49:24 -07:00
|
|
|
officer: Box<Account<'info, Officer>>,
|
2021-06-27 13:17:05 -07:00
|
|
|
market: DexMarketAccounts<'info>,
|
|
|
|
#[account(
|
2021-09-16 15:49:24 -07:00
|
|
|
seeds = [b"market-auth", officer.key().as_ref(), market.market.key.as_ref()],
|
|
|
|
bump = market_auth.bump,
|
2021-06-27 13:17:05 -07:00
|
|
|
)]
|
2021-09-16 15:49:24 -07:00
|
|
|
market_auth: Account<'info, MarketAuth>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(
|
2021-09-16 15:49:24 -07:00
|
|
|
mut,
|
|
|
|
seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()],
|
|
|
|
bump,
|
|
|
|
)]
|
|
|
|
usdc_vault: Box<Account<'info, TokenAccount>>,
|
|
|
|
#[account(
|
|
|
|
mut,
|
|
|
|
seeds = [b"token", officer.key().as_ref(), srm_mint.key().as_ref()],
|
2021-08-15 16:35:53 -07:00
|
|
|
bump,
|
2021-06-27 13:17:05 -07:00
|
|
|
)]
|
2021-09-16 15:49:24 -07:00
|
|
|
srm_vault: Box<Account<'info, TokenAccount>>,
|
|
|
|
#[cfg_attr(not(feature = "test"), account(address = mint::SRM))]
|
|
|
|
srm_mint: Box<Account<'info, Mint>>,
|
|
|
|
#[cfg_attr(not(feature = "test"), account(address = mint::USDC))]
|
|
|
|
usdc_mint: Box<Account<'info, Mint>>,
|
2021-09-11 14:43:12 -07:00
|
|
|
swap_program: Program<'info, Swap>,
|
|
|
|
dex_program: Program<'info, Dex>,
|
|
|
|
token_program: Program<'info, Token>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(address = tx_instructions::ID)]
|
2021-09-16 15:49:24 -07:00
|
|
|
instructions: UncheckedAccount<'info>,
|
2021-08-15 16:35:53 -07:00
|
|
|
rent: Sysvar<'info, Rent>,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
// Dex accounts are used for CPI only.
|
|
|
|
// They are not read or written and so are not validated.
|
2021-06-27 13:17:05 -07:00
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct DexMarketAccounts<'info> {
|
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
market: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
open_orders: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
request_queue: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
event_queue: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
bids: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
asks: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
// The `spl_token::Account` that funds will be taken from, i.e., transferred
|
|
|
|
// from the user into the market's vault.
|
|
|
|
//
|
|
|
|
// For bids, this is the base currency. For asks, the quote.
|
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
order_payer_token_account: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
// Also known as the "base" currency. For a given A/B market,
|
|
|
|
// this is the vault for the A mint.
|
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
coin_vault: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
// Also known as the "quote" currency. For a given A/B market,
|
|
|
|
// this is the vault for the B mint.
|
|
|
|
#[account(mut)]
|
2021-09-16 15:49:24 -07:00
|
|
|
pc_vault: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
// PDA owner of the DEX's token accounts for base + quote currencies.
|
2021-09-16 15:49:24 -07:00
|
|
|
vault_signer: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct Distribute<'info> {
|
2021-09-16 15:49:24 -07:00
|
|
|
#[account(
|
|
|
|
has_one = srm_vault,
|
|
|
|
has_one = treasury,
|
|
|
|
has_one = stake,
|
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
officer: Account<'info, Officer>,
|
2021-09-16 15:49:24 -07:00
|
|
|
#[account(mut)]
|
|
|
|
treasury: Account<'info, TokenAccount>,
|
|
|
|
#[account(mut)]
|
|
|
|
stake: Account<'info, TokenAccount>,
|
|
|
|
#[account(mut)]
|
2021-09-07 13:06:15 -07:00
|
|
|
srm_vault: Account<'info, TokenAccount>,
|
2021-09-16 15:49:24 -07:00
|
|
|
#[account(mut)]
|
|
|
|
srm_mint: Account<'info, Mint>,
|
2021-09-11 14:43:12 -07:00
|
|
|
token_program: Program<'info, Token>,
|
|
|
|
dex_program: Program<'info, Dex>,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct DropStakeReward<'info> {
|
|
|
|
#[account(
|
|
|
|
has_one = stake,
|
|
|
|
constraint = srm.registrar.key == &officer.registrar,
|
|
|
|
constraint = msrm.registrar.key == &officer.msrm_registrar,
|
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
officer: Box<Account<'info, Officer>>,
|
2021-08-15 16:35:53 -07:00
|
|
|
#[account(
|
2021-08-29 14:25:38 -07:00
|
|
|
seeds = [b"stake", officer.key().as_ref()],
|
|
|
|
bump = officer.bumps.stake,
|
2021-08-15 16:35:53 -07:00
|
|
|
)]
|
2021-09-07 13:06:15 -07:00
|
|
|
stake: Box<Account<'info, TokenAccount>>,
|
2021-06-27 13:17:05 -07:00
|
|
|
#[cfg_attr(
|
|
|
|
not(feature = "test"),
|
|
|
|
account(address = mint::SRM),
|
|
|
|
)]
|
2021-09-16 15:49:24 -07:00
|
|
|
mint: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
srm: DropStakeRewardPool<'info>,
|
|
|
|
msrm: DropStakeRewardPool<'info>,
|
2021-09-07 13:06:15 -07:00
|
|
|
msrm_registrar: Box<Account<'info, Registrar>>,
|
2021-09-11 14:43:12 -07:00
|
|
|
token_program: Program<'info, Token>,
|
|
|
|
registry_program: Program<'info, Registry>,
|
|
|
|
lockup_program: Program<'info, Lockup>,
|
|
|
|
dex_program: Program<'info, Dex>,
|
2021-06-27 13:17:05 -07:00
|
|
|
clock: Sysvar<'info, Clock>,
|
|
|
|
rent: Sysvar<'info, Rent>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't bother doing validation on the individual accounts. Allow the stake
|
|
|
|
// program to handle it.
|
|
|
|
#[derive(Accounts)]
|
|
|
|
pub struct DropStakeRewardPool<'info> {
|
2021-09-16 15:49:24 -07:00
|
|
|
registrar: UncheckedAccount<'info>,
|
|
|
|
reward_event_q: UncheckedAccount<'info>,
|
2021-09-07 13:06:15 -07:00
|
|
|
pool_mint: Account<'info, Mint>,
|
2021-09-16 15:49:24 -07:00
|
|
|
vendor: UncheckedAccount<'info>,
|
|
|
|
vendor_vault: UncheckedAccount<'info>,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Accounts.
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
/// Officer represents a deployed instance of the CFO mechanism. It is tied
|
|
|
|
/// to a single deployment of the dex program.
|
|
|
|
///
|
|
|
|
/// PDA - [dex_program_id].
|
2021-08-15 16:35:53 -07:00
|
|
|
#[account]
|
2021-06-27 13:17:05 -07:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct Officer {
|
|
|
|
// Priviledged account.
|
|
|
|
pub authority: Pubkey,
|
|
|
|
// Vault holding the officer's SRM tokens prior to distribution.
|
|
|
|
pub srm_vault: Pubkey,
|
|
|
|
// Escrow SRM vault holding tokens which are dropped onto stakers.
|
|
|
|
pub stake: Pubkey,
|
|
|
|
// SRM token account to send treasury earned tokens to.
|
|
|
|
pub treasury: Pubkey,
|
|
|
|
// Defines the fee distribution, i.e., what percent each fee category gets.
|
|
|
|
pub distribution: Distribution,
|
|
|
|
// Swap frontend for the dex.
|
|
|
|
pub swap_program: Pubkey,
|
|
|
|
// Dex program the officer is associated with.
|
|
|
|
pub dex_program: Pubkey,
|
|
|
|
// SRM stake pool address
|
|
|
|
pub registrar: Pubkey,
|
|
|
|
// MSRM stake pool address.
|
|
|
|
pub msrm_registrar: Pubkey,
|
2021-08-15 16:35:53 -07:00
|
|
|
// Bump seeds for pdas.
|
|
|
|
pub bumps: OfficerBumps,
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
/// MarketAuth represents an authorization token created by the Officer
|
|
|
|
/// authority. This is used as an authorization token which allows one to
|
|
|
|
/// permissionlessly invoke the swap instructions on the market. Without this
|
|
|
|
/// one would be able to create their own market with prices unfavorable
|
|
|
|
/// to the smart contract (and subsequently swap on it).
|
|
|
|
///
|
|
|
|
/// Because a PDA is used here, the account existing (without a tombstone) is
|
|
|
|
/// proof of the validity of a given market, which means that anyone can use
|
|
|
|
/// the vault here to swap.
|
|
|
|
///
|
|
|
|
/// PDA - [b"market-auth", officer, market_address]
|
|
|
|
#[account]
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct MarketAuth {
|
|
|
|
// Bump seed for this account's PDA.
|
|
|
|
pub bump: u8,
|
|
|
|
}
|
|
|
|
|
2021-08-15 16:35:53 -07:00
|
|
|
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
|
|
|
|
pub struct OfficerBumps {
|
|
|
|
pub bump: u8,
|
|
|
|
pub srm: u8,
|
2021-09-16 15:49:24 -07:00
|
|
|
pub usdc: u8,
|
2021-08-15 16:35:53 -07:00
|
|
|
pub stake: u8,
|
|
|
|
pub treasury: u8,
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)]
|
|
|
|
pub struct Distribution {
|
|
|
|
burn: u8,
|
|
|
|
stake: u8,
|
|
|
|
treasury: u8,
|
|
|
|
}
|
|
|
|
|
|
|
|
// CpiContext transformations.
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
impl<'info> From<&CreateOfficerOpenOrders<'info>>
|
|
|
|
for CpiContext<'_, '_, '_, 'info, dex::InitOpenOrders<'info>>
|
|
|
|
{
|
|
|
|
fn from(accs: &CreateOfficerOpenOrders<'info>) -> Self {
|
|
|
|
let program = accs.dex_program.to_account_info();
|
|
|
|
let accounts = dex::InitOpenOrders {
|
|
|
|
open_orders: accs.open_orders.to_account_info(),
|
|
|
|
authority: accs.officer.to_account_info(),
|
|
|
|
market: accs.market.to_account_info(),
|
|
|
|
rent: accs.rent.to_account_info(),
|
|
|
|
};
|
|
|
|
CpiContext::new(program, accounts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-27 13:17:05 -07:00
|
|
|
impl<'info> From<&SweepFees<'info>> for CpiContext<'_, '_, '_, 'info, dex::SweepFees<'info>> {
|
|
|
|
fn from(sweep: &SweepFees<'info>) -> Self {
|
|
|
|
let program = sweep.dex.dex_program.to_account_info();
|
|
|
|
let accounts = dex::SweepFees {
|
|
|
|
market: sweep.dex.market.to_account_info(),
|
|
|
|
pc_vault: sweep.dex.pc_vault.to_account_info(),
|
|
|
|
sweep_authority: sweep.dex.sweep_authority.to_account_info(),
|
|
|
|
sweep_receiver: sweep.sweep_vault.to_account_info(),
|
|
|
|
vault_signer: sweep.dex.vault_signer.to_account_info(),
|
|
|
|
token_program: sweep.dex.token_program.to_account_info(),
|
|
|
|
};
|
2021-09-11 14:43:12 -07:00
|
|
|
CpiContext::new(program.to_account_info(), accounts)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'info> From<&SwapToSrm<'info>> for CpiContext<'_, '_, '_, 'info, swap::Swap<'info>> {
|
|
|
|
fn from(accs: &SwapToSrm<'info>) -> Self {
|
|
|
|
let program = accs.swap_program.to_account_info();
|
|
|
|
let accounts = swap::Swap {
|
|
|
|
market: swap::MarketAccounts {
|
2021-09-16 15:49:24 -07:00
|
|
|
market: accs.market.market.to_account_info(),
|
|
|
|
open_orders: accs.market.open_orders.to_account_info(),
|
|
|
|
request_queue: accs.market.request_queue.to_account_info(),
|
|
|
|
event_queue: accs.market.event_queue.to_account_info(),
|
|
|
|
bids: accs.market.bids.to_account_info(),
|
|
|
|
asks: accs.market.asks.to_account_info(),
|
|
|
|
order_payer_token_account: accs.market.order_payer_token_account.to_account_info(),
|
|
|
|
coin_vault: accs.market.coin_vault.to_account_info(),
|
|
|
|
pc_vault: accs.market.pc_vault.to_account_info(),
|
|
|
|
vault_signer: accs.market.vault_signer.to_account_info(),
|
|
|
|
coin_wallet: accs.srm_vault.to_account_info(),
|
2021-06-27 13:17:05 -07:00
|
|
|
},
|
|
|
|
authority: accs.officer.to_account_info(),
|
2021-09-16 15:49:24 -07:00
|
|
|
pc_wallet: accs.usdc_vault.to_account_info(),
|
2021-06-27 13:17:05 -07:00
|
|
|
dex_program: accs.dex_program.to_account_info(),
|
|
|
|
token_program: accs.token_program.to_account_info(),
|
|
|
|
rent: accs.rent.to_account_info(),
|
|
|
|
};
|
2021-09-11 14:43:12 -07:00
|
|
|
CpiContext::new(program.to_account_info(), accounts)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'info> From<&SwapToUsdc<'info>> for CpiContext<'_, '_, '_, 'info, swap::Swap<'info>> {
|
|
|
|
fn from(accs: &SwapToUsdc<'info>) -> Self {
|
|
|
|
let program = accs.swap_program.to_account_info();
|
|
|
|
let accounts = swap::Swap {
|
|
|
|
market: swap::MarketAccounts {
|
2021-09-16 15:49:24 -07:00
|
|
|
market: accs.market.market.to_account_info(),
|
|
|
|
open_orders: accs.market.open_orders.to_account_info(),
|
|
|
|
request_queue: accs.market.request_queue.to_account_info(),
|
|
|
|
event_queue: accs.market.event_queue.to_account_info(),
|
|
|
|
bids: accs.market.bids.to_account_info(),
|
|
|
|
asks: accs.market.asks.to_account_info(),
|
|
|
|
order_payer_token_account: accs.market.order_payer_token_account.to_account_info(),
|
|
|
|
coin_vault: accs.market.coin_vault.to_account_info(),
|
|
|
|
pc_vault: accs.market.pc_vault.to_account_info(),
|
|
|
|
vault_signer: accs.market.vault_signer.to_account_info(),
|
2021-06-27 13:17:05 -07:00
|
|
|
coin_wallet: accs.from_vault.to_account_info(),
|
|
|
|
},
|
|
|
|
authority: accs.officer.to_account_info(),
|
2021-09-16 15:49:24 -07:00
|
|
|
pc_wallet: accs.usdc_vault.to_account_info(),
|
2021-06-27 13:17:05 -07:00
|
|
|
dex_program: accs.dex_program.to_account_info(),
|
|
|
|
token_program: accs.token_program.to_account_info(),
|
|
|
|
rent: accs.rent.to_account_info(),
|
|
|
|
};
|
2021-09-11 14:43:12 -07:00
|
|
|
CpiContext::new(program.to_account_info(), accounts)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:49:24 -07:00
|
|
|
impl<'info> Distribute<'info> {
|
|
|
|
fn into_burn(&self) -> CpiContext<'_, '_, '_, 'info, token::Burn<'info>> {
|
|
|
|
let program = self.token_program.to_account_info();
|
2021-06-27 13:17:05 -07:00
|
|
|
let accounts = token::Burn {
|
2021-09-16 15:49:24 -07:00
|
|
|
mint: self.srm_mint.to_account_info(),
|
|
|
|
to: self.srm_vault.to_account_info(),
|
|
|
|
authority: self.officer.to_account_info(),
|
2021-06-27 13:17:05 -07:00
|
|
|
};
|
2021-09-16 15:49:24 -07:00
|
|
|
CpiContext::new(program, accounts)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn into_stake_transfer(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
|
|
|
|
let program = self.token_program.to_account_info();
|
|
|
|
let accounts = token::Transfer {
|
|
|
|
from: self.srm_vault.to_account_info(),
|
|
|
|
to: self.stake.to_account_info(),
|
|
|
|
authority: self.officer.to_account_info(),
|
|
|
|
};
|
|
|
|
CpiContext::new(program, accounts)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn into_treasury_transfer(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
|
|
|
|
let program = self.token_program.to_account_info();
|
|
|
|
let accounts = token::Transfer {
|
|
|
|
from: self.srm_vault.to_account_info(),
|
|
|
|
to: self.treasury.to_account_info(),
|
|
|
|
authority: self.officer.to_account_info(),
|
|
|
|
};
|
|
|
|
CpiContext::new(program, accounts)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'info> DropStakeReward<'info> {
|
|
|
|
fn into_srm_reward(&self) -> CpiContext<'_, '_, '_, 'info, registry::DropReward<'info>> {
|
|
|
|
let program = self.registry_program.clone();
|
|
|
|
let accounts = registry::DropReward {
|
2021-09-01 14:53:39 -07:00
|
|
|
registrar: ProgramAccount::try_from(program.key, &self.srm.registrar).unwrap(),
|
2021-09-07 13:06:15 -07:00
|
|
|
reward_event_q: ProgramAccount::try_from(program.key, &self.srm.reward_event_q)
|
|
|
|
.unwrap(),
|
|
|
|
pool_mint: self.srm.pool_mint.clone().into(),
|
2021-09-01 14:53:39 -07:00
|
|
|
vendor: ProgramAccount::try_from(program.key, &self.srm.vendor).unwrap(),
|
2021-06-27 13:17:05 -07:00
|
|
|
vendor_vault: CpiAccount::try_from(&self.srm.vendor_vault).unwrap(),
|
|
|
|
depositor: self.stake.to_account_info(),
|
|
|
|
depositor_authority: self.officer.to_account_info(),
|
2021-09-11 14:43:12 -07:00
|
|
|
token_program: self.token_program.to_account_info(),
|
2021-06-27 13:17:05 -07:00
|
|
|
clock: self.clock.clone(),
|
|
|
|
rent: self.rent.clone(),
|
|
|
|
};
|
2021-09-11 14:43:12 -07:00
|
|
|
CpiContext::new(program.to_account_info(), accounts)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn into_msrm_reward(&self) -> CpiContext<'_, '_, '_, 'info, registry::DropReward<'info>> {
|
|
|
|
let program = self.registry_program.clone();
|
|
|
|
let accounts = registry::DropReward {
|
2021-09-01 14:53:39 -07:00
|
|
|
registrar: ProgramAccount::try_from(program.key, &self.msrm.registrar).unwrap(),
|
2021-09-07 13:06:15 -07:00
|
|
|
reward_event_q: ProgramAccount::try_from(program.key, &self.msrm.reward_event_q)
|
|
|
|
.unwrap(),
|
|
|
|
pool_mint: self.msrm.pool_mint.clone().into(),
|
2021-09-01 14:53:39 -07:00
|
|
|
vendor: ProgramAccount::try_from(program.key, &self.msrm.vendor).unwrap(),
|
2021-06-27 13:17:05 -07:00
|
|
|
vendor_vault: CpiAccount::try_from(&self.msrm.vendor_vault).unwrap(),
|
|
|
|
depositor: self.stake.to_account_info(),
|
|
|
|
depositor_authority: self.officer.to_account_info(),
|
2021-09-11 14:43:12 -07:00
|
|
|
token_program: self.token_program.to_account_info(),
|
2021-06-27 13:17:05 -07:00
|
|
|
clock: self.clock.clone(),
|
|
|
|
rent: self.rent.clone(),
|
|
|
|
};
|
2021-09-11 14:43:12 -07:00
|
|
|
CpiContext::new(program.to_account_info(), accounts)
|
2021-06-27 13:17:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Events.
|
|
|
|
|
|
|
|
#[event]
|
|
|
|
pub struct DistributionDidChange {
|
|
|
|
distribution: Distribution,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[event]
|
|
|
|
pub struct OfficerDidCreate {
|
|
|
|
pubkey: Pubkey,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error.
|
|
|
|
|
|
|
|
#[error]
|
|
|
|
pub enum ErrorCode {
|
|
|
|
#[msg("Distribution does not add to 100")]
|
|
|
|
InvalidDistribution,
|
|
|
|
#[msg("u128 cannot be converted into u64")]
|
|
|
|
U128CannotConvert,
|
|
|
|
#[msg("Only one instruction is allowed for this transaction")]
|
|
|
|
TooManyInstructions,
|
|
|
|
#[msg("Not enough SRM has been accumulated to distribute")]
|
|
|
|
InsufficientDistributionAmount,
|
|
|
|
#[msg("Must drop more SRM onto the stake pool")]
|
|
|
|
InsufficientStakeReward,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Access control.
|
|
|
|
|
|
|
|
fn is_distribution_valid(d: &Distribution) -> Result<()> {
|
|
|
|
if d.burn + d.stake + d.treasury != 100 {
|
|
|
|
return Err(ErrorCode::InvalidDistribution.into());
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_distribution_ready(accounts: &Distribute) -> Result<()> {
|
|
|
|
if accounts.srm_vault.amount < 1_000_000 {
|
|
|
|
return Err(ErrorCode::InsufficientDistributionAmount.into());
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
// `ixs` must be the Instructions sysvar.
|
2021-09-16 15:49:24 -07:00
|
|
|
fn is_not_trading(ixs: &UncheckedAccount) -> Result<()> {
|
2021-06-27 13:17:05 -07:00
|
|
|
let data = ixs.try_borrow_data()?;
|
|
|
|
match tx_instructions::load_instruction_at(1, &data) {
|
|
|
|
Ok(_) => Err(ErrorCode::TooManyInstructions.into()),
|
|
|
|
Err(_) => Ok(()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_stake_reward_ready(accounts: &DropStakeReward) -> Result<()> {
|
|
|
|
// Min drop is 15,0000 SRM.
|
|
|
|
let min_reward: u64 = 15_000_000_000;
|
|
|
|
if accounts.stake.amount < min_reward {
|
|
|
|
return Err(ErrorCode::InsufficientStakeReward.into());
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Redefintions.
|
|
|
|
//
|
|
|
|
// The following types are redefined so that they can be parsed into the IDL,
|
|
|
|
// since Anchor doesn't yet support idl parsing across multiple crates.
|
|
|
|
|
|
|
|
#[derive(AnchorSerialize, AnchorDeserialize)]
|
|
|
|
pub struct ExchangeRate {
|
|
|
|
rate: u64,
|
|
|
|
from_decimals: u8,
|
|
|
|
quote_decimals: u8,
|
|
|
|
strict: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ExchangeRate> for swap::ExchangeRate {
|
|
|
|
fn from(e: ExchangeRate) -> Self {
|
|
|
|
let ExchangeRate {
|
|
|
|
rate,
|
|
|
|
from_decimals,
|
|
|
|
quote_decimals,
|
|
|
|
strict,
|
|
|
|
} = e;
|
|
|
|
Self {
|
|
|
|
rate,
|
|
|
|
from_decimals,
|
|
|
|
quote_decimals,
|
|
|
|
strict,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|