2022-02-23 02:09:17 -08:00
|
|
|
import { Program, Provider } from '@project-serum/anchor';
|
2022-04-07 09:58:42 -07:00
|
|
|
import * as spl from '@solana/spl-token';
|
2022-04-07 08:58:20 -07:00
|
|
|
import { PublicKey, TransactionSignature } from '@solana/web3.js';
|
2022-04-07 09:58:42 -07:00
|
|
|
import {
|
|
|
|
Bank,
|
|
|
|
getBanksForGroup,
|
|
|
|
getMintInfoForTokenIndex,
|
2022-04-07 10:42:00 -07:00
|
|
|
registerToken,
|
2022-04-07 09:58:42 -07:00
|
|
|
} from './accounts/types/bank';
|
2022-04-07 10:42:00 -07:00
|
|
|
import { createGroup, getGroupForAdmin, Group } from './accounts/types/group';
|
2022-04-07 08:58:20 -07:00
|
|
|
import {
|
|
|
|
createMangoAccount,
|
2022-04-07 09:58:42 -07:00
|
|
|
deposit,
|
2022-04-07 08:58:20 -07:00
|
|
|
getMangoAccountsForGroupAndOwner,
|
|
|
|
MangoAccount,
|
2022-04-07 09:58:42 -07:00
|
|
|
withdraw,
|
2022-04-07 08:58:20 -07:00
|
|
|
} from './accounts/types/mangoAccount';
|
2022-04-07 10:42:00 -07:00
|
|
|
import {
|
|
|
|
createStubOracle,
|
|
|
|
getStubOracleForGroupAndMint,
|
|
|
|
setStubOracle,
|
|
|
|
StubOracle,
|
|
|
|
} from './accounts/types/oracle';
|
2022-03-31 04:55:59 -07:00
|
|
|
import { IDL, MangoV4 } from './mango_v4';
|
2022-02-23 02:09:17 -08:00
|
|
|
|
|
|
|
export const MANGO_V4_ID = new PublicKey(
|
2022-03-30 08:16:32 -07:00
|
|
|
'm43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD',
|
2022-02-23 02:09:17 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
export class MangoClient {
|
2022-04-07 06:06:26 -07:00
|
|
|
constructor(public program: Program<MangoV4>, public devnet?: boolean) {}
|
2022-02-23 02:09:17 -08:00
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
/// public
|
|
|
|
|
2022-04-07 10:42:00 -07:00
|
|
|
// Group
|
|
|
|
|
|
|
|
public async createGroup() {
|
|
|
|
return await createGroup(this, this.program.provider.wallet.publicKey);
|
|
|
|
}
|
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
public async getGroup(adminPk: PublicKey): Promise<Group> {
|
|
|
|
return await getGroupForAdmin(this, adminPk);
|
|
|
|
}
|
|
|
|
|
2022-04-07 10:42:00 -07:00
|
|
|
// Tokens/Banks
|
|
|
|
|
|
|
|
public async registerToken(
|
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
oraclePk: PublicKey,
|
|
|
|
tokenIndex: number,
|
|
|
|
): Promise<TransactionSignature> {
|
|
|
|
return await registerToken(
|
|
|
|
this,
|
|
|
|
group.publicKey,
|
|
|
|
this.program.provider.wallet.publicKey,
|
|
|
|
mintPk,
|
|
|
|
oraclePk,
|
|
|
|
tokenIndex,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
public async getBanksForGroup(group: Group): Promise<Bank[]> {
|
|
|
|
return await getBanksForGroup(this, group.publicKey);
|
|
|
|
}
|
|
|
|
|
2022-04-07 10:42:00 -07:00
|
|
|
public async createStubOracle(
|
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
price: number,
|
|
|
|
): Promise<TransactionSignature> {
|
|
|
|
return await createStubOracle(
|
|
|
|
this,
|
|
|
|
group.publicKey,
|
|
|
|
this.program.provider.wallet.publicKey,
|
|
|
|
mintPk,
|
|
|
|
price,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async setStubOracle(
|
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
price: number,
|
|
|
|
): Promise<TransactionSignature> {
|
|
|
|
return await setStubOracle(
|
|
|
|
this,
|
|
|
|
group.publicKey,
|
|
|
|
this.program.provider.wallet.publicKey,
|
|
|
|
mintPk,
|
|
|
|
price,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async getStubOracle(
|
|
|
|
group: Group,
|
|
|
|
mintPk: PublicKey,
|
|
|
|
): Promise<StubOracle> {
|
|
|
|
return await getStubOracleForGroupAndMint(this, group.publicKey, mintPk);
|
|
|
|
}
|
|
|
|
|
|
|
|
// MangoAccount
|
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
public async createMangoAccount(
|
|
|
|
group: Group,
|
|
|
|
accountNumber: number,
|
|
|
|
): Promise<TransactionSignature> {
|
2022-04-07 10:42:00 -07:00
|
|
|
return createMangoAccount(
|
|
|
|
this,
|
|
|
|
group.publicKey,
|
|
|
|
this.program.provider.wallet.publicKey,
|
|
|
|
accountNumber,
|
|
|
|
);
|
2022-04-07 09:58:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public async getMangoAccount(
|
|
|
|
group: Group,
|
|
|
|
ownerPk: PublicKey,
|
|
|
|
): Promise<MangoAccount[]> {
|
|
|
|
return await getMangoAccountsForGroupAndOwner(
|
|
|
|
this,
|
|
|
|
group.publicKey,
|
|
|
|
ownerPk,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async deposit(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
bank: Bank,
|
|
|
|
amount: number,
|
|
|
|
) {
|
|
|
|
const tokenAccountPk = await spl.getAssociatedTokenAddress(
|
|
|
|
bank.mint,
|
|
|
|
mangoAccount.owner,
|
|
|
|
);
|
|
|
|
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
await this.buildHealthRemainingAccounts(group, mangoAccount, bank);
|
|
|
|
|
|
|
|
return await deposit(
|
|
|
|
this,
|
|
|
|
group.publicKey,
|
|
|
|
mangoAccount.publicKey,
|
|
|
|
bank.publicKey,
|
|
|
|
bank.vault,
|
|
|
|
tokenAccountPk,
|
|
|
|
mangoAccount.owner,
|
|
|
|
healthRemainingAccounts,
|
|
|
|
amount,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async withdraw(
|
|
|
|
group: Group,
|
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
bank: Bank,
|
|
|
|
amount: number,
|
|
|
|
allowBorrow: boolean,
|
|
|
|
) {
|
|
|
|
const tokenAccountPk = await spl.getAssociatedTokenAddress(
|
|
|
|
bank.mint,
|
|
|
|
mangoAccount.owner,
|
|
|
|
);
|
|
|
|
|
|
|
|
const healthRemainingAccounts: PublicKey[] =
|
|
|
|
await this.buildHealthRemainingAccounts(group, mangoAccount, bank);
|
|
|
|
|
|
|
|
return await withdraw(
|
|
|
|
this,
|
|
|
|
group.publicKey,
|
|
|
|
mangoAccount.publicKey,
|
|
|
|
bank.publicKey,
|
|
|
|
bank.vault,
|
|
|
|
tokenAccountPk,
|
|
|
|
mangoAccount.owner,
|
|
|
|
healthRemainingAccounts,
|
|
|
|
amount,
|
|
|
|
allowBorrow,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// static
|
|
|
|
|
2022-04-07 06:06:26 -07:00
|
|
|
static async connect(
|
|
|
|
provider: Provider,
|
|
|
|
devnet?: boolean,
|
|
|
|
): Promise<MangoClient> {
|
2022-03-31 23:59:39 -07:00
|
|
|
// TODO: use IDL on chain or in repository? decide...
|
2022-03-31 04:55:59 -07:00
|
|
|
// Alternatively we could fetch IDL from chain.
|
|
|
|
// const idl = await Program.fetchIdl(MANGO_V4_ID, provider);
|
2022-03-31 00:10:06 -07:00
|
|
|
let idl = IDL;
|
|
|
|
|
2022-03-31 23:59:39 -07:00
|
|
|
// TODO: remove...
|
2022-03-31 07:16:11 -07:00
|
|
|
// Temporarily add missing (dummy) type definitions, so we can do new Program(...) below
|
|
|
|
// without anchor throwing errors. These types come from part of the code we don't yet care about
|
|
|
|
// in the client.
|
|
|
|
function addDummyType(idl: MangoV4, typeName: string) {
|
2022-04-02 02:52:28 -07:00
|
|
|
if (idl.types.find((type) => type.name === typeName)) {
|
|
|
|
return;
|
|
|
|
}
|
2022-03-31 07:16:11 -07:00
|
|
|
(idl.types as any).push({
|
|
|
|
name: typeName,
|
|
|
|
type: {
|
|
|
|
kind: 'struct',
|
|
|
|
fields: [],
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
addDummyType(idl, 'usize');
|
|
|
|
addDummyType(idl, 'AnyNode');
|
|
|
|
addDummyType(idl, 'EventQueueHeader');
|
|
|
|
addDummyType(idl, 'AnyEvent');
|
|
|
|
addDummyType(idl, 'H');
|
|
|
|
addDummyType(idl, 'H::Item');
|
|
|
|
addDummyType(idl, 'NodeHandle');
|
2022-02-23 02:09:17 -08:00
|
|
|
|
|
|
|
return new MangoClient(
|
2022-03-30 08:16:32 -07:00
|
|
|
new Program<MangoV4>(idl as MangoV4, MANGO_V4_ID, provider),
|
2022-04-07 06:06:26 -07:00
|
|
|
devnet,
|
2022-02-23 02:09:17 -08:00
|
|
|
);
|
|
|
|
}
|
2022-04-07 08:16:46 -07:00
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
/// private
|
2022-04-07 08:58:20 -07:00
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
private async buildHealthRemainingAccounts(
|
2022-04-07 08:58:20 -07:00
|
|
|
group: Group,
|
2022-04-07 09:58:42 -07:00
|
|
|
mangoAccount: MangoAccount,
|
|
|
|
bank: Bank,
|
|
|
|
) {
|
|
|
|
const healthRemainingAccounts: PublicKey[] = [];
|
|
|
|
{
|
|
|
|
const tokenIndices = mangoAccount.tokens
|
|
|
|
.filter((token) => token.tokenIndex !== 65535)
|
|
|
|
.map((token) => token.tokenIndex);
|
|
|
|
tokenIndices.push(bank.tokenIndex);
|
2022-04-07 08:58:20 -07:00
|
|
|
|
2022-04-07 09:58:42 -07:00
|
|
|
const mintInfos = await Promise.all(
|
|
|
|
[...new Set(tokenIndices)].map(async (tokenIndex) =>
|
|
|
|
getMintInfoForTokenIndex(this, group.publicKey, tokenIndex),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
healthRemainingAccounts.push(
|
|
|
|
...mintInfos.flatMap((mintinfos) => {
|
|
|
|
return mintinfos.flatMap((mintinfo) => {
|
|
|
|
return mintinfo.bank;
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
healthRemainingAccounts.push(
|
|
|
|
...mintInfos.flatMap((mintinfos) => {
|
|
|
|
return mintinfos.flatMap((mintinfo) => {
|
|
|
|
return mintinfo.oracle;
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
healthRemainingAccounts.push(
|
|
|
|
...mangoAccount.serum3
|
|
|
|
.filter((serum3Account) => serum3Account.marketIndex !== 65535)
|
|
|
|
.map((serum3Account) => serum3Account.openOrders),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return healthRemainingAccounts;
|
2022-04-07 08:58:20 -07:00
|
|
|
}
|
2022-02-23 02:09:17 -08:00
|
|
|
}
|