EVM patterns
ERC3156 Flash Loan Security: Callback Authority and Repayment
Repayment protects the lender's financing boundary, while the receiver must authorize the work performed during the loan. ERC3156 flash loan security requires lender and initiator checks plus the application's own request and allowance policy.

Key facts
- Original policy grid
- 64 logical inputs across 6 illustrative Boolean conditions
- Lender-only policy
- 32 accepted inputs, including 31 outside full illustrative policy
- Lender and initiator
- 16 accepted inputs, including 15 outside full illustrative policy
- Full illustrative policy
- 1 of 64 logical inputs accepted; not a normative ERC3156 control count
- Limit
- No EVM, loan, callback or token transfer executed
Follow the receiver rather than the borrowed balance
A lender can receive repayment while a borrower executes an unauthorized action.
ERC3156 flash loan security requires the receiver to authenticate its callback and bind the requested work to its own policy. Atomic repayment protects a lender's financing boundary. It does not certify the business logic that runs while the temporary funds are available.
Consider a receiver that holds tokens and offers a strategy callback. An approved lender calls its onFlashLoan function, but another actor initiated the loan. If the receiver assumes that lender identity also proves approval of the strategy data, it has skipped an authorization decision. This is a constructed teaching scenario; no loss or deployed exploit is asserted.
A flash loan callback is the receiver's execution step after the lender supplies the temporary tokens. ERC3156 defines a single-asset interface with a callback, a return-value check and repayment of principal plus fee. It distinguishes the callback caller from the initiator: the lender calls the receiver, while the initiator is whoever called the lender's flashLoan function.
| Element | Meaning | Receiver question |
|---|---|---|
| Callback caller | The contract invoking the receiver | Is this an approved lender? |
| Initiator | The caller that requested the lender's loan | May this actor request the receiver's work? |
| Receiver | The contract executing the callback | Which balances and authorities can it use? |
| Callback data | Receiver-specific action information | Was this exact action authorized? |
The ERC3156 specification recommends checking an approved lender before treating callback fields as genuine, then a trusted initiator before trusting the provenance of callback data. Its reference borrower uses the narrower policy that the initiator must be the borrower itself. Other applications can intentionally permit different initiators, but that permission needs an explicit rule.
Our broader flash loan attack guide examines temporary capital and downstream protocol state. Here the reader's job is narrower: review who can make a receiver act and approve repayment. That job remains relevant even when the strategy contains no oracle interaction and the temporary loan is fully repaid.
Start from the receiver's assets and permissions. A callback might trade, approve a spender or invoke a privileged function. The review needs to follow those effects under the receiver's identity. A familiar lender address does not establish that every possible use of those permissions is intended.
Original callback acceptance grid
Method
We defined an illustrative receiver policy with six independently supplied Boolean conditions, then enumerated the complete input product. The policy requires an approved lender, authorized initiator, requested token, requested amount, approved fee bound and matching request data. These are our teaching fixtures, not six mandatory ERC3156 requirements.
- Mechanism sources
- ERC3156 and OpenZeppelin Contracts
v5.4.0flash-mint source, retrieved on . - Population
- Both values for each of six policy conditions, producing
64logical input cases. - Compared predicates
- Lender-only acceptance, lender-plus-initiator acceptance and the full illustrative conjunction.
- Exclusions and missing cases
- None within the declared finite grid. Each input row is retained.
- Artifacts
seo/research/integration-boundaries-2026-10-11/support-execution/finite-models.pyanderc3156-flash-loan-security-model-2026-10-11.json, with the complete CSV ledger. These are repository artifacts, not public downloads.
Results
| Acceptance policy | Accepted cases | Accepted outside full policy |
|---|---|---|
| Approved lender only | 32 of 64 | 31 |
| Approved lender and initiator | 16 of 64 | 15 |
| All illustrative conditions | 1 of 64 | 0 |
This arithmetic separates identity checks from action constraints. Adding initiator authorization reduces the admitted population but does not bind the remaining action fields in this model. A real borrower might accept several tokens or an amount range rather than exact equality. Its full policy would then define a different accepted set.
Limits
- No EVM, token transfer or callback ran. Inputs are logical fixtures and the outputs are policy decisions.
- Independence is deliberate. A correct lender already constrains some callback fields, so every logical combination need not be reachable through that lender.
- The outside-policy counts are not vulnerabilities, attacks or deployed failure rates. They apply only to our declared illustrative policy.
Our result is useful as a review question: what does an authenticated callback still need to prove before the receiver spends its balances or grants approval? The full answer belongs to the application. The standard provides actor relationships, while the receiver defines which requested actions are permitted.
Authenticate the lender and the initiator
Checking the lender establishes the provenance of the callback fields only under that lender's correct implementation and trust assumptions. ERC3156 requires the lender to pass token, amount and data without modification, supply the specified initiator and use the fee returned by flashFee. Those guarantees do not identify who the receiver intended to authorize.
The next check is the initiator. If the receiver initiates its own loans, the reference policy initiator == address(this) may fit. A shared receiver or routed application may intentionally allow another contract. Record that actor and the scope of its authority instead of weakening the check to accommodate every caller.
- Caller trust
- The callback contract is an approved lender with the expected interface behavior.
- Initiator trust
- The actor that requested the loan is authorized to invoke this receiver workflow.
- Request binding
- The supplied action matches the receiver's accepted request or explicitly permitted policy.
Decode untrusted action data only within the intended authorization sequence. The reference borrower checks lender and initiator before decoding its callback data. An application may need preliminary parsing to locate a policy field, but parsing is not approval. Make the point where the data becomes authoritative explicit in the control flow.
Do not use the callback's return hash as proof of borrower safety. The lender requires the prescribed hash to recognize a successful interface response. A receiver can return that value after performing an unintended action. The hash check and the receiver's business invariant answer different questions.
Also inspect any way to invoke the callback directly. A caller can supply plausible-looking token, amount, fee and initiator values without transferring a loan. A direct callback test should demonstrate rejection at the lender guard using otherwise valid business fixtures. If it fails earlier due to malformed data, it has not exercised the intended authority check.
Lender upgrades or trust-list changes can alter the receiver's assumption. Identify the authority that changes the accepted lender and decide whether outstanding requests remain valid after that change. This is a deployment-specific policy question, not an automatic property of the ERC3156 interface.
Bind repayment to the approved action
The standard requires the receiver to approve principal plus fee to the callback caller before the callback finishes, so the lender can collect repayment. This creates an allowance boundary. Verify the amount, spender and timing against the accepted request, rather than treating repayment approval as harmless because the loan is temporary.
ERC3156's security discussion warns about trusted initiators both when a receiver maintains an approval and when it grants one during the callback. A long-lived allowance can give a later request access to funds beyond the receiver's intended action. The exact risk depends on lender behavior and receiver policy; an allowance alone is not proof of an exploit.
OpenZeppelin's pinned flash-mint implementation shows one settlement composition. It mints to the receiver, checks the callback return, spends allowance and burns principal or principal plus fee. With a nonzero fee receiver, it burns principal and transfers the fee separately. Its default fee is zero. Other ERC3156 lenders can lend preexisting balances instead of minting.
| Evidence | What to inspect | What it does not establish |
|---|---|---|
| Repayment allowance | Approved spender and bounded amount | Authorization of strategy data |
| Correct callback return | Lender-side interface check | Receiver business invariant |
| Principal and fee collected | Lender settlement path | Safety of downstream pool interactions |
| Flash-mint supply reconciled | Pinned mint and burn accounting | Compatibility of every token extension |
Choose the fee policy before action execution. It may use a fixed supported lender quote, an approved cap or another explicit rule. A caller-controlled fee that is never checked against the request can expand repayment authority. Our model uses a Boolean approved-fee condition and does not recommend a universal cap.
Maximum-loan query and fee query have different unsupported-token behavior in the standard. maxFlashLoan returns zero for an unsupported token, while flashFee reverts. An adapter should distinguish unsupported capability from a transport failure and from a supported loan that exceeds its maximum. Do not convert every negative observation into the same liquidity message.
OpenZeppelin warns that its default flash-mint maximum needs an override for capped-token compositions such as the cited cap-sensitive extensions. A mathematically large maximum is not automatically a usable amount under all inherited constraints. Pin the composed token implementation and test the actual boundary.
Approval cleanup is an application requirement to define and verify. Inspect residual allowance after successful settlement and after relevant failure paths in the actual token/lender pair. A conventional ERC20 expectation should not be generalized to every token without checking its behavior.
Keep capability discovery separate from execution evidence. A maximum-loan query reports a supported bound, and a fee query reports its corresponding charge. Neither transfers tokens or demonstrates that this receiver can repay. The actual loan call must follow its callback and settlement requirements and return success. Preserve the query results as prerequisites alongside the completed transaction's own evidence.
Supply reconciliation needs the fee branch, not a blanket restored-supply assertion. For the pinned flash-mint implementation, a zero fee leaves the original supply after principal is burned. A nonzero fee sent to a separate fee receiver also leaves the original supply while moving fee tokens. If that nonzero fee is instead burned with principal, supply ends below its original value by the fee. These are source-derived branch expectations, not measurements of executed loans.
The receiver must have the tokens needed for its fee as well as the borrowed principal. Define where those tokens come from in a test fixture. A strategy that produces enough repayment tokens in one case may fail in another. Keep the funding precondition visible so a fee branch is not diagnosed as a callback-authorization error.
Inspect the final supply and balance assertions together. When fees are transferred, the expected fee-receiver change belongs in the accounting record. When fees are burned, the supply change belongs there instead. A single assertion that the lender received its amount cannot distinguish the intended token-accounting branch.
Token extensions make those expectations application-specific. A capped or vote-sensitive composition may constrain the usable flash-mint maximum differently from the base calculation. Test the composed maximum and a neighboring rejected amount with otherwise valid callback data. That positive control distinguishes a supported amount boundary from a borrower that always fails because it cannot repay.
A retained settlement fixture can later reveal changes caused by a fee override, a new fee receiver or a token hook. Record those configuration and source dependencies as review triggers. The callback identity matrix remains useful, but it cannot certify the accounting assumptions of a changed token composition.
Test callbacks, approvals and nested execution
Use an accepted loan request as the control fixture. Keep balances, lender configuration and action data valid while changing one authorization condition. The resulting failure should identify the intended check. A borrower that rejects every call due to a missing balance can pass a superficial unauthorized-initiator test without enforcing initiator policy.
- Establish the permitted lender, initiator, token, amount rule, fee policy and requested action before starting the loan.
- Call the callback directly from an unapproved address with otherwise valid parameters. Confirm the caller guard is the failing condition.
- Use the approved lender with an unauthorized initiator, then repeat with the permitted initiator as the positive control.
- Alter a material action field while preserving the actor checks. Verify that the receiver cannot reuse approval for a different action.
- Exercise incorrect callback return, callback revert and failed repayment in the actual implementation. Inspect the final application and allowance state.
- Reconcile the completed transaction with the business invariant, not only the lender's repayment result.
Nested execution deserves a policy of its own. A strategy may intentionally request another loan or call an external pool that calls back into the receiver. Determine whether the in-progress request state is frame-specific, shared or deliberately locked. A single stored expected amount can be overwritten by a nested workflow unless the design prevents or distinguishes it. This is a test recommendation, not an observed finding in the finite grid.
Reentrancy protection should fit the receiver's state transitions. The reentrancy analysis explains why external calls must be examined with the state they can observe or change. A lender guard identifies a caller; it does not automatically prevent that caller's authorized path from invoking unguarded receiver functions.
Check both the loan wrapper's atomic outcome and the application assertions. A reverted transaction can restore state, while a successful transaction may preserve an unintended trade or authority change. The test should state which outcome is expected and why, then inspect the relevant balances and permissions after it.
A fork can supply a real lender and token configuration, but it still reflects the chosen block and test inputs. Preserve the code versions, addresses and block reference. Supplement that realistic fixture with boundary cases absent from the selected state, including unsupported tokens and fee changes where the implementation permits them.
Explain what the borrower review establishes
A receiver review should produce an authorization map, request-binding tests and repayment evidence. Name the supported lenders and token behaviors, the initiators allowed to request work and the application effects permitted during the callback. List nested-call assumptions and residual allowance policy beside the test results.
The security provider directory and Pharos Production member profile can help select an independent reviewer. Give the reviewer the receiver and its strategy dependencies, rather than a lender interface alone. The lender's correct repayment mechanism cannot establish that a receiver's external trades preserve its own accounting.
Keep the original grid in its role as an explanatory artifact. Its lender-only predicate admitted 31 cases outside our complete illustrative policy. That count shows why authentication and action authorization need separate reasoning; it does not count reachable attacks. Application tests must supply the actual allowed set and demonstrate the relevant behavior.
A useful public conclusion is therefore specific: the receiver authenticates its callback context, constrains its requested action and grants repayment authority under stated assumptions. The evidence packet should make each of those statements inspectable. Repayment alone supports a narrower conclusion.
Frequently asked questions
Does ERC3156 standardize a simultaneous multi-asset loan?
The cited standard covers single-asset flash loans. A multi-asset interface or nested composition needs its own review; do not infer its callback semantics from a similar function name.
Can an application use a flash loan without an oracle?
Yes. The financing interface does not require an oracle. Review whatever state and external effects the receiver's actual strategy uses rather than force an oracle checklist onto every borrower.
Does the default zero fee make a flash-mint operation costless?
The pinned default flash fee is zero, but that is distinct from transaction execution costs and any application effects. Do not advertise a total-cost guarantee from the fee function alone.