Merge pull request #1 from blockworks-foundation/v3

V3
This commit is contained in:
dafyddd 2021-02-18 16:24:17 -05:00 committed by GitHub
commit d9cbc9239f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 552 additions and 460 deletions

View File

@ -1,6 +1,7 @@
CLUSTER=devnet CLUSTER=devnet
DEX_PROGRAM_ID=9MVDeYQnJmN2Dt7H44Z8cob4bET2ysdNu2uFJcatDJno
KEYPAIR=~/.config/solana/id.json KEYPAIR=~/.config/solana/id.json
BTCUSD=6pZpjgfwKJhQ93ZYNjbJFtWEaB8kZjErsiGAEXn9WNjn
BTC_WALLET=HLoPtihB8oETm1kkTpx17FEnXm7afQdS4hojTNvbg3Rg BTC_WALLET=HLoPtihB8oETm1kkTpx17FEnXm7afQdS4hojTNvbg3Rg
USDC_WALLET=GBBtcVE7WA8qdrHyhWTZkYDaz71EVHsg7wVaca9iq9xs USDC_WALLET=GBBtcVE7WA8qdrHyhWTZkYDaz71EVHsg7wVaca9iq9xs
BTC=C6kYXcaRUMqeBF5fhg165RWU7AnpT9z92fvKNoMqjmz6
ETH=8p968u9m7jZzKSsqxFDqki69MjqdFkwPM9FN4AN8hvHR
USDC=Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7

View File

