This EIP defines a standard interface for financial lease agreements represented on-chain. Each lease is identified by a leaseId and the lessor position is represented as an ERC-721 token, making assignment and securitization of lease portfolios compatible with existing NFT infrastructure. The interface exposes the payment schedule, outstanding balance, arrears and default state, purchase option, and party assignment, while remaining neutral with respect to jurisdiction, interest mathematics, and compliance regime.
Schedules are denominated in abstract units of account with an explicit conversion to the payment asset, so that both fixed-amount leases and index-adjusted leases (UVA in Argentina, UF in Chile, IPCA-linked in Brazil) implement the same interface.
An optional extension binds the lease to an on-chain token representing the leased asset, enabling atomic settlement of the purchase option.
Financial leasing is a major credit instrument worldwide, yet no ERC defines how a lease contract should be queried or observed on-chain. ERC-4907 standardizes temporary usage rights for NFTs but does not model installments, interest, arrears, purchase options, or assignment. ERC-2615 (a stagnant Draft) adds rental and mortgage roles and liens to ERC-721 but has no credit semantics: no schedules, no indexed denominations, no delinquency model, no purchase option. ERC-3475 standardizes fungible debt securities organized in classes and nonces, oriented to redemption, not bilateral contracts with named parties, formal default, or asset escrow. Outside the EVM standards process, ACTUS (Algorithmic Contract Types Unified Standards) defines a taxonomy of roughly three dozen contract types describing financial instruments as algorithmic cash-flow patterns, and has been used as a reference standard by the US Office of Financial Research. A Solidity encoding existed but covered a single contract type and has been unmaintained since 2020. ACTUS and this standard address different problems: ACTUS is a modelling ontology that requires adopting its full taxonomy to describe an instrument, while this standard defines how a lease contract is observed and queried without prescribing how its obligations are computed. An implementation may use an ACTUS engine internally and expose this interface externally. Compliance standards such as ERC-3643 and ERC-7943 govern who may hold a token, but not the financial semantics of a lease.
Without a common interface, every originator exposes bespoke contracts, and integrators (secondary marketplaces, securitization vaults, auditors, credit scorers) must write custom adapters per originator and per country. This standard follows the approach of ERC-4626: it does not invent leasing; it defines a common way to interact with lease contracts, so that a fund can price and acquire lease portfolios originated in different countries through one interface.
The semantics of "financial lease" used here are informed by the UNIDROIT Convention on International Financial Leasing (Ottawa, 1988): a lessor grants a lessee the use of an asset against periodic payments, with an eventual option to purchase. The term refers to the contractual instrument this interface models. This standard does not determine the accounting classification of a lease, for the lessor or the lessee, under IFRS 16 or ASC 842.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
tokenId equals leaseId.Implementations MUST implement ERC-165 and ERC-721, and MUST use tokenId == leaseId. Transferring the ERC-721 token assigns the lessor position; implementations MAY restrict transfers through a compliance layer.
interface IFinancialLease {
enum LeaseStatus {
Active, InArrears, InDefault,
Terminated, Completed, PurchaseExercised
}
// Legal layer
function jurisdiction(uint256 leaseId) external view returns (bytes2);
function governingLaw(uint256 leaseId) external view returns (string memory);
function agreementHash(uint256 leaseId) external view returns (bytes32);
function assetReference(uint256 leaseId) external view returns (string memory);
// Parties
function lessor(uint256 leaseId) external view returns (address);
function lessee(uint256 leaseId) external view returns (address);
// Denomination and conversion
function paymentAsset(uint256 leaseId) external view returns (address);
function denomination(uint256 leaseId)
external view returns (string memory symbol, address oracle);
function convertToAssets(uint256 leaseId, uint256 units)
external view returns (uint256 assets);
function convertToUnits(uint256 leaseId, uint256 assets)
external view returns (uint256 units);
function conversionRateAsOf(uint256 leaseId)
external view returns (uint256 rate, uint64 asOf);
// Schedule (immutable, in units)
function paymentCount(uint256 leaseId) external view returns (uint256);
function paymentAt(uint256 leaseId, uint256 index)
external view returns (uint256 units, uint64 dueDate, bool paid);
// Live economics
function outstandingUnits(uint256 leaseId) external view returns (uint256);
function outstandingBalance(uint256 leaseId) external view returns (uint256);
function nextPayment(uint256 leaseId)
external view returns (uint256 assets, uint64 dueDate);
function arrears(uint256 leaseId) external view returns (uint256);
function status(uint256 leaseId) external view returns (LeaseStatus);
// Input freshness (see "Input freshness")
function inputFreshness(uint256 leaseId, bytes32 input)
external view returns (uint64 observedAt, uint64 reportedAt, uint64 maxStaleness);
// Actions
function pay(uint256 leaseId, uint256 assets) external;
function purchaseOption(uint256 leaseId)
external view returns (uint256 priceInAssets, bool exercisable);
function exercisePurchaseOption(uint256 leaseId) external;
// Events
event LeaseCreated(uint256 indexed leaseId, address indexed lessor,
address indexed lessee, bytes2 jurisdiction, address paymentAsset);
event PaymentReceived(uint256 indexed leaseId, address indexed payer,
uint256 assets, uint256 unitsSettled, uint256 conversionRate,
uint256 newOutstandingUnits);
event ArrearsAccrued(uint256 indexed leaseId, uint256 totalArrearsUnits);
event DefaultDeclared(uint256 indexed leaseId, address indexed declarer);
event DefaultCured(uint256 indexed leaseId);
event PurchaseOptionExercised(uint256 indexed leaseId, uint256 priceInAssets);
event LeaseTerminated(uint256 indexed leaseId, LeaseStatus finalStatus);
event LesseeAssigned(uint256 indexed leaseId,
address indexed oldLessee, address indexed newLessee);
event TerminationRecorded(uint256 indexed leaseId, bytes32 indexed reason);
event ServicingUpdated(uint256 indexed leaseId, bytes32 indexed input,
uint64 observedAt, uint64 reportedAt);
}
Normative requirements:
paymentAt MUST be immutable after LeaseCreated, and MUST be denominated in units of account.denomination MUST return oracle == address(0) and both conversion functions MUST be the identity function.outstandingBalance(id) MUST equal convertToAssets(id, outstandingUnits(id)).conversionRateAsOf MUST express how many base units of the payment asset correspond to 1e18 units of account, absorbing both the oracle's and the payment asset's decimal conventions.convertToAssets used to compute amounts owed MUST round up; convertToUnits used to credit payments MUST round down. Implementations MUST document rounding.arrears(leaseId) MUST be a function of the payment schedule, the recorded payment history, and the time elapsed since each installment's due date. They MUST NOT depend on the number, frequency, or timing of the transactions that caused accrual state to be persisted. Two leases with an identical schedule, identical payments and identical elapsed time MUST report identical arrears, however many state-mutating calls occurred in between.paymentAt MUST report an installment as paid only when it has been effectively settled; overdue unpaid installments MUST NOT be reported as paid.block.timestamp, regardless of when the implementation last persisted state. An implementation MAY accrue lazily in state-changing calls, but a caller that only reads MUST NOT observe stale delinquency, arrears, or status.block.timestamp exceeds its dueDate. Payment at exactly dueDate MUST NOT be treated as overdue.PaymentReceived.conversionRate MUST record the rate applied, using the same scaling as conversionRateAsOf, so historical conversions are reconstructible without oracle history.InArrears MUST be objective (a due date elapsed unpaid). Transitions to InDefault MUST be explicit acts by an authorized declarer and MUST emit DefaultDeclared.ArrearsAccrued signals the objective fact that a due date elapsed unpaid. It does not by itself imply economic loss: an installment settled shortly after its due date may emit the event with no penalty accrued. Consumers evaluating credit behaviour SHOULD read the reported amount rather than the mere occurrence of the event.LesseeAssigned. It MUST be authorized either by the outgoing lessee, or by a role explicitly and separately granted that authority. Holding a role whose purpose is default declaration, servicing, or holding the lessor position MUST NOT by itself confer authority to reassign the lessee, and an implementation MUST NOT default such authority to the lease originator.conversionRateAsOf MUST always expose the timestamp of the rate that would be applied.Completed, PurchaseExercised and Terminated are terminal. An implementation MUST NOT transition a lease out of a terminal status, except from Completed to PurchaseExercised when the purchase option is exercised, and MUST determine terminality by explicit status membership rather than by the ordering of enumeration members.InDefault whose arrears are settled MUST return to a non-delinquent status and MUST emit DefaultCured. This preserves the standard's separation between the objective fact of delinquency (InArrears) and the formal act of declaring default (InDefault), and gives DefaultCured operational meaning.INPUT_COLLECTIONS and INPUT_ARREARS_RECORD report the state of the collections process — notices sent, payment plans agreed, referral to legal. They MUST NOT be read as an authoritative restatement of the amount returned by arrears(), which remains the sole on-chain source of truth for amounts owed. INPUT_INSURANCE_STATUS is scoped to insurance covering the leased asset itself, not to instruments guaranteeing payment.A lease is originated in Active and ends in a terminal status. The only transition between terminal statuses is Completed to PurchaseExercised.
| From | To | Trigger |
|---|---|---|
Active |
InArrears |
a due date elapses unpaid |
InArrears |
Active |
arrears settled |
Active / InArrears |
InDefault |
formal declaration by an authorized declarer |
InDefault |
Active |
arrears settled (DefaultCured) |
Active / InArrears / InDefault |
Terminated |
early termination |
| any non-terminal | Completed |
full schedule settled |
Completed |
PurchaseExercised |
purchase option exercised |
Permitted operations by status:
Operation names in this table other than pay and exercisePurchaseOption denote implementation-defined entry points. This includes declareDefault, lessee reassignment, the termination operations and updateServicing. This standard constrains their preconditions and observable effects, not their signatures, so they are not part of any interface or identifier defined here.
| Operation | Active |
InArrears |
InDefault |
Terminal |
|---|---|---|---|---|
pay |
✓ | ✓ | ✓ | ✗ |
declareDefault |
✓ | ✓ | ✗ | ✗ |
| lessee reassignment | ✓ | ✓ | see below | ✗ |
| propose / accept termination | ✓ | ✓ | ✓ | ✗ |
| terminate for default | ✗ | ✗ | ✓ | ✗ |
exercisePurchaseOption |
✗ | ✗ | ✗ | only from Completed |
updateServicing |
✓ | ✓ | ✓ | ✓ |
updateServicing remains available on terminated leases because servicing attestations report observed facts and do not alter lease state.
Whether lessee reassignment is permitted while a lease is InDefault is a commercial policy rather than an interoperability concern. Implementations MAY permit or forbid it, and SHOULD document which they do.
A lease MAY terminate before its schedule completes. The standard defines when a lease may reach Terminated and requires the reason to be observable; it does not define how any remaining obligation is settled.
Termination reasons are domain-separated identifiers:
bytes32 constant TERMINATION_MUTUAL_AGREEMENT = keccak256("erc8348.termination.mutualAgreement");
bytes32 constant TERMINATION_DEFAULT = keccak256("erc8348.termination.default");
InDefault and MUST NOT take effect before a configurable delay has elapsed since the declaration. That delay MUST be greater than zero: a zero value admits several reasonable readings and MUST be rejected at origination rather than assigned a default meaning.Terminated MUST emit both LeaseTerminated and TerminationRecorded, the latter carrying the reason. Implementations MAY define additional reasons under their own domain without changing this interface.Where a lease is written over a titled asset recorded in an ERC-8325 registry, implementations MAY expose the binding explicitly rather than encoding it in the assetReference string. This avoids cross-registry ambiguity: an anchor identifier alone is insufficient without the registry it lives in and the chain that registry is deployed on.
interface IFinancialLeaseAnchored {
/// @return chainId EIP-155 chain ID of the registry
/// @return registry ERC-8325 registry address
/// @return anchorId anchor identifier within that registry
function assetAnchor(uint256 leaseId) external view returns (
uint256 chainId, address registry, bytes32 anchorId);
}
assetAnchor returns zero values.anchorId with a zero registry, for instance — MUST be invalid, since it leaves consumers to define their own handling of an ambiguous reference.leaseId remains the lease layer's own identifier; this extension expresses a relationship to the asset layer, it does not replace the lease identifier with the anchor identifier. The two objects have distinct lifecycles.supportsInterface for 0x09fce36a.A lease depends on inputs with independent freshness characteristics. The reference-index observation used to convert an indexed schedule has one timestamp; off-chain servicing data (reconciled collections, manually recorded arrears, insurance status, asset condition, residual-value appraisals) has others. A consumer composing a lease-portfolio NAV cannot treat a freshly published NAV as fresh if it incorporates an economically old input.
Implementations MUST expose per-input freshness through inputFreshness, a single parameterized view declared in the core interface. It returns observedAt (when the datum was observed off-chain), reportedAt (when it was recorded on-chain) and maxStaleness (the tolerance declared for that input, 0 if none is declared). This view reports; it does not enforce. A consumer decides what staleness is acceptable for its purpose.
/// Base input identifiers, domain-separated. Implementations MAY define
/// additional identifiers under their own domain.
bytes32 constant INPUT_INDEX_OBSERVATION = keccak256("erc8348.input.indexObservation");
bytes32 constant INPUT_COLLECTIONS = keccak256("erc8348.input.collections");
bytes32 constant INPUT_ARREARS_RECORD = keccak256("erc8348.input.arrearsRecord");
bytes32 constant INPUT_INSURANCE_STATUS = keccak256("erc8348.input.insuranceStatus");
bytes32 constant INPUT_ASSET_CONDITION = keccak256("erc8348.input.assetCondition");
bytes32 constant INPUT_RESIDUAL_APPRAISAL = keccak256("erc8348.input.residualAppraisal");
INPUT_INDEX_OBSERVATION is not a stored attestation: its timestamps MUST be derived from conversionRateAsOf, and its maxStaleness reflects the oracle tolerance already governing conversion. It is the only input whose staleness MAY revert a state-changing call (pay, exercisePurchaseOption), because conversion requires it.ServicingUpdated, and MUST be rejected for INPUT_INDEX_OBSERVATION.observedAt in the future. It MUST NOT reject an old one: how old is too old is the consumer's judgement, not the standard's.maxStaleness MUST be set as lease configuration, not by the servicer supplying the attestation. A servicer declaring the tolerance applied to its own attestations is self-assessment.bytes32 values rather than a closed enumeration, so that lease structures with inputs beyond the base set extend without an interface change. The base identifiers above are defined by this standard and MUST have the values given, so the common set does not fragment across implementations.Contracts implementing this extension MUST hold the bound asset token in escrow while status is Active, InArrears or InDefault.
interface IFinancialLeaseAssetBound {
enum AssetStandard { ERC721, ERC1155, ERC20 }
function boundAsset(uint256 leaseId) external view returns (
address token, uint256 tokenId, uint256 amount, AssetStandard std);
function canSettleAsset(uint256 leaseId, address to)
external view returns (bool);
event AssetEscrowed(uint256 indexed leaseId, address token, uint256 tokenId);
event AssetSettled(uint256 indexed leaseId, address indexed to);
event AssetReleased(uint256 indexed leaseId, address indexed to);
}
exercisePurchaseOption, if canSettleAsset(id, lessee) is true, settlement MUST be atomic with payment. If false, the option exercise MUST still be recorded and the asset MUST remain claimable by the lessee once the underlying token's transfer restrictions allow it.InDefault. It MUST require an explicit call by an authorized role and SHOULD be subject to a configurable timelock, reflecting jurisdictional repossession requirements.user for the duration of the lease.The ERC-165 identifiers for this standard are:
| Interface | Identifier |
|---|---|
IFinancialLease |
0xf6b0f493 |
IFinancialLeaseAssetBound |
0xf71550a8 |
IFinancialLeaseAnchored |
0x09fce36a |
Each identifier is the XOR of the function selectors declared in that interface alone; inherited and extension functions are not included. Implementation-defined entry points are not included. An implementation MUST return true from supportsInterface for 0xf6b0f493, and MUST return true for the identifier of each optional extension it implements.
Units of account follow the ERC-4626 share/asset separation: indexed leases are first-class citizens rather than a special case, which is essential in the jurisdictions where leasing volume is concentrated in Latin America. A fixed-rate lease is the degenerate case with identity conversion.
Lessor-position-as-NFT makes portfolio assignment and securitization free: existing ERC-721 marketplaces, custody and vault tooling work unchanged, with compliance enforced in the transfer hook rather than in this interface.
Two-tier delinquency (InArrears vs InDefault) separates the objectively computable fact of a missed payment from the legally significant declaration of default, which in many jurisdictions requires notice or grace periods. The standard records both without opining on local law.
Payment imputation is unspecified (penalties vs interest vs principal) because ordering rules differ by jurisdiction and are sometimes mandatory law. PaymentReceived exposes enough data for indexers to reconstruct any imputation.
Observation rather than modelling. A complete taxonomy of financial instruments — the approach taken by ACTUS — describes what a contract is with enough fidelity to compute its cash flows from first principles. That fidelity has a cost: an integrator must adopt the taxonomy to read anything. This standard takes the opposite trade: it says nothing about how obligations are computed and only defines how the result is observed, so that a marketplace or credit scorer can read a lease without adopting a modelling framework.
Accrual is defined by elapsed time, not by transaction history. An implementation that accrues penalties by repeatedly applying a periodic rate to a running balance produces different totals depending on how often the contract happens to be touched — and since payment entry points are typically permissionless, the party that benefits from a higher figure can also produce it at no cost. Requiring the reported figure to be a function of the schedule and elapsed time removes that possibility without prescribing whether interest is simple or compound.
Authority over the lessee position is separated from other roles. A role granted for one purpose — declaring default, servicing a portfolio, holding the lessor position — does not thereby acquire authority over who receives the purchase option. Conflating them creates a conflict of interest whenever the role holder benefits from a reassignment, and the party best positioned to exploit it is often the one the role defaults to.
The standard composes with, and does not modify, ERC-20, ERC-721, ERC-165, ERC-4907 and ERC-8325.
pay() cannot succeed while delinquency keeps accruing and default declaration and termination for default remain available. A lease could therefore reach default without the lessee having been able to pay, and some of these conditions can be induced by the counterparty. Implementations SHOULD document how they handle payment-path outages and the transition back to availability. Normative handling is under discussion.exercisePurchaseOption may be unable to deliver a compliance-restricted asset; recording the exercise while deferring delivery prevents loss of an acquired right.agreementHash binds the on-chain state to an off-chain agreement, but enforceability of the token as title or receivable depends on each jurisdiction's legal wrapper. This standard makes jurisdiction observable; it does not create legal effect.inputFreshness for off-chain categories reflects what an authorized servicer has attested, not independently verified truth. A stale or dishonest servicer degrades the freshness signal but cannot alter lease state, since servicing data is isolated from balance and status computation. Consumers relying on servicing freshness SHOULD treat the servicer as a trusted party and weight the signal accordingly.Copyright and related rights waived via CC0.