adding CancelAllSpotOrders instruction into the lib (#79)

* adding CancelAllSpotOrders instruction into the lib

* adding test to test cancelallspotorders

* correcting tests for cancelallspotorders

* adding new yarn script to launch cancelallspotorders test

* cancelallspotorders test changes after max's review.

* moving pyth-client from dependencies to dev-dependecies
This commit is contained in:
godmodegalactus 2022-06-09 20:19:05 +02:00 committed by GitHub
parent ac2a042efc
commit 8951e86b46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 262 additions and 2 deletions

View File

@ -37,6 +37,7 @@
"test-stoploss": "ts-node test/stoploss.test.ts",
"test-closeaccount": "ts-node test/closeaccount.test.ts",
"test-cancelside": "ts-node test/cancelside.test.ts",
"test-cancelallspotorders": "ts-node test/cancelallspotorders.test.ts",
"mint-devnet": "ts-node src/mintDevnet.ts",
"test": "mocha -r ts-node/register test/**/*.test.ts --timeout 0",
"test:build": "yarn build && yarn",
@ -62,7 +63,8 @@
"prettier": "^2.0.5",
"ts-node": "^9.1.1",
"typedoc": "^0.22.5",
"typescript": "^4.6.3"
"typescript": "^4.6.3",
"@pythnetwork/client": "^2.7.0"
},
"files": [
"lib"

View File

@ -116,6 +116,7 @@ import {
makeUpgradeMangoAccountV0V1Instruction,
makeWithdrawInstruction,
makeWithdrawMsrmInstruction,
makeCancelAllSpotOrdersInstruction,
} from './instruction';
import {
getFeeRates,
@ -5296,4 +5297,64 @@ export class MangoClient {
return referrerIds;
}
}
async cancelAllSpotOrders(
mangoGroup: MangoGroup,
mangoAccount: MangoAccount,
spotMarket: Market,
owner: Payer,
limit: number,
) {
if(!owner.publicKey)
return;
const marketIndex = mangoGroup.getSpotMarketIndex(spotMarket.address);
const baseRootBank = mangoGroup.rootBankAccounts[marketIndex];
const quoteRootBank = mangoGroup.rootBankAccounts[QUOTE_INDEX];
if(baseRootBank == null || quoteRootBank == null)
{
console.log("A root bank is null")
return;
}
const baseNodeBanks = await baseRootBank.loadNodeBanks(this.connection);
const quoteNodeBanks = await quoteRootBank.loadNodeBanks(this.connection);
const spotMarketIndex = mangoGroup.getSpotMarketIndex(spotMarket.publicKey);
const dexSigner = await PublicKey.createProgramAddress(
[
spotMarket.publicKey.toBuffer(),
spotMarket['_decoded'].vaultSignerNonce.toArrayLike(Buffer, 'le', 8),
],
spotMarket.programId,
);
const instruction = makeCancelAllSpotOrdersInstruction(
this.programId,
mangoGroup.publicKey,
mangoGroup.mangoCache,
mangoAccount.publicKey,
owner.publicKey,
baseRootBank.publicKey,
baseNodeBanks[0].publicKey,
baseNodeBanks[0].vault,
quoteRootBank.publicKey,
quoteNodeBanks[0].publicKey,
quoteNodeBanks[0].vault,
spotMarket.publicKey,
spotMarket.bidsAddress,
spotMarket.asksAddress,
mangoAccount.spotOpenOrders[spotMarketIndex],
mangoGroup.signerKey,
spotMarket['_decoded'].eventQueue,
spotMarket['_decoded'].baseVault,
spotMarket['_decoded'].quoteVault,
dexSigner,
mangoGroup.dexProgramId,
new BN(limit),
);
const transaction = new Transaction();
transaction.add(instruction);
return await this.sendTransaction(transaction, owner, []);
}
}

View File

