481 lines
12 KiB
TypeScript
481 lines
12 KiB
TypeScript
import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
|
|
import {
|
|
AddressLookupTableProgram,
|
|
ComputeBudgetProgram,
|
|
Connection,
|
|
Keypair,
|
|
PublicKey,
|
|
SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
SYSVAR_RENT_PUBKEY,
|
|
SystemProgram,
|
|
} from '@solana/web3.js';
|
|
import fs from 'fs';
|
|
import { TokenIndex } from '../src/accounts/bank';
|
|
import { Group } from '../src/accounts/group';
|
|
import { MangoClient } from '../src/client';
|
|
import { DefaultTokenRegisterParams } from '../src/clientIxParamBuilder';
|
|
import { MANGO_V4_ID } from '../src/constants';
|
|
import { buildVersionedTx } from '../src/utils';
|
|
import {
|
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
NATIVE_MINT,
|
|
TOKEN_PROGRAM_ID,
|
|
} from '../src/utils/spl';
|
|
|
|
const MANGO_PROGRAM = MANGO_V4_ID['mainnet-beta'];
|
|
|
|
const MAINNET_MINTS = new Map([
|
|
['USDC', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'], // 0
|
|
['SOL', 'So11111111111111111111111111111111111111112'], // 1
|
|
]);
|
|
const MAINNET_ORACLES = new Map([
|
|
['USDC', 'Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD'],
|
|
['SOL', 'H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG'],
|
|
]);
|
|
const MAINNET_SERUM3_MARKETS = new Map([
|
|
['SOL/USDC', '8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6'],
|
|
]);
|
|
const {
|
|
MB_CLUSTER_URL,
|
|
MB_PAYER_KEYPAIR,
|
|
GROUP_NUM,
|
|
}: {
|
|
MB_CLUSTER_URL: string;
|
|
MB_PAYER_KEYPAIR: string;
|
|
GROUP_NUM: number;
|
|
} = process.env as any;
|
|
|
|
const defaultOracleConfig = {
|
|
confFilter: 0.1,
|
|
maxStalenessSlots: null,
|
|
};
|
|
|
|
const defaultInterestRate = {
|
|
adjustmentFactor: 0.0,
|
|
util0: 0.0,
|
|
rate0: 0.0,
|
|
util1: 0.0,
|
|
rate1: 0.0,
|
|
maxRate: 0.51,
|
|
};
|
|
|
|
const defaultTokenParams = {
|
|
...DefaultTokenRegisterParams,
|
|
oracleConfig: defaultOracleConfig,
|
|
interestRateParams: defaultInterestRate,
|
|
loanOriginationFeeRate: 0.0,
|
|
loanFeeRate: 0.0,
|
|
initAssetWeight: 0,
|
|
maintAssetWeight: 0,
|
|
initLiabWeight: 1,
|
|
maintLiabWeight: 1,
|
|
liquidationFee: 0,
|
|
minVaultToDepositsRatio: 1,
|
|
netBorrowLimitPerWindowQuote: 0,
|
|
};
|
|
|
|
async function buildAdminClient(): Promise<[MangoClient, Keypair]> {
|
|
const admin = Keypair.fromSecretKey(
|
|
Buffer.from(JSON.parse(fs.readFileSync(MB_PAYER_KEYPAIR!, 'utf-8'))),
|
|
);
|
|
const options = AnchorProvider.defaultOptions();
|
|
const connection = new Connection(MB_CLUSTER_URL!, options);
|
|
const adminWallet = new Wallet(admin);
|
|
const adminProvider = new AnchorProvider(connection, adminWallet, options);
|
|
const client = await MangoClient.connect(
|
|
adminProvider,
|
|
'mainnet-beta',
|
|
MANGO_PROGRAM,
|
|
{
|
|
idsSource: 'get-program-accounts',
|
|
},
|
|
);
|
|
return [client, admin];
|
|
}
|
|
|
|
async function buildUserClient(): Promise<[MangoClient, Group, Keypair]> {
|
|
const options = AnchorProvider.defaultOptions();
|
|
const connection = new Connection(MB_CLUSTER_URL!, options);
|
|
|
|
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.connect(
|
|
userProvider,
|
|
'mainnet-beta',
|
|
MANGO_PROGRAM,
|
|
);
|
|
const group = await client.getGroupForCreator(user.publicKey, GROUP_NUM);
|
|
return [client, group, user];
|
|
}
|
|
|
|
async function createGroup(): Promise<void> {
|
|
const result = await buildAdminClient();
|
|
const client = result[0];
|
|
const admin = result[1];
|
|
|
|
const insuranceMint = new PublicKey(MAINNET_MINTS.get('USDC')!);
|
|
await client.groupCreate(GROUP_NUM, true, 2, insuranceMint);
|
|
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
|
console.log(`...registered group ${group.publicKey}`);
|
|
|
|
await client.groupEdit(
|
|
group,
|
|
undefined, // admin
|
|
admin.publicKey, // fast listing
|
|
);
|
|
console.log(`...set fast listing admin`);
|
|
}
|
|
|
|
async function registerTokens(): Promise<void> {
|
|
const result = await buildAdminClient();
|
|
const client = result[0];
|
|
const admin = result[1];
|
|
|
|
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
|
|
|
const usdcMainnetMint = new PublicKey(MAINNET_MINTS.get('USDC')!);
|
|
const usdcMainnetOracle = new PublicKey(MAINNET_ORACLES.get('USDC')!);
|
|
let sig = await client.tokenRegister(
|
|
group,
|
|
usdcMainnetMint,
|
|
usdcMainnetOracle,
|
|
PublicKey.default,
|
|
0,
|
|
'USDC',
|
|
defaultTokenParams,
|
|
);
|
|
console.log(`registered usdc ${sig}`);
|
|
|
|
const solMainnetMint = new PublicKey(MAINNET_MINTS.get('SOL')!);
|
|
const solMainnetOracle = new PublicKey(MAINNET_ORACLES.get('SOL')!);
|
|
sig = await client.tokenRegisterTrustless(
|
|
group,
|
|
solMainnetMint,
|
|
solMainnetOracle,
|
|
1,
|
|
'SOL',
|
|
);
|
|
console.log(`registered sol ${sig}`);
|
|
}
|
|
|
|
async function registerSerum3Market(): Promise<void> {
|
|
const result = await buildAdminClient();
|
|
const client = result[0];
|
|
const admin = result[1];
|
|
|
|
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
|
await client.serum3RegisterMarket(
|
|
group,
|
|
new PublicKey(MAINNET_SERUM3_MARKETS.get('SOL/USDC')!),
|
|
group.getFirstBankByTokenIndex(1 as TokenIndex),
|
|
group.getFirstBankByTokenIndex(0 as TokenIndex),
|
|
0,
|
|
'SOL/USDC',
|
|
0.5,
|
|
);
|
|
}
|
|
|
|
async function createAndPopulateAlt() {
|
|
const result = await buildAdminClient();
|
|
const client = result[0];
|
|
const admin = result[1];
|
|
|
|
const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM);
|
|
|
|
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)).signature;
|
|
console.log(`...https://explorer.solana.com/tx/${sig}`);
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
// Extend using mango v4 relevant pub keys
|
|
try {
|
|
const 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),
|
|
);
|
|
|
|
const serum3MarketAddresses = Array.from(
|
|
group.serum3MarketsMapByExternal.values(),
|
|
)
|
|
.flat()
|
|
.map((serum3Market) => serum3Market.publicKey);
|
|
|
|
const serum3ExternalMarketAddresses = Array.from(
|
|
group.serum3ExternalMarketsMap.values(),
|
|
)
|
|
.flat()
|
|
.map((serum3ExternalMarket) => [
|
|
serum3ExternalMarket.publicKey,
|
|
serum3ExternalMarket.bidsAddress,
|
|
serum3ExternalMarket.asksAddress,
|
|
])
|
|
.flat();
|
|
|
|
const perpMarketAddresses = Array.from(
|
|
group.perpMarketsMapByMarketIndex.values(),
|
|
)
|
|
.flat()
|
|
.map((perpMarket) => [
|
|
perpMarket.publicKey,
|
|
perpMarket.oracle,
|
|
perpMarket.bids,
|
|
perpMarket.asks,
|
|
perpMarket.eventQueue,
|
|
])
|
|
.flat();
|
|
|
|
// eslint-disable-next-line no-inner-declarations
|
|
async function extendTable(addresses: PublicKey[]): Promise<void> {
|
|
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],
|
|
);
|
|
const 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);
|
|
|
|
// TODO: dont extend for perps atm
|
|
await extendTable(perpMarketAddresses);
|
|
|
|
// Well known addresses
|
|
await extendTable([
|
|
SystemProgram.programId,
|
|
SYSVAR_RENT_PUBKEY,
|
|
TOKEN_PROGRAM_ID,
|
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
NATIVE_MINT,
|
|
SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
ComputeBudgetProgram.programId,
|
|
]);
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
async function doUserAction(): Promise<void> {
|
|
const result = await buildUserClient();
|
|
const client = result[0];
|
|
const group = result[1];
|
|
const user = result[2];
|
|
|
|
let mangoAccount = await client.getMangoAccountForOwner(
|
|
group,
|
|
user.publicKey,
|
|
0,
|
|
);
|
|
|
|
if (!mangoAccount) {
|
|
await client.createMangoAccount(group, 0);
|
|
mangoAccount = await client.getMangoAccountForOwner(
|
|
group,
|
|
user.publicKey,
|
|
0,
|
|
);
|
|
}
|
|
|
|
// await client.tokenDeposit(
|
|
// group,
|
|
// mangoAccount!,
|
|
// new PublicKey(MAINNET_MINTS.get('SOL')!),
|
|
// 0.01,
|
|
// );
|
|
|
|
// await client.tcsStopLossOnDeposit(
|
|
// group,
|
|
// mangoAccount!,
|
|
// group.getFirstBankByTokenIndex(1 as TokenIndex),
|
|
// group.getFirstBankByTokenIndex(0 as TokenIndex),
|
|
// group.getFirstBankByTokenIndex(1 as TokenIndex).uiPrice * 1.1,
|
|
// false,
|
|
// null,
|
|
// null,
|
|
// null,
|
|
// );
|
|
|
|
await mangoAccount?.reload(client);
|
|
mangoAccount
|
|
?.tokenConditionalSwapsActive()
|
|
.map((tcs) => console.log(tcs.toString(group)));
|
|
}
|
|
|
|
async function doUserAction2(): Promise<void> {
|
|
const result = await buildUserClient();
|
|
const client = result[0];
|
|
const group = result[1];
|
|
const user = result[2];
|
|
|
|
let mangoAccount = await client.getMangoAccountForOwner(
|
|
group,
|
|
user.publicKey,
|
|
1,
|
|
);
|
|
|
|
if (!mangoAccount) {
|
|
await client.createMangoAccount(group, 1);
|
|
mangoAccount = await client.getMangoAccountForOwner(
|
|
group,
|
|
user.publicKey,
|
|
1,
|
|
);
|
|
}
|
|
|
|
await client.tokenDeposit(
|
|
group,
|
|
mangoAccount!,
|
|
new PublicKey(MAINNET_MINTS.get('USDC')!),
|
|
5,
|
|
);
|
|
}
|
|
|
|
async function placeAuction(): Promise<void> {
|
|
const result = await buildUserClient();
|
|
const client = result[0];
|
|
const group = result[1];
|
|
const user = result[2];
|
|
|
|
const mangoAccount = await client.getMangoAccountForOwner(
|
|
group,
|
|
user.publicKey,
|
|
1,
|
|
);
|
|
console.log(mangoAccount?.publicKey.toString());
|
|
|
|
const usdcBank = group.getFirstBankByMint(
|
|
new PublicKey(MAINNET_MINTS.get('USDC')!),
|
|
);
|
|
const solBank = group.getFirstBankByMint(
|
|
new PublicKey(MAINNET_MINTS.get('SOL')!),
|
|
);
|
|
|
|
await client.tokenConditionalSwapCancelAll(group, mangoAccount!);
|
|
|
|
for (let i = 0; i < 3; i += 1) {
|
|
await client.tokenConditionalSwapCreateLinearAuction(
|
|
group,
|
|
mangoAccount!,
|
|
usdcBank,
|
|
solBank,
|
|
20.0,
|
|
24.0,
|
|
0.1,
|
|
Number.MAX_SAFE_INTEGER,
|
|
true,
|
|
false,
|
|
true,
|
|
Math.floor(Date.now() / 1000) + 10,
|
|
180000,
|
|
null,
|
|
);
|
|
}
|
|
// await client.tokenConditionalSwapCreateLinearAuction(
|
|
// group,
|
|
// mangoAccount!,
|
|
// solBank,
|
|
// usdcBank,
|
|
// 1 / 24.0,
|
|
// 1 / 19.0,
|
|
// 2.0,
|
|
// Number.MAX_SAFE_INTEGER,
|
|
// true,
|
|
// false,
|
|
// true,
|
|
// Math.floor(Date.now() / 1000) + 300,
|
|
// 180,
|
|
// null,
|
|
// );
|
|
// await client.tokenConditionalSwapCreatePremiumAuction(
|
|
// group,
|
|
// mangoAccount!,
|
|
// usdcBank,
|
|
// solBank,
|
|
// 20.0,
|
|
// 26.0,
|
|
// 2.0,
|
|
// Number.MAX_SAFE_INTEGER,
|
|
// null,
|
|
// 1, // in percent, eww
|
|
// true,
|
|
// false,
|
|
// null,
|
|
// true,
|
|
// 300,
|
|
// );
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
try {
|
|
// await createGroup();
|
|
// await registerTokens();
|
|
// await registerSerum3Market();
|
|
// await createAndPopulateAlt();
|
|
// await doUserAction();
|
|
// await doUserAction2();
|
|
await placeAuction();
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
}
|
|
|
|
main();
|