ts client support for perps
Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
b7eda83e1b
commit
81f0f38188
|
@ -154,6 +154,9 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
|
||||
let mango_client = Arc::new(MangoClient::new(cluster, commitment, payer, admin));
|
||||
|
||||
log::info!("Program Id {}", &mango_client.program().id());
|
||||
log::info!("Admin {}", &mango_client.admin.to_base58_string());
|
||||
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
set -e pipefail
|
||||
|
||||
rg m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD -l | xargs -I % sed -i '' 's/m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD/5V2zCYCQkm4sZc3WctiwQEAzvfAiFxyjbwCvzQnmtmkM/g' %;
|
||||
# rg m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD -l | xargs -I % sed -i '' 's/m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD/5V2zCYCQkm4sZc3WctiwQEAzvfAiFxyjbwCvzQnmtmkM/g' %;
|
||||
|
||||
WALLET_WITH_FUNDS=~/.config/solana/mango-devnet.json
|
||||
PROGRAM_ID=5V2zCYCQkm4sZc3WctiwQEAzvfAiFxyjbwCvzQnmtmkM
|
||||
|
|
|
@ -20,6 +20,7 @@ pub struct MarginTrade<'info> {
|
|||
pub owner: Signer<'info>,
|
||||
}
|
||||
|
||||
// TODO: add loan fees
|
||||
pub fn margin_trade<'key, 'accounts, 'remaining, 'info>(
|
||||
ctx: Context<'key, 'accounts, 'remaining, 'info, MarginTrade<'info>>,
|
||||
banks_len: usize,
|
||||
|
|
|
@ -2,6 +2,7 @@ use anchor_lang::prelude::*;
|
|||
use bytemuck::{cast, cast_mut, cast_ref};
|
||||
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use static_assertions::const_assert_eq;
|
||||
|
||||
use crate::state::orderbook::bookside_iterator::BookSideIter;
|
||||
|
||||
|
@ -44,6 +45,11 @@ pub struct BookSide {
|
|||
pub leaf_count: usize,
|
||||
pub nodes: [AnyNode; MAX_BOOK_NODES],
|
||||
}
|
||||
const_assert_eq!(
|
||||
std::mem::size_of::<BookSide>(),
|
||||
8 + 8 * 2 + 4 + 4 + 8 + 88 * 1024
|
||||
);
|
||||
const_assert_eq!(std::mem::size_of::<BookSide>() % 8, 0);
|
||||
|
||||
impl BookSide {
|
||||
/// Iterate over all entries in the book filtering out invalid orders
|
||||
|
|
|
@ -153,6 +153,8 @@ impl QueueHeader for EventQueueHeader {
|
|||
}
|
||||
|
||||
pub type EventQueue = Queue<EventQueueHeader>;
|
||||
const_assert_eq!(std::mem::size_of::<EventQueue>(), 8 * 3 + 512 * 200);
|
||||
const_assert_eq!(std::mem::size_of::<EventQueue>() % 8, 0);
|
||||
|
||||
const EVENT_SIZE: usize = 200;
|
||||
#[derive(Copy, Clone, Debug, Pod)]
|
||||
|
|
|
@ -92,7 +92,6 @@ impl<'info> LoadZeroCopy for AccountInfo<'info> {
|
|||
|
||||
pub fn fill16_from_str(name: String) -> Result<[u8; 16]> {
|
||||
let name_bytes = name.as_bytes();
|
||||
msg!("{}", name);
|
||||
require!(name_bytes.len() < 16, MangoError::SomeError);
|
||||
let mut name_ = [0u8; 16];
|
||||
name_[..name_bytes.len()].copy_from_slice(name_bytes);
|
||||
|
@ -101,7 +100,6 @@ pub fn fill16_from_str(name: String) -> Result<[u8; 16]> {
|
|||
|
||||
pub fn fill32_from_str(name: String) -> Result<[u8; 32]> {
|
||||
let name_bytes = name.as_bytes();
|
||||
msg!("{}", name);
|
||||
require!(name_bytes.len() < 32, MangoError::SomeError);
|
||||
let mut name_ = [0u8; 32];
|
||||
name_[..name_bytes.len()].copy_from_slice(name_bytes);
|
||||
|
|
|
@ -151,10 +151,10 @@ async fn test_perp() -> Result<(), BanksClientError> {
|
|||
quote_token_index: tokens[1].index,
|
||||
quote_lot_size: 10,
|
||||
base_lot_size: 100,
|
||||
init_asset_weight: 0.95,
|
||||
maint_asset_weight: 0.975,
|
||||
init_liab_weight: 1.05,
|
||||
init_asset_weight: 0.95,
|
||||
maint_liab_weight: 1.025,
|
||||
init_liab_weight: 1.05,
|
||||
liquidation_fee: 0.012,
|
||||
maker_fee: 0.0002,
|
||||
taker_fee: 0.000,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { PublicKey } from '@solana/web3.js';
|
||||
import { MangoClient } from '../client';
|
||||
import { Bank } from './bank';
|
||||
import { PerpMarket } from './perp';
|
||||
import { Serum3Market } from './serum3';
|
||||
|
||||
export class Group {
|
||||
static from(publicKey: PublicKey, obj: { admin: PublicKey }): Group {
|
||||
return new Group(publicKey, obj.admin, new Map(), new Map());
|
||||
return new Group(publicKey, obj.admin, new Map(), new Map(), new Map());
|
||||
}
|
||||
|
||||
constructor(
|
||||
|
@ -13,6 +14,7 @@ export class Group {
|
|||
public admin: PublicKey,
|
||||
public banksMap: Map<string, Bank>,
|
||||
public serum3MarketsMap: Map<string, Serum3Market>,
|
||||
public perpMarketsMap: Map<string, PerpMarket>,
|
||||
) {}
|
||||
|
||||
public findBank(tokenIndex: number): Bank | undefined {
|
||||
|
@ -24,6 +26,7 @@ export class Group {
|
|||
public async reload(client: MangoClient) {
|
||||
await this.reloadBanks(client);
|
||||
await this.reloadSerum3Markets(client);
|
||||
await this.reloadPerpMarkets(client);
|
||||
}
|
||||
|
||||
public async reloadBanks(client: MangoClient) {
|
||||
|
@ -37,4 +40,11 @@ export class Group {
|
|||
serum3Markets.map((serum3Market) => [serum3Market.name, serum3Market]),
|
||||
);
|
||||
}
|
||||
|
||||
public async reloadPerpMarkets(client: MangoClient) {
|
||||
const perpMarkets = await client.perpGetMarket(this);
|
||||
this.perpMarketsMap = new Map(
|
||||
perpMarkets.map((perpMarket) => [perpMarket.name, perpMarket]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { BN } from '@project-serum/anchor';
|
||||
import { utf8 } from '@project-serum/anchor/dist/cjs/utils/bytes';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { MangoClient } from '../client';
|
||||
|
@ -6,6 +7,7 @@ import { I80F48, I80F48Dto } from './I80F48';
|
|||
export class MangoAccount {
|
||||
public tokens: TokenAccount[];
|
||||
public serum3: Serum3Account[];
|
||||
public perps: PerpAccount[];
|
||||
public name: string;
|
||||
|
||||
static from(
|
||||
|
@ -33,7 +35,7 @@ export class MangoAccount {
|
|||
obj.delegate,
|
||||
obj.tokens as { values: TokenAccountDto[] },
|
||||
obj.serum3 as { values: Serum3AccountDto[] },
|
||||
obj.perps,
|
||||
obj.perps as { accounts: PerpAccountDto[] },
|
||||
obj.beingLiquidated,
|
||||
obj.isBankrupt,
|
||||
obj.accountNum,
|
||||
|
@ -50,7 +52,7 @@ export class MangoAccount {
|
|||
public delegate: PublicKey,
|
||||
tokens: { values: TokenAccountDto[] },
|
||||
serum3: { values: Serum3AccountDto[] },
|
||||
perps: unknown,
|
||||
perps: { accounts: PerpAccountDto[] },
|
||||
beingLiquidated: number,
|
||||
isBankrupt: number,
|
||||
accountNum: number,
|
||||
|
@ -60,6 +62,7 @@ export class MangoAccount {
|
|||
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
|
||||
this.tokens = tokens.values.map((dto) => TokenAccount.from(dto));
|
||||
this.serum3 = serum3.values.map((dto) => Serum3Account.from(dto));
|
||||
this.perps = perps.accounts.map((dto) => PerpAccount.from(dto));
|
||||
}
|
||||
|
||||
async reload(client: MangoClient) {
|
||||
|
@ -83,9 +86,40 @@ export class MangoAccount {
|
|||
const ta = this.findToken(bank.tokenIndex);
|
||||
return bank.borrowIndex.mul(ta?.indexedValue!);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return (
|
||||
'tokens:' +
|
||||
JSON.stringify(
|
||||
this.tokens.filter(
|
||||
(token) => token.tokenIndex != TokenAccount.TokenIndexUnset,
|
||||
),
|
||||
null,
|
||||
4,
|
||||
) +
|
||||
'\nserum:' +
|
||||
JSON.stringify(
|
||||
this.serum3.filter(
|
||||
(serum3) =>
|
||||
serum3.marketIndex != Serum3Account.Serum3MarketIndexUnset,
|
||||
),
|
||||
null,
|
||||
4,
|
||||
) +
|
||||
'\nperps:' +
|
||||
JSON.stringify(
|
||||
this.perps.filter(
|
||||
(perp) => perp.marketIndex != PerpAccount.PerpMarketIndexUnset,
|
||||
),
|
||||
null,
|
||||
4,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class TokenAccount {
|
||||
static TokenIndexUnset: number = 65535;
|
||||
static from(dto: TokenAccountDto) {
|
||||
return new TokenAccount(
|
||||
I80F48.from(dto.indexedValue),
|
||||
|
@ -138,3 +172,41 @@ export class Serum3AccountDto {
|
|||
public reserved: number[],
|
||||
) {}
|
||||
}
|
||||
|
||||
export class PerpAccount {
|
||||
static PerpMarketIndexUnset = 65535;
|
||||
static from(dto: PerpAccountDto) {
|
||||
return new PerpAccount(
|
||||
dto.marketIndex,
|
||||
dto.basePositionLots.toNumber(),
|
||||
dto.quotePositionNative.val.toNumber(),
|
||||
dto.bidsBaseLots.toNumber(),
|
||||
dto.asksBaseLots.toNumber(),
|
||||
dto.takerBaseLots.toNumber(),
|
||||
dto.takerQuoteLots.toNumber(),
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
public marketIndex: number,
|
||||
public basePositionLots: number,
|
||||
public quotePositionNative: number,
|
||||
public bidsBaseLots: number,
|
||||
public asksBaseLots: number,
|
||||
public takerBaseLots: number,
|
||||
public takerQuoteLots: number,
|
||||
) {}
|
||||
}
|
||||
|
||||
export class PerpAccountDto {
|
||||
constructor(
|
||||
public marketIndex: number,
|
||||
public reserved: [],
|
||||
public basePositionLots: BN,
|
||||
public quotePositionNative: { val: BN },
|
||||
public bidsBaseLots: BN,
|
||||
public asksBaseLots: BN,
|
||||
public takerBaseLots: BN,
|
||||
public takerQuoteLots: BN,
|
||||
) {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
import { BN } from '@project-serum/anchor';
|
||||
import { utf8 } from '@project-serum/anchor/dist/cjs/utils/bytes';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { I80F48, I80F48Dto } from './I80F48';
|
||||
|
||||
export class PerpMarket {
|
||||
public name: string;
|
||||
public quoteLotSize: number;
|
||||
public baseLotSize: number;
|
||||
public maintAssetWeight: I80F48;
|
||||
public initAssetWeight: I80F48;
|
||||
public maintLiabWeight: I80F48;
|
||||
public initLiabWeight: I80F48;
|
||||
public liquidationFee: I80F48;
|
||||
public makerFee: I80F48;
|
||||
public takerFee: I80F48;
|
||||
public openInterest: number;
|
||||
public seqNum: number;
|
||||
public feesAccrued: I80F48;
|
||||
|
||||
static from(
|
||||
publicKey: PublicKey,
|
||||
obj: {
|
||||
name: number[];
|
||||
group: PublicKey;
|
||||
oracle: PublicKey;
|
||||
bids: PublicKey;
|
||||
asks: PublicKey;
|
||||
eventQueue: PublicKey;
|
||||
quoteLotSize: BN;
|
||||
baseLotSize: BN;
|
||||
maintAssetWeight: I80F48Dto;
|
||||
initAssetWeight: I80F48Dto;
|
||||
maintLiabWeight: I80F48Dto;
|
||||
initLiabWeight: I80F48Dto;
|
||||
liquidationFee: I80F48Dto;
|
||||
makerFee: I80F48Dto;
|
||||
takerFee: I80F48Dto;
|
||||
openInterest: BN;
|
||||
seqNum: any; // TODO: ts complains that this is unknown for whatever reason
|
||||
feesAccrued: I80F48Dto;
|
||||
bump: number;
|
||||
reserved: number[];
|
||||
perpMarketIndex: number;
|
||||
baseTokenIndex: number;
|
||||
quoteTokenIndex: number;
|
||||
},
|
||||
): PerpMarket {
|
||||
return new PerpMarket(
|
||||
publicKey,
|
||||
obj.name,
|
||||
obj.group,
|
||||
obj.oracle,
|
||||
obj.bids,
|
||||
obj.asks,
|
||||
obj.eventQueue,
|
||||
obj.quoteLotSize,
|
||||
obj.baseLotSize,
|
||||
obj.maintAssetWeight,
|
||||
obj.initAssetWeight,
|
||||
obj.maintLiabWeight,
|
||||
obj.initLiabWeight,
|
||||
obj.liquidationFee,
|
||||
obj.makerFee,
|
||||
obj.takerFee,
|
||||
obj.openInterest,
|
||||
obj.seqNum,
|
||||
obj.feesAccrued,
|
||||
obj.bump,
|
||||
obj.reserved,
|
||||
obj.perpMarketIndex,
|
||||
obj.baseTokenIndex,
|
||||
obj.quoteTokenIndex,
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
public publicKey: PublicKey,
|
||||
name: number[],
|
||||
public group: PublicKey,
|
||||
public oracle: PublicKey,
|
||||
public bids: PublicKey,
|
||||
public asks: PublicKey,
|
||||
public eventQueue: PublicKey,
|
||||
quoteLotSize: BN,
|
||||
baseLotSize: BN,
|
||||
maintAssetWeight: I80F48Dto,
|
||||
initAssetWeight: I80F48Dto,
|
||||
maintLiabWeight: I80F48Dto,
|
||||
initLiabWeight: I80F48Dto,
|
||||
liquidationFee: I80F48Dto,
|
||||
makerFee: I80F48Dto,
|
||||
takerFee: I80F48Dto,
|
||||
openInterest: BN,
|
||||
seqNum: BN,
|
||||
feesAccrued: I80F48Dto,
|
||||
bump: number,
|
||||
reserved: number[],
|
||||
public perpMarketIndex: number,
|
||||
public baseTokenIndex: number,
|
||||
public quoteTokenIndex: number,
|
||||
) {
|
||||
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
|
||||
this.quoteLotSize = quoteLotSize.toNumber();
|
||||
this.baseLotSize = baseLotSize.toNumber();
|
||||
this.maintAssetWeight = I80F48.from(maintAssetWeight);
|
||||
this.initAssetWeight = I80F48.from(initAssetWeight);
|
||||
this.maintLiabWeight = I80F48.from(maintLiabWeight);
|
||||
this.initLiabWeight = I80F48.from(initLiabWeight);
|
||||
this.liquidationFee = I80F48.from(liquidationFee);
|
||||
this.makerFee = I80F48.from(makerFee);
|
||||
this.takerFee = I80F48.from(takerFee);
|
||||
this.openInterest = openInterest.toNumber();
|
||||
this.seqNum = seqNum.toNumber();
|
||||
this.feesAccrued = I80F48.from(feesAccrued);
|
||||
}
|
||||
}
|
||||
|
||||
export class Side {
|
||||
static bid = { bid: {} };
|
||||
static ask = { ask: {} };
|
||||
}
|
||||
|
||||
export class OrderType {
|
||||
static limit = { limit: {} };
|
||||
static immediateOrCancel = { immediateorcancel: {} };
|
||||
static postOnly = { postonly: {} };
|
||||
static market = { market: {} };
|
||||
static postOnlySlide = { postonlyslide: {} };
|
||||
}
|
|
@ -4,8 +4,10 @@ import { Order } from '@project-serum/serum/lib/market';
|
|||
import * as spl from '@solana/spl-token';
|
||||
import {
|
||||
AccountMeta,
|
||||
Keypair,
|
||||
MemcmpFilter,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
TransactionSignature,
|
||||
} from '@solana/web3.js';
|
||||
|
@ -15,6 +17,7 @@ import { Group } from './accounts/group';
|
|||
import { I80F48 } from './accounts/I80F48';
|
||||
import { MangoAccount } from './accounts/mangoAccount';
|
||||
import { StubOracle } from './accounts/oracle';
|
||||
import { OrderType, PerpMarket, Side } from './accounts/perp';
|
||||
import {
|
||||
Serum3Market,
|
||||
Serum3OrderType,
|
||||
|
@ -621,6 +624,175 @@ export class MangoClient {
|
|||
);
|
||||
}
|
||||
|
||||
/// perps
|
||||
|
||||
async perpCreateMarket(
|
||||
group: Group,
|
||||
oraclePk: PublicKey,
|
||||
perpMarketIndex: number,
|
||||
name: string,
|
||||
baseTokenIndex: number,
|
||||
quoteTokenIndex: number,
|
||||
quoteLotSize: number,
|
||||
baseLotSize: number,
|
||||
maintAssetWeight: number,
|
||||
initAssetWeight: number,
|
||||
maintLiabWeight: number,
|
||||
initLiabWeight: number,
|
||||
liquidationFee: number,
|
||||
makerFee: number,
|
||||
takerFee: number,
|
||||
): Promise<TransactionSignature> {
|
||||
const bids = new Keypair();
|
||||
const asks = new Keypair();
|
||||
const eventQueue = new Keypair();
|
||||
|
||||
console.log(this.program.provider.wallet.publicKey.toBase58());
|
||||
|
||||
return await this.program.methods
|
||||
.perpCreateMarket(
|
||||
perpMarketIndex,
|
||||
name,
|
||||
baseTokenIndex,
|
||||
quoteTokenIndex,
|
||||
new BN(quoteLotSize),
|
||||
new BN(baseLotSize),
|
||||
maintAssetWeight,
|
||||
initAssetWeight,
|
||||
maintLiabWeight,
|
||||
initLiabWeight,
|
||||
liquidationFee,
|
||||
makerFee,
|
||||
takerFee,
|
||||
)
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
admin: this.program.provider.wallet.publicKey,
|
||||
oracle: oraclePk,
|
||||
bids: bids.publicKey,
|
||||
asks: asks.publicKey,
|
||||
eventQueue: eventQueue.publicKey,
|
||||
payer: this.program.provider.wallet.publicKey,
|
||||
})
|
||||
.preInstructions([
|
||||
SystemProgram.createAccount({
|
||||
programId: this.program.programId,
|
||||
space: 8 + 90152,
|
||||
lamports:
|
||||
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
||||
90160,
|
||||
),
|
||||
fromPubkey: this.program.provider.wallet.publicKey,
|
||||
newAccountPubkey: bids.publicKey,
|
||||
}),
|
||||
SystemProgram.createAccount({
|
||||
programId: this.program.programId,
|
||||
space: 8 + 90152,
|
||||
lamports:
|
||||
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
||||
90160,
|
||||
),
|
||||
fromPubkey: this.program.provider.wallet.publicKey,
|
||||
newAccountPubkey: asks.publicKey,
|
||||
}),
|
||||
SystemProgram.createAccount({
|
||||
programId: this.program.programId,
|
||||
space: 8 + 102424,
|
||||
lamports:
|
||||
await this.program.provider.connection.getMinimumBalanceForRentExemption(
|
||||
102432,
|
||||
),
|
||||
fromPubkey: this.program.provider.wallet.publicKey,
|
||||
newAccountPubkey: eventQueue.publicKey,
|
||||
}),
|
||||
])
|
||||
.signers([bids, asks, eventQueue])
|
||||
.rpc();
|
||||
}
|
||||
|
||||
public async perpGetMarket(
|
||||
group: Group,
|
||||
baseTokenIndex?: number,
|
||||
quoteTokenIndex?: number,
|
||||
): Promise<PerpMarket[]> {
|
||||
const bumpfbuf = Buffer.alloc(1);
|
||||
bumpfbuf.writeUInt8(255);
|
||||
|
||||
const filters: MemcmpFilter[] = [
|
||||
{
|
||||
memcmp: {
|
||||
bytes: group.publicKey.toBase58(),
|
||||
offset: 24,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (baseTokenIndex) {
|
||||
const bbuf = Buffer.alloc(2);
|
||||
bbuf.writeUInt16LE(baseTokenIndex);
|
||||
filters.push({
|
||||
memcmp: {
|
||||
bytes: bs58.encode(bbuf),
|
||||
offset: 348,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (quoteTokenIndex) {
|
||||
const qbuf = Buffer.alloc(2);
|
||||
qbuf.writeUInt16LE(quoteTokenIndex);
|
||||
filters.push({
|
||||
memcmp: {
|
||||
bytes: bs58.encode(qbuf),
|
||||
offset: 350,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return (await this.program.account.perpMarket.all(filters)).map((tuple) =>
|
||||
PerpMarket.from(tuple.publicKey, tuple.account),
|
||||
);
|
||||
}
|
||||
|
||||
async perpPlaceOrder(
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
perpMarketName: string,
|
||||
side: Side,
|
||||
priceLots: number,
|
||||
maxBaseLots: number,
|
||||
maxQuoteLots: number,
|
||||
clientOrderId: number,
|
||||
orderType: OrderType,
|
||||
expiryTimestamp: number,
|
||||
limit: number,
|
||||
) {
|
||||
const perpMarket = group.perpMarketsMap.get(perpMarketName)!;
|
||||
|
||||
await this.program.methods
|
||||
.perpPlaceOrder(
|
||||
side,
|
||||
new BN((priceLots * perpMarket.baseLotSize) / perpMarket.quoteLotSize),
|
||||
new BN(maxBaseLots),
|
||||
new BN(maxQuoteLots),
|
||||
new BN(clientOrderId),
|
||||
orderType,
|
||||
new BN(expiryTimestamp),
|
||||
limit,
|
||||
)
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
account: mangoAccount.publicKey,
|
||||
perpMarket: perpMarket.publicKey,
|
||||
asks: perpMarket.asks,
|
||||
bids: perpMarket.bids,
|
||||
eventQueue: perpMarket.eventQueue,
|
||||
oracle: perpMarket.oracle,
|
||||
owner: this.program.provider.wallet.publicKey,
|
||||
})
|
||||
.rpc();
|
||||
}
|
||||
|
||||
/// static
|
||||
|
||||
static async connect(
|
||||
|
@ -703,6 +875,16 @@ export class MangoClient {
|
|||
.filter((serum3Account) => serum3Account.marketIndex !== 65535)
|
||||
.map((serum3Account) => serum3Account.openOrders),
|
||||
);
|
||||
healthRemainingAccounts.push(
|
||||
...mangoAccount.perps
|
||||
.filter((perp) => perp.marketIndex !== 65535)
|
||||
.map(
|
||||
(perp) =>
|
||||
Array.from(group.perpMarketsMap.values()).filter(
|
||||
(perpMarket) => perpMarket.perpMarketIndex === perp.marketIndex,
|
||||
)[0].publicKey,
|
||||
),
|
||||
);
|
||||
|
||||
return healthRemainingAccounts;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -136,6 +136,36 @@ async function main() {
|
|||
);
|
||||
console.log(`...registerd serum3 market ${markets[0].publicKey}`);
|
||||
|
||||
// register perp market
|
||||
console.log(`Registering perp market...`);
|
||||
try {
|
||||
await client.perpCreateMarket(
|
||||
group,
|
||||
btcDevnetOracle,
|
||||
0,
|
||||
'BTC/USDC',
|
||||
0,
|
||||
1,
|
||||
10,
|
||||
100,
|
||||
0.975,
|
||||
0.95,
|
||||
1.025,
|
||||
1.05,
|
||||
0.012,
|
||||
0.0002,
|
||||
0.0,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
const perpMarkets = await client.perpGetMarket(
|
||||
group,
|
||||
group.banksMap.get('BTC')?.tokenIndex,
|
||||
group.banksMap.get('USDC')?.tokenIndex,
|
||||
);
|
||||
console.log(`...created perp market ${perpMarkets[0].publicKey}`);
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Provider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { OrderType, Side } from '../accounts/perp';
|
||||
import {
|
||||
Serum3OrderType,
|
||||
Serum3SelfTradeBehavior,
|
||||
|
@ -156,6 +157,46 @@ async function main() {
|
|||
'BTC/USDC',
|
||||
);
|
||||
|
||||
// perps
|
||||
console.log(`Placing perp bid...`);
|
||||
await client.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'BTC/USDC',
|
||||
Side.bid,
|
||||
1,
|
||||
1,
|
||||
65535,
|
||||
65535,
|
||||
OrderType.limit,
|
||||
0,
|
||||
1,
|
||||
);
|
||||
|
||||
console.log(`Placing perp ask...`);
|
||||
await client.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'BTC/USDC',
|
||||
Side.ask,
|
||||
1,
|
||||
1,
|
||||
65535,
|
||||
65535,
|
||||
OrderType.limit,
|
||||
0,
|
||||
1,
|
||||
);
|
||||
|
||||
while (true) {
|
||||
// TODO: quotePositionNative might be buggy on program side, investigate...
|
||||
console.log(
|
||||
`Waiting for self trade to consume (note: make sure keeper crank is running)...`,
|
||||
);
|
||||
await mangoAccount.reload(client);
|
||||
console.log(mangoAccount.toString());
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"declaration": true,
|
||||
"declarationDir": "dist",
|
||||
"declarationMap": true,
|
||||
"noErrorTruncation": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"es2019"
|
||||
|
|
Loading…
Reference in New Issue