@project-serum/anchor#Wallet TypeScript Examples

The following examples show how to use @project-serum/anchor#Wallet. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.ts    From protocol with Apache License 2.0 6 votes vote down vote up
export async function createMetadata(
  program: anchor.Program,
  user: anchor.web3.Keypair | Wallet,
): Promise<Metadata> {
  const [metadataAddress, metadataNonce] = await getMetadataProgramAddress(
    program,
    user.publicKey,
  );
  const tx = await program.rpc.createMetadata(new anchor.BN(metadataNonce), {
    accounts: {
      user: user.publicKey,
      metadata: metadataAddress,
      rent: anchor.web3.SYSVAR_RENT_PUBKEY,
      systemProgram: anchor.web3.SystemProgram.programId,
    },
    signers: 'secretKey' in user ? [user] : [],
  });
  await waitForFinality(program, tx);
  return await getMetadata(program, user.publicKey);
}
Example #2
Source File: index.ts    From protocol with Apache License 2.0 6 votes vote down vote up
export async function deleteMetadata(
  program: anchor.Program,
  user: anchor.web3.Keypair | Wallet,
): Promise<void> {
  const [metadataAddress, metadataNonce] = await getMetadataProgramAddress(
    program,
    user.publicKey,
  );
  await program.rpc.closeMetadata(new anchor.BN(metadataNonce), {
    accounts: {
      user: user.publicKey,
      metadata: metadataAddress,
      rent: anchor.web3.SYSVAR_RENT_PUBKEY,
      systemProgram: anchor.web3.SystemProgram.programId,
    },
    signers: 'secretKey' in user ? [user] : [],
  });
}
Example #3
Source File: index.ts    From protocol with Apache License 2.0 6 votes vote down vote up
export async function getDialects(
  program: anchor.Program,
  user: anchor.web3.Keypair | Wallet,
  encryptionProps?: EncryptionProps,
): Promise<DialectAccount[]> {
  const metadata = await getMetadata(program, user.publicKey);
  const enabledSubscriptions = metadata.subscriptions.filter(
    (it) => it.enabled,
  );
  return Promise.all(
    enabledSubscriptions.map(async ({ pubkey }) =>
      getDialect(program, pubkey, encryptionProps),
    ),
  ).then((dialects) =>
    dialects.sort(
      ({ dialect: d1 }, { dialect: d2 }) =>
        d2.lastMessageTimestamp - d1.lastMessageTimestamp,
    ),
  );
}
Example #4
Source File: index.ts    From protocol with Apache License 2.0 6 votes vote down vote up
export async function createDialect(
  program: anchor.Program,
  owner: anchor.web3.Keypair | Wallet,
  members: Member[],
  encrypted = false,
  encryptionProps?: EncryptionProps,
): Promise<DialectAccount> {
  const sortedMembers = members.sort((a, b) =>
    a.publicKey.toBuffer().compare(b.publicKey.toBuffer()),
  );
  const [publicKey, nonce] = await getDialectProgramAddress(
    program,
    sortedMembers,
  );
  // TODO: assert owner in members
  const keyedMembers = sortedMembers.reduce(
    (ms, m, idx) => ({ ...ms, [`member${idx}`]: m.publicKey }),
    {},
  );
  const tx = await program.rpc.createDialect(
    new anchor.BN(nonce),
    encrypted,
    sortedMembers.map((m) => m.scopes),
    {
      accounts: {
        dialect: publicKey,
        owner: owner.publicKey,
        ...keyedMembers,
        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
        systemProgram: anchor.web3.SystemProgram.programId,
      },
      signers: 'secretKey' in owner ? [owner] : [],
    },
  );
  await waitForFinality(program, tx);
  return await getDialectForMembers(program, members, encryptionProps);
}
Example #5
Source File: index.ts    From protocol with Apache License 2.0 6 votes vote down vote up
export async function deleteDialect(
  program: anchor.Program,
  { dialect }: DialectAccount,
  owner: anchor.web3.Keypair | Wallet,
): Promise<void> {
  const [dialectPublicKey, nonce] = await getDialectProgramAddress(
    program,
    dialect.members,
  );
  await program.rpc.closeDialect(new anchor.BN(nonce), {
    accounts: {
      dialect: dialectPublicKey,
      owner: owner.publicKey,
      rent: anchor.web3.SYSVAR_RENT_PUBKEY,
      systemProgram: anchor.web3.SystemProgram.programId,
    },
    signers: 'secretKey' in owner ? [owner] : [],
  });
}
Example #6
Source File: index.ts    From protocol with Apache License 2.0 5 votes vote down vote up
/*
Messages
*/

