use {
crate::types::{NonZeroPubkeyOption, OracleConfig},
carbon_core::{
account::AccountMetadata,
postgres::{
metadata::AccountRowMetadata,
primitives::{Pubkey, U128, U64, U8},
},
},
};
#[derive(sqlx::FromRow, Debug, Clone)]
pub struct MarketRow {
#[sqlx(flatten)]
pub account_metadata: AccountRowMetadata,
pub bump: U8,
pub base_decimals: U8,
pub quote_decimals: U8,
pub padding1: Vec<u8>,
pub market_authority: Pubkey,
pub time_expiry: i64,
pub collect_fee_admin: Pubkey,
pub open_orders_admin: sqlx::types::Json<NonZeroPubkeyOption>,
pub consume_events_admin: sqlx::types::Json<NonZeroPubkeyOption>,
pub close_market_admin: sqlx::types::Json<NonZeroPubkeyOption>,
pub name: Vec<u8>,
pub bids: Pubkey,
pub asks: Pubkey,
pub event_heap: Pubkey,
pub oracle_a: sqlx::types::Json<NonZeroPubkeyOption>,
pub oracle_b: sqlx::types::Json<NonZeroPubkeyOption>,
pub oracle_config: sqlx::types::Json<OracleConfig>,
pub quote_lot_size: i64,
pub base_lot_size: i64,
pub seq_num: U64,
pub registration_time: i64,
pub maker_fee: i64,
pub taker_fee: i64,
pub fees_accrued: U128,
pub fees_to_referrers: U128,
pub referrer_rebates_accrued: U64,
pub fees_available: U64,
pub maker_volume: U128,
pub taker_volume_wo_oo: U128,
pub base_mint: Pubkey,
pub quote_mint: Pubkey,
pub market_base_vault: Pubkey,
pub base_deposit_total: U64,
pub market_quote_vault: Pubkey,
pub quote_deposit_total: U64,
pub reserved: Vec<u8>,
}
impl MarketRow {
pub fn from_parts(source: crate::accounts::market::Market, metadata: AccountMetadata) -> Self {
Self {
account_metadata: metadata.into(),
bump: source.bump.into(),
base_decimals: source.base_decimals.into(),
quote_decimals: source.quote_decimals.into(),
padding1: source.padding1.to_vec(),
market_authority: source.market_authority.into(),
time_expiry: source.time_expiry,
collect_fee_admin: source.collect_fee_admin.into(),
open_orders_admin: sqlx::types::Json(source.open_orders_admin),
consume_events_admin: sqlx::types::Json(source.consume_events_admin),
close_market_admin: sqlx::types::Json(source.close_market_admin),
name: source.name.to_vec(),
bids: source.bids.into(),
asks: source.asks.into(),
event_heap: source.event_heap.into(),
oracle_a: sqlx::types::Json(source.oracle_a),
oracle_b: sqlx::types::Json(source.oracle_b),
oracle_config: sqlx::types::Json(source.oracle_config),
quote_lot_size: source.quote_lot_size,
base_lot_size: source.base_lot_size,
seq_num: source.seq_num.into(),
registration_time: source.registration_time,
maker_fee: source.maker_fee,
taker_fee: source.taker_fee,
fees_accrued: source.fees_accrued.into(),
fees_to_referrers: source.fees_to_referrers.into(),
referrer_rebates_accrued: source.referrer_rebates_accrued.into(),
fees_available: source.fees_available.into(),
maker_volume: source.maker_volume.into(),
taker_volume_wo_oo: source.taker_volume_wo_oo.into(),
base_mint: source.base_mint.into(),
quote_mint: source.quote_mint.into(),
market_base_vault: source.market_base_vault.into(),
base_deposit_total: source.base_deposit_total.into(),
market_quote_vault: source.market_quote_vault.into(),
quote_deposit_total: source.quote_deposit_total.into(),
reserved: source.reserved.to_vec(),
}
}
}
impl TryFrom<MarketRow> for crate::accounts::market::Market {
type Error = carbon_core::error::Error;
fn try_from(source: MarketRow) -> Result<Self, Self::Error> {
Ok(Self {
bump: source.bump.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
base_decimals: source.base_decimals.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
quote_decimals: source.quote_decimals.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
padding1: source.padding1.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 5 bytes"
.to_string(),
)
})?,
market_authority: *source.market_authority,
time_expiry: source.time_expiry,
collect_fee_admin: *source.collect_fee_admin,
open_orders_admin: source.open_orders_admin.0,
consume_events_admin: source.consume_events_admin.0,
close_market_admin: source.close_market_admin.0,
name: source.name.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 16 bytes"
.to_string(),
)
})?,
bids: *source.bids,
asks: *source.asks,
event_heap: *source.event_heap,
oracle_a: source.oracle_a.0,
oracle_b: source.oracle_b.0,
oracle_config: source.oracle_config.0,
quote_lot_size: source.quote_lot_size,
base_lot_size: source.base_lot_size,
seq_num: *source.seq_num,
registration_time: source.registration_time,
maker_fee: source.maker_fee,
taker_fee: source.taker_fee,
fees_accrued: *source.fees_accrued,
fees_to_referrers: *source.fees_to_referrers,
referrer_rebates_accrued: *source.referrer_rebates_accrued,
fees_available: *source.fees_available,
maker_volume: *source.maker_volume,
taker_volume_wo_oo: *source.taker_volume_wo_oo,
base_mint: *source.base_mint,
quote_mint: *source.quote_mint,
market_base_vault: *source.market_base_vault,
base_deposit_total: *source.base_deposit_total,
market_quote_vault: *source.market_quote_vault,
quote_deposit_total: *source.quote_deposit_total,
reserved: source.reserved.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 128 bytes"
.to_string(),
)
})?,
})
}
}
impl carbon_core::postgres::operations::Table for crate::accounts::market::Market {
fn table() -> &'static str {
"market_account"
}
fn columns() -> Vec<&'static str> {
vec![
"__pubkey",
"__slot",
"bump",
"base_decimals",
"quote_decimals",
"padding1",
"market_authority",
"time_expiry",
"collect_fee_admin",
"open_orders_admin",
"consume_events_admin",
"close_market_admin",
"name",
"bids",
"asks",
"event_heap",
"oracle_a",
"oracle_b",
"oracle_config",
"quote_lot_size",
"base_lot_size",
"seq_num",
"registration_time",
"maker_fee",
"taker_fee",
"fees_accrued",
"fees_to_referrers",
"referrer_rebates_accrued",
"fees_available",
"maker_volume",
"taker_volume_wo_oo",
"base_mint",
"quote_mint",
"market_base_vault",
"base_deposit_total",
"market_quote_vault",
"quote_deposit_total",
"reserved",
]
}
}
#[async_trait::async_trait]
impl carbon_core::postgres::operations::Insert for MarketRow {
async fn insert(&self, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(r#"
INSERT INTO market_account (
"bump",
"base_decimals",
"quote_decimals",
"padding1",
"market_authority",
"time_expiry",
"collect_fee_admin",
"open_orders_admin",
"consume_events_admin",
"close_market_admin",
"name",
"bids",
"asks",
"event_heap",
"oracle_a",
"oracle_b",
"oracle_config",
"quote_lot_size",
"base_lot_size",
"seq_num",
"registration_time",
"maker_fee",
"taker_fee",
"fees_accrued",
"fees_to_referrers",
"referrer_rebates_accrued",
"fees_available",
"maker_volume",
"taker_volume_wo_oo",
"base_mint",
"quote_mint",
"market_base_vault",
"base_deposit_total",
"market_quote_vault",
"quote_deposit_total",
"reserved",
__pubkey, __slot
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38
)"#)
.bind(self.bump)
.bind(self.base_decimals)
.bind(self.quote_decimals)
.bind(&self.padding1)
.bind(self.market_authority)
.bind(self.time_expiry)
.bind(self.collect_fee_admin)
.bind(&self.open_orders_admin)
.bind(&self.consume_events_admin)
.bind(&self.close_market_admin)
.bind(&self.name)
.bind(self.bids)
.bind(self.asks)
.bind(self.event_heap)
.bind(&self.oracle_a)
.bind(&self.oracle_b)
.bind(&self.oracle_config)
.bind(self.quote_lot_size)
.bind(self.base_lot_size)
.bind(&self.seq_num)
.bind(self.registration_time)
.bind(self.maker_fee)
.bind(self.taker_fee)
.bind(&self.fees_accrued)
.bind(&self.fees_to_referrers)
.bind(&self.referrer_rebates_accrued)
.bind(&self.fees_available)
.bind(&self.maker_volume)
.bind(&self.taker_volume_wo_oo)
.bind(self.base_mint)
.bind(self.quote_mint)
.bind(self.market_base_vault)
.bind(&self.base_deposit_total)
.bind(self.market_quote_vault)
.bind(&self.quote_deposit_total)
.bind(&self.reserved)
.bind(self.account_metadata.pubkey)
.bind(&self.account_metadata.slot)
.execute(pool).await
.map_err(|e| carbon_core::error::Error::Custom(e.to_string()))?;
Ok(())
}
}
#[async_trait::async_trait]
impl carbon_core::postgres::operations::Upsert for MarketRow {
async fn upsert(&self, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(r#"INSERT INTO market_account (
"bump",
"base_decimals",
"quote_decimals",
"padding1",
"market_authority",
"time_expiry",
"collect_fee_admin",
"open_orders_admin",
"consume_events_admin",
"close_market_admin",
"name",
"bids",
"asks",
"event_heap",
"oracle_a",
"oracle_b",
"oracle_config",
"quote_lot_size",
"base_lot_size",
"seq_num",
"registration_time",
"maker_fee",
"taker_fee",
"fees_accrued",
"fees_to_referrers",
"referrer_rebates_accrued",
"fees_available",
"maker_volume",
"taker_volume_wo_oo",
"base_mint",
"quote_mint",
"market_base_vault",
"base_deposit_total",
"market_quote_vault",
"quote_deposit_total",
"reserved",
__pubkey, __slot
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38
) ON CONFLICT (
__pubkey
) DO UPDATE SET
"bump" = EXCLUDED."bump",
"base_decimals" = EXCLUDED."base_decimals",
"quote_decimals" = EXCLUDED."quote_decimals",
"padding1" = EXCLUDED."padding1",
"market_authority" = EXCLUDED."market_authority",
"time_expiry" = EXCLUDED."time_expiry",
"collect_fee_admin" = EXCLUDED."collect_fee_admin",
"open_orders_admin" = EXCLUDED."open_orders_admin",
"consume_events_admin" = EXCLUDED."consume_events_admin",
"close_market_admin" = EXCLUDED."close_market_admin",
"name" = EXCLUDED."name",
"bids" = EXCLUDED."bids",
"asks" = EXCLUDED."asks",
"event_heap" = EXCLUDED."event_heap",
"oracle_a" = EXCLUDED."oracle_a",
"oracle_b" = EXCLUDED."oracle_b",
"oracle_config" = EXCLUDED."oracle_config",
"quote_lot_size" = EXCLUDED."quote_lot_size",
"base_lot_size" = EXCLUDED."base_lot_size",
"seq_num" = EXCLUDED."seq_num",
"registration_time" = EXCLUDED."registration_time",
"maker_fee" = EXCLUDED."maker_fee",
"taker_fee" = EXCLUDED."taker_fee",
"fees_accrued" = EXCLUDED."fees_accrued",
"fees_to_referrers" = EXCLUDED."fees_to_referrers",
"referrer_rebates_accrued" = EXCLUDED."referrer_rebates_accrued",
"fees_available" = EXCLUDED."fees_available",
"maker_volume" = EXCLUDED."maker_volume",
"taker_volume_wo_oo" = EXCLUDED."taker_volume_wo_oo",
"base_mint" = EXCLUDED."base_mint",
"quote_mint" = EXCLUDED."quote_mint",
"market_base_vault" = EXCLUDED."market_base_vault",
"base_deposit_total" = EXCLUDED."base_deposit_total",
"market_quote_vault" = EXCLUDED."market_quote_vault",
"quote_deposit_total" = EXCLUDED."quote_deposit_total",
"reserved" = EXCLUDED."reserved",
__slot = EXCLUDED.__slot
"#)
.bind(self.bump)
.bind(self.base_decimals)
.bind(self.quote_decimals)
.bind(&self.padding1)
.bind(self.market_authority)
.bind(self.time_expiry)
.bind(self.collect_fee_admin)
.bind(&self.open_orders_admin)
.bind(&self.consume_events_admin)
.bind(&self.close_market_admin)
.bind(&self.name)
.bind(self.bids)
.bind(self.asks)
.bind(self.event_heap)
.bind(&self.oracle_a)
.bind(&self.oracle_b)
.bind(&self.oracle_config)
.bind(self.quote_lot_size)
.bind(self.base_lot_size)
.bind(&self.seq_num)
.bind(self.registration_time)
.bind(self.maker_fee)
.bind(self.taker_fee)
.bind(&self.fees_accrued)
.bind(&self.fees_to_referrers)
.bind(&self.referrer_rebates_accrued)
.bind(&self.fees_available)
.bind(&self.maker_volume)
.bind(&self.taker_volume_wo_oo)
.bind(self.base_mint)
.bind(self.quote_mint)
.bind(self.market_base_vault)
.bind(&self.base_deposit_total)
.bind(self.market_quote_vault)
.bind(&self.quote_deposit_total)
.bind(&self.reserved)
.bind(self.account_metadata.pubkey)
.bind(&self.account_metadata.slot)
.execute(pool).await
.map_err(|e| carbon_core::error::Error::Custom(e.to_string()))?;
Ok(())
}
}
#[async_trait::async_trait]
impl carbon_core::postgres::operations::Delete for MarketRow {
type Key = carbon_core::postgres::primitives::Pubkey;
async fn delete(key: Self::Key, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(
r#"DELETE FROM market_account WHERE
__pubkey = $1
"#,
)
.bind(key)
.execute(pool)
.await
.map_err(|e| carbon_core::error::Error::Custom(e.to_string()))?;
Ok(())
}
}
#[async_trait::async_trait]
impl carbon_core::postgres::operations::Lookup for MarketRow {
type Key = carbon_core::postgres::primitives::Pubkey;
async fn lookup(
key: Self::Key,
pool: &sqlx::PgPool,
) -> carbon_core::error::CarbonResult<Option<Self>> {
let row = sqlx::query_as(
r#"SELECT * FROM market_account WHERE
__pubkey = $1
"#,
)
.bind(key)
.fetch_optional(pool)
.await
.map_err(|e| carbon_core::error::Error::Custom(e.to_string()))?;
Ok(row)
}
}
pub struct MarketMigrationOperation;
#[async_trait::async_trait]
impl sqlx_migrator::Operation<sqlx::Postgres> for MarketMigrationOperation {
async fn up(
&self,
connection: &mut sqlx::PgConnection,
) -> Result<(), sqlx_migrator::error::Error> {
sqlx::query(
r#"CREATE TABLE IF NOT EXISTS market_account (
-- Account data
"bump" INT2 NOT NULL,
"base_decimals" INT2 NOT NULL,
"quote_decimals" INT2 NOT NULL,
"padding1" BYTEA NOT NULL,
"market_authority" BYTEA NOT NULL,
"time_expiry" INT8 NOT NULL,
"collect_fee_admin" BYTEA NOT NULL,
"open_orders_admin" JSONB NOT NULL,
"consume_events_admin" JSONB NOT NULL,
"close_market_admin" JSONB NOT NULL,
"name" BYTEA NOT NULL,
"bids" BYTEA NOT NULL,
"asks" BYTEA NOT NULL,
"event_heap" BYTEA NOT NULL,
"oracle_a" JSONB NOT NULL,
"oracle_b" JSONB NOT NULL,
"oracle_config" JSONB NOT NULL,
"quote_lot_size" INT8 NOT NULL,
"base_lot_size" INT8 NOT NULL,
"seq_num" NUMERIC(20) NOT NULL,
"registration_time" INT8 NOT NULL,
"maker_fee" INT8 NOT NULL,
"taker_fee" INT8 NOT NULL,
"fees_accrued" NUMERIC(39) NOT NULL,
"fees_to_referrers" NUMERIC(39) NOT NULL,
"referrer_rebates_accrued" NUMERIC(20) NOT NULL,
"fees_available" NUMERIC(20) NOT NULL,
"maker_volume" NUMERIC(39) NOT NULL,
"taker_volume_wo_oo" NUMERIC(39) NOT NULL,
"base_mint" BYTEA NOT NULL,
"quote_mint" BYTEA NOT NULL,
"market_base_vault" BYTEA NOT NULL,
"base_deposit_total" NUMERIC(20) NOT NULL,
"market_quote_vault" BYTEA NOT NULL,
"quote_deposit_total" NUMERIC(20) NOT NULL,
"reserved" BYTEA NOT NULL,
-- Account metadata
__pubkey BYTEA NOT NULL,
__slot NUMERIC(20),
PRIMARY KEY (__pubkey)
)"#,
)
.execute(connection)
.await?;
Ok(())
}
async fn down(
&self,
connection: &mut sqlx::PgConnection,
) -> Result<(), sqlx_migrator::error::Error> {
sqlx::query(r#"DROP TABLE IF EXISTS market_account"#)
.execute(connection)
.await?;
Ok(())
}
}