use carbon_core::{
account::AccountMetadata,
postgres::{
metadata::AccountRowMetadata,
primitives::{Pubkey, U32, U64, U8},
},
};
#[derive(sqlx::FromRow, Debug, Clone)]
pub struct TokenLendingLoanRow {
#[sqlx(flatten)]
pub account_metadata: AccountRowMetadata,
pub orderbook: Pubkey,
pub borrower: Pubkey,
pub pool: Pubkey,
pub principal_usdc_amount: U64,
pub collateral_required_amount: U64,
pub start_ts: i64,
pub end_ts: i64,
pub apr: U32,
pub version: U8,
pub padding: Vec<u8>,
}
impl TokenLendingLoanRow {
pub fn from_parts(
source: crate::accounts::token_lending_loan::TokenLendingLoan,
metadata: AccountMetadata,
) -> Self {
Self {
account_metadata: metadata.into(),
orderbook: source.orderbook.into(),
borrower: source.borrower.into(),
pool: source.pool.into(),
principal_usdc_amount: source.principal_usdc_amount.into(),
collateral_required_amount: source.collateral_required_amount.into(),
start_ts: source.start_ts,
end_ts: source.end_ts,
apr: source.apr.into(),
version: source.version.into(),
padding: source.padding.to_vec(),
}
}
}
impl TryFrom<TokenLendingLoanRow> for crate::accounts::token_lending_loan::TokenLendingLoan {
type Error = carbon_core::error::Error;
fn try_from(source: TokenLendingLoanRow) -> Result<Self, Self::Error> {
Ok(Self {
orderbook: *source.orderbook,
borrower: *source.borrower,
pool: *source.pool,
principal_usdc_amount: *source.principal_usdc_amount,
collateral_required_amount: *source.collateral_required_amount,
start_ts: source.start_ts,
end_ts: source.end_ts,
apr: source.apr.try_into().map_err(|_| {
carbon_core::error::Error::Custom(
"Failed to convert value from postgres primitive".to_string(),
)
})?,
version: source.version.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 3 bytes"
.to_string(),
)
})?,
})
}
}
impl carbon_core::postgres::operations::Table
for crate::accounts::token_lending_loan::TokenLendingLoan
{
fn table() -> &'static str {
"token_lending_loan_account"
}
fn columns() -> Vec<&'static str> {
vec![
"__pubkey",
"__slot",
"orderbook",
"borrower",
"pool",
"principal_usdc_amount",
"collateral_required_amount",
"start_ts",
"end_ts",
"apr",
"version",
"padding",
]
}
}
#[async_trait::async_trait]
impl carbon_core::postgres::operations::Insert for TokenLendingLoanRow {
async fn insert(&self, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(
r#"
INSERT INTO token_lending_loan_account (
"orderbook",
"borrower",
"pool",
"principal_usdc_amount",
"collateral_required_amount",
"start_ts",
"end_ts",
"apr",
"version",
"padding",
__pubkey, __slot
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
)"#,
)
.bind(self.orderbook)
.bind(self.borrower)
.bind(self.pool)
.bind(&self.principal_usdc_amount)
.bind(&self.collateral_required_amount)
.bind(self.start_ts)
.bind(self.end_ts)
.bind(self.apr)
.bind(self.version)
.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 TokenLendingLoanRow {
async fn upsert(&self, pool: &sqlx::PgPool) -> carbon_core::error::CarbonResult<()> {
sqlx::query(
r#"INSERT INTO token_lending_loan_account (
"orderbook",
"borrower",
"pool",
"principal_usdc_amount",
"collateral_required_amount",
"start_ts",
"end_ts",
"apr",
"version",
"padding",
__pubkey, __slot
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
) ON CONFLICT (
__pubkey
) DO UPDATE SET
"orderbook" = EXCLUDED."orderbook",
"borrower" = EXCLUDED."borrower",
"pool" = EXCLUDED."pool",
"principal_usdc_amount" = EXCLUDED."principal_usdc_amount",
"collateral_required_amount" = EXCLUDED."collateral_required_amount",
"start_ts" = EXCLUDED."start_ts",
"end_ts" = EXCLUDED."end_ts",
"apr" = EXCLUDED."apr",
"version" = EXCLUDED."version",
"padding" = EXCLUDED."padding",
__slot = EXCLUDED.__slot
"#,
)
.bind(self.orderbook)
.bind(self.borrower)
.bind(self.pool)
.bind(&self.principal_usdc_amount)
.bind(&self.collateral_required_amount)
.bind(self.start_ts)
.bind(self.end_ts)
.bind(self.apr)
.bind(self.version)
.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 TokenLendingLoanRow {
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 token_lending_loan_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 TokenLendingLoanRow {
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 token_lending_loan_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 TokenLendingLoanMigrationOperation;
#[async_trait::async_trait]
impl sqlx_migrator::Operation<sqlx::Postgres> for TokenLendingLoanMigrationOperation {
async fn up(
&self,
connection: &mut sqlx::PgConnection,
) -> Result<(), sqlx_migrator::error::Error> {
sqlx::query(
r#"CREATE TABLE IF NOT EXISTS token_lending_loan_account (
-- Account data
"orderbook" BYTEA NOT NULL,
"borrower" BYTEA NOT NULL,
"pool" BYTEA NOT NULL,
"principal_usdc_amount" NUMERIC(20) NOT NULL,
"collateral_required_amount" NUMERIC(20) NOT NULL,
"start_ts" INT8 NOT NULL,
"end_ts" INT8 NOT NULL,
"apr" INT8 NOT NULL,
"version" 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 token_lending_loan_account"#)
.execute(connection)
.await?;
Ok(())
}
}