@ -2326,3 +2326,63 @@ export function makeRegisterReferrerIdInstruction(
programId,
});
}
export function makeCancelAllSpotOrdersInstruction(
programId: PublicKey,
mangoGroupPk: PublicKey,
mangoCachePk: PublicKey,
mangoAccountPk: PublicKey,
owner: PublicKey,
baseRootBankPk: PublicKey,
baseNodeBankPk: PublicKey,
baseVaultPk: PublicKey,
quoteRootBankPk: PublicKey,
quoteNodeBankPk: PublicKey,
quoteVaultPk: PublicKey,
spotMarketPk: PublicKey,
bidsPk: PublicKey,
asksPk: PublicKey,
openOrders: PublicKey,
signerPk: PublicKey,
dexEventQueuePk: PublicKey,
dexBasePk: PublicKey,
dexQuotePk: PublicKey,
dexSignerPk: PublicKey,
dexProgramPk: PublicKey,
limit: BN,
): TransactionInstruction {
const keys = [
{ isSigner: false, isWritable: false, pubkey: mangoGroupPk },
{ isSigner: false, isWritable: false, pubkey: mangoCachePk },
{ isSigner: false, isWritable: true, pubkey: mangoAccountPk },
{ isSigner: true, isWritable: false, pubkey: owner },
{ isSigner: false, isWritable: false, pubkey: baseRootBankPk },
{ isSigner: false, isWritable: true, pubkey: baseNodeBankPk },
{ isSigner: false, isWritable: true, pubkey: baseVaultPk },
{ isSigner: false, isWritable: false, pubkey: quoteRootBankPk },
{ isSigner: false, isWritable: true, pubkey: quoteNodeBankPk },
{ isSigner: false, isWritable: true, pubkey: quoteVaultPk },
{ isSigner: false, isWritable: true, pubkey: spotMarketPk },
{ isSigner: false, isWritable: true, pubkey: bidsPk },
{ isSigner: false, isWritable: true, pubkey: asksPk },
{ isSigner: false, isWritable: true, pubkey: openOrders },
{ isSigner: false, isWritable: false, pubkey: signerPk },
{ isSigner: false, isWritable: true, pubkey: dexEventQueuePk },
{ isSigner: false, isWritable: true, pubkey: dexBasePk },
{ isSigner: false, isWritable: true, pubkey: dexQuotePk },
{ isSigner: false, isWritable: false, pubkey: dexSignerPk },
{ isSigner: false, isWritable: false, pubkey: dexProgramPk },
{ isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID },
];
const data = encodeMangoInstruction({
CancelAllSpotOrders: {
limit,
},
});
return new TransactionInstruction({
keys,
data,
programId,
});
}

View File

