use {
crate::types::{FeeStructure, OracleGuardRails},
carbon_core::{
account::AccountMetadata,
postgres::{
metadata::AccountRowMetadata,
primitives::{Pubkey, U16, U32, U64, U8},
},
},
};
#[derive(sqlx::FromRow, Debug, Clone)]
pub struct StateRow {
#[sqlx(flatten)]
pub account_metadata: AccountRowMetadata,
pub admin: Pubkey,
pub whitelist_mint: Pubkey,
pub discount_mint: Pubkey,
pub signer: Pubkey,
pub srm_vault: Pubkey,
pub perp_fee_structure: sqlx::types::Json<FeeStructure>,
pub spot_fee_structure: sqlx::types::Json<FeeStructure>,
pub oracle_guard_rails: sqlx::types::Json<OracleGuardRails>,
pub number_of_authorities: U64,
pub number_of_sub_accounts: U64,
pub lp_cooldown_time: U64,
pub liquidation_margin_buffer_ratio: U32,
pub settlement_duration: U16,
pub number_of_markets: U16,
pub number_of_spot_markets: U16,
pub signer_nonce: U8,
pub min_perp_auction_duration: U8,
pub default_market_order_time_in_force: U8,
pub default_spot_auction_duration: U8,
pub exchange_status: U8,
pub liquidation_duration: U8,
pub initial_pct_to_liquidate: U16,
pub max_number_of_sub_accounts: U16,
pub max_initialize_user_fee: U16,
pub feature_bit_flags: U8,
pub lp_pool_feature_bit_flags: U8,
pub padding: Vec<u8>,
}
impl StateRow {
pub fn from_parts(source: crate::accounts::state::State, metadata: AccountMetadata) -> Self {
Self {
account_metadata: metadata.into(),
admin: source.admin.into(),
whitelist_mint: source.whitelist_mint.into(),
discount_mint: source.discount_mint.into(),
signer: source.signer.into(),
srm_vault: source.srm_vault.into(),
perp_fee_structure: sqlx::types::Json(source.perp_fee_structure),
spot_fee_structure: sqlx::types::Json(source.spot_fee_structure),
oracle_guard_rails: sqlx::types::Json(source.oracle_guard_rails),
number_of_authorities: source.number_of_authorities.into(),
number_of_sub_accounts: source.number_of_sub_accounts.into(),
lp_cooldown_time: source.lp_cooldown_time.into(),
liquidation_margin_buffer_ratio: source.liquidation_margin_buffer_ratio.into(),
settlement_duration: source.settlement_duration.into(),
number_of_markets: source.number_of_markets.into(),
number_of_spot_markets: source.number_of_spot_markets.into(),
signer_nonce: source.signer_nonce.into(),
min_perp_auction_duration: source.min_perp_auction_duration.into(),
default_market_order_time_in_force: source.default_market_order_time_in_force.into(),
default_spot_auction_duration: source.default_spot_auction_duration.into(),
exchange_status: source.exchange_status.into(),
liquidation_duration: source.liquidation_duration.into(),
initial_pct_to_liquidate: source.initial_pct_to_liquidate.into(),
max_number_of_sub_accounts: source.max_number_of_sub_accounts.into(),
max_initialize_user_fee: source.max_initialize_user_fee.into(),
feature_bit_flags: source.feature_bit_flags.into(),
lp_pool_feature_bit_flags: source.lp_pool_feature_bit_flags.into(),
padding: source.padding.to_vec(),
}
}
}
impl TryFrom<StateRow> for crate::accounts::state::State {
type Error = carbon_core::error::Error;
fn try_from(source: StateRow) -> Result<Self, Self::Error> {
Ok(Self {
admin: *source.admin,
whitelist_mint: *source.whitelist_mint,
discount_mint: *source.discount_mint,
signer: *source.signer,
srm_vault: *source.srm_vault,
perp_fee_structure: source.perp_fee_structure.0,
spot_fee_structure: source.spot_fee_structure.0,
oracle_guard_rails: source.oracle_guard_rails.0,
number_of_authorities: *source.number_of_authorities,
number_of_sub_accounts: *source.number_of_sub_accounts,
lp_cooldown_time: *source.lp_cooldown_time,
liquidation_margin_buffer_ratio: source
.liquidation_margin_buffer_ratio
.try_into()
.map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
settlement_duration: source.settlement_duration.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
number_of_markets: source.number_of_markets.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
number_of_spot_markets: source.number_of_spot_markets.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
signer_nonce: source.signer_nonce.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
min_perp_auction_duration: source.min_perp_auction_duration.try_into().map_err(
|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
},
)?,
default_market_order_time_in_force: source
.default_market_order_time_in_force
.try_into()
.map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
default_spot_auction_duration: source
.default_spot_auction_duration
.try_into()
.map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
exchange_status: source.exchange_status.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
liquidation_duration: source.liquidation_duration.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
initial_pct_to_liquidate: source.initial_pct_to_liquidate.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
max_number_of_sub_accounts: source.max_number_of_sub_accounts.try_into().map_err(
|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
},
)?,
max_initialize_user_fee: source.max_initialize_user_fee.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
feature_bit_flags: source.feature_bit_flags.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
lp_pool_feature_bit_flags: source.lp_pool_feature_bit_flags.try_into().map_err(
|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
},
)?,
padding: source.padding.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 8 bytes"
.to_string(),
)
})?,
})
}
}
impl carbon_core::postgres::operations::Table for crate::accounts::state::State {
fn table() -> &'static str {
"state_account"
}
fn columns() -> Vec<&'static str> {
vec![
"__pubkey",
"__slot",
"admin",
"whitelist_mint",
"discount_mint",
"signer",
"srm_vault",
"perp_fee_structure",
"spot_fee_structure",
"oracle_guard_rails",
"number_of_authorities",
"number_of_sub_accounts",
"lp_cooldown_time",
"liquidation_margin_buffer_ratio",
"settlement_duration",
"number_of_markets",
"number_of_spot_markets",
"signer_nonce",
"min_perp_auction_duration",
"default_market_order_time_in_force",
"default_spot_auction_duration",
"exchange_status",
"liquidation_duration",
"initial_pct_to_liquidate",
"max_number_of_sub_accounts",
"max_initialize_user_fee",
"feature_bit_flags",
"lp_pool_feature_bit_flags",
"padding",
]
}
}
#[async_trait::async_trait]
impl carbon_core::postgres::operations::Insert for StateRow {
async fn insert(&self, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(r#"
INSERT INTO state_account (
"admin",
"whitelist_mint",
"discount_mint",
"signer",
"srm_vault",
"perp_fee_structure",
"spot_fee_structure",
"oracle_guard_rails",
"number_of_authorities",
"number_of_sub_accounts",
"lp_cooldown_time",
"liquidation_margin_buffer_ratio",
"settlement_duration",
"number_of_markets",
"number_of_spot_markets",
"signer_nonce",
"min_perp_auction_duration",
"default_market_order_time_in_force",
"default_spot_auction_duration",
"exchange_status",
"liquidation_duration",
"initial_pct_to_liquidate",
"max_number_of_sub_accounts",
"max_initialize_user_fee",
"feature_bit_flags",
"lp_pool_feature_bit_flags",
"padding",
__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
)"#)
.bind(self.admin)
.bind(self.whitelist_mint)
.bind(self.discount_mint)
.bind(self.signer)
.bind(self.srm_vault)
.bind(&self.perp_fee_structure)
.bind(&self.spot_fee_structure)
.bind(&self.oracle_guard_rails)
.bind(&self.number_of_authorities)
.bind(&self.number_of_sub_accounts)
.bind(&self.lp_cooldown_time)
.bind(self.liquidation_margin_buffer_ratio)
.bind(self.settlement_duration)
.bind(self.number_of_markets)
.bind(self.number_of_spot_markets)
.bind(self.signer_nonce)
.bind(self.min_perp_auction_duration)
.bind(self.default_market_order_time_in_force)
.bind(self.default_spot_auction_duration)
.bind(self.exchange_status)
.bind(self.liquidation_duration)
.bind(self.initial_pct_to_liquidate)
.bind(self.max_number_of_sub_accounts)
.bind(self.max_initialize_user_fee)
.bind(self.feature_bit_flags)
.bind(self.lp_pool_feature_bit_flags)
.bind(&self.padding)
.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 StateRow {
async fn upsert(&self, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(r#"INSERT INTO state_account (
"admin",
"whitelist_mint",
"discount_mint",
"signer",
"srm_vault",
"perp_fee_structure",
"spot_fee_structure",
"oracle_guard_rails",
"number_of_authorities",
"number_of_sub_accounts",
"lp_cooldown_time",
"liquidation_margin_buffer_ratio",
"settlement_duration",
"number_of_markets",
"number_of_spot_markets",
"signer_nonce",
"min_perp_auction_duration",
"default_market_order_time_in_force",
"default_spot_auction_duration",
"exchange_status",
"liquidation_duration",
"initial_pct_to_liquidate",
"max_number_of_sub_accounts",
"max_initialize_user_fee",
"feature_bit_flags",
"lp_pool_feature_bit_flags",
"padding",
__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
) ON CONFLICT (
__pubkey
) DO UPDATE SET
"admin" = EXCLUDED."admin",
"whitelist_mint" = EXCLUDED."whitelist_mint",
"discount_mint" = EXCLUDED."discount_mint",
"signer" = EXCLUDED."signer",
"srm_vault" = EXCLUDED."srm_vault",
"perp_fee_structure" = EXCLUDED."perp_fee_structure",
"spot_fee_structure" = EXCLUDED."spot_fee_structure",
"oracle_guard_rails" = EXCLUDED."oracle_guard_rails",
"number_of_authorities" = EXCLUDED."number_of_authorities",
"number_of_sub_accounts" = EXCLUDED."number_of_sub_accounts",
"lp_cooldown_time" = EXCLUDED."lp_cooldown_time",
"liquidation_margin_buffer_ratio" = EXCLUDED."liquidation_margin_buffer_ratio",
"settlement_duration" = EXCLUDED."settlement_duration",
"number_of_markets" = EXCLUDED."number_of_markets",
"number_of_spot_markets" = EXCLUDED."number_of_spot_markets",
"signer_nonce" = EXCLUDED."signer_nonce",
"min_perp_auction_duration" = EXCLUDED."min_perp_auction_duration",
"default_market_order_time_in_force" = EXCLUDED."default_market_order_time_in_force",
"default_spot_auction_duration" = EXCLUDED."default_spot_auction_duration",
"exchange_status" = EXCLUDED."exchange_status",
"liquidation_duration" = EXCLUDED."liquidation_duration",
"initial_pct_to_liquidate" = EXCLUDED."initial_pct_to_liquidate",
"max_number_of_sub_accounts" = EXCLUDED."max_number_of_sub_accounts",
"max_initialize_user_fee" = EXCLUDED."max_initialize_user_fee",
"feature_bit_flags" = EXCLUDED."feature_bit_flags",
"lp_pool_feature_bit_flags" = EXCLUDED."lp_pool_feature_bit_flags",
"padding" = EXCLUDED."padding",
__slot = EXCLUDED.__slot
"#)
.bind(self.admin)
.bind(self.whitelist_mint)
.bind(self.discount_mint)
.bind(self.signer)
.bind(self.srm_vault)
.bind(&self.perp_fee_structure)
.bind(&self.spot_fee_structure)
.bind(&self.oracle_guard_rails)
.bind(&self.number_of_authorities)
.bind(&self.number_of_sub_accounts)
.bind(&self.lp_cooldown_time)
.bind(self.liquidation_margin_buffer_ratio)
.bind(self.settlement_duration)
.bind(self.number_of_markets)
.bind(self.number_of_spot_markets)
.bind(self.signer_nonce)
.bind(self.min_perp_auction_duration)
.bind(self.default_market_order_time_in_force)
.bind(self.default_spot_auction_duration)
.bind(self.exchange_status)
.bind(self.liquidation_duration)
.bind(self.initial_pct_to_liquidate)
.bind(self.max_number_of_sub_accounts)
.bind(self.max_initialize_user_fee)
.bind(self.feature_bit_flags)
.bind(self.lp_pool_feature_bit_flags)
.bind(&self.padding)
.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 StateRow {
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 state_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 StateRow {
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 state_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 StateMigrationOperation;
#[async_trait::async_trait]
impl sqlx_migrator::Operation<sqlx::Postgres> for StateMigrationOperation {
async fn up(
&self,
connection: &mut sqlx::PgConnection,
) -> Result<(), sqlx_migrator::error::Error> {
sqlx::query(
r#"CREATE TABLE IF NOT EXISTS state_account (
-- Account data
"admin" BYTEA NOT NULL,
"whitelist_mint" BYTEA NOT NULL,
"discount_mint" BYTEA NOT NULL,
"signer" BYTEA NOT NULL,
"srm_vault" BYTEA NOT NULL,
"perp_fee_structure" JSONB NOT NULL,
"spot_fee_structure" JSONB NOT NULL,
"oracle_guard_rails" JSONB NOT NULL,
"number_of_authorities" NUMERIC(20) NOT NULL,
"number_of_sub_accounts" NUMERIC(20) NOT NULL,
"lp_cooldown_time" NUMERIC(20) NOT NULL,
"liquidation_margin_buffer_ratio" INT8 NOT NULL,
"settlement_duration" INT4 NOT NULL,
"number_of_markets" INT4 NOT NULL,
"number_of_spot_markets" INT4 NOT NULL,
"signer_nonce" INT2 NOT NULL,
"min_perp_auction_duration" INT2 NOT NULL,
"default_market_order_time_in_force" INT2 NOT NULL,
"default_spot_auction_duration" INT2 NOT NULL,
"exchange_status" INT2 NOT NULL,
"liquidation_duration" INT2 NOT NULL,
"initial_pct_to_liquidate" INT4 NOT NULL,
"max_number_of_sub_accounts" INT4 NOT NULL,
"max_initialize_user_fee" INT4 NOT NULL,
"feature_bit_flags" INT2 NOT NULL,
"lp_pool_feature_bit_flags" INT2 NOT NULL,
"padding" 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 state_account"#)
.execute(connection)
.await?;
Ok(())
}
}