Loans¶
Calls¶
admin_write_off¶
Writes off a loan from admin origin.
Forces a writing off of a loan if the percentage
and penalty
parameters respecting the policy values as the maximum.
This action can write down/up the current write off status of the
loan. If there is no active policy, an admin write off action can
write up the write off status. But if there is a policy applied, the
admin can only write up until the policy. Write down more than the
policy is always allowed. The portfolio valuation of the pool is
updated to reflect the new present value of the loan.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
loan_id | T::LoanId |
percentage | T::Rate |
penalty | T::Rate |
Python¶
call = substrate.compose_call(
'Loans', 'admin_write_off', {
'loan_id': 'u64',
'penalty': 'u128',
'percentage': 'u128',
'pool_id': 'u64',
}
)
apply_loan_mutation¶
Apply a proposed change identified by a change id. It will only perform the change if the requirements for it are fulfilled.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
change_id | T::Hash |
Python¶
call = substrate.compose_call(
'Loans', 'apply_loan_mutation', {
'change_id': 'scale_info::12',
'pool_id': 'u64',
}
)
apply_transfer_debt¶
Transfer debt from one loan to another loan,
repaying from the first loan and borrowing the same amount from the
second loan. from_loan_id
is the loan used to repay.
to_loan_id
is the loan used to borrow.
The repaid and borrow amount must match.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
change_id | T::Hash |
Python¶
call = substrate.compose_call(
'Loans', 'apply_transfer_debt', {
'change_id': 'scale_info::12',
'pool_id': 'u64',
}
)
apply_write_off_policy¶
Apply a proposed change identified by a change id. It will only perform the change if the requirements for it are fulfilled.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
change_id | T::Hash |
Python¶
call = substrate.compose_call(
'Loans', 'apply_write_off_policy', {
'change_id': 'scale_info::12',
'pool_id': 'u64',
}
)
borrow¶
Transfers borrow amount to the borrower.
The origin must be the borrower of the loan.
The borrow action should fulfill the borrow restrictions configured
at [types::LoanRestrictions
]. The amount
will be transferred
from pool reserve to borrower. The portfolio valuation of the pool
is updated to reflect the new present value of the loan.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
loan_id | T::LoanId |
amount | PrincipalInput<T> |
Python¶
call = substrate.compose_call(
'Loans', 'borrow', {
'amount': {
'External': {
'quantity': 'u128',
'settlement_price': 'u128',
},
'Internal': 'u128',
},
'loan_id': 'u64',
'pool_id': 'u64',
}
)
close¶
Closes a given loan
A loan only can be closed if it's fully repaid by the loan borrower. Closing a loan gives back the collateral used for the loan to the borrower .
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
loan_id | T::LoanId |
Python¶
call = substrate.compose_call(
'Loans', 'close', {'loan_id': 'u64', 'pool_id': 'u64'}
)
create¶
Creates a new loan against the collateral provided
The origin must be the owner of the collateral. This collateral will be transferred to the existing pool.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
info | LoanInfo<T> |
Python¶
call = substrate.compose_call(
'Loans', 'create', {
'info': {
'collateral': ('u64', 'u128'),
'interest_rate': {
'Fixed': {
'compounding': (
'Secondly',
),
'rate_per_year': 'u128',
},
},
'pricing': {
'External': {
'max_borrow_amount': {
'NoLimit': None,
'Quantity': 'u128',
},
'max_price_variation': 'u128',
'notional': 'u128',
'price_id': {
'ConversionRatio': (
{
'Native': None,
'Tranche': (
'u64',
'[u8; 16]',
),
None: None,
'AUSD': None,
'ForeignAsset': 'u32',
'LocalAsset': 'u32',
'Staking': 'scale_info::79',
},
{
'Native': None,
'Tranche': (
'u64',
'[u8; 16]',
),
None: None,
'AUSD': None,
'ForeignAsset': 'u32',
'LocalAsset': 'u32',
'Staking': 'scale_info::79',
},
),
'Isin': '[u8; 12]',
},
},
'Internal': {
'collateral_value': 'u128',
'max_borrow_amount': {
'UpToOutstandingDebt': {
'advance_rate': 'u128',
},
'UpToTotalBorrowed': {
'advance_rate': 'u128',
},
},
'valuation_method': {
'Cash': None,
'DiscountedCashFlow': {
'discount_rate': {
'Fixed': 'InnerStruct',
},
'loss_given_default': 'u128',
'probability_of_default': 'u128',
},
'OutstandingDebt': None,
},
},
},
'restrictions': {
'borrows': (
'NotWrittenOff',
'FullOnce',
'OraclePriceRequired',
),
'repayments': (
'None',
'Full',
),
},
'schedule': {
'interest_payments': (
'None',
),
'maturity': {
'Fixed': {
'date': 'u64',
'extension': 'u64',
},
},
'pay_down_schedule': (
'None',
),
},
},
'pool_id': 'u64',
}
)
increase_debt¶
Increase debt for a loan. Similar to [Pallet::borrow()
] but
without transferring from the pool.
The origin must be the borrower of the loan.
The increase debt action should fulfill the borrow restrictions
configured at [types::LoanRestrictions
]. The portfolio valuation
of the pool is updated to reflect the new present value of the loan.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
loan_id | T::LoanId |
amount | PrincipalInput<T> |
Python¶
call = substrate.compose_call(
'Loans', 'increase_debt', {
'amount': {
'External': {
'quantity': 'u128',
'settlement_price': 'u128',
},
'Internal': 'u128',
},
'loan_id': 'u64',
'pool_id': 'u64',
}
)
propose_loan_mutation¶
Propose a change.
The change is not performed until you call
[Pallet::apply_loan_mutation()
].
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
loan_id | T::LoanId |
mutation | LoanMutation<T::Rate> |
Python¶
call = substrate.compose_call(
'Loans', 'propose_loan_mutation', {
'loan_id': 'u64',
'mutation': {
'InterestPayments': ('None', ),
'InterestRate': {
'Fixed': {
'compounding': (
'Secondly',
),
'rate_per_year': 'u128',
},
},
'Internal': {
'DiscountRate': {
'Fixed': {
'compounding': (
'Secondly',
),
'rate_per_year': 'u128',
},
},
'LossGivenDefault': 'u128',
'ProbabilityOfDefault': 'u128',
'ValuationMethod': {
'Cash': None,
'DiscountedCashFlow': {
'discount_rate': {
'Fixed': {
'compounding': 'scale_info::118',
'rate_per_year': 'u128',
},
},
'loss_given_default': 'u128',
'probability_of_default': 'u128',
},
'OutstandingDebt': None,
},
},
'Maturity': {
'Fixed': {
'date': 'u64',
'extension': 'u64',
},
},
'MaturityExtension': 'u64',
'PayDownSchedule': ('None', ),
},
'pool_id': 'u64',
}
)
propose_transfer_debt¶
Transfer debt from one loan to another loan,
repaying from the first loan and borrowing the same amount from the
second loan. from_loan_id
is the loan used to repay.
to_loan_id
is the loan used to borrow.
The repaid and borrow amount must match.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
from_loan_id | T::LoanId |
to_loan_id | T::LoanId |
repaid_amount | RepaidInput<T> |
borrow_amount | PrincipalInput<T> |
Python¶
call = substrate.compose_call(
'Loans', 'propose_transfer_debt', {
'borrow_amount': {
'External': {
'quantity': 'u128',
'settlement_price': 'u128',
},
'Internal': 'u128',
},
'from_loan_id': 'u64',
'pool_id': 'u64',
'repaid_amount': {
'interest': 'u128',
'principal': {
'External': {
'quantity': 'u128',
'settlement_price': 'u128',
},
'Internal': 'u128',
},
'unscheduled': 'u128',
},
'to_loan_id': 'u64',
}
)
propose_write_off_policy¶
Updates the write off policy with write off rules.
The write off policy is used to automatically set a write off minimum value to the loan.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
policy | BoundedVec<WriteOffRule<T::Rate>, T::MaxWriteOffPolicySize> |
Python¶
call = substrate.compose_call(
'Loans', 'propose_write_off_policy', {
'policy': [
{
'status': {
'penalty': 'u128',
'percentage': 'u128',
},
'triggers': 'scale_info::129',
},
],
'pool_id': 'u64',
}
)
repay¶
Transfers amount borrowed to the pool reserve.
The origin must be the borrower of the loan.
The repay action should fulfill the repay restrictions
configured at [types::RepayRestrictions
].
If the repaying amount
is more than current debt, only current
debt is transferred. This does not apply to unscheduled_amount
,
which can be used to repay more than the outstanding debt.
The portfolio valuation of the pool is updated to reflect the new
present value of the loan.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
loan_id | T::LoanId |
amount | RepaidInput<T> |
Python¶
call = substrate.compose_call(
'Loans', 'repay', {
'amount': {
'interest': 'u128',
'principal': {
'External': {
'quantity': 'u128',
'settlement_price': 'u128',
},
'Internal': 'u128',
},
'unscheduled': 'u128',
},
'loan_id': 'u64',
'pool_id': 'u64',
}
)
update_portfolio_valuation¶
Updates the porfolio valuation for the given pool
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
Python¶
call = substrate.compose_call(
'Loans', 'update_portfolio_valuation', {'pool_id': 'u64'}
)
write_off¶
Writes off an overdue loan.
This action will write off based on the configured write off policy. The write off action will only take effect if it writes down more (percentage or penalty) than the current write off status of the loan. This action will never writes up. i.e: - Write off by admin with percentage 0.5 and penalty 0.2 - Time passes and the policy can be applied. - Write of with a policy that says: percentage 0.3, penaly 0.4 - The loan is written off with the maximum between the policy and the current rule: percentage 0.5, penalty 0.4
No special permisions are required to this call. The portfolio valuation of the pool is updated to reflect the new present value of the loan.
Attributes¶
Name | Type |
---|---|
pool_id | T::PoolId |
loan_id | T::LoanId |
Python¶
call = substrate.compose_call(
'Loans', 'write_off', {'loan_id': 'u64', 'pool_id': 'u64'}
)
Events¶
Borrowed¶
An amount was borrowed for a loan
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
loan_id | T::LoanId |
u64 |
amount | PrincipalInput<T> |
{'Internal': 'u128', 'External': {'quantity': 'u128', 'settlement_price': 'u128'}} |
Closed¶
A loan was closed
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
loan_id | T::LoanId |
u64 |
collateral | AssetOf<T> |
('u64', 'u128') |
Created¶
A loan was created
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
loan_id | T::LoanId |
u64 |
loan_info | LoanInfo<T> |
{'schedule': {'maturity': {'Fixed': {'date': 'u64', 'extension': 'u64'}}, 'interest_payments': ('None',), 'pay_down_schedule': ('None',)}, 'collateral': ('u64', 'u128'), 'interest_rate': {'Fixed': {'rate_per_year': 'u128', 'compounding': ('Secondly',)}}, 'pricing': {'Internal': {'collateral_value': 'u128', 'valuation_method': {'DiscountedCashFlow': {'probability_of_default': 'u128', 'loss_given_default': 'u128', 'discount_rate': 'scale_info::117'}, 'OutstandingDebt': None, 'Cash': None}, 'max_borrow_amount': {'UpToTotalBorrowed': {'advance_rate': 'u128'}, 'UpToOutstandingDebt': {'advance_rate': 'u128'}}}, 'External': {'price_id': {'Isin': '[u8; 12]', 'ConversionRatio': ('scale_info::77', 'scale_info::77')}, 'max_borrow_amount': {'NoLimit': None, 'Quantity': 'u128'}, 'notional': 'u128', 'max_price_variation': 'u128'}}, 'restrictions': {'borrows': ('NotWrittenOff', 'FullOnce', 'OraclePriceRequired'), 'repayments': ('None', 'Full')}} |
DebtIncreased¶
Debt of a loan has been increased
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
loan_id | T::LoanId |
u64 |
amount | PrincipalInput<T> |
{'Internal': 'u128', 'External': {'quantity': 'u128', 'settlement_price': 'u128'}} |
DebtTransferred¶
Debt has been transfered between loans
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
from_loan_id | T::LoanId |
u64 |
to_loan_id | T::LoanId |
u64 |
repaid_amount | RepaidInput<T> |
{'principal': {'Internal': 'u128', 'External': {'quantity': 'u128', 'settlement_price': 'u128'}}, 'interest': 'u128', 'unscheduled': 'u128'} |
borrow_amount | PrincipalInput<T> |
{'Internal': 'u128', 'External': {'quantity': 'u128', 'settlement_price': 'u128'}} |
Mutated¶
An active loan was mutated
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
loan_id | T::LoanId |
u64 |
mutation | LoanMutation<T::Rate> |
{'Maturity': {'Fixed': {'date': 'u64', 'extension': 'u64'}}, 'MaturityExtension': 'u64', 'InterestRate': {'Fixed': {'rate_per_year': 'u128', 'compounding': ('Secondly',)}}, 'InterestPayments': ('None',), 'PayDownSchedule': ('None',), 'Internal': {'ValuationMethod': {'DiscountedCashFlow': {'probability_of_default': 'u128', 'loss_given_default': 'u128', 'discount_rate': {'Fixed': 'InnerStruct'}}, 'OutstandingDebt': None, 'Cash': None}, 'ProbabilityOfDefault': 'u128', 'LossGivenDefault': 'u128', 'DiscountRate': {'Fixed': {'rate_per_year': 'u128', 'compounding': ('Secondly',)}}}} |
PortfolioValuationUpdated¶
The portfolio valuation for a pool was updated.
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
valuation | T::Balance |
u128 |
update_type | PortfolioValuationUpdateType |
('Exact', 'Inexact') |
Repaid¶
An amount was repaid for a loan
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
loan_id | T::LoanId |
u64 |
amount | RepaidInput<T> |
{'principal': {'Internal': 'u128', 'External': {'quantity': 'u128', 'settlement_price': 'u128'}}, 'interest': 'u128', 'unscheduled': 'u128'} |
WriteOffPolicyUpdated¶
The write off policy for a pool was updated.
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
policy | BoundedVec<WriteOffRule<T::Rate>, T::MaxWriteOffPolicySize> |
[{'triggers': 'scale_info::129', 'status': {'percentage': 'u128', 'penalty': 'u128'}}] |
WrittenOff¶
A loan was written off
Attributes¶
Name | Type | Composition |
---|---|---|
pool_id | T::PoolId |
u64 |
loan_id | T::LoanId |
u64 |
status | WriteOffStatus<T::Rate> |
{'percentage': 'u128', 'penalty': 'u128'} |
Storage functions¶
ActiveLoans¶
Storage for active loans.
The indexation of this storage differs from CreatedLoan
or
ClosedLoan
because here we try to minimize the iteration speed over
all active loans in a pool.
Python¶
result = substrate.query(
'Loans', 'ActiveLoans', ['u64']
)
Return value¶
[
(
'u64',
{
'borrower': 'AccountId',
'collateral': ('u64', 'u128'),
'origination_date': 'u64',
'pricing': {
'External': 'scale_info::706',
'Internal': 'scale_info::704',
},
'repayments_on_schedule_until': 'u64',
'restrictions': {
'borrows': 'scale_info::182',
'repayments': 'scale_info::183',
},
'schedule': {
'interest_payments': 'scale_info::119',
'maturity': 'scale_info::116',
'pay_down_schedule': 'scale_info::120',
},
'total_borrowed': 'u128',
'total_repaid': {
'interest': 'u128',
'principal': 'u128',
'unscheduled': 'u128',
},
'write_off_percentage': 'u128',
},
),
]
ClosedLoan¶
Storage for closed loans. No mutations are expected in this storage. Loans are stored here for historical purposes.
Python¶
result = substrate.query(
'Loans', 'ClosedLoan', ['u64', 'u64']
)
Return value¶
{
'closed_at': 'u32',
'info': {
'collateral': ('u64', 'u128'),
'interest_rate': {
'Fixed': {'compounding': ('Secondly', ), 'rate_per_year': 'u128'},
},
'pricing': {
'External': {
'max_borrow_amount': {'NoLimit': None, 'Quantity': 'u128'},
'max_price_variation': 'u128',
'notional': 'u128',
'price_id': {
'ConversionRatio': ('scale_info::77', 'scale_info::77'),
'Isin': '[u8; 12]',
},
},
'Internal': {
'collateral_value': 'u128',
'max_borrow_amount': {
'UpToOutstandingDebt': 'InnerStruct',
'UpToTotalBorrowed': 'InnerStruct',
},
'valuation_method': {
'Cash': None,
'DiscountedCashFlow': 'scale_info::123',
'OutstandingDebt': None,
},
},
},
'restrictions': {
'borrows': ('NotWrittenOff', 'FullOnce', 'OraclePriceRequired'),
'repayments': ('None', 'Full'),
},
'schedule': {
'interest_payments': ('None', ),
'maturity': {'Fixed': {'date': 'u64', 'extension': 'u64'}},
'pay_down_schedule': ('None', ),
},
},
'total_borrowed': 'u128',
'total_repaid': {
'interest': 'u128',
'principal': 'u128',
'unscheduled': 'u128',
},
}
CreatedLoan¶
Storage for loans that has been created but are not still active.
Python¶
result = substrate.query(
'Loans', 'CreatedLoan', ['u64', 'u64']
)
Return value¶
{
'borrower': 'AccountId',
'info': {
'collateral': ('u64', 'u128'),
'interest_rate': {
'Fixed': {'compounding': ('Secondly', ), 'rate_per_year': 'u128'},
},
'pricing': {
'External': {
'max_borrow_amount': {'NoLimit': None, 'Quantity': 'u128'},
'max_price_variation': 'u128',
'notional': 'u128',
'price_id': {
'ConversionRatio': ('scale_info::77', 'scale_info::77'),
'Isin': '[u8; 12]',
},
},
'Internal': {
'collateral_value': 'u128',
'max_borrow_amount': {
'UpToOutstandingDebt': 'InnerStruct',
'UpToTotalBorrowed': 'InnerStruct',
},
'valuation_method': {
'Cash': None,
'DiscountedCashFlow': 'scale_info::123',
'OutstandingDebt': None,
},
},
},
'restrictions': {
'borrows': ('NotWrittenOff', 'FullOnce', 'OraclePriceRequired'),
'repayments': ('None', 'Full'),
},
'schedule': {
'interest_payments': ('None', ),
'maturity': {'Fixed': {'date': 'u64', 'extension': 'u64'}},
'pay_down_schedule': ('None', ),
},
},
}
LastLoanId¶
Contains the last loan id generated
Python¶
result = substrate.query(
'Loans', 'LastLoanId', ['u64']
)
Return value¶
'u64'
PortfolioValuation¶
Stores the portfolio valuation associated to each pool
Python¶
result = substrate.query(
'Loans', 'PortfolioValuation', ['u64']
)
Return value¶
{'last_updated': 'u64', 'value': 'u128', 'values': [('u64', 'u128')]}
WriteOffPolicy¶
Stores write off policy used in each pool
Python¶
result = substrate.query(
'Loans', 'WriteOffPolicy', ['u64']
)
Return value¶
[
{'status': {'penalty': 'u128', 'percentage': 'u128'}, 'triggers': 'scale_info::129'},
]
Constants¶
MaxActiveLoansPerPool¶
Max number of active loans per pool.
Value¶
300
Python¶
constant = substrate.get_constant('Loans', 'MaxActiveLoansPerPool')
MaxWriteOffPolicySize¶
Max number of write-off groups per pool.
Value¶
100
Python¶
constant = substrate.get_constant('Loans', 'MaxWriteOffPolicySize')
Errors¶
BorrowLoanError¶
Emits when the loan can not be borrowed from
CloseLoanError¶
Emits when the loan can not be closed
CreateLoanError¶
Emits when the loan is incorrectly specified and can not be created
LoanNotActiveOrNotFound¶
Emits when loan doesn't exist or it's not active yet.
MaxActiveLoansReached¶
Emits when the max number of active loans was reached
MismatchedPricingMethod¶
Emits when the pricing method is not compatible with the input
MutationError¶
Emits when the loan can not be mutated
NFTOwnerNotFound¶
Emits when the NFT owner is not found
NoLoanChangeId¶
The Change Id does not belong to a loan change
NoValidWriteOffRule¶
Emits when a write-off rule is not found in a policy for a specific loan. It happens when there is no policy or the loan is not overdue.
NotLoanBorrower¶
Emits when the applicant account is not the borrower of the loan
NotNFTOwner¶
Emits when NFT owner doesn't match the expected owner
PoolNotFound¶
Emits when pool doesn't exist
RepayLoanError¶
Emits when the loan can not be repaid from
SettlementPriceExceedsVariation¶
Emits when settlement price is exceeds the configured variation.
TransferDebtAmountMismatched¶
Emits when debt is transfered with different repaid/borrow amounts
TransferDebtToSameLoan¶
Emits when debt is transfered to the same loan
UnrelatedChangeId¶
The Change Id exists but it's not releated with the expected change
WrittenOffError¶
Emits when the loan can not be written off