Mango Markets
Mango provides a single venue to lend, borrow, swap, and leverage trade cryptoassets through an on-chain risk engine. You can connect to Mango's on-chain program using the Client API libraries. You'll also need the Solana javascript API library.
"@blockworks-foundation/mango-client": "^3.3.27",
"@solana/web3.js": "^1.37.0"
How to get a Mango Group
A mango group is a basket of cross-margined tokens. It holds broad market info about tokens, serum dex markets, perp markets, oracles, insurance fund and fees vaults. Each version of Mango Markets uses a different Mango Group containing different tokens. The current v3 group is mainnet.1
. Here's a table showing the various groups:
Group | Version | Cluster |
---|---|---|
mainnet.1 | v3 | mainnet |
devnet.2 | v3 | devnet |
devnet.3 | v3 | devnet |
BTC_ETH_SOL_SRM_USDC | v2 | mainnet & devnet |
BTC_ETH_USDT | v2 | devnet |
BTC_ETH_USDC | v2 | testnet |
Note
If you wish to use the v2 groups, you'll have to use the v2 client library. You can find it here
import {Connection, PublicKey} from "@solana/web3.js";
import {IDS, MangoClient, Config, I80F48} from "@blockworks-foundation/mango-client";
(async () => {
const cluster = 'devnet';
const group = 'devnet.3';
const config = new Config(IDS);
const groupConfig = config.getGroup(cluster, group);
if(!groupConfig) {
throw new Error("unable to get mango group config");
}
const mangoGroupKey = groupConfig.publicKey;
const clusterData = IDS.groups.find((g) => {
return g.name == group && g.cluster == cluster;
});
const mangoProgramIdPk = new PublicKey(clusterData.mangoProgramId);
const clusterUrl = IDS.cluster_urls[cluster];
const connection = new Connection(clusterUrl, 'singleGossip');
const client = new MangoClient(connection, mangoProgramIdPk);
const mangoGroup = await client.getMangoGroup(mangoGroupKey);
})();
const mangoGroup = await client.getMangoGroup(mangoGroupKey);
How to create a Mango Account
A Mango Account is associated with a Mango Group, and it holds your tokens and allows you to trade that Group’s markets. You can find the reference here.
import { useWallet } from "@solana/wallet-adapter-react";
import { Connection, PublicKey } from "@solana/web3.js";
import { IDS, MangoClient, Config } from "@blockworks-foundation/mango-client";
(async () => {
const { wallet } = useWallet();
const cluster = 'devnet';
const group = 'devnet.3';
const config = new Config(IDS);
const groupConfig = config.getGroup(cluster, group);
if(!groupConfig) {
throw new Error("unable to get mango group config");
}
const mangoGroupKey = groupConfig.publicKey;
const clusterData = IDS.groups.find((g) => {
return g.name == group && g.cluster == cluster;
});
const mangoProgramIdPk = new PublicKey(clusterData.mangoProgramId);
const clusterUrl = IDS.cluster_urls[cluster];
const connection = new Connection(clusterUrl, 'singleGossip');
const client = new MangoClient(connection, mangoProgramIdPk);
const mangoGroup = await client.getMangoGroup(mangoGroupKey);
const mangoAccount = await client.createMangoAccount(mangoGroup, wallet?.adapter, 23);
})();
const mangoAccount = await client.createMangoAccount(mangoGroup, wallet.adapter, 23);
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
msg,
program::{invoke_signed},
program_error::ProgramError,
pubkey::Pubkey,
system_instruction,
system_program::ID as SYSTEM_PROGRAM_ID,
sysvar::{rent::Rent, Sysvar},
};
// Add this to Cargo.toml to be able to use the mango program repository as a crate
// mango = { version = "3.4.2", git = "https://github.com/blockworks-foundation/mango-v3.git", default-features=false, features = ["no-entrypoint", "program"] }
use mango::instruction::MangoInstruction;
use crate::instruction::ProgramInstruction;
pub struct Processor {}
impl Processor {
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8]
) -> ProgramResult {
let instruction = ProgramInstruction::try_from_slice(instruction_data)
.map_err(|_| ProgramError::InvalidInstructionData)?;
let accounts_iter = &mut accounts.iter();
match instruction {
ProgramInstruction::CreateMangoAccount { account_num } => {
msg!("Instruction: CreateMangoAccount");
let mango_group_ai = next_account_info(accounts_iter)?;
let mango_account_ai = next_account_info(accounts_iter)?;
let user = next_account_info(accounts_iter)?;
let mango_program = next_account_info(accounts_iter)?;
let system_program = next_account_info(accounts_iter)?;
invoke(
&mango::instruction::create_mango_account(
*mango_program.key,
*mango_account_ai.key,
*user.key,
*system_program.key,
*user.key,
*account_num
),
&[
mango_program.clone(),
user.clone(),
system_program.clone(),
mango_account_ai.clone(),
]
)?;
}
}
Ok(())
}
}
invoke(
&mango_v3::instruction::create_mango_account(
*mango_program.key,
*mango_account_ai.key,
*mango_account_ai.key,
*user.key,
*system_program.key,
*user.key,
*account_num
),
&[
mango_program.clone(),
user.clone(),
system_program.clone(),
mango_account_ai.clone(),
]
)?;
How to deposit USDC into a Mango Account
After creating a mango account, you'll need to fund it with tokens for trading. You can find the reference for the deposit method here.
import { useWallet } from "@solana/wallet-adapter-react";
import { Connection, PublicKey } from "@solana/web3.js";
import { IDS, MangoClient, Config, getTokenAccountsByOwnerWithWrappedSol } from "@blockworks-foundation/mango-client";
(async () => {
const { wallet } = useWallet();
const cluster = 'devnet';
const group = 'devnet.3';
const config = new Config(IDS);
const groupConfig = config.getGroup(cluster, group);
if(!groupConfig) {
throw new Error("unable to get mango group config");
}
const mangoGroupKey = groupConfig.publicKey;
const clusterData = IDS.groups.find((g) => {
return g.name == group && g.cluster == cluster;
});
const mangoProgramIdPk = new PublicKey(clusterData.mangoProgramId);
const clusterUrl = IDS.cluster_urls[cluster];
const connection = new Connection(clusterUrl, 'singleGossip');
const client = new MangoClient(connection, mangoProgramIdPk);
const mangoGroup = await client.getMangoGroup(mangoGroupKey);
const mangoAccount = await client.createMangoAccount(mangoGroup, wallet?.adapter, 23);
const tokenAccounts = await getTokenAccountsByOwnerWithWrappedSol(
connection,
wallet.adapter.publicKey
);
const tokenAccount = tokenAccounts.find((account) => account.mint.equals(new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'))); // USDC mint address
const tokenIndex = mangoGroup.getTokenIndex(tokenAccount.mint);
await client.deposit(
mangoGroup,
mangoAccount,
wallet?.adapter,
mangoGroup.tokens[tokenIndex].rootBank,
mangoGroup.rootBankAccounts[tokenIndex].nodeBankAccounts[0].publicKey,
mangoGroup.rootBankAccounts[tokenIndex].nodeBankAccounts[0].vault,
tokenAccount.publicKey,
Number(4)
);
})();
await client.deposit(
mangoGroup,
mangoAccount,
wallet?.adapter,
mangoGroup.tokens[tokenIndex].rootBank,
mangoGroup.rootBankAccounts[tokenIndex].nodeBankAccounts[0].publicKey,
mangoGroup.rootBankAccounts[tokenIndex].nodeBankAccounts[0].vault,
tokenAccount.publicKey,
Number(4)
);
How to place a spot order
Mango interacts with Serum Protocol to place spot orders on markets. You can place a spot order by doing this. You can find the reference for the placeSpotOrder function here. Mango has a config file that contains information on groups, markets, tokens and oracles, you can find it here. We use information from that file to find the right group and market.
import { useWallet } from "@solana/wallet-adapter-react";
import { Connection, PublicKey } from "@solana/web3.js";
import { Market } from '@project-serum/serum';
import { IDS, MangoClient, Config, getSpotMarketByBaseSymbol } from "@blockworks-foundation/mango-client";
(async () => {
const { wallet } = useWallet();
const cluster = 'devnet';
const group = 'devnet.3';
const config = new Config(IDS);
const groupConfig = config.getGroup(cluster, group);
if(!groupConfig) {
throw new Error("unable to get mango group config");
}
const mangoGroupKey = groupConfig.publicKey;
const clusterData = IDS.groups.find((g) => {
return g.name == group && g.cluster == cluster;
});
const mangoProgramIdPk = new PublicKey(clusterData.mangoProgramId);
const clusterUrl = IDS.cluster_urls[cluster];
const connection = new Connection(clusterUrl, 'singleGossip');
const client = new MangoClient(connection, mangoProgramIdPk);
const mangoGroup = await client.getMangoGroup(mangoGroupKey);
const mangoAccount = await client.createMangoAccount(mangoGroup, wallet?.adapter, 23);
const marketConfig = getSpotMarketByBaseSymbol(groupConfig, "SOL");
const market = await Market.load(connection, marketConfig.publicKey, {}, groupConfig.serumProgramId);
await client.placeSpotOrder(
mangoGroup,
mangoAccount,
mangoGroup.mangoCache,
market,
wallet?.adapter,
'buy',
3,
3.5
)
})();
await client.placeSpotOrder(
mangoGroup,
mangoAccount,
mangoGroup.mangoCache,
market,
wallet?.adapter,
'buy',
3,
3.5
)
How to load bids
Mango uses the market information from Serum Protocol to load bids. You can load them directly from Serum to work with on Mango. You can find out more about Serum's markets here
import { Connection, PublicKey } from "@solana/web3.js";
import { Market } from '@project-serum/serum';
import { IDS, Config, getSpotMarketByBaseSymbol } from "@blockworks-foundation/mango-client";
(async () => {
const cluster = 'devnet';
const group = 'devnet.3';
const config = new Config(IDS);
const groupConfig = config.getGroup(cluster, group);
if(!groupConfig) {
throw new Error("unable to get mango group config");
}
const clusterUrl = IDS.cluster_urls[cluster];
const connection = new Connection(clusterUrl, 'singleGossip');
const marketConfig = getSpotMarketByBaseSymbol(groupConfig, "SOL");
const market = await Market.load(connection, marketConfig.publicKey, {}, groupConfig.serumProgramId);
const bids = market.loadBids(connection);
})();
const bids = market.loadBids(connection);
How to load asks
Mango uses the market information from Serum Protocol to load asks. You can load them directly from Serum to work with on Mango. You can find out more about Serum's markets here
import { Connection, PublicKey } from "@solana/web3.js";
import { Market } from '@project-serum/serum';
import { IDS, MangoClient, Config, getSpotMarketByBaseSymbol } from "@blockworks-foundation/mango-client";
(async () => {
const cluster = 'devnet';
const group = 'devnet.3';
const config = new Config(IDS);
const groupConfig = config.getGroup(cluster, group);
if(!groupConfig) {
throw new Error("unable to get mango group config");
}
const clusterUrl = IDS.cluster_urls[cluster];
const connection = new Connection(clusterUrl, 'singleGossip');
const marketConfig = getSpotMarketByBaseSymbol(groupConfig, "SOL");
const market = await Market.load(connection, marketConfig.publicKey, {}, groupConfig.serumProgramId);
const asks = await market.loadBids(connection);
})();
const asks = await market.loadBids(connection);