Merge branch 'dev' of github.com:blockworks-foundation/mango-v4 into dev
This commit is contained in:
commit
2b694e553f
|
@ -59,8 +59,8 @@ jobs:
|
|||
tags: |
|
||||
us-docker.pkg.dev/${{ env.PROJECT_ID }}/gcr.io/${{ env.IMAGE }}:${{ github.sha }}
|
||||
us-docker.pkg.dev/${{ env.PROJECT_ID }}/gcr.io/${{ env.IMAGE }}:latest
|
||||
cache-from: type=registry,ref=us-docker.pkg.dev/${{ env.PROJECT_ID }}/gcr.io/${{ env.IMAGE }}:buildcache
|
||||
cache-to: type=registry,ref=us-docker.pkg.dev/${{ env.PROJECT_ID }}/gcr.io/${{ env.IMAGE }}:buildcache,mode=max
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
# Build and push the liquidator runtime image
|
||||
- name: Build and Push Liquidator
|
||||
uses: docker/build-push-action@v2
|
||||
|
|
|
@ -18,9 +18,4 @@ programs/mango-v4/src/lib-expanded.rs
|
|||
|
||||
keeper/.env
|
||||
|
||||
ts/client/**/*.js
|
||||
ts/client/**/*.js.map
|
||||
migrations/*.js
|
||||
migrations/*.js.map
|
||||
|
||||
ts/client/src/scripts/archive/ts.ts
|
|
@ -6,6 +6,8 @@ Update this for each mainnet deployment.
|
|||
|
||||
## mainnet
|
||||
|
||||
|
||||
|
||||
Oct 8, 2022 at 14:38:31 Central European Summer Time
|
||||
https://explorer.solana.com/tx/3m8EDohkgwJZyiwpGXztBWARWQVxyhnSNDVuH467D7FPS2wxJerr79HhdhDEed5hpConHgGsKHvxtW1HJP6GixX9
|
||||
|
||||
|
|
|
@ -3174,9 +3174,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lz4-sys"
|
||||
version = "1.9.3"
|
||||
version = "1.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7be8908e2ed6f31c02db8a9fa962f03e36c53fbfde437363eae3306b85d7e17"
|
||||
checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
|
28
Dockerfile
28
Dockerfile
|
@ -1,23 +1,29 @@
|
|||
# syntax = docker/dockerfile:1.2
|
||||
# Base image containing all binaries, deployed to gcr.io/mango-markets/mango-v4:latest
|
||||
FROM rust:1.60 as build
|
||||
FROM rust:1.60 as base
|
||||
RUN cargo install cargo-chef --locked
|
||||
RUN rustup component add rustfmt
|
||||
RUN apt-get update && apt-get -y install clang cmake
|
||||
|
||||
WORKDIR /app
|
||||
COPY ./ .
|
||||
|
||||
FROM base as plan
|
||||
COPY . .
|
||||
# Hack to prevent a ghost member lib/init
|
||||
RUN sed -i 's|lib/\*|lib/checked_math|' Cargo.toml
|
||||
# Hack to prevent local serum_dex manifests conflicting with cargo dependency
|
||||
RUN rm -rf anchor/tests
|
||||
RUN cargo chef prepare --bin keeper --recipe-path recipe-keeper.json
|
||||
RUN cargo chef prepare --bin liquidator --recipe-path recipe-liquidator.json
|
||||
|
||||
# Mount cache for downloaded and compiled dependencies
|
||||
RUN --mount=type=cache,mode=0777,target=/usr/local/cargo,from=rust,source=/usr/local/cargo \
|
||||
--mount=type=cache,mode=0777,target=target \
|
||||
cargo build --release --bins
|
||||
|
||||
# Copy bins out of cache
|
||||
RUN --mount=type=cache,mode=0777,target=target mkdir .bin && cp target/release/keeper target/release/liquidator .bin/
|
||||
FROM base as build
|
||||
COPY --from=plan /app/recipe-*.json .
|
||||
RUN cargo chef cook --release --recipe-path recipe-keeper.json --bin keeper
|
||||
RUN cargo chef cook --release --recipe-path recipe-liquidator.json --bin liquidator
|
||||
COPY . .
|
||||
|
||||
FROM debian:bullseye-slim as run
|
||||
RUN apt-get update && apt-get -y install ca-certificates libc6
|
||||
COPY --from=build /app/.bin/* /usr/local/bin/
|
||||
COPY --from=build /app/target/release/keeper /usr/local/bin/
|
||||
COPY --from=build /app/target/release/liquidator /usr/local/bin/
|
||||
RUN adduser --system --group --no-create-home mangouser
|
||||
USER mangouser
|
||||
|
|
|
@ -42,9 +42,8 @@ async function createMints(
|
|||
program: anchor.Program<MangoV4>,
|
||||
payer: anchor.web3.Keypair,
|
||||
admin,
|
||||
) {
|
||||
let mintsMap: Partial<Record<keyof typeof MINTS, spl.Token>>;
|
||||
let mints: spl.Token[] = [];
|
||||
): Promise<Partial<Record<keyof typeof MINTS, spl.Token>>> {
|
||||
const mints: spl.Token[] = [];
|
||||
for (let i = 0; i < 2; i++) {
|
||||
mints.push(
|
||||
await spl.Token.createMint(
|
||||
|
@ -57,7 +56,7 @@ async function createMints(
|
|||
),
|
||||
);
|
||||
}
|
||||
mintsMap = {
|
||||
const mintsMap = {
|
||||
USDC: mints[0],
|
||||
BTC: mints[1],
|
||||
};
|
||||
|
@ -72,20 +71,20 @@ async function createUsers(
|
|||
group: Group,
|
||||
connection: Connection,
|
||||
programId: PublicKey,
|
||||
) {
|
||||
let users: TestUser[] = [];
|
||||
): Promise<TestUser[]> {
|
||||
const users: TestUser[] = [];
|
||||
for (let i = 0; i < NUM_USERS; i++) {
|
||||
let user = anchor.web3.Keypair.generate();
|
||||
const user = anchor.web3.Keypair.generate();
|
||||
|
||||
await provider.connection.requestAirdrop(
|
||||
user.publicKey,
|
||||
LAMPORTS_PER_SOL * 1000,
|
||||
);
|
||||
|
||||
let tokenAccounts: spl.AccountInfo[] = [];
|
||||
for (let mintKey in mintsMap) {
|
||||
let mint: spl.Token = mintsMap[mintKey];
|
||||
let tokenAccount = await mint.getOrCreateAssociatedAccountInfo(
|
||||
const tokenAccounts: spl.AccountInfo[] = [];
|
||||
for (const mintKey in mintsMap) {
|
||||
const mint: spl.Token = mintsMap[mintKey];
|
||||
const tokenAccount = await mint.getOrCreateAssociatedAccountInfo(
|
||||
user.publicKey,
|
||||
);
|
||||
await mint.mintTo(tokenAccount.address, payer, [], 1_000_000_000_000_000);
|
||||
|
@ -100,14 +99,10 @@ async function createUsers(
|
|||
),
|
||||
'devnet',
|
||||
programId,
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{ idsSource: 'get-program-accounts' },
|
||||
);
|
||||
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(
|
||||
group,
|
||||
user.publicKey,
|
||||
);
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(group);
|
||||
await mangoAccount!.reload(client);
|
||||
|
||||
console.log('created user ' + i);
|
||||
|
@ -123,12 +118,12 @@ async function createUsers(
|
|||
}
|
||||
|
||||
describe('mango-v4', () => {
|
||||
let programId = new PublicKey(PROGRAM_ID);
|
||||
const programId = new PublicKey(PROGRAM_ID);
|
||||
// Configure the client to use the local cluster.
|
||||
const envProvider = anchor.AnchorProvider.env();
|
||||
anchor.setProvider(envProvider);
|
||||
let envProviderWallet = envProvider.wallet;
|
||||
let envProviderPayer = (envProviderWallet as NodeWallet).payer;
|
||||
const envProviderWallet = envProvider.wallet;
|
||||
const envProviderPayer = (envProviderWallet as NodeWallet).payer;
|
||||
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
|
@ -158,13 +153,9 @@ describe('mango-v4', () => {
|
|||
|
||||
// Passing devnet as the cluster here - client cannot accept localnet
|
||||
// I think this is only for getting the serum market though?
|
||||
envClient = await MangoClient.connect(
|
||||
envProvider,
|
||||
'devnet',
|
||||
programId,
|
||||
{},
|
||||
'get-program-accounts',
|
||||
);
|
||||
envClient = await MangoClient.connect(envProvider, 'devnet', programId, {
|
||||
idsSource: 'get-program-accounts',
|
||||
});
|
||||
await envClient.groupCreate(groupNum, true, 1, insuranceMintPk);
|
||||
group = await envClient.getGroupForCreator(adminPk, groupNum);
|
||||
|
||||
|
@ -358,14 +349,10 @@ describe('mango-v4', () => {
|
|||
),
|
||||
'devnet',
|
||||
programId,
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{ idsSource: 'get-program-accounts' },
|
||||
);
|
||||
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(
|
||||
group,
|
||||
users[0].keypair.publicKey,
|
||||
);
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(group);
|
||||
await mangoAccount!.reload(client);
|
||||
|
||||
await client.tokenDeposit(
|
||||
|
@ -384,7 +371,7 @@ describe('mango-v4', () => {
|
|||
);
|
||||
await mangoAccount!.reload(client);
|
||||
|
||||
let banks = await envClient.getBanksForGroup(group);
|
||||
const banks = await envClient.getBanksForGroup(group);
|
||||
assert.equal(
|
||||
mangoAccount!.getTokenBalanceUi(banks[0]),
|
||||
100.5,
|
||||
|
@ -524,21 +511,19 @@ describe('mango-v4', () => {
|
|||
assert.equal(makerOrders.length, 1, 'Maker still has one open order');
|
||||
assert.equal(makerOrders[0].uiSize, 1.0, 'Size reduced');
|
||||
|
||||
let makerPerps = makerAccount.perpActive();
|
||||
const makerPerps = makerAccount.perpActive();
|
||||
assert.equal(makerPerps.length, 1, 'Maker has perp position');
|
||||
assert.equal(makerPerps[0].marketIndex, 0, 'Market index matches');
|
||||
assert.equal(
|
||||
makerPerps[0].basePositionLots,
|
||||
10000,
|
||||
assert.isTrue(
|
||||
makerPerps[0].basePositionLots.eq(new anchor.BN(10000)),
|
||||
'base position correct',
|
||||
);
|
||||
|
||||
let takerPerps = takerAccount.perpActive();
|
||||
const takerPerps = takerAccount.perpActive();
|
||||
assert.equal(takerPerps.length, 1, 'Taker has perp position');
|
||||
assert.equal(takerPerps[0].marketIndex, 0, 'Market index matches');
|
||||
assert.equal(
|
||||
takerPerps[0].basePositionLots,
|
||||
-10000,
|
||||
assert.isTrue(
|
||||
takerPerps[0].basePositionLots.eq(new anchor.BN(-10000)),
|
||||
'base position correct',
|
||||
);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ pub fn new(
|
|||
n_banks: active_token_len,
|
||||
n_perps: active_perp_len,
|
||||
begin_perp: active_token_len * 2,
|
||||
begin_serum3: active_token_len * 2 + active_perp_len,
|
||||
begin_serum3: active_token_len * 2 + active_perp_len * 2,
|
||||
};
|
||||
mango_v4::state::new_health_cache(&account.borrow(), &retriever).context("make health cache")
|
||||
}
|
||||
|
|
|
@ -197,7 +197,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
|
||||
let mut metric_websocket_queue_len = metrics.register_u64("websocket_queue_length".into());
|
||||
let mut metric_snapshot_queue_len = metrics.register_u64("snapshot_queue_length".into());
|
||||
let mut metric_mango_accounts = metrics.register_u64("mango_accouns".into());
|
||||
let mut metric_mango_accounts = metrics.register_u64("mango_accounts".into());
|
||||
|
||||
//
|
||||
// mango client setup
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@blockworks-foundation/mango-v4",
|
||||
"version": "0.0.1-beta.3",
|
||||
"version": "0.0.1-beta.5",
|
||||
"description": "Typescript Client for mango-v4 program.",
|
||||
"repository": "https://github.com/blockworks-foundation/mango-v4",
|
||||
"author": {
|
||||
|
@ -45,6 +45,7 @@
|
|||
"cross-fetch": "^3.1.5",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"ftx-api": "^1.1.13",
|
||||
"mocha": "^9.1.3",
|
||||
"prettier": "^2.0.5",
|
||||
"ts-mocha": "^10.0.0",
|
||||
|
|
|
@ -83,7 +83,7 @@ pub struct MangoAccount {
|
|||
pub perp_spot_transfers: i64,
|
||||
|
||||
/// Init health as calculated during HealthReginBegin, rounded up.
|
||||
pub health_region_pre_init_health: i64,
|
||||
pub health_region_begin_init_health: i64,
|
||||
|
||||
pub reserved: [u8; 240],
|
||||
|
||||
|
@ -118,8 +118,7 @@ impl MangoAccount {
|
|||
bump: 0,
|
||||
padding: Default::default(),
|
||||
net_deposits: 0,
|
||||
perp_spot_transfers: 0,
|
||||
health_region_pre_init_health: 0,
|
||||
health_region_begin_init_health: 0,
|
||||
reserved: [0; 240],
|
||||
header_version: DEFAULT_MANGO_ACCOUNT_VERSION,
|
||||
padding3: Default::default(),
|
||||
|
@ -131,6 +130,7 @@ impl MangoAccount {
|
|||
perps: vec![PerpPosition::default(); 4],
|
||||
padding7: Default::default(),
|
||||
perp_open_orders: vec![PerpOpenOrder::default(); 6],
|
||||
perp_spot_transfers: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1095,8 +1095,7 @@ mod tests {
|
|||
account.in_health_region = 3;
|
||||
account.bump = 4;
|
||||
account.net_deposits = 5;
|
||||
account.perp_spot_transfers = 6;
|
||||
account.health_region_pre_init_health = 7;
|
||||
account.health_region_begin_init_health = 7;
|
||||
account.tokens.resize(8, TokenPosition::default());
|
||||
account.tokens[0].token_index = 8;
|
||||
account.serum3.resize(8, Serum3Orders::default());
|
||||
|
@ -1125,7 +1124,7 @@ mod tests {
|
|||
account2.fixed.perp_spot_transfers
|
||||
);
|
||||
assert_eq!(
|
||||
account.health_region_pre_init_health,
|
||||
account.health_region_begin_init_health,
|
||||
account2.fixed.health_region_begin_init_health
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
|
@ -95,17 +95,7 @@ export class Group {
|
|||
public vaultAmountsMap: Map<string, BN>,
|
||||
) {}
|
||||
|
||||
public async reloadAll(client: MangoClient): Promise<void> {
|
||||
let ids: Id | undefined = undefined;
|
||||
|
||||
if (client.idsSource === 'api') {
|
||||
ids = await Id.fromApi(this.publicKey);
|
||||
} else if (client.idsSource === 'static') {
|
||||
ids = Id.fromIdsByPk(this.publicKey);
|
||||
} else {
|
||||
ids = undefined;
|
||||
}
|
||||
|
||||
public async reloadAll(client: MangoClient, ids?: Id): Promise<void> {
|
||||
// console.time('group.reload');
|
||||
await Promise.all([
|
||||
this.reloadAlts(client),
|
||||
|
|
|
@ -85,10 +85,17 @@ describe('Health Cache', () => {
|
|||
I80F48.fromNumber(-310),
|
||||
new BN(7),
|
||||
new BN(11),
|
||||
I80F48.fromNumber(0),
|
||||
I80F48.fromNumber(0),
|
||||
new BN(1),
|
||||
new BN(2),
|
||||
I80F48.fromNumber(0),
|
||||
I80F48.fromNumber(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
0,
|
||||
0,
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
);
|
||||
const pi1 = PerpInfo.fromPerpPosition(pM, pp);
|
||||
|
||||
|
@ -186,10 +193,17 @@ describe('Health Cache', () => {
|
|||
I80F48.fromNumber(fixture.perp1[1]),
|
||||
new BN(fixture.perp1[2]),
|
||||
new BN(fixture.perp1[3]),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
I80F48.fromNumber(0),
|
||||
I80F48.fromNumber(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
0,
|
||||
0,
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
);
|
||||
const pi1 = PerpInfo.fromPerpPosition(pM, pp);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { BN } from '@project-serum/anchor';
|
||||
import { AnchorProvider, BN } from '@project-serum/anchor';
|
||||
import { utf8 } from '@project-serum/anchor/dist/cjs/utils/bytes';
|
||||
import { OpenOrders, Order, Orderbook } from '@project-serum/serum/lib/market';
|
||||
import { AccountInfo, PublicKey } from '@solana/web3.js';
|
||||
import { AccountInfo, PublicKey, TransactionSignature } from '@solana/web3.js';
|
||||
import { MangoClient } from '../client';
|
||||
import { SERUM3_PROGRAM_ID } from '../constants';
|
||||
import { I80F48, I80F48Dto, ONE_I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
|
@ -12,12 +12,11 @@ import { HealthCache } from './healthCache';
|
|||
import { PerpMarket, PerpMarketIndex, PerpOrder, PerpOrderSide } from './perp';
|
||||
import { MarketIndex, Serum3Side } from './serum3';
|
||||
export class MangoAccount {
|
||||
public name: string;
|
||||
public tokens: TokenPosition[];
|
||||
public serum3: Serum3Orders[];
|
||||
public perps: PerpPosition[];
|
||||
public perpOpenOrders: PerpOo[];
|
||||
public name: string;
|
||||
public netDeposits: BN;
|
||||
|
||||
static from(
|
||||
publicKey: PublicKey,
|
||||
|
@ -26,29 +25,31 @@ export class MangoAccount {
|
|||
owner: PublicKey;
|
||||
name: number[];
|
||||
delegate: PublicKey;
|
||||
beingLiquidated: number;
|
||||
accountNum: number;
|
||||
bump: number;
|
||||
beingLiquidated: number;
|
||||
inHealthRegion: number;
|
||||
netDeposits: BN;
|
||||
netSettled: BN;
|
||||
perpSpotTransfers: BN;
|
||||
healthRegionBeginInitHealth: BN;
|
||||
headerVersion: number;
|
||||
tokens: unknown;
|
||||
serum3: unknown;
|
||||
perps: unknown;
|
||||
perpOpenOrders: unknown;
|
||||
},
|
||||
) {
|
||||
): MangoAccount {
|
||||
return new MangoAccount(
|
||||
publicKey,
|
||||
obj.group,
|
||||
obj.owner,
|
||||
obj.name,
|
||||
obj.delegate,
|
||||
obj.beingLiquidated,
|
||||
obj.accountNum,
|
||||
obj.bump,
|
||||
obj.beingLiquidated == 1,
|
||||
obj.inHealthRegion == 1,
|
||||
obj.netDeposits,
|
||||
obj.netSettled,
|
||||
obj.perpSpotTransfers,
|
||||
obj.healthRegionBeginInitHealth,
|
||||
obj.headerVersion,
|
||||
obj.tokens as TokenPositionDto[],
|
||||
obj.serum3 as Serum3PositionDto[],
|
||||
|
@ -64,12 +65,13 @@ export class MangoAccount {
|
|||
public owner: PublicKey,
|
||||
name: number[],
|
||||
public delegate: PublicKey,
|
||||
beingLiquidated: number,
|
||||
public accountNum: number,
|
||||
bump: number,
|
||||
netDeposits: BN,
|
||||
netSettled: BN,
|
||||
headerVersion: number,
|
||||
public beingLiquidated: boolean,
|
||||
public inHealthRegion: boolean,
|
||||
public netDeposits: BN,
|
||||
public perpSpotTransfers: BN,
|
||||
public healthRegionBeginInitHealth: BN,
|
||||
public headerVersion: number,
|
||||
tokens: TokenPositionDto[],
|
||||
serum3: Serum3PositionDto[],
|
||||
perps: PerpPositionDto[],
|
||||
|
@ -81,17 +83,16 @@ export class MangoAccount {
|
|||
this.serum3 = serum3.map((dto) => Serum3Orders.from(dto));
|
||||
this.perps = perps.map((dto) => PerpPosition.from(dto));
|
||||
this.perpOpenOrders = perpOpenOrders.map((dto) => PerpOo.from(dto));
|
||||
this.netDeposits = netDeposits;
|
||||
}
|
||||
|
||||
async reload(client: MangoClient): Promise<MangoAccount> {
|
||||
public async reload(client: MangoClient): Promise<MangoAccount> {
|
||||
const mangoAccount = await client.getMangoAccount(this);
|
||||
await mangoAccount.reloadAccountData(client);
|
||||
Object.assign(this, mangoAccount);
|
||||
return mangoAccount;
|
||||
}
|
||||
|
||||
async reloadWithSlot(
|
||||
public async reloadWithSlot(
|
||||
client: MangoClient,
|
||||
): Promise<{ value: MangoAccount; slot: number }> {
|
||||
const resp = await client.getMangoAccountWithSlot(this.publicKey);
|
||||
|
@ -127,46 +128,59 @@ export class MangoAccount {
|
|||
return this;
|
||||
}
|
||||
|
||||
tokensActive(): TokenPosition[] {
|
||||
public isDelegate(client: MangoClient): boolean {
|
||||
return this.delegate.equals(
|
||||
(client.program.provider as AnchorProvider).wallet.publicKey,
|
||||
);
|
||||
}
|
||||
|
||||
public tokensActive(): TokenPosition[] {
|
||||
return this.tokens.filter((token) => token.isActive());
|
||||
}
|
||||
|
||||
serum3Active(): Serum3Orders[] {
|
||||
public serum3Active(): Serum3Orders[] {
|
||||
return this.serum3.filter((serum3) => serum3.isActive());
|
||||
}
|
||||
|
||||
perpActive(): PerpPosition[] {
|
||||
public perpActive(): PerpPosition[] {
|
||||
return this.perps.filter((perp) => perp.isActive());
|
||||
}
|
||||
|
||||
perpOrdersActive(): PerpOo[] {
|
||||
public perpOrdersActive(): PerpOo[] {
|
||||
return this.perpOpenOrders.filter(
|
||||
(oo) => oo.orderMarket !== PerpOo.OrderMarketUnset,
|
||||
);
|
||||
}
|
||||
|
||||
getToken(tokenIndex: TokenIndex): TokenPosition | undefined {
|
||||
public getToken(tokenIndex: TokenIndex): TokenPosition | undefined {
|
||||
return this.tokens.find((ta) => ta.tokenIndex == tokenIndex);
|
||||
}
|
||||
|
||||
getSerum3Account(marketIndex: MarketIndex): Serum3Orders | undefined {
|
||||
public getSerum3Account(marketIndex: MarketIndex): Serum3Orders | undefined {
|
||||
return this.serum3.find((sa) => sa.marketIndex == marketIndex);
|
||||
}
|
||||
|
||||
getPerpPosition(perpMarketIndex: PerpMarketIndex): PerpPosition | undefined {
|
||||
public getPerpPosition(
|
||||
perpMarketIndex: PerpMarketIndex,
|
||||
useEventQueue?: boolean,
|
||||
): PerpPosition | undefined {
|
||||
return this.perps.find((pp) => pp.marketIndex == perpMarketIndex);
|
||||
}
|
||||
|
||||
getPerpPositionUi(group: Group, perpMarketIndex: PerpMarketIndex): number {
|
||||
public getPerpPositionUi(
|
||||
group: Group,
|
||||
perpMarketIndex: PerpMarketIndex,
|
||||
useEventQueue?: boolean,
|
||||
): number {
|
||||
const pp = this.perps.find((pp) => pp.marketIndex == perpMarketIndex);
|
||||
if (!pp) {
|
||||
throw new Error(`No position found for PerpMarket ${perpMarketIndex}!`);
|
||||
}
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
return pp.getBasePositionUi(perpMarket);
|
||||
return pp.getBasePositionUi(perpMarket, useEventQueue);
|
||||
}
|
||||
|
||||
getSerum3OoAccount(marketIndex: MarketIndex): OpenOrders {
|
||||
public getSerum3OoAccount(marketIndex: MarketIndex): OpenOrders {
|
||||
const oo: OpenOrders | undefined =
|
||||
this.serum3OosMapByMarketIndex.get(marketIndex);
|
||||
|
||||
|
@ -189,7 +203,7 @@ export class MangoAccount {
|
|||
* @param bank
|
||||
* @returns native balance for a token, is signed
|
||||
*/
|
||||
getTokenBalance(bank: Bank): I80F48 {
|
||||
public getTokenBalance(bank: Bank): I80F48 {
|
||||
const tp = this.getToken(bank.tokenIndex);
|
||||
return tp ? tp.balance(bank) : ZERO_I80F48();
|
||||
}
|
||||
|
@ -199,7 +213,7 @@ export class MangoAccount {
|
|||
* @param bank
|
||||
* @returns native deposits for a token, 0 if position has borrows
|
||||
*/
|
||||
getTokenDeposits(bank: Bank): I80F48 {
|
||||
public getTokenDeposits(bank: Bank): I80F48 {
|
||||
const tp = this.getToken(bank.tokenIndex);
|
||||
return tp ? tp.deposits(bank) : ZERO_I80F48();
|
||||
}
|
||||
|
@ -209,7 +223,7 @@ export class MangoAccount {
|
|||
* @param bank
|
||||
* @returns native borrows for a token, 0 if position has deposits
|
||||
*/
|
||||
getTokenBorrows(bank: Bank): I80F48 {
|
||||
public getTokenBorrows(bank: Bank): I80F48 {
|
||||
const tp = this.getToken(bank.tokenIndex);
|
||||
return tp ? tp.borrows(bank) : ZERO_I80F48();
|
||||
}
|
||||
|
@ -219,7 +233,7 @@ export class MangoAccount {
|
|||
* @param bank
|
||||
* @returns UI balance for a token, is signed
|
||||
*/
|
||||
getTokenBalanceUi(bank: Bank): number {
|
||||
public getTokenBalanceUi(bank: Bank): number {
|
||||
const tp = this.getToken(bank.tokenIndex);
|
||||
return tp ? tp.balanceUi(bank) : 0;
|
||||
}
|
||||
|
@ -229,7 +243,7 @@ export class MangoAccount {
|
|||
* @param bank
|
||||
* @returns UI deposits for a token, 0 or more
|
||||
*/
|
||||
getTokenDepositsUi(bank: Bank): number {
|
||||
public getTokenDepositsUi(bank: Bank): number {
|
||||
const ta = this.getToken(bank.tokenIndex);
|
||||
return ta ? ta.depositsUi(bank) : 0;
|
||||
}
|
||||
|
@ -239,7 +253,7 @@ export class MangoAccount {
|
|||
* @param bank
|
||||
* @returns UI borrows for a token, 0 or less
|
||||
*/
|
||||
getTokenBorrowsUi(bank: Bank): number {
|
||||
public getTokenBorrowsUi(bank: Bank): number {
|
||||
const ta = this.getToken(bank.tokenIndex);
|
||||
return ta ? ta.borrowsUi(bank) : 0;
|
||||
}
|
||||
|
@ -249,7 +263,7 @@ export class MangoAccount {
|
|||
* @param healthType
|
||||
* @returns raw health number, in native quote
|
||||
*/
|
||||
getHealth(group: Group, healthType: HealthType): I80F48 {
|
||||
public getHealth(group: Group, healthType: HealthType): I80F48 {
|
||||
const hc = HealthCache.fromMangoAccount(group, this);
|
||||
return hc.health(healthType);
|
||||
}
|
||||
|
@ -260,7 +274,7 @@ export class MangoAccount {
|
|||
* @param healthType
|
||||
* @returns health ratio, in percentage form
|
||||
*/
|
||||
getHealthRatio(group: Group, healthType: HealthType): I80F48 {
|
||||
public getHealthRatio(group: Group, healthType: HealthType): I80F48 {
|
||||
const hc = HealthCache.fromMangoAccount(group, this);
|
||||
return hc.healthRatio(healthType);
|
||||
}
|
||||
|
@ -270,7 +284,7 @@ export class MangoAccount {
|
|||
* @param healthType
|
||||
* @returns health ratio, in percentage form, capped to 100
|
||||
*/
|
||||
getHealthRatioUi(group: Group, healthType: HealthType): number {
|
||||
public getHealthRatioUi(group: Group, healthType: HealthType): number {
|
||||
const ratio = this.getHealthRatio(group, healthType).toNumber();
|
||||
return ratio > 100 ? 100 : Math.trunc(ratio);
|
||||
}
|
||||
|
@ -279,7 +293,7 @@ export class MangoAccount {
|
|||
* Sum of all the assets i.e. token deposits, borrows, total assets in spot open orders, and perps positions.
|
||||
* @returns equity, in native quote
|
||||
*/
|
||||
getEquity(group: Group): I80F48 {
|
||||
public getEquity(group: Group): I80F48 {
|
||||
const tokensMap = new Map<number, I80F48>();
|
||||
for (const tp of this.tokensActive()) {
|
||||
const bank = group.getFirstBankByTokenIndex(tp.tokenIndex);
|
||||
|
@ -322,7 +336,7 @@ export class MangoAccount {
|
|||
* The amount of native quote you could withdraw against your existing assets.
|
||||
* @returns collateral value, in native quote
|
||||
*/
|
||||
getCollateralValue(group: Group): I80F48 {
|
||||
public getCollateralValue(group: Group): I80F48 {
|
||||
return this.getHealth(group, HealthType.init);
|
||||
}
|
||||
|
||||
|
@ -330,7 +344,7 @@ export class MangoAccount {
|
|||
* Sum of all positive assets.
|
||||
* @returns assets, in native quote
|
||||
*/
|
||||
getAssetsValue(group: Group, healthType: HealthType): I80F48 {
|
||||
public getAssetsValue(group: Group, healthType: HealthType): I80F48 {
|
||||
const hc = HealthCache.fromMangoAccount(group, this);
|
||||
return hc.assets(healthType);
|
||||
}
|
||||
|
@ -339,7 +353,7 @@ export class MangoAccount {
|
|||
* Sum of all negative assets.
|
||||
* @returns liabs, in native quote
|
||||
*/
|
||||
getLiabsValue(group: Group, healthType: HealthType): I80F48 {
|
||||
public getLiabsValue(group: Group, healthType: HealthType): I80F48 {
|
||||
const hc = HealthCache.fromMangoAccount(group, this);
|
||||
return hc.liabs(healthType);
|
||||
}
|
||||
|
@ -349,7 +363,7 @@ export class MangoAccount {
|
|||
* PNL is defined here as spot value + serum3 open orders value + perp value - net deposits value (evaluated at native quote price at the time of the deposit/withdraw)
|
||||
* spot value + serum3 open orders value + perp value is returned by getEquity (open orders values are added to spot token values implicitly)
|
||||
*/
|
||||
getPnl(group: Group): I80F48 {
|
||||
public getPnl(group: Group): I80F48 {
|
||||
return this.getEquity(group)?.add(
|
||||
I80F48.fromI64(this.netDeposits).mul(I80F48.fromNumber(-1)),
|
||||
);
|
||||
|
@ -359,7 +373,10 @@ export class MangoAccount {
|
|||
* The amount of given native token you can withdraw including borrows, considering all existing assets as collateral.
|
||||
* @returns amount of given native token you can borrow, considering all existing assets as collateral, in native token
|
||||
*/
|
||||
getMaxWithdrawWithBorrowForToken(group: Group, mintPk: PublicKey): I80F48 {
|
||||
public getMaxWithdrawWithBorrowForToken(
|
||||
group: Group,
|
||||
mintPk: PublicKey,
|
||||
): I80F48 {
|
||||
const tokenBank: Bank = group.getFirstBankByMint(mintPk);
|
||||
const initHealth = this.getHealth(group, HealthType.init);
|
||||
|
||||
|
@ -403,7 +420,10 @@ export class MangoAccount {
|
|||
return maxBorrowNativeWithoutFees.add(existingTokenDeposits);
|
||||
}
|
||||
|
||||
getMaxWithdrawWithBorrowForTokenUi(group: Group, mintPk: PublicKey): number {
|
||||
public getMaxWithdrawWithBorrowForTokenUi(
|
||||
group: Group,
|
||||
mintPk: PublicKey,
|
||||
): number {
|
||||
const maxWithdrawWithBorrow = this.getMaxWithdrawWithBorrowForToken(
|
||||
group,
|
||||
mintPk,
|
||||
|
@ -453,7 +473,7 @@ export class MangoAccount {
|
|||
* Note: health ratio is technically ∞ if liabs are 0
|
||||
* @returns health ratio, in percentage form
|
||||
*/
|
||||
simHealthRatioWithTokenPositionUiChanges(
|
||||
public simHealthRatioWithTokenPositionUiChanges(
|
||||
group: Group,
|
||||
uiTokenChanges: {
|
||||
uiTokenAmount: number;
|
||||
|
@ -485,7 +505,7 @@ export class MangoAccount {
|
|||
): Promise<OpenOrders[]> {
|
||||
const response =
|
||||
await client.program.provider.connection.getMultipleAccountsInfo(
|
||||
this.serum3.map((s) => s.openOrders),
|
||||
this.serum3Active().map((s) => s.openOrders),
|
||||
);
|
||||
const accounts = response.filter((a): a is AccountInfo<Buffer> =>
|
||||
Boolean(a),
|
||||
|
@ -689,6 +709,42 @@ export class MangoAccount {
|
|||
.toNumber();
|
||||
}
|
||||
|
||||
// TODO: don't send a settle instruction if there's nothing to settle
|
||||
public async serum3SettleFundsForAllMarkets(
|
||||
client: MangoClient,
|
||||
group: Group,
|
||||
): Promise<TransactionSignature[]> {
|
||||
// Future: collect ixs, batch them, and send them in fewer txs
|
||||
return await Promise.all(
|
||||
this.serum3Active().map((s) => {
|
||||
const serum3Market = group.getSerum3MarketByMarketIndex(s.marketIndex);
|
||||
return client.serum3SettleFunds(
|
||||
group,
|
||||
this,
|
||||
serum3Market.serumMarketExternal,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: cancel until all are cancelled
|
||||
public async serum3CancelAllOrdersForAllMarkets(
|
||||
client: MangoClient,
|
||||
group: Group,
|
||||
): Promise<TransactionSignature[]> {
|
||||
// Future: collect ixs, batch them, and send them in in fewer txs
|
||||
return await Promise.all(
|
||||
this.serum3Active().map((s) => {
|
||||
const serum3Market = group.getSerum3MarketByMarketIndex(s.marketIndex);
|
||||
return client.serum3CancelAllOrders(
|
||||
group,
|
||||
this,
|
||||
serum3Market.serumMarketExternal,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param group
|
||||
|
@ -1000,10 +1056,17 @@ export class PerpPosition {
|
|||
I80F48.from(dto.quotePositionNative),
|
||||
dto.bidsBaseLots,
|
||||
dto.asksBaseLots,
|
||||
dto.takerBaseLots,
|
||||
dto.takerQuoteLots,
|
||||
I80F48.from(dto.longSettledFunding),
|
||||
I80F48.from(dto.shortSettledFunding),
|
||||
dto.takerBaseLots,
|
||||
dto.takerQuoteLots,
|
||||
dto.takerBaseLots,
|
||||
dto.takerQuoteLots,
|
||||
dto.cumulativeLongFunding,
|
||||
dto.cumulativeShortFunding,
|
||||
dto.makerVolume,
|
||||
dto.takerVolume,
|
||||
dto.perpSpotTransfers,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1016,10 +1079,17 @@ export class PerpPosition {
|
|||
ZERO_I80F48(),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
ZERO_I80F48(),
|
||||
ZERO_I80F48(),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
0,
|
||||
0,
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
new BN(0),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1027,20 +1097,34 @@ export class PerpPosition {
|
|||
public marketIndex: PerpMarketIndex,
|
||||
public basePositionLots: BN,
|
||||
public quotePositionNative: I80F48,
|
||||
public quoteEntryNative: BN,
|
||||
public quoteRunningNative: BN,
|
||||
public longSettledFunding: I80F48,
|
||||
public shortSettledFunding: I80F48,
|
||||
public bidsBaseLots: BN,
|
||||
public asksBaseLots: BN,
|
||||
public takerBaseLots: BN,
|
||||
public takerQuoteLots: BN,
|
||||
public longSettledFunding: I80F48,
|
||||
public shortSettledFunding: I80F48,
|
||||
public cumulativeLongFunding: number,
|
||||
public cumulativeShortFunding: number,
|
||||
public makerVolume: BN,
|
||||
public takerVolume: BN,
|
||||
public perpSpotTransfers: BN,
|
||||
) {}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.marketIndex != PerpPosition.PerpMarketIndexUnset;
|
||||
}
|
||||
|
||||
public getBasePositionUi(perpMarket: PerpMarket): number {
|
||||
return perpMarket.baseLotsToUi(this.basePositionLots);
|
||||
public getBasePositionUi(
|
||||
perpMarket: PerpMarket,
|
||||
useEventQueue?: boolean,
|
||||
): number {
|
||||
return perpMarket.baseLotsToUi(
|
||||
useEventQueue
|
||||
? this.basePositionLots.add(this.takerBaseLots)
|
||||
: this.basePositionLots,
|
||||
);
|
||||
}
|
||||
|
||||
public getUnsettledFunding(perpMarket: PerpMarket): I80F48 {
|
||||
|
@ -1090,15 +1174,21 @@ export class PerpPosition {
|
|||
export class PerpPositionDto {
|
||||
constructor(
|
||||
public marketIndex: number,
|
||||
public reserved: [],
|
||||
public basePositionLots: BN,
|
||||
public quotePositionNative: { val: BN },
|
||||
public quoteEntryNative: BN,
|
||||
public quoteRunningNative: BN,
|
||||
public longSettledFunding: I80F48Dto,
|
||||
public shortSettledFunding: I80F48Dto,
|
||||
public bidsBaseLots: BN,
|
||||
public asksBaseLots: BN,
|
||||
public takerBaseLots: BN,
|
||||
public takerQuoteLots: BN,
|
||||
public longSettledFunding: I80F48Dto,
|
||||
public shortSettledFunding: I80F48Dto,
|
||||
public cumulativeLongFunding: number,
|
||||
public cumulativeShortFunding: number,
|
||||
public makerVolume: BN,
|
||||
public takerVolume: BN,
|
||||
public perpSpotTransfers: BN,
|
||||
) {}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import Big from 'big.js';
|
|||
import { MangoClient } from '../client';
|
||||
import { I80F48, I80F48Dto } from '../numbers/I80F48';
|
||||
import { As, toNative, U64_MAX_BN } from '../utils';
|
||||
import { OracleConfig, QUOTE_DECIMALS } from './bank';
|
||||
import { OracleConfig, QUOTE_DECIMALS, TokenIndex } from './bank';
|
||||
|
||||
export type PerpMarketIndex = number & As<'perp-market-index'>;
|
||||
|
||||
|
@ -22,22 +22,23 @@ export class PerpMarket {
|
|||
public maxFunding: I80F48;
|
||||
public longFunding: I80F48;
|
||||
public shortFunding: I80F48;
|
||||
public openInterest: BN;
|
||||
public seqNum: BN;
|
||||
public feesAccrued: I80F48;
|
||||
public feesSettled: I80F48;
|
||||
priceLotsToUiConverter: number;
|
||||
baseLotsToUiConverter: number;
|
||||
quoteLotsToUiConverter: number;
|
||||
public _price: I80F48;
|
||||
public _uiPrice: number;
|
||||
|
||||
private priceLotsToUiConverter: number;
|
||||
private baseLotsToUiConverter: number;
|
||||
private quoteLotsToUiConverter: number;
|
||||
|
||||
static from(
|
||||
publicKey: PublicKey,
|
||||
obj: {
|
||||
group: PublicKey;
|
||||
settleTokenIndex: number;
|
||||
perpMarketIndex: number;
|
||||
trustedMarket: number;
|
||||
groupInsuranceFund: number;
|
||||
name: number[];
|
||||
oracle: PublicKey;
|
||||
oracleConfig: OracleConfig;
|
||||
|
@ -62,17 +63,22 @@ export class PerpMarket {
|
|||
openInterest: BN;
|
||||
seqNum: BN;
|
||||
feesAccrued: I80F48Dto;
|
||||
feesSettled: I80F48Dto;
|
||||
bump: number;
|
||||
baseDecimals: number;
|
||||
registrationTime: BN;
|
||||
feesSettled: I80F48Dto;
|
||||
feePenalty: number;
|
||||
settleFeeFlat: number;
|
||||
settleFeeAmountThreshold: number;
|
||||
settleFeeFractionLowHealth: number;
|
||||
},
|
||||
): PerpMarket {
|
||||
return new PerpMarket(
|
||||
publicKey,
|
||||
obj.group,
|
||||
obj.settleTokenIndex as TokenIndex,
|
||||
obj.perpMarketIndex as PerpMarketIndex,
|
||||
obj.trustedMarket == 1,
|
||||
obj.groupInsuranceFund == 1,
|
||||
obj.name,
|
||||
obj.oracle,
|
||||
obj.oracleConfig,
|
||||
|
@ -97,18 +103,23 @@ export class PerpMarket {
|
|||
obj.openInterest,
|
||||
obj.seqNum,
|
||||
obj.feesAccrued,
|
||||
obj.feesSettled,
|
||||
obj.bump,
|
||||
obj.baseDecimals,
|
||||
obj.registrationTime,
|
||||
obj.feesSettled,
|
||||
obj.feePenalty,
|
||||
obj.settleFeeFlat,
|
||||
obj.settleFeeAmountThreshold,
|
||||
obj.settleFeeFractionLowHealth,
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
public publicKey: PublicKey,
|
||||
public group: PublicKey,
|
||||
public settleTokenIndex: TokenIndex,
|
||||
public perpMarketIndex: PerpMarketIndex, // TODO rename to marketIndex?
|
||||
public trustedMarket: boolean,
|
||||
public groupInsuranceFund: boolean,
|
||||
name: number[],
|
||||
public oracle: PublicKey,
|
||||
oracleConfig: OracleConfig,
|
||||
|
@ -129,14 +140,17 @@ export class PerpMarket {
|
|||
public impactQuantity: BN,
|
||||
longFunding: I80F48Dto,
|
||||
shortFunding: I80F48Dto,
|
||||
fundingLastUpdated: BN,
|
||||
openInterest: BN,
|
||||
seqNum: BN,
|
||||
public fundingLastUpdated: BN,
|
||||
public openInterest: BN,
|
||||
public seqNum: BN,
|
||||
feesAccrued: I80F48Dto,
|
||||
feesSettled: I80F48Dto,
|
||||
bump: number,
|
||||
public baseDecimals: number,
|
||||
public registrationTime: BN,
|
||||
feesSettled: I80F48Dto,
|
||||
public feePenalty: number,
|
||||
public settleFeeFlat: number,
|
||||
public settleFeeAmountThreshold: number,
|
||||
public settleFeeFractionLowHealth: number,
|
||||
) {
|
||||
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
|
||||
this.maintAssetWeight = I80F48.from(maintAssetWeight);
|
||||
|
@ -150,8 +164,6 @@ export class PerpMarket {
|
|||
this.maxFunding = I80F48.from(maxFunding);
|
||||
this.longFunding = I80F48.from(longFunding);
|
||||
this.shortFunding = I80F48.from(shortFunding);
|
||||
this.openInterest = openInterest;
|
||||
this.seqNum = seqNum;
|
||||
this.feesAccrued = I80F48.from(feesAccrued);
|
||||
this.feesSettled = I80F48.from(feesSettled);
|
||||
|
||||
|
@ -187,6 +199,15 @@ export class PerpMarket {
|
|||
}
|
||||
return this._uiPrice;
|
||||
}
|
||||
|
||||
get minOrderSize(): number {
|
||||
return this.baseLotsToUiConverter;
|
||||
}
|
||||
|
||||
get tickSize(): number {
|
||||
return this.priceLotsToUiConverter;
|
||||
}
|
||||
|
||||
public async loadAsks(client: MangoClient): Promise<BookSide> {
|
||||
const asks = await client.program.account.bookSide.fetch(this.asks);
|
||||
return BookSide.from(client, this, BookSideType.asks, asks);
|
||||
|
@ -551,6 +572,7 @@ export class PerpOrder {
|
|||
side,
|
||||
leafNode.timestamp,
|
||||
expiryTimestamp,
|
||||
perpMarket.perpMarketIndex,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -567,7 +589,16 @@ export class PerpOrder {
|
|||
public side: PerpOrderSide,
|
||||
public timestamp: BN,
|
||||
public expiryTimestamp: BN,
|
||||
public perpMarketIndex: number,
|
||||
) {}
|
||||
|
||||
get price(): number {
|
||||
return this.uiPrice;
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.uiSize;
|
||||
}
|
||||
}
|
||||
|
||||
export class PerpEventQueue {
|
||||
|
|
|
@ -4,10 +4,10 @@ import { Cluster, PublicKey } from '@solana/web3.js';
|
|||
import BN from 'bn.js';
|
||||
import { MangoClient } from '../client';
|
||||
import { SERUM3_PROGRAM_ID } from '../constants';
|
||||
import { MAX_I80F48, ONE_I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
import { As } from '../utils';
|
||||
import { TokenIndex } from './bank';
|
||||
import { Group } from './group';
|
||||
import { MAX_I80F48, ONE_I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
|
||||
export type MarketIndex = number & As<'market-index'>;
|
||||
|
||||
|
@ -23,7 +23,6 @@ export class Serum3Market {
|
|||
serumProgram: PublicKey;
|
||||
serumMarketExternal: PublicKey;
|
||||
marketIndex: number;
|
||||
bump: number;
|
||||
registrationTime: BN;
|
||||
},
|
||||
): Serum3Market {
|
||||
|
|
|
@ -68,8 +68,15 @@ enum AccountRetriever {
|
|||
|
||||
export type IdsSource = 'api' | 'static' | 'get-program-accounts';
|
||||
|
||||
export type MangoClientOptions = {
|
||||
idsSource?: IdsSource;
|
||||
postSendTxCallback?: ({ txid }: { txid: string }) => void;
|
||||
prioritizationFee?: number;
|
||||
};
|
||||
|
||||
// TODO: replace ui values with native as input wherever possible
|
||||
export class MangoClient {
|
||||
private idsSource: IdsSource;
|
||||
private postSendTxCallback?: ({ txid }) => void;
|
||||
private prioritizationFee: number;
|
||||
|
||||
|
@ -77,12 +84,9 @@ export class MangoClient {
|
|||
public program: Program<MangoV4>,
|
||||
public programId: PublicKey,
|
||||
public cluster: Cluster,
|
||||
public opts: {
|
||||
postSendTxCallback?: ({ txid }: { txid: string }) => void;
|
||||
prioritizationFee?: number;
|
||||
} = {},
|
||||
public idsSource: IdsSource = 'api',
|
||||
public opts: MangoClientOptions = {},
|
||||
) {
|
||||
this.idsSource = opts?.idsSource || 'api';
|
||||
this.prioritizationFee = opts?.prioritizationFee || 0;
|
||||
this.postSendTxCallback = opts?.postSendTxCallback;
|
||||
// TODO: evil side effect, but limited backtraces are a nightmare
|
||||
|
@ -148,7 +152,8 @@ export class MangoClient {
|
|||
public async getGroup(groupPk: PublicKey): Promise<Group> {
|
||||
const groupAccount = await this.program.account.group.fetch(groupPk);
|
||||
const group = Group.from(groupPk, groupAccount);
|
||||
await group.reloadAll(this);
|
||||
const ids: Id | undefined = await this.getIds(groupPk);
|
||||
await group.reloadAll(this, ids);
|
||||
return group;
|
||||
}
|
||||
|
||||
|
@ -196,6 +201,17 @@ export class MangoClient {
|
|||
return groups[0];
|
||||
}
|
||||
|
||||
public async getIds(groupPk: PublicKey): Promise<Id | undefined> {
|
||||
switch (this.idsSource) {
|
||||
case 'api':
|
||||
return await Id.fromApi(groupPk);
|
||||
case 'get-program-accounts':
|
||||
return undefined;
|
||||
case 'static':
|
||||
return Id.fromIdsByPk(groupPk);
|
||||
}
|
||||
}
|
||||
|
||||
// Tokens/Banks
|
||||
|
||||
public async tokenRegister(
|
||||
|
@ -1211,7 +1227,7 @@ export class MangoClient {
|
|||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
externalMarketPk: PublicKey,
|
||||
limit: number,
|
||||
limit?: number,
|
||||
): Promise<TransactionSignature> {
|
||||
const serum3Market = group.serum3MarketsMapByExternal.get(
|
||||
externalMarketPk.toBase58(),
|
||||
|
@ -1222,7 +1238,7 @@ export class MangoClient {
|
|||
)!;
|
||||
|
||||
const ix = await this.program.methods
|
||||
.serum3CancelAllOrders(limit)
|
||||
.serum3CancelAllOrders(limit ? limit : 10)
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
account: mangoAccount.publicKey,
|
||||
|
@ -1683,6 +1699,49 @@ export class MangoClient {
|
|||
.instruction();
|
||||
}
|
||||
|
||||
public async perpCancelOrderIx(
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
perpMarketIndex: PerpMarketIndex,
|
||||
orderId: BN,
|
||||
): Promise<TransactionInstruction> {
|
||||
const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex);
|
||||
return await this.program.methods
|
||||
.perpCancelOrder(new BN(orderId))
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
account: mangoAccount.publicKey,
|
||||
owner: (this.program.provider as AnchorProvider).wallet.publicKey,
|
||||
perpMarket: perpMarket.publicKey,
|
||||
asks: perpMarket.asks,
|
||||
bids: perpMarket.bids,
|
||||
})
|
||||
.instruction();
|
||||
}
|
||||
|
||||
public async perpCancelOrder(
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
perpMarketIndex: PerpMarketIndex,
|
||||
orderId: BN,
|
||||
): Promise<TransactionSignature> {
|
||||
return await sendTransaction(
|
||||
this.program.provider as AnchorProvider,
|
||||
[
|
||||
await this.perpCancelOrderIx(
|
||||
group,
|
||||
mangoAccount,
|
||||
perpMarketIndex,
|
||||
orderId,
|
||||
),
|
||||
],
|
||||
group.addressLookupTablesList,
|
||||
{
|
||||
postSendTxCallback: this.postSendTxCallback,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
public async perpCancelAllOrders(
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
|
@ -2169,14 +2228,76 @@ export class MangoClient {
|
|||
.rpc();
|
||||
}
|
||||
|
||||
public async healthRegionBeginIx(
|
||||
group: Group,
|
||||
account: MangoAccount,
|
||||
banks: Bank[] = [],
|
||||
perpMarkets: PerpMarket[] = [],
|
||||
): Promise<TransactionInstruction> {
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
this.buildHealthRemainingAccounts(
|
||||
AccountRetriever.Fixed,
|
||||
group,
|
||||
[account],
|
||||
[...banks],
|
||||
[...perpMarkets],
|
||||
);
|
||||
const parsedHealthAccounts = healthRemainingAccounts.map(
|
||||
(pk) =>
|
||||
({
|
||||
pubkey: pk,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
} as AccountMeta),
|
||||
);
|
||||
|
||||
return await this.program.methods
|
||||
.healthRegionBegin()
|
||||
.accounts({
|
||||
account: account.publicKey,
|
||||
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
||||
})
|
||||
.remainingAccounts(parsedHealthAccounts)
|
||||
.instruction();
|
||||
}
|
||||
|
||||
public async healthRegionEndIx(
|
||||
group: Group,
|
||||
account: MangoAccount,
|
||||
banks: Bank[] = [],
|
||||
perpMarkets: PerpMarket[] = [],
|
||||
): Promise<TransactionInstruction> {
|
||||
const healthRemainingAccounts: PublicKey[] =
|
||||
this.buildHealthRemainingAccounts(
|
||||
AccountRetriever.Fixed,
|
||||
group,
|
||||
[account],
|
||||
[...banks],
|
||||
[...perpMarkets],
|
||||
);
|
||||
const parsedHealthAccounts = healthRemainingAccounts.map(
|
||||
(pk) =>
|
||||
({
|
||||
pubkey: pk,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
} as AccountMeta),
|
||||
);
|
||||
|
||||
return await this.program.methods
|
||||
.healthRegionEnd()
|
||||
.accounts({ account: account.publicKey })
|
||||
.remainingAccounts(parsedHealthAccounts)
|
||||
.instruction();
|
||||
}
|
||||
|
||||
/// static
|
||||
|
||||
static connect(
|
||||
provider: Provider,
|
||||
cluster: Cluster,
|
||||
programId: PublicKey,
|
||||
opts: any = {},
|
||||
getIdsFromApi: IdsSource = 'api',
|
||||
opts?: MangoClientOptions,
|
||||
): MangoClient {
|
||||
const idl = IDL;
|
||||
|
||||
|
@ -2185,7 +2306,6 @@ export class MangoClient {
|
|||
programId,
|
||||
cluster,
|
||||
opts,
|
||||
getIdsFromApi,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
|||
import { coder } from '@project-serum/anchor/dist/cjs/spl/token';
|
||||
import { Cluster, Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { I80F48, ZERO_I80F48 } from '../accounts/I80F48';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
import { I80F48, ZERO_I80F48 } from '../numbers/I80F48';
|
||||
import { toUiDecimals } from '../utils';
|
||||
|
||||
const CLUSTER_URL =
|
||||
|
@ -15,7 +15,7 @@ const GROUP_NUM = Number(process.env.GROUP_NUM || 2);
|
|||
const CLUSTER: Cluster =
|
||||
(process.env.CLUSTER_OVERRIDE as Cluster) || 'mainnet-beta';
|
||||
|
||||
async function main() {
|
||||
async function main(): Promise<void> {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(CLUSTER_URL!, options);
|
||||
|
||||
|
@ -29,8 +29,7 @@ async function main() {
|
|||
adminProvider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{ idsSource: 'get-program-accounts' },
|
||||
);
|
||||
|
||||
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
||||
|
|
|
@ -24,7 +24,7 @@ async function debugUser(
|
|||
client: MangoClient,
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
) {
|
||||
): Promise<void> {
|
||||
console.log(mangoAccount.toString(group));
|
||||
|
||||
await mangoAccount.reload(client);
|
||||
|
@ -80,7 +80,9 @@ async function debugUser(
|
|||
),
|
||||
);
|
||||
|
||||
async function getMaxWithdrawWithBorrowForTokenUiWrapper(token) {
|
||||
async function getMaxWithdrawWithBorrowForTokenUiWrapper(
|
||||
token,
|
||||
): Promise<void> {
|
||||
console.log(
|
||||
`mangoAccount.getMaxWithdrawWithBorrowForTokenUi(group, ${token}) ` +
|
||||
mangoAccount.getMaxWithdrawWithBorrowForTokenUi(
|
||||
|
@ -93,7 +95,7 @@ async function debugUser(
|
|||
await getMaxWithdrawWithBorrowForTokenUiWrapper(srcToken);
|
||||
}
|
||||
|
||||
function simHealthRatioWithTokenPositionChangesWrapper(debug, change) {
|
||||
function simHealthRatioWithTokenPositionChangesWrapper(debug, change): void {
|
||||
console.log(
|
||||
`mangoAccount.simHealthRatioWithTokenPositionChanges ${debug}` +
|
||||
mangoAccount.simHealthRatioWithTokenPositionUiChanges(group, [change]),
|
||||
|
@ -110,7 +112,7 @@ async function debugUser(
|
|||
});
|
||||
}
|
||||
|
||||
function getMaxSourceForTokenSwapWrapper(src, tgt) {
|
||||
function getMaxSourceForTokenSwapWrapper(src, tgt): void {
|
||||
console.log(
|
||||
`getMaxSourceForTokenSwap ${src.padEnd(4)} ${tgt.padEnd(4)} ` +
|
||||
mangoAccount.getMaxSourceUiForTokenSwap(
|
||||
|
@ -129,22 +131,14 @@ async function debugUser(
|
|||
}
|
||||
}
|
||||
|
||||
function getMaxForPerpWrapper(perpMarket: PerpMarket) {
|
||||
function getMaxForPerpWrapper(perpMarket: PerpMarket): void {
|
||||
console.log(
|
||||
`getMaxQuoteForPerpBidUi ${perpMarket.perpMarketIndex} ` +
|
||||
mangoAccount.getMaxQuoteForPerpBidUi(
|
||||
group,
|
||||
perpMarket.perpMarketIndex,
|
||||
perpMarket.price.toNumber(),
|
||||
),
|
||||
mangoAccount.getMaxQuoteForPerpBidUi(group, perpMarket.perpMarketIndex),
|
||||
);
|
||||
console.log(
|
||||
`getMaxBaseForPerpAskUi ${perpMarket.perpMarketIndex} ` +
|
||||
mangoAccount.getMaxBaseForPerpAskUi(
|
||||
group,
|
||||
perpMarket.perpMarketIndex,
|
||||
perpMarket.price.toNumber(),
|
||||
),
|
||||
mangoAccount.getMaxBaseForPerpAskUi(group, perpMarket.perpMarketIndex),
|
||||
);
|
||||
}
|
||||
for (const perpMarket of Array.from(
|
||||
|
@ -153,7 +147,7 @@ async function debugUser(
|
|||
getMaxForPerpWrapper(perpMarket);
|
||||
}
|
||||
|
||||
function getMaxForSerum3Wrapper(serum3Market: Serum3Market) {
|
||||
function getMaxForSerum3Wrapper(serum3Market: Serum3Market): void {
|
||||
// if (serum3Market.name !== 'SOL/USDC') return;
|
||||
console.log(
|
||||
`getMaxQuoteForSerum3BidUi ${serum3Market.name} ` +
|
||||
|
@ -177,7 +171,7 @@ async function debugUser(
|
|||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
async function main(): Promise<void> {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(CLUSTER_URL!, options);
|
||||
|
||||
|
@ -192,8 +186,9 @@ async function main() {
|
|||
adminProvider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
||||
|
|
|
@ -50,8 +50,9 @@ async function createGroup() {
|
|||
adminProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
console.log(`Creating Group...`);
|
||||
|
@ -83,8 +84,9 @@ async function registerTokens() {
|
|||
adminProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
{},
|
||||
'get-program-accounts' /* idsjson service doesn't know about this group yet */,
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
} /* idsjson service doesn't know about this group yet */,
|
||||
);
|
||||
|
||||
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
||||
|
|
|
@ -3626,11 +3626,11 @@ export type MangoV4 = {
|
|||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "netSettled",
|
||||
"name": "perpSpotTransfers",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "healthRegionPreInitHealth",
|
||||
"name": "healthRegionBeginInitHealth",
|
||||
"docs": [
|
||||
"Init health as calculated during HealthReginBegin, rounded up."
|
||||
],
|
||||
|
@ -4618,7 +4618,7 @@ export type MangoV4 = {
|
|||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
16
|
||||
8
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -4630,11 +4630,11 @@ export type MangoV4 = {
|
|||
},
|
||||
{
|
||||
"name": "cumulativeDepositInterest",
|
||||
"type": "f32"
|
||||
"type": "f64"
|
||||
},
|
||||
{
|
||||
"name": "cumulativeBorrowInterest",
|
||||
"type": "f32"
|
||||
"type": "f64"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -4792,9 +4792,29 @@ export type MangoV4 = {
|
|||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
64
|
||||
24
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "cumulativeLongFunding",
|
||||
"type": "f64"
|
||||
},
|
||||
{
|
||||
"name": "cumulativeShortFunding",
|
||||
"type": "f64"
|
||||
},
|
||||
{
|
||||
"name": "makerVolume",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "takerVolume",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "perpSpotTransfers",
|
||||
"type": "i64"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -5561,7 +5581,7 @@ export type MangoV4 = {
|
|||
},
|
||||
{
|
||||
"name": "marketIndex",
|
||||
"type": "u64",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
|
@ -5833,7 +5853,7 @@ export type MangoV4 = {
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "UpdateFundingLog",
|
||||
"name": "PerpUpdateFundingLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
|
@ -5859,6 +5879,16 @@ export type MangoV4 = {
|
|||
"name": "price",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "feesAccrued",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "openInterest",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -5904,6 +5934,16 @@ export type MangoV4 = {
|
|||
"name": "loanFeeRate",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "totalBorrows",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "totalDeposits",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -5938,7 +5978,7 @@ export type MangoV4 = {
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "LiquidateTokenAndTokenLog",
|
||||
"name": "TokenLiqWithTokenLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
|
@ -6075,7 +6115,7 @@ export type MangoV4 = {
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "LiquidateTokenBankruptcyLog",
|
||||
"name": "TokenLiqBankruptcyLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
|
@ -6144,12 +6184,312 @@ export type MangoV4 = {
|
|||
},
|
||||
{
|
||||
"name": "cumulativeDepositInterest",
|
||||
"type": "f32",
|
||||
"type": "f64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "cumulativeBorrowInterest",
|
||||
"type": "f32",
|
||||
"type": "f64",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DeactivatePerpPositionLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mangoAccount",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "marketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "cumulativeLongFunding",
|
||||
"type": "f64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "cumulativeShortFunding",
|
||||
"type": "f64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "makerVolume",
|
||||
"type": "u64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "takerVolume",
|
||||
"type": "u64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpSpotTransfers",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TokenMetaDataLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "tokenIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mintDecimals",
|
||||
"type": "u8",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "oracle",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mintInfo",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpMarketMetaDataLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarket",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "baseDecimals",
|
||||
"type": "u8",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "baseLotSize",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "quoteLotSize",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "oracle",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Serum3RegisterMarketLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "serumMarket",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "marketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "baseTokenIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "quoteTokenIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "serumProgram",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "serumProgramExternal",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpLiqBasePositionLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "liqor",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "liqee",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "baseTransfer",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "quoteTransfer",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "price",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpLiqBankruptcyLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "liqee",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "liqor",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "insuranceTransfer",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "socializedLoss",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpSettlePnlLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mangoAccountA",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mangoAccountB",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "settlement",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "settler",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "fee",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpSettleFeesLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mangoAccount",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "settlement",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
|
@ -9902,11 +10242,11 @@ export const IDL: MangoV4 = {
|
|||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "netSettled",
|
||||
"name": "perpSpotTransfers",
|
||||
"type": "i64"
|
||||
},
|
||||
{
|
||||
"name": "healthRegionPreInitHealth",
|
||||
"name": "healthRegionBeginInitHealth",
|
||||
"docs": [
|
||||
"Init health as calculated during HealthReginBegin, rounded up."
|
||||
],
|
||||
|
@ -10894,7 +11234,7 @@ export const IDL: MangoV4 = {
|
|||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
16
|
||||
8
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -10906,11 +11246,11 @@ export const IDL: MangoV4 = {
|
|||
},
|
||||
{
|
||||
"name": "cumulativeDepositInterest",
|
||||
"type": "f32"
|
||||
"type": "f64"
|
||||
},
|
||||
{
|
||||
"name": "cumulativeBorrowInterest",
|
||||
"type": "f32"
|
||||
"type": "f64"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -11068,9 +11408,29 @@ export const IDL: MangoV4 = {
|
|||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
64
|
||||
24
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "cumulativeLongFunding",
|
||||
"type": "f64"
|
||||
},
|
||||
{
|
||||
"name": "cumulativeShortFunding",
|
||||
"type": "f64"
|
||||
},
|
||||
{
|
||||
"name": "makerVolume",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "takerVolume",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "perpSpotTransfers",
|
||||
"type": "i64"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -11837,7 +12197,7 @@ export const IDL: MangoV4 = {
|
|||
},
|
||||
{
|
||||
"name": "marketIndex",
|
||||
"type": "u64",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
|
@ -12109,7 +12469,7 @@ export const IDL: MangoV4 = {
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "UpdateFundingLog",
|
||||
"name": "PerpUpdateFundingLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
|
@ -12135,6 +12495,16 @@ export const IDL: MangoV4 = {
|
|||
"name": "price",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "feesAccrued",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "openInterest",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -12180,6 +12550,16 @@ export const IDL: MangoV4 = {
|
|||
"name": "loanFeeRate",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "totalBorrows",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "totalDeposits",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -12214,7 +12594,7 @@ export const IDL: MangoV4 = {
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "LiquidateTokenAndTokenLog",
|
||||
"name": "TokenLiqWithTokenLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
|
@ -12351,7 +12731,7 @@ export const IDL: MangoV4 = {
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "LiquidateTokenBankruptcyLog",
|
||||
"name": "TokenLiqBankruptcyLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
|
@ -12420,12 +12800,312 @@ export const IDL: MangoV4 = {
|
|||
},
|
||||
{
|
||||
"name": "cumulativeDepositInterest",
|
||||
"type": "f32",
|
||||
"type": "f64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "cumulativeBorrowInterest",
|
||||
"type": "f32",
|
||||
"type": "f64",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "DeactivatePerpPositionLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mangoAccount",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "marketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "cumulativeLongFunding",
|
||||
"type": "f64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "cumulativeShortFunding",
|
||||
"type": "f64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "makerVolume",
|
||||
"type": "u64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "takerVolume",
|
||||
"type": "u64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpSpotTransfers",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "TokenMetaDataLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "tokenIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mintDecimals",
|
||||
"type": "u8",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "oracle",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mintInfo",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpMarketMetaDataLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarket",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "baseDecimals",
|
||||
"type": "u8",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "baseLotSize",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "quoteLotSize",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "oracle",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Serum3RegisterMarketLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "serumMarket",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "marketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "baseTokenIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "quoteTokenIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "serumProgram",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "serumProgramExternal",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpLiqBasePositionLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "liqor",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "liqee",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "baseTransfer",
|
||||
"type": "i64",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "quoteTransfer",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "price",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpLiqBankruptcyLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "liqee",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "liqor",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "insuranceTransfer",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "socializedLoss",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpSettlePnlLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mangoAccountA",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mangoAccountB",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "settlement",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "settler",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "fee",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PerpSettleFeesLog",
|
||||
"fields": [
|
||||
{
|
||||
"name": "mangoGroup",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "mangoAccount",
|
||||
"type": "publicKey",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
"type": "u16",
|
||||
"index": false
|
||||
},
|
||||
{
|
||||
"name": "settlement",
|
||||
"type": "i128",
|
||||
"index": false
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import idsJson from '../../../ids.json';
|
||||
import { MangoClient } from '../../client';
|
||||
import { MANGO_V4_ID, SERUM3_PROGRAM_ID } from '../../constants';
|
||||
import { Id } from '../../ids';
|
||||
|
||||
//
|
||||
// script to add a group to ids json
|
||||
//
|
||||
|
||||
function replacer(key, value) {
|
||||
if (value instanceof Map) {
|
||||
return Object.fromEntries(value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const groupName = 'devnet.microwavedcola';
|
||||
const cluster = 'devnet';
|
||||
|
||||
// build client and fetch group for admin
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
cluster,
|
||||
MANGO_V4_ID[cluster],
|
||||
);
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 0);
|
||||
|
||||
// collect mappings &
|
||||
// collect pubkeys
|
||||
const banks = await client.getBanksForGroup(group);
|
||||
const banksMapByMint = new Map(
|
||||
banks.map((tuple) => [tuple.mint.toBase58(), tuple]),
|
||||
);
|
||||
const stubOracles = await client.getStubOracle(group);
|
||||
const mintInfos = await client.getMintInfosForGroup(group);
|
||||
const serum3Markets = await client.serum3GetMarkets(group);
|
||||
const perpMarkets = await client.perpGetMarkets(group);
|
||||
|
||||
// build ids
|
||||
const toDump = new Id(
|
||||
cluster,
|
||||
groupName,
|
||||
group.publicKey.toBase58(),
|
||||
SERUM3_PROGRAM_ID[cluster].toBase58(),
|
||||
MANGO_V4_ID[cluster].toBase58(),
|
||||
banks.map((tuple) => ({
|
||||
name: tuple.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
})),
|
||||
stubOracles.map((tuple) => ({
|
||||
name: banksMapByMint.get(tuple.mint.toBase58())!.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
})),
|
||||
mintInfos.map((tuple) => ({
|
||||
name: banksMapByMint.get(tuple.mint.toBase58())!.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
})),
|
||||
serum3Markets.map((tuple) => ({
|
||||
name: tuple.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
marketExternal: tuple.serumMarketExternal.toBase58(),
|
||||
})),
|
||||
perpMarkets.map((tuple) => ({
|
||||
name: tuple.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
})),
|
||||
);
|
||||
|
||||
// adds ids for group in existing ids.json
|
||||
const existingGroup = idsJson.groups.find((group) => group.name == groupName);
|
||||
if (existingGroup) {
|
||||
console.log('Updating existing group with latest state...');
|
||||
} else {
|
||||
console.log('Group does not exist yet...');
|
||||
}
|
||||
idsJson.groups = idsJson.groups.filter((group) => group.name !== groupName);
|
||||
idsJson.groups.push(toDump);
|
||||
|
||||
// dump
|
||||
const file = `${process.cwd()}/ts/client/ids.json`;
|
||||
await fs.writeFileSync(file, JSON.stringify(idsJson, replacer, 2));
|
||||
|
||||
process.exit();
|
||||
}
|
||||
main();
|
|
@ -1,122 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { MangoClient } from '../../client';
|
||||
import { MANGO_V4_ID } from '../../constants';
|
||||
|
||||
const GROUP_NUM = Number(process.env.GROUP_NUM || 0);
|
||||
|
||||
const DEVNET_MINTS = new Map([
|
||||
['USDC', '8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN'], // use devnet usdc
|
||||
['BTC', '3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU'],
|
||||
['SOL', 'So11111111111111111111111111111111111111112'],
|
||||
['ORCA', 'orcarKHSqC5CDDsGbho8GKvwExejWHxTqGzXgcewB9L'],
|
||||
['MNGO', 'Bb9bsTQa1bGEtQ5KagGkvSHyuLqDWumFUcRqFusFNJWC'],
|
||||
]);
|
||||
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
// user1
|
||||
const user1 = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.PAYER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const user1Wallet = new Wallet(user1);
|
||||
const user1Provider = new AnchorProvider(connection, user1Wallet, options);
|
||||
const user1Client = await MangoClient.connect(
|
||||
user1Provider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
console.log(`user1 ${user1Wallet.publicKey.toBase58()}`);
|
||||
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await user1Client.getGroupForAdmin(admin.publicKey, GROUP_NUM);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
|
||||
const user1MangoAccount = await user1Client.getOrCreateMangoAccount(group);
|
||||
|
||||
console.log(`...mangoAccount1 ${user1MangoAccount.publicKey}`);
|
||||
|
||||
/// user1 deposits some btc, so user2 can borrow it
|
||||
let amount = 0.001;
|
||||
let token = 'BTC';
|
||||
console.log(`Depositing...${amount} 'BTC'`);
|
||||
await user1Client.tokenDeposit(group, user1MangoAccount, token, amount);
|
||||
await user1MangoAccount.reload(user1Client, group);
|
||||
console.log(`${user1MangoAccount.toString(group)}`);
|
||||
|
||||
// user 2
|
||||
const user2 = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const user2Wallet = new Wallet(user2);
|
||||
const user2Provider = new AnchorProvider(connection, user2Wallet, options);
|
||||
const user2Client = await MangoClient.connect(
|
||||
user2Provider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
console.log(`user2 ${user2Wallet.publicKey.toBase58()}`);
|
||||
|
||||
const user2MangoAccount = await user2Client.getOrCreateMangoAccount(group);
|
||||
console.log(`...mangoAccount2 ${user2MangoAccount.publicKey}`);
|
||||
|
||||
/// Increase usdc price temporarily to allow lots of borrows
|
||||
console.log(
|
||||
`Setting USDC price to 1.5, to allow the user to borrow lots of btc`,
|
||||
);
|
||||
const adminWallet = new Wallet(admin);
|
||||
console.log(`Admin ${adminWallet.publicKey.toBase58()}`);
|
||||
const adminProvider = new AnchorProvider(connection, adminWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
adminProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
await client.stubOracleSet(group, group.banksMap.get('USDC')?.oracle!, 1.5);
|
||||
|
||||
/// user2 deposits some collateral and borrows BTC
|
||||
amount = 1;
|
||||
console.log(`Depositing...${amount} 'USDC'`);
|
||||
await user2Client.tokenDeposit(group, user2MangoAccount, 'USDC', amount);
|
||||
await user2MangoAccount.reload(user2Client, group);
|
||||
console.log(`${user2MangoAccount.toString(group)}`);
|
||||
|
||||
const maxNative = await (
|
||||
await user2MangoAccount.getMaxWithdrawWithBorrowForToken(group, token)
|
||||
).toNumber();
|
||||
amount = 0.9 * maxNative;
|
||||
console.log(`Withdrawing...${amount} native BTC'`);
|
||||
await user2Client.tokenWithdrawNative(
|
||||
group,
|
||||
user2MangoAccount,
|
||||
token,
|
||||
amount,
|
||||
true,
|
||||
);
|
||||
await user2MangoAccount.reload(user2Client, group);
|
||||
console.log(`${user2MangoAccount.toString(group)}`);
|
||||
|
||||
/// Reduce usdc price to normal again
|
||||
console.log(
|
||||
`Setting USDC price back to 1.0, decreasing the user's collateral size`,
|
||||
);
|
||||
await client.stubOracleSet(group, group.banksMap.get('USDC')?.oracle!, 1.0);
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,84 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { MangoClient } from '../../client';
|
||||
import { MANGO_V4_ID } from '../../constants';
|
||||
|
||||
//
|
||||
// (unfinished?) script which shows how to use the flash loan 1 ix
|
||||
//
|
||||
|
||||
const DEVNET_MINTS = new Map([
|
||||
['USDC', '8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN'], // use devnet usdc
|
||||
['BTC', '3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU'],
|
||||
['SOL', 'So11111111111111111111111111111111111111112'],
|
||||
['ORCA', 'orcarKHSqC5CDDsGbho8GKvwExejWHxTqGzXgcewB9L'],
|
||||
['MNGO', 'Bb9bsTQa1bGEtQ5KagGkvSHyuLqDWumFUcRqFusFNJWC'],
|
||||
]);
|
||||
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
// fetch group
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 0);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
|
||||
// create + fetch account
|
||||
console.log(`Creating mangoaccount...`);
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(group);
|
||||
console.log(`...created/found mangoAccount ${mangoAccount.publicKey}`);
|
||||
console.log(mangoAccount.toString());
|
||||
|
||||
if (false) {
|
||||
// deposit and withdraw
|
||||
console.log(`Depositing...50 USDC`);
|
||||
await client.tokenDeposit(group, mangoAccount, 'USDC', 50);
|
||||
await mangoAccount.reload(client, group);
|
||||
|
||||
console.log(`Depositing...0.0005 BTC`);
|
||||
await client.tokenDeposit(group, mangoAccount, 'BTC', 0.0005);
|
||||
await mangoAccount.reload(client, group);
|
||||
}
|
||||
try {
|
||||
const sig = await client.marginTrade({
|
||||
group: group,
|
||||
mangoAccount: mangoAccount,
|
||||
inputMintPk: new PublicKey(DEVNET_MINTS['USDC']),
|
||||
amountIn: 0.001,
|
||||
outputMintPk: new PublicKey(DEVNET_MINTS['SOL']),
|
||||
slippage: 1,
|
||||
});
|
||||
console.log(
|
||||
`sig https://explorer.solana.com/address/${sig}?cluster=devnet`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,39 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { MangoClient } from '../../client';
|
||||
|
||||
//
|
||||
// script which shows example usage of ids json (saves having to do gpa)
|
||||
//
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connectForGroupName(
|
||||
userProvider,
|
||||
'devnet.microwavedcola' /* Use ids json instead of getProgramAccounts */,
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 0);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,69 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { MANGO_V4_ID } from '../../constants';
|
||||
import { MangoClient } from '../../index';
|
||||
|
||||
//
|
||||
// An example for users based on high level api i.e. the client
|
||||
// Create
|
||||
// process.env.USER_KEYPAIR - mango account owner keypair path
|
||||
// process.env.ADMIN_KEYPAIR - group admin keypair path (useful for automatically finding the group)
|
||||
//
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
// fetch group
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 0);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
|
||||
// create + fetch account
|
||||
console.log(`Creating mangoaccount...`);
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(group);
|
||||
console.log(`...created/found mangoAccount ${mangoAccount.publicKey}`);
|
||||
|
||||
// logging serum3 open orders for user
|
||||
while (true) {
|
||||
console.log(`Current own orders on OB...`);
|
||||
const orders = await client.getSerum3Orders(
|
||||
group,
|
||||
|
||||
'BTC/USDC',
|
||||
);
|
||||
for (const order of orders) {
|
||||
console.log(
|
||||
` - Order orderId ${order.orderId}, ${order.side}, ${order.price}, ${
|
||||
order.size
|
||||
} ${order.openOrdersAddress.toBase58()}`,
|
||||
);
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,58 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { TokenPosition } from '../../accounts/mangoAccount';
|
||||
import { MANGO_V4_ID } from '../../constants';
|
||||
import { MangoClient } from '../../index';
|
||||
|
||||
//
|
||||
// An example for users based on high level api i.e. the client
|
||||
// Create
|
||||
// process.env.USER_KEYPAIR - mango account owner keypair path
|
||||
// process.env.ADMIN_KEYPAIR - group admin keypair path (useful for automatically finding the group)
|
||||
//
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
// fetch group
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 0);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
|
||||
// create + fetch account
|
||||
console.log(`Creating mangoaccount...`);
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(group);
|
||||
console.log(`...created/found mangoAccount ${mangoAccount.publicKey}`);
|
||||
|
||||
// log users tokens
|
||||
for (const token of mangoAccount.tokens) {
|
||||
if (token.tokenIndex == TokenPosition.TokenIndexUnset) continue;
|
||||
console.log(token.toString());
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,192 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { OrderType, Side } from '../../accounts/perp';
|
||||
import {
|
||||
Serum3OrderType,
|
||||
Serum3SelfTradeBehavior,
|
||||
Serum3Side,
|
||||
} from '../../accounts/serum3';
|
||||
import { MangoClient } from '../../client';
|
||||
import { MANGO_V4_ID } from '../../constants';
|
||||
|
||||
//
|
||||
// An example for users based on high level api i.e. the client
|
||||
// Create
|
||||
// process.env.USER_KEYPAIR - mango account owner keypair path
|
||||
// process.env.ADMIN_KEYPAIR - group admin keypair path (useful for automatically finding the group)
|
||||
//
|
||||
// This script deposits some tokens, places some serum orders, cancels them, places some perp orders
|
||||
//
|
||||
|
||||
const DEVNET_MINTS = new Map([
|
||||
['USDC', '8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN'], // use devnet usdc
|
||||
['BTC', '3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU'],
|
||||
['SOL', 'So11111111111111111111111111111111111111112'],
|
||||
['ORCA', 'orcarKHSqC5CDDsGbho8GKvwExejWHxTqGzXgcewB9L'],
|
||||
['MNGO', 'Bb9bsTQa1bGEtQ5KagGkvSHyuLqDWumFUcRqFusFNJWC'],
|
||||
]);
|
||||
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
// mango account owner
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
// delegate
|
||||
const delegate = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER3_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const delegateWallet = new Wallet(delegate);
|
||||
const delegateProvider = new AnchorProvider(
|
||||
connection,
|
||||
delegateWallet,
|
||||
options,
|
||||
);
|
||||
// Note: simply create a client with delegate and use this client to execute ixs
|
||||
const delegateClient = await MangoClient.connect(
|
||||
delegateProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
console.log(`Delegate ${delegateWallet.publicKey.toBase58()}`);
|
||||
|
||||
// fetch group
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await delegateClient.getGroupForAdmin(admin.publicKey, 0);
|
||||
console.log(group.toString());
|
||||
|
||||
// fetch mango account using owners pubkey
|
||||
console.log(`Fetching mangoaccount...`);
|
||||
const mangoAccount = (
|
||||
await delegateClient.getMangoAccountForOwner(group, user.publicKey)
|
||||
)[0];
|
||||
console.log(`...created/found mangoAccount ${mangoAccount.publicKey}`);
|
||||
console.log(mangoAccount.toString());
|
||||
|
||||
if (true) {
|
||||
// set delegate, and change name
|
||||
console.log(`...changing mango account name, and setting a delegate`);
|
||||
await client.editMangoAccount(
|
||||
group,
|
||||
mangoAccount,
|
||||
'my_changed_name',
|
||||
delegate.publicKey,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
console.log(mangoAccount.toString());
|
||||
}
|
||||
|
||||
if (true) {
|
||||
// deposit
|
||||
console.log(`...depositing 50 USDC`);
|
||||
await client.tokenDeposit(
|
||||
group,
|
||||
mangoAccount,
|
||||
new PublicKey(DEVNET_MINTS['USDC']),
|
||||
50,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
|
||||
console.log(`...depositing 0.0005 BTC`);
|
||||
await client.tokenDeposit(
|
||||
group,
|
||||
mangoAccount,
|
||||
new PublicKey(DEVNET_MINTS['BTC']),
|
||||
0.0005,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
|
||||
// serum3
|
||||
console.log(`...placing serum3 bid`);
|
||||
await delegateClient.serum3PlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'BTC/USDC',
|
||||
Serum3Side.bid,
|
||||
20,
|
||||
0.0001,
|
||||
Serum3SelfTradeBehavior.decrementTake,
|
||||
Serum3OrderType.limit,
|
||||
Date.now(),
|
||||
10,
|
||||
);
|
||||
await mangoAccount.reload(delegateClient, group);
|
||||
|
||||
console.log(`...current own orders on OB`);
|
||||
let orders = await delegateClient.getSerum3Orders(
|
||||
group,
|
||||
|
||||
'BTC/USDC',
|
||||
);
|
||||
for (const order of orders) {
|
||||
console.log(
|
||||
` - order orderId ${order.orderId}, ${order.side}, ${order.price}, ${order.size}`,
|
||||
);
|
||||
console.log(` - cancelling order with ${order.orderId}`);
|
||||
await delegateClient.serum3CancelOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'BTC/USDC',
|
||||
order.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
|
||||
order.orderId,
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`...settling funds`);
|
||||
await delegateClient.serum3SettleFunds(
|
||||
group,
|
||||
mangoAccount,
|
||||
|
||||
'BTC/USDC',
|
||||
);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
// perps
|
||||
console.log(`...placing perp bid`);
|
||||
try {
|
||||
await delegateClient.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'BTC-PERP',
|
||||
Side.bid,
|
||||
30000,
|
||||
0.000001,
|
||||
30000 * 0.000001,
|
||||
Math.floor(Math.random() * 99999),
|
||||
OrderType.limit,
|
||||
0,
|
||||
1,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,150 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { HealthType } from '../../accounts/mangoAccount';
|
||||
import { MangoClient } from '../../client';
|
||||
import { MANGO_V4_ID } from '../../constants';
|
||||
import { toUiDecimalsForQuote } from '../../utils';
|
||||
|
||||
const DEVNET_MINTS = new Map([
|
||||
['USDC', '8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN'], // use devnet usdc
|
||||
['BTC', '3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU'],
|
||||
['SOL', 'So11111111111111111111111111111111111111112'],
|
||||
['ORCA', 'orcarKHSqC5CDDsGbho8GKvwExejWHxTqGzXgcewB9L'],
|
||||
['MNGO', 'Bb9bsTQa1bGEtQ5KagGkvSHyuLqDWumFUcRqFusFNJWC'],
|
||||
]);
|
||||
|
||||
const GROUP_NUM = Number(process.env.GROUP_NUM || 0);
|
||||
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER2_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
// fetch group
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
||||
console.log(group.toString());
|
||||
|
||||
// create + fetch account
|
||||
console.log(`Creating mangoaccount...`);
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(group);
|
||||
console.log(`...created/found mangoAccount ${mangoAccount.publicKey}`);
|
||||
console.log(mangoAccount.toString());
|
||||
|
||||
if (true) {
|
||||
await group.reloadAll(client);
|
||||
console.log(group.banksMapByName.get('USDC')![0].toString());
|
||||
console.log(group.banksMapByName.get('BTC')![0].toString());
|
||||
}
|
||||
|
||||
if (false) {
|
||||
// deposit and withdraw
|
||||
try {
|
||||
console.log(`...depositing 0.0005 BTC`);
|
||||
await client.tokenDeposit(
|
||||
group,
|
||||
mangoAccount,
|
||||
new PublicKey(DEVNET_MINTS['BTC']),
|
||||
0.0005,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
console.log(`...withdrawing 5 USDC`);
|
||||
await client.tokenWithdraw(
|
||||
group,
|
||||
mangoAccount,
|
||||
new PublicKey(DEVNET_MINTS['USDC']),
|
||||
50,
|
||||
true,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (true) {
|
||||
await mangoAccount.reload(client, group);
|
||||
console.log(
|
||||
'...mangoAccount.getEquity() ' +
|
||||
toUiDecimalsForQuote(mangoAccount.getEquity().toNumber()),
|
||||
);
|
||||
console.log(
|
||||
'...mangoAccount.getCollateralValue() ' +
|
||||
toUiDecimalsForQuote(mangoAccount.getCollateralValue().toNumber()),
|
||||
);
|
||||
console.log(
|
||||
'...mangoAccount.accountData["healthCache"].health(HealthType.init) ' +
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount.accountData['healthCache']
|
||||
.health(HealthType.init)
|
||||
.toNumber(),
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
'...mangoAccount.getAssetsVal() ' +
|
||||
toUiDecimalsForQuote(mangoAccount.getAssetsVal().toNumber()),
|
||||
);
|
||||
console.log(
|
||||
'...mangoAccount.getLiabsVal() ' +
|
||||
toUiDecimalsForQuote(mangoAccount.getLiabsVal().toNumber()),
|
||||
);
|
||||
console.log(
|
||||
'...mangoAccount.getMaxWithdrawWithBorrowForToken(group, "SOL") ' +
|
||||
toUiDecimalsForQuote(
|
||||
(
|
||||
await mangoAccount.getMaxWithdrawWithBorrowForToken(
|
||||
group,
|
||||
new PublicKey(DEVNET_MINTS['SOL']),
|
||||
)
|
||||
).toNumber(),
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
"...mangoAccount.getSerum3MarketMarginAvailable(group, 'BTC/USDC') " +
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount
|
||||
.getSerum3MarketMarginAvailable(group, 'BTC/USDC')
|
||||
.toNumber(),
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
"...mangoAccount.getPerpMarketMarginAvailable(group, 'BTC-PERP') " +
|
||||
toUiDecimalsForQuote(
|
||||
mangoAccount
|
||||
.getPerpMarketMarginAvailable(group, 'BTC-PERP')
|
||||
.toNumber(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (true) {
|
||||
await group.reloadAll(client);
|
||||
console.log(group.banksMapByName.get('USDC')![0].toString());
|
||||
console.log(group.banksMapByName.get('BTC')![0].toString());
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,109 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import idsJson from '../../../ids.json';
|
||||
import { MangoClient } from '../../client';
|
||||
import { MANGO_V4_ID, SERUM3_PROGRAM_ID } from '../../constants';
|
||||
import { Id } from '../../ids';
|
||||
|
||||
//
|
||||
// script to add a group to ids json
|
||||
//
|
||||
|
||||
function replacer(key, value) {
|
||||
if (value instanceof Map) {
|
||||
return Object.fromEntries(value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const groupName = 'mainnet-beta.microwavedcola';
|
||||
const cluster = 'mainnet-beta';
|
||||
|
||||
// build client and fetch group for admin
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(process.env.MB_CLUSTER_URL, options);
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
cluster,
|
||||
MANGO_V4_ID[cluster],
|
||||
);
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.MB_PAYER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
console.log(`Admin ${admin.publicKey.toBase58()}`);
|
||||
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 0);
|
||||
|
||||
// collect mappings &
|
||||
// collect pubkeys
|
||||
const banks = await client.getBanksForGroup(group);
|
||||
const banksMapByMint = new Map(
|
||||
banks.map((tuple) => [tuple.mint.toBase58(), tuple]),
|
||||
);
|
||||
const stubOracles = await client.getStubOracle(group);
|
||||
const mintInfos = await client.getMintInfosForGroup(group);
|
||||
const serum3Markets = await client.serum3GetMarkets(group);
|
||||
const perpMarkets = await client.perpGetMarkets(group);
|
||||
|
||||
// build ids
|
||||
const toDump = new Id(
|
||||
cluster,
|
||||
groupName,
|
||||
group.publicKey.toBase58(),
|
||||
SERUM3_PROGRAM_ID[cluster].toBase58(),
|
||||
MANGO_V4_ID[cluster].toBase58(),
|
||||
banks.map((tuple) => ({
|
||||
name: tuple.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
})),
|
||||
stubOracles.map((tuple) => ({
|
||||
name: banksMapByMint.get(tuple.mint.toBase58())!.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
})),
|
||||
mintInfos.map((tuple) => ({
|
||||
name: banksMapByMint.get(tuple.mint.toBase58())!.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
})),
|
||||
serum3Markets.map((tuple) => ({
|
||||
name: tuple.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
marketExternal: tuple.serumMarketExternal.toBase58(),
|
||||
})),
|
||||
perpMarkets.map((tuple) => ({
|
||||
name: tuple.name,
|
||||
publicKey: tuple.publicKey.toBase58(),
|
||||
})),
|
||||
);
|
||||
|
||||
console.log(toDump);
|
||||
|
||||
// adds ids for group in existing ids.json
|
||||
const existingGroup = idsJson.groups.find((group) => group.name == groupName);
|
||||
if (existingGroup) {
|
||||
console.log('Updating existing group with latest state...');
|
||||
} else {
|
||||
console.log('Group does not exist yet...');
|
||||
}
|
||||
idsJson.groups = idsJson.groups.filter((group) => group.name !== groupName);
|
||||
idsJson.groups.push(toDump);
|
||||
|
||||
// dump
|
||||
const file = `${process.cwd()}/ts/client/ids.json`;
|
||||
await fs.writeFileSync(file, JSON.stringify(idsJson, replacer, 2));
|
||||
|
||||
process.exit();
|
||||
}
|
||||
main();
|
|
@ -1,280 +0,0 @@
|
|||
import { Jupiter } from '@jup-ag/core';
|
||||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import {
|
||||
AccountMeta,
|
||||
Connection,
|
||||
Keypair,
|
||||
SYSVAR_INSTRUCTIONS_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from '@solana/web3.js';
|
||||
import BN from 'bn.js';
|
||||
import fs from 'fs';
|
||||
import { QUOTE_DECIMALS } from '../../accounts/bank';
|
||||
import { MangoClient } from '../../index';
|
||||
import { getAssociatedTokenAddress } from '../../utils';
|
||||
|
||||
const MB_CLUSTER_URL =
|
||||
process.env.MB_CLUSTER_URL ||
|
||||
'https://mango.rpcpool.com/946ef7337da3f5b8d3e4a34e7f88';
|
||||
const MB_PAYER_KEYPAIR =
|
||||
process.env.MB_PAYER_KEYPAIR ||
|
||||
'/Users/tylershipe/.config/solana/deploy.json';
|
||||
|
||||
//
|
||||
// example script which shows usage of flash loan ix using a jupiter swap
|
||||
//
|
||||
// NOTE: we assume that ATA for source and target already exist for wallet
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(MB_CLUSTER_URL, options);
|
||||
|
||||
// load user key
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(JSON.parse(fs.readFileSync(MB_PAYER_KEYPAIR!, 'utf-8'))),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connectForGroupName(
|
||||
userProvider,
|
||||
'mainnet-beta.microwavedcola',
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
// load admin key
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(JSON.parse(fs.readFileSync(MB_PAYER_KEYPAIR!, 'utf-8'))),
|
||||
);
|
||||
console.log(`Admin ${admin.publicKey.toBase58()}`);
|
||||
|
||||
// fetch group
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 0);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
console.log(`start btc bank ${group.banksMap.get('BTC').toString()}`);
|
||||
|
||||
// create + fetch account
|
||||
console.log(`Creating mangoaccount...`);
|
||||
const mangoAccount = await client.getOrCreateMangoAccount(group);
|
||||
console.log(`...created/found mangoAccount ${mangoAccount.publicKey}`);
|
||||
console.log(`start balance \n${mangoAccount.toString(group)}`);
|
||||
|
||||
//
|
||||
// flash loan 3
|
||||
//
|
||||
if (true) {
|
||||
// source of swap
|
||||
const sourceBank = group.banksMap.get('USDC');
|
||||
// target of swap
|
||||
const targetBank = group.banksMap.get('BTC');
|
||||
// 0.2$, at 1BTC=20,000$, 0.2$=0.00001BTC
|
||||
const sourceAmount = 2 * Math.pow(10, QUOTE_DECIMALS - 1);
|
||||
|
||||
console.log(`Flash loaning ${sourceBank.name} to ${targetBank.name}`);
|
||||
|
||||
// jupiter route
|
||||
const jupiter = await Jupiter.load({
|
||||
connection: client.program.provider.connection,
|
||||
cluster: 'mainnet-beta',
|
||||
user: mangoAccount.owner, // or public key
|
||||
// platformFeeAndAccounts: NO_PLATFORM_FEE,
|
||||
routeCacheDuration: 10_000, // Will not refetch data on computeRoutes for up to 10 seconds
|
||||
});
|
||||
const routes = await jupiter.computeRoutes({
|
||||
inputMint: sourceBank.mint, // Mint address of the input token
|
||||
outputMint: targetBank.mint, // Mint address of the output token
|
||||
inputAmount: sourceAmount, // raw input amount of tokens
|
||||
slippage: 5, // The slippage in % terms
|
||||
forceFetch: false, // false is the default value => will use cache if not older than routeCacheDuration
|
||||
});
|
||||
const routesInfosWithoutRaydium = routes.routesInfos.filter((r) => {
|
||||
if (r.marketInfos.length > 1) {
|
||||
for (const mkt of r.marketInfos) {
|
||||
if (mkt.amm.label === 'Raydium' || mkt.amm.label === 'Serum')
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// loop until we manage first successful swap
|
||||
let res;
|
||||
let i = 0;
|
||||
while (true) {
|
||||
const instructions: TransactionInstruction[] = [];
|
||||
|
||||
// select a route and fetch+build its tx
|
||||
const selectedRoute = routesInfosWithoutRaydium[i];
|
||||
const { transactions } = await jupiter.exchange({
|
||||
routeInfo: selectedRoute,
|
||||
});
|
||||
|
||||
const { setupTransaction, swapTransaction } = transactions;
|
||||
for (const ix of swapTransaction.instructions) {
|
||||
if (
|
||||
ix.programId.toBase58() ===
|
||||
'JUP2jxvXaqu7NQY1GmNF4m1vodw12LVXYxbFL2uJvfo'
|
||||
) {
|
||||
instructions.push(ix);
|
||||
}
|
||||
}
|
||||
|
||||
// run jup setup in a separate tx, ideally this should be packed before flashLoanBegin in same tx,
|
||||
// but it increases chance of flash loan tx to exceed tx size limit
|
||||
if (setupTransaction) {
|
||||
await this.program.provider.sendAndConfirm(setupTransaction);
|
||||
}
|
||||
|
||||
// flash loan start ix - takes a loan for source token,
|
||||
// flash loan end ix - returns increase in all token account's amounts to respective vaults,
|
||||
const healthRemainingAccounts =
|
||||
client.buildFixedAccountRetrieverHealthAccounts(
|
||||
group,
|
||||
mangoAccount,
|
||||
[sourceBank, targetBank], // we would be taking a sol loan potentially
|
||||
);
|
||||
// 1. build flash loan end ix
|
||||
const flashLoadnEndIx = await client.program.methods
|
||||
.flashLoanEnd(true)
|
||||
.accounts({
|
||||
account: mangoAccount.publicKey,
|
||||
owner: (client.program.provider as AnchorProvider).wallet.publicKey,
|
||||
})
|
||||
.remainingAccounts([
|
||||
...healthRemainingAccounts.map(
|
||||
(pk) =>
|
||||
({
|
||||
pubkey: pk,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
} as AccountMeta),
|
||||
),
|
||||
{
|
||||
pubkey: sourceBank.vault,
|
||||
isWritable: true,
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: targetBank.vault,
|
||||
isWritable: true,
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: await getAssociatedTokenAddress(
|
||||
sourceBank.mint,
|
||||
mangoAccount.owner,
|
||||
),
|
||||
isWritable: true, // increase in this address amount is transferred back to the sourceBank.vault above in this case whatever is residual of source bank loan
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: await getAssociatedTokenAddress(
|
||||
targetBank.mint,
|
||||
mangoAccount.owner,
|
||||
),
|
||||
isWritable: true, // increase in this address amount is transferred back to the targetBank.vault above in this case whatever is result of swap
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: group.publicKey,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
])
|
||||
.instruction();
|
||||
instructions.push(flashLoadnEndIx);
|
||||
// 2. build flash loan start ix, add end ix as a post ix
|
||||
try {
|
||||
res = await client.program.methods
|
||||
.flashLoanBegin([
|
||||
new BN(sourceAmount),
|
||||
new BN(
|
||||
0,
|
||||
) /* we don't care about borrowing the target amount, this is just a dummy */,
|
||||
])
|
||||
.accounts({
|
||||
// for observing ixs in the entire tx,
|
||||
// e.g. apart from flash loan start and end no other ix should target mango v4 program
|
||||
// e.g. forbid FlashLoanBegin been called from CPI
|
||||
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
|
||||
})
|
||||
.remainingAccounts([
|
||||
{
|
||||
pubkey: sourceBank.publicKey,
|
||||
isWritable: true, // metadata for flash loan is updated
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: targetBank.publicKey,
|
||||
isWritable: true, // this is a dummy, its just done so that we match flash loan start and end ix
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: sourceBank.vault,
|
||||
isWritable: true,
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: targetBank.vault,
|
||||
isWritable: true, // this is a dummy, its just done so that we match flash loan start and end ix
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: await getAssociatedTokenAddress(
|
||||
sourceBank.mint,
|
||||
mangoAccount.owner,
|
||||
),
|
||||
isWritable: true, // token transfer i.e. loan to a desired token account e.g. user's ATA when using a route made for a specific user
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: await getAssociatedTokenAddress(
|
||||
targetBank.mint,
|
||||
mangoAccount.owner,
|
||||
),
|
||||
isWritable: false, // this is a dummy, its just done so that we match flash loan start and end ix
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
{
|
||||
pubkey: group.publicKey,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
} as AccountMeta,
|
||||
])
|
||||
.postInstructions(instructions)
|
||||
.rpc();
|
||||
|
||||
// break when success
|
||||
break;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
if (
|
||||
(error.toString() as string).includes('Transaction too large:') ||
|
||||
(error.toString() as string).includes(
|
||||
'encoding overruns Uint8Array',
|
||||
) ||
|
||||
(error.toString() as string).includes(
|
||||
'The value of "offset" is out of range. It must be >= 0 and <= 1231. Received 1232',
|
||||
) ||
|
||||
(error.toString() as string).includes(
|
||||
'The value of "value" is out of range. It must be >= 0 and <= 255. Received',
|
||||
) ||
|
||||
i > 10
|
||||
) {
|
||||
console.log(`route ${i} was bad, trying next one...`);
|
||||
i++;
|
||||
} else {
|
||||
throw error; // let others bubble up
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`success tx - https://explorer.solana.com/tx/${res}`);
|
||||
|
||||
group.reloadBanks(client);
|
||||
console.log(`end btc bank ${group.banksMap.get('BTC').toString()}`);
|
||||
|
||||
await mangoAccount.reload(client, group);
|
||||
console.log(`end balance \n${mangoAccount.toString(group)}`);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,42 +0,0 @@
|
|||
import { AnchorProvider } from '@project-serum/anchor';
|
||||
import { Connection, PublicKey } from '@solana/web3.js';
|
||||
import { parseSwitchboardOracle } from '../../accounts/oracle';
|
||||
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
|
||||
async function foo(obj) {
|
||||
let connection = new Connection(obj.net, options);
|
||||
let ai = await connection.getAccountInfo(new PublicKey(obj.pk));
|
||||
console.log(`${obj.name} price ${await parseSwitchboardOracle(ai!)}`);
|
||||
}
|
||||
|
||||
for (const oracle of [
|
||||
{
|
||||
name: 'devnet mngo v1',
|
||||
pk: '8k7F9Xb36oFJsjpCKpsXvg4cgBRoZtwNTc3EzG5Ttd2o',
|
||||
net: 'https://mango.devnet.rpcpool.com',
|
||||
},
|
||||
{
|
||||
name: 'devnet sol v2',
|
||||
pk: 'GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR',
|
||||
net: 'https://mango.devnet.rpcpool.com',
|
||||
},
|
||||
{
|
||||
name: 'mainnet btc v2',
|
||||
pk: '3HtmwdXJPAdMZ73fTGeCFgbDQZGLZWpmsm3JAB5quGJN',
|
||||
net: 'http://api.mainnet-beta.solana.com/',
|
||||
},
|
||||
{
|
||||
name: 'mainnet sol v2',
|
||||
pk: 'GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR',
|
||||
net: 'http://api.mainnet-beta.solana.com/',
|
||||
},
|
||||
]) {
|
||||
await foo(oracle);
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,51 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Market } from '@project-serum/serum';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import * as os from 'os';
|
||||
|
||||
import { MangoClient } from '../../client';
|
||||
import { MANGO_V4_ID } from '../../constants';
|
||||
|
||||
const main = async () => {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(
|
||||
fs.readFileSync(os.homedir() + '/.config/solana/admin.json', 'utf-8'),
|
||||
),
|
||||
),
|
||||
);
|
||||
const adminWallet = new Wallet(admin);
|
||||
console.log(`Admin ${adminWallet.publicKey.toBase58()}`);
|
||||
const adminProvider = new AnchorProvider(connection, adminWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
adminProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
);
|
||||
|
||||
const btcMint = new PublicKey('3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU');
|
||||
const usdcMint = new PublicKey(
|
||||
'EmXq3Ni9gfudTiyNKzzYvpnQqnJEMRw2ttnVXoJXjLo1',
|
||||
);
|
||||
const serumProgramId = new PublicKey(
|
||||
'DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY',
|
||||
);
|
||||
|
||||
const market = await Market.findAccountsByMints(
|
||||
connection,
|
||||
btcMint,
|
||||
usdcMint,
|
||||
serumProgramId,
|
||||
);
|
||||
|
||||
console.log('market', market);
|
||||
};
|
||||
|
||||
main();
|
|
@ -33,8 +33,9 @@ async function main() {
|
|||
adminProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
||||
|
@ -57,16 +58,19 @@ async function main() {
|
|||
group.consoleLogBanks();
|
||||
|
||||
// deregister all serum markets
|
||||
for (const market of group.serum3MarketsMap.values()) {
|
||||
sig = await client.serum3deregisterMarket(group, market.name);
|
||||
for (const market of group.serum3MarketsMapByExternal.values()) {
|
||||
sig = await client.serum3deregisterMarket(
|
||||
group,
|
||||
market.serumMarketExternal,
|
||||
);
|
||||
console.log(
|
||||
`Deregistered serum market ${market.name}, sig https://explorer.solana.com/tx/${sig}?cluster=devnet`,
|
||||
);
|
||||
}
|
||||
|
||||
// close all perp markets
|
||||
for (const market of group.perpMarketsMap.values()) {
|
||||
sig = await client.perpCloseMarket(group, market.name);
|
||||
for (const market of group.perpMarketsMapByMarketIndex.values()) {
|
||||
sig = await client.perpCloseMarket(group, market.perpMarketIndex);
|
||||
console.log(
|
||||
`Closed perp market ${market.name}, sig https://explorer.solana.com/tx/${sig}?cluster=devnet`,
|
||||
);
|
||||
|
|
|
@ -65,8 +65,9 @@ async function main() {
|
|||
adminProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
// group
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { Serum3Side } from '../accounts/serum3';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
|
||||
//
|
||||
// script which shows how to close a mango account cleanly i.e. close all active positions, withdraw all tokens, etc.
|
||||
//
|
||||
|
||||
const GROUP_NUM = Number(process.env.GROUP_NUM || 0);
|
||||
|
||||
// note: either use finalized or expect closing certain things to fail and having to runs scrript multiple times
|
||||
async function main() {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
|
||||
// note: see note above
|
||||
// options.commitment = 'finalized';
|
||||
|
||||
const connection = new Connection(
|
||||
'https://mango.devnet.rpcpool.com',
|
||||
options,
|
||||
);
|
||||
|
||||
// user
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.USER2_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
try {
|
||||
// fetch group
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.ADMIN_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
);
|
||||
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
|
||||
// fetch account
|
||||
const mangoAccount = (
|
||||
await client.getMangoAccountsForOwner(group, user.publicKey)
|
||||
)[0];
|
||||
console.log(`...found mangoAccount ${mangoAccount.publicKey}`);
|
||||
console.log(mangoAccount.toString());
|
||||
|
||||
// close mango account serum3 positions, closing might require cancelling orders and settling
|
||||
for (const serum3Account of mangoAccount.serum3Active()) {
|
||||
let orders = await client.getSerum3Orders(
|
||||
group,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
);
|
||||
for (const order of orders) {
|
||||
console.log(
|
||||
` - Order orderId ${order.orderId}, ${order.side}, ${order.price}, ${order.size}`,
|
||||
);
|
||||
console.log(` - Cancelling order with ${order.orderId}`);
|
||||
await client.serum3CancelOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
|
||||
'BTC/USDC',
|
||||
order.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
|
||||
order.orderId,
|
||||
);
|
||||
}
|
||||
await client.serum3SettleFunds(
|
||||
group,
|
||||
mangoAccount,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
);
|
||||
await client.serum3CloseOpenOrders(
|
||||
group,
|
||||
mangoAccount,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
);
|
||||
}
|
||||
|
||||
// we closed a serum account, this changes the health accounts we are passing in for future ixs
|
||||
await mangoAccount.reload(client, group);
|
||||
|
||||
// withdraw all tokens
|
||||
for (const token of mangoAccount.tokensActive()) {
|
||||
let native = token.balance(
|
||||
group.getFirstBankByTokenIndex(token.tokenIndex),
|
||||
);
|
||||
|
||||
// to avoid rounding issues
|
||||
if (native.toNumber() < 1) {
|
||||
continue;
|
||||
}
|
||||
let nativeFlooredNumber = Math.floor(native.toNumber());
|
||||
console.log(
|
||||
`withdrawing token ${
|
||||
group.getFirstBankByTokenIndex(token.tokenIndex).name
|
||||
} native amount ${nativeFlooredNumber} `,
|
||||
);
|
||||
|
||||
await client.tokenWithdrawNative(
|
||||
group,
|
||||
mangoAccount,
|
||||
group.getFirstBankByTokenIndex(token.tokenIndex).mint,
|
||||
nativeFlooredNumber - 1 /* see comment in token_withdraw in program */,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// reload and print current positions
|
||||
await mangoAccount.reload(client, group);
|
||||
console.log(`...mangoAccount ${mangoAccount.publicKey}`);
|
||||
console.log(mangoAccount.toString());
|
||||
|
||||
// close account
|
||||
console.log(`Close mango account...`);
|
||||
const res = await client.closeMangoAccount(group, mangoAccount);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
main();
|
|
@ -55,8 +55,9 @@ async function main() {
|
|||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
|
@ -86,7 +87,7 @@ async function main() {
|
|||
new PublicKey(DEVNET_MINTS.get('USDC')!),
|
||||
1000,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
await client.tokenDeposit(
|
||||
group,
|
||||
|
@ -94,7 +95,7 @@ async function main() {
|
|||
new PublicKey(DEVNET_MINTS.get('MNGO')!),
|
||||
100,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
await client.tokenDeposit(
|
||||
group,
|
||||
|
@ -102,7 +103,7 @@ async function main() {
|
|||
new PublicKey(DEVNET_MINTS.get('ETH')!),
|
||||
500,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
await client.tokenDeposit(
|
||||
group,
|
||||
|
@ -110,7 +111,7 @@ async function main() {
|
|||
new PublicKey(DEVNET_MINTS.get('SRM')!),
|
||||
500,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
await client.tokenDeposit(
|
||||
group,
|
||||
|
@ -118,7 +119,7 @@ async function main() {
|
|||
new PublicKey(DEVNET_MINTS.get('BTC')!),
|
||||
1,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
console.log(mangoAccount.toString(group));
|
||||
} catch (error) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { AnchorProvider, BN, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { Serum3Side } from '../accounts/serum3';
|
||||
|
@ -35,8 +35,9 @@ async function main() {
|
|||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
|
@ -59,9 +60,11 @@ async function main() {
|
|||
|
||||
// close mango account serum3 positions, closing might require cancelling orders and settling
|
||||
for (const serum3Account of mangoAccount.serum3Active()) {
|
||||
let orders = await client.getSerum3Orders(
|
||||
let orders = await mangoAccount.loadSerum3OpenOrdersForMarket(
|
||||
client,
|
||||
group,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
group.serum3MarketsMapByMarketIndex.get(serum3Account.marketIndex)
|
||||
?.serumMarketExternal!,
|
||||
);
|
||||
for (const order of orders) {
|
||||
console.log(
|
||||
|
@ -71,8 +74,8 @@ async function main() {
|
|||
await client.serum3CancelOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
|
||||
'BTC/USDC',
|
||||
group.serum3MarketsMapByMarketIndex.get(serum3Account.marketIndex)
|
||||
?.serumMarketExternal!,
|
||||
order.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
|
||||
order.orderId,
|
||||
);
|
||||
|
@ -80,17 +83,19 @@ async function main() {
|
|||
await client.serum3SettleFunds(
|
||||
group,
|
||||
mangoAccount,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
group.serum3MarketsMapByMarketIndex.get(serum3Account.marketIndex)
|
||||
?.serumMarketExternal!,
|
||||
);
|
||||
await client.serum3CloseOpenOrders(
|
||||
group,
|
||||
mangoAccount,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
group.serum3MarketsMapByMarketIndex.get(serum3Account.marketIndex)
|
||||
?.serumMarketExternal!,
|
||||
);
|
||||
}
|
||||
|
||||
// we closed a serum account, this changes the health accounts we are passing in for future ixs
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
// withdraw all tokens
|
||||
for (const token of mangoAccount.tokensActive()) {
|
||||
|
@ -113,13 +118,15 @@ async function main() {
|
|||
group,
|
||||
mangoAccount,
|
||||
group.getFirstBankByTokenIndex(token.tokenIndex).mint,
|
||||
nativeFlooredNumber - 1 /* see comment in token_withdraw in program */,
|
||||
new BN(
|
||||
nativeFlooredNumber - 1,
|
||||
) /* see comment in token_withdraw in program */,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// reload and print current positions
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
console.log(`...mangoAccount ${mangoAccount.publicKey}`);
|
||||
console.log(mangoAccount.toString());
|
||||
|
||||
|
|
|
@ -54,8 +54,9 @@ async function main() {
|
|||
userProvider,
|
||||
'devnet',
|
||||
MANGO_V4_ID['devnet'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { Serum3Side } from '../accounts/serum3';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
|
||||
const MAINNET_ORACLES = new Map([
|
||||
['USDT', '3vxLXJqLqF3JG5TCbYycbKWRBbCJQLxQmBGCkyqEEefL'],
|
||||
['BTC', 'GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU'],
|
||||
['ETH', 'JBu1AL4obBcCMqKBBxhpWCNUt136ijcuMZLFvTP7iWdB'],
|
||||
['soETH', 'JBu1AL4obBcCMqKBBxhpWCNUt136ijcuMZLFvTP7iWdB'],
|
||||
['SOL', 'H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG'],
|
||||
['MSOL', 'E4v1BBgoso9s64TQvmyownAVJbhbEPGyzA3qn4n46qj9'],
|
||||
['MNGO', '79wm3jjcPr6RaNQ4DGvP5KxG1mNd3gEBsg6FsNVFezK4'],
|
||||
]);
|
||||
|
||||
const PAYER_KEYPAIR = process.env.MB_PAYER_KEYPAIR || '';
|
||||
|
||||
//
|
||||
// (untested?) script which closes a mango account cleanly, first closes all positions, withdraws all tokens and then closes it
|
||||
//
|
||||
async function viewUnownedAccount(userKeypairFile: string) {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.rpcpool.com/0f9acc0d45173b51bf7d7e09c1e5',
|
||||
options,
|
||||
);
|
||||
|
||||
// user
|
||||
// const userWallet = new Wallet(Keypair.generate());
|
||||
// const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
// console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
// admin
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(JSON.parse(fs.readFileSync(PAYER_KEYPAIR, 'utf-8'))),
|
||||
);
|
||||
const adminWallet = new Wallet(admin);
|
||||
const adminProvider = new AnchorProvider(connection, adminWallet, options);
|
||||
console.log(`Admin ${admin.publicKey.toBase58()}`);
|
||||
|
||||
const client = await MangoClient.connect(
|
||||
adminProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
);
|
||||
|
||||
// fetch group
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 2);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
|
||||
const btcMainnetOracle = new PublicKey(MAINNET_ORACLES.get('BTC')!);
|
||||
console.log(`Registering perp market...`);
|
||||
try {
|
||||
await client.perpCreateMarket(
|
||||
group,
|
||||
btcMainnetOracle,
|
||||
0,
|
||||
'BTC-PERP',
|
||||
0.1,
|
||||
6,
|
||||
0,
|
||||
1,
|
||||
10,
|
||||
0.975,
|
||||
0.95,
|
||||
1.025,
|
||||
1.05,
|
||||
0.012,
|
||||
0.0002,
|
||||
0.0,
|
||||
0.05,
|
||||
0.05,
|
||||
100,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
console.log('done');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await viewUnownedAccount(process.env.MB_USER2_KEYPAIR || '');
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,5 +1,5 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
|
@ -27,8 +27,9 @@ async function main() {
|
|||
adminProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
const groups = await (async () => {
|
||||
|
@ -65,8 +66,8 @@ async function main() {
|
|||
}
|
||||
|
||||
// close all perp markets
|
||||
for (const market of group.perpMarketsMap.values()) {
|
||||
sig = await client.perpCloseMarket(group, market.name);
|
||||
for (const market of group.perpMarketsMapByMarketIndex.values()) {
|
||||
sig = await client.perpCloseMarket(group, market.perpMarketIndex);
|
||||
console.log(
|
||||
`Closed perp market ${market.name}, sig https://explorer.solana.com/tx/${sig}`,
|
||||
);
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import {
|
||||
AddressLookupTableProgram,
|
||||
Connection,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
} from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { TokenIndex } from '../accounts/bank';
|
||||
import { Group } from '../accounts/group';
|
||||
import {
|
||||
Serum3OrderType,
|
||||
|
@ -9,6 +15,7 @@ import {
|
|||
} from '../accounts/serum3';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
import { buildVersionedTx } from '../utils';
|
||||
|
||||
const MAINNET_MINTS = new Map([
|
||||
['USDC', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'],
|
||||
|
@ -19,6 +26,8 @@ const MAINNET_MINTS = new Map([
|
|||
['SOL', 'So11111111111111111111111111111111111111112'], // Wrapped SOL
|
||||
['MSOL', 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So'],
|
||||
['MNGO', 'MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac'],
|
||||
['RAY', '4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R'],
|
||||
['DUST', 'DUSTawucrTsGU8hcqRdHDCbuYhCPADMLM2VcCb8VnFnQ'],
|
||||
]);
|
||||
const MAINNET_ORACLES = new Map([
|
||||
['USDT', '3vxLXJqLqF3JG5TCbYycbKWRBbCJQLxQmBGCkyqEEefL'],
|
||||
|
@ -28,6 +37,8 @@ const MAINNET_ORACLES = new Map([
|
|||
['SOL', 'H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG'],
|
||||
['MSOL', 'E4v1BBgoso9s64TQvmyownAVJbhbEPGyzA3qn4n46qj9'],
|
||||
['MNGO', '79wm3jjcPr6RaNQ4DGvP5KxG1mNd3gEBsg6FsNVFezK4'],
|
||||
['RAY', 'AnLf8tVYCM816gmBjiy8n53eXKKEDydT5piYjjQDPgTB'],
|
||||
['DUST', 'C5tuUPi7xJHBHZGZX6wWYf1Svm6jtTVwYrYrBCiEVejK'],
|
||||
]);
|
||||
|
||||
// External markets are matched with those in https://github.com/blockworks-foundation/mango-client-v3/blob/main/src/ids.json
|
||||
|
@ -35,17 +46,20 @@ const MAINNET_ORACLES = new Map([
|
|||
const MAINNET_SERUM3_MARKETS = new Map([
|
||||
['BTC/USDC', 'A8YFbxQYFVqKZaoYJLLUVcQiWP7G2MeEgW5wsAQgMvFw'],
|
||||
['SOL/USDC', '9wFFyRfZBsuAha4YcuxcXLKwMxJR43S7fPfQLusDBzvT'],
|
||||
['RAY/SOL', 'C6tp2RVZnxBPFbnAsfTjis8BN9tycESAT4SgDQgbbrsA'],
|
||||
['DUST/SOL', '8WCzJpSNcLUYXPYeUDAXpH4hgqxFJpkYkVT6GJDSpcGx'],
|
||||
]);
|
||||
|
||||
const { MB_CLUSTER_URL, MB_PAYER_KEYPAIR, MB_USER_KEYPAIR, MB_USER2_KEYPAIR } =
|
||||
process.env;
|
||||
|
||||
async function buildAdminClient(): Promise<[MangoClient, Keypair]> {
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.MB_PAYER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
Buffer.from(JSON.parse(fs.readFileSync(MB_PAYER_KEYPAIR!, 'utf-8'))),
|
||||
);
|
||||
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(process.env.MB_CLUSTER_URL!, options);
|
||||
const connection = new Connection(MB_CLUSTER_URL!, options);
|
||||
|
||||
const adminWallet = new Wallet(admin);
|
||||
console.log(`Admin ${adminWallet.publicKey.toBase58()}`);
|
||||
|
@ -55,6 +69,9 @@ async function buildAdminClient(): Promise<[MangoClient, Keypair]> {
|
|||
adminProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
),
|
||||
admin,
|
||||
];
|
||||
|
@ -64,7 +81,7 @@ async function buildUserClient(
|
|||
userKeypair: string,
|
||||
): Promise<[MangoClient, Group, Keypair]> {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(process.env.MB_CLUSTER_URL!, options);
|
||||
const connection = new Connection(MB_CLUSTER_URL!, options);
|
||||
|
||||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(JSON.parse(fs.readFileSync(userKeypair, 'utf-8'))),
|
||||
|
@ -79,9 +96,7 @@ async function buildUserClient(
|
|||
);
|
||||
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.MB_PAYER_KEYPAIR!, 'utf-8')),
|
||||
),
|
||||
Buffer.from(JSON.parse(fs.readFileSync(MB_PAYER_KEYPAIR!, 'utf-8'))),
|
||||
);
|
||||
console.log(`Admin ${admin.publicKey.toBase58()}`);
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 2);
|
||||
|
@ -287,6 +302,55 @@ async function registerTokens() {
|
|||
1.2,
|
||||
0.05,
|
||||
);
|
||||
console.log(`Registering RAY...`);
|
||||
const rayMainnetMint = new PublicKey(MAINNET_MINTS.get('RAY')!);
|
||||
const rayMainnetOracle = new PublicKey(MAINNET_ORACLES.get('RAY')!);
|
||||
await client.tokenRegister(
|
||||
group,
|
||||
rayMainnetMint,
|
||||
rayMainnetOracle,
|
||||
0.1,
|
||||
7,
|
||||
'RAY',
|
||||
0.004,
|
||||
0.7,
|
||||
0.2,
|
||||
0.85,
|
||||
0.4,
|
||||
4.0,
|
||||
0.005,
|
||||
0.0005,
|
||||
7 / 8,
|
||||
3 / 4,
|
||||
8 / 7,
|
||||
4 / 3,
|
||||
1 / 16,
|
||||
);
|
||||
|
||||
console.log(`Registering DUST...`);
|
||||
const dustMainnetMint = new PublicKey(MAINNET_MINTS.get('DUST')!);
|
||||
const dustMainnetOracle = new PublicKey(MAINNET_ORACLES.get('DUST')!);
|
||||
await client.tokenRegister(
|
||||
group,
|
||||
dustMainnetMint,
|
||||
dustMainnetOracle,
|
||||
0.1,
|
||||
8,
|
||||
'DUST',
|
||||
0.004,
|
||||
0.7,
|
||||
0.3,
|
||||
0.85,
|
||||
0.6,
|
||||
6.0,
|
||||
0.005,
|
||||
0.0005,
|
||||
0, // no asset weight for isolation
|
||||
0,
|
||||
81 / 80,
|
||||
41 / 40, // 40x leverage so we can test something
|
||||
1 / 160, // no liquidation fee
|
||||
);
|
||||
|
||||
// log tokens/banks
|
||||
await group.reloadAll(client);
|
||||
|
@ -295,6 +359,20 @@ async function registerTokens() {
|
|||
}
|
||||
}
|
||||
|
||||
async function unregisterTokens() {
|
||||
const result = await buildAdminClient();
|
||||
const client = result[0];
|
||||
const admin = result[1];
|
||||
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 2);
|
||||
|
||||
let bank = group.getFirstBankByTokenIndex(8 as TokenIndex);
|
||||
let sig = await client.tokenDeregister(group, bank.mint);
|
||||
console.log(
|
||||
`Removed token ${bank.name}, sig https://explorer.solana.com/tx/${sig}`,
|
||||
);
|
||||
}
|
||||
|
||||
async function registerSerum3Markets() {
|
||||
const result = await buildAdminClient();
|
||||
const client = result[0];
|
||||
|
@ -328,6 +406,50 @@ async function registerSerum3Markets() {
|
|||
1,
|
||||
'SOL/USDC',
|
||||
);
|
||||
|
||||
// Register RAY and DUST markets
|
||||
await client.serum3RegisterMarket(
|
||||
group,
|
||||
new PublicKey(MAINNET_SERUM3_MARKETS.get('RAY/SOL')!),
|
||||
group.getFirstBankByMint(new PublicKey(MAINNET_MINTS.get('RAY')!)),
|
||||
group.getFirstBankByMint(new PublicKey(MAINNET_MINTS.get('SOL')!)),
|
||||
2,
|
||||
'RAY/SOL',
|
||||
);
|
||||
await client.serum3RegisterMarket(
|
||||
group,
|
||||
new PublicKey(MAINNET_SERUM3_MARKETS.get('DUST/SOL')!),
|
||||
group.getFirstBankByMint(new PublicKey(MAINNET_MINTS.get('DUST')!)),
|
||||
group.getFirstBankByMint(new PublicKey(MAINNET_MINTS.get('SOL')!)),
|
||||
3,
|
||||
'DUST/SOL',
|
||||
);
|
||||
}
|
||||
|
||||
async function unregisterSerum3Markets() {
|
||||
const result = await buildAdminClient();
|
||||
const client = result[0];
|
||||
const admin = result[1];
|
||||
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 2);
|
||||
|
||||
let serum3Market = group.getSerum3MarketByName('RAY/SOL');
|
||||
let sig = await client.serum3deregisterMarket(
|
||||
group,
|
||||
serum3Market.serumMarketExternal,
|
||||
);
|
||||
console.log(
|
||||
`Deregistered serum market ${serum3Market.name}, sig https://explorer.solana.com/tx/${sig}`,
|
||||
);
|
||||
|
||||
serum3Market = group.getSerum3MarketByName('DUST/SOL');
|
||||
sig = await client.serum3deregisterMarket(
|
||||
group,
|
||||
serum3Market.serumMarketExternal,
|
||||
);
|
||||
console.log(
|
||||
`Deregistered serum market ${serum3Market.name}, sig https://explorer.solana.com/tx/${sig}`,
|
||||
);
|
||||
}
|
||||
|
||||
async function createUser(userKeypair: string) {
|
||||
|
@ -350,7 +472,7 @@ async function createUser(userKeypair: string) {
|
|||
new PublicKey(MAINNET_MINTS.get('USDC')!),
|
||||
10,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
console.log(`...deposited 10 USDC`);
|
||||
|
||||
await client.tokenDeposit(
|
||||
|
@ -359,7 +481,7 @@ async function createUser(userKeypair: string) {
|
|||
new PublicKey(MAINNET_MINTS.get('SOL')!),
|
||||
1,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
console.log(`...deposited 1 SOL`);
|
||||
}
|
||||
|
||||
|
@ -422,7 +544,7 @@ async function placeSerum3TradeAndCancelIt(userKeypair: string) {
|
|||
console.log(order);
|
||||
}
|
||||
console.log(`...cancelling serum3 orders`);
|
||||
await client.serum3CancelAllorders(
|
||||
await client.serum3CancelAllOrders(
|
||||
group,
|
||||
mangoAccount,
|
||||
new PublicKey(MAINNET_SERUM3_MARKETS.get('SOL/USDC')!),
|
||||
|
@ -440,6 +562,126 @@ async function placeSerum3TradeAndCancelIt(userKeypair: string) {
|
|||
}
|
||||
}
|
||||
|
||||
async function createAndPopulateAlt() {
|
||||
const result = await buildAdminClient();
|
||||
const client = result[0];
|
||||
const admin = result[1];
|
||||
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 2);
|
||||
|
||||
const connection = client.program.provider.connection;
|
||||
|
||||
// Create ALT, and set to group at index 0
|
||||
if (group.addressLookupTables[0].equals(PublicKey.default)) {
|
||||
try {
|
||||
console.log(`ALT: Creating`);
|
||||
const createIx = AddressLookupTableProgram.createLookupTable({
|
||||
authority: admin.publicKey,
|
||||
payer: admin.publicKey,
|
||||
recentSlot: await connection.getSlot('finalized'),
|
||||
});
|
||||
const createTx = await buildVersionedTx(
|
||||
client.program.provider as AnchorProvider,
|
||||
[createIx[0]],
|
||||
);
|
||||
let sig = await connection.sendTransaction(createTx);
|
||||
console.log(
|
||||
`...created ALT ${createIx[1]} https://explorer.solana.com/tx/${sig}`,
|
||||
);
|
||||
|
||||
console.log(`ALT: set at index 0 for group...`);
|
||||
sig = await client.altSet(group, createIx[1], 0);
|
||||
console.log(`...https://explorer.solana.com/tx/${sig}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Extend using mango v4 relevant pub keys
|
||||
try {
|
||||
let bankAddresses = Array.from(group.banksMapByMint.values())
|
||||
.flat()
|
||||
.map((bank) => [bank.publicKey, bank.oracle, bank.vault])
|
||||
.flat()
|
||||
.concat(
|
||||
Array.from(group.banksMapByMint.values())
|
||||
.flat()
|
||||
.map((mintInfo) => mintInfo.publicKey),
|
||||
);
|
||||
|
||||
let serum3MarketAddresses = Array.from(
|
||||
group.serum3MarketsMapByExternal.values(),
|
||||
)
|
||||
.flat()
|
||||
.map((serum3Market) => serum3Market.publicKey);
|
||||
|
||||
let serum3ExternalMarketAddresses = Array.from(
|
||||
group.serum3ExternalMarketsMap.values(),
|
||||
)
|
||||
.flat()
|
||||
.map((serum3ExternalMarket) => [
|
||||
serum3ExternalMarket.publicKey,
|
||||
serum3ExternalMarket.bidsAddress,
|
||||
serum3ExternalMarket.asksAddress,
|
||||
])
|
||||
.flat();
|
||||
|
||||
let perpMarketAddresses = Array.from(
|
||||
group.perpMarketsMapByMarketIndex.values(),
|
||||
)
|
||||
.flat()
|
||||
.map((perpMarket) => [
|
||||
perpMarket.publicKey,
|
||||
perpMarket.oracle,
|
||||
perpMarket.bids,
|
||||
perpMarket.asks,
|
||||
perpMarket.eventQueue,
|
||||
])
|
||||
.flat();
|
||||
|
||||
async function extendTable(addresses: PublicKey[]) {
|
||||
await group.reloadAll(client);
|
||||
const alt =
|
||||
await client.program.provider.connection.getAddressLookupTable(
|
||||
group.addressLookupTables[0],
|
||||
);
|
||||
|
||||
addresses = addresses.filter(
|
||||
(newAddress) =>
|
||||
alt.value?.state.addresses &&
|
||||
alt.value?.state.addresses.findIndex((addressInALt) =>
|
||||
addressInALt.equals(newAddress),
|
||||
) === -1,
|
||||
);
|
||||
if (addresses.length === 0) {
|
||||
return;
|
||||
}
|
||||
const extendIx = AddressLookupTableProgram.extendLookupTable({
|
||||
lookupTable: group.addressLookupTables[0],
|
||||
payer: admin.publicKey,
|
||||
authority: admin.publicKey,
|
||||
addresses,
|
||||
});
|
||||
const extendTx = await buildVersionedTx(
|
||||
client.program.provider as AnchorProvider,
|
||||
[extendIx],
|
||||
);
|
||||
let sig = await client.program.provider.connection.sendTransaction(
|
||||
extendTx,
|
||||
);
|
||||
console.log(`https://explorer.solana.com/tx/${sig}`);
|
||||
}
|
||||
|
||||
console.log(`ALT: extending using mango v4 relevant public keys`);
|
||||
await extendTable(bankAddresses);
|
||||
await extendTable(serum3MarketAddresses);
|
||||
await extendTable(serum3ExternalMarketAddresses);
|
||||
await extendTable(perpMarketAddresses);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// await createGroup();
|
||||
|
@ -448,22 +690,28 @@ async function main() {
|
|||
}
|
||||
try {
|
||||
// await registerTokens();
|
||||
// await unregisterTokens();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
try {
|
||||
// await registerSerum3Markets();
|
||||
// await unregisterSerum3Markets();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
try {
|
||||
// await createUser(process.env.MB_USER_KEYPAIR!);
|
||||
// await createUser(process.env.MB_USER2_KEYPAIR!);
|
||||
// await expandMangoAccount(process.env.MB_USER_KEYPAIR!);
|
||||
await placeSerum3TradeAndCancelIt(process.env.MB_USER_KEYPAIR!);
|
||||
// await createUser(MB_USER_KEYPAIR!);
|
||||
// await createUser(MB_USER2_KEYPAIR!);
|
||||
// await expandMangoAccount(MB_USER_KEYPAIR!);
|
||||
// await placeSerum3TradeAndCancelIt(MB_USER_KEYPAIR!);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
try {
|
||||
// await createAndPopulateAlt();
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { AnchorProvider, BN, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { Serum3Side } from '../accounts/serum3';
|
||||
|
@ -47,9 +47,11 @@ async function closeUserAccount(userKeypairFile: string) {
|
|||
try {
|
||||
// cancel serum3 accounts, closing might require cancelling orders and settling
|
||||
for (const serum3Account of mangoAccount.serum3Active()) {
|
||||
let orders = await client.getSerum3Orders(
|
||||
let orders = await mangoAccount.loadSerum3OpenOrdersForMarket(
|
||||
client,
|
||||
group,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
group.serum3MarketsMapByMarketIndex.get(serum3Account.marketIndex)
|
||||
?.serumMarketExternal!,
|
||||
);
|
||||
for (const order of orders) {
|
||||
console.log(
|
||||
|
@ -59,8 +61,8 @@ async function closeUserAccount(userKeypairFile: string) {
|
|||
await client.serum3CancelOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
|
||||
'BTC/USDC',
|
||||
group.serum3MarketsMapByMarketIndex.get(serum3Account.marketIndex)
|
||||
?.serumMarketExternal!,
|
||||
order.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
|
||||
order.orderId,
|
||||
);
|
||||
|
@ -69,23 +71,29 @@ async function closeUserAccount(userKeypairFile: string) {
|
|||
await client.serum3SettleFunds(
|
||||
group,
|
||||
mangoAccount,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
group.serum3MarketsMapByMarketIndex.get(serum3Account.marketIndex)
|
||||
?.serumMarketExternal!,
|
||||
);
|
||||
await client.serum3CloseOpenOrders(
|
||||
group,
|
||||
mangoAccount,
|
||||
group.getSerum3MarketByIndex(serum3Account.marketIndex)!.name,
|
||||
group.serum3MarketsMapByMarketIndex.get(serum3Account.marketIndex)
|
||||
?.serumMarketExternal!,
|
||||
);
|
||||
}
|
||||
|
||||
// we closed a serum account, this changes the health accounts we are passing in for future ixs
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
// withdraw all tokens
|
||||
for (const token of mangoAccount.tokensActive()) {
|
||||
const native = token.native(group.findBank(token.tokenIndex)!);
|
||||
const native = token.balance(
|
||||
group.getFirstBankByTokenIndex(token.tokenIndex)!,
|
||||
);
|
||||
console.log(
|
||||
`token native ${native} ${group.findBank(token.tokenIndex)!.name}`,
|
||||
`token native ${native} ${
|
||||
group.getFirstBankByTokenIndex(token.tokenIndex)!.name
|
||||
}`,
|
||||
);
|
||||
if (native.toNumber() < 1) {
|
||||
continue;
|
||||
|
@ -94,8 +102,12 @@ async function closeUserAccount(userKeypairFile: string) {
|
|||
await client.tokenWithdrawNative(
|
||||
group,
|
||||
mangoAccount,
|
||||
group.findBank(token.tokenIndex)!.name,
|
||||
token.native(group.findBank(token.tokenIndex)!).toNumber(),
|
||||
group.getFirstBankByTokenIndex(token.tokenIndex)!.mint,
|
||||
new BN(
|
||||
token
|
||||
.balance(group.getFirstBankByTokenIndex(token.tokenIndex)!)
|
||||
.toNumber(),
|
||||
),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
@ -103,7 +115,7 @@ async function closeUserAccount(userKeypairFile: string) {
|
|||
console.log(error);
|
||||
}
|
||||
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
console.log(`...mangoAccount ${mangoAccount.publicKey}`);
|
||||
console.log(mangoAccount.toString());
|
||||
|
||||
|
|
|
@ -115,16 +115,18 @@ async function main() {
|
|||
console.log(
|
||||
'mangoAccount.simHealthWithTokenPositionChanges ' +
|
||||
toUiDecimalsForQuote(
|
||||
await mangoAccount.simHealthRatioWithTokenPositionUiChanges(group, [
|
||||
mangoAccount.simHealthRatioWithTokenPositionUiChanges(group, [
|
||||
{
|
||||
mintPk: group.banksMapByName.get('USDC')![0].mint,
|
||||
uiTokenAmount:
|
||||
-20_000 * Math.pow(10, group.banksMap.get('BTC')?.mintDecimals!),
|
||||
-20000 *
|
||||
Math.pow(10, group.banksMapByName.get('BTC')![0].mintDecimals!),
|
||||
},
|
||||
{
|
||||
mintPk: group.banksMapByName.get('BTC')![0].mint,
|
||||
uiTokenAmount:
|
||||
1 * Math.pow(10, group.banksMap.get('BTC')?.mintDecimals!),
|
||||
1 *
|
||||
Math.pow(10, group.banksMapByName.get('BTC')![0].mintDecimals!),
|
||||
},
|
||||
]),
|
||||
),
|
||||
|
|
|
@ -51,8 +51,9 @@ async function main() {
|
|||
adminProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
// group
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
|
@ -28,8 +28,9 @@ async function main() {
|
|||
userProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
|
@ -59,15 +60,15 @@ async function main() {
|
|||
try {
|
||||
console.log(`...depositing 5 USDC`);
|
||||
await client.tokenDeposit(group, mangoAccount, usdcMint, 5);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
console.log(`...depositing 0.0002 BTC`);
|
||||
await client.tokenDeposit(group, mangoAccount, btcMint, 0.0002);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
console.log(`...depositing 0.15 SOL`);
|
||||
await client.tokenDeposit(group, mangoAccount, solMint, 0.15);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { AnchorProvider, BN, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { MangoAccount } from '../accounts/mangoAccount';
|
||||
import { PerpOrderSide, PerpOrderType } from '../accounts/perp';
|
||||
import {
|
||||
Serum3OrderType,
|
||||
Serum3SelfTradeBehavior,
|
||||
Serum3Side,
|
||||
} from '../accounts/serum3';
|
||||
import { Side, PerpOrderType } from '../accounts/perp';
|
||||
import { MangoAccount } from '../accounts/mangoAccount';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
|
||||
|
@ -56,8 +56,9 @@ async function main() {
|
|||
userProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
|
@ -105,9 +106,9 @@ async function main() {
|
|||
group,
|
||||
mangoAccount,
|
||||
assetMint,
|
||||
assetAmount,
|
||||
new BN(assetAmount),
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
if (liabAmount > 0) {
|
||||
// temporarily drop the borrowed token value, so the borrow goes through
|
||||
|
@ -119,7 +120,7 @@ async function main() {
|
|||
group,
|
||||
mangoAccount,
|
||||
liabMint,
|
||||
liabAmount,
|
||||
new BN(liabAmount),
|
||||
true,
|
||||
);
|
||||
} finally {
|
||||
|
@ -139,12 +140,17 @@ async function main() {
|
|||
`...created mangoAccount ${mangoAccount.publicKey} for ${name}`,
|
||||
);
|
||||
|
||||
const market = group.getSerum3MarketByIndexByName('SOL/USDC')!;
|
||||
const market = group.getSerum3MarketByName('SOL/USDC')!;
|
||||
const sellMint = new PublicKey(MAINNET_MINTS.get('USDC')!);
|
||||
const buyMint = new PublicKey(MAINNET_MINTS.get('SOL')!);
|
||||
|
||||
await client.tokenDepositNative(group, mangoAccount, sellMint, 100000);
|
||||
await mangoAccount.reload(client, group);
|
||||
await client.tokenDepositNative(
|
||||
group,
|
||||
mangoAccount,
|
||||
sellMint,
|
||||
new BN(100000),
|
||||
);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
// temporarily up the init asset weight of the bought token
|
||||
await client.tokenEdit(
|
||||
|
@ -215,9 +221,9 @@ async function main() {
|
|||
group,
|
||||
mangoAccount,
|
||||
collateralMint,
|
||||
100000,
|
||||
new BN(100000),
|
||||
); // valued as $0.004 maint collateral
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
await client.stubOracleSet(group, collateralOracle, PRICES['SOL'] * 4);
|
||||
|
||||
|
@ -225,8 +231,8 @@ async function main() {
|
|||
await client.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'MNGO-PERP',
|
||||
Side.bid,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
PerpOrderSide.bid,
|
||||
1, // ui price that won't get hit
|
||||
0.0011, // ui base quantity, 11 base lots, $0.044
|
||||
0.044, // ui quote quantity
|
||||
|
@ -258,9 +264,9 @@ async function main() {
|
|||
group,
|
||||
mangoAccount,
|
||||
collateralMint,
|
||||
100000,
|
||||
new BN(100000),
|
||||
); // valued as $0.004 maint collateral
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
await client.stubOracleSet(group, collateralOracle, PRICES['SOL'] * 5);
|
||||
|
||||
|
@ -268,8 +274,8 @@ async function main() {
|
|||
await client.perpPlaceOrder(
|
||||
group,
|
||||
fundingAccount,
|
||||
'MNGO-PERP',
|
||||
Side.ask,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
PerpOrderSide.ask,
|
||||
40,
|
||||
0.0011, // ui base quantity, 11 base lots, $0.044
|
||||
0.044, // ui quote quantity
|
||||
|
@ -282,8 +288,8 @@ async function main() {
|
|||
await client.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'MNGO-PERP',
|
||||
Side.bid,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
PerpOrderSide.bid,
|
||||
40,
|
||||
0.0011, // ui base quantity, 11 base lots, $0.044
|
||||
0.044, // ui quote quantity
|
||||
|
@ -293,7 +299,10 @@ async function main() {
|
|||
5,
|
||||
);
|
||||
|
||||
await client.perpConsumeAllEvents(group, 'MNGO-PERP');
|
||||
await client.perpConsumeAllEvents(
|
||||
group,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
);
|
||||
} finally {
|
||||
await client.stubOracleSet(group, collateralOracle, PRICES['SOL']);
|
||||
}
|
||||
|
@ -320,9 +329,9 @@ async function main() {
|
|||
group,
|
||||
mangoAccount,
|
||||
collateralMint,
|
||||
100000,
|
||||
new BN(100000),
|
||||
); // valued as $0.004 maint collateral
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
try {
|
||||
await client.stubOracleSet(group, collateralOracle, PRICES['SOL'] * 10);
|
||||
|
@ -332,18 +341,18 @@ async function main() {
|
|||
group,
|
||||
mangoAccount,
|
||||
liabMint,
|
||||
-5000,
|
||||
new BN(-5000),
|
||||
true,
|
||||
);
|
||||
await mangoAccount.reload(client, group);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
// Execute two trades that leave the account with +$0.022 positive pnl
|
||||
await client.stubOracleSet(group, baseOracle, PRICES['MNGO'] / 2);
|
||||
await client.perpPlaceOrder(
|
||||
group,
|
||||
fundingAccount,
|
||||
'MNGO-PERP',
|
||||
Side.ask,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
PerpOrderSide.ask,
|
||||
20,
|
||||
0.0011, // ui base quantity, 11 base lots, $0.022
|
||||
0.022, // ui quote quantity
|
||||
|
@ -355,8 +364,8 @@ async function main() {
|
|||
await client.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'MNGO-PERP',
|
||||
Side.bid,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
PerpOrderSide.bid,
|
||||
20,
|
||||
0.0011, // ui base quantity, 11 base lots, $0.022
|
||||
0.022, // ui quote quantity
|
||||
|
@ -365,15 +374,18 @@ async function main() {
|
|||
0,
|
||||
5,
|
||||
);
|
||||
await client.perpConsumeAllEvents(group, 'MNGO-PERP');
|
||||
await client.perpConsumeAllEvents(
|
||||
group,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
);
|
||||
|
||||
await client.stubOracleSet(group, baseOracle, PRICES['MNGO']);
|
||||
|
||||
await client.perpPlaceOrder(
|
||||
group,
|
||||
fundingAccount,
|
||||
'MNGO-PERP',
|
||||
Side.bid,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
PerpOrderSide.bid,
|
||||
40,
|
||||
0.0011, // ui base quantity, 11 base lots, $0.044
|
||||
0.044, // ui quote quantity
|
||||
|
@ -385,8 +397,8 @@ async function main() {
|
|||
await client.perpPlaceOrder(
|
||||
group,
|
||||
mangoAccount,
|
||||
'MNGO-PERP',
|
||||
Side.ask,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
PerpOrderSide.ask,
|
||||
40,
|
||||
0.0011, // ui base quantity, 11 base lots, $0.044
|
||||
0.044, // ui quote quantity
|
||||
|
@ -395,7 +407,10 @@ async function main() {
|
|||
0,
|
||||
5,
|
||||
);
|
||||
await client.perpConsumeAllEvents(group, 'MNGO-PERP');
|
||||
await client.perpConsumeAllEvents(
|
||||
group,
|
||||
group.perpMarketsMapByName.get('MNGO-PERP')?.perpMarketIndex!,
|
||||
);
|
||||
} finally {
|
||||
await client.stubOracleSet(group, collateralOracle, PRICES['SOL']);
|
||||
await client.stubOracleSet(group, baseOracle, PRICES['MNGO']);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { BN, AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { MangoClient } from '../client';
|
||||
import { Side, PerpOrderType } from '../accounts/perp';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
|
||||
//
|
||||
|
@ -32,8 +31,9 @@ async function main() {
|
|||
userProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
|
@ -49,14 +49,14 @@ async function main() {
|
|||
let accounts = await client.getMangoAccountsForOwner(group, admin.publicKey);
|
||||
for (let account of accounts) {
|
||||
for (let serumOrders of account.serum3Active()) {
|
||||
const serumMarket = group.getSerum3MarketByIndex(
|
||||
const serumMarket = group.getSerum3MarketByMarketIndex(
|
||||
serumOrders.marketIndex,
|
||||
)!;
|
||||
const serumExternal = serumMarket.serumMarketExternal;
|
||||
console.log(
|
||||
`closing serum orders on: ${account} for market ${serumMarket.name}`,
|
||||
);
|
||||
await client.serum3CancelAllorders(group, account, serumExternal, 10);
|
||||
await client.serum3CancelAllOrders(group, account, serumExternal, 10);
|
||||
await client.serum3SettleFunds(group, account, serumExternal);
|
||||
await client.serum3CloseOpenOrders(group, account, serumExternal);
|
||||
}
|
||||
|
@ -66,7 +66,12 @@ async function main() {
|
|||
console.log(
|
||||
`closing perp orders on: ${account} for market ${perpMarket.name}`,
|
||||
);
|
||||
await client.perpCancelAllOrders(group, account, perpMarket.name, 10);
|
||||
await client.perpCancelAllOrders(
|
||||
group,
|
||||
account,
|
||||
perpMarket.perpMarketIndex,
|
||||
10,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import { AnchorProvider, Wallet } from '@project-serum/anchor';
|
||||
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { Serum3Side } from '../accounts/serum3';
|
||||
import { MangoClient } from '../client';
|
||||
import { MANGO_V4_ID } from '../constants';
|
||||
|
||||
//
|
||||
// (untested?) script which closes a mango account cleanly, first closes all positions, withdraws all tokens and then closes it
|
||||
//
|
||||
async function viewUnownedAccount(userKeypairFile: string) {
|
||||
const options = AnchorProvider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
'https://mango.rpcpool.com/0f9acc0d45173b51bf7d7e09c1e5',
|
||||
options,
|
||||
);
|
||||
|
||||
// user
|
||||
const userWallet = new Wallet(Keypair.generate());
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
'mainnet-beta',
|
||||
MANGO_V4_ID['mainnet-beta'],
|
||||
);
|
||||
console.log(`User ${userWallet.publicKey.toBase58()}`);
|
||||
|
||||
// admin
|
||||
const admin = Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(fs.readFileSync(process.env.MB_PAYER_KEYPAIR || '', 'utf-8')),
|
||||
),
|
||||
);
|
||||
console.log(`Admin ${admin.publicKey.toBase58()}`);
|
||||
|
||||
// fetch group
|
||||
const group = await client.getGroupForCreator(admin.publicKey, 2);
|
||||
console.log(`Found group ${group.publicKey.toBase58()}`);
|
||||
|
||||
const x = await client.getMangoAccount(
|
||||
new PublicKey('6cTqJrSzQZWGEeHHePqFuJV4Kf54YDVfSamdCrT3agw6'),
|
||||
);
|
||||
const y = await x.reloadAccountData(client, group);
|
||||
|
||||
process.exit();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await viewUnownedAccount(process.env.MB_USER2_KEYPAIR || '');
|
||||
}
|
||||
|
||||
main();
|
|
@ -22,8 +22,9 @@ async function main() {
|
|||
userProvider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
// Load mango account
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
TransactionInstruction,
|
||||
} from '@solana/web3.js';
|
||||
import fs from 'fs';
|
||||
import { RestClient } from 'ftx-api';
|
||||
import path from 'path';
|
||||
import { Group } from '../../accounts/group';
|
||||
import { MangoAccount } from '../../accounts/mangoAccount';
|
||||
|
@ -27,7 +28,12 @@ import {
|
|||
seqEnforcerProgramIds,
|
||||
} from './sequence-enforcer-util';
|
||||
|
||||
// TODO switch to more efficient async logging if available in nodejs
|
||||
// Future
|
||||
// * use async nodejs logging
|
||||
// * merge gMa calls
|
||||
// * take out spammers
|
||||
// * batch ixs across various markets
|
||||
// * only refresh part of the group which market maker is interested in
|
||||
|
||||
// Env vars
|
||||
const CLUSTER: Cluster =
|
||||
|
@ -57,14 +63,14 @@ type State = {
|
|||
};
|
||||
type MarketContext = {
|
||||
params: any;
|
||||
market: PerpMarket;
|
||||
perpMarket: PerpMarket;
|
||||
bids: BookSide;
|
||||
asks: BookSide;
|
||||
lastBookUpdate: number;
|
||||
|
||||
aggBid: number | undefined;
|
||||
aggAsk: number | undefined;
|
||||
ftxMid: number | undefined;
|
||||
ftxBid: number | undefined;
|
||||
ftxAsk: number | undefined;
|
||||
ftxLast: number | undefined;
|
||||
|
||||
sequenceAccount: PublicKey;
|
||||
sequenceAccountBump: number;
|
||||
|
@ -74,7 +80,18 @@ type MarketContext = {
|
|||
lastOrderUpdate: number;
|
||||
};
|
||||
|
||||
// Refresh mango account and perp market order books
|
||||
const ftxClient = new RestClient();
|
||||
|
||||
function getPerpMarketAssetsToTradeOn(group: Group) {
|
||||
const allMangoGroupPerpMarketAssets = Array.from(
|
||||
group.perpMarketsMapByName.keys(),
|
||||
).map((marketName) => marketName.replace('-PERP', ''));
|
||||
return Object.keys(params.assets).filter((asset) =>
|
||||
allMangoGroupPerpMarketAssets.includes(asset),
|
||||
);
|
||||
}
|
||||
|
||||
// Refresh group, mango account and perp markets
|
||||
async function refreshState(
|
||||
client: MangoClient,
|
||||
group: Group,
|
||||
|
@ -83,18 +100,27 @@ async function refreshState(
|
|||
): Promise<State> {
|
||||
const ts = Date.now() / 1000;
|
||||
|
||||
// TODO do all updates in one RPC call
|
||||
await Promise.all([group.reloadAll(client), mangoAccount.reload(client)]);
|
||||
const result = await Promise.all([
|
||||
group.reloadAll(client),
|
||||
mangoAccount.reload(client),
|
||||
...Array.from(marketContexts.values()).map((mc) =>
|
||||
ftxClient.getMarket(mc.perpMarket.name),
|
||||
),
|
||||
]);
|
||||
|
||||
for (const perpMarket of Array.from(
|
||||
group.perpMarketsMapByMarketIndex.values(),
|
||||
)) {
|
||||
const mc = marketContexts.get(perpMarket.perpMarketIndex)!;
|
||||
mc.market = perpMarket;
|
||||
Array.from(marketContexts.values()).map(async (mc, i) => {
|
||||
const perpMarket = mc.perpMarket;
|
||||
mc.perpMarket = group.getPerpMarketByMarketIndex(
|
||||
perpMarket.perpMarketIndex,
|
||||
);
|
||||
mc.bids = await perpMarket.loadBids(client);
|
||||
mc.asks = await perpMarket.loadAsks(client);
|
||||
mc.lastBookUpdate = ts;
|
||||
}
|
||||
|
||||
mc.ftxAsk = (result[i + 2] as any).result.ask;
|
||||
mc.ftxBid = (result[i + 2] as any).result.bid;
|
||||
mc.ftxLast = (result[i + 2] as any).result.last;
|
||||
});
|
||||
|
||||
return {
|
||||
mangoAccount,
|
||||
|
@ -113,7 +139,7 @@ async function initSequenceEnforcerAccounts(
|
|||
mc.sequenceAccount,
|
||||
(client.program.provider as AnchorProvider).wallet.publicKey,
|
||||
mc.sequenceAccountBump,
|
||||
mc.market.name,
|
||||
mc.perpMarket.name,
|
||||
CLUSTER,
|
||||
),
|
||||
);
|
||||
|
@ -138,6 +164,40 @@ async function initSequenceEnforcerAccounts(
|
|||
}
|
||||
}
|
||||
|
||||
async function cancelAllOrdersForAMarket(
|
||||
client: MangoClient,
|
||||
group: Group,
|
||||
mangoAccount: MangoAccount,
|
||||
perpMarket: PerpMarket,
|
||||
) {
|
||||
for (const i of Array(100).keys()) {
|
||||
await sendTransaction(
|
||||
client.program.provider as AnchorProvider,
|
||||
[
|
||||
await client.perpCancelAllOrdersIx(
|
||||
group,
|
||||
mangoAccount,
|
||||
perpMarket.perpMarketIndex,
|
||||
10,
|
||||
),
|
||||
],
|
||||
[],
|
||||
);
|
||||
await mangoAccount.reload(client);
|
||||
if (
|
||||
(
|
||||
await mangoAccount.loadPerpOpenOrdersForMarket(
|
||||
client,
|
||||
group,
|
||||
perpMarket.perpMarketIndex,
|
||||
)
|
||||
).length === 0
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel all orders on exit
|
||||
async function onExit(
|
||||
client: MangoClient,
|
||||
|
@ -145,17 +205,9 @@ async function onExit(
|
|||
mangoAccount: MangoAccount,
|
||||
marketContexts: MarketContext[],
|
||||
) {
|
||||
const ixs: TransactionInstruction[] = [];
|
||||
for (const mc of marketContexts) {
|
||||
const cancelAllIx = await client.perpCancelAllOrdersIx(
|
||||
group,
|
||||
mangoAccount,
|
||||
mc.market.perpMarketIndex,
|
||||
10,
|
||||
);
|
||||
ixs.push(cancelAllIx);
|
||||
cancelAllOrdersForAMarket(client, group, mangoAccount, mc.perpMarket);
|
||||
}
|
||||
await sendTransaction(client.program.provider as AnchorProvider, ixs, []);
|
||||
}
|
||||
|
||||
// Main driver for the market maker
|
||||
|
@ -166,15 +218,15 @@ async function fullMarketMaker() {
|
|||
const user = Keypair.fromSecretKey(
|
||||
Buffer.from(JSON.parse(fs.readFileSync(USER_KEYPAIR!, 'utf-8'))),
|
||||
);
|
||||
// TODO: make work for delegate
|
||||
const userWallet = new Wallet(user);
|
||||
const userProvider = new AnchorProvider(connection, userWallet, options);
|
||||
const client = await MangoClient.connect(
|
||||
userProvider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
// Load mango account
|
||||
|
@ -182,7 +234,9 @@ async function fullMarketMaker() {
|
|||
new PublicKey(MANGO_ACCOUNT_PK),
|
||||
);
|
||||
console.log(
|
||||
`MangoAccount ${mangoAccount.publicKey} for user ${user.publicKey}`,
|
||||
`MangoAccount ${mangoAccount.publicKey} for user ${user.publicKey} ${
|
||||
mangoAccount.isDelegate(client) ? 'via delegate ' + user.publicKey : ''
|
||||
}`,
|
||||
);
|
||||
await mangoAccount.reload(client);
|
||||
|
||||
|
@ -204,9 +258,8 @@ async function fullMarketMaker() {
|
|||
|
||||
// Build and maintain an aggregate context object per market
|
||||
const marketContexts: Map<PerpMarketIndex, MarketContext> = new Map();
|
||||
for (const perpMarket of Array.from(
|
||||
group.perpMarketsMapByMarketIndex.values(),
|
||||
)) {
|
||||
for (const perpMarketAsset of getPerpMarketAssetsToTradeOn(group)) {
|
||||
const perpMarket = group.getPerpMarketByName(perpMarketAsset + '-PERP');
|
||||
const [sequenceAccount, sequenceAccountBump] =
|
||||
await PublicKey.findProgramAddress(
|
||||
[
|
||||
|
@ -218,8 +271,8 @@ async function fullMarketMaker() {
|
|||
seqEnforcerProgramIds[CLUSTER],
|
||||
);
|
||||
marketContexts.set(perpMarket.perpMarketIndex, {
|
||||
params: params.assets[perpMarket.name.replace('-PERP', '')].perp,
|
||||
market: perpMarket,
|
||||
params: params.assets[perpMarketAsset].perp,
|
||||
perpMarket: perpMarket,
|
||||
bids: await perpMarket.loadBids(client),
|
||||
asks: await perpMarket.loadAsks(client),
|
||||
lastBookUpdate: 0,
|
||||
|
@ -231,10 +284,9 @@ async function fullMarketMaker() {
|
|||
sentAskPrice: 0,
|
||||
lastOrderUpdate: 0,
|
||||
|
||||
// TODO
|
||||
aggBid: undefined,
|
||||
aggAsk: undefined,
|
||||
ftxMid: undefined,
|
||||
ftxBid: undefined,
|
||||
ftxAsk: undefined,
|
||||
ftxLast: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -257,8 +309,9 @@ async function fullMarketMaker() {
|
|||
// Loop indefinitely
|
||||
while (control.isRunning) {
|
||||
try {
|
||||
// TODO update this in a non blocking manner
|
||||
state = await refreshState(client, group, mangoAccount, marketContexts);
|
||||
refreshState(client, group, mangoAccount, marketContexts).then(
|
||||
(result) => (state = result),
|
||||
);
|
||||
|
||||
mangoAccount = state.mangoAccount;
|
||||
|
||||
|
@ -267,16 +320,15 @@ async function fullMarketMaker() {
|
|||
for (const mc of Array.from(marketContexts.values())) {
|
||||
const pos = mangoAccount.getPerpPositionUi(
|
||||
group,
|
||||
mc.market.perpMarketIndex,
|
||||
mc.perpMarket.perpMarketIndex,
|
||||
);
|
||||
// TODO use ftx to get mid then also combine with books from other exchanges
|
||||
const midWorkaround = mc.market.uiPrice;
|
||||
if (midWorkaround) {
|
||||
pfQuoteValue += pos * midWorkaround;
|
||||
const mid = (mc.ftxBid! + mc.ftxAsk!) / 2;
|
||||
if (mid) {
|
||||
pfQuoteValue += pos * mid;
|
||||
} else {
|
||||
pfQuoteValue = undefined;
|
||||
console.log(
|
||||
`Breaking pfQuoteValue computation, since mid is undefined for ${mc.market.name}!`,
|
||||
`Breaking pfQuoteValue computation, since mid is undefined for ${mc.perpMarket.name}!`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
@ -303,7 +355,6 @@ async function fullMarketMaker() {
|
|||
continue;
|
||||
}
|
||||
|
||||
// TODO: batch ixs
|
||||
const sig = await sendTransaction(
|
||||
client.program.provider as AnchorProvider,
|
||||
ixs,
|
||||
|
@ -333,13 +384,13 @@ async function makeMarketUpdateInstructions(
|
|||
mc: MarketContext,
|
||||
pfQuoteValue: number,
|
||||
): Promise<TransactionInstruction[]> {
|
||||
const perpMarketIndex = mc.market.perpMarketIndex;
|
||||
const perpMarket = mc.market;
|
||||
const perpMarketIndex = mc.perpMarket.perpMarketIndex;
|
||||
const perpMarket = mc.perpMarket;
|
||||
|
||||
const aggBid = perpMarket.uiPrice; // TODO mc.aggBid;
|
||||
const aggAsk = perpMarket.uiPrice; // TODO mc.aggAsk;
|
||||
const aggBid = mc.ftxBid;
|
||||
const aggAsk = mc.ftxAsk;
|
||||
if (aggBid === undefined || aggAsk === undefined) {
|
||||
console.log(`No Aggregate Book for ${mc.market.name}!`);
|
||||
console.log(`No Aggregate Book for ${mc.perpMarket.name}!`);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -353,8 +404,7 @@ async function makeMarketUpdateInstructions(
|
|||
const sizePerc = mc.params.sizePerc;
|
||||
const quoteSize = equity * sizePerc;
|
||||
const size = quoteSize / fairValue;
|
||||
// TODO look at event queue as well for unprocessed fills
|
||||
const basePos = mangoAccount.getPerpPositionUi(group, perpMarketIndex);
|
||||
const basePos = mangoAccount.getPerpPositionUi(group, perpMarketIndex, true);
|
||||
const lean = (-leanCoeff * basePos) / size;
|
||||
const pfQuoteLeanCoeff = params.pfQuoteLeanCoeff || 0.001; // How much to move if pf pos is equal to equity
|
||||
const pfQuoteLean = (pfQuoteValue / equity) * -pfQuoteLeanCoeff;
|
||||
|
@ -362,9 +412,6 @@ async function makeMarketUpdateInstructions(
|
|||
const bias = mc.params.bias;
|
||||
const bidPrice = fairValue * (1 - charge + lean + bias + pfQuoteLean);
|
||||
const askPrice = fairValue * (1 + charge + lean + bias + pfQuoteLean);
|
||||
|
||||
// TODO volatility adjustment
|
||||
|
||||
const modelBidPrice = perpMarket.uiPriceToLots(bidPrice);
|
||||
const nativeBidSize = perpMarket.uiBaseToLots(size);
|
||||
const modelAskPrice = perpMarket.uiPriceToLots(askPrice);
|
||||
|
@ -383,15 +430,8 @@ async function makeMarketUpdateInstructions(
|
|||
? BN.max(bestBid.priceLots.add(new BN(1)), modelAskPrice)
|
||||
: modelAskPrice;
|
||||
|
||||
// TODO use order book to requote if size has changed
|
||||
|
||||
// TODO
|
||||
// const takeSpammers = mc.params.takeSpammers;
|
||||
// const spammerCharge = mc.params.spammerCharge;
|
||||
|
||||
let moveOrders = false;
|
||||
if (mc.lastBookUpdate >= mc.lastOrderUpdate + 2) {
|
||||
// console.log(` - moveOrders - 303`);
|
||||
// If mango book was updated recently, then MangoAccount was also updated
|
||||
const openOrders = await mangoAccount.loadPerpOpenOrdersForMarket(
|
||||
client,
|
||||
|
@ -407,9 +447,6 @@ async function makeMarketUpdateInstructions(
|
|||
requoteThresh;
|
||||
}
|
||||
} else {
|
||||
// console.log(
|
||||
// ` - moveOrders - 319, mc.lastBookUpdate ${mc.lastBookUpdate}, mc.lastOrderUpdate ${mc.lastOrderUpdate}`,
|
||||
// );
|
||||
// If order was updated before MangoAccount, then assume that sent order already executed
|
||||
moveOrders =
|
||||
moveOrders ||
|
||||
|
@ -427,7 +464,9 @@ async function makeMarketUpdateInstructions(
|
|||
),
|
||||
];
|
||||
|
||||
// TODO Clear 1 lot size orders at the top of book that bad people use to manipulate the price
|
||||
instructions.push(
|
||||
await client.healthRegionBeginIx(group, mangoAccount, [], [perpMarket]),
|
||||
);
|
||||
|
||||
if (moveOrders) {
|
||||
// Cancel all, requote
|
||||
|
@ -446,7 +485,6 @@ async function makeMarketUpdateInstructions(
|
|||
mangoAccount,
|
||||
perpMarketIndex,
|
||||
PerpOrderSide.bid,
|
||||
// TODO fix this, native to ui to native
|
||||
perpMarket.priceLotsToUi(bookAdjBid),
|
||||
perpMarket.baseLotsToUi(nativeBidSize),
|
||||
undefined,
|
||||
|
@ -479,7 +517,7 @@ async function makeMarketUpdateInstructions(
|
|||
instructions.push(placeAskIx);
|
||||
}
|
||||
console.log(
|
||||
`Requoting for market ${mc.market.name} sentBid: ${
|
||||
`Requoting for market ${mc.perpMarket.name} sentBid: ${
|
||||
mc.sentBidPrice
|
||||
} newBid: ${bookAdjBid} sentAsk: ${
|
||||
mc.sentAskPrice
|
||||
|
@ -492,12 +530,16 @@ async function makeMarketUpdateInstructions(
|
|||
mc.lastOrderUpdate = Date.now() / 1000;
|
||||
} else {
|
||||
console.log(
|
||||
`Not requoting for market ${mc.market.name}. No need to move orders`,
|
||||
`Not requoting for market ${mc.perpMarket.name}. No need to move orders`,
|
||||
);
|
||||
}
|
||||
|
||||
// If instruction is only the sequence enforcement, then just send empty
|
||||
if (instructions.length === 1) {
|
||||
instructions.push(
|
||||
await client.healthRegionEndIx(group, mangoAccount, [], [perpMarket]),
|
||||
);
|
||||
|
||||
// If instruction is only the sequence enforcement and health region ixs, then just send empty
|
||||
if (instructions.length === 3) {
|
||||
return [];
|
||||
} else {
|
||||
return instructions;
|
||||
|
|
|
@ -86,8 +86,9 @@ async function main() {
|
|||
userProvider,
|
||||
CLUSTER,
|
||||
MANGO_V4_ID[CLUSTER],
|
||||
{},
|
||||
'get-program-accounts',
|
||||
{
|
||||
idsSource: 'get-program-accounts',
|
||||
},
|
||||
);
|
||||
|
||||
// Load mango account
|
||||
|
|
|
@ -34,9 +34,11 @@ export function toUiDecimals(
|
|||
nativeAmount: BN | I80F48 | number,
|
||||
decimals: number,
|
||||
): number {
|
||||
// TODO: remove BN and upgrade to bigint https://github.com/solana-labs/solana/issues/27440
|
||||
if (nativeAmount instanceof BN) {
|
||||
return nativeAmount.div(new BN(Math.pow(10, decimals))).toNumber();
|
||||
} else if (nativeAmount instanceof I80F48) {
|
||||
nativeAmount = I80F48.fromU64(nativeAmount);
|
||||
}
|
||||
if (nativeAmount instanceof I80F48) {
|
||||
return nativeAmount
|
||||
.div(I80F48.fromNumber(Math.pow(10, decimals)))
|
||||
.toNumber();
|
||||
|
|
|
@ -45,8 +45,6 @@ export async function sendTransaction(
|
|||
latestBlockhash.blockhash != null &&
|
||||
latestBlockhash.lastValidBlockHeight != null
|
||||
) {
|
||||
// TODO: tyler, can we remove these?
|
||||
console.log('confirming via blockhash');
|
||||
status = (
|
||||
await connection.confirmTransaction(
|
||||
{
|
||||
|
@ -58,8 +56,6 @@ export async function sendTransaction(
|
|||
)
|
||||
).value;
|
||||
} else {
|
||||
// TODO: tyler, can we remove these?
|
||||
console.log('confirming via timeout');
|
||||
status = (await connection.confirmTransaction(signature, 'processed'))
|
||||
.value;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"lib": ["es2019"],
|
||||
"lib": [
|
||||
"es2019"
|
||||
],
|
||||
"outDir": "./dist",
|
||||
"resolveJsonModule": true,
|
||||
"noImplicitAny": false,
|
||||
|
@ -11,12 +13,14 @@
|
|||
"target": "es2019",
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": ["ts/client/src", "ts/client/scripts", "ts/client/scripts"],
|
||||
"include": [
|
||||
"ts/client/src",
|
||||
"ts/client/src/scripts",
|
||||
"ts/client/src/debug-scripts",
|
||||
"ts/client/src/deployment-scripts"
|
||||
],
|
||||
"exclude": [
|
||||
"./ts/**/*.test.js",
|
||||
"node_modules",
|
||||
"**/node_modules",
|
||||
"./ts/client/src/scripts",
|
||||
"./ts/client/src/debug-scripts"
|
||||
]
|
||||
}
|
||||
}
|
13
yarn.lock
13
yarn.lock
|
@ -1027,7 +1027,7 @@ available-typed-arrays@^1.0.5:
|
|||
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
|
||||
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
|
||||
|
||||
axios@^0.21.1:
|
||||
axios@^0.21.1, axios@^0.21.4:
|
||||
version "0.21.4"
|
||||
resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz"
|
||||
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
|
||||
|
@ -1904,6 +1904,15 @@ fsevents@~2.3.2:
|
|||
resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
|
||||
ftx-api@^1.1.13:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ftx-api/-/ftx-api-1.1.13.tgz#89ced81182020c80bcf5a97082fd3b2b62d1fea3"
|
||||
integrity sha512-YZdiU4QN2H8wId4Bglb/Yj6hJrLImKv/yEIj3jw1pHMb1Bv/kdM25mlr+t+TqGPWy2prX/ywsh1+OVVI6G5jhA==
|
||||
dependencies:
|
||||
axios "^0.21.4"
|
||||
isomorphic-ws "^4.0.1"
|
||||
ws "^7.4.0"
|
||||
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
|
@ -3465,7 +3474,7 @@ wrappy@1:
|
|||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
ws@^7.4.5:
|
||||
ws@^7.4.0, ws@^7.4.5:
|
||||
version "7.5.9"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
|
||||
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
|
||||
|
|
Loading…
Reference in New Issue