@ -611,6 +611,14 @@ MangoInstructionLayout.addVariant(
'PlacePerpOrder2',
);
MangoInstructionLayout.addVariant(
65,
struct([
u8('limit'),
]),
'CancelAllSpotOrders',
);
const instructionMaxSpan = Math.max(
// @ts-ignore
...Object.values(MangoInstructionLayout.registry).map((r) => r.span),

View File

@ -0,0 +1,129 @@
import fs from 'fs';
import os from 'os';
import { Cluster, Config, MangoClient, QUOTE_INDEX, sleep } from '../src';
import configFile from '../src/ids.json';
import { Commitment, Connection, Keypair, PublicKey, Transaction } from '@solana/web3.js';
import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
import TestGroup from './TestGroup';
import * as serum from '@project-serum/serum';
import * as pyth from "@pythnetwork/client";
import { expect } from 'chai';
async function testCancelAllSpotOrders() {
const cluster = (process.env.CLUSTER || 'devnet') as Cluster;
const sleepTime = 2000;
const config = new Config(configFile);
const groupConfig = config.getGroup(cluster, 'devnet.2')!;
const mangoProgramId = groupConfig.mangoProgramId;
const mangoGroupKey = groupConfig.publicKey;
const payer = Keypair.fromSecretKey(
new Uint8Array(
JSON.parse(
process.env.KEYPAIR ||
fs.readFileSync(os.homedir() + '/.config/solana/devnet.json', 'utf-8'),
),
)
);
const connection = new Connection(
config.cluster_urls[cluster],
'processed' as Commitment,
);
// init
//const testGroup = new TestGroup(connection, payer, mangoProgramId);
const client = new MangoClient(connection, mangoProgramId);
const mangoGroup = await client.getMangoGroup(mangoGroupKey);
const cache = await mangoGroup.loadCache(connection);
const rootBanks = await mangoGroup.loadRootBanks(connection);
const quoteRootBank = rootBanks[QUOTE_INDEX];
if (!quoteRootBank) {
throw new Error('Quote Rootbank Not Found');
}
const quoteNodeBanks = await quoteRootBank.loadNodeBanks(connection);
const accountPk: PublicKey = (await client.createMangoAccount(
mangoGroup,
payer,
1,
))!;
console.log('Created Account:', accountPk.toBase58());
await sleep(sleepTime);
const mangoAccount = await client.getMangoAccount(
accountPk,
mangoGroup.dexProgramId,
);
const quoteTokenInfo = mangoGroup.tokens[QUOTE_INDEX];
const quoteToken = new Token(
connection,
quoteTokenInfo.mint,
TOKEN_PROGRAM_ID,
payer,
);
const quoteWallet = await quoteToken.getOrCreateAssociatedAccountInfo(
payer.publicKey,
);
await client.deposit(
mangoGroup,
mangoAccount,
payer,
quoteRootBank.publicKey,
quoteNodeBanks[0].publicKey,
quoteNodeBanks[0].vault,
quoteWallet.address,
10000,
);
await sleep(sleepTime);
const marketPk = mangoGroup.spotMarkets[3].spotMarket;
const market = await serum.Market.load(connection, marketPk, undefined, groupConfig.serumProgramId);
const oracle = mangoGroup.oracles[3];
await mangoAccount.reload(connection);
const oracleData = await connection.getAccountInfo(oracle);
if(oracleData == null)
return;
// determine asset price
const price = pyth.parsePriceData(oracleData?.data);
const assetPrice = price!.price;
console.log('asset price is ' + assetPrice)
// Buy some spot to create sell instructions later
{
await client.placeSpotOrder(mangoGroup, mangoAccount, mangoGroup.mangoCache, market, payer, "buy", assetPrice ?? 0 * 1.05, 1, 'limit');
const consumeItx = market.makeConsumeEventsInstruction(mangoAccount.spotOpenOrders, 10);
const trx = new Transaction();
trx.add(consumeItx);
await client.sendTransaction(trx, payer, []);
await mangoAccount.reload(connection);
}
// placing orders
console.log('place order 1')
await client.placeSpotOrder(mangoGroup, mangoAccount, mangoGroup.mangoCache, market, payer, "buy", assetPrice ?? 0 * 0.99, 1, 'limit');
await mangoAccount.reload(connection);
console.log('place order 2')
await client.placeSpotOrder(mangoGroup, mangoAccount, mangoGroup.mangoCache, market, payer, "sell", assetPrice ?? 0 * 1.02, 1, 'limit');
// checking open orders length
{
const orders = await mangoAccount.loadSpotOrdersForMarket(connection, market, 3)
expect( orders.length === 2 );
console.log("Number of orders placed are : " + orders.length)
}
// canceling all spot orders for a market
console.log("Cancel All Spot Orders");
const signature = await client.cancelAllSpotOrders(mangoGroup, mangoAccount, market, payer, 255);
console.log("cancel all spot orders signature " + signature)
await sleep(sleepTime);
{
const orders = await mangoAccount.loadSpotOrdersForMarket(connection, market, 3)
expect( orders.length === 0 );
console.log("Number of orders placed are : " + orders.length)
}
}
testCancelAllSpotOrders()