Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: disbursal velocity checks #1268

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 12 additions & 19 deletions lana/app/src/credit_facility/credit_chart_of_accounts/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use audit::AuditInfo;
use cala_ledger::CalaLedger;
use cala_ledger::LedgerOperation;
use chart_of_accounts::TransactionAccountFactory;
use lana_ids::CreditFacilityId;

Expand All @@ -11,7 +11,6 @@ use super::CreditFacilityAccountIds;

#[derive(Clone)]
pub struct CreditChartOfAccounts {
cala: CalaLedger,
collateral_factory: TransactionAccountFactory,
facility_factory: TransactionAccountFactory,
disbursed_receivable_factory: TransactionAccountFactory,
Expand All @@ -21,43 +20,39 @@ pub struct CreditChartOfAccounts {
}

impl CreditChartOfAccounts {
pub async fn init(
cala: &CalaLedger,
pub fn new(
collateral_factory: TransactionAccountFactory,
facility_factory: TransactionAccountFactory,
disbursed_receivable_factory: TransactionAccountFactory,
interest_receivable_factory: TransactionAccountFactory,
interest_income_factory: TransactionAccountFactory,
fee_income_factory: TransactionAccountFactory,
) -> Result<Self, CreditChartOfAccountsError> {
Ok(Self {
cala: cala.clone(),
) -> Self {
Self {
collateral_factory,
facility_factory,
disbursed_receivable_factory,
interest_receivable_factory,
interest_income_factory,
fee_income_factory,
})
}
}

pub async fn create_accounts_for_credit_facility(
&self,
op: es_entity::DbOp<'_>,
op: &mut LedgerOperation<'_>,
credit_facility_id: CreditFacilityId,
account_ids: CreditFacilityAccountIds,
audit_info: AuditInfo,
) -> Result<(), CreditChartOfAccountsError> {
let mut op = self.cala.ledger_operation_from_db_op(op);

let collateral_name = &format!(
"Credit Facility Collateral Account for {}",
credit_facility_id
);
let _collateral_details = self
.collateral_factory
.create_transaction_account_in_op(
&mut op,
op,
account_ids.collateral_account_id,
collateral_name,
collateral_name,
Expand All @@ -72,7 +67,7 @@ impl CreditChartOfAccounts {
let _facility_details = self
.facility_factory
.create_transaction_account_in_op(
&mut op,
op,
account_ids.facility_account_id,
facility_name,
facility_name,
Expand All @@ -87,7 +82,7 @@ impl CreditChartOfAccounts {
let _disbursed_receivable_details = self
.disbursed_receivable_factory
.create_transaction_account_in_op(
&mut op,
op,
account_ids.disbursed_receivable_account_id,
disbursed_receivable_name,
disbursed_receivable_name,
Expand All @@ -102,7 +97,7 @@ impl CreditChartOfAccounts {
let _interest_receivable_details = self
.interest_receivable_factory
.create_transaction_account_in_op(
&mut op,
op,
account_ids.interest_receivable_account_id,
interest_receivable_name,
interest_receivable_name,
Expand All @@ -117,7 +112,7 @@ impl CreditChartOfAccounts {
let _interest_income_details = self
.interest_income_factory
.create_transaction_account_in_op(
&mut op,
op,
account_ids.interest_account_id,
interest_income_name,
interest_income_name,
Expand All @@ -132,16 +127,14 @@ impl CreditChartOfAccounts {
let _fee_income_details = self
.fee_income_factory
.create_transaction_account_in_op(
&mut op,
op,
account_ids.fee_income_account_id,
fee_income_name,
fee_income_name,
audit_info.clone(),
)
.await?;

op.commit().await?;

Ok(())
}
}
2 changes: 2 additions & 0 deletions lana/app/src/credit_facility/ledger/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ pub enum CreditLedgerError {
CalaBalance(#[from] cala_ledger::balance::error::BalanceError),
#[error("CreditLedgerError - ConversionError: {0}")]
ConversionError(#[from] core_money::ConversionError),
#[error("CreditLedgerError - CalaVelocityError: {0}")]
CalaVelocity(#[from] cala_ledger::velocity::error::VelocityError),
}
60 changes: 60 additions & 0 deletions lana/app/src/credit_facility/ledger/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod credit_facility_accounts;
pub mod error;
mod templates;
mod velocity;

use cala_ledger::{
account::{error::AccountError, NewAccount},
velocity::{NewVelocityControl, VelocityControlId},
AccountId, CalaLedger, Currency, DebitOrCredit, JournalId, TransactionId,
};

Expand All @@ -14,6 +16,8 @@ use error::*;

pub(super) const BANK_COLLATERAL_ACCOUNT_CODE: &str = "BANK.COLLATERAL.OMNIBUS";
pub(super) const CREDIT_OMNIBUS_ACCOUNT_CODE: &str = "CREDIT.OMNIBUS";
pub(super) const CREDIT_FACILITY_VELOCITY_CONTROL_ID: uuid::Uuid =
uuid::uuid!("00000000-0000-0000-0000-000000000002");

#[derive(Debug, Clone)]
pub struct CreditFacilityCollateralUpdate {
Expand All @@ -29,6 +33,7 @@ pub struct CreditLedger {
journal_id: JournalId,
credit_omnibus_account: AccountId,
bank_collateral_account_id: AccountId,
credit_facility_control_id: VelocityControlId,
usd: Currency,
btc: Currency,
}
Expand All @@ -51,11 +56,26 @@ impl CreditLedger {
templates::CreditFacilityAccrueInterest::init(cala).await?;
templates::CreditFacilityDisbursal::init(cala).await?;

let disbursal_limit_id = velocity::DisbursalLimit::init(cala).await?;

let credit_facility_control_id = Self::create_credit_facility_control(cala).await?;

match cala
.velocities()
.add_limit_to_control(credit_facility_control_id, disbursal_limit_id)
.await
{
Ok(_)
| Err(cala_ledger::velocity::error::VelocityError::LimitAlreadyAddedToControl) => {}
Err(e) => return Err(e.into()),
}

Ok(Self {
cala: cala.clone(),
journal_id,
bank_collateral_account_id,
credit_omnibus_account,
credit_facility_control_id,
usd: "USD".parse().expect("Could not parse 'USD'"),
btc: "BTC".parse().expect("Could not parse 'BTC'"),
})
Expand Down Expand Up @@ -418,4 +438,44 @@ impl CreditLedger {
Ok(account) => Ok(account.id),
}
}

pub async fn create_credit_facility_control(
cala: &CalaLedger,
) -> Result<VelocityControlId, CreditLedgerError> {
let control = NewVelocityControl::builder()
.id(CREDIT_FACILITY_VELOCITY_CONTROL_ID)
.name("Credit Facility Control")
.description("Velocity Control for Deposits")
.build()
.expect("build control");

match cala.velocities().create_control(control).await {
Err(cala_ledger::velocity::error::VelocityError::ControlIdAlreadyExists) => {
Ok(CREDIT_FACILITY_VELOCITY_CONTROL_ID.into())
}
Err(e) => Err(e.into()),
Ok(control) => Ok(control.id()),
}
}

pub async fn add_credit_facility_control_to_account(
&self,
op: &mut cala_ledger::LedgerOperation<'_>,
account_id: impl Into<AccountId>,
disbursal_limit: UsdCents,
) -> Result<(), CreditLedgerError> {
self.cala
.velocities()
.attach_control_to_account_in_op(
op,
self.credit_facility_control_id,
account_id.into(),
velocity::DisbursalLimitParams {
disbursal_limit: disbursal_limit.to_usd(),
},
)
.await?;

Ok(())
}
}
68 changes: 68 additions & 0 deletions lana/app/src/credit_facility/ledger/velocity/disbursal_limit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use rust_decimal::Decimal;
use tracing::instrument;

use cala_ledger::{velocity::*, *};

pub struct DisbursalLimit;

const DISBURSAL_LIMIT_ID: uuid::Uuid = uuid::uuid!("00000000-0000-0000-0000-000000000002");

#[derive(Debug)]
pub struct DisbursalLimitParams {
pub disbursal_limit: Decimal,
}

impl DisbursalLimitParams {
pub fn defs() -> Vec<NewParamDefinition> {
vec![NewParamDefinition::builder()
.name("disbursal_limit")
.r#type(ParamDataType::Decimal)
.build()
.unwrap()]
}
}

impl From<DisbursalLimitParams> for Params {
fn from(params: DisbursalLimitParams) -> Self {
let mut p = Self::default();
p.insert("disbursal_limit", params.disbursal_limit);
p
}
}

impl DisbursalLimit {
#[instrument(name = "ledger.overdraft_prevention.init", skip_all)]
pub async fn init(
ledger: &CalaLedger,
) -> Result<VelocityLimitId, crate::credit_facility::ledger::CreditLedgerError> {
let params = DisbursalLimitParams::defs();

let limit = NewVelocityLimit::builder()
.id(DISBURSAL_LIMIT_ID)
.name("Disbursal Limit")
.description("Limit for disbursals")
.window(vec![])
.limit(
NewLimit::builder()
.balance(vec![NewBalanceLimit::builder()
.layer("SETTLED")
.amount("params.disbursal_limit")
.enforcement_direction("DEBIT")
.build()
.expect("balance limit")])
.build()
.expect("limit"),
)
.params(params)
.build()
.expect("velocity limit");

match ledger.velocities().create_limit(limit).await {
Err(cala_ledger::velocity::error::VelocityError::LimitIdAlreadyExists) => {
Ok(DISBURSAL_LIMIT_ID.into())
}
Err(e) => Err(e.into()),
Ok(limit) => Ok(limit.id()),
}
}
}
3 changes: 3 additions & 0 deletions lana/app/src/credit_facility/ledger/velocity/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod disbursal_limit;

pub use disbursal_limit::*;
24 changes: 17 additions & 7 deletions lana/app/src/credit_facility/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub struct CreditFacilities {
price: Price,
config: CreditFacilityConfig,
approve_disbursal: ApproveDisbursal,
cala: CalaLedger,
approve_credit_facility: ApproveCreditFacility,
}

Expand Down Expand Up @@ -102,16 +103,14 @@ impl CreditFacilities {
governance,
&ledger,
);
let chart_of_accounts = CreditChartOfAccounts::init(
cala,
let chart_of_accounts = CreditChartOfAccounts::new(
collateral_factory,
facility_factory,
disbursed_receivable_factory,
interest_receivable_factory,
interest_income_factory,
fee_income_factory,
)
.await?;
);

let approve_credit_facility =
ApproveCreditFacility::new(&credit_facility_repo, authz.audit(), governance);
Expand Down Expand Up @@ -180,6 +179,7 @@ impl CreditFacilities {
chart_of_accounts,
price: price.clone(),
config,
cala: cala.clone(),
approve_disbursal,
approve_credit_facility,
})
Expand Down Expand Up @@ -242,15 +242,27 @@ impl CreditFacilities {
.credit_facility_repo
.create_in_op(&mut db, new_credit_facility)
.await?;

let mut op = self.cala.ledger_operation_from_db_op(db);
self.chart_of_accounts
.create_accounts_for_credit_facility(
db,
&mut op,
credit_facility.id,
credit_facility.account_ids,
audit_info,
)
.await?;

self.ledger
.add_credit_facility_control_to_account(
&mut op,
credit_facility.account_ids.disbursed_receivable_account_id,
facility,
)
.await?;

op.commit().await?;

Ok(credit_facility)
}

Expand Down Expand Up @@ -326,8 +338,6 @@ impl CreditFacilities {
.await?;
credit_facility.balances().check_against_ledger(balances)?;

credit_facility.balances().check_disbursal_amount(amount)?;

let price = self.price.usd_cents_per_btc().await?;

let mut db = self.credit_facility_repo.begin_op().await?;
Expand Down
Loading