const anchor = require("@project-serum/anchor"); import { createTokenAccount } from "@project-serum/common"; import { assert } from "chai"; const { setupTestAccounts } = require("./utils"); const { BN } = anchor; import { instructions } from "../packages/psyfi-euros/src"; import { assertParsedError } from "./utils/helpers"; describe("Test initialize vault instruction", () => { const psyfiProgram = anchor.workspace.PsyfiEuros; const provider = anchor.Provider.env(); anchor.setProvider(provider); let vaultOwnerKeypair; let vaultManagerKeypair; let vaultTaskerKeypair; let feeTokenAccount; let vaultCollateralAssetAccount; let withdrawalCollateralAssetAccount; let vaultOwnershipTokenAccount; let vaultOwnershipTokenMint; let vault_signer_bump; let FEES, vaultAccount, epochHistory, epoch_history_bump; let SEED_VAULT_TOKEN_EXCHANGE_RATE; let PYTH_BTC_ORACLE_ADDRESS; // Vault Parameters const MAX_DEPOSITS_BTC = new BN(10_000_000_000); // 10 BTC const BTC_PRICE_DOLLARS = 500; const BTC_PRICE_CONFIDENCE = new BN(11_12_100_000); // 11.121 USD (8dp) let uniqueSeed1 = 1000; let uniqueSeed2 = 23; // Store accounts that already preexist in the network. let network; it("Initializes a covered call vault successfully.", async () => { ({ feeTokenAccount, vaultCollateralAssetAccount, withdrawalCollateralAssetAccount, vaultAccount, vault_signer_bump, epochHistory, epoch_history_bump, vaultOwnershipTokenMint, vaultOwnershipTokenAccount, FEES, network, vaultOwnerKeypair, vaultManagerKeypair, vaultTaskerKeypair, SEED_VAULT_TOKEN_EXCHANGE_RATE, PYTH_BTC_ORACLE_ADDRESS, } = await setupTestAccounts( provider, BTC_PRICE_DOLLARS, BTC_PRICE_CONFIDENCE, /*collateralDecimals=*/ 9, /*quoteDecimals=*/ 6, /*seedRate=*/ { rate: new BN(1_00_000_000), decimals: new BN(8), }, /*strategy_type=*/ 0, /*unique_seed=*/ uniqueSeed1 )); await instructions.initializeVaultRpc( psyfiProgram, /*unique_seed=*/ uniqueSeed1, /*strategy_type=*/ 0, /*vault_token_decimals=*/ 8, SEED_VAULT_TOKEN_EXCHANGE_RATE, /*ending_exchange_rate_decimals=*/ 8, MAX_DEPOSITS_BTC, FEES, vaultOwnerKeypair.publicKey, vaultManagerKeypair.publicKey, vaultTaskerKeypair.publicKey, feeTokenAccount, network.btcToken.publicKey, network.usdcToken.publicKey, PYTH_BTC_ORACLE_ADDRESS, [vaultOwnerKeypair] ); const vaultResult = await psyfiProgram.account.vaultAccount.fetch( vaultAccount ); assert(vaultResult.ownerAuthority.equals(vaultOwnerKeypair.publicKey)); assert(vaultResult.managerAuthority.equals(vaultManagerKeypair.publicKey)); assert(vaultResult.taskerAuthority.equals(vaultTaskerKeypair.publicKey)); assert( vaultResult.vaultCollateralAssetAccount.equals( vaultCollateralAssetAccount ) ); assert( vaultResult.withdrawalCollateralAssetAccount.equals( withdrawalCollateralAssetAccount ) ); assert( vaultResult.vaultOwnershipTokenAccount.equals(vaultOwnershipTokenAccount) ); assert(vaultResult.feeTokenAccount.equals(feeTokenAccount)); assert(vaultResult.collateralAssetMint.equals(network.btcToken.publicKey)); assert(vaultResult.quoteAssetMint.equals(network.usdcToken.publicKey)); assert(vaultResult.vaultOwnershipTokenMint.equals(vaultOwnershipTokenMint)); assert(vaultResult.pythPriceOracle.equals(PYTH_BTC_ORACLE_ADDRESS)); assert( vaultResult.seedVaultTokenPerCollateral.rate.eq( SEED_VAULT_TOKEN_EXCHANGE_RATE.rate ) ); assert.equal( vaultResult.seedVaultTokenPerCollateral.decimals, SEED_VAULT_TOKEN_EXCHANGE_RATE.decimals.toNumber() ); assert.equal(vaultResult.strategyType, 0); assert(vaultResult.vaultAccountBump == vault_signer_bump); assert(vaultResult.uniqueSeed == uniqueSeed1); assert(vaultResult.maxDeposits.eq(MAX_DEPOSITS_BTC)); assert(vaultResult.fees.managementFeeBps.eq(FEES.managementFeeBps)); assert(vaultResult.fees.performanceFeeBps.eq(FEES.performanceFeeBps)); assert(vaultResult.fees.withdrawalFeeBps.eq(FEES.withdrawalFeeBps)); assert(vaultResult.currentEpoch.toNumber() == 1); assert(vaultResult.currentEpochHistory.equals(epochHistory)); assert(vaultResult.pendingCollateralAssetDeposits.isZero()); assert(vaultResult.pendingWithdrawalTokens.isZero()); assert(vaultResult.optionsCollateralHeld.isZero()); assert.equal(vaultResult.optionsActive, false); const epochHistoryResult = await psyfiProgram.account.epochHistory.fetch( epochHistory ); assert(epochHistoryResult.vaultAccount.equals(vaultAccount)); assert(epochHistoryResult.epoch.toNumber() == 1); assert(epochHistoryResult.epochHistoryBump == epoch_history_bump); assert(epochHistoryResult.optionMarketMetaVec.length == 0); assert(epochHistoryResult.optionSaleAmount.isZero()); assert(epochHistoryResult.endingCollateralPerVaultToken.rate.isZero()); assert(epochHistoryResult.endingCollateralPerVaultToken.decimals == 0); }); it("Vault initialization fails when fees are exceeded.", async () => { ({ feeTokenAccount, vaultCollateralAssetAccount, withdrawalCollateralAssetAccount, vaultAccount, vault_signer_bump, epochHistory, epoch_history_bump, vaultOwnershipTokenMint, vaultOwnershipTokenAccount, FEES, network, vaultOwnerKeypair, vaultManagerKeypair, vaultTaskerKeypair, SEED_VAULT_TOKEN_EXCHANGE_RATE, PYTH_BTC_ORACLE_ADDRESS, } = await setupTestAccounts( provider, BTC_PRICE_DOLLARS, BTC_PRICE_CONFIDENCE, /*collateralDecimals=*/ 9, /*quoteDecimals=*/ 6, /*seedRate=*/ { rate: new BN(1_00_000_000), decimals: new BN(8), }, /*strategy_type=*/ 1, uniqueSeed1 )); // Initialize a PUT vault with the same unique_seed is valid. try { await instructions.initializeVaultRpc( psyfiProgram, uniqueSeed1, /*strategy_type=*/ 1, /*vault_token_decimals=*/ 8, SEED_VAULT_TOKEN_EXCHANGE_RATE, /*ending_exchange_rate_decimals=*/ 8, MAX_DEPOSITS_BTC, { managementFeeBps: new BN(0), performanceFeeBps: new BN(0), withdrawalFeeBps: new BN(1001), }, vaultOwnerKeypair.publicKey, vaultManagerKeypair.publicKey, vaultTaskerKeypair.publicKey, feeTokenAccount, network.usdcToken.publicKey, network.btcToken.publicKey, PYTH_BTC_ORACLE_ADDRESS, [vaultOwnerKeypair] ); assert(false); } catch (err) { assertParsedError(err, "Maximum configurable fees exceeded."); } }); it("Initializes a secured put vault successfully.", async () => { // Initialize a PUT vault with the same unique_seed is valid. await instructions.initializeVaultRpc( psyfiProgram, uniqueSeed1, /*strategy_type=*/ 1, /*vault_token_decimals=*/ 8, SEED_VAULT_TOKEN_EXCHANGE_RATE, /*ending_exchange_rate_decimals=*/ 8, MAX_DEPOSITS_BTC, FEES, vaultOwnerKeypair.publicKey, vaultManagerKeypair.publicKey, vaultTaskerKeypair.publicKey, feeTokenAccount, network.usdcToken.publicKey, network.btcToken.publicKey, PYTH_BTC_ORACLE_ADDRESS, [vaultOwnerKeypair] ); const vaultResult = await psyfiProgram.account.vaultAccount.fetch( vaultAccount ); assert(vaultResult.ownerAuthority.equals(vaultOwnerKeypair.publicKey)); assert(vaultResult.managerAuthority.equals(vaultManagerKeypair.publicKey)); assert(vaultResult.taskerAuthority.equals(vaultTaskerKeypair.publicKey)); assert( vaultResult.vaultCollateralAssetAccount.equals( vaultCollateralAssetAccount ) ); assert( vaultResult.withdrawalCollateralAssetAccount.equals( withdrawalCollateralAssetAccount ) ); assert( vaultResult.vaultOwnershipTokenAccount.equals(vaultOwnershipTokenAccount) ); assert(vaultResult.feeTokenAccount.equals(feeTokenAccount)); assert(vaultResult.collateralAssetMint.equals(network.usdcToken.publicKey)); assert(vaultResult.quoteAssetMint.equals(network.btcToken.publicKey)); assert(vaultResult.vaultOwnershipTokenMint.equals(vaultOwnershipTokenMint)); assert(vaultResult.pythPriceOracle.equals(PYTH_BTC_ORACLE_ADDRESS)); assert( vaultResult.seedVaultTokenPerCollateral.rate.eq( SEED_VAULT_TOKEN_EXCHANGE_RATE.rate ) ); assert.equal( vaultResult.seedVaultTokenPerCollateral.decimals, SEED_VAULT_TOKEN_EXCHANGE_RATE.decimals.toNumber() ); assert.equal(vaultResult.strategyType, 1); // PUT Vault assert(vaultResult.vaultAccountBump == vault_signer_bump); assert(vaultResult.uniqueSeed == uniqueSeed1); assert(vaultResult.maxDeposits.eq(MAX_DEPOSITS_BTC)); assert(vaultResult.fees.managementFeeBps.eq(FEES.managementFeeBps)); assert(vaultResult.fees.performanceFeeBps.eq(FEES.performanceFeeBps)); assert(vaultResult.fees.withdrawalFeeBps.eq(FEES.withdrawalFeeBps)); assert(vaultResult.currentEpoch.toNumber() == 1); assert(vaultResult.currentEpochHistory.equals(epochHistory)); assert(vaultResult.pendingCollateralAssetDeposits.isZero()); assert(vaultResult.pendingWithdrawalTokens.isZero()); assert(vaultResult.optionsCollateralHeld.isZero()); assert.equal(vaultResult.optionsActive, false); const epochHistoryResult = await psyfiProgram.account.epochHistory.fetch( epochHistory ); assert(epochHistoryResult.vaultAccount.equals(vaultAccount)); assert(epochHistoryResult.epoch.toNumber() == 1); assert(epochHistoryResult.epochHistoryBump == epoch_history_bump); assert(epochHistoryResult.optionMarketMetaVec.length == 0); assert(epochHistoryResult.optionSaleAmount.isZero()); assert(epochHistoryResult.endingCollateralPerVaultToken.rate.isZero()); assert(epochHistoryResult.endingCollateralPerVaultToken.decimals == 0); }); it("Vault initialization fails when strategy type is not supported.", async () => { ({ feeTokenAccount, vaultCollateralAssetAccount, withdrawalCollateralAssetAccount, vaultAccount, vault_signer_bump, epochHistory, epoch_history_bump, vaultOwnershipTokenMint, vaultOwnershipTokenAccount, FEES, network, vaultOwnerKeypair, vaultManagerKeypair, vaultTaskerKeypair, SEED_VAULT_TOKEN_EXCHANGE_RATE, PYTH_BTC_ORACLE_ADDRESS, } = await setupTestAccounts( provider, BTC_PRICE_DOLLARS, BTC_PRICE_CONFIDENCE, /*collateralDecimals=*/ 9, /*quoteDecimals=*/ 6, /*seedRate=*/ { rate: new BN(1_00_000_000), decimals: new BN(8), }, /*strategy_type=*/ 2, uniqueSeed2 )); try { await instructions.initializeVaultRpc( psyfiProgram, uniqueSeed2, /*strategy_type=*/ 2, /*vault_token_decimals=*/ 8, SEED_VAULT_TOKEN_EXCHANGE_RATE, /*ending_exchange_rate_decimals=*/ 8, MAX_DEPOSITS_BTC, { managementFeeBps: new BN(0), performanceFeeBps: new BN(0), withdrawalFeeBps: new BN(555), }, vaultOwnerKeypair.publicKey, vaultManagerKeypair.publicKey, vaultTaskerKeypair.publicKey, feeTokenAccount, network.btcToken.publicKey, network.usdcToken.publicKey, PYTH_BTC_ORACLE_ADDRESS, [vaultOwnerKeypair] ); assert(false); } catch (err) { assertParsedError(err, "Unsuported Strategy."); } }); it("Initializes a secured put vault successfully with different unique seed.", async () => { ({ feeTokenAccount, vaultCollateralAssetAccount, withdrawalCollateralAssetAccount, vaultAccount, vault_signer_bump, epochHistory, epoch_history_bump, vaultOwnershipTokenMint, vaultOwnershipTokenAccount, FEES, network, vaultOwnerKeypair, vaultManagerKeypair, vaultTaskerKeypair, SEED_VAULT_TOKEN_EXCHANGE_RATE, PYTH_BTC_ORACLE_ADDRESS, } = await setupTestAccounts( provider, BTC_PRICE_DOLLARS, BTC_PRICE_CONFIDENCE, /*collateralDecimals=*/ 9, /*quoteDecimals=*/ 6, /*seedRate=*/ { rate: new BN(1_00_000_000), decimals: new BN(8), }, /*strategy_type=*/ 1, uniqueSeed2 )); // Initialize a PUT vault with no error. await instructions.initializeVaultRpc( psyfiProgram, uniqueSeed2, /*strategy_type=*/ 1, /*vault_token_decimals=*/ 8, SEED_VAULT_TOKEN_EXCHANGE_RATE, /*ending_exchange_rate_decimals=*/ 8, MAX_DEPOSITS_BTC, FEES, vaultOwnerKeypair.publicKey, vaultManagerKeypair.publicKey, vaultTaskerKeypair.publicKey, feeTokenAccount, network.usdcToken.publicKey, network.btcToken.publicKey, PYTH_BTC_ORACLE_ADDRESS, [vaultOwnerKeypair] ); const vaultResult = await psyfiProgram.account.vaultAccount.fetch( vaultAccount ); assert.equal(vaultResult.strategyType, 1); // PUT Vault assert(vaultResult.vaultAccountBump == vault_signer_bump); assert(vaultResult.uniqueSeed == uniqueSeed2); }); it("Initializes a secured put vault sueccesfully with fee token account not owned by owner.", async () => { ({ vaultCollateralAssetAccount, withdrawalCollateralAssetAccount, vaultAccount, vault_signer_bump, epochHistory, epoch_history_bump, vaultOwnershipTokenMint, vaultOwnershipTokenAccount, FEES, network, vaultOwnerKeypair, vaultManagerKeypair, vaultTaskerKeypair, SEED_VAULT_TOKEN_EXCHANGE_RATE, PYTH_BTC_ORACLE_ADDRESS, } = await setupTestAccounts( provider, BTC_PRICE_DOLLARS, BTC_PRICE_CONFIDENCE, /*collateralDecimals=*/ 9, /*quoteDecimals=*/ 6, /*seedRate=*/ { rate: new BN(1_00_000_000), decimals: new BN(8), }, /*strategy_type=*/ 1, uniqueSeed2 )); // Create a token account not owned by Vault owner. feeTokenAccount = await createTokenAccount( provider, network.usdcToken.publicKey, /*owner=*/ vaultTaskerKeypair.publicKey ); // Initialize a PUT vault with no error. await instructions.initializeVaultRpc( psyfiProgram, uniqueSeed2, /*strategy_type=*/ 1, /*vault_token_decimals=*/ 8, SEED_VAULT_TOKEN_EXCHANGE_RATE, /*ending_exchange_rate_decimals=*/ 8, MAX_DEPOSITS_BTC, FEES, vaultOwnerKeypair.publicKey, vaultManagerKeypair.publicKey, vaultTaskerKeypair.publicKey, feeTokenAccount, network.usdcToken.publicKey, network.btcToken.publicKey, PYTH_BTC_ORACLE_ADDRESS, [vaultOwnerKeypair] ); const vaultResult = await psyfiProgram.account.vaultAccount.fetch( vaultAccount ); assert.equal(vaultResult.strategyType, 1); // PUT Vault assert(vaultResult.vaultAccountBump == vault_signer_bump); assert(vaultResult.uniqueSeed == uniqueSeed2); assert(vaultResult.feeTokenAccount.equals(feeTokenAccount)); }); });