123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- const assert = require("assert");
- const { Token, TOKEN_PROGRAM_ID } = require("@solana/spl-token");
- const anchor = require("@project-serum/anchor");
- const serum = require("@project-serum/serum");
- const { BN } = anchor;
- const {
- Keypair,
- Transaction,
- TransactionInstruction,
- PublicKey,
- SystemProgram,
- SYSVAR_RENT_PUBKEY,
- } = anchor.web3;
- const {
- DexInstructions,
- OpenOrders,
- OpenOrdersPda,
- Logger,
- ReferralFees,
- MarketProxyBuilder,
- } = serum;
- const { initMarket, sleep } = require("./utils");
- const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin");
- const REFERRAL_AUTHORITY = new PublicKey(
- "3oSfkjQZKCneYvsCTZc9HViGAPqR8pYr4h9YeGB5ZxHf"
- );
- describe("permissioned-markets", () => {
- // Anchor client setup.
- const provider = anchor.Provider.env();
- anchor.setProvider(provider);
- const programs = [
- anchor.workspace.PermissionedMarkets,
- anchor.workspace.PermissionedMarketsMiddleware,
- ];
- programs.forEach((program, index) => {
- // Token client.
- let usdcClient;
- // Global DEX accounts and clients shared across all tests.
- let marketProxy, tokenAccount, usdcAccount;
- let openOrders, openOrdersBump, openOrdersInitAuthority, openOrdersBumpinit;
- let usdcPosted;
- let referralTokenAddress;
- it("BOILERPLATE: Initializes an orderbook", async () => {
- const getAuthority = async (market) => {
- return (
- await PublicKey.findProgramAddress(
- [
- anchor.utils.bytes.utf8.encode("open-orders-init"),
- DEX_PID.toBuffer(),
- market.toBuffer(),
- ],
- program.programId
- )
- )[0];
- };
- const marketLoader = async (market) => {
- return new MarketProxyBuilder()
- .middleware(
- new OpenOrdersPda({
- proxyProgramId: program.programId,
- dexProgramId: DEX_PID,
- })
- )
- .middleware(new ReferralFees())
- .middleware(new Identity())
- .middleware(new Logger())
- .load({
- connection: provider.connection,
- market,
- dexProgramId: DEX_PID,
- proxyProgramId: program.programId,
- options: { commitment: "recent" },
- });
- };
- const { marketA, godA, godUsdc, usdc } = await initMarket({
- provider,
- getAuthority,
- proxyProgramId: program.programId,
- marketLoader,
- });
- marketProxy = marketA;
- usdcAccount = godUsdc;
- tokenAccount = godA;
- usdcClient = new Token(
- provider.connection,
- usdc,
- TOKEN_PROGRAM_ID,
- provider.wallet.payer
- );
- referral = await usdcClient.createAccount(REFERRAL_AUTHORITY);
- });
- it("BOILERPLATE: Calculates open orders addresses", async () => {
- const [_openOrders, bump] = await PublicKey.findProgramAddress(
- [
- anchor.utils.bytes.utf8.encode("open-orders"),
- DEX_PID.toBuffer(),
- marketProxy.market.address.toBuffer(),
- program.provider.wallet.publicKey.toBuffer(),
- ],
- program.programId
- );
- const [
- _openOrdersInitAuthority,
- bumpInit,
- ] = await PublicKey.findProgramAddress(
- [
- anchor.utils.bytes.utf8.encode("open-orders-init"),
- DEX_PID.toBuffer(),
- marketProxy.market.address.toBuffer(),
- ],
- program.programId
- );
- // Save global variables re-used across tests.
- openOrders = _openOrders;
- openOrdersBump = bump;
- openOrdersInitAuthority = _openOrdersInitAuthority;
- openOrdersBumpInit = bumpInit;
- });
- it("Creates an open orders account", async () => {
- const tx = new Transaction();
- tx.add(
- await marketProxy.instruction.initOpenOrders(
- program.provider.wallet.publicKey,
- marketProxy.market.address,
- marketProxy.market.address, // Dummy. Replaced by middleware.
- marketProxy.market.address // Dummy. Replaced by middleware.
- )
- );
- await provider.send(tx);
- const account = await provider.connection.getAccountInfo(openOrders);
- assert.ok(account.owner.toString() === DEX_PID.toString());
- });
- it("Posts a bid on the orderbook", async () => {
- const size = 1;
- const price = 1;
- usdcPosted = new BN(
- marketProxy.market._decoded.quoteLotSize.toNumber()
- ).mul(
- marketProxy.market
- .baseSizeNumberToLots(size)
- .mul(marketProxy.market.priceNumberToLots(price))
- );
- const tx = new Transaction();
- tx.add(
- marketProxy.instruction.newOrderV3({
- owner: program.provider.wallet.publicKey,
- payer: usdcAccount,
- side: "buy",
- price,
- size,
- orderType: "postOnly",
- clientId: new BN(999),
- openOrdersAddressKey: openOrders,
- selfTradeBehavior: "abortTransaction",
- })
- );
- await provider.send(tx);
- });
- it("Cancels a bid on the orderbook", async () => {
- // Given.
- const beforeOoAccount = await OpenOrders.load(
- provider.connection,
- openOrders,
- DEX_PID
- );
- // When.
- const tx = new Transaction();
- tx.add(
- await marketProxy.instruction.cancelOrderByClientId(
- program.provider.wallet.publicKey,
- openOrders,
- new BN(999)
- )
- );
- await provider.send(tx);
- // Then.
- const afterOoAccount = await OpenOrders.load(
- provider.connection,
- openOrders,
- DEX_PID
- );
- assert.ok(beforeOoAccount.quoteTokenFree.eq(new BN(0)));
- assert.ok(beforeOoAccount.quoteTokenTotal.eq(usdcPosted));
- assert.ok(afterOoAccount.quoteTokenFree.eq(usdcPosted));
- assert.ok(afterOoAccount.quoteTokenTotal.eq(usdcPosted));
- });
- // Need to crank the cancel so that we can close later.
- it("Cranks the cancel transaction", async () => {
- // TODO: can do this in a single transaction if we covert the pubkey bytes
- // into a [u64; 4] array and sort. I'm lazy though.
- let eq = await marketProxy.market.loadEventQueue(provider.connection);
- while (eq.length > 0) {
- const tx = new Transaction();
- tx.add(
- marketProxy.market.makeConsumeEventsInstruction([eq[0].openOrders], 1)
- );
- await provider.send(tx);
- eq = await marketProxy.market.loadEventQueue(provider.connection);
- }
- });
- it("Settles funds on the orderbook", async () => {
- // Given.
- const beforeTokenAccount = await usdcClient.getAccountInfo(usdcAccount);
- // When.
- const tx = new Transaction();
- tx.add(
- await marketProxy.instruction.settleFunds(
- openOrders,
- provider.wallet.publicKey,
- tokenAccount,
- usdcAccount,
- referral
- )
- );
- await provider.send(tx);
- // Then.
- const afterTokenAccount = await usdcClient.getAccountInfo(usdcAccount);
- assert.ok(
- afterTokenAccount.amount.sub(beforeTokenAccount.amount).toNumber() ===
- usdcPosted.toNumber()
- );
- });
- it("Closes an open orders account", async () => {
- // Given.
- const beforeAccount = await program.provider.connection.getAccountInfo(
- program.provider.wallet.publicKey
- );
- // When.
- const tx = new Transaction();
- tx.add(
- marketProxy.instruction.closeOpenOrders(
- openOrders,
- provider.wallet.publicKey,
- provider.wallet.publicKey
- )
- );
- await provider.send(tx);
- // Then.
- const afterAccount = await program.provider.connection.getAccountInfo(
- program.provider.wallet.publicKey
- );
- const closedAccount = await program.provider.connection.getAccountInfo(
- openOrders
- );
- assert.ok(23352768 === afterAccount.lamports - beforeAccount.lamports);
- assert.ok(closedAccount === null);
- });
- });
- });
- // Dummy identity middleware used for testing.
- class Identity {
- initOpenOrders(ix) {
- this.proxy(ix);
- }
- newOrderV3(ix) {
- this.proxy(ix);
- }
- cancelOrderV2(ix) {
- this.proxy(ix);
- }
- cancelOrderByClientIdV2(ix) {
- this.proxy(ix);
- }
- settleFunds(ix) {
- this.proxy(ix);
- }
- closeOpenOrders(ix) {
- this.proxy(ix);
- }
- proxy(ix) {
- ix.keys = [
- { pubkey: SYSVAR_RENT_PUBKEY, isWritable: false, isSigner: false },
- ...ix.keys,
- ];
- }
- }
|