Merge branch 'deploy-v0.21.0' into dev

This commit is contained in:
Christian Kamm 2023-12-08 11:39:01 +01:00
commit 0e8fc5e10e
13 changed files with 953 additions and 215 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@blockworks-foundation/mango-v4",
"version": "0.19.43",
"version": "0.20.9",
"description": "Typescript Client for mango-v4 program.",
"repository": "https://github.com/blockworks-foundation/mango-v4",
"author": {
@ -27,6 +27,7 @@
"lint": "eslint ./ts/client/src --ext ts --ext tsx --ext js --quiet",
"typecheck": "tsc --noEmit --pretty",
"prepublishOnly": "yarn validate && yarn build",
"deduplicate": "npx yarn-deduplicate --list --fail",
"validate": "yarn lint && yarn format"
},
"devDependencies": {

View File

@ -3,10 +3,10 @@ import { getAccount } from '@solana/spl-token';
import { Cluster, Connection, Keypair } from '@solana/web3.js';
import * as dotenv from 'dotenv';
import fs from 'fs';
import { MangoClient } from '../../src/client';
import { MANGO_V4_ID } from '../../src/constants';
import { I80F48, ZERO_I80F48 } from '../../src/numbers/I80F48';
import { toUiDecimals } from '../../src/utils';
import { MangoClient } from '../src/client';
import { MANGO_V4_ID } from '../src/constants';
import { I80F48, ZERO_I80F48 } from '../src/numbers/I80F48';
import { toUiDecimals } from '../src/utils';
dotenv.config();
const CLUSTER_URL =
@ -42,13 +42,14 @@ async function main(): Promise<void> {
banks.map((bank) => {
(bank as any).indexedDepositsByMangoAccounts = ZERO_I80F48();
(bank as any).indexedBorrowsByMangoAccounts = ZERO_I80F48();
(bank as any).serum3Total = ZERO_I80F48();
return [bank.tokenIndex, bank];
}),
);
const mangoAccounts = await client.getAllMangoAccounts(group);
const mangoAccounts = await client.getAllMangoAccounts(group, true);
mangoAccounts.map((mangoAccount) =>
mangoAccounts.map((mangoAccount) => {
mangoAccount.tokensActive().forEach((token) => {
const bank = banksMapUsingTokenIndex.get(token.tokenIndex);
if (token.indexedPosition.isPos()) {
@ -69,8 +70,21 @@ async function main(): Promise<void> {
.mul(banksMapUsingTokenIndex.get(token.tokenIndex)!.borrowIndex),
);
}
}),
);
});
mangoAccount.serum3Active().map((s3a) => {
const baseBank = group.getFirstBankByTokenIndex(s3a.baseTokenIndex);
const quoteBank = group.getFirstBankByTokenIndex(s3a.quoteTokenIndex);
const oo = mangoAccount.serum3OosMapByMarketIndex.get(s3a.marketIndex);
(baseBank as any).serum3Total = (baseBank as any).serum3Total.add(
I80F48.fromU64(oo!.baseTokenTotal),
);
(quoteBank as any).serum3Total = (quoteBank as any).serum3Total.add(
I80F48.fromU64(oo!.quoteTokenTotal),
);
});
});
for (const bank of await Array.from(banksMapUsingTokenIndex.values()).sort(
(a, b) => a.tokenIndex - b.tokenIndex,
@ -81,67 +95,60 @@ async function main(): Promise<void> {
);
const vault = I80F48.fromNumber(Number(account.amount));
const error = vault.sub(
(bank as any).indexedDepositsByMangoAccounts
.sub((bank as any).indexedBorrowsByMangoAccounts)
.add(bank.collectedFeesNative)
.add(bank.dust),
);
const error = vault
.sub(
bank.indexedDeposits
.mul(bank.depositIndex)
.sub(bank.indexedBorrows.mul(bank.borrowIndex)),
)
.sub(bank.collectedFeesNative)
.sub(bank.dust)
.add(I80F48.fromU64(bank.feesWithdrawn));
let res = `${bank.name}`;
res =
res +
`\n ${'tokenIndex'.padEnd(40)} ${bank.tokenIndex}` +
`\n ${'bank'.padEnd(40)} ${bank.publicKey}` +
`\n ${'vault'.padEnd(40)} ${bank.vault}` +
`\n ${'oracle'.padEnd(40)} ${bank.oracle}` +
`\n ${'mint'.padEnd(40)} ${bank.mint}` +
`\n ${'price'.padEnd(40)} ${bank.price?.toNumber()}` +
`\n ${'uiPrice'.padEnd(40)} ${bank.uiPrice}` +
`\n ${'error'.padEnd(40)} ${error}` +
`\n ${'collectedFeesNative'.padEnd(40)} ${bank.collectedFeesNative}` +
`\n ${'dust'.padEnd(40)} ${bank.dust}` +
`\n ${'vault balance'.padEnd(40)} ${toUiDecimals(
`\n ${'error'.padEnd(40)} ${toUiDecimals(
error,
bank.mintDecimals,
).toLocaleString()}` +
`\n ${'vault'.padEnd(40)} ${toUiDecimals(
vault,
bank.mintDecimals,
)}, ${vault} native` +
`\n ${'deposits'.padEnd(40)} ${bank.indexedDeposits.mul(
bank.depositIndex,
)}` +
`\n ${'deposits (sum over all mango accounts)'.padEnd(40)} ${
(bank as any).indexedDepositsByMangoAccounts
}` +
`\n ${'borrows'.padEnd(40)} ${bank.indexedBorrows.mul(
bank.borrowIndex,
)}` +
`\n ${'borrows (sum over all mango accounts)'.padEnd(40)} ${
(bank as any).indexedBorrowsByMangoAccounts
}` +
`\n ${'avgUtilization since last rate update'.padEnd(40)} ${(
100 * bank.avgUtilization.toNumber()
).toFixed(1)}%` +
`\n ${'rate parameters'.padEnd(40)} ${(
100 * bank.rate0.toNumber()
).toFixed()}% @ ${(100 * bank.util0.toNumber()).toFixed()}% util, ${(
100 * bank.rate1.toNumber()
).toFixed()}% @${(100 * bank.util1.toNumber()).toFixed()}% util, ${(
100 * bank.maxRate.toNumber()
).toFixed()}% @ 100% util` +
`\n ${'depositRate'.padEnd(40)} ${(
100 * bank.getDepositRate().toNumber()
).toFixed(2)}%` +
`\n ${'borrowRate'.padEnd(40)} ${(
100 * bank.getBorrowRate().toNumber()
).toFixed(2)}%` +
`\n ${'last index update'.padEnd(40)} ${new Date(
1000 * bank.indexLastUpdated.toNumber(),
)}` +
`\n ${'last rates update'.padEnd(40)} ${new Date(
1000 * bank.bankRateLastUpdated.toNumber(),
)}` +
`\n ${'net borrows in window'.padEnd(
40,
)} ${bank.netBorrowsInWindow.toNumber()} / ${bank.netBorrowLimitPerWindowQuote.toNumber()}`;
).toLocaleString()}` +
`\n ${'collected fees'.padEnd(40)} ${toUiDecimals(
bank.collectedFeesNative,
bank.mintDecimals,
).toLocaleString()}` +
`\n ${'fees withdrawn'.padEnd(40)} ${toUiDecimals(
bank.feesWithdrawn,
bank.mintDecimals,
).toLocaleString()}` +
`\n ${'deposits'.padEnd(40)} ${toUiDecimals(
bank.indexedDeposits.mul(bank.depositIndex),
bank.mintDecimals,
).toLocaleString()}` +
`\n ${'deposits (sum over all mango accounts)'.padEnd(40)} ${toUiDecimals(
(bank as any).indexedDepositsByMangoAccounts,
bank.mintDecimals,
).toLocaleString()}` +
`\n ${'borrows'.padEnd(40)} ${toUiDecimals(
bank.indexedBorrows.mul(bank.borrowIndex),
bank.mintDecimals,
).toLocaleString()}` +
`\n ${'borrows (sum over all mango accounts)'.padEnd(40)} ${toUiDecimals(
(bank as any).indexedBorrowsByMangoAccounts,
bank.mintDecimals,
).toLocaleString()}` +
`\n ${'deposits - borrows'.padEnd(40)} ${toUiDecimals(
bank.indexedDeposits
.mul(bank.depositIndex)
.sub(bank.indexedBorrows.mul(bank.borrowIndex)),
bank.mintDecimals,
).toLocaleString()}` +
`\n ${`serum3 total`.padEnd(40)} ${toUiDecimals(
(bank as any).serum3Total,
bank.mintDecimals,
).toLocaleString()}`;
console.log(`${res}`);
}

View File

@ -0,0 +1,50 @@
import { PublicKey } from '@solana/web3.js';
import { MangoClient } from '../src/client';
import { toUiDecimals } from '../src/utils';
async function run() {
const client = await MangoClient.connectDefault(process.env.MB_CLUSTER_URL!);
let group = await client.getGroup(
new PublicKey('78b8f4cGCwmZ9ysPFMWLaLTkkaYnUjwMJYStWe5RTSSX'),
);
let accounts = await client.getAllMangoAccounts(group, true);
const jitoBank = group.getFirstBankByMint(
new PublicKey('J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn'),
);
const jitoSpotMarket = group.getSerum3MarketByName('JitoSOL/USDC');
accounts = accounts.filter(
(a) =>
a.getTokenBalanceUi(jitoBank) +
(a.getSerum3Account(jitoSpotMarket.marketIndex)
? a
.getSerum3OoAccount(jitoSpotMarket.marketIndex)
.baseTokenTotal.toNumber()
: 0) >
0.1,
);
accounts.sort((a, b) =>
a.publicKey.toBase58().localeCompare(b.publicKey.toBase58()),
);
console.log(`wallet,mango_account,jito_sol_balance_ui`);
accounts.forEach((a) =>
console.log(
`${a.owner},${a.publicKey},${
a.getTokenBalanceUi(jitoBank) +
(a.getSerum3Account(jitoSpotMarket.marketIndex)
? toUiDecimals(
a
.getSerum3OoAccount(jitoSpotMarket.marketIndex)
.baseTokenTotal.toNumber(),
jitoBank.mintDecimals,
)
: 0)
}`,
),
);
}
run();

View File

@ -52,7 +52,7 @@ async function updateBanks(client: MangoClient, group: Group): Promise<void> {
);
console.log(
` - Token update index and rate success, tokenIndices - ${tokenIndices}, sig https://explorer.solana.com/tx/${sig}`,
` - Token update index and rate success, tokenIndices - ${tokenIndices}, sig https://explorer.solana.com/tx/${sig.signature}`,
);
} catch (e) {
console.log(
@ -104,7 +104,7 @@ async function consumeEvents(client: MangoClient, group: Group): Promise<void> {
);
console.log(
` - Consume events success, perpMarketIndex - ${perpMarketIndex}, sig https://explorer.solana.com/tx/${sig}`,
` - Consume events success, perpMarketIndex - ${perpMarketIndex}, sig https://explorer.solana.com/tx/${sig.signature}`,
);
} catch (e) {
console.log(
@ -126,7 +126,7 @@ async function updateFunding(client: MangoClient, group: Group): Promise<void> {
);
for (const perpMarketIndex of perpMarketIndices) {
try {
const sig = await sendTransaction(
const status = await sendTransaction(
client.program.provider as AnchorProvider,
[
await client.perpUpdateFundingIx(
@ -139,7 +139,7 @@ async function updateFunding(client: MangoClient, group: Group): Promise<void> {
);
console.log(
` - Update funding success, perpMarketIndex - ${perpMarketIndex}, sig https://explorer.solana.com/tx/${sig}`,
` - Update funding success, perpMarketIndex - ${perpMarketIndex}, sig https://explorer.solana.com/tx/${status.signature}`,
);
} catch (e) {
console.log(

View File

@ -0,0 +1,271 @@
import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
NATIVE_MINT,
TOKEN_PROGRAM_ID,
} from '@solana/spl-token';
import {
AddressLookupTableProgram,
Cluster,
ComputeBudgetProgram,
Connection,
Keypair,
PublicKey,
SYSVAR_INSTRUCTIONS_PUBKEY,
SystemProgram,
} from '@solana/web3.js';
import fs from 'fs';
import chunk from 'lodash/chunk';
import { Group } from '../src/accounts/group';
import { MangoClient } from '../src/client';
import {
MANGO_V4_ID,
MANGO_V4_MAIN_GROUP,
OPENBOOK_PROGRAM_ID,
} from '../src/constants';
import { buildVersionedTx } from '../src/utils';
const { MB_CLUSTER_URL, MB_PAYER3_KEYPAIR, DRY_RUN } = process.env;
const CLUSTER: Cluster = (process.env.CLUSTER as Cluster) || 'mainnet-beta';
// eslint-disable-next-line no-inner-declarations
async function extendTable(
client: MangoClient,
group: Group,
payer: Keypair,
nick: string,
altAddress: PublicKey,
addresses: PublicKey[],
): Promise<void> {
await group.reloadAll(client);
const alt = await client.program.provider.connection.getAddressLookupTable(
altAddress,
);
addresses = addresses.filter(
(newAddress) =>
alt.value?.state.addresses &&
alt.value?.state.addresses.findIndex((addressInAlt) =>
addressInAlt.equals(newAddress),
) === -1,
);
if (addresses.length === 0) {
return;
}
console.log(
`Extending ${altAddress} with ${nick} ${
addresses.length
} addresses - ${addresses.join(', ')}`,
);
if (DRY_RUN) {
return;
}
for (const chunk_ of chunk(addresses, 20)) {
const extendIx = AddressLookupTableProgram.extendLookupTable({
lookupTable: alt.value!.key,
payer: payer.publicKey,
authority: payer.publicKey,
addresses: chunk_,
});
const extendTx = await buildVersionedTx(
client.program.provider as AnchorProvider,
[extendIx],
);
const sig = await client.program.provider.connection.sendTransaction(
extendTx,
);
console.log(`https://explorer.solana.com/tx/${sig}`);
}
}
async function run(): Promise<void> {
try {
const options = AnchorProvider.defaultOptions();
const connection = new Connection(MB_CLUSTER_URL!, options);
const payer = Keypair.fromSecretKey(
Buffer.from(JSON.parse(fs.readFileSync(MB_PAYER3_KEYPAIR!, 'utf-8'))),
);
const payerWallet = new Wallet(payer);
const userProvider = new AnchorProvider(connection, payerWallet, options);
const client = await MangoClient.connect(
userProvider,
CLUSTER,
MANGO_V4_ID[CLUSTER],
{
idsSource: 'get-program-accounts',
},
);
const group = await client.getGroup(MANGO_V4_MAIN_GROUP);
//
// Table 0 - liquidation relevant accounts
//
// const altAddress0 = group.addressLookupTablesList[0].key;
const altAddress0 = new PublicKey(
'AgCBUZ6UMWqPLftTxeAqpQxtrfiCyL2HgRfmmM6QTfCj',
);
// group and insurance vault
await extendTable(client, group, payer, 'group', altAddress0, [
group.publicKey,
group.insuranceVault,
]);
// Banks + vaults + oracles
// Split into 3 ixs since we end up with RangeError: encoding overruns Uint8Array otherwise
await extendTable(
client,
group,
payer,
'token banks',
altAddress0,
Array.from(group.banksMapByMint.values())
.flat()
.map((bank) => bank.publicKey),
);
await extendTable(
client,
group,
payer,
'token bank oracles',
altAddress0,
Array.from(group.banksMapByMint.values())
.flat()
.map((bank) => bank.oracle),
);
await extendTable(
client,
group,
payer,
'token bank vaults',
altAddress0,
Array.from(group.banksMapByMint.values())
.flat()
.map((bank) => bank.vault),
);
// Perps + oracles
await extendTable(
client,
group,
payer,
'perp markets and perp oracles',
altAddress0,
Array.from(group.perpMarketsMapByMarketIndex.values())
.flat()
.map((perpMarket) => [perpMarket.publicKey, perpMarket.oracle])
.flat(),
);
// Well known addressess
await extendTable(
client,
group,
payer,
'well known addresses',
altAddress0,
[
// Solana specific
SystemProgram.programId,
TOKEN_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID,
NATIVE_MINT,
SYSVAR_INSTRUCTIONS_PUBKEY,
ComputeBudgetProgram.programId,
// Misc.
OPENBOOK_PROGRAM_ID['mainnet-beta'],
],
);
//
// Table 1 - everything else
//
// const altAddress1 = group.addressLookupTablesList[1].key;
const altAddress1 = new PublicKey(
'FGZCgVhVGqzfWnmJFP9Hx4BvGvnFApEp1dM2whzXvg1Z',
);
// bank mints
await extendTable(
client,
group,
payer,
'token mints',
altAddress1,
Array.from(group.banksMapByMint.values())
.flat()
.map((bank) => [bank.mint])
.flat(),
);
// bank mint infos
await extendTable(
client,
group,
payer,
'mint infos',
altAddress1,
Array.from(group.mintInfosMapByMint.values())
.flat()
.map((mintInto) => [mintInto.publicKey])
.flat(),
);
// serum3
// Split into 4 ixs since we end up with RangeError: encoding overruns Uint8Array otherwise
await extendTable(
client,
group,
payer,
'serum3 markets',
altAddress1,
Array.from(group.serum3MarketsMapByMarketIndex.values())
.flat()
.map((serum3Market) => serum3Market.publicKey),
);
await extendTable(
client,
group,
payer,
'serum3 external markets',
altAddress1,
Array.from(group.serum3ExternalMarketsMap.values())
.flat()
.map((serum3ExternalMarket) => serum3ExternalMarket.publicKey),
);
await extendTable(
client,
group,
payer,
'serum3 external markets bids',
altAddress1,
Array.from(group.serum3ExternalMarketsMap.values())
.flat()
.map((serum3ExternalMarket) => serum3ExternalMarket.bidsAddress),
);
await extendTable(
client,
group,
payer,
'serum3 external markets asks',
altAddress1,
Array.from(group.serum3ExternalMarketsMap.values())
.flat()
.map((serum3ExternalMarket) => serum3ExternalMarket.asksAddress),
);
await extendTable(
client,
group,
payer,
'perp market event queues, bids, and asks',
altAddress1,
Array.from(group.perpMarketsMapByMarketIndex.values())
.flat()
.map((perpMarket) => [
perpMarket.eventQueue,
perpMarket.bids,
perpMarket.asks,
])
.flat(),
);
} catch (error) {
console.log(error);
}
}
run();

View File

@ -1,9 +1,6 @@
import {
LISTING_PRESETS,
LISTING_PRESETS_PYTH,
MidPriceImpact,
getMidPriceImpacts,
getProposedTier,
} from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools';
import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
import { BN } from '@project-serum/anchor';
@ -21,12 +18,14 @@ import {
TransactionInstruction,
} from '@solana/web3.js';
import fs from 'fs';
import { OracleProvider } from '../src/accounts/oracle';
import { Bank } from '../src/accounts/bank';
import { Group } from '../src/accounts/group';
import { Builder } from '../src/builder';
import { MangoClient } from '../src/client';
import { NullTokenEditParams } from '../src/clientIxParamBuilder';
import { MANGO_V4_MAIN_GROUP as MANGO_V4_PRIMARY_GROUP } from '../src/constants';
import { toUiDecimalsForQuote } from '../src/utils';
import { getEquityForMangoAccounts } from '../src/risk';
import { buildFetch } from '../src/utils';
import {
MANGO_DAO_WALLET_GOVERNANCE,
MANGO_GOVERNANCE_PROGRAM,
@ -38,6 +37,7 @@ import {
DEFAULT_VSR_ID,
VsrClient,
} from './governanceInstructions/voteStakeRegistryClient';
import { MangoAccount } from '../src/accounts/mangoAccount';
const {
MB_CLUSTER_URL,
@ -77,6 +77,52 @@ async function setupVsr(
return vsrClient;
}
async function getTotalLiqorEquity(
client: MangoClient,
group: Group,
mangoAccounts: MangoAccount[],
): Promise<number> {
const liqors = (
await (
await (
await buildFetch()
)(
`https://api.mngo.cloud/data/v4/stats/liqors-over_period?over_period=1MONTH`,
{
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
},
)
).json()
).map((data) => new PublicKey(data['liqor']));
const ttlLiqorEquity = (
await getEquityForMangoAccounts(client, group, liqors, mangoAccounts)
).reduce((partialSum, ae) => partialSum + ae.Equity.val, 0);
return ttlLiqorEquity;
}
function getPriceImpactForBank(
midPriceImpacts: MidPriceImpact[],
bank: Bank,
): MidPriceImpact {
const tokenToPriceImpact = midPriceImpacts
.filter((x) => x.avg_price_impact_percent < 1)
.reduce((acc: { [key: string]: MidPriceImpact }, val: MidPriceImpact) => {
if (
!acc[val.symbol] ||
val.target_amount > acc[val.symbol].target_amount
) {
acc[val.symbol] = val;
}
return acc;
}, {});
const priceImpact = tokenToPriceImpact[getApiTokenName(bank.name)];
return priceImpact;
}
async function updateTokenParams(): Promise<void> {
const [client, wallet] = await Promise.all([buildClient(), setupWallet()]);
const vsrClient = await setupVsr(client.connection, wallet);
@ -84,87 +130,65 @@ async function updateTokenParams(): Promise<void> {
const group = await client.getGroup(MANGO_V4_PRIMARY_GROUP);
const instructions: TransactionInstruction[] = [];
const mangoAccounts = await client.getAllMangoAccounts(group, true);
const ttlLiqorEquityUi = await getTotalLiqorEquity(
client,
group,
mangoAccounts,
);
const midPriceImpacts = getMidPriceImpacts(group.pis);
Array.from(group.banksMapByTokenIndex.values())
.map((banks) => banks[0])
.filter(
(bank) =>
bank.mint.toBase58() == 'So11111111111111111111111111111111111111112' ||
bank.name.toLocaleLowerCase().indexOf('usdc') > -1 ||
bank.name.toLocaleLowerCase().indexOf('stsol') > -1,
// // low low liquidity
// bank.name.includes('DUAL') ||
// bank.name.includes('MNGO') ||
// // low liquidity
// bank.name.includes('ALL') ||
// bank.name.includes('CROWN') ||
// bank.name.includes('GUAC') ||
// bank.name.includes('HNT') ||
// bank.name.includes('KIN') ||
// bank.name.includes('OPOS') ||
// bank.name.includes('RLB') ||
// bank.name.includes('USDH') ||
// better liquidity
bank.name.includes('BONK') ||
bank.name.includes('bSOL') ||
bank.name.includes('CHAI') ||
bank.name.includes('DAI') ||
bank.name.includes('ETH (Portal)') ||
bank.name.includes('JitoSOL') ||
bank.name.includes('LDO') ||
bank.name.includes('MSOL') ||
bank.name.includes('ORCA') ||
bank.name.includes('RAY') ||
bank.name.includes('SOL') ||
bank.name.includes('stSOL') ||
bank.name.includes('TBTC') ||
bank.name.includes('USDC') ||
bank.name.includes('wBTC (Portal)') ||
bank.name.includes('USDT'),
)
.forEach(async (bank) => {
// Limit borrows to 1/3rd of deposit, rounded to 1000, only update if more than 10% different
const depositsInUsd = bank.nativeDeposits().mul(bank.price);
let newNetBorrowLimitPerWindowQuote: number | null =
depositsInUsd.toNumber() / 3;
newNetBorrowLimitPerWindowQuote =
Math.round(newNetBorrowLimitPerWindowQuote / 1_000_000_000) *
1_000_000_000;
newNetBorrowLimitPerWindowQuote =
Math.abs(
(newNetBorrowLimitPerWindowQuote -
bank.netBorrowLimitPerWindowQuote.toNumber()) /
bank.netBorrowLimitPerWindowQuote.toNumber(),
) > 0.1
? newNetBorrowLimitPerWindowQuote
: null;
const priceImpact = getPriceImpactForBank(midPriceImpacts, bank);
// Kick in weight scaling as late as possible until liquidation fee remains reasonable
// Only update if more than 10% different
let newWeightScaleQuote: number | null = null;
if (
bank.tokenIndex != 0 && // USDC
bank.mint.toBase58() != 'So11111111111111111111111111111111111111112' // SOL
) {
const PRESETS =
bank?.oracleProvider === OracleProvider.Pyth
? LISTING_PRESETS_PYTH
: LISTING_PRESETS;
const scaleStartQuoteUi = Math.min(
50 * ttlLiqorEquityUi,
4 * priceImpact.target_amount,
);
const tokenToPriceImpact = midPriceImpacts
.filter((x) => x.avg_price_impact_percent < 1)
.reduce(
(acc: { [key: string]: MidPriceImpact }, val: MidPriceImpact) => {
if (
!acc[val.symbol] ||
val.target_amount > acc[val.symbol].target_amount
) {
acc[val.symbol] = val;
}
return acc;
},
{},
);
const priceImpact = tokenToPriceImpact[getApiTokenName(bank.name)];
const suggestedTier = getProposedTier(
PRESETS,
priceImpact?.target_amount,
bank.oracleProvider === OracleProvider.Pyth,
);
newWeightScaleQuote =
PRESETS[suggestedTier].borrowWeightScaleStartQuote;
const newNetDepositsUi = Math.max(
10_000,
Math.min(bank.uiDeposits(), 300_000) / 3 +
Math.max(0, bank.uiDeposits() - 300_000) / 5,
);
newWeightScaleQuote =
bank.depositWeightScaleStartQuote !== newWeightScaleQuote ||
bank.borrowWeightScaleStartQuote !== newWeightScaleQuote
? newWeightScaleQuote
: null;
}
if (
newNetBorrowLimitPerWindowQuote == null &&
newWeightScaleQuote == null
) {
return;
}
const params = Builder(NullTokenEditParams)
.netBorrowLimitPerWindowQuote(newNetBorrowLimitPerWindowQuote)
.borrowWeightScaleStartQuote(newWeightScaleQuote)
.depositWeightScaleStartQuote(newWeightScaleQuote)
.build();
const params = Builder(NullTokenEditParams).build();
const ix = await client.program.methods
.tokenEdit(
@ -198,7 +222,7 @@ async function updateTokenParams(): Promise<void> {
params.forceClose,
params.tokenConditionalSwapTakerFeeRate,
params.tokenConditionalSwapMakerFeeRate,
params.flashLoanDepositFeeRate,
params.flashLoanSwapFeeRate,
)
.accounts({
group: group.publicKey,
@ -224,21 +248,6 @@ async function updateTokenParams(): Promise<void> {
throw simulated.value.logs;
}
console.log(`Bank ${bank.name}`);
console.log(
`- netBorrowLimitPerWindowQuote UI old ${toUiDecimalsForQuote(
bank.netBorrowLimitPerWindowQuote.toNumber(),
).toLocaleString()} new ${toUiDecimalsForQuote(
newNetBorrowLimitPerWindowQuote!,
).toLocaleString()}`,
);
console.log(
`- WeightScaleQuote UI old ${toUiDecimalsForQuote(
bank.depositWeightScaleStartQuote,
).toLocaleString()} new ${toUiDecimalsForQuote(
newWeightScaleQuote!,
).toLocaleString()}`,
);
instructions.push(ix);
});

View File

@ -47,6 +47,9 @@ export class Group {
ixGate: BN;
buybackFeesSwapMangoAccount: PublicKey;
buybackFeesExpiryInterval: BN;
fastListingIntervalStart: BN;
fastListingsInInterval: number;
allowedFastListingsPerInterval: number;
},
): Group {
return new Group(
@ -68,6 +71,9 @@ export class Group {
obj.ixGate,
obj.buybackFeesSwapMangoAccount,
obj.buybackFeesExpiryInterval,
obj.fastListingIntervalStart,
obj.fastListingsInInterval,
obj.allowedFastListingsPerInterval,
[], // addressLookupTablesList
new Map(), // banksMapByName
new Map(), // banksMapByMint
@ -104,6 +110,9 @@ export class Group {
public ixGate: BN,
public buybackFeesSwapMangoAccount: PublicKey,
public buybackFeesExpiryInterval: BN,
public fastListingIntervalStart: BN,
public fastListingsInInterval: number,
public allowedFastListingsPerInterval: number,
public addressLookupTablesList: AddressLookupTableAccount[],
public banksMapByName: Map<string, Bank[]>,
public banksMapByMint: Map<string, Bank[]>,

View File

@ -1263,6 +1263,8 @@ export class Serum3Orders {
dto.quoteTokenIndex as TokenIndex,
dto.highestPlacedBidInv,
dto.lowestPlacedAsk,
// dto.baseDepositsReserved.toNumber(),
// dto.quoteDepositsReserved.toNumber(),
);
}
@ -1272,7 +1274,7 @@ export class Serum3Orders {
public baseTokenIndex: TokenIndex,
public quoteTokenIndex: TokenIndex,
public highestPlacedBidInv: number,
public lowestPlacedAsk: number,
public lowestPlacedAsk: number, // public baseDepositsReserved: number, // public quoteDepositsReserved: number,
) {}
public isActive(): boolean {
@ -1290,6 +1292,8 @@ export class Serum3PositionDto {
public quoteTokenIndex: number,
public highestPlacedBidInv: number,
public lowestPlacedAsk: number,
// public baseDepositsReserved: BN,
// public quoteDepositsReserved: BN,
public reserved: number[],
) {}
}

View File

@ -114,6 +114,7 @@ export type MangoClientOptions = {
txConfirmationCommitment?: Commitment;
openbookFeesToDao?: boolean;
prependedGlobalAdditionalInstructions?: TransactionInstruction[];
multipleConnections?: Connection[];
};
export class MangoClient {
@ -124,6 +125,7 @@ export class MangoClient {
private txConfirmationCommitment: Commitment;
private openbookFeesToDao: boolean;
private prependedGlobalAdditionalInstructions: TransactionInstruction[] = [];
private multipleProviders: AnchorProvider[] = [];
constructor(
public program: Program<MangoV4>,
@ -144,6 +146,16 @@ export class MangoClient {
'processed';
// TODO: evil side effect, but limited backtraces are a nightmare
Error.stackTraceLimit = 1000;
this.multipleProviders = opts?.multipleConnections
? opts.multipleConnections.map(
(c) =>
new AnchorProvider(
c,
new Wallet(new Keypair()),
(program.provider as AnchorProvider).opts,
),
)
: [];
}
/// Convenience accessors
@ -159,6 +171,40 @@ export class MangoClient {
public async sendAndConfirmTransaction(
ixs: TransactionInstruction[],
opts: any = {},
): Promise<MangoSignatureStatus> {
let prioritizationFee: number;
if (opts.prioritizationFee) {
prioritizationFee = opts.prioritizationFee;
} else if (this.estimateFee) {
prioritizationFee = await this.estimatePrioritizationFee(ixs);
} else {
prioritizationFee = this.prioritizationFee;
}
const providers = [
this.program.provider as AnchorProvider,
...this.multipleProviders,
];
const status = await Promise.race(
providers.map((p) =>
sendTransaction(
p,
[...this.prependedGlobalAdditionalInstructions, ...ixs],
opts.alts ?? [],
{
postSendTxCallback: this.postSendTxCallback,
prioritizationFee,
txConfirmationCommitment: this.txConfirmationCommitment,
...opts,
},
),
),
);
return status;
}
public async sendAndConfirmTransactionSingle(
ixs: TransactionInstruction[],
opts: any = {},
): Promise<MangoSignatureStatus> {
let prioritizationFee: number;
if (opts.prioritizationFee) {
@ -3618,7 +3664,7 @@ export class MangoClient {
[
...preInstructions,
flashLoanBeginIx,
...userDefinedInstructions.filter((ix) => ix.keys.length > 2),
...userDefinedInstructions,
flashLoanEndIx,
],
{ alts: [...group.addressLookupTablesList, ...userDefinedAlts] },
@ -3719,6 +3765,31 @@ export class MangoClient {
pricePremium: number | null,
expiryTimestamp: number | null,
): Promise<MangoSignatureStatus> {
const ixs = await this.tcsTakeProfitOnDepositIx(
group,
account,
sellBank,
buyBank,
thresholdPrice,
thresholdPriceInSellPerBuyToken,
maxSell,
pricePremium,
expiryTimestamp,
);
return await this.sendAndConfirmTransactionForGroup(group, ixs);
}
public async tcsTakeProfitOnDepositIx(
group: Group,
account: MangoAccount,
sellBank: Bank,
buyBank: Bank,
thresholdPrice: number,
thresholdPriceInSellPerBuyToken: boolean,
maxSell: number | null,
pricePremium: number | null,
expiryTimestamp: number | null,
): Promise<TransactionInstruction[]> {
if (account.getTokenBalanceUi(sellBank) < 0) {
throw new Error(
`Only allowed to take profits on deposits! Current balance ${account.getTokenBalanceUi(
@ -3738,7 +3809,7 @@ export class MangoClient {
const lowerLimit = 0;
const upperLimit = thresholdPriceNativeNative;
return await this.tokenConditionalSwapCreate(
return await this.tokenConditionalSwapCreateIx(
group,
account,
sellBank,
@ -3767,6 +3838,31 @@ export class MangoClient {
pricePremium: number | null,
expiryTimestamp: number | null,
): Promise<MangoSignatureStatus> {
const ixs = await this.tcsStopLossOnDepositIx(
group,
account,
sellBank,
buyBank,
thresholdPrice,
thresholdPriceInSellPerBuyToken,
maxSell,
pricePremium,
expiryTimestamp,
);
return await this.sendAndConfirmTransactionForGroup(group, ixs);
}
public async tcsStopLossOnDepositIx(
group: Group,
account: MangoAccount,
sellBank: Bank,
buyBank: Bank,
thresholdPrice: number,
thresholdPriceInSellPerBuyToken: boolean,
maxSell: number | null,
pricePremium: number | null,
expiryTimestamp: number | null,
): Promise<TransactionInstruction[]> {
if (account.getTokenBalanceUi(sellBank) < 0) {
throw new Error(
`Only allowed to set a stop loss on deposits! Current balance ${account.getTokenBalanceUi(
@ -3786,7 +3882,7 @@ export class MangoClient {
const lowerLimit = thresholdPriceNativeNative;
const upperLimit = Number.MAX_SAFE_INTEGER;
return await this.tokenConditionalSwapCreate(
return await this.tokenConditionalSwapCreateIx(
group,
account,
sellBank,
@ -3816,6 +3912,33 @@ export class MangoClient {
allowMargin: boolean | null,
expiryTimestamp: number | null,
): Promise<MangoSignatureStatus> {
const ixs = await this.tcsTakeProfitOnBorrowIx(
group,
account,
sellBank,
buyBank,
thresholdPrice,
thresholdPriceInSellPerBuyToken,
maxBuyUi,
pricePremium,
allowMargin,
expiryTimestamp,
);
return await this.sendAndConfirmTransactionForGroup(group, ixs);
}
public async tcsTakeProfitOnBorrowIx(
group: Group,
account: MangoAccount,
sellBank: Bank,
buyBank: Bank,
thresholdPrice: number,
thresholdPriceInSellPerBuyToken: boolean,
maxBuyUi: number | null,
pricePremium: number | null,
allowMargin: boolean | null,
expiryTimestamp: number | null,
): Promise<TransactionInstruction[]> {
if (account.getTokenBalanceUi(buyBank) > 0) {
throw new Error(
`Only allowed to take profits on borrows! Current balance ${account.getTokenBalanceUi(
@ -3835,7 +3958,7 @@ export class MangoClient {
const lowerLimit = 0;
const upperLimit = thresholdPriceNativeNative;
return await this.tokenConditionalSwapCreate(
return await this.tokenConditionalSwapCreateIx(
group,
account,
sellBank,
@ -3865,6 +3988,33 @@ export class MangoClient {
allowMargin: boolean | null,
expiryTimestamp: number | null,
): Promise<MangoSignatureStatus> {
const ixs = await this.tcsStopLossOnBorrowIx(
group,
account,
sellBank,
buyBank,
thresholdPrice,
thresholdPriceInSellPerBuyToken,
maxBuyUi,
pricePremium,
allowMargin,
expiryTimestamp,
);
return await this.sendAndConfirmTransactionForGroup(group, ixs);
}
public async tcsStopLossOnBorrowIx(
group: Group,
account: MangoAccount,
sellBank: Bank,
buyBank: Bank,
thresholdPrice: number,
thresholdPriceInSellPerBuyToken: boolean,
maxBuyUi: number | null,
pricePremium: number | null,
allowMargin: boolean | null,
expiryTimestamp: number | null,
): Promise<TransactionInstruction[]> {
if (account.getTokenBalanceUi(buyBank) > 0) {
throw new Error(
`Only allowed to set stop loss on borrows! Current balance ${account.getTokenBalanceUi(
@ -3884,7 +4034,7 @@ export class MangoClient {
const lowerLimit = thresholdPriceNativeNative;
const upperLimit = Number.MAX_SAFE_INTEGER;
return await this.tokenConditionalSwapCreate(
return await this.tokenConditionalSwapCreateIx(
group,
account,
sellBank,
@ -3902,6 +4052,81 @@ export class MangoClient {
);
}
public async tokenConditionalSwapCreateIx(
group: Group,
account: MangoAccount,
sellBank: Bank,
buyBank: Bank,
lowerLimitNativeNative: number,
upperLimitNativeNative: number,
maxBuy: number,
maxSell: number,
tcsIntention:
| 'TakeProfitOnDeposit'
| 'StopLossOnDeposit'
| 'TakeProfitOnBorrow'
| 'StopLossOnBorrow'
| null,
pricePremium: number | null,
allowCreatingDeposits: boolean,
allowCreatingBorrows: boolean,
expiryTimestamp: number | null,
displayPriceInSellTokenPerBuyToken: boolean,
): Promise<TransactionInstruction[]> {
const maxBuyNative =
maxBuy == Number.MAX_SAFE_INTEGER
? U64_MAX_BN
: toNative(maxBuy, buyBank.mintDecimals);
const maxSellNative =
maxSell == Number.MAX_SAFE_INTEGER
? U64_MAX_BN
: toNative(maxSell, sellBank.mintDecimals);
pricePremium = TokenConditionalSwap.computePremium(
group,
buyBank,
sellBank,
maxBuyNative,
maxSellNative,
maxBuy,
maxSell,
);
const pricePremiumRate = pricePremium > 0 ? pricePremium / 100 : 0.03;
let intention: TokenConditionalSwapIntention;
switch (tcsIntention) {
case 'StopLossOnBorrow':
case 'StopLossOnDeposit':
intention = TokenConditionalSwapIntention.stopLoss;
break;
case 'TakeProfitOnBorrow':
case 'TakeProfitOnDeposit':
intention = TokenConditionalSwapIntention.takeProfit;
break;
default:
intention = TokenConditionalSwapIntention.unknown;
break;
}
return await this.tokenConditionalSwapCreateRawIx(
group,
account,
buyBank.mint,
sellBank.mint,
maxBuyNative,
maxSellNative,
expiryTimestamp,
lowerLimitNativeNative,
upperLimitNativeNative,
pricePremiumRate,
allowCreatingDeposits,
allowCreatingBorrows,
displayPriceInSellTokenPerBuyToken
? TokenConditionalSwapDisplayPriceStyle.sellTokenPerBuyToken
: TokenConditionalSwapDisplayPriceStyle.buyTokenPerSellToken,
intention,
);
}
public async tokenConditionalSwapCreate(
group: Group,
account: MangoAccount,
@ -3977,7 +4202,7 @@ export class MangoClient {
);
}
public async tokenConditionalSwapCreateLinearAuction(
public async tokenConditionalSwapCreateLinearAuctionIx(
group: Group,
account: MangoAccount,
sellBank: Bank,
@ -3992,7 +4217,7 @@ export class MangoClient {
startTimestamp: number,
durationSeconds: number,
expiryTimestamp: number | null,
): Promise<MangoSignatureStatus> {
): Promise<TransactionInstruction[]> {
let maxBuyNative, maxSellNative;
if (maxBuy == Number.MAX_SAFE_INTEGER) {
maxBuyNative = U64_MAX_BN;
@ -4056,10 +4281,45 @@ export class MangoClient {
}
ixs.push(tcsIx);
return ixs;
}
public async tokenConditionalSwapCreateLinearAuction(
group: Group,
account: MangoAccount,
sellBank: Bank,
buyBank: Bank,
priceStart: number,
priceEnd: number,
maxBuy: number,
maxSell: number,
allowCreatingDeposits: boolean,
allowCreatingBorrows: boolean,
displayPriceInSellTokenPerBuyToken: boolean,
startTimestamp: number,
durationSeconds: number,
expiryTimestamp: number | null,
): Promise<MangoSignatureStatus> {
const ixs = await this.tokenConditionalSwapCreateLinearAuctionIx(
group,
account,
sellBank,
buyBank,
priceStart,
priceEnd,
maxBuy,
maxSell,
allowCreatingDeposits,
allowCreatingBorrows,
displayPriceInSellTokenPerBuyToken,
startTimestamp,
durationSeconds,
expiryTimestamp,
);
return await this.sendAndConfirmTransactionForGroup(group, ixs);
}
public async tokenConditionalSwapCreatePremiumAuction(
public async tokenConditionalSwapCreatePremiumAuctionIx(
group: Group,
account: MangoAccount,
sellBank: Bank,
@ -4080,7 +4340,7 @@ export class MangoClient {
expiryTimestamp: number | null,
displayPriceInSellTokenPerBuyToken: boolean,
durationSeconds: number,
): Promise<MangoSignatureStatus> {
): Promise<TransactionInstruction[]> {
const lowerLimitNative = toNativeSellPerBuyTokenPrice(
lowerLimit,
sellBank,
@ -4190,6 +4450,47 @@ export class MangoClient {
}
ixs.push(tcsIx);
return ixs;
}
public async tokenConditionalSwapCreatePremiumAuction(
group: Group,
account: MangoAccount,
sellBank: Bank,
buyBank: Bank,
lowerLimit: number,
upperLimit: number,
maxBuy: number,
maxSell: number,
tcsIntention:
| 'TakeProfitOnDeposit'
| 'StopLossOnDeposit'
| 'TakeProfitOnBorrow'
| 'StopLossOnBorrow'
| null,
maxPricePremiumPercent: number | null,
allowCreatingDeposits: boolean,
allowCreatingBorrows: boolean,
expiryTimestamp: number | null,
displayPriceInSellTokenPerBuyToken: boolean,
durationSeconds: number,
): Promise<MangoSignatureStatus> {
const ixs = await this.tokenConditionalSwapCreatePremiumAuctionIx(
group,
account,
sellBank,
buyBank,
lowerLimit,
upperLimit,
maxBuy,
maxSell,
tcsIntention,
maxPricePremiumPercent,
allowCreatingDeposits,
allowCreatingBorrows,
expiryTimestamp,
displayPriceInSellTokenPerBuyToken,
durationSeconds,
);
return await this.sendAndConfirmTransactionForGroup(group, ixs);
}
@ -4209,6 +4510,41 @@ export class MangoClient {
priceDisplayStyle: TokenConditionalSwapDisplayPriceStyle,
intention: TokenConditionalSwapIntention,
): Promise<MangoSignatureStatus> {
const ixs = await this.tokenConditionalSwapCreateRawIx(
group,
account,
buyMintPk,
sellMintPk,
maxBuy,
maxSell,
expiryTimestamp,
priceLowerLimit,
priceUpperLimit,
pricePremiumRate,
allowCreatingDeposits,
allowCreatingBorrows,
priceDisplayStyle,
intention,
);
return await this.sendAndConfirmTransactionForGroup(group, ixs);
}
public async tokenConditionalSwapCreateRawIx(
group: Group,
account: MangoAccount,
buyMintPk: PublicKey,
sellMintPk: PublicKey,
maxBuy: BN,
maxSell: BN,
expiryTimestamp: number | null,
priceLowerLimit: number,
priceUpperLimit: number,
pricePremiumRate: number,
allowCreatingDeposits: boolean,
allowCreatingBorrows: boolean,
priceDisplayStyle: TokenConditionalSwapDisplayPriceStyle,
intention: TokenConditionalSwapIntention,
): Promise<TransactionInstruction[]> {
const buyBank: Bank = group.getFirstBankByMint(buyMintPk);
const sellBank: Bank = group.getFirstBankByMint(sellMintPk);
const tcsIx = await this.program.methods
@ -4249,14 +4585,14 @@ export class MangoClient {
}
ixs.push(tcsIx);
return await this.sendAndConfirmTransactionForGroup(group, ixs);
return ixs;
}
public async tokenConditionalSwapCancel(
public async tokenConditionalSwapCancelIx(
group: Group,
account: MangoAccount,
tokenConditionalSwapId: BN,
): Promise<MangoSignatureStatus> {
): Promise<TransactionInstruction> {
const tokenConditionalSwapIndex = account.tokenConditionalSwaps.findIndex(
(tcs) => tcs.id.eq(tokenConditionalSwapId),
);
@ -4268,7 +4604,7 @@ export class MangoClient {
const buyBank = group.banksMapByTokenIndex.get(tcs.buyTokenIndex)![0];
const sellBank = group.banksMapByTokenIndex.get(tcs.sellTokenIndex)![0];
const ix = await this.program.methods
return this.program.methods
.tokenConditionalSwapCancel(
tokenConditionalSwapIndex,
new BN(tokenConditionalSwapId),
@ -4281,7 +4617,18 @@ export class MangoClient {
sellBank: sellBank.publicKey,
})
.instruction();
}
public async tokenConditionalSwapCancel(
group: Group,
account: MangoAccount,
tokenConditionalSwapId: BN,
): Promise<MangoSignatureStatus> {
const ix = await this.tokenConditionalSwapCancelIx(
group,
account,
tokenConditionalSwapId,
);
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
}
@ -4322,6 +4669,26 @@ export class MangoClient {
maxBuyTokenToLiqee: number,
maxSellTokenToLiqor: number,
): Promise<MangoSignatureStatus> {
const ix = await this.tokenConditionalSwapTriggerIx(
group,
liqee,
liqor,
tokenConditionalSwapId,
maxBuyTokenToLiqee,
maxSellTokenToLiqor,
);
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
}
public async tokenConditionalSwapTriggerIx(
group: Group,
liqee: MangoAccount,
liqor: MangoAccount,
tokenConditionalSwapId: BN,
maxBuyTokenToLiqee: number,
maxSellTokenToLiqor: number,
): Promise<TransactionInstruction> {
const tokenConditionalSwapIndex = liqee.tokenConditionalSwaps.findIndex(
(tcs) => tcs.id.eq(tokenConditionalSwapId),
);
@ -4353,7 +4720,7 @@ export class MangoClient {
} as AccountMeta),
);
const ix = await this.program.methods
return this.program.methods
.tokenConditionalSwapTrigger(
tokenConditionalSwapIndex,
new BN(tokenConditionalSwapId),
@ -4369,8 +4736,6 @@ export class MangoClient {
})
.remainingAccounts(parsedHealthAccounts)
.instruction();
return await this.sendAndConfirmTransactionForGroup(group, [ix]);
}
public async altSet(

View File

@ -2,6 +2,9 @@ import { Connection } from '@solana/web3.js';
import { JUPITER } from './constants';
export enum TransactionErrors {
MangoNoFreeTokenPositionIndex,
MangoNoFreeSerum3OpenOrdersIndex,
MangoNoFreePerpPositionIndex,
// Slippage incurred was higher than user expected
JupiterSlippageToleranceExceeded,
Unknown,
@ -25,6 +28,25 @@ export async function parseTxForKnownErrors(
});
if (tx && tx.meta && tx.meta.logMessages) {
if (
tx.meta.logMessages.some((msg) =>
msg.includes('NoFreeTokenPositionIndex'),
)
) {
return TransactionErrors.MangoNoFreeTokenPositionIndex;
}
if (
tx.meta.logMessages.some((msg) =>
msg.includes('NoFreeSerum3OpenOrdersIndex'),
)
) {
return TransactionErrors.MangoNoFreeSerum3OpenOrdersIndex;
}
if (
tx.meta.logMessages.some((msg) => msg.includes('NoFreePerpPositionIndex'))
) {
return TransactionErrors.MangoNoFreePerpPositionIndex;
}
if (
tx.meta.logMessages.some((msg) =>
msg.includes('SlippageToleranceExceeded'),

View File

@ -4,6 +4,7 @@ import { MangoClient } from './client';
import { MANGO_V4_ID } from './constants';
export * from './accounts/bank';
export * from './accounts/oracle';
export * from './accounts/mangoAccount';
export * from './accounts/perp';
export {

View File

@ -461,20 +461,25 @@ export async function getRiskStats(
.map((bank) => bank.mint.toString()),
),
];
const prices = await getOnChainPriceForMints([
...new Set(
Array.from(group.banksMapByTokenIndex.values())
.flat()
.map((bank) => bank.mint.toString()),
),
]);
const onChainPrices = Object.fromEntries(
prices.map((price, i) => [mints[i], price]),
);
// Note:
// Disable for now
// Getting rate limited
// const prices = await getOnChainPriceForMints([
// ...new Set(
// Array.from(group.banksMapByTokenIndex.values())
// .flat()
// .map((bank) => bank.mint.toString()),
// ),
// ]);
// const onChainPrices = Object.fromEntries(
// prices.map((price, i) => [mints[i], price]),
// );
Array.from(group.banksMapByTokenIndex.values())
.flat()
.forEach((b) => {
b['onChainPrice'] = onChainPrices[b.mint.toBase58()];
b['onChainPrice'] = b.uiPrice;
});
// Clone group, and simulate change % price drop for all assets except stables

View File

@ -5,6 +5,8 @@ import {
AddressLookupTableAccount,
ComputeBudgetProgram,
MessageV0,
RpcResponseAndContext,
SignatureResult,
Signer,
TransactionConfirmationStatus,
TransactionError,
@ -15,11 +17,11 @@ import {
import { COMPUTE_BUDGET_PROGRAM_ID } from '../constants';
export interface MangoSignatureStatus {
slot: number;
confirmations: number | null;
err: TransactionError | null;
confirmations?: number | null;
confirmationStatus?: TransactionConfirmationStatus;
err: TransactionError | null;
signature: TransactionSignature;
slot: number;
}
export async function sendTransaction(
@ -96,13 +98,6 @@ export async function sendTransaction(
skipPreflight: true, // mergedOpts.skipPreflight,
});
// const signature = await connection.sendTransactionss(
// vtx as any as VersionedTransaction,
// {
// skipPreflight: true,
// },
// );
if (opts.postSendTxCallback) {
try {
opts.postSendTxCallback({ txid: signature });
@ -112,28 +107,27 @@ export async function sendTransaction(
}
const txConfirmationCommitment = opts.txConfirmationCommitment ?? 'processed';
let status: any;
let status: RpcResponseAndContext<SignatureResult>;
if (
latestBlockhash.blockhash != null &&
latestBlockhash.lastValidBlockHeight != null
) {
status = (
await connection.confirmTransaction(
{
signature: signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
},
txConfirmationCommitment,
)
).value;
status = await connection.confirmTransaction(
{
signature: signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
},
txConfirmationCommitment,
);
} else {
status = (
await connection.confirmTransaction(signature, txConfirmationCommitment)
).value;
status = await connection.confirmTransaction(
signature,
txConfirmationCommitment,
);
}
if (status.err) {
const signatureResult = status.value;
if (signatureResult.err) {
console.warn('Tx status: ', status);
throw new MangoError({
txid: signature,
@ -141,7 +135,7 @@ export async function sendTransaction(
});
}
return { signature, ...status };
return { signature, slot: status.context.slot, ...signatureResult };
}
export const createComputeBudgetIx = (