export async function sendMessage(
  program: anchor.Program,
  { dialect, publicKey }: DialectAccount,
  sender: anchor.web3.Keypair | Wallet,
  text: string,
  encryptionProps?: EncryptionProps,
): Promise<Message> {
  const [dialectPublicKey, nonce] = await getDialectProgramAddress(
    program,
    dialect.members,
  );
  const textSerde = TextSerdeFactory.create(
    {
      encrypted: dialect.encrypted,
      members: dialect.members,
    },
    encryptionProps,
  );
  const serializedText = textSerde.serialize(text);
  await program.rpc.sendMessage(
    new anchor.BN(nonce),
    Buffer.from(serializedText),
    {
      accounts: {
        dialect: dialectPublicKey,
        sender: sender ? sender.publicKey : program.provider.wallet.publicKey,
        member0: dialect.members[0].publicKey,
        member1: dialect.members[1].publicKey,
        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
        systemProgram: anchor.web3.SystemProgram.programId,
      },
      signers: sender && 'secretKey' in sender ? [sender] : [],
    },
  );
  const d = await getDialect(program, publicKey, encryptionProps);
  return d.dialect.messages[d.dialect.nextMessageIdx - 1]; // TODO: Support ring
}
Example #7
Source File: user_position.ts    From jet-engine with GNU Affero General Public License v3.0 5 votes vote down vote up
async function getBitcoinPosition() {
  // This users positions will be fetched
  const userAddress = new PublicKey("6XEn2q37nqsYQB5R79nueGi6n3uhgjiDwxoJeAVzWvaS")
  //transaction commitment options
  const options = AnchorProvider.defaultOptions()
  const connection = new Connection("https://api.devnet.solana.com", options)
  // A wallet is not required in this example
  const wallet = undefined as any as Wallet
  const provider = new AnchorProvider(connection, wallet, options)

  // Load the Anchor IDL from RPC
  const client = await JetClient.connect(provider, true)

  // Load devnet market data from RPC
  const market = await JetMarket.load(client, JET_MARKET_ADDRESS_DEVNET)
  // Load all reserves
  const reserves = await JetReserve.loadMultiple(client, market)
  // Load user data
  const user = await JetUser.load(client, market, reserves, userAddress)
  // create obligation
  const obligation = JetObligation.create(
    market,
    user,
    reserves.map(reserve => reserve.data)
  )

  // All these can be condensed to
  const userObligation = await JetObligation.load(client, JET_MARKET_ADDRESS_DEVNET, reserves, userAddress)

  // Locate the bitcoin position and log some information
  const bitcoinMint = new PublicKey("5ym2kCTCcqCHutbQXnPdsGAGFMEVQBQzTQ1CPun9W5A5")
  // Get btc position by filtering out token mint address
  const bitcoinPosition = obligation.positions.find(position => position.reserve.tokenMint.equals(bitcoinMint))
  if (bitcoinPosition) {
    const position: CollateralizedPosition = {
      mint: bitcoinPosition.reserve.tokenMint.toBase58(),
      deposited: bitcoinPosition.collateralBalance.tokens,
      borrowed: bitcoinPosition.loanBalance.tokens,
      borrowApr: bitcoinPosition.reserve.borrowApr,
      depositApy: bitcoinPosition.reserve.depositApy,
      collateralRatio: userObligation.collateralRatio
    }

    console.log(position)
    /**
   {
      mint: '5ym2kCTCcqCHutbQXnPdsGAGFMEVQBQzTQ1CPun9W5A5',
      deposited: 2.000009,
      borrowed: 0.500125,
      borrowApr: 0.00638284671447752,
      depositApy: 0.00012888670151030092,
      collateralRatio: 2.2676541147165414
    }
    */
  }
}
Example #8
Source File: fees.ts    From protocol-v1 with Apache License 2.0 4 votes vote down vote up
describe('fees', () => {
	const provider = anchor.AnchorProvider.local(undefined, {
		preflightCommitment: 'confirmed',
		commitment: 'confirmed',
	});
	const connection = provider.connection;
	anchor.setProvider(provider);
	const chProgram = anchor.workspace.ClearingHouse as Program;

	let clearingHouse: Admin;
	let referrerClearingHouse: ClearingHouse;

	let userAccountPublicKey: PublicKey;

	let usdcMint;
	let userUSDCAccount;

	// ammInvariant == k == x * y
	const mantissaSqrtScale = new BN(Math.sqrt(MARK_PRICE_PRECISION.toNumber()));
	const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul(
		mantissaSqrtScale
	);
	const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul(
		mantissaSqrtScale
	);

	const usdcAmount = new BN(10 * 10 ** 6);

	let discountMint: Token;
	let discountTokenAccount: AccountInfo;

	const referrerKeyPair = new Keypair();
	let referrerUSDCAccount: Keypair;
	let referrerUserAccountPublicKey: PublicKey;

	before(async () => {
		usdcMint = await mockUSDCMint(provider);
		userUSDCAccount = await mockUserUSDCAccount(usdcMint, usdcAmount, provider);

		clearingHouse = Admin.from(
			connection,
			provider.wallet,
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.initialize(usdcMint.publicKey, true);
		await clearingHouse.subscribe();

		const solUsd = await mockOracle(1);
		const periodicity = new BN(60 * 60); // 1 HOUR

		await clearingHouse.initializeMarket(
			Markets[0].marketIndex,
			solUsd,
			ammInitialBaseAssetReserve,
			ammInitialQuoteAssetReserve,
			periodicity
		);

		[, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		discountMint = await Token.createMint(
			connection,
			// @ts-ignore
			provider.wallet.payer,
			provider.wallet.publicKey,
			provider.wallet.publicKey,
			6,
			TOKEN_PROGRAM_ID
		);

		await clearingHouse.updateDiscountMint(discountMint.publicKey);

		discountTokenAccount = await discountMint.getOrCreateAssociatedAccountInfo(
			provider.wallet.publicKey
		);

		provider.connection.requestAirdrop(referrerKeyPair.publicKey, 10 ** 9);
		referrerUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			referrerKeyPair.publicKey
		);
		referrerClearingHouse = ClearingHouse.from(
			connection,
			new Wallet(referrerKeyPair),
			chProgram.programId
		);
		await referrerClearingHouse.subscribe();

		[, referrerUserAccountPublicKey] =
			await referrerClearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				referrerUSDCAccount.publicKey
			);
	});

	after(async () => {
		await clearingHouse.unsubscribe();
		await referrerClearingHouse.unsubscribe();
	});

	it('Trade no rebate', async () => {
		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0),
			discountTokenAccount.address
		);

		const user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		assert(user.collateral.eq(new BN(9990000)));
		assert(user.totalFeePaid.eq(new BN(10000)));
		assert(user.totalTokenDiscount.eq(new BN(0)));
		assert(user.totalRefereeDiscount.eq(new BN(0)));
	});

	it('Trade fourth tier rebate', async () => {
		await discountMint.mintTo(
			discountTokenAccount.address,
			// @ts-ignore
			provider.wallet.payer,
			[],
			1000 * 10 ** 6
		);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0),
			discountTokenAccount.address,
			referrerUserAccountPublicKey
		);

		const user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		assert(user.collateral.eq(new BN(9981000)));
		assert(user.totalFeePaid.eq(new BN(19000)));
		assert(user.totalTokenDiscount.eq(new BN(500)));
		assert(user.totalRefereeDiscount.eq(new BN(500)));

		const referrer: any = await clearingHouse.program.account.user.fetch(
			referrerUserAccountPublicKey
		);

		assert(referrer.totalReferralReward.eq(new BN(500)));
	});

	it('Trade third tier rebate', async () => {
		await discountMint.mintTo(
			discountTokenAccount.address,
			// @ts-ignore
			provider.wallet.payer,
			[],
			10000 * 10 ** 6
		);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0),
			discountTokenAccount.address,
			referrerUserAccountPublicKey
		);

		const user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		assert(user.collateral.eq(new BN(9972500)));
		assert(user.totalFeePaid.eq(new BN(27500)));
		assert(user.totalTokenDiscount.eq(new BN(1500)));
		assert(user.totalRefereeDiscount.eq(new BN(1000)));

		const referrer: any = await clearingHouse.program.account.user.fetch(
			referrerUserAccountPublicKey
		);

		assert(referrer.totalReferralReward.eq(new BN(1000)));
	});

	it('Trade second tier rebate', async () => {
		await discountMint.mintTo(
			discountTokenAccount.address,
			// @ts-ignore
			provider.wallet.payer,
			[],
			100000 * 10 ** 6
		);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0),
			discountTokenAccount.address,
			referrerUserAccountPublicKey
		);

		const user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		assert(user.collateral.eq(new BN(9964500)));
		assert(user.totalFeePaid.eq(new BN(35500)));
		assert(user.totalTokenDiscount.eq(new BN(3000)));
		assert(user.totalRefereeDiscount.eq(new BN(1500)));

		const referrer: any = await clearingHouse.program.account.user.fetch(
			referrerUserAccountPublicKey
		);

		assert(referrer.totalReferralReward.eq(new BN(1500)));
	});

	it('Trade first tier rebate', async () => {
		await discountMint.mintTo(
			discountTokenAccount.address,
			// @ts-ignore
			provider.wallet.payer,
			[],
			1000000 * 10 ** 6
		);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount.mul(new BN(9)).div(new BN(10)),
			marketIndex,
			new BN(0),
			discountTokenAccount.address,
			referrerUserAccountPublicKey
		);

		const user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		assert(user.collateral.eq(new BN(9957750)));
		assert(user.totalFeePaid.eq(new BN(42250)));
		assert(user.totalTokenDiscount.eq(new BN(4800)));
		assert(user.totalRefereeDiscount.eq(new BN(1950)));

		const referrer: any = await clearingHouse.program.account.user.fetch(
			referrerUserAccountPublicKey
		);

		assert(referrer.totalReferralReward.eq(new BN(1950)));
	});

	it('Close position', async () => {
		const marketIndex = new BN(0);
		await clearingHouse.closePosition(
			marketIndex,
			discountTokenAccount.address,
			referrerUserAccountPublicKey
		);

		const user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		assert(user.collateral.eq(new BN(9921000)));
		assert(user.totalFeePaid.eq(new BN(79000)));
		assert(user.totalTokenDiscount.eq(new BN(14600)));
		assert(user.totalRefereeDiscount.eq(new BN(4400)));

		const referrer: any = await clearingHouse.program.account.user.fetch(
			referrerUserAccountPublicKey
		);

		assert(referrer.totalReferralReward.eq(new BN(4400)));
	});
});
Example #9
Source File: idempotentCurve.ts    From protocol-v1 with Apache License 2.0 4 votes vote down vote up
describe('idempotent curve', () => {
	const provider = anchor.AnchorProvider.local(undefined, {
		commitment: 'confirmed',
		preflightCommitment: 'confirmed',
	});
	const connection = provider.connection;
	anchor.setProvider(provider);
	const chProgram = anchor.workspace.ClearingHouse as Program;

	let usdcMint;
	let primaryClearingHouse: Admin;

	// ammInvariant == k == x * y
	const mantissaSqrtScale = new BN(Math.sqrt(MARK_PRICE_PRECISION.toNumber()));
	const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul(
		mantissaSqrtScale
	);
	const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul(
		mantissaSqrtScale
	);

	const usdcAmount = new BN(10 * 10 ** 6);

	before(async () => {
		usdcMint = await mockUSDCMint(provider);

		primaryClearingHouse = Admin.from(
			connection,
			provider.wallet,
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await primaryClearingHouse.initialize(usdcMint.publicKey, true);
		await primaryClearingHouse.subscribe();

		const solUsd = await mockOracle(1);
		const periodicity = new BN(60 * 60); // 1 HOUR

		await primaryClearingHouse.initializeMarket(
			Markets[0].marketIndex,
			solUsd,
			ammInitialBaseAssetReserve,
			ammInitialQuoteAssetReserve,
			periodicity
		);

		const newFeeStructure: FeeStructure = {
			feeNumerator: new BN(0),
			feeDenominator: new BN(1),
			discountTokenTiers: {
				firstTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				secondTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				thirdTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				fourthTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
			},
			referralDiscount: {
				referrerRewardNumerator: new BN(1),
				referrerRewardDenominator: new BN(1),
				refereeDiscountNumerator: new BN(1),
				refereeDiscountDenominator: new BN(1),
			},
		};

		await primaryClearingHouse.updateFee(newFeeStructure);
	});

	beforeEach(async () => {
		await primaryClearingHouse.moveAmmPrice(
			ammInitialBaseAssetReserve,
			ammInitialQuoteAssetReserve,
			new BN(0)
		);
	});

	after(async () => {
		await primaryClearingHouse.unsubscribe();
	});

	const shrinkProfitableLong = async (chunks: number) => {
		const userKeypair = new Keypair();
		await provider.connection.requestAirdrop(userKeypair.publicKey, 10 ** 9);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			userKeypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			new Wallet(userKeypair),
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.subscribe();

		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		await primaryClearingHouse.moveAmmPrice(
			ammInitialBaseAssetReserve,
			ammInitialQuoteAssetReserve.mul(new BN(2)),
			new BN(0)
		);

		let user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);
		let userPositionsAccount: any =
			await clearingHouse.program.account.userPositions.fetch(user.positions);

		const numberOfReduces = chunks;
		const market = clearingHouse.getMarket(marketIndex);
		const baseAssetValue = calculateBaseAssetValue(
			market,
			userPositionsAccount.positions[0]
		);
		for (let i = 0; i < numberOfReduces - 1; i++) {
			await clearingHouse.openPosition(
				PositionDirection.SHORT,
				baseAssetValue.div(new BN(numberOfReduces)),
				marketIndex,
				new BN(0)
			);
		}
		await clearingHouse.closePosition(new BN(0));

		user = await clearingHouse.program.account.user.fetch(userAccountPublicKey);
		userPositionsAccount =
			await clearingHouse.program.account.userPositions.fetch(user.positions);

		assert(user.collateral.eq(new BN(19999200)));
		assert(userPositionsAccount.positions[0].quoteAssetAmount.eq(new BN(0)));
		await clearingHouse.unsubscribe();
	};

	const shrinkUnprofitableLong = async (chunks: number) => {
		const userKeypair = new Keypair();
		await provider.connection.requestAirdrop(userKeypair.publicKey, 10 ** 9);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			userKeypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			new Wallet(userKeypair),
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.subscribe();

		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		await primaryClearingHouse.moveAmmPrice(
			ammInitialBaseAssetReserve.mul(new BN(2)),
			ammInitialQuoteAssetReserve,
			new BN(0)
		);

		let user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);
		let userPositionsAccount: any =
			await clearingHouse.program.account.userPositions.fetch(user.positions);

		const numberOfReduces = chunks;
		const market = clearingHouse.getMarket(marketIndex);
		const baseAssetValue = calculateBaseAssetValue(
			market,
			userPositionsAccount.positions[0]
		);
		for (let i = 0; i < numberOfReduces - 1; i++) {
			await clearingHouse.openPosition(
				PositionDirection.SHORT,
				baseAssetValue.div(new BN(numberOfReduces)),
				marketIndex,
				new BN(0)
			);
		}
		await clearingHouse.closePosition(new BN(0));

		user = await clearingHouse.program.account.user.fetch(userAccountPublicKey);
		userPositionsAccount =
			await clearingHouse.program.account.userPositions.fetch(user.positions);

		assert(user.collateral.eq(new BN(4999850)));
		assert(userPositionsAccount.positions[0].quoteAssetAmount.eq(new BN(0)));
		await clearingHouse.unsubscribe();
	};

	const shrinkProfitableShort = async (chunks: number) => {
		const userKeypair = new Keypair();
		await provider.connection.requestAirdrop(userKeypair.publicKey, 10 ** 9);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			userKeypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			new Wallet(userKeypair),
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.subscribe();

		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.SHORT,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		await primaryClearingHouse.moveAmmPrice(
			ammInitialBaseAssetReserve.mul(new BN(2)),
			ammInitialQuoteAssetReserve,
			new BN(0)
		);

		let user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);
		let userPositionsAccount: any =
			await clearingHouse.program.account.userPositions.fetch(user.positions);

		const numberOfReduces = chunks;
		const market = clearingHouse.getMarket(marketIndex);
		const baseAssetValue = calculateBaseAssetValue(
			market,
			userPositionsAccount.positions[0]
		);
		for (let i = 0; i < numberOfReduces - 1; i++) {
			await clearingHouse.openPosition(
				PositionDirection.LONG,
				baseAssetValue.div(new BN(numberOfReduces)),
				marketIndex,
				new BN(0)
			);
		}
		await clearingHouse.closePosition(new BN(0));

		user = await clearingHouse.program.account.user.fetch(userAccountPublicKey);
		userPositionsAccount =
			await clearingHouse.program.account.userPositions.fetch(user.positions);

		assert(user.collateral.eq(new BN(14999849)));
		assert(userPositionsAccount.positions[0].quoteAssetAmount.eq(new BN(0)));
		await clearingHouse.unsubscribe();
	};

	const shrinkUnrofitableShort = async (chunks: number) => {
		const userKeypair = new Keypair();
		await provider.connection.requestAirdrop(userKeypair.publicKey, 10 ** 9);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			userKeypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			new Wallet(userKeypair),
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.subscribe();

		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.SHORT,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		await primaryClearingHouse.moveAmmPrice(
			ammInitialBaseAssetReserve.mul(new BN(3)),
			ammInitialQuoteAssetReserve.mul(new BN(4)),
			new BN(0)
		);

		let user: any = await clearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);
		let userPositionsAccount: any =
			await clearingHouse.program.account.userPositions.fetch(user.positions);

		const numberOfReduces = chunks;
		const market = clearingHouse.getMarket(marketIndex);
		const baseAssetValue = calculateBaseAssetValue(
			market,
			userPositionsAccount.positions[0]
		);
		for (let i = 0; i < numberOfReduces - 1; i++) {
			await clearingHouse.openPosition(
				PositionDirection.LONG,
				baseAssetValue.div(new BN(numberOfReduces)),
				marketIndex,
				new BN(0)
			);
		}
		await clearingHouse.closePosition(new BN(0));

		user = await clearingHouse.program.account.user.fetch(userAccountPublicKey);
		userPositionsAccount =
			await clearingHouse.program.account.userPositions.fetch(user.positions);

		assert(user.collateral.eq(new BN(6666311)));
		assert(userPositionsAccount.positions[0].quoteAssetAmount.eq(new BN(0)));
		await clearingHouse.unsubscribe();
	};

	it('open and shrink profitable long twice', async () => {
		await shrinkProfitableLong(2);
	});

	it('open and shrink profitable long fource', async () => {
		await shrinkProfitableLong(4);
	});

	it('open and shrink unprofitable long twice', async () => {
		await shrinkUnprofitableLong(2);
	});

	it('open and shrink unprofitable long fource', async () => {
		await shrinkUnprofitableLong(4);
	});

	it('open and shrink profitable short twice', async () => {
		await shrinkProfitableShort(2);
	});

	it('open and shrink profitable short fource', async () => {
		await shrinkProfitableShort(4);
	});

	it('open and shrink unprofitable short twice', async () => {
		await shrinkUnrofitableShort(2);
	});

	it('open and shrink unprofitable short fource', async () => {
		await shrinkUnrofitableShort(4);
	});
});
Example #10
Source File: minimumTradeSize.ts    From protocol-v1 with Apache License 2.0 4 votes vote down vote up
describe('minimum trade size', () => {
	const provider = anchor.AnchorProvider.local(undefined, {
		commitment: 'confirmed',
		preflightCommitment: 'confirmed',
	});
	const connection = provider.connection;
	anchor.setProvider(provider);
	const chProgram = anchor.workspace.ClearingHouse as Program;

	let usdcMint;

	let primaryClearingHouse: Admin;

	// ammInvariant == k == x * y
	const ammInitialQuoteAssetReserve = new anchor.BN(17 * 10 ** 13);
	const ammInitialBaseAssetReserve = new anchor.BN(17 * 10 ** 13);
	const invariant = ammInitialQuoteAssetReserve.mul(ammInitialBaseAssetReserve);

	const usdcAmount = new BN(10000 * 10 ** 3);

	before(async () => {
		usdcMint = await mockUSDCMint(provider);

		primaryClearingHouse = Admin.from(
			connection,
			provider.wallet,
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await primaryClearingHouse.initialize(usdcMint.publicKey, true);
		await primaryClearingHouse.subscribe();

		const solUsd = await mockOracle(63000);
		const periodicity = new BN(60 * 60); // 1 HOUR

		await primaryClearingHouse.initializeMarket(
			Markets[0].marketIndex,
			solUsd,
			ammInitialBaseAssetReserve,
			ammInitialQuoteAssetReserve,
			periodicity,
			new BN(63000000)
		);

		const newFeeStructure: FeeStructure = {
			feeNumerator: new BN(0),
			feeDenominator: new BN(1),
			discountTokenTiers: {
				firstTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				secondTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				thirdTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				fourthTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
			},
			referralDiscount: {
				referrerRewardNumerator: new BN(1),
				referrerRewardDenominator: new BN(1),
				refereeDiscountNumerator: new BN(1),
				refereeDiscountDenominator: new BN(1),
			},
		};

		await primaryClearingHouse.updateFee(newFeeStructure);
	});

	beforeEach(async () => {
		await primaryClearingHouse.moveAmmPrice(
			ammInitialBaseAssetReserve,
			ammInitialQuoteAssetReserve,
			new BN(0)
		);
	});

	after(async () => {
		await primaryClearingHouse.unsubscribe();
	});

	it('short round', async () => {
		const keypair = new Keypair();
		await provider.connection.requestAirdrop(keypair.publicKey, 10 ** 9);
		const wallet = new Wallet(keypair);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			keypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			wallet,
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.subscribe();
		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.SHORT,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		let user: UserAccount =
			await primaryClearingHouse.program.account.user.fetch(
				userAccountPublicKey
			);
		assert(user.collateral.eq(usdcAmount));

		let userPositions: UserPositionsAccount =
			await primaryClearingHouse.program.account.userPositions.fetch(
				user.positions
			);

		// make price slightly worse so we need to round trade amount
		const newBaseAssetReserve = ammInitialBaseAssetReserve
			.mul(new BN(9999999))
			.div(new BN(10000000));
		const newQuoteAssetReserve = invariant.div(newBaseAssetReserve);
		await primaryClearingHouse.moveAmmPrice(
			newBaseAssetReserve,
			newQuoteAssetReserve,
			new BN(0)
		);

		const market = primaryClearingHouse.getMarket(0);
		let position = userPositions.positions[0];
		const baseAssetValue = calculateBaseAssetValue(market, position);

		const expectedBaseAssetValue = new BN(10000188);
		assert(position.quoteAssetAmount.eq(usdcAmount));
		assert(baseAssetValue.eq(expectedBaseAssetValue));

		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		user = await primaryClearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		userPositions =
			await primaryClearingHouse.program.account.userPositions.fetch(
				user.positions
			);
		position = userPositions.positions[0];

		assert(position.quoteAssetAmount.eq(ZERO));
		assert(position.baseAssetAmount.eq(ZERO));
		await clearingHouse.unsubscribe();
	});

	it('short dont round', async () => {
		const keypair = new Keypair();
		await provider.connection.requestAirdrop(keypair.publicKey, 10 ** 9);
		const wallet = new Wallet(keypair);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			keypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			wallet,
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.subscribe();
		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.SHORT,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		let user: UserAccount =
			await primaryClearingHouse.program.account.user.fetch(
				userAccountPublicKey
			);
		assert(user.collateral.eq(usdcAmount));

		let userPositions: UserPositionsAccount =
			await primaryClearingHouse.program.account.userPositions.fetch(
				user.positions
			);

		// make price much worse
		const newBaseAssetReserve = ammInitialBaseAssetReserve
			.mul(new BN(1))
			.div(new BN(2));
		const newQuoteAssetReserve = invariant.div(newBaseAssetReserve);
		await primaryClearingHouse.moveAmmPrice(
			newBaseAssetReserve,
			newQuoteAssetReserve,
			new BN(0)
		);

		const market = primaryClearingHouse.getMarket(0);
		let position = userPositions.positions[0];
		const _baseAssetValue = calculateBaseAssetValue(market, position);

		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		user = await primaryClearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		userPositions =
			await primaryClearingHouse.program.account.userPositions.fetch(
				user.positions
			);
		position = userPositions.positions[0];

		assert(position.quoteAssetAmount.gt(ZERO));
		assert(position.baseAssetAmount.lt(ZERO));
		await clearingHouse.unsubscribe();
	});

	it('long round', async () => {
		const keypair = new Keypair();
		await provider.connection.requestAirdrop(keypair.publicKey, 10 ** 9);
		const wallet = new Wallet(keypair);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			keypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			wallet,
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.subscribe();
		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		let user: UserAccount =
			await primaryClearingHouse.program.account.user.fetch(
				userAccountPublicKey
			);
		assert(user.collateral.eq(usdcAmount));

		let userPositions: UserPositionsAccount =
			await primaryClearingHouse.program.account.userPositions.fetch(
				user.positions
			);

		// make price slightly worse so we need to round trade amount
		const newQuoteAssetReserve = ammInitialBaseAssetReserve
			.mul(new BN(9999999))
			.div(new BN(10000000));
		const newBaseAssetReserve = invariant.div(newQuoteAssetReserve);
		await primaryClearingHouse.moveAmmPrice(
			newBaseAssetReserve,
			newQuoteAssetReserve,
			new BN(0)
		);

		const market = primaryClearingHouse.getMarket(0);
		let position = userPositions.positions[0];
		const baseAssetValue = calculateBaseAssetValue(market, position);

		const expectedBaseAssetValue = new BN(10000002);
		assert(position.quoteAssetAmount.eq(usdcAmount));
		assert(baseAssetValue.eq(expectedBaseAssetValue));

		await clearingHouse.openPosition(
			PositionDirection.SHORT,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		user = await primaryClearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		userPositions =
			await primaryClearingHouse.program.account.userPositions.fetch(
				user.positions
			);
		position = userPositions.positions[0];

		assert(position.quoteAssetAmount.eq(ZERO));
		assert(position.baseAssetAmount.eq(ZERO));
		await clearingHouse.unsubscribe();
	});

	it('short dont round', async () => {
		const keypair = new Keypair();
		await provider.connection.requestAirdrop(keypair.publicKey, 10 ** 9);
		const wallet = new Wallet(keypair);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			keypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			wallet,
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await clearingHouse.subscribe();
		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		let user: UserAccount =
			await primaryClearingHouse.program.account.user.fetch(
				userAccountPublicKey
			);
		assert(user.collateral.eq(usdcAmount));

		let userPositions: UserPositionsAccount =
			await primaryClearingHouse.program.account.userPositions.fetch(
				user.positions
			);

		// make price better so we dont need to round trade amount
		const newQuoteAssetReserve = ammInitialBaseAssetReserve
			.mul(new BN(4))
			.div(new BN(3));
		const newBaseAssetReserve = invariant.div(newQuoteAssetReserve);
		await primaryClearingHouse.moveAmmPrice(
			newBaseAssetReserve,
			newQuoteAssetReserve,
			new BN(0)
		);

		const _market = primaryClearingHouse.getMarket(0);
		let position = userPositions.positions[0];

		await clearingHouse.openPosition(
			PositionDirection.SHORT,
			usdcAmount,
			marketIndex,
			new BN(0)
		);

		user = await primaryClearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);

		userPositions =
			await primaryClearingHouse.program.account.userPositions.fetch(
				user.positions
			);
		position = userPositions.positions[0];

		assert(position.quoteAssetAmount.gt(ZERO));
		assert(position.baseAssetAmount.gt(ZERO));
		await clearingHouse.unsubscribe();
	});
});
Example #11
Source File: roundInFavor.ts    From protocol-v1 with Apache License 2.0 4 votes vote down vote up
describe('round in favor', () => {
	const provider = anchor.AnchorProvider.local();
	const connection = provider.connection;
	anchor.setProvider(provider);
	const chProgram = anchor.workspace.ClearingHouse as Program;

	let usdcMint;

	let primaryClearingHouse: Admin;

	// ammInvariant == k == x * y
	const ammInitialQuoteAssetReserve = new anchor.BN(17 * 10 ** 13);
	const ammInitialBaseAssetReserve = new anchor.BN(17 * 10 ** 13);

	const usdcAmount = new BN(9999 * 10 ** 3);

	before(async () => {
		usdcMint = await mockUSDCMint(provider);

		primaryClearingHouse = Admin.from(
			connection,
			provider.wallet,
			chProgram.programId
		);
		await primaryClearingHouse.initialize(usdcMint.publicKey, true);
		await primaryClearingHouse.subscribe();

		const solUsd = await mockOracle(63000);
		const periodicity = new BN(60 * 60); // 1 HOUR

		await primaryClearingHouse.initializeMarket(
			Markets[0].marketIndex,
			solUsd,
			ammInitialBaseAssetReserve,
			ammInitialQuoteAssetReserve,
			periodicity,
			new BN(63000000)
		);

		const newFeeStructure: FeeStructure = {
			feeNumerator: new BN(0),
			feeDenominator: new BN(1),
			discountTokenTiers: {
				firstTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				secondTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				thirdTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
				fourthTier: {
					minimumBalance: new BN(1),
					discountNumerator: new BN(1),
					discountDenominator: new BN(1),
				},
			},
			referralDiscount: {
				referrerRewardNumerator: new BN(1),
				referrerRewardDenominator: new BN(1),
				refereeDiscountNumerator: new BN(1),
				refereeDiscountDenominator: new BN(1),
			},
		};

		await primaryClearingHouse.updateFee(newFeeStructure);
	});

	after(async () => {
		await primaryClearingHouse.unsubscribe();
	});

	it('short', async () => {
		const keypair = new Keypair();
		await provider.connection.requestAirdrop(keypair.publicKey, 10 ** 9);
		const wallet = new Wallet(keypair);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			keypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			wallet,
			chProgram.programId
		);
		await clearingHouse.subscribe();
		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.SHORT,
			calculateTradeAmount(usdcAmount),
			marketIndex,
			new BN(0)
		);

		let user: any = await primaryClearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);
		assert(user.collateral.eq(new BN(9999000)));

		await clearingHouse.closePosition(marketIndex);

		user = await primaryClearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);
		assert(user.collateral.eq(new BN(9999000)));
		await clearingHouse.unsubscribe();
	});

	it('long', async () => {
		const keypair = new Keypair();
		await provider.connection.requestAirdrop(keypair.publicKey, 10 ** 9);
		const wallet = new Wallet(keypair);
		const userUSDCAccount = await mockUserUSDCAccount(
			usdcMint,
			usdcAmount,
			provider,
			keypair.publicKey
		);
		const clearingHouse = ClearingHouse.from(
			connection,
			wallet,
			chProgram.programId
		);
		await clearingHouse.subscribe();

		const [, userAccountPublicKey] =
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

		const marketIndex = new BN(0);
		await clearingHouse.openPosition(
			PositionDirection.LONG,
			calculateTradeAmount(usdcAmount),
			marketIndex,
			new BN(0)
		);

		let user: any = await primaryClearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);
		assert(user.collateral.eq(new BN(9999000)));

		await clearingHouse.closePosition(marketIndex);

		user = await primaryClearingHouse.program.account.user.fetch(
			userAccountPublicKey
		);
		assert(user.collateral.eq(new BN(9998999)));
		await clearingHouse.unsubscribe();
	});
});
Example #12
Source File: settleAndClaimCollateral.ts    From protocol-v1 with Apache License 2.0 4 votes vote down vote up
describe('settle and claim collateral', () => {
	const provider = anchor.AnchorProvider.local(undefined, {
		commitment: 'confirmed',
		preflightCommitment: 'confirmed',
	});
	const connection = provider.connection;
	anchor.setProvider(provider);
	const chProgram = anchor.workspace.ClearingHouse as Program;

	let usdcMint;
	let primaryClearingHouse: Admin;

	const mantissaSqrtScale = new BN(Math.sqrt(MARK_PRICE_PRECISION.toNumber()));
	const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul(
		mantissaSqrtScale
	);
	const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul(
		mantissaSqrtScale
	);

	const usdcAmount = new BN(10 * 10 ** 6);

	it('settle and claim collateral', async () => {
		usdcMint = await mockUSDCMint(provider);

		primaryClearingHouse = Admin.from(
			connection,
			provider.wallet,
			chProgram.programId,
			{
				commitment: 'confirmed',
			}
		);
		await primaryClearingHouse.initialize(usdcMint.publicKey, true);

		await primaryClearingHouse.subscribe();

		const solUsd = await mockOracle(1);
		const periodicity = new BN(60 * 60); // 1 HOUR

		await primaryClearingHouse.initializeMarket(
			Markets[0].marketIndex,
			solUsd,
			ammInitialBaseAssetReserve,
			ammInitialQuoteAssetReserve,
			periodicity
		);

		const createUser = async (): Promise<
			[ClearingHouse, ClearingHouseUser, PublicKey]
		> => {
			const userKeyPair = new Keypair();
			await provider.connection.requestAirdrop(userKeyPair.publicKey, 10 ** 9);
			const userUSDCAccount = await mockUserUSDCAccount(
				usdcMint,
				usdcAmount,
				provider,
				userKeyPair.publicKey
			);
			const clearingHouse = ClearingHouse.from(
				connection,
				new Wallet(userKeyPair),
				chProgram.programId,
				{
					commitment: 'confirmed',
				}
			);
			await clearingHouse.subscribe();
			await clearingHouse.initializeUserAccountAndDepositCollateral(
				usdcAmount,
				userUSDCAccount.publicKey
			);

			const clearingHouseUser = ClearingHouseUser.from(
				clearingHouse,
				userKeyPair.publicKey
			);
			await clearingHouseUser.subscribe();

			return [clearingHouse, clearingHouseUser, userUSDCAccount.publicKey];
		};

		const [
			firstClearingHouse,
			firstClearingHouseUser,
			firstUSDCTokenAccountPublicKey,
		] = await createUser();
		const [
			secondClearingHouse,
			secondClearingHouseUser,
			secondUSDCTokenAccountPublicKey,
		] = await createUser();
		const [
			thirdClearingHouse,
			thirdClearingHouseUser,
			thirdUSDCTokenAccountPublicKey,
		] = await createUser();

		await secondClearingHouse.openPosition(
			PositionDirection.LONG,
			usdcAmount,
			new BN(0)
		);

		await primaryClearingHouse.moveAmmPrice(
			ammInitialBaseAssetReserve.div(new BN(10)),
			ammInitialQuoteAssetReserve,
			new BN(0)
		);

		await primaryClearingHouse.fetchAccounts();

		await primaryClearingHouse.adminUpdateUserForgoSettlement(
			firstClearingHouseUser.authority
		);

		await firstClearingHouseUser.fetchAccounts();
		assert(
			firstClearingHouseUser.getUserAccount().forgoPositionSettlement === 1
		);

		await primaryClearingHouse.initializeSettlementState();
		let settlementState = await primaryClearingHouse.getSettlementAccount();
		assert(settlementState.collateralClaimed.eq(ZERO));
		assert(
			settlementState.collateralAvailableToClaim.eq(usdcAmount.mul(new BN(3)))
		);
		assert(settlementState.totalSettlementValue.eq(new BN(46244321)));
		assert(!settlementState.enabled);

		await primaryClearingHouse.updateSettlementStateEnabled(true);

		try {
			await firstClearingHouse.settlePositionAndClaimCollateral(
				firstUSDCTokenAccountPublicKey
			);
			assert(false);
		} catch (e) {
			//should throw err
		}

		assert(secondClearingHouseUser.getUserAccount().hasSettledPosition === 0);
		let expectedCollateralClaimed =
			secondClearingHouseUser.getClaimableCollateral(settlementState);
		await secondClearingHouse.settlePositionAndClaimCollateral(
			secondUSDCTokenAccountPublicKey
		);

		await secondClearingHouseUser.fetchAccounts();
		assert(
			secondClearingHouseUser
				.getUserAccount()
				.collateralClaimed.eq(expectedCollateralClaimed)
		);
		assert(
			secondClearingHouseUser
				.getUserAccount()
				.lastCollateralAvailableToClaim.eq(
					settlementState.collateralAvailableToClaim
				)
		);
		assert(secondClearingHouseUser.getUserAccount().hasSettledPosition === 1);

		const secondUSDCTokenAccountBalance =
			await provider.connection.getTokenAccountBalance(
				secondUSDCTokenAccountPublicKey
			);
		assert(
			new BN(secondUSDCTokenAccountBalance.value.amount).eq(
				expectedCollateralClaimed
			)
		);

		settlementState = await primaryClearingHouse.getSettlementAccount();
		assert(settlementState.collateralClaimed.eq(expectedCollateralClaimed));

		expectedCollateralClaimed =
			thirdClearingHouseUser.getClaimableCollateral(settlementState);
		await thirdClearingHouse.settlePositionAndClaimCollateral(
			thirdUSDCTokenAccountPublicKey
		);

		await thirdClearingHouse.fetchAccounts();
		assert(
			thirdClearingHouseUser
				.getUserAccount()
				.collateralClaimed.eq(expectedCollateralClaimed)
		);
		assert(
			thirdClearingHouseUser
				.getUserAccount()
				.lastCollateralAvailableToClaim.eq(
					settlementState.collateralAvailableToClaim
				)
		);

		const thirdUSDCTokenAccountBalance =
			await provider.connection.getTokenAccountBalance(
				thirdUSDCTokenAccountPublicKey
			);
		assert(
			new BN(thirdUSDCTokenAccountBalance.value.amount).eq(
				expectedCollateralClaimed
			)
		);

		await primaryClearingHouse.unsubscribe();
		await firstClearingHouse.unsubscribe();
		await firstClearingHouseUser.unsubscribe();
		await secondClearingHouse.unsubscribe();
		await secondClearingHouseUser.unsubscribe();
		await thirdClearingHouse.unsubscribe();
		await thirdClearingHouseUser.unsubscribe();
	});
});
Example #13
Source File: exercise_v2.ts    From psyoptions with Apache License 2.0 4 votes vote down vote up
describe("exerciseOption", () => {
  const payer = anchor.web3.Keypair.generate();
  const mintAuthority = anchor.web3.Keypair.generate();
  const program = anchor.workspace.PsyAmerican as anchor.Program<PsyAmerican>;
  const provider = program.provider;
  // @ts-ignore
  const wallet = provider.wallet as unknown as anchor.Wallet;

  const minter = anchor.web3.Keypair.generate();
  const minterProvider = new AnchorProvider(
    provider.connection,
    new Wallet(minter),
    {}
  );
  const minterProgram = new Program(
    program.idl,
    program.programId,
    minterProvider
  );
  const exerciser = anchor.web3.Keypair.generate();
  const exerciserProvider = new AnchorProvider(
    provider.connection,
    new Wallet(exerciser),
    {}
  );
  const exerciserProgram = new Program(
    program.idl,
    program.programId,
    exerciserProvider
  );

  let quoteToken: Token;
  let underlyingToken: Token;
  let optionToken: Token;
  let underlyingAmountPerContract: anchor.BN;
  let quoteAmountPerContract: anchor.BN;
  let optionMarketKey: PublicKey;
  let optionMarket: OptionMarketWithKey;
  let exerciseFeeKey: PublicKey;
  let exerciserOptionAcct: Keypair;
  let exerciserQuoteAcct: Keypair;
  let exerciserUnderlyingAcct: Keypair;
  let remainingAccounts: AccountMeta[] = [];
  let instructions: TransactionInstruction[] = [];

  let size = new u64(2);

  before(async () => {
    // airdrop SOL to the payer and minter
    await provider.connection.confirmTransaction(
      await provider.connection.requestAirdrop(
        payer.publicKey,
        100 * LAMPORTS_PER_SOL
      ),
      "confirmed"
    );
    await provider.connection.confirmTransaction(
      await provider.connection.requestAirdrop(
        minter.publicKey,
        100 * LAMPORTS_PER_SOL
      ),
      "confirmed"
    );
    await provider.connection.confirmTransaction(
      await provider.connection.requestAirdrop(
        exerciser.publicKey,
        100 * LAMPORTS_PER_SOL
      ),
      "confirmed"
    );
  });

  describe("Non-nft OptionMarket", () => {
    before(async () => {
      // Initialize a new OptionMarket
      ({
        quoteToken,
        underlyingToken,
        optionToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        optionMarketKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      // Create a new minter
      const {
        optionAccount: minterOptionAcct,
        underlyingAccount: minterUnderlyingAccount,
        writerTokenAccount: minterWriterAcct,
      } = await createMinter(
        provider.connection,
        minter,
        mintAuthority,
        underlyingToken,
        new anchor.BN(100)
          .mul(optionMarket.underlyingAmountPerContract)
          .muln(2)
          .toNumber(),
        optionMarket.optionMint,
        optionMarket.writerTokenMint,
        quoteToken
      );
      // Mint a bunch of contracts to the minter
      const { ix: mintOptionsIx } =
        await psyAmericanInstructions.mintOptionV2Instruction(
          minterProgram,
          minterOptionAcct.publicKey,
          minterWriterAcct.publicKey,
          minterUnderlyingAccount.publicKey,
          new anchor.BN(100),
          optionMarket
        );
      await program.provider.sendAndConfirm!(
        new Transaction().add(mintOptionsIx),
        [minter]
      );
      // Create an exerciser
      ({
        optionAccount: exerciserOptionAcct,
        quoteAccount: exerciserQuoteAcct,
        underlyingAccount: exerciserUnderlyingAcct,
      } = await createExerciser(
        provider.connection,
        exerciser,
        mintAuthority,
        quoteToken,
        new anchor.BN(100)
          .mul(optionMarket.quoteAmountPerContract)
          .muln(2)
          .toNumber(),
        optionMarket.optionMint,
        underlyingToken.publicKey
      ));

      // Transfer a options to the exerciser
      await optionToken.transfer(
        minterOptionAcct.publicKey,
        exerciserOptionAcct.publicKey,
        minter,
        [],
        new u64(100)
      );
    });
    beforeEach(async () => {
      size = new u64(2);
    });

    it("should be properly setup", async () => {
      const exerciserOption = await optionToken.getAccountInfo(
        exerciserOptionAcct.publicKey
      );
      assert.equal(exerciserOption.amount.toString(), new u64(100).toString());
    });

    describe("proper exercise", () => {
      it("should burn the option token, swap the quote and underlying assets", async () => {
        const optionTokenBefore = await optionToken.getMintInfo();
        const underlyingPoolBefore = await underlyingToken.getAccountInfo(
          optionMarket.underlyingAssetPool
        );
        const quotePoolBefore = await quoteToken.getAccountInfo(
          optionMarket.quoteAssetPool
        );
        const exerciserQuoteBefore = await quoteToken.getAccountInfo(
          exerciserQuoteAcct.publicKey
        );
        try {
          const instruction =
            psyAmericanInstructions.exerciseOptionsV2Instruction(
              exerciserProgram,
              size,
              optionMarket,
              exerciserOptionAcct.publicKey,
              exerciserUnderlyingAcct.publicKey,
              exerciserQuoteAcct.publicKey
            );
          await exerciserProgram.provider.sendAndConfirm!(
            new Transaction().add(instruction)
          );
        } catch (err) {
          console.error((err as AnchorError).error.errorMessage);
          throw err;
        }
        const optionTokenAfter = await optionToken.getMintInfo();
        const optionTokenDiff = optionTokenAfter.supply.sub(
          optionTokenBefore.supply
        );
        assert.equal(optionTokenDiff.toString(), size.neg().toString());

        const underlyingPoolAfter = await underlyingToken.getAccountInfo(
          optionMarket.underlyingAssetPool
        );
        const underlyingPoolDiff = underlyingPoolAfter.amount.sub(
          underlyingPoolBefore.amount
        );
        assert.equal(
          underlyingPoolDiff.toString(),
          size.mul(underlyingAmountPerContract).neg().toString()
        );

        const quotePoolAfter = await quoteToken.getAccountInfo(
          optionMarket.quoteAssetPool
        );
        const quotePoolDiff = quotePoolAfter.amount.sub(quotePoolBefore.amount);
        assert.equal(
          quotePoolDiff.toString(),
          size.mul(quoteAmountPerContract).toString()
        );

        const exerciserQuoteAfter = await quoteToken.getAccountInfo(
          exerciserQuoteAcct.publicKey
        );
        const exerciserQuoteDiff = exerciserQuoteAfter.amount.sub(
          exerciserQuoteBefore.amount
        );
        const exerciseFee = new BN(0);
        assert.equal(
          exerciserQuoteDiff.neg().toString(),
          exerciseFee.add(size.mul(quoteAmountPerContract)).toString()
        );
      });
    });
    describe("quote asset pool is not the same as the OptionMarket", () => {
      let badQuoteAssetPoolAcct: Keypair;
      beforeEach(async () => {
        // Create a new token account and set it as the mintFeeKey
        const { tokenAccount } = await initNewTokenAccount(
          provider.connection,
          FEE_OWNER_KEY,
          quoteToken.publicKey,
          payer
        );
        badQuoteAssetPoolAcct = tokenAccount;
      });
      it("should error", async () => {
        try {
          const instruction =
            psyAmericanInstructions.exerciseOptionsV2Instruction(
              exerciserProgram,
              size,
              {
                ...optionMarket,
                quoteAssetPool: badQuoteAssetPoolAcct.publicKey,
              },
              exerciserOptionAcct.publicKey,
              exerciserUnderlyingAcct.publicKey,
              exerciserQuoteAcct.publicKey
            );
          await exerciserProgram.provider.sendAndConfirm!(
            new Transaction().add(instruction)
          );
          assert.ok(false);
        } catch (err) {
          const programError = parseTransactionError(err);
          console.log("*** programError", programError);
          const errMsg =
            "Quote pool account does not match the value on the OptionMarket";
          assert.equal(programError.msg, errMsg);
        }
      });
    });
    describe("Underlying asset pool is not the same as the OptionMarket", () => {
      let badUnderlyingAssetPoolAcct: Keypair;
      beforeEach(async () => {
        // Create a new token account and set it as the mintFeeKey
        const { tokenAccount } = await initNewTokenAccount(
          provider.connection,
          FEE_OWNER_KEY,
          underlyingToken.publicKey,
          payer
        );
        badUnderlyingAssetPoolAcct = tokenAccount;
      });
      it("should error", async () => {
        try {
          const instruction =
            psyAmericanInstructions.exerciseOptionsV2Instruction(
              exerciserProgram,
              size,
              {
                ...optionMarket,
                underlyingAssetPool: badUnderlyingAssetPoolAcct.publicKey,
              },
              exerciserOptionAcct.publicKey,
              exerciserUnderlyingAcct.publicKey,
              exerciserQuoteAcct.publicKey
            );
          await exerciserProgram.provider.sendAndConfirm!(
            new Transaction().add(instruction)
          );
          assert.ok(false);
        } catch (err) {
          const programError = parseTransactionError(err);
          const errMsg =
            "Underlying pool account does not match the value on the OptionMarket";
          assert.equal(programError.msg, errMsg);
        }
      });
    });
    describe("Underlying destination mint is not the same as the underlying asset", () => {
      let badUnderlyingDest: Keypair;
      beforeEach(async () => {
        // Create a new token account and set it as the mintFeeKey
        const { tokenAccount } = await initNewTokenAccount(
          provider.connection,
          FEE_OWNER_KEY,
          quoteToken.publicKey,
          payer
        );
        badUnderlyingDest = tokenAccount;
      });
      it("should error", async () => {
        try {
          const instruction =
            psyAmericanInstructions.exerciseOptionsV2Instruction(
              exerciserProgram,
              size,
              optionMarket,
              exerciserOptionAcct.publicKey,
              badUnderlyingDest.publicKey,
              exerciserQuoteAcct.publicKey
            );
          await exerciserProgram.provider.sendAndConfirm!(
            new Transaction().add(instruction)
          );
          assert.ok(false);
        } catch (err) {
          const programError = parseTransactionError(err);
          const errMsg =
            "Underlying destination mint must match underlying asset mint address";
          assert.equal(programError.msg, errMsg);
        }
      });
    });
    describe("OptionToken Mint is not the same as the OptionMarket", () => {
      let badOptionToken: Token;
      beforeEach(async () => {
        // Create a new token account and set it as the mintFeeKey
        const { mintAccount } = await initNewTokenMint(
          provider.connection,
          FEE_OWNER_KEY,
          payer
        );
        badOptionToken = new Token(
          provider.connection,
          mintAccount.publicKey,
          TOKEN_PROGRAM_ID,
          payer
        );
      });
      it("should error", async () => {
        try {
          const instruction =
            psyAmericanInstructions.exerciseOptionsV2Instruction(
              exerciserProgram,
              size,
              { ...optionMarket, optionMint: badOptionToken.publicKey },
              exerciserOptionAcct.publicKey,
              exerciserUnderlyingAcct.publicKey,
              exerciserQuoteAcct.publicKey
            );
          await exerciserProgram.provider.sendAndConfirm!(
            new Transaction().add(instruction)
          );
          assert.ok(false);
        } catch (err) {
          const programError = parseTransactionError(err);
          const errMsg =
            "OptionToken mint does not match the value on the OptionMarket";
          assert.equal(programError.msg, errMsg);
        }
      });
    });
  });

  describe("Expired option market", () => {
    before(async () => {
      // Initialize a new OptionMarket
      ({
        quoteToken,
        underlyingToken,
        optionToken,
        underlyingAmountPerContract,
        quoteAmountPerContract,
        optionMarketKey,
        exerciseFeeKey,
        optionMarket,
        remainingAccounts,
        instructions,
      } = await initSetup(provider, payer, mintAuthority, program, {
        // set expiration to 2 seconds from now
        expiration: new anchor.BN(new Date().getTime() / 1000 + 4),
      }));
      await initOptionMarket(
        program,
        payer,
        optionMarket,
        remainingAccounts,
        instructions
      );
      // Create a new minter
      const {
        optionAccount: minterOptionAcct,
        underlyingAccount: minterUnderlyingAccount,
        writerTokenAccount: minterWriterAcct,
      } = await createMinter(
        provider.connection,
        minter,
        mintAuthority,
        underlyingToken,
        new anchor.BN(100)
          .mul(optionMarket.underlyingAmountPerContract)
          .muln(2)
          .toNumber(),
        optionMarket.optionMint,
        optionMarket.writerTokenMint,
        quoteToken
      );
      // Mint a bunch of contracts to the minter
      const { ix: mintOptionsIx } =
        await psyAmericanInstructions.mintOptionV2Instruction(
          minterProgram,
          minterOptionAcct.publicKey,
          minterWriterAcct.publicKey,
          minterUnderlyingAccount.publicKey,
          new anchor.BN(100),
          optionMarket
        );
      await program.provider.sendAndConfirm!(
        new Transaction().add(mintOptionsIx),
        [minter]
      );
      // Create an exerciser
      ({
        optionAccount: exerciserOptionAcct,
        quoteAccount: exerciserQuoteAcct,
        underlyingAccount: exerciserUnderlyingAcct,
      } = await createExerciser(
        provider.connection,
        exerciser,
        mintAuthority,
        quoteToken,
        new anchor.BN(100)
          .mul(optionMarket.quoteAmountPerContract)
          .muln(2)
          .toNumber(),
        optionMarket.optionMint,
        optionMarket.underlyingAssetMint
      ));

      // Transfer a options to the exerciser
      await optionToken.transfer(
        minterOptionAcct.publicKey,
        exerciserOptionAcct.publicKey,
        minter,
        [],
        new u64(100)
      );
    });
    beforeEach(async () => {
      size = new u64(2);
    });
    it("should error", async () => {
      try {
        await wait(3000);
        const instruction =
          psyAmericanInstructions.exerciseOptionsV2Instruction(
            exerciserProgram,
            size,
            optionMarket,
            exerciserOptionAcct.publicKey,
            exerciserUnderlyingAcct.publicKey,
            exerciserQuoteAcct.publicKey
          );
        await exerciserProgram.provider.sendAndConfirm!(
          new Transaction().add(instruction)
        );
        assert.ok(false);
      } catch (err) {
        const programError = parseTransactionError(err);
        const errMsg = "OptionMarket is expired, can't exercise";
        assert.equal(programError.msg, errMsg);
      }
    });
  });
});