This commit is contained in:
Armani Ferrante 2021-09-18 14:31:35 -05:00
parent f58835ed60
commit 31ceb4e22e
No known key found for this signature in database
GPG Key ID: D597A80BCF8E12B7
2 changed files with 67 additions and 12 deletions

View File

@ -29,6 +29,7 @@ pub mod cfo {
d: Distribution,
registrar: Pubkey,
msrm_registrar: Pubkey,
swap_interval: i64,
) -> Result<()> {
let officer = &mut ctx.accounts.officer;
officer.authority = *ctx.accounts.authority.key;
@ -41,6 +42,7 @@ pub mod cfo {
officer.treasury = *ctx.accounts.treasury.to_account_info().key;
officer.srm_vault = *ctx.accounts.srm_vault.to_account_info().key;
officer.bumps = bumps;
officer.swap_interval = swap_interval;
emit!(OfficerDidCreate {
pubkey: *officer.to_account_info().key,
});
@ -50,6 +52,7 @@ pub mod cfo {
/// Creates a market authorization token.
pub fn authorize_market(ctx: Context<AuthorizeMarket>, bump: u8) -> Result<()> {
ctx.accounts.market_auth.bump = bump;
ctx.accounts.market_auth.last_trade = Clock::get()?.unix_timestamp;
Ok(())
}
@ -98,11 +101,19 @@ pub mod cfo {
/// 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))]
#[access_control(can_swap(
&ctx.accounts.officer,
&ctx.accounts.market_auth,
&ctx.accounts.instructions,
))]
pub fn swap_to_usdc<'info>(
ctx: Context<'_, '_, '_, 'info, SwapToUsdc<'info>>,
min_exchange_rate: ExchangeRate,
) -> Result<()> {
// Update last trade ts.
ctx.accounts.market_auth.last_trade = Clock::get()?.unix_timestamp;
// Trade.
let seeds = [
ctx.accounts.dex_program.key.as_ref(),
&[ctx.accounts.officer.bumps.bump],
@ -119,11 +130,19 @@ pub mod cfo {
/// Convert the CFO's entire token balance into SRM.
/// Assumes SRM is the base currency.
#[access_control(is_not_trading(&ctx.accounts.instructions))]
#[access_control(can_swap(
&ctx.accounts.officer,
&ctx.accounts.market_auth,
&ctx.accounts.instructions,
))]
pub fn swap_to_srm<'info>(
ctx: Context<'_, '_, '_, 'info, SwapToSrm<'info>>,
min_exchange_rate: ExchangeRate,
) -> Result<()> {
// Update last trade ts.
ctx.accounts.market_auth.last_trade = Clock::get()?.unix_timestamp;
// Trade.
let seeds = [
ctx.accounts.dex_program.key.as_ref(),
&[ctx.accounts.officer.bumps.bump],
@ -156,7 +175,9 @@ pub mod cfo {
.unwrap()
.try_into()
.map_err(|_| ErrorCode::U128CannotConvert)?;
token::burn(ctx.accounts.into_burn().with_signer(&[&seeds]), burn_amount)?;
if burn_amount > 0 {
token::burn(ctx.accounts.into_burn().with_signer(&[&seeds]), burn_amount)?;
}
// Stake.
let stake_amount: u64 = u128::from(total_fees)
@ -166,10 +187,12 @@ pub mod cfo {
.unwrap()
.try_into()
.map_err(|_| ErrorCode::U128CannotConvert)?;
token::transfer(
ctx.accounts.into_stake_transfer().with_signer(&[&seeds]),
stake_amount,
)?;
if stake_amount > 0 {
token::transfer(
ctx.accounts.into_stake_transfer().with_signer(&[&seeds]),
stake_amount,
)?;
}
// Treasury.
let treasury_amount: u64 = u128::from(total_fees)
@ -179,10 +202,12 @@ pub mod cfo {
.unwrap()
.try_into()
.map_err(|_| ErrorCode::U128CannotConvert)?;
token::transfer(
ctx.accounts.into_treasury_transfer().with_signer(&[&seeds]),
treasury_amount,
)?;
if treasury_amount > 0 {
token::transfer(
ctx.accounts.into_treasury_transfer().with_signer(&[&seeds]),
treasury_amount,
)?;
}
Ok(())
}
@ -667,6 +692,8 @@ pub struct DropStakeRewardPool<'info> {
pub struct Officer {
// Priviledged account.
pub authority: Pubkey,
// Required trade interval in seconds.
pub swap_interval: i64,
// Vault holding the officer's SRM tokens prior to distribution.
pub srm_vault: Pubkey,
// Escrow SRM vault holding tokens which are dropped onto stakers.
@ -701,6 +728,9 @@ pub struct Officer {
#[account]
#[derive(Default)]
pub struct MarketAuth {
// Unix timestamp of hte last time the program traded on this market.
// Used to help ensure the program doesn't have too much market impact.
pub last_trade: i64,
// Bump seed for this account's PDA.
pub bump: u8,
}
@ -903,6 +933,8 @@ pub enum ErrorCode {
InsufficientDistributionAmount,
#[msg("Must drop more SRM onto the stake pool")]
InsufficientStakeReward,
#[msg("Swap interval must pass before another trade can occur")]
SwapIntervalBlocked,
}
// Access control.
@ -921,6 +953,25 @@ fn is_distribution_ready(accounts: &Distribute) -> Result<()> {
Ok(())
}
// Asserts the given accounts can swap in this transaction.
fn can_swap(officer: &Officer, auth: &MarketAuth, ixs: &UncheckedAccount) -> Result<()> {
is_swap_interval_elapsed(officer, auth)?;
is_not_trading(ixs)?;
Ok(())
}
fn is_swap_interval_elapsed(officer: &Officer, auth: &MarketAuth) -> Result<()> {
if Clock::get()?
.unix_timestamp
.checked_sub(auth.last_trade)
.unwrap()
< officer.swap_interval
{
return Err(ErrorCode::SwapIntervalBlocked.into());
}
Ok(())
}
// `ixs` must be the Instructions sysvar.
fn is_not_trading(ixs: &UncheckedAccount) -> Result<()> {
let data = ixs.try_borrow_data()?;

View File

@ -27,6 +27,7 @@ const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
"Sysvar1nstructions1111111111111111111111111"
);
const FEES = "6160355581";
const SWAP_INTERVAL = new anchor.BN(1);
describe("cfo", () => {
anchor.setProvider(anchor.Provider.env());
@ -240,7 +241,8 @@ describe("cfo", () => {
bumps,
distribution,
registrar,
msrmRegistrar,
msrmRegistrar,
SWAP_INTERVAL,
{
accounts: {
officer,
@ -367,6 +369,8 @@ describe("cfo", () => {
systemProgram: SystemProgram.programId,
},
});
// Let the "last trade interval" elapse.
await serumCmn.sleep(1000);
});
it("Transfers into the mintB vault", async () => {