2022-05-25 17:28:42 -07:00
|
|
|
import {
|
|
|
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
|
|
TOKEN_PROGRAM_ID,
|
|
|
|
} from '@solana/spl-token';
|
2022-08-09 15:27:24 -07:00
|
|
|
import {
|
|
|
|
AccountMeta,
|
|
|
|
PublicKey,
|
|
|
|
SystemProgram,
|
2022-08-11 08:44:12 -07:00
|
|
|
TransactionInstruction,
|
2022-08-09 15:27:24 -07:00
|
|
|
} from '@solana/web3.js';
|
2022-06-02 10:30:39 -07:00
|
|
|
import BN from 'bn.js';
|
2022-08-31 02:36:44 -07:00
|
|
|
import { Bank, QUOTE_DECIMALS } from './accounts/bank';
|
|
|
|
import { Group } from './accounts/group';
|
2022-06-21 03:38:01 -07:00
|
|
|
import { I80F48 } from './accounts/I80F48';
|
2022-08-31 02:36:44 -07:00
|
|
|
import { MangoAccount, Serum3Orders } from './accounts/mangoAccount';
|
|
|
|
import { PerpMarket } from './accounts/perp';
|
2022-06-02 10:30:39 -07:00
|
|
|
|
2022-09-20 03:57:01 -07:00
|
|
|
export const U64_MAX_BN = new BN('18446744073709551615');
|
2022-06-02 10:30:39 -07:00
|
|
|
export const I64_MAX_BN = new BN('9223372036854775807').toTwos(64);
|
2022-04-02 02:52:28 -07:00
|
|
|
|
|
|
|
export function debugAccountMetas(ams: AccountMeta[]) {
|
|
|
|
for (const am of ams) {
|
|
|
|
console.log(
|
|
|
|
`${am.pubkey.toBase58()}, isSigner: ${am.isSigner
|
|
|
|
.toString()
|
|
|
|
.padStart(5, ' ')}, isWritable - ${am.isWritable
|
|
|
|
.toString()
|
|
|
|
.padStart(5, ' ')}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-04-02 11:15:56 -07:00
|
|
|
|
2022-08-31 02:36:44 -07:00
|
|
|
export function debugHealthAccounts(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
publicKeys: PublicKey[],
|
|
|
|
) {
|
|
|
|
const banks = new Map(
|
|
|
|
Array.from(group.banksMapByName.values()).map((banks: Bank[]) => [
|
|
|
|
banks[0].publicKey.toBase58(),
|
|
|
|
`${banks[0].name} bank`,
|
|
|
|
]),
|
|
|
|
);
|
|
|
|
const oracles = new Map(
|
|
|
|
Array.from(group.banksMapByName.values()).map((banks: Bank[]) => [
|
|
|
|
banks[0].oracle.toBase58(),
|
|
|
|
`${banks[0].name} oracle`,
|
|
|
|
]),
|
|
|
|
);
|
|
|
|
const serum3 = new Map(
|
|
|
|
mangoAccount.serum3Active().map((serum3: Serum3Orders) => {
|
2022-08-31 02:55:54 -07:00
|
|
|
const serum3Market = Array.from(
|
|
|
|
group.serum3MarketsMapByExternal.values(),
|
|
|
|
).find((serum3Market) => serum3Market.marketIndex === serum3.marketIndex);
|
|
|
|
if (!serum3Market) {
|
|
|
|
throw new Error(
|
|
|
|
`Serum3Orders for non existent market with market index ${serum3.marketIndex}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return [serum3.openOrders.toBase58(), `${serum3Market.name} spot oo`];
|
2022-08-31 02:36:44 -07:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
const perps = new Map(
|
|
|
|
Array.from(group.perpMarketsMap.values()).map((perpMarket: PerpMarket) => [
|
|
|
|
perpMarket.publicKey.toBase58(),
|
|
|
|
`${perpMarket.name} perp market`,
|
|
|
|
]),
|
|
|
|
);
|
|
|
|
|
|
|
|
publicKeys.map((pk) => {
|
|
|
|
if (banks.get(pk.toBase58())) {
|
|
|
|
console.log(banks.get(pk.toBase58()));
|
|
|
|
}
|
|
|
|
if (oracles.get(pk.toBase58())) {
|
|
|
|
console.log(oracles.get(pk.toBase58()));
|
|
|
|
}
|
|
|
|
if (serum3.get(pk.toBase58())) {
|
|
|
|
console.log(serum3.get(pk.toBase58()));
|
|
|
|
}
|
|
|
|
if (perps.get(pk.toBase58())) {
|
|
|
|
console.log(perps.get(pk.toBase58()));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-02 11:15:56 -07:00
|
|
|
export async function findOrCreate<T>(
|
|
|
|
entityName: string,
|
2022-08-04 10:42:41 -07:00
|
|
|
findMethod: (...x: any) => any,
|
2022-04-02 11:15:56 -07:00
|
|
|
findArgs: any[],
|
2022-08-04 10:42:41 -07:00
|
|
|
createMethod: (...x: any) => any,
|
2022-04-02 11:15:56 -07:00
|
|
|
createArgs: any[],
|
|
|
|
): Promise<T> {
|
|
|
|
let many: T[] = await findMethod(...findArgs);
|
|
|
|
let one: T;
|
|
|
|
if (many.length > 0) {
|
|
|
|
one = many[0];
|
|
|
|
return one;
|
|
|
|
}
|
|
|
|
await createMethod(...createArgs);
|
|
|
|
many = await findMethod(...findArgs);
|
|
|
|
one = many[0];
|
|
|
|
return one;
|
|
|
|
}
|
2022-05-25 17:28:42 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the address of the associated token account for a given mint and owner
|
|
|
|
*
|
|
|
|
* @param mint Token mint account
|
|
|
|
* @param owner Owner of the new account
|
|
|
|
* @param allowOwnerOffCurve Allow the owner account to be a PDA (Program Derived Address)
|
|
|
|
* @param programId SPL Token program account
|
|
|
|
* @param associatedTokenProgramId SPL Associated Token program account
|
|
|
|
*
|
|
|
|
* @return Address of the associated token account
|
|
|
|
*/
|
|
|
|
export async function getAssociatedTokenAddress(
|
|
|
|
mint: PublicKey,
|
|
|
|
owner: PublicKey,
|
|
|
|
allowOwnerOffCurve = false,
|
|
|
|
programId = TOKEN_PROGRAM_ID,
|
|
|
|
associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
|
|
): Promise<PublicKey> {
|
|
|
|
if (!allowOwnerOffCurve && !PublicKey.isOnCurve(owner.toBuffer()))
|
|
|
|
throw new Error('TokenOwnerOffCurve');
|
|
|
|
|
|
|
|
const [address] = await PublicKey.findProgramAddress(
|
|
|
|
[owner.toBuffer(), programId.toBuffer(), mint.toBuffer()],
|
|
|
|
associatedTokenProgramId,
|
|
|
|
);
|
|
|
|
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
2022-08-09 15:27:24 -07:00
|
|
|
export async function createAssociatedTokenAccountIdempotentInstruction(
|
|
|
|
payer: PublicKey,
|
|
|
|
owner: PublicKey,
|
|
|
|
mint: PublicKey,
|
|
|
|
): Promise<TransactionInstruction> {
|
|
|
|
const account = await getAssociatedTokenAddress(mint, owner);
|
|
|
|
return new TransactionInstruction({
|
|
|
|
keys: [
|
|
|
|
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
|
|
{ pubkey: account, isSigner: false, isWritable: true },
|
|
|
|
{ pubkey: owner, isSigner: false, isWritable: false },
|
|
|
|
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
|
|
{
|
|
|
|
pubkey: SystemProgram.programId,
|
|
|
|
isSigner: false,
|
|
|
|
isWritable: false,
|
|
|
|
},
|
|
|
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
|
|
],
|
|
|
|
programId: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
|
|
data: Buffer.from([0x1]),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-08-23 04:33:47 -07:00
|
|
|
export function toNative(uiAmount: number, decimals: number): I80F48 {
|
|
|
|
return I80F48.fromNumber(uiAmount).mul(
|
|
|
|
I80F48.fromNumber(Math.pow(10, decimals)),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-05-25 17:28:42 -07:00
|
|
|
export function toNativeDecimals(amount: number, decimals: number): BN {
|
2022-05-31 18:38:47 -07:00
|
|
|
return new BN(Math.trunc(amount * Math.pow(10, decimals)));
|
|
|
|
}
|
|
|
|
|
2022-07-04 03:29:35 -07:00
|
|
|
export function toUiDecimals(
|
|
|
|
amount: I80F48 | number,
|
2022-08-11 08:44:12 -07:00
|
|
|
decimals: number,
|
2022-07-04 03:29:35 -07:00
|
|
|
): number {
|
|
|
|
amount = amount instanceof I80F48 ? amount.toNumber() : amount;
|
2022-06-10 06:02:03 -07:00
|
|
|
return amount / Math.pow(10, decimals);
|
|
|
|
}
|
|
|
|
|
2022-08-11 08:44:12 -07:00
|
|
|
export function toUiDecimalsForQuote(amount: I80F48 | number): number {
|
|
|
|
amount = amount instanceof I80F48 ? amount.toNumber() : amount;
|
|
|
|
return amount / Math.pow(10, QUOTE_DECIMALS);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function toU64(amount: number, decimals: number): BN {
|
2022-05-31 18:38:47 -07:00
|
|
|
const bn = toNativeDecimals(amount, decimals).toString();
|
|
|
|
console.log('bn', bn);
|
|
|
|
|
2022-06-23 03:58:43 -07:00
|
|
|
return new BN(bn);
|
2022-05-25 17:28:42 -07:00
|
|
|
}
|
2022-06-21 03:38:01 -07:00
|
|
|
|
|
|
|
export function nativeI80F48ToUi(amount: I80F48, decimals: number): I80F48 {
|
|
|
|
return amount.div(I80F48.fromNumber(Math.pow(10, decimals)));
|
|
|
|
}
|