Armani Ferrante 4 years ago
parent
commit
a910d583c6
3 changed files with 67 additions and 18 deletions
  1. 41 15
      tests/cfo/programs/cfo/src/lib.rs
  2. 19 3
      tests/cfo/tests/cfo.js
  3. 7 0
      tests/cfo/tests/utils/stake.js

+ 41 - 15
tests/cfo/programs/cfo/src/lib.rs

@@ -30,6 +30,7 @@ pub mod cfo {
         registrar: Pubkey,
         msrm_registrar: Pubkey,
         swap_interval: i64,
+        max_swap_usdc: u64,
     ) -> Result<()> {
         let officer = &mut ctx.accounts.officer;
         officer.authority = *ctx.accounts.authority.key;
@@ -43,6 +44,7 @@ pub mod cfo {
         officer.srm_vault = *ctx.accounts.srm_vault.to_account_info().key;
         officer.bumps = bumps;
         officer.swap_interval = swap_interval;
+        officer.max_swap_usdc = max_swap_usdc;
         emit!(OfficerDidCreate {
             pubkey: *officer.to_account_info().key,
         });
@@ -114,18 +116,34 @@ pub mod cfo {
         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],
-        ];
-        let cpi_ctx = CpiContext::from(&*ctx.accounts);
-        swap::cpi::swap(
-            cpi_ctx.with_signer(&[&seeds]),
-            swap::Side::Ask,
-            ctx.accounts.from_vault.amount,
-            min_exchange_rate.into(),
-        )
-        .map_err(Into::into)
+        let amount_traded_usdc = {
+            // USDC before the swap.
+            let usdc_before = ctx.accounts.usdc_vault.amount;
+
+            // Swap..
+            let seeds = [
+                ctx.accounts.dex_program.key.as_ref(),
+                &[ctx.accounts.officer.bumps.bump],
+            ];
+            let cpi_ctx = CpiContext::from(&*ctx.accounts);
+            swap::cpi::swap(
+                cpi_ctx.with_signer(&[&seeds]),
+                swap::Side::Ask,
+                ctx.accounts.from_vault.amount,
+                min_exchange_rate.into(),
+            )?;
+
+            // USDC after the swap.
+            ctx.accounts.usdc_vault.reload()?;
+            ctx.accounts.usdc_vault.amount - usdc_before
+        };
+
+        // Reject any swap over the max size.
+        if amount_traded_usdc > ctx.accounts.officer.max_swap_usdc {
+            return Err(ErrorCode::SwapTooLarge.into());
+        }
+
+        Ok(())
     }
 
     /// Convert the CFO's entire token balance into SRM.
@@ -151,10 +169,14 @@ pub mod cfo {
         swap::cpi::swap(
             cpi_ctx.with_signer(&[&seeds]),
             swap::Side::Bid,
-            ctx.accounts.usdc_vault.amount,
+            std::cmp::min(
+                ctx.accounts.usdc_vault.amount,
+                ctx.accounts.officer.max_swap_usdc,
+            ),
             min_exchange_rate.into(),
-        )
-        .map_err(Into::into)
+        )?;
+
+        Ok(())
     }
 
     /// Distributes srm tokens to the various categories. Before calling this,
@@ -694,6 +716,8 @@ pub struct Officer {
     pub authority: Pubkey,
     // Required trade interval in seconds.
     pub swap_interval: i64,
+    // Maximum amount of native USDC that can be swapped in one tx.
+    pub max_swap_usdc: u64,
     // Vault holding the officer's SRM tokens prior to distribution.
     pub srm_vault: Pubkey,
     // Escrow SRM vault holding tokens which are dropped onto stakers.
@@ -935,6 +959,8 @@ pub enum ErrorCode {
     InsufficientStakeReward,
     #[msg("Swap interval must pass before another trade can occur")]
     SwapIntervalBlocked,
+    #[msg("Swap too large")]
+    SwapTooLarge,
 }
 
 // Access control.

+ 19 - 3
tests/cfo/tests/cfo.js

@@ -28,6 +28,7 @@ const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
 );
 const FEES = "6160355581";
 const SWAP_INTERVAL = new anchor.BN(1);
+const MAX_SWAP_USDC = new anchor.BN(50000000000);
 
 describe("cfo", () => {
   anchor.setProvider(anchor.Provider.env());
@@ -127,9 +128,23 @@ describe("cfo", () => {
   });
 
   it("BOILERPLATE: Sets up the staking pools", async () => {
-    await setupStakePool(ORDERBOOK_ENV.mintA, ORDERBOOK_ENV.godA);
-    registrar = ORDERBOOK_ENV.usdc;
-    msrmRegistrar = registrar;
+			const {
+					registrar: _registrar,
+					rewardEventQ: _rewardEventQ,
+					poolMint: _poolMint,
+			} = await setupStakePool(ORDERBOOK_ENV.mintA, ORDERBOOK_ENV.godA);
+			const {
+					registrar: _msrmRegistrar,
+					rewardEventQ: _msrmRewardEventQ,
+					poolMint: _msrmPoolMint,
+			} = await setupStakePool(ORDERBOOK_ENV.mintB, ORDERBOOK_ENV.godB);
+
+			registrar = _registrar;
+			rewardEventQ = _rewardEventQ;
+			poolMint = _poolMint;
+			msrmRegistrar = _msrmRegistrar;
+			msrmRewardEventQ = _msrmRewardEventQ;
+			msrmPoolMint = _msrmPoolMint;
   });
 
   it("BOILERPLATE: Finds PDA addresses", async () => {
@@ -243,6 +258,7 @@ describe("cfo", () => {
       registrar,
 			msrmRegistrar,
 			SWAP_INTERVAL,
+			MAX_SWAP_USDC,
       {
         accounts: {
           officer,

+ 7 - 0
tests/cfo/tests/utils/stake.js

@@ -161,6 +161,13 @@ async function setupStakePool(mint, god) {
       tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
     },
   });
+
+		return {
+				registrar: registrar.publicKey,
+				poolMint,
+				rewardEventQ: rewardQ.publicKey,
+				member,
+		};
 }
 
 module.exports = {