Repository navigation
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Microloans
Step-up microloans: a lender offers a zero-interest loan to one borrower and escrows a grant that
unlocks once the loan is repaid. For example, Bread lends a community center 1,000 USDT for
computers, and once the center pays that back, another 1,000 USDT is released to it for free.
This is the design rationale for
src/contracts/Microloans.sol.0. What problem it solves
The other stack types are built for groups that save together. Some partners need something
different: one organization receiving money up front and paying it back over time, with an
incentive to finish. Microfinance calls this progressive lending, where repaying one loan earns
access to the next. Here the next step is a grant rather than a bigger loan, so the lender absorbs
the grant as the cost of the program, but only once the borrower has shown it can repay.
1. The mechanism
A loan has immutable terms fixed at
create:(token, vault, principal, grant, acceptBy, repaymentPeriod, termsHash). The caller becomes the lender and paysprincipal + grantintoescrow in the same transaction, so the borrower can verify on-chain that both the loan and the
reward are real before agreeing to anything.
State is derived, never stored:
create, or left empty and set once withsetBorrower. This matches theapp flow where someone asks to join off-chain and the lender adds them. Only the borrower can
accept.
accept(id, termsHash)must pass the hash of the off-chain agreement (for examplean IPFS document with the conditions in plain language). This records on-chain exactly which
terms the borrower agreed to. The principal is paid out immediately and the clock starts:
repayBy = acceptedAt + repaymentPeriod.repayaccepts any amount at any time, from the borrower or anyone paying on theirbehalf. Overpayment is capped, so only what is owed is pulled. Repayments are credited to the
lender and paid out with
collect, which anyone can trigger.releaseGrantpaysthe grant to the borrower. It is permissionless, so the app or an automation can trigger it,
but the grant can only ever go to the borrower.
repayBythe loan is Overdue. The borrower can still repay and claim thegrant until the lender acts. The lender can either
extendRepayByto give more time orreclaimGrantto take the grant back. The principal remains owed after a reclaim and can stillbe repaid, but the grant is gone.
canceland get everything back, includingyield.
2. Yield on escrow
Money waits in escrow twice: the principal between offer and acceptance, and the grant for the
whole repayment period. Each loan can name an ERC-4626 vault from an admin allowlist, and escrow is
deposited there instead of sitting idle. On Celo the natural choice is Aave v3, which supports
USDT; Aave's static aToken wrappers expose the ERC-4626 interface. The exact wrapper address for
USDT on Celo should be confirmed before it is allowlisted.
All yield belongs to the lender. When escrow empties (grant paid, reclaimed, or offer cancelled),
whatever is left beyond what was owed out is credited to the lender. Each loan tracks its own
vault shares, so two loans in the same vault never touch each other's yield.
Using a generic ERC-4626 interface keeps the contract chain-agnostic. A loan with
vault = address(0)keeps escrow as a plain token balance.3. Safety
plus lender credits. For vault loans, the contract's vault shares equal the sum of per-loan
shares. Every increment is matched by a transfer in, and every decrement by a transfer out.
new loans may use. Existing loans keep their token and vault. The lender can cancel only before
acceptance and reclaim the grant only once the loan is Overdue; neither touches the borrower's
principal or repayments.
borrower only owes what was actually disbursed (
disbursed, notprincipal), so a vault lossnever leaves them owing money they did not receive.
utilization), acceptance, grant release and cancellation revert until liquidity returns. No
funds are lost, but actions can be delayed. Allowlist only vaults with deep liquidity.
nonReentrantand updates state beforetransfers, except where a vault's return value is needed to update shares.
policy. USDT on Celo uses 6 decimals, which the tests exercise.
4. Decisions worth defending
borrower's only choice is to accept as written, which keeps the offer verifiable and avoids a
negotiation protocol on-chain. A borrower-initiated request flow can live in the app.
earns yield on escrow instead, which partly offsets the cost of the grant.
incentive clear and the accounting simple.
right safety rule for a savings group but rules out lending to a borrower with no savings.
Loosening that cap would weaken the ASCA, so this lives on its own.
5. Scope (v1) and follow-ups
Implemented: lender-funded offers, late-bound borrower, terms-hash acceptance, flexible repayment
by anyone, permissionless grant release, reclaim on default, deadline extensions, ERC-4626 escrow
yield, and the view surface, behind an OZ v5
TransparentUpgradeableProxy, with 44 unit testsincluding a lifecycle fuzz test.
Deferred: multiple backers funding one loan, more than one step (e.g. 500 then 1,000 then a
grant), installment schedules with per-installment deadlines, yield routed to the borrower or a
solidarity fund, Chainlink automation for
releaseGrantandcollect, and fork tests against thelive Aave wrapper on Celo.