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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { try { // await createGroup(); // await registerTokens(); // await registerSerum3Market(); // await createAndPopulateAlt(); // await doUserAction(); // await doUserAction2(); await placeAuction(); } catch (error) { console.log(error); } } main();