Docs Técnicas
Wallet Loan
This is the canonical loan example used by the current test fixtures.
O conteúdo abaixo vem das fontes técnicas do repositório e é prerenderizado no site para leitura direta por pessoas, crawlers e agentes.
This is the canonical loan example used by the current test fixtures.
Commented source is available at wallet-loan-commented.trea.
python
contract WalletLoan:
storage:
active: bool = False
lender: Address
borrower: Address
requested_principal: u128 = 0
requested_rate_ppm: u64 = 0
rate_ppm: u64 = 0
principal_due: u128 = 0
last_accrual_ts: u64 = 0
@view
def is_active() -> bool:
return self.active
@tx
def request_loan(lender: Address, principal: u128, rate_ppm: u64):
require(self.active == False, "loan_already_active")
self.lender = lender
self.borrower = ctx.caller
self.requested_principal = principal
self.requested_rate_ppm = rate_ppm
emit LoanRequested(lender, ctx.caller, principal, rate_ppm)
@tx
def approve_loan():
require(ctx.caller == self.lender, "only_lender_can_approve")
require(self.active == False, "loan_already_active")
require(self.requested_principal != 0, "no_pending_request")
self.rate_ppm = self.requested_rate_ppm
self.principal_due = self.requested_principal
self.last_accrual_ts = ctx.block_timestamp
self.active = True
post(loan.origination_lines(ctx.asset, self.lender, self.borrower, self.requested_principal))
emit LoanApproved(self.lender, self.borrower, self.requested_principal, self.rate_ppm)
@tx
def accrue():
require(self.active == True, "loan_not_active")
let interest = loan.accrue_interest(self.principal_due, self.rate_ppm, self.last_accrual_ts, ctx.block_timestamp)
post(loan.accrual_lines(ctx.asset, self.lender, self.borrower, interest))
self.principal_due = self.principal_due + interest
self.last_accrual_ts = ctx.block_timestamp
emit InterestAccrued(self.principal_due, ctx.block_timestamp)
@tx
def repay(amount: u128):
require(ctx.caller == self.borrower, "only_borrower_can_repay")
require(self.active == True, "loan_not_active")
require(self.principal_due >= amount, "amount_exceeds_principal_due")
self.principal_due = self.principal_due + loan.accrue_interest(self.principal_due, self.rate_ppm, self.last_accrual_ts, ctx.block_timestamp)
post(loan.repayment_lines(ctx.asset, self.lender, self.borrower, amount))
self.principal_due = self.principal_due - amount
self.active = self.principal_due != 0
self.last_accrual_ts = ctx.block_timestamp
emit LoanRepaid(self.borrower, self.lender, amount)Execution Order
- guards happen before state transitions and postings;
- loan origination uses the canonical helper;
- accrual is explicit and timestamp-based;
- repayment posts accounting meaning before reducing business obligation;
activecloses when principal reaches zero.
State Transition Rules
request_loanrecords the pending request without creating an obligation;approve_loanactivates the contract and initializes principal state;accrueupdates business state after emitting the accrual posting plan;repayaccrues up to the current timestamp before applying repayment lines.
Ledger Effect Semantics
Loan origination is not a simple transfer. The borrower receives spendable capacity and also carries an obligation.
The canonical origination helper produces the spendable and obligation lines together, preventing contracts from forgetting the liability side.
Invariants Preserved
- lender receivable and borrower payable stay aligned;
- principal due matches the business-side obligation after each successful call;
activeonly remains true while principal due is non-zero.
Commented Entry Point Pattern
Use structured comments when a contract needs more review guidance than the raw code can carry:
python
@tx
def approve_loan():
# Purpose:
# Activate a pending loan request for the current lender.
# Guards:
require(ctx.caller == self.lender, "only_lender_can_approve")
require(self.active == False, "loan_already_active")
require(self.requested_principal != 0, "no_pending_request")
# State:
self.rate_ppm = self.requested_rate_ppm
self.principal_due = self.requested_principal
self.last_accrual_ts = ctx.block_timestamp
self.active = True
# Ledger effect:
post(loan.origination_lines(ctx.asset, self.lender, self.borrower, self.requested_principal))
# Event:
emit LoanApproved(self.lender, self.borrower, self.requested_principal, self.rate_ppm)