Design
Payments and the two guards
Every lender is paid in one step, to the cent. Two guards stand between a typo and the money: the enclave enforces the deal, and the contract enforces the money.
An interest payment
- The agent fixes the rate. In the demo deal: base rate 4.30% to 15 Dec 2026, all-in 7.80%.
- The agent types the total it means to request. For the first period that is 1,971,666.66. The chain shows the total; each lender’s share stays sealed.
- The borrower pays once. One transfer of test USDC into the deal.
- One settlement pays every lender. Alder is credited 985,833.33, Birch 591,500.00 and Cedar 394,333.33 inside the books. The chain shows one total, 1,971,666.66, and no lender’s share.
Two guards stand between a typo and the money
- The enclave enforces the deal. The agent types the total it means to request. The enclave works the total out from the register, and if the two differ it refuses and says so: “You entered 900,000,000.00; the register says 1,971,666.66.”
- The contract enforces the money. It never lets lenders be credited more than the borrower paid in for that request, and never pays out more test USDC than it holds for the deal. It checks this itself on every answer. If an answer ever broke a money rule, the contract would freeze the deal instead of paying.
The refusal is on the public record, and the books do not move. Approval does not replace the rule either: when the agent is a DAO, two operators can approve a request for 900,000,000.00 and the enclave still refuses it. See The agent, two of three.
One token transfer that also runs the books
The borrower pays with ft_transfer_call (NEP-141); the contract records the public amount and the enclave credits the books in the same transaction. If the books refuse, the tokens go back. Only the deal’s test USDC is accepted, and only the borrower pays a request.
Lender payments are credits inside the books, not token transfers, so no per-lender amount ever appears on chain. A lender takes its cash out with a withdrawal: Alder withdraws 500,000.00, and the contract pays exactly that in test USDC. Deposits and withdrawals are token transfers, so their amounts are public; what the money is for, and what is left in the books, are not.
A short payment, and the period that pays the rest
A short payment is shared pro rata, and the unpaid part is paid with the next period. In the demo the borrower pays 975,000.00 of the 1,950,000.00 requested for the second period. One settlement pays every lender the same half of what it is owed: Alder 422,500.00 of 845,000.00, Birch 292,500.00, Cedar 195,000.00 and Delta 65,000.00, each on its own screen only.
The third period’s request then reads 2,968,333.32: the third period’s interest plus every unpaid half. After it settles, Alder shows 1,219,833.33 paid, and nothing left owed from the short period.
The arithmetic
Money is whole cents in integers everywhere. Interest is ACT/360, rounded down to the cent. A short payment pays each lender the same fraction of what it is owed, rounded down to the cent, and a cent lost to rounding goes back to the borrower, never to a lender. The expected figures (985,833.33, 591,500.00, 394,333.33, total 1,971,666.66, and 65,000.00 kept by the seller) are the same ones the Canton tests use.
Tested
| Rule | Test |
|---|---|
| Every lender is paid in one step, to the cent | interest test_interest_paid_to_every_lender_in_one_step |
| A short payment is shared pro rata | interest test_short_payment_is_shared_pro_rata |
| The borrower cannot favour a lender | interest test_borrower_cannot_favour_a_lender |
| Every lender is paid, or none is | interest test_every_lender_is_paid_or_none_is |
| The agent cannot request more than the register says | principal test_agent_cannot_request_more_than_the_register_says |
| A settlement cannot pay more than was requested | principal test_settlement_cannot_pay_more_than_requested |
| No settlement without an open request | principal test_no_settlement_without_an_open_request |
| A withdrawal is capped by the party's cash | principal test_withdrawal_is_capped_by_the_party_s_cash |
| Lenders cannot be credited more than the borrower paid in | contract test_lenders_cannot_be_credited_more_than_the_borrower_paid_in |
| A payout cannot exceed the USDC held | contract test_a_payout_cannot_exceed_the_usdc_held |