diff --git a/release-to-mainnet.sh b/release-to-mainnet.sh new file mode 100755 index 000000000..98aadc306 --- /dev/null +++ b/release-to-mainnet.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +set -e pipefail + +WALLET_WITH_FUNDS=~/.config/solana/mango-mainnet.json +PROGRAM_ID=m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD + +# TODO fix need for --skip-lint +# build program, +anchor build --skip-lint + +# patch types, which we want in rust, but anchor client doesn't support +./idl-fixup.sh + +# update types in ts client package +cp -v ./target/types/mango_v4.ts ./ts/client/src/mango_v4.ts + +(cd ./ts/client && tsc) + +if [[ -z "${NO_DEPLOY}" ]]; then + # publish program + solana --url $CLUSTER_URL program deploy --program-id $PROGRAM_ID \ + -k $WALLET_WITH_FUNDS target/deploy/mango_v4.so + + # publish idl + anchor idl upgrade --provider.cluster $CLUSTER_URL --provider.wallet $WALLET_WITH_FUNDS \ + --filepath target/idl/mango_v4.json $PROGRAM_ID +else + echo "Skipping deployment..." +fi + + +# build npm package +(cd ./ts/client && tsc) diff --git a/ts/client/ids.json b/ts/client/ids.json index 86a2b763b..c02dc6f5e 100644 --- a/ts/client/ids.json +++ b/ts/client/ids.json @@ -61,6 +61,49 @@ "publicKey": "5ZwVhP5PYkKtx2H4WL2JxSvWL6AALFzwubwM5dsR5XHF" } ] + }, + { + "cluster": "mainnet-beta", + "name": "mainnet-beta.microwavedcola", + "publicKey": "A9XhGqUUjV992cD36qWDY8wDiZnGuCaUWtSE3NGXjDCb", + "serum3ProgramId": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", + "mangoProgramId": "m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD", + "banks": [ + { + "name": "USDC", + "publicKey": "BYq994HWWuXC1UC7ByTiDLQBsnBQQAzJaLvMCQU3priu" + }, + { + "name": "BTC", + "publicKey": "96e336hgaPWbbHqPbpPcmr1pbTY1CALtFWkNk2tD4ZmK" + }, + { + "name": "SOL", + "publicKey": "J3dxEFhJG1maNva8W9u4XzxCR1DQm6qfkgrkY29Ujrh3" + } + ], + "stubOracles": [ + { + "name": "USDC", + "publicKey": "DcCuQDbDqHj7G5pBe7epuPQSwwvysm8GfJSa51MYqFXk" + } + ], + "mintInfos": [ + { + "name": "USDC", + "publicKey": "42bXYX5sidUkrijuArv4D11g1aAm9947ht6qUB8Me5Q3" + }, + { + "name": "BTC", + "publicKey": "4KZkrJQRvrVy8BWmbUKk7YWezMAsSb97ZMHFLUtj6Q5u" + }, + { + "name": "SOL", + "publicKey": "EvjoLBh7ej78C1yA7jaLcHQfoGx3KkDBnZbrztSZFGmC" + } + ], + "serum3Markets": [], + "perpMarkets": [] } ] } \ No newline at end of file diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 80967ccbb..5efc25684 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -6,7 +6,7 @@ import { initializeAccount, WRAPPED_SOL_MINT, } from '@project-serum/serum/lib/token-instructions'; -import { parsePriceData, PriceData } from '@pythnetwork/client'; +import { parsePriceData } from '@pythnetwork/client'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; import { AccountMeta, @@ -122,6 +122,7 @@ export class MangoClient { const groups = (await this.program.account.group.all(filters)).map( (tuple) => Group.from(tuple.publicKey, tuple.account), ); + console.log(groups); await groups[0].reloadAll(this); return groups[0]; } @@ -1221,10 +1222,10 @@ export class MangoClient { return new MangoClient( new Program( idl as MangoV4, - new PublicKey(id.publicKey), + new PublicKey(id.mangoProgramId), provider, ), - new PublicKey(id.publicKey), + new PublicKey(id.mangoProgramId), id.cluster, groupName, ); diff --git a/ts/client/src/constants/index.ts b/ts/client/src/constants/index.ts index b833ca87c..d517f4fc3 100644 --- a/ts/client/src/constants/index.ts +++ b/ts/client/src/constants/index.ts @@ -1,13 +1,13 @@ import { PublicKey } from '@solana/web3.js'; export const SERUM3_PROGRAM_ID = { - testnet: new PublicKey('DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY'), // todo: fix + testnet: new PublicKey('DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY'), devnet: new PublicKey('DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY'), - mainnet: new PublicKey('9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin'), + 'mainnet-beta': new PublicKey('9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin'), }; export const MANGO_V4_ID = { - testnet: new PublicKey('m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD'), // todo: fix + testnet: new PublicKey('m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD'), devnet: new PublicKey('m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD'), - mainnet: new PublicKey('m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD'), // note: never deployed so far + 'mainnet-beta': new PublicKey('m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD'), }; diff --git a/ts/client/src/scripts/example1-admin.ts b/ts/client/src/scripts/example1-admin.ts index b4c77f627..9ccc05c86 100644 --- a/ts/client/src/scripts/example1-admin.ts +++ b/ts/client/src/scripts/example1-admin.ts @@ -76,7 +76,7 @@ async function main() { 0.07, 0.8, 0.9, - 0.0005, + 0.88, 0.0005, 1.5, 0.8, @@ -114,7 +114,7 @@ async function main() { 0.07, 0.8, 0.9, - 0.0005, + 1.5, 0.0005, 1.5, 0.8, @@ -142,7 +142,7 @@ async function main() { 0.07, 0.8, 0.9, - 0.0005, + 0.63, 0.0005, 1.5, 0.8, @@ -172,7 +172,7 @@ async function main() { 0.07, 0.8, 0.9, - 0.0005, + 0.63, 0.0005, 1.5, 0.8, diff --git a/ts/client/src/scripts/mb-add-group-to-ids-json.ts b/ts/client/src/scripts/mb-add-group-to-ids-json.ts new file mode 100644 index 000000000..404334123 --- /dev/null +++ b/ts/client/src/scripts/mb-add-group-to-ids-json.ts @@ -0,0 +1,105 @@ +import { AnchorProvider, Wallet } from '@project-serum/anchor'; + +import { Connection, Keypair } from '@solana/web3.js'; +import fs from 'fs'; +import idsJson from '../../ids.json'; +import { MangoClient } from '../client'; +import { MANGO_V4_ID, SERUM3_PROGRAM_ID } from '../constants'; +import { Id } from '../ids'; + +function replacer(key, value) { + if (value instanceof Map) { + return Object.fromEntries(value); + } else { + return value; + } +} + +async function main() { + const groupName = 'mainnet-beta.microwavedcola'; + const cluster = 'mainnet-beta'; + + // build client and fetch group for admin + const options = AnchorProvider.defaultOptions(); + const connection = new Connection(process.env.CLUSTER_URL, options); + const user = Keypair.fromSecretKey( + Buffer.from( + JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')), + ), + ); + const userWallet = new Wallet(user); + const userProvider = new AnchorProvider(connection, userWallet, options); + const client = await MangoClient.connect( + userProvider, + cluster, + MANGO_V4_ID[cluster], + ); + const admin = Keypair.fromSecretKey( + Buffer.from( + JSON.parse( + fs.readFileSync(process.env.MANGO_MAINNET_PAYER_KEYPAIR!, 'utf-8'), + ), + ), + ); + console.log(`Admin ${admin.publicKey.toBase58()}`); + + const group = await client.getGroupForAdmin(admin.publicKey, 0); + + // collect mappings & + // collect pubkeys + const banks = await client.getBanksForGroup(group); + const banksMapByMint = new Map( + banks.map((tuple) => [tuple.mint.toBase58(), tuple]), + ); + const stubOracles = await client.getStubOracle(group); + const mintInfos = await client.getMintInfosForGroup(group); + const serum3Markets = await client.serum3GetMarkets(group); + const perpMarkets = await client.perpGetMarkets(group); + + // build ids + const toDump = new Id( + cluster, + groupName, + group.publicKey.toBase58(), + SERUM3_PROGRAM_ID[cluster].toBase58(), + MANGO_V4_ID[cluster].toBase58(), + banks.map((tuple) => ({ + name: tuple.name, + publicKey: tuple.publicKey.toBase58(), + })), + stubOracles.map((tuple) => ({ + name: banksMapByMint.get(tuple.mint.toBase58())!.name, + publicKey: tuple.publicKey.toBase58(), + })), + mintInfos.map((tuple) => ({ + name: banksMapByMint.get(tuple.mint.toBase58())!.name, + publicKey: tuple.publicKey.toBase58(), + })), + serum3Markets.map((tuple) => ({ + name: tuple.name, + publicKey: tuple.publicKey.toBase58(), + marketExternal: tuple.serumMarketExternal.toBase58(), + })), + perpMarkets.map((tuple) => ({ + name: tuple.name, + publicKey: tuple.publicKey.toBase58(), + })), + ); + + // adds ids for group in existing ids.json + const existingGroup = idsJson.groups.find((group) => group.name == groupName); + if (existingGroup) { + console.log('Updating existing group with latest state...'); + } else { + console.log('Group does not exist yet...'); + } + idsJson.groups = idsJson.groups.filter((group) => group.name !== groupName); + idsJson.groups.push(toDump); + + // dump + const file = `${process.cwd()}/ts/client/ids.json`; + await fs.writeFileSync(file, JSON.stringify(idsJson, replacer, 2)); + + process.exit(); +} +main(); diff --git a/ts/client/src/scripts/mb-example1-admin-close.ts b/ts/client/src/scripts/mb-example1-admin-close.ts new file mode 100644 index 000000000..029774706 --- /dev/null +++ b/ts/client/src/scripts/mb-example1-admin-close.ts @@ -0,0 +1,100 @@ +import { AnchorProvider, Wallet } from '@project-serum/anchor'; +import { Connection, Keypair, PublicKey } from '@solana/web3.js'; +import fs from 'fs'; +import { MangoClient } from '../client'; +import { MANGO_V4_ID } from '../constants'; + +const MAINNET_MINTS = new Map([ + ['USDC', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'], + ['BTC', '9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E'], + ['SOL', 'So11111111111111111111111111111111111111112'], +]); + +async function main() { + const options = AnchorProvider.defaultOptions(); + const connection = new Connection(process.env.CLUSTER_URL, options); + + const admin = Keypair.fromSecretKey( + Buffer.from( + JSON.parse( + fs.readFileSync(process.env.MANGO_MAINNET_PAYER_KEYPAIR!, 'utf-8'), + ), + ), + ); + const adminWallet = new Wallet(admin); + console.log(`Admin ${adminWallet.publicKey.toBase58()}`); + const adminProvider = new AnchorProvider(connection, adminWallet, options); + const client = await MangoClient.connect( + adminProvider, + 'mainnet-beta', + MANGO_V4_ID['mainnet-beta'], + ); + + const group = await client.getGroupForAdmin(admin.publicKey); + console.log(`Group ${group.publicKey}`); + + let sig; + + // close stub oracle + const usdcDevnetMint = new PublicKey(MAINNET_MINTS.get('USDC')!); + try { + const usdcDevnetOracle = await client.getStubOracle( + group, + usdcDevnetMint, + )[0]; + let sig = await client.closeStubOracle(group, usdcDevnetOracle.publicKey); + console.log( + `Closed USDC stub oracle, sig https://explorer.solana.com/address/${sig}`, + ); + } catch (error) { + console.error(error); + } + + // close all bank + for (const bank of group.banksMap.values()) { + try { + sig = await client.tokenDeregister(group, bank.name); + console.log( + `Removed token ${bank.name}, sig https://explorer.solana.com/address/${sig}`, + ); + } catch (error) { + console.error(error); + } + } + + // deregister all serum markets + for (const market of group.serum3MarketsMap.values()) { + try { + sig = await client.serum3deregisterMarket(group, market.name); + console.log( + `Deregistered serum market ${market.name}, sig https://explorer.solana.com/address/${sig}`, + ); + } catch (error) { + console.error(error); + } + } + + // close all perp markets + for (const market of group.perpMarketsMap.values()) { + try { + sig = await client.perpCloseMarket(group, market.name); + console.log( + `Closed perp market ${market.name}, sig https://explorer.solana.com/address/${sig}`, + ); + } catch (error) { + console.error(error); + } + } + + // finally, close the group + try { + sig = await client.closeGroup(group); + console.log(`Closed group, sig https://explorer.solana.com/address/${sig}`); + } catch (error) { + console.error(error); + } + + process.exit(); +} + +main(); diff --git a/ts/client/src/scripts/mb-example1-admin.ts b/ts/client/src/scripts/mb-example1-admin.ts new file mode 100644 index 000000000..1d4e63257 --- /dev/null +++ b/ts/client/src/scripts/mb-example1-admin.ts @@ -0,0 +1,157 @@ +import { AnchorProvider, Wallet } from '@project-serum/anchor'; +import { Connection, Keypair, PublicKey } from '@solana/web3.js'; +import fs from 'fs'; +import { MangoClient } from '../client'; +import { MANGO_V4_ID } from '../constants'; + +const MAINNET_MINTS = new Map([ + ['USDC', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'], + ['BTC', '9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E'], + ['SOL', 'So11111111111111111111111111111111111111112'], +]); +const MAINNET_ORACLES = new Map([ + ['BTC', 'GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU'], + ['SOL', 'H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG'], +]); + +async function main() { + const options = AnchorProvider.defaultOptions(); + const connection = new Connection(process.env.CLUSTER_URL, options); + + const admin = Keypair.fromSecretKey( + Buffer.from( + JSON.parse( + fs.readFileSync(process.env.MANGO_MAINNET_PAYER_KEYPAIR!, 'utf-8'), + ), + ), + ); + const adminWallet = new Wallet(admin); + console.log(`Admin ${adminWallet.publicKey.toBase58()}`); + const adminProvider = new AnchorProvider(connection, adminWallet, options); + const client = await MangoClient.connect( + adminProvider, + 'mainnet-beta', + MANGO_V4_ID['mainnet-beta'], + ); + + // group + console.log(`Creating Group...`); + try { + await client.createGroup(0, true); + } catch (error) { + console.log(error); + } + const group = await client.getGroupForAdmin(admin.publicKey); + console.log(`...registered group ${group.publicKey}`); + + // register token 0 + console.log(`Registering BTC...`); + const btcMainnetMint = new PublicKey(MAINNET_MINTS.get('BTC')!); + const btcMainnetOracle = new PublicKey(MAINNET_ORACLES.get('BTC')!); + try { + await client.tokenRegister( + group, + btcMainnetMint, + btcMainnetOracle, + 0.1, + 0, + 'BTC', + 0.4, + 0.07, + 0.8, + 0.9, + 0.88, + 0.0005, + 1.5, + 0.8, + 0.6, + 1.2, + 1.4, + 0.02, + ); + await group.reloadAll(client); + } catch (error) { + console.log(error); + } + + // stub oracle + register token 1 + console.log(`Creating USDC stub oracle...`); + const usdcMainnetMint = new PublicKey(MAINNET_MINTS.get('USDC')!); + try { + await client.createStubOracle(group, usdcMainnetMint, 1.0); + } catch (error) { + console.log(error); + } + const usdcMainnetOracle = ( + await client.getStubOracle(group, usdcMainnetMint) + )[0]; + console.log(`...created stub oracle ${usdcMainnetOracle.publicKey}`); + + console.log(`Registering USDC...`); + try { + await client.tokenRegister( + group, + usdcMainnetMint, + usdcMainnetOracle.publicKey, + 0.1, + 1, + 'USDC', + 0.4, + 0.07, + 0.8, + 0.9, + 1.5, + 0.0005, + 1.5, + 0.8, + 0.6, + 1.2, + 1.4, + 0.02, + ); + await group.reloadAll(client); + } catch (error) { + console.log(error); + } + + // register token 2 + console.log(`Registering SOL...`); + const solMainnetMint = new PublicKey(MAINNET_MINTS.get('SOL')!); + const solMainnetOracle = new PublicKey(MAINNET_ORACLES.get('SOL')!); + try { + await client.tokenRegister( + group, + solMainnetMint, + solMainnetOracle, + 0.1, + 2, // tokenIndex + 'SOL', + 0.4, + 0.07, + 0.8, + 0.9, + 0.63, + 0.0005, + 1.5, + 0.8, + 0.6, + 1.2, + 1.4, + 0.02, + ); + await group.reloadAll(client); + } catch (error) { + console.log(error); + } + + // log tokens/banks + for (const bank of await group.banksMap.values()) { + console.log( + `...registered Bank ${bank.name} ${bank.tokenIndex} ${bank.publicKey}, mint ${bank.mint}, oracle ${bank.oracle}`, + ); + } + + process.exit(); +} + +main(); diff --git a/ts/client/src/scripts/mb-example1-ids-json.ts b/ts/client/src/scripts/mb-example1-ids-json.ts new file mode 100644 index 000000000..363be2817 --- /dev/null +++ b/ts/client/src/scripts/mb-example1-ids-json.ts @@ -0,0 +1,37 @@ +import { AnchorProvider, Wallet } from '@project-serum/anchor'; +import { Connection, Keypair } from '@solana/web3.js'; +import fs from 'fs'; +import { MangoClient } from '../client'; + +async function main() { + const options = AnchorProvider.defaultOptions(); + const connection = new Connection(process.env.CLUSTER_URL, options); + + const user = Keypair.fromSecretKey( + Buffer.from( + JSON.parse(fs.readFileSync(process.env.USER_KEYPAIR!, 'utf-8')), + ), + ); + const userWallet = new Wallet(user); + const userProvider = new AnchorProvider(connection, userWallet, options); + const client = await MangoClient.connectForGroupName( + userProvider, + 'mainnet-beta.microwavedcola' /* Use ids json instead of getProgramAccounts */, + ); + console.log(`User ${userWallet.publicKey.toBase58()}`); + + const admin = Keypair.fromSecretKey( + Buffer.from( + JSON.parse( + fs.readFileSync(process.env.MANGO_MAINNET_PAYER_KEYPAIR!, 'utf-8'), + ), + ), + ); + console.log(`Admin ${admin.publicKey.toBase58()}`); + + const group = await client.getGroupForAdmin(admin.publicKey, 0); + console.log(`Found group ${group.publicKey.toBase58()}`); + process.exit(); +} + +main();