keeper: consume events
Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
7abfc417ac
commit
5266668bc2
|
@ -0,0 +1,133 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use anchor_lang::{AccountDeserialize, __private::bytemuck::cast_ref};
|
||||||
|
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use mango_v4::state::{EventQueue, EventType, FillEvent, OutEvent, PerpMarket};
|
||||||
|
|
||||||
|
use solana_sdk::{
|
||||||
|
instruction::{AccountMeta, Instruction},
|
||||||
|
pubkey::Pubkey,
|
||||||
|
};
|
||||||
|
use tokio::time;
|
||||||
|
|
||||||
|
use crate::MangoClient;
|
||||||
|
|
||||||
|
pub async fn loop_blocking(
|
||||||
|
mango_client: &'static MangoClient,
|
||||||
|
pk: Pubkey,
|
||||||
|
perp_market: PerpMarket,
|
||||||
|
) {
|
||||||
|
let mut interval = time::interval(Duration::from_secs(5));
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
perform_operation(mango_client, pk, perp_market).expect("Something went wrong here...");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn perform_operation(
|
||||||
|
mango_client: &'static MangoClient,
|
||||||
|
pk: Pubkey,
|
||||||
|
perp_market: PerpMarket,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut event_queue = match get_event_queue(mango_client, &perp_market) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(value) => return value,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ams_ = vec![];
|
||||||
|
|
||||||
|
for _ in 0..10 {
|
||||||
|
let event = match event_queue.peek_front() {
|
||||||
|
None => break,
|
||||||
|
Some(e) => e,
|
||||||
|
};
|
||||||
|
match EventType::try_from(event.event_type)? {
|
||||||
|
EventType::Fill => {
|
||||||
|
let fill: &FillEvent = cast_ref(event);
|
||||||
|
ams_.push(AccountMeta {
|
||||||
|
pubkey: fill.maker,
|
||||||
|
is_signer: false,
|
||||||
|
is_writable: true,
|
||||||
|
});
|
||||||
|
ams_.push(AccountMeta {
|
||||||
|
pubkey: fill.taker,
|
||||||
|
is_signer: false,
|
||||||
|
is_writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
EventType::Out => {
|
||||||
|
let out: &OutEvent = cast_ref(event);
|
||||||
|
ams_.push(AccountMeta {
|
||||||
|
pubkey: out.owner,
|
||||||
|
is_signer: false,
|
||||||
|
is_writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
EventType::Liquidate => {}
|
||||||
|
}
|
||||||
|
event_queue.pop_front()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sig_result = mango_client
|
||||||
|
.program()
|
||||||
|
.request()
|
||||||
|
.instruction(Instruction {
|
||||||
|
program_id: mango_v4::id(),
|
||||||
|
accounts: {
|
||||||
|
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
||||||
|
&mango_v4::accounts::PerpConsumeEvents {
|
||||||
|
group: perp_market.group,
|
||||||
|
perp_market: pk,
|
||||||
|
event_queue: perp_market.event_queue,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
ams.append(&mut ams_);
|
||||||
|
ams
|
||||||
|
},
|
||||||
|
data: anchor_lang::InstructionData::data(&mango_v4::instruction::PerpConsumeEvents {
|
||||||
|
limit: 10,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.send();
|
||||||
|
match sig_result {
|
||||||
|
Ok(sig) => {
|
||||||
|
info!(
|
||||||
|
"Crank: consume event for perp_market {:?} ix signature: {:?}",
|
||||||
|
format!("{: >6}", perp_market.name()),
|
||||||
|
sig
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => error!("Crank: {:?}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_event_queue(
|
||||||
|
mango_client: &MangoClient,
|
||||||
|
perp_market: &PerpMarket,
|
||||||
|
) -> Result<mango_v4::state::EventQueue, Result<(), anyhow::Error>> {
|
||||||
|
let event_queue_opt: Option<EventQueue> = {
|
||||||
|
let res = mango_client
|
||||||
|
.rpc
|
||||||
|
.get_account_with_commitment(&perp_market.event_queue, mango_client.commitment);
|
||||||
|
|
||||||
|
let res = match res {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("{}", e);
|
||||||
|
return Err(Ok(()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = res.value.unwrap().data;
|
||||||
|
let mut data_slice: &[u8] = &data;
|
||||||
|
AccountDeserialize::try_deserialize(&mut data_slice).ok()
|
||||||
|
};
|
||||||
|
let mut event_queue = event_queue_opt.unwrap();
|
||||||
|
Ok(event_queue)
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
use crate::{consume_events, update_index, MangoClient};
|
||||||
|
|
||||||
|
use anyhow::ensure;
|
||||||
|
|
||||||
|
use mango_v4::state::{Bank, PerpMarket};
|
||||||
|
|
||||||
|
use solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType};
|
||||||
|
|
||||||
|
use solana_sdk::{pubkey::Pubkey, signer::Signer};
|
||||||
|
|
||||||
|
pub async fn runner(mango_client: &'static MangoClient) -> Result<(), anyhow::Error> {
|
||||||
|
// Collect all banks for a group belonging to an admin
|
||||||
|
let banks = mango_client
|
||||||
|
.program()
|
||||||
|
.accounts::<Bank>(vec![RpcFilterType::Memcmp(Memcmp {
|
||||||
|
offset: 24,
|
||||||
|
bytes: MemcmpEncodedBytes::Base58({
|
||||||
|
// find group belonging to admin
|
||||||
|
Pubkey::find_program_address(
|
||||||
|
&["Group".as_ref(), mango_client.admin.pubkey().as_ref()],
|
||||||
|
&mango_client.program().id(),
|
||||||
|
)
|
||||||
|
.0
|
||||||
|
.to_string()
|
||||||
|
}),
|
||||||
|
encoding: None,
|
||||||
|
})])?;
|
||||||
|
|
||||||
|
ensure!(!banks.is_empty());
|
||||||
|
|
||||||
|
let handles1 = banks
|
||||||
|
.iter()
|
||||||
|
.map(|(pk, bank)| update_index::loop_blocking(mango_client, *pk, *bank))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Collect all perp markets for a group belonging to an admin
|
||||||
|
let perp_markets =
|
||||||
|
mango_client
|
||||||
|
.program()
|
||||||
|
.accounts::<PerpMarket>(vec![RpcFilterType::Memcmp(Memcmp {
|
||||||
|
offset: 24,
|
||||||
|
bytes: MemcmpEncodedBytes::Base58({
|
||||||
|
// find group belonging to admin
|
||||||
|
Pubkey::find_program_address(
|
||||||
|
&["Group".as_ref(), mango_client.admin.pubkey().as_ref()],
|
||||||
|
&mango_client.program().id(),
|
||||||
|
)
|
||||||
|
.0
|
||||||
|
.to_string()
|
||||||
|
}),
|
||||||
|
encoding: None,
|
||||||
|
})])?;
|
||||||
|
|
||||||
|
ensure!(!perp_markets.is_empty());
|
||||||
|
|
||||||
|
let handles2 = perp_markets
|
||||||
|
.iter()
|
||||||
|
.map(|(pk, perp_market)| consume_events::loop_blocking(mango_client, *pk, *perp_market))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
futures::join!(
|
||||||
|
futures::future::join_all(handles1),
|
||||||
|
futures::future::join_all(handles2)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
mod consume_events;
|
||||||
|
mod crank;
|
||||||
mod update_index;
|
mod update_index;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -92,7 +94,6 @@ struct Cli {
|
||||||
command: Command,
|
command: Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
// future: more subcommands e.g. Liquidator
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
enum Command {
|
enum Command {
|
||||||
Crank {},
|
Crank {},
|
||||||
|
@ -146,10 +147,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
let cluster = Cluster::Custom(rpc_url, ws_url);
|
let cluster = Cluster::Custom(rpc_url, ws_url);
|
||||||
let commitment = match command {
|
let commitment = match command {
|
||||||
Command::Crank { .. } => CommitmentConfig::confirmed(),
|
Command::Crank { .. } => CommitmentConfig::confirmed(),
|
||||||
Command::Liquidator {} => CommitmentConfig::confirmed(),
|
Command::Liquidator {} => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// let mango_client = Arc::new(MangoClient::new(cluster, commitment, payer, admin));
|
|
||||||
let mango_client: &'static _ = Box::leak(Box::new(MangoClient::new(
|
let mango_client: &'static _ = Box::leak(Box::new(MangoClient::new(
|
||||||
cluster, commitment, payer, admin,
|
cluster, commitment, payer, admin,
|
||||||
)));
|
)));
|
||||||
|
@ -161,10 +161,12 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
Command::Crank { .. } => {
|
Command::Crank { .. } => {
|
||||||
let x: Result<(), anyhow::Error> = rt.block_on(update_index::runner(mango_client));
|
let x: Result<(), anyhow::Error> = rt.block_on(crank::runner(mango_client));
|
||||||
x.expect("Something went wrong here...");
|
x.expect("Something went wrong here...");
|
||||||
}
|
}
|
||||||
Command::Liquidator { .. } => {}
|
Command::Liquidator { .. } => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,53 +1,14 @@
|
||||||
use std::{time::Duration};
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
||||||
use anyhow::ensure;
|
|
||||||
|
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use mango_v4::state::Bank;
|
use mango_v4::state::Bank;
|
||||||
|
|
||||||
use solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType};
|
use solana_sdk::{instruction::Instruction, pubkey::Pubkey};
|
||||||
|
|
||||||
use solana_sdk::{
|
|
||||||
instruction::Instruction,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
signer::{Signer},
|
|
||||||
};
|
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
use crate::MangoClient;
|
use crate::MangoClient;
|
||||||
|
|
||||||
pub async fn runner(mango_client: &'static MangoClient) -> Result<(), anyhow::Error> {
|
pub async fn loop_blocking(mango_client: &'static MangoClient, pk: Pubkey, bank: Bank) {
|
||||||
// Collect all banks for a group belonging to an admin
|
|
||||||
let banks = mango_client
|
|
||||||
.program()
|
|
||||||
.accounts::<Bank>(vec![RpcFilterType::Memcmp(Memcmp {
|
|
||||||
offset: 24,
|
|
||||||
bytes: MemcmpEncodedBytes::Base58({
|
|
||||||
// find group belonging to admin
|
|
||||||
Pubkey::find_program_address(
|
|
||||||
&["Group".as_ref(), mango_client.admin.pubkey().as_ref()],
|
|
||||||
&mango_client.program().id(),
|
|
||||||
)
|
|
||||||
.0
|
|
||||||
.to_string()
|
|
||||||
}),
|
|
||||||
encoding: None,
|
|
||||||
})])?;
|
|
||||||
|
|
||||||
ensure!(!banks.is_empty());
|
|
||||||
|
|
||||||
let handles = banks
|
|
||||||
.iter()
|
|
||||||
.map(|(pk, bank)| loop_blocking(mango_client, *pk, *bank))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
futures::join!(futures::future::join_all(handles));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn loop_blocking(mango_client: &'static MangoClient, pk: Pubkey, bank: Bank) {
|
|
||||||
let mut interval = time::interval(Duration::from_secs(5));
|
let mut interval = time::interval(Duration::from_secs(5));
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
|
|
|
@ -3,6 +3,7 @@ use fixed::types::I80F48;
|
||||||
|
|
||||||
use crate::error::MangoError;
|
use crate::error::MangoError;
|
||||||
use crate::state::*;
|
use crate::state::*;
|
||||||
|
use crate::util::fill16_from_str;
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
#[instruction(perp_market_index: PerpMarketIndex)]
|
#[instruction(perp_market_index: PerpMarketIndex)]
|
||||||
|
@ -43,6 +44,7 @@ pub struct PerpCreateMarket<'info> {
|
||||||
pub fn perp_create_market(
|
pub fn perp_create_market(
|
||||||
ctx: Context<PerpCreateMarket>,
|
ctx: Context<PerpCreateMarket>,
|
||||||
perp_market_index: PerpMarketIndex,
|
perp_market_index: PerpMarketIndex,
|
||||||
|
name: String,
|
||||||
base_token_index_opt: Option<TokenIndex>,
|
base_token_index_opt: Option<TokenIndex>,
|
||||||
quote_token_index: TokenIndex,
|
quote_token_index: TokenIndex,
|
||||||
quote_lot_size: i64,
|
quote_lot_size: i64,
|
||||||
|
@ -57,6 +59,7 @@ pub fn perp_create_market(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut perp_market = ctx.accounts.perp_market.load_init()?;
|
let mut perp_market = ctx.accounts.perp_market.load_init()?;
|
||||||
*perp_market = PerpMarket {
|
*perp_market = PerpMarket {
|
||||||
|
name: fill16_from_str(name)?,
|
||||||
group: ctx.accounts.group.key(),
|
group: ctx.accounts.group.key(),
|
||||||
oracle: ctx.accounts.oracle.key(),
|
oracle: ctx.accounts.oracle.key(),
|
||||||
bids: ctx.accounts.bids.key(),
|
bids: ctx.accounts.bids.key(),
|
||||||
|
@ -78,6 +81,7 @@ pub fn perp_create_market(
|
||||||
perp_market_index,
|
perp_market_index,
|
||||||
base_token_index: base_token_index_opt.ok_or(TokenIndex::MAX).unwrap(),
|
base_token_index: base_token_index_opt.ok_or(TokenIndex::MAX).unwrap(),
|
||||||
quote_token_index,
|
quote_token_index,
|
||||||
|
reserved: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut bids = ctx.accounts.bids.load_init()?;
|
let mut bids = ctx.accounts.bids.load_init()?;
|
||||||
|
|
|
@ -181,6 +181,7 @@ pub mod mango_v4 {
|
||||||
pub fn perp_create_market(
|
pub fn perp_create_market(
|
||||||
ctx: Context<PerpCreateMarket>,
|
ctx: Context<PerpCreateMarket>,
|
||||||
perp_market_index: PerpMarketIndex,
|
perp_market_index: PerpMarketIndex,
|
||||||
|
name: String,
|
||||||
base_token_index_opt: Option<TokenIndex>,
|
base_token_index_opt: Option<TokenIndex>,
|
||||||
quote_token_index: TokenIndex,
|
quote_token_index: TokenIndex,
|
||||||
quote_lot_size: i64,
|
quote_lot_size: i64,
|
||||||
|
@ -196,6 +197,7 @@ pub mod mango_v4 {
|
||||||
instructions::perp_create_market(
|
instructions::perp_create_market(
|
||||||
ctx,
|
ctx,
|
||||||
perp_market_index,
|
perp_market_index,
|
||||||
|
name,
|
||||||
base_token_index_opt,
|
base_token_index_opt,
|
||||||
quote_token_index,
|
quote_token_index,
|
||||||
quote_lot_size,
|
quote_lot_size,
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use anchor_lang::prelude::*;
|
use anchor_lang::prelude::*;
|
||||||
use fixed::types::I80F48;
|
use fixed::types::I80F48;
|
||||||
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
use crate::state::orderbook::order_type::Side;
|
use crate::state::orderbook::order_type::Side;
|
||||||
use crate::state::TokenIndex;
|
use crate::state::TokenIndex;
|
||||||
|
@ -9,6 +12,8 @@ pub type PerpMarketIndex = u16;
|
||||||
|
|
||||||
#[account(zero_copy)]
|
#[account(zero_copy)]
|
||||||
pub struct PerpMarket {
|
pub struct PerpMarket {
|
||||||
|
pub name: [u8; 16],
|
||||||
|
|
||||||
pub group: Pubkey,
|
pub group: Pubkey,
|
||||||
|
|
||||||
pub oracle: Pubkey,
|
pub oracle: Pubkey,
|
||||||
|
@ -60,6 +65,7 @@ pub struct PerpMarket {
|
||||||
|
|
||||||
/// PDA bump
|
/// PDA bump
|
||||||
pub bump: u8,
|
pub bump: u8,
|
||||||
|
pub reserved: [u8; 1],
|
||||||
|
|
||||||
/// Lookup indices
|
/// Lookup indices
|
||||||
pub perp_market_index: PerpMarketIndex,
|
pub perp_market_index: PerpMarketIndex,
|
||||||
|
@ -69,8 +75,19 @@ pub struct PerpMarket {
|
||||||
pub quote_token_index: TokenIndex,
|
pub quote_token_index: TokenIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const_assert_eq!(
|
||||||
|
size_of::<PerpMarket>(),
|
||||||
|
16 + 32 * 5 + 8 * 2 + 16 * 7 + 8 * 2 + 16 + 8
|
||||||
|
);
|
||||||
|
const_assert_eq!(size_of::<PerpMarket>() % 8, 0);
|
||||||
|
|
||||||
impl PerpMarket {
|
impl PerpMarket {
|
||||||
/// TODO why is this based on price?
|
pub fn name(&self) -> &str {
|
||||||
|
std::str::from_utf8(&self.name)
|
||||||
|
.unwrap()
|
||||||
|
.trim_matches(char::from(0))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gen_order_id(&mut self, side: Side, price: i64) -> i128 {
|
pub fn gen_order_id(&mut self, side: Side, price: i64) -> i128 {
|
||||||
self.seq_num += 1;
|
self.seq_num += 1;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,14 @@ pub struct Serum3Market {
|
||||||
const_assert_eq!(size_of::<Serum3Market>(), 16 + 32 * 3 + 3 * 2 + 1 + 1);
|
const_assert_eq!(size_of::<Serum3Market>(), 16 + 32 * 3 + 3 * 2 + 1 + 1);
|
||||||
const_assert_eq!(size_of::<Serum3Market>() % 8, 0);
|
const_assert_eq!(size_of::<Serum3Market>() % 8, 0);
|
||||||
|
|
||||||
|
impl Serum3Market {
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
std::str::from_utf8(&self.name)
|
||||||
|
.unwrap()
|
||||||
|
.trim_matches(char::from(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! serum_market_seeds {
|
macro_rules! serum_market_seeds {
|
||||||
( $acc:expr ) => {
|
( $acc:expr ) => {
|
||||||
|
|
|
@ -1293,6 +1293,7 @@ impl<'keypair> ClientInstruction for PerpCreateMarketInstruction<'keypair> {
|
||||||
) -> (Self::Accounts, instruction::Instruction) {
|
) -> (Self::Accounts, instruction::Instruction) {
|
||||||
let program_id = mango_v4::id();
|
let program_id = mango_v4::id();
|
||||||
let instruction = Self::Instruction {
|
let instruction = Self::Instruction {
|
||||||
|
name: "UUU-PERP".to_string(),
|
||||||
perp_market_index: self.perp_market_index,
|
perp_market_index: self.perp_market_index,
|
||||||
base_token_index_opt: Option::from(self.base_token_index),
|
base_token_index_opt: Option::from(self.base_token_index),
|
||||||
quote_token_index: self.quote_token_index,
|
quote_token_index: self.quote_token_index,
|
||||||
|
|
Loading…
Reference in New Issue