use {
crate::types::{BankCache, BankConfig, BankRateLimiter, EmodeSettings, WrappedI80F48},
carbon_core::{
account::AccountMetadata,
postgres::{
metadata::AccountRowMetadata,
primitives::{Pubkey, U64, U8},
},
},
};
#[derive(sqlx::FromRow, Debug, Clone)]
pub struct BankRow {
#[sqlx(flatten)]
pub account_metadata: AccountRowMetadata,
pub mint: Pubkey,
pub mint_decimals: U8,
pub group: Pubkey,
pub pad0: Vec<u8>,
pub asset_share_value: sqlx::types::Json<WrappedI80F48>,
pub liability_share_value: sqlx::types::Json<WrappedI80F48>,
pub liquidity_vault: Pubkey,
pub liquidity_vault_bump: U8,
pub liquidity_vault_authority_bump: U8,
pub insurance_vault: Pubkey,
pub insurance_vault_bump: U8,
pub insurance_vault_authority_bump: U8,
pub pad1: Vec<u8>,
pub collected_insurance_fees_outstanding: sqlx::types::Json<WrappedI80F48>,
pub fee_vault: Pubkey,
pub fee_vault_bump: U8,
pub fee_vault_authority_bump: U8,
pub pad2: Vec<u8>,
pub collected_group_fees_outstanding: sqlx::types::Json<WrappedI80F48>,
pub total_liability_shares: sqlx::types::Json<WrappedI80F48>,
pub total_asset_shares: sqlx::types::Json<WrappedI80F48>,
pub last_update: i64,
pub config: sqlx::types::Json<BankConfig>,
pub flags: U64,
pub emissions_rate: U64,
pub emissions_remaining: sqlx::types::Json<WrappedI80F48>,
pub emissions_mint: Pubkey,
pub collected_program_fees_outstanding: sqlx::types::Json<WrappedI80F48>,
pub emode: sqlx::types::Json<EmodeSettings>,
pub fees_destination_account: Pubkey,
pub cache: sqlx::types::Json<BankCache>,
pub lending_position_count: i32,
pub borrowing_position_count: i32,
pub padding0: Vec<u8>,
pub integration_acc1: Pubkey,
pub integration_acc2: Pubkey,
pub integration_acc3: Pubkey,
pub rate_limiter: sqlx::types::Json<BankRateLimiter>,
pub pad0_1: Vec<u8>,
pub padding1: sqlx::types::Json<Vec<Vec<U64>>>,
}
impl BankRow {
pub fn from_parts(source: crate::accounts::bank::Bank, metadata: AccountMetadata) -> Self {
Self {
account_metadata: metadata.into(),
mint: source.mint.into(),
mint_decimals: source.mint_decimals.into(),
group: source.group.into(),
pad0: source._pad0.to_vec(),
asset_share_value: sqlx::types::Json(source.asset_share_value),
liability_share_value: sqlx::types::Json(source.liability_share_value),
liquidity_vault: source.liquidity_vault.into(),
liquidity_vault_bump: source.liquidity_vault_bump.into(),
liquidity_vault_authority_bump: source.liquidity_vault_authority_bump.into(),
insurance_vault: source.insurance_vault.into(),
insurance_vault_bump: source.insurance_vault_bump.into(),
insurance_vault_authority_bump: source.insurance_vault_authority_bump.into(),
pad1: source.pad1.to_vec(),
collected_insurance_fees_outstanding: sqlx::types::Json(
source.collected_insurance_fees_outstanding,
),
fee_vault: source.fee_vault.into(),
fee_vault_bump: source.fee_vault_bump.into(),
fee_vault_authority_bump: source.fee_vault_authority_bump.into(),
pad2: source.pad2.to_vec(),
collected_group_fees_outstanding: sqlx::types::Json(
source.collected_group_fees_outstanding,
),
total_liability_shares: sqlx::types::Json(source.total_liability_shares),
total_asset_shares: sqlx::types::Json(source.total_asset_shares),
last_update: source.last_update,
config: sqlx::types::Json(source.config),
flags: source.flags.into(),
emissions_rate: source.emissions_rate.into(),
emissions_remaining: sqlx::types::Json(source.emissions_remaining),
emissions_mint: source.emissions_mint.into(),
collected_program_fees_outstanding: sqlx::types::Json(
source.collected_program_fees_outstanding,
),
emode: sqlx::types::Json(source.emode),
fees_destination_account: source.fees_destination_account.into(),
cache: sqlx::types::Json(source.cache),
lending_position_count: source.lending_position_count,
borrowing_position_count: source.borrowing_position_count,
padding0: source.padding0.to_vec(),
integration_acc1: source.integration_acc1.into(),
integration_acc2: source.integration_acc2.into(),
integration_acc3: source.integration_acc3.into(),
rate_limiter: sqlx::types::Json(source.rate_limiter),
pad0_1: source.pad0.to_vec(),
padding1: sqlx::types::Json(
source
.padding1
.into_iter()
.map(|element| element.into_iter().map(|element| element.into()).collect())
.collect(),
),
}
}
}
impl TryFrom<BankRow> for crate::accounts::bank::Bank {
type Error = carbon_core::error::Error;
fn try_from(source: BankRow) -> Result<Self, Self::Error> {
Ok(Self {
mint: *source.mint,
mint_decimals: source.mint_decimals.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
group: *source.group,
_pad0: source.pad0.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 7 bytes"
.to_string(),
)
})?,
asset_share_value: source.asset_share_value.0,
liability_share_value: source.liability_share_value.0,
liquidity_vault: *source.liquidity_vault,
liquidity_vault_bump: source.liquidity_vault_bump.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
liquidity_vault_authority_bump: source
.liquidity_vault_authority_bump
.try_into()
.map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
insurance_vault: *source.insurance_vault,
insurance_vault_bump: source.insurance_vault_bump.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
insurance_vault_authority_bump: source
.insurance_vault_authority_bump
.try_into()
.map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
pad1: source.pad1.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 4 bytes"
.to_string(),
)
})?,
collected_insurance_fees_outstanding: source.collected_insurance_fees_outstanding.0,
fee_vault: *source.fee_vault,
fee_vault_bump: source.fee_vault_bump.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
fee_vault_authority_bump: source.fee_vault_authority_bump.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
pad2: source.pad2.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 6 bytes"
.to_string(),
)
})?,
collected_group_fees_outstanding: source.collected_group_fees_outstanding.0,
total_liability_shares: source.total_liability_shares.0,
total_asset_shares: source.total_asset_shares.0,
last_update: source.last_update,
config: source.config.0,
flags: *source.flags,
emissions_rate: *source.emissions_rate,
emissions_remaining: source.emissions_remaining.0,
emissions_mint: *source.emissions_mint,
collected_program_fees_outstanding: source.collected_program_fees_outstanding.0,
emode: source.emode.0,
fees_destination_account: *source.fees_destination_account,
cache: source.cache.0,
lending_position_count: source.lending_position_count,
borrowing_position_count: source.borrowing_position_count,
padding0: source.padding0.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 16 bytes"
.to_string(),
)
})?,
integration_acc1: *source.integration_acc1,
integration_acc2: *source.integration_acc2,
integration_acc3: *source.integration_acc3,
rate_limiter: source.rate_limiter.0,
pad0: source.pad0.as_slice().try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert padding from postgres primitive: expected 16 bytes"
.to_string(),
)
})?,
padding1: source
.padding1
.0
.into_iter()
.map(|element| {
element
.into_iter()
.map(|element| Ok(*element))
.collect::<Result<Vec<_>, carbon_core::error::Error>>()
.map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to collect array elements".to_string(),
)
})?
.try_into()
.map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert array element to primitive".to_string(),
)
})
})
.collect::<Result<Vec<_>, carbon_core::error::Error>>()?
.try_into()
.map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
})
}
}
impl carbon_core::postgres::operations::Table for crate::accounts::bank::Bank {
fn table() -> &'static str {
"bank_account"
}
fn columns() -> Vec<&'static str> {
vec![
"__pubkey",
"__slot",
"mint",
"mint_decimals",
"group",
"pad0",
"asset_share_value",
"liability_share_value",
"liquidity_vault",
"liquidity_vault_bump",
"liquidity_vault_authority_bump",
"insurance_vault",
"insurance_vault_bump",
"insurance_vault_authority_bump",
"pad1",
"collected_insurance_fees_outstanding",
"fee_vault",
"fee_vault_bump",
"fee_vault_authority_bump",
"pad2",
"collected_group_fees_outstanding",
"total_liability_shares",
"total_asset_shares",
"last_update",
"config",
"flags",
"emissions_rate",
"emissions_remaining",
"emissions_mint",
"collected_program_fees_outstanding",
"emode",
"fees_destination_account",
"cache",
"lending_position_count",
"borrowing_position_count",
"padding0",
"integration_acc1",
"integration_acc2",
"integration_acc3",
"rate_limiter",
"pad0_1",
"padding1",
]
}
}
#[async_trait::async_trait]
impl carbon_core::postgres::operations::Insert for BankRow {
async fn insert(&self, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(r#"
INSERT INTO bank_account (
"mint",
"mint_decimals",
"group",
"pad0",
"asset_share_value",
"liability_share_value",
"liquidity_vault",
"liquidity_vault_bump",
"liquidity_vault_authority_bump",
"insurance_vault",
"insurance_vault_bump",
"insurance_vault_authority_bump",
"pad1",
"collected_insurance_fees_outstanding",
"fee_vault",
"fee_vault_bump",
"fee_vault_authority_bump",
"pad2",
"collected_group_fees_outstanding",
"total_liability_shares",
"total_asset_shares",
"last_update",
"config",
"flags",
"emissions_rate",
"emissions_remaining",
"emissions_mint",
"collected_program_fees_outstanding",
"emode",
"fees_destination_account",
"cache",
"lending_position_count",
"borrowing_position_count",
"padding0",
"integration_acc1",
"integration_acc2",
"integration_acc3",
"rate_limiter",
"pad0_1",
"padding1",
__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, $39, $40, $41, $42
)"#)
.bind(self.mint)
.bind(self.mint_decimals)
.bind(self.group)
.bind(&self.pad0)
.bind(&self.asset_share_value)
.bind(&self.liability_share_value)
.bind(self.liquidity_vault)
.bind(self.liquidity_vault_bump)
.bind(self.liquidity_vault_authority_bump)
.bind(self.insurance_vault)
.bind(self.insurance_vault_bump)
.bind(self.insurance_vault_authority_bump)
.bind(&self.pad1)
.bind(&self.collected_insurance_fees_outstanding)
.bind(self.fee_vault)
.bind(self.fee_vault_bump)
.bind(self.fee_vault_authority_bump)
.bind(&self.pad2)
.bind(&self.collected_group_fees_outstanding)
.bind(&self.total_liability_shares)
.bind(&self.total_asset_shares)
.bind(self.last_update)
.bind(&self.config)
.bind(&self.flags)
.bind(&self.emissions_rate)
.bind(&self.emissions_remaining)
.bind(self.emissions_mint)
.bind(&self.collected_program_fees_outstanding)
.bind(&self.emode)
.bind(self.fees_destination_account)
.bind(&self.cache)
.bind(self.lending_position_count)
.bind(self.borrowing_position_count)
.bind(&self.padding0)
.bind(self.integration_acc1)
.bind(self.integration_acc2)
.bind(self.integration_acc3)
.bind(&self.rate_limiter)
.bind(&self.pad0_1)
.bind(&self.padding1)
.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 BankRow {
async fn upsert(&self, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(r#"INSERT INTO bank_account (
"mint",
"mint_decimals",
"group",
"pad0",
"asset_share_value",
"liability_share_value",
"liquidity_vault",
"liquidity_vault_bump",
"liquidity_vault_authority_bump",
"insurance_vault",
"insurance_vault_bump",
"insurance_vault_authority_bump",
"pad1",
"collected_insurance_fees_outstanding",
"fee_vault",
"fee_vault_bump",
"fee_vault_authority_bump",
"pad2",
"collected_group_fees_outstanding",
"total_liability_shares",
"total_asset_shares",
"last_update",
"config",
"flags",
"emissions_rate",
"emissions_remaining",
"emissions_mint",
"collected_program_fees_outstanding",
"emode",
"fees_destination_account",
"cache",
"lending_position_count",
"borrowing_position_count",
"padding0",
"integration_acc1",
"integration_acc2",
"integration_acc3",
"rate_limiter",
"pad0_1",
"padding1",
__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, $39, $40, $41, $42
) ON CONFLICT (
__pubkey
) DO UPDATE SET
"mint" = EXCLUDED."mint",
"mint_decimals" = EXCLUDED."mint_decimals",
"group" = EXCLUDED."group",
"pad0" = EXCLUDED."pad0",
"asset_share_value" = EXCLUDED."asset_share_value",
"liability_share_value" = EXCLUDED."liability_share_value",
"liquidity_vault" = EXCLUDED."liquidity_vault",
"liquidity_vault_bump" = EXCLUDED."liquidity_vault_bump",
"liquidity_vault_authority_bump" = EXCLUDED."liquidity_vault_authority_bump",
"insurance_vault" = EXCLUDED."insurance_vault",
"insurance_vault_bump" = EXCLUDED."insurance_vault_bump",
"insurance_vault_authority_bump" = EXCLUDED."insurance_vault_authority_bump",
"pad1" = EXCLUDED."pad1",
"collected_insurance_fees_outstanding" = EXCLUDED."collected_insurance_fees_outstanding",
"fee_vault" = EXCLUDED."fee_vault",
"fee_vault_bump" = EXCLUDED."fee_vault_bump",
"fee_vault_authority_bump" = EXCLUDED."fee_vault_authority_bump",
"pad2" = EXCLUDED."pad2",
"collected_group_fees_outstanding" = EXCLUDED."collected_group_fees_outstanding",
"total_liability_shares" = EXCLUDED."total_liability_shares",
"total_asset_shares" = EXCLUDED."total_asset_shares",
"last_update" = EXCLUDED."last_update",
"config" = EXCLUDED."config",
"flags" = EXCLUDED."flags",
"emissions_rate" = EXCLUDED."emissions_rate",
"emissions_remaining" = EXCLUDED."emissions_remaining",
"emissions_mint" = EXCLUDED."emissions_mint",
"collected_program_fees_outstanding" = EXCLUDED."collected_program_fees_outstanding",
"emode" = EXCLUDED."emode",
"fees_destination_account" = EXCLUDED."fees_destination_account",
"cache" = EXCLUDED."cache",
"lending_position_count" = EXCLUDED."lending_position_count",
"borrowing_position_count" = EXCLUDED."borrowing_position_count",
"padding0" = EXCLUDED."padding0",
"integration_acc1" = EXCLUDED."integration_acc1",
"integration_acc2" = EXCLUDED."integration_acc2",
"integration_acc3" = EXCLUDED."integration_acc3",
"rate_limiter" = EXCLUDED."rate_limiter",
"pad0_1" = EXCLUDED."pad0_1",
"padding1" = EXCLUDED."padding1",
__slot = EXCLUDED.__slot
"#)
.bind(self.mint)
.bind(self.mint_decimals)
.bind(self.group)
.bind(&self.pad0)
.bind(&self.asset_share_value)
.bind(&self.liability_share_value)
.bind(self.liquidity_vault)
.bind(self.liquidity_vault_bump)
.bind(self.liquidity_vault_authority_bump)
.bind(self.insurance_vault)
.bind(self.insurance_vault_bump)
.bind(self.insurance_vault_authority_bump)
.bind(&self.pad1)
.bind(&self.collected_insurance_fees_outstanding)
.bind(self.fee_vault)
.bind(self.fee_vault_bump)
.bind(self.fee_vault_authority_bump)
.bind(&self.pad2)
.bind(&self.collected_group_fees_outstanding)
.bind(&self.total_liability_shares)
.bind(&self.total_asset_shares)
.bind(self.last_update)
.bind(&self.config)
.bind(&self.flags)
.bind(&self.emissions_rate)
.bind(&self.emissions_remaining)
.bind(self.emissions_mint)
.bind(&self.collected_program_fees_outstanding)
.bind(&self.emode)
.bind(self.fees_destination_account)
.bind(&self.cache)
.bind(self.lending_position_count)
.bind(self.borrowing_position_count)
.bind(&self.padding0)
.bind(self.integration_acc1)
.bind(self.integration_acc2)
.bind(self.integration_acc3)
.bind(&self.rate_limiter)
.bind(&self.pad0_1)
.bind(&self.padding1)
.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 BankRow {
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 bank_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 BankRow {
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 bank_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 BankMigrationOperation;
#[async_trait::async_trait]
impl sqlx_migrator::Operation<sqlx::Postgres> for BankMigrationOperation {
async fn up(
&self,
connection: &mut sqlx::PgConnection,
) -> Result<(), sqlx_migrator::error::Error> {
sqlx::query(
r#"CREATE TABLE IF NOT EXISTS bank_account (
-- Account data
"mint" BYTEA NOT NULL,
"mint_decimals" INT2 NOT NULL,
"group" BYTEA NOT NULL,
"pad0" BYTEA NOT NULL,
"asset_share_value" JSONB NOT NULL,
"liability_share_value" JSONB NOT NULL,
"liquidity_vault" BYTEA NOT NULL,
"liquidity_vault_bump" INT2 NOT NULL,
"liquidity_vault_authority_bump" INT2 NOT NULL,
"insurance_vault" BYTEA NOT NULL,
"insurance_vault_bump" INT2 NOT NULL,
"insurance_vault_authority_bump" INT2 NOT NULL,
"pad1" BYTEA NOT NULL,
"collected_insurance_fees_outstanding" JSONB NOT NULL,
"fee_vault" BYTEA NOT NULL,
"fee_vault_bump" INT2 NOT NULL,
"fee_vault_authority_bump" INT2 NOT NULL,
"pad2" BYTEA NOT NULL,
"collected_group_fees_outstanding" JSONB NOT NULL,
"total_liability_shares" JSONB NOT NULL,
"total_asset_shares" JSONB NOT NULL,
"last_update" INT8 NOT NULL,
"config" JSONB NOT NULL,
"flags" NUMERIC(20) NOT NULL,
"emissions_rate" NUMERIC(20) NOT NULL,
"emissions_remaining" JSONB NOT NULL,
"emissions_mint" BYTEA NOT NULL,
"collected_program_fees_outstanding" JSONB NOT NULL,
"emode" JSONB NOT NULL,
"fees_destination_account" BYTEA NOT NULL,
"cache" JSONB NOT NULL,
"lending_position_count" INT4 NOT NULL,
"borrowing_position_count" INT4 NOT NULL,
"padding0" BYTEA NOT NULL,
"integration_acc1" BYTEA NOT NULL,
"integration_acc2" BYTEA NOT NULL,
"integration_acc3" BYTEA NOT NULL,
"rate_limiter" JSONB NOT NULL,
"pad0_1" BYTEA NOT NULL,
"padding1" JSONB 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 bank_account"#)
.execute(connection)
.await?;
Ok(())
}
}