@ -7,7 +7,7 @@
"name": "Blockworks Foundation", "name": "Blockworks Foundation",
"email": "hello@blockworks.foundation", "email": "hello@blockworks.foundation",
"url": "https://blockworks.foundation" "url": "https://blockworks.foundation"
}, },
"main": "lib/index.js", "main": "lib/index.js",
"source": "src/index.js", "source": "src/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -56,10 +56,10 @@
}, },
"dependencies": { "dependencies": {
"@project-serum/serum": "^0.13.20", "@project-serum/serum": "^0.13.20",
"@project-serum/sol-wallet-adapter": "^0.1.4",
"@solana/web3.js": "^0.90.0", "@solana/web3.js": "^0.90.0",
"bn.js": "^5.1.2", "bn.js": "^5.1.2",
"buffer-layout": "^1.2.0", "buffer-layout": "^1.2.0"
"@project-serum/sol-wallet-adapter": "^0.1.4"
}, },
"browserslist": [ "browserslist": [
">0.2%", ">0.2%",

View File

@ -28,14 +28,13 @@ import {
zeroKey, zeroKey,
} from './utils'; } from './utils';
import { Market, OpenOrders, Orderbook } from '@project-serum/serum'; import { Market, OpenOrders, Orderbook } from '@project-serum/serum';
import { TOKEN_PROGRAM_ID } from '@project-serum/serum/lib/token-instructions'; import { SRM_DECIMALS, TOKEN_PROGRAM_ID } from '@project-serum/serum/lib/token-instructions';
import { Order } from '@project-serum/serum/lib/market'; import { Order } from '@project-serum/serum/lib/market';
import Wallet from '@project-serum/sol-wallet-adapter'; import Wallet from '@project-serum/sol-wallet-adapter';
export class MangoGroup { export class MangoGroup {
publicKey: PublicKey; publicKey: PublicKey;
mintDecimals: number[];
accountFlags!: WideBits; accountFlags!: WideBits;
tokens!: PublicKey[]; tokens!: PublicKey[];
@ -50,10 +49,12 @@ export class MangoGroup {
totalBorrows!: number[]; totalBorrows!: number[];
maintCollRatio!: number; maintCollRatio!: number;
initCollRatio!: number; initCollRatio!: number;
srmVault!: PublicKey;
mintDecimals!: number[];
oracleDecimals!: number[];
constructor(publicKey: PublicKey, mintDecimals: number[], decoded: any) { constructor(publicKey: PublicKey, decoded: any) {
this.publicKey = publicKey; this.publicKey = publicKey;
this.mintDecimals = mintDecimals;
Object.assign(this, decoded); Object.assign(this, decoded);
} }
@ -108,13 +109,14 @@ export class MarginAccount {
deposits!: number[]; deposits!: number[];
borrows!: number[]; borrows!: number[];
openOrders!: PublicKey[]; openOrders!: PublicKey[];
srmBalance!: number;
openOrdersAccounts: undefined | (OpenOrders | undefined)[] // undefined if an openOrdersAccount not yet initialized and has zeroKey openOrdersAccounts: (OpenOrders | undefined)[] // undefined if an openOrdersAccount not yet initialized and has zeroKey
// TODO keep updated with websocket // TODO keep updated with websocket
constructor(publicKey: PublicKey, decoded: any) { constructor(publicKey: PublicKey, decoded: any) {
this.publicKey = publicKey this.publicKey = publicKey
this.createTime = getUnixTs() this.createTime = getUnixTs()
this.openOrdersAccounts = new Array(NUM_MARKETS).fill(undefined)
Object.assign(this, decoded) Object.assign(this, decoded)
} }
@ -131,6 +133,10 @@ export class MarginAccount {
return nativeToUi(this.getNativeBorrow(mangoGroup, tokenIndex), mangoGroup.mintDecimals[tokenIndex]) return nativeToUi(this.getNativeBorrow(mangoGroup, tokenIndex), mangoGroup.mintDecimals[tokenIndex])
} }
getUiSrmBalance() {
return nativeToUi(this.srmBalance, SRM_DECIMALS)
}
async loadOpenOrders( async loadOpenOrders(
connection: Connection, connection: Connection,
dexProgramId: PublicKey dexProgramId: PublicKey
@ -147,6 +153,7 @@ export class MarginAccount {
this.openOrdersAccounts = await Promise.all(promises) this.openOrdersAccounts = await Promise.all(promises)
return this.openOrdersAccounts return this.openOrdersAccounts
} }
toPrettyString( toPrettyString(
mangoGroup: MangoGroup mangoGroup: MangoGroup
): string { ): string {
@ -176,10 +183,6 @@ export class MarginAccount {
value += (this.getUiDeposit(mangoGroup, i) - this.getUiBorrow(mangoGroup, i)) * prices[i] value += (this.getUiDeposit(mangoGroup, i) - this.getUiBorrow(mangoGroup, i)) * prices[i]
} }
if (this.openOrdersAccounts == undefined) {
return value
}
for (let i = 0; i < this.openOrdersAccounts.length; i++) { for (let i = 0; i < this.openOrdersAccounts.length; i++) {
const oos = this.openOrdersAccounts[i] const oos = this.openOrdersAccounts[i]
if (oos != undefined) { if (oos != undefined) {
@ -192,10 +195,6 @@ export class MarginAccount {
} }
getAssets(mangoGroup: MangoGroup): number[] { getAssets(mangoGroup: MangoGroup): number[] {
if (this.openOrdersAccounts == undefined) {
throw new Error("openOrdersAccounts not yet loaded")
}
const assets = new Array<number>(NUM_TOKENS) const assets = new Array<number>(NUM_TOKENS)
for (let i = 0; i < NUM_TOKENS; i++) { for (let i = 0; i < NUM_TOKENS; i++) {
@ -229,9 +228,6 @@ export class MarginAccount {
for (let i = 0; i < NUM_TOKENS; i++) { for (let i = 0; i < NUM_TOKENS; i++) {
assetsVal += this.getUiDeposit(mangoGroup, i) * prices[i] assetsVal += this.getUiDeposit(mangoGroup, i) * prices[i]
} }
if (this.openOrdersAccounts == undefined) {
return assetsVal
}
for (let i = 0; i < NUM_MARKETS; i++) { for (let i = 0; i < NUM_MARKETS; i++) {
const openOrdersAccount = this.openOrdersAccounts[i] const openOrdersAccount = this.openOrdersAccounts[i]
@ -271,13 +267,10 @@ export class MarginAccount {
asks: Orderbook, asks: Orderbook,
owner: Account owner: Account
): Promise<TransactionSignature[]> { ): Promise<TransactionSignature[]> {
if (this.openOrdersAccounts == undefined) {
throw new Error("Must load open orders accounts first")
}
const marketIndex = mangoGroup.getMarketIndex(market) const marketIndex = mangoGroup.getMarketIndex(market)
const openOrdersAccount = this.openOrdersAccounts[marketIndex] const openOrdersAccount = this.openOrdersAccounts[marketIndex]
if (!openOrdersAccount) { // no open orders for this market if (openOrdersAccount == undefined) { // no open orders for this market
return [] return []
} }
@ -290,16 +283,6 @@ export class MarginAccount {
} }
async cancelAllOrders(
connection: Connection
): Promise<boolean> {
// fetch all orders using order id
return true
}
} }
export class MangoClient { export class MangoClient {
@ -380,7 +363,6 @@ export class MangoClient {
{ isSigner: false, isWritable: true, pubkey: mangoGroup.publicKey}, { isSigner: false, isWritable: true, pubkey: mangoGroup.publicKey},
{ isSigner: false, isWritable: true, pubkey: marginAccount.publicKey }, { isSigner: false, isWritable: true, pubkey: marginAccount.publicKey },
{ isSigner: true, isWritable: false, pubkey: owner.publicKey }, { isSigner: true, isWritable: false, pubkey: owner.publicKey },
{ isSigner: false, isWritable: false, pubkey: token },
{ isSigner: false, isWritable: true, pubkey: tokenAcc }, { isSigner: false, isWritable: true, pubkey: tokenAcc },
{ isSigner: false, isWritable: true, pubkey: mangoGroup.vaults[tokenIndex] }, { isSigner: false, isWritable: true, pubkey: mangoGroup.vaults[tokenIndex] },
{ isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID }, { isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID },
@ -422,10 +404,9 @@ export class MangoClient {
{ isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID }, { isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID },
{ isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY }, { isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY },
...marginAccount.openOrders.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })), ...marginAccount.openOrders.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
...mangoGroup.oracles.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })), ...mangoGroup.oracles.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey }))
...mangoGroup.tokens.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
] ]
const data = encodeMangoInstruction({Withdraw: {tokenIndex, quantity: nativeQuantity}}) const data = encodeMangoInstruction({Withdraw: {quantity: nativeQuantity}})
const instruction = new TransactionInstruction( { keys, data, programId }) const instruction = new TransactionInstruction( { keys, data, programId })
@ -457,9 +438,8 @@ export class MangoClient {
{ isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY }, { isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY },
...marginAccount.openOrders.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })), ...marginAccount.openOrders.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
...mangoGroup.oracles.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })), ...mangoGroup.oracles.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
...mangoGroup.tokens.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
] ]
const data = encodeMangoInstruction({Borrow: {tokenIndex, quantity: nativeQuantity}}) const data = encodeMangoInstruction({Borrow: {tokenIndex: new BN(tokenIndex), quantity: nativeQuantity}})
const instruction = new TransactionInstruction( { keys, data, programId }) const instruction = new TransactionInstruction( { keys, data, programId })
@ -519,10 +499,6 @@ export class MangoClient {
owner: Account owner: Account
): Promise<TransactionSignature> { ): Promise<TransactionSignature> {
if (!marginAccount.openOrdersAccounts) {
throw new Error("openOrderesAccounts must be initialized")
}
const transaction = new Transaction() const transaction = new Transaction()
for (let i = 0; i < NUM_MARKETS; i++) { for (let i = 0; i < NUM_MARKETS; i++) {
if (marginAccount.openOrdersAccounts[i] == undefined) { if (marginAccount.openOrdersAccounts[i] == undefined) {
@ -604,7 +580,6 @@ export class MangoClient {
...mangoGroup.oracles.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })), ...mangoGroup.oracles.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
...mangoGroup.vaults.map( (pubkey) => ( { isSigner: false, isWritable: true, pubkey })), ...mangoGroup.vaults.map( (pubkey) => ( { isSigner: false, isWritable: true, pubkey })),
...tokenAccs.map( (pubkey) => ( { isSigner: false, isWritable: true, pubkey })), ...tokenAccs.map( (pubkey) => ( { isSigner: false, isWritable: true, pubkey })),
...mangoGroup.tokens.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
] ]
const data = encodeMangoInstruction({Liquidate: {depositQuantities: depositsBN}}) const data = encodeMangoInstruction({Liquidate: {depositQuantities: depositsBN}})
@ -618,6 +593,71 @@ export class MangoClient {
return await this.sendTransaction(connection, transaction, liqor, additionalSigners) return await this.sendTransaction(connection, transaction, liqor, additionalSigners)
} }
async depositSrm(
connection: Connection,
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
owner: Account,
srmAccount: PublicKey,
quantity: number
): Promise<TransactionSignature> {
const nativeQuantity = uiToNative(quantity, SRM_DECIMALS)
const keys = [
{ isSigner: false, isWritable: true, pubkey: mangoGroup.publicKey },
{ isSigner: false, isWritable: true, pubkey: marginAccount.publicKey },
{ isSigner: true, isWritable: false, pubkey: owner.publicKey },
{ isSigner: false, isWritable: true, pubkey: srmAccount },
{ isSigner: false, isWritable: true, pubkey: mangoGroup.srmVault },
{ isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID },
{ isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY }
]
const data = encodeMangoInstruction({DepositSrm: {quantity: nativeQuantity}})
const instruction = new TransactionInstruction( { keys, data, programId })
const transaction = new Transaction()
transaction.add(instruction)
const additionalSigners = []
return await this.sendTransaction(connection, transaction, owner, additionalSigners)
}
async withdrawSrm(
connection: Connection,
programId: PublicKey,
mangoGroup: MangoGroup,
marginAccount: MarginAccount,
owner: Account,
srmAccount: PublicKey,
quantity: number
): Promise<TransactionSignature> {
const nativeQuantity = uiToNative(quantity, SRM_DECIMALS)
const keys = [
{ isSigner: false, isWritable: true, pubkey: mangoGroup.publicKey },
{ isSigner: false, isWritable: true, pubkey: marginAccount.publicKey },
{ isSigner: true, isWritable: false, pubkey: owner.publicKey },
{ isSigner: false, isWritable: true, pubkey: srmAccount },
{ isSigner: false, isWritable: true, pubkey: mangoGroup.srmVault },
{ isSigner: false, isWritable: false, pubkey: mangoGroup.signerKey },
{ isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID },
{ isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY }
]
const data = encodeMangoInstruction({WithdrawSrm: {quantity: nativeQuantity}})
const instruction = new TransactionInstruction( { keys, data, programId })
const transaction = new Transaction()
transaction.add(instruction)
const additionalSigners = []
return await this.sendTransaction(connection, transaction, owner, additionalSigners)
}
async placeOrder( async placeOrder(
connection: Connection, connection: Connection,
programId: PublicKey, programId: PublicKey,
@ -639,8 +679,14 @@ export class MangoClient {
orderType = orderType == undefined ? 'limit' : orderType orderType = orderType == undefined ? 'limit' : orderType
// orderType = orderType ?? 'limit' // orderType = orderType ?? 'limit'
const limitPrice = spotMarket.priceNumberToLots(price) const limitPrice = spotMarket.priceNumberToLots(price)
const maxQuantity = spotMarket.baseSizeNumberToLots(size) const maxBaseQuantity = spotMarket.baseSizeNumberToLots(size)
if (maxQuantity.lte(new BN(0))) {
// TODO verify if multiplying by highest fee tier is appropriate
const maxQuoteQuantity = new BN(spotMarket['_decoded'].quoteLotSize.toNumber()).mul(
maxBaseQuantity.mul(limitPrice),
)
if (maxBaseQuantity.lte(new BN(0))) {
throw new Error('size too small') throw new Error('size too small')
} }
if (limitPrice.lte(new BN(0))) { if (limitPrice.lte(new BN(0))) {
@ -683,23 +729,26 @@ export class MangoClient {
{ isSigner: false, isWritable: false, pubkey: spotMarket.programId }, { isSigner: false, isWritable: false, pubkey: spotMarket.programId },
{ isSigner: false, isWritable: true, pubkey: spotMarket.publicKey }, { isSigner: false, isWritable: true, pubkey: spotMarket.publicKey },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].requestQueue }, { isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].requestQueue },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].eventQueue },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].bids },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].asks },
{ isSigner: false, isWritable: true, pubkey: mangoGroup.vaults[vaultIndex] }, { isSigner: false, isWritable: true, pubkey: mangoGroup.vaults[vaultIndex] },
{ isSigner: false, isWritable: false, pubkey: mangoGroup.signerKey }, { isSigner: false, isWritable: false, pubkey: mangoGroup.signerKey },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].baseVault }, { isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].baseVault },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].quoteVault }, { isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].quoteVault },
{ isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID }, { isSigner: false, isWritable: false, pubkey: TOKEN_PROGRAM_ID },
{ isSigner: false, isWritable: false, pubkey: SYSVAR_RENT_PUBKEY }, { isSigner: false, isWritable: false, pubkey: SYSVAR_RENT_PUBKEY },
{ isSigner: false, isWritable: true, pubkey: mangoGroup.srmVault },
...openOrdersKeys.map( (pubkey) => ( { isSigner: false, isWritable: true, pubkey })), ...openOrdersKeys.map( (pubkey) => ( { isSigner: false, isWritable: true, pubkey })),
...mangoGroup.oracles.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })), ...mangoGroup.oracles.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
...mangoGroup.tokens.map( (pubkey) => ( { isSigner: false, isWritable: false, pubkey })),
] ]
const data = encodeMangoInstruction( const data = encodeMangoInstruction(
{ {
PlaceOrder: PlaceOrder:
clientId clientId
? { side, limitPrice, maxQuantity, orderType, clientId, selfTradeBehavior } ? { side, limitPrice, maxBaseQuantity, maxQuoteQuantity, selfTradeBehavior, orderType, clientId, limit: 65535}
: { side, limitPrice, maxQuantity, orderType, selfTradeBehavior } : { side, limitPrice, maxBaseQuantity, maxQuoteQuantity, selfTradeBehavior, orderType, limit: 65535}
} }
) )
@ -752,7 +801,7 @@ export class MangoClient {
const transaction = new Transaction() const transaction = new Transaction()
transaction.add(instruction) transaction.add(instruction)
// Specify signers in addition to the wallet // Specify signers in addition to the owner account
const additionalSigners = [] const additionalSigners = []
// sign, send and confirm transaction // sign, send and confirm transaction
@ -775,17 +824,17 @@ export class MangoClient {
{ isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY }, { isSigner: false, isWritable: false, pubkey: SYSVAR_CLOCK_PUBKEY },
{ isSigner: false, isWritable: false, pubkey: mangoGroup.dexProgramId }, { isSigner: false, isWritable: false, pubkey: mangoGroup.dexProgramId },
{ isSigner: false, isWritable: true, pubkey: spotMarket.publicKey }, { isSigner: false, isWritable: true, pubkey: spotMarket.publicKey },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].bids },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].asks },
{ isSigner: false, isWritable: true, pubkey: order.openOrdersAddress }, { isSigner: false, isWritable: true, pubkey: order.openOrdersAddress },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].requestQueue },
{ isSigner: false, isWritable: false, pubkey: mangoGroup.signerKey }, { isSigner: false, isWritable: false, pubkey: mangoGroup.signerKey },
{ isSigner: false, isWritable: true, pubkey: spotMarket['_decoded'].eventQueue },
] ]
const data = encodeMangoInstruction({ const data = encodeMangoInstruction({
CancelOrder: { CancelOrder: {
side: order.side, side: order.side,
orderId: order.orderId, orderId: order.orderId,
openOrders: order.openOrdersAddress,
openOrdersSlot: order.openOrdersSlot
} }
}) })
@ -799,31 +848,22 @@ export class MangoClient {
return await this.sendTransaction(connection, transaction, owner, additionalSigners) return await this.sendTransaction(connection, transaction, owner, additionalSigners)
} }
async getMangoGroup( async getMangoGroup(
connection: Connection, connection: Connection,
mangoGroupPk: PublicKey mangoGroupPk: PublicKey
): Promise<MangoGroup> { ): Promise<MangoGroup> {
const acc = await connection.getAccountInfo(mangoGroupPk); const acc = await connection.getAccountInfo(mangoGroupPk);
const decoded = MangoGroupLayout.decode(acc == null ? undefined : acc.data); const decoded = MangoGroupLayout.decode(acc == null ? undefined : acc.data);
const mintDecimals: number[] = await Promise.all(decoded.tokens.map( (pk) => getMintDecimals(connection, pk) )) return new MangoGroup(mangoGroupPk, decoded);
return new MangoGroup(mangoGroupPk, mintDecimals, decoded);
} }
async getMarginAccount( async getMarginAccount(
connection: Connection,
marginAccountPk: PublicKey
): Promise<MarginAccount> {
const acc = await connection.getAccountInfo(marginAccountPk, 'singleGossip')
return new MarginAccount(marginAccountPk, MarginAccountLayout.decode(acc == null ? undefined : acc.data))
}
async getCompleteMarginAccount(
connection: Connection, connection: Connection,
marginAccountPk: PublicKey, marginAccountPk: PublicKey,
dexProgramId: PublicKey dexProgramId: PublicKey
): Promise<MarginAccount> { ): Promise<MarginAccount> {
const ma = await this.getMarginAccount(connection, marginAccountPk) const acc = await connection.getAccountInfo(marginAccountPk, 'singleGossip')
const ma = new MarginAccount(marginAccountPk, MarginAccountLayout.decode(acc == null ? undefined : acc.data))
await ma.loadOpenOrders(connection, dexProgramId) await ma.loadOpenOrders(connection, dexProgramId)
return ma return ma
} }
@ -831,13 +871,13 @@ export class MangoClient {
async getAllMarginAccounts( async getAllMarginAccounts(
connection: Connection, connection: Connection,
programId: PublicKey, programId: PublicKey,
mangoGroupPk: PublicKey mangoGroup: MangoGroup
): Promise<MarginAccount[]>{ ): Promise<MarginAccount[]>{
const filters = [ const filters = [
{ {
memcmp: { memcmp: {
offset: MarginAccountLayout.offsetOf('mangoGroup'), offset: MarginAccountLayout.offsetOf('mangoGroup'),
bytes: mangoGroupPk.toBase58(), bytes: mangoGroup.publicKey.toBase58(),
} }
}, },
@ -847,10 +887,14 @@ export class MangoClient {
]; ];
const accounts = await getFilteredProgramAccounts(connection, programId, filters); const accounts = await getFilteredProgramAccounts(connection, programId, filters);
return accounts.map( const marginAccounts = accounts.map(
({ publicKey, accountInfo }) => ({ publicKey, accountInfo }) =>
new MarginAccount(publicKey, MarginAccountLayout.decode(accountInfo == null ? undefined : accountInfo.data)) new MarginAccount(publicKey, MarginAccountLayout.decode(accountInfo == null ? undefined : accountInfo.data))
); )
await Promise.all(marginAccounts.map((ma) => ma.loadOpenOrders(connection, mangoGroup.dexProgramId)))
return marginAccounts
} }
async getMarginAccountsForOwner( async getMarginAccountsForOwner(
@ -880,11 +924,14 @@ export class MangoClient {
]; ];
const accounts = await getFilteredProgramAccounts(connection, programId, filters); const accounts = await getFilteredProgramAccounts(connection, programId, filters);
return accounts.map( const marginAccounts = accounts.map(
({ publicKey, accountInfo }) => ({ publicKey, accountInfo }) =>
new MarginAccount(publicKey, MarginAccountLayout.decode(accountInfo == null ? undefined : accountInfo.data)) new MarginAccount(publicKey, MarginAccountLayout.decode(accountInfo == null ? undefined : accountInfo.data))
); )
await Promise.all(marginAccounts.map((ma) => ma.loadOpenOrders(connection, mangoGroup.dexProgramId)))
return marginAccounts
} }
} }
async function getMultipleAccounts( async function getMultipleAccounts(

View File

@ -6,10 +6,11 @@
"testnet": "https://testnet.solana.com" "testnet": "https://testnet.solana.com"
}, },
"devnet": { "devnet": {
"dex_program_id": "9MVDeYQnJmN2Dt7H44Z8cob4bET2ysdNu2uFJcatDJno", "dex_program_id": "6XffKSLZRxmQcRinmb4K32AegYnmkoTm6TLR2Ezp9FBR",
"fee_symbol": "SRM",
"mango_groups": { "mango_groups": {
"BTC_ETH_USDC": { "BTC_ETH_USDC": {
"mango_group_pk": "FK6o45mYRDNvgSqcZ4xd5wjyuSbxxM3LQhFz3n9eMr3x", "mango_group_pk": "6hcULrp3j5JZS7PDK3XYehzdbXZoqaiKPNNGC2sF4oz2",
"mint_pks": [ "mint_pks": [
"C6kYXcaRUMqeBF5fhg165RWU7AnpT9z92fvKNoMqjmz6", "C6kYXcaRUMqeBF5fhg165RWU7AnpT9z92fvKNoMqjmz6",
"8p968u9m7jZzKSsqxFDqki69MjqdFkwPM9FN4AN8hvHR", "8p968u9m7jZzKSsqxFDqki69MjqdFkwPM9FN4AN8hvHR",
@ -20,8 +21,8 @@
"DBiZZ6riT6QVoGVBMBnSsYFwkie1GAxWdac3NCpyrDR1" "DBiZZ6riT6QVoGVBMBnSsYFwkie1GAxWdac3NCpyrDR1"
], ],
"spot_market_pks": [ "spot_market_pks": [
"6pZpjgfwKJhQ93ZYNjbJFtWEaB8kZjErsiGAEXn9WNjn", "DY6X83vWk5VdHQEtVxFdecmEsGXHyLskkeQeo8BHn75s",
"GAZmMjiABoZimoePnrA2BiPx7BTDByskopZ7coBDnKFQ" "CQwr1dHHPeoUpzGcQHLtEYE1LdXkszxha8oG6Y7v5KWZ"
], ],
"symbols": { "symbols": {
"BTC": "C6kYXcaRUMqeBF5fhg165RWU7AnpT9z92fvKNoMqjmz6", "BTC": "C6kYXcaRUMqeBF5fhg165RWU7AnpT9z92fvKNoMqjmz6",
@ -29,9 +30,9 @@
"USDC": "Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7" "USDC": "Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7"
}, },
"vault_pks": [ "vault_pks": [
"9vUVVAXPZHVKV2vo8uv1jFtg2uR6iHQR3AvpzEnJejdR", "HHZCZBqkNrJp51g6T5Gu4eCVrCNfSJekCMgx4cjMDBJC",
"MhtHbYG65N6e4dwhCbHJFzyRX9KndxQ3mGmmLmunHoS", "GCa1SdEaod5VucgJoCvc9wFSiX4G7i5GpDbtPbhBFCHY",
"5ygigzB3ngRXzuaiz7MQdh94ZH2SAR7wNbYe4GXHZbZ2" "134B3occRaHiiMxo1M5GXavW7ocWgwR6951951LU5Vub"
] ]
} }
}, },
@ -41,13 +42,16 @@
"ETH/USDC": "DBiZZ6riT6QVoGVBMBnSsYFwkie1GAxWdac3NCpyrDR1" "ETH/USDC": "DBiZZ6riT6QVoGVBMBnSsYFwkie1GAxWdac3NCpyrDR1"
}, },
"spot_markets": { "spot_markets": {
"BTC/USDC": "6pZpjgfwKJhQ93ZYNjbJFtWEaB8kZjErsiGAEXn9WNjn", "BTC/USDC": "DY6X83vWk5VdHQEtVxFdecmEsGXHyLskkeQeo8BHn75s",
"ETH/USDC": "GAZmMjiABoZimoePnrA2BiPx7BTDByskopZ7coBDnKFQ" "ETH/USDC": "CQwr1dHHPeoUpzGcQHLtEYE1LdXkszxha8oG6Y7v5KWZ"
}, },
"symbols": { "symbols": {
"BTC": "C6kYXcaRUMqeBF5fhg165RWU7AnpT9z92fvKNoMqjmz6", "BTC": "C6kYXcaRUMqeBF5fhg165RWU7AnpT9z92fvKNoMqjmz6",
"ETH": "8p968u9m7jZzKSsqxFDqki69MjqdFkwPM9FN4AN8hvHR", "ETH": "8p968u9m7jZzKSsqxFDqki69MjqdFkwPM9FN4AN8hvHR",
"USDC": "Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7" "MSRM": "934bNdNw9QfE8dXD4mKQiKajYURfSkPhxfYZzpvmygca",
"SRM": "9FbAMDvXqNjPqZSYt4EWTguJuDrGkfvwr3gSFpiSbX9S",
"USDC": "Fq939Y5hycK62ZGwBjftLY2VyxqAQ8f1MxRqBMdAaBS7",
"USDT": "7tSPGVhneTBWZjLGJGZb9V2UntC7T98cwtSLtgcXjeSs"
} }
}, },
"localnet": { "localnet": {
@ -68,6 +72,7 @@
}, },
"mainnet-beta": { "mainnet-beta": {
"dex_program_id": "EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o", "dex_program_id": "EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o",
"fee_token": "SRM",
"spot_markets": { "spot_markets": {
"BTC/USDC": "CVfYa8RGXnuDBeGmniCcdkBwoLqVxh92xB1JqgRQx3F", "BTC/USDC": "CVfYa8RGXnuDBeGmniCcdkBwoLqVxh92xB1JqgRQx3F",
"BTC/USDT": "EXnGBBSamqzd3uxEdRLUiYzjJkTwQyorAaFXdfteuGXe", "BTC/USDT": "EXnGBBSamqzd3uxEdRLUiYzjJkTwQyorAaFXdfteuGXe",
@ -75,6 +80,10 @@
"ETH/USDT": "5abZGhrELnUnfM9ZUnvK6XJPoBU5eShZwfFPkdhAC7o", "ETH/USDT": "5abZGhrELnUnfM9ZUnvK6XJPoBU5eShZwfFPkdhAC7o",
"SOL/USDT": "7xLk17EQQ5KLDLDe44wCmupJKJjTGd8hs3eSVVhCx932", "SOL/USDT": "7xLk17EQQ5KLDLDe44wCmupJKJjTGd8hs3eSVVhCx932",
"SRM/USDC": "CDdR97S8y96v3To93aKvi3nCnjUrbuVSuumw8FLvbVeg" "SRM/USDC": "CDdR97S8y96v3To93aKvi3nCnjUrbuVSuumw8FLvbVeg"
},
"symbols": {
"MSRM": "MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L",
"SRM": "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"
} }
}, },
"testnet": { "testnet": {

View File

@ -1,4 +1,4 @@
import { bits, BitStructure, Blob, Layout, seq, struct, u32, u8, UInt, union } from 'buffer-layout'; import { bits, BitStructure, Blob, Layout, seq, struct, u32, u8, u16, UInt, union } from 'buffer-layout';
import { PublicKey } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js';
import BN from 'bn.js'; import BN from 'bn.js';
@ -134,6 +134,7 @@ export const MangoGroupLayout = struct([
seq(U64F64(), NUM_TOKENS, 'totalBorrows'), seq(U64F64(), NUM_TOKENS, 'totalBorrows'),
U64F64('maintCollRatio'), U64F64('maintCollRatio'),
U64F64('initCollRatio'), U64F64('initCollRatio'),
publicKeyLayout('srmVault'),
seq(u8(), NUM_TOKENS, 'mintDecimals'), seq(u8(), NUM_TOKENS, 'mintDecimals'),
seq(u8(), NUM_MARKETS, 'oracleDecimals'), seq(u8(), NUM_MARKETS, 'oracleDecimals'),
seq(u8(), MANGO_GROUP_PADDING, 'padding') seq(u8(), MANGO_GROUP_PADDING, 'padding')
@ -147,8 +148,8 @@ export const MarginAccountLayout = struct([
seq(U64F64(), NUM_TOKENS, 'deposits'), seq(U64F64(), NUM_TOKENS, 'deposits'),
seq(U64F64(), NUM_TOKENS, 'borrows'), seq(U64F64(), NUM_TOKENS, 'borrows'),
// seq(u64(), NUM_TOKENS, 'positions'), seq(publicKeyLayout(), NUM_MARKETS, 'openOrders'),
seq(publicKeyLayout(), NUM_MARKETS, 'openOrders') u64('srmBalance')
]); ]);
@ -186,7 +187,7 @@ export function orderTypeLayout(property) {
} }
export function selfTradeBehaviorLayout(property) { export function selfTradeBehaviorLayout(property) {
return new EnumLayout({ decrementTake: 0, cancelProvide: 1 }, 4, property); return new EnumLayout({ decrementTake: 0, cancelProvide: 1, abortTransaction: 2 }, 4, property);
} }
export const MangoInstructionLayout = union(u32('instruction')) export const MangoInstructionLayout = union(u32('instruction'))
@ -194,39 +195,41 @@ export const MangoInstructionLayout = union(u32('instruction'))
MangoInstructionLayout.addVariant(0, struct([]), 'InitMangoGroup') MangoInstructionLayout.addVariant(0, struct([]), 'InitMangoGroup')
MangoInstructionLayout.addVariant(1, struct([]), 'InitMarginAccount') MangoInstructionLayout.addVariant(1, struct([]), 'InitMarginAccount')
MangoInstructionLayout.addVariant(2, struct([u64('quantity')]), 'Deposit') MangoInstructionLayout.addVariant(2, struct([u64('quantity')]), 'Deposit')
MangoInstructionLayout.addVariant(3, struct([u64('tokenIndex'), u64('quantity')]), 'Withdraw') MangoInstructionLayout.addVariant(3, struct([u64('quantity')]), 'Withdraw')
MangoInstructionLayout.addVariant(4, struct([u64('tokenIndex'), u64('quantity')]), 'Borrow') MangoInstructionLayout.addVariant(4, struct([u64('tokenIndex'), u64('quantity')]), 'Borrow')
MangoInstructionLayout.addVariant(5, struct([u64('tokenIndex'), u64('quantity')]), 'SettleBorrow') MangoInstructionLayout.addVariant(5, struct([u64('tokenIndex'), u64('quantity')]), 'SettleBorrow')
MangoInstructionLayout.addVariant(6, struct([seq(u64(), NUM_TOKENS, 'depositQuantities')]), 'Liquidate') MangoInstructionLayout.addVariant(6, struct([seq(u64(), NUM_TOKENS, 'depositQuantities')]), 'Liquidate')
MangoInstructionLayout.addVariant(7, struct([u64('quantity')]), 'DepositSrm')
MangoInstructionLayout.addVariant(8, struct([u64('quantity')]), 'WithdrawSrm')
MangoInstructionLayout.addVariant(7, MangoInstructionLayout.addVariant(9,
struct( struct(
[ [
sideLayout('side'), sideLayout('side'),
u64('limitPrice'), u64('limitPrice'),
u64('maxQuantity'), u64('maxBaseQuantity'),
u64('maxQuoteQuantity'),
selfTradeBehaviorLayout('selfTradeBehavior'),
orderTypeLayout('orderType'), orderTypeLayout('orderType'),
u64('clientId'), u64('clientId'),
selfTradeBehaviorLayout('selfTradeBehavior') u16('limit'),
] ]
), ),
'PlaceOrder' 'PlaceOrder'
) )
MangoInstructionLayout.addVariant(8, struct([]), 'SettleFunds') MangoInstructionLayout.addVariant(10, struct([]), 'SettleFunds')
MangoInstructionLayout.addVariant(9, MangoInstructionLayout.addVariant(11,
struct( struct(
[ [
sideLayout('side'), sideLayout('side'),
u128('orderId'), u128('orderId')
publicKeyLayout('openOrders'),
u8('openOrdersSlot')
] ]
), ),
'CancelOrder' 'CancelOrder'
) )
MangoInstructionLayout.addVariant(10, struct([u64('clientId')]), 'CancelOrderByClientId') MangoInstructionLayout.addVariant(12, struct([u64('clientId')]), 'CancelOrderByClientId')
// @ts-ignore // @ts-ignore
const instructionMaxSpan = Math.max(...Object.values(MangoInstructionLayout.registry).map((r) => r.span)); const instructionMaxSpan = Math.max(...Object.values(MangoInstructionLayout.registry).map((r) => r.span));

View File

754
yarn.lock

File diff suppressed because it is too large Load Diff