Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Home

Foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.

Foundry consists of:

  • Forge: Ethereum testing framework (like Truffle, Hardhat and DappTools).
  • Cast: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
  • Anvil: Local Ethereum node, akin to Ganache, Hardhat Network.
  • Chisel: Fast, utilitarian, and verbose solidity REPL.

Documentation

https://book.getfoundry.sh/

Usage

Build

$ forge build

Test

$ forge test

Format

$ forge fmt

Gas Snapshots

$ forge snapshot

Anvil

$ anvil

Deploy

$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>

Cast

$ cast <subcommand>

Help

$ forge --help
$ anvil --help
$ cast --help

Contents

HHelper

Git Source

Inherits: Ownable

Title: Helper features and modifiers.

Author: Obiaraije Henry

Inherits OpenZeppelin {Ownable} so the launch owner is set at construction.

State Variables

owner2

Secondary owner who may run every admin action but cannot manage owners.

Set by owner1 via {Henriable.actionAdminSetOwner2}; address(0) means unset.

address internal owner2

ownerUsdToken

USDC token used for all investments, payouts and payments.

IERC20 internal ownerUsdToken

vehicleNft

Vehicle-title NFT collection used to hold and hand over asset titles.

Minted to this contract when an investment is fully funded and transferred to the rider once the hire purchase is paid in full.

HenriableVehicleNFT internal vehicleNft

ownerPayoutsList

All owner/contract payouts ever made.

HOwnerPayout[] internal ownerPayoutsList

ownerPayouts

Maps an investment id => 1-based position in {ownerPayoutsList} of the most recent owner payout for that investment.

Key: investment id (bytes32). Value: index + 1 into {ownerPayoutsList}, so 0 means no owner payout has been recorded for the investment yet.

mapping(bytes32 => uint256) internal ownerPayouts

ownerPayoutHistory

Maps an investment id => the aggregate owner payout history for it.

Key: investment id (bytes32). Value: {HOwnerPayoutsHistory} holding the running totals of every owner payout recorded on that investment.

mapping(bytes32 => HOwnerPayoutsHistory) internal ownerPayoutHistory

assetsList

All registered assets, in insertion order.

HAsset[] internal assetsList

assets

Maps an asset id => 1-based position in {assetsList}.

Key: asset id (bytes32). Value: index + 1 into {assetsList}, so 0 means “not registered”; this single mapping provides an O(1) existence check and lookup.

mapping(bytes32 => uint256) internal assets

assetInvestments

Maps an asset id => the investments created for that asset.

Key: asset id (bytes32). Value: array of 1-based positions into {investmentsList} (subtract 1 to index the array).

mapping(bytes32 => uint256[]) internal assetInvestments

investmentsList

All investment opportunities ever created.

HInvestment[] internal investmentsList

investments

Maps an investment id => 1-based position in {investmentsList}.

Key: investment id (bytes32). Value: index + 1 into {investmentsList}, so 0 means “not registered” for an O(1) existence check and lookup.

mapping(bytes32 => uint256) internal investments

investorsList

All registered investors.

HInvestor[] internal investorsList

completedRiderInvestmentList

Indices (into {investmentsList}) of investments whose rider has fully paid and that are complete.

uint256[] internal completedRiderInvestmentList

investmentsCompleted

Indices (into {investmentsList}) of completed rider investments. This list is append-only and is never cleared.

uint256[] internal investmentsCompleted

titleInvestmentId

Maps a minted title token id => the investment it was minted for.

The forward link lives on the investment ({HInvestment.nftTokenId}); this is the reverse, so a title can resolve its investment – and thus its asset and rider – without scanning the investment list. Set once when the title mints.

mapping(uint256 => bytes32) internal titleInvestmentId

investors

Maps an investor address => 1-based position in {investorsList}.

Key: investor address. Value: index + 1 into {investorsList}, so 0 means “not registered”.

mapping(address => uint256) internal investors

investorPayoutList

All investor payouts ever made.

HInvestorPayout[] internal investorPayoutList

investorPayouts

Maps an investor and investment id => the investor’s payout in {investorPayoutList}.

Key: investor address, then investment id (bytes32). Value: 1-based position in {investorPayoutList} of that investor’s most recent payout for the investment, or 0 if none has been recorded.

mapping(address => mapping(bytes32 => uint256)) internal investorPayouts

ridersList

All registered riders.

HRider[] internal ridersList

riders

Maps a rider address => 1-based position in {ridersList}.

Key: rider address. Value: index + 1 into {ridersList}, so 0 means “not registered”.

mapping(address => uint256) internal riders

ridersHirePurchasesRequestsList

All rider hire-purchase requests ever made.

HRiderHirePurchaseRequest[] internal ridersHirePurchasesRequestsList

ridersHireRequestPurchasesByInvestment

Maps a rider id and investment id => the rider’s hire request in {ridersHirePurchasesRequestsList}.

Key: rider id (bytes32), then investment id (bytes32). Value: 1-based position in {ridersHirePurchasesRequestsList}, or 0 if the rider has not requested that investment.

mapping(bytes32 => mapping(bytes32 => uint256)) internal ridersHireRequestPurchasesByInvestment

rejectedRiders

Maps a rider id and investment id => a rejection marker.

Key: rider id (bytes32), then investment id (bytes32). Value: non-zero once the rider has been rejected for that investment, 0 otherwise.

mapping(bytes32 => mapping(bytes32 => uint256)) internal rejectedRiders

riderPayinsList

All rider periodic payments ever made.

HRiderPayin[] internal riderPayinsList

riderSavingsSlashesList

All rider savings slashes ever applied, in insertion order.

HRiderSavingsSlash[] internal riderSavingsSlashesList

riderSavingsSlashesByInvestment

Maps an investment id => 1-based position in {riderSavingsSlashesList} of the most recent slash for that investment.

Key: investment id (bytes32). Value: index + 1 into {riderSavingsSlashesList}, so 0 means no slash has been recorded yet.

mapping(bytes32 => uint256) internal riderSavingsSlashesByInvestment

stats

Running platform totals exposed by {Henriable.getStats}.

Counters are maintained in O(1) at each write site; the totals that mirror list lengths are filled in by the getter at read time.

HStats internal stats

assetActiveInvestmentsCount

Maps an asset id => the number of its currently active investments.

Used to keep {HStats.assetsWithRunningInvestments} exact without looping: the global counter only moves when this per-asset count crosses 0.

mapping(bytes32 => uint256) internal assetActiveInvestmentsCount

assetHasCompletedInvestment

Maps an asset id => whether one of its investments has completed.

Completion is monotonic, so a boolean is enough to count {HStats.assetsWithCompletedInvestments} exactly once per asset.

mapping(bytes32 => bool) internal assetHasCompletedInvestment

Functions

constructor

Deploys the helper, setting the contract owner, the USDC token and the NFT.

constructor(address initialOwner, IERC20 ownerUsdToken_, HenriableVehicleNFT vehicleNft_) Ownable(initialOwner);

Parameters

NameTypeDescription
initialOwneraddressThe address that will own the contract.
ownerUsdToken_IERC20The USDC token used for investments, payouts and payments.
vehicleNft_HenriableVehicleNFTThe vehicle-title NFT collection whose minter is this contract.

onlyOwners

Restricts a function to owner1 or owner2.

Owner-managing functions keep OpenZeppelin’s {onlyOwner} (owner1 only), so owner2 can run admin actions but cannot set or clear owners. Reverts with OpenZeppelin’s {OwnableUnauthorizedAccount} when the caller is neither.

modifier onlyOwners() ;

isInvestmentStatus

Returns whether an investment is currently in any of the given statuses.

function isInvestmentStatus(bytes32 investmentId, HInvestmentStatus[] memory statuses)
    internal
    view
    returns (bool);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.
statusesHInvestmentStatus[]The statuses to test for.

Returns

NameTypeDescription
<none>boolTrue if the investment’s current status equals any entry in statuses.

isAssetSettingsCorrect

Checks that an asset’s pricing settings can pay every investor and the owner.

The investor pools must cover the asset: every investor’s buying price times its slot count must be at least {HAsset.costPrice}, and the rider’s {HAsset.sellPrice} must cover that principal plus the profits promised to both pools plus {HAsset.ownerProfit}. The rider’s slashable savings ({HAsset.riderSlashableRepayableExtraCharge}) is charged on top of the sell price and refunded later, so it is deliberately excluded here.

function isAssetSettingsCorrect(HAsset memory asset) internal pure returns (bool);

Parameters

NameTypeDescription
assetHAssetThe asset configuration to validate.

Returns

NameTypeDescription
<none>boolTrue when the pools cover the cost price and the sell price covers the investor principal, their profits and the owner’s profit.

calcInvestmentOpportunityLevel1SettingsAccuracy

Checks that the level-1 settings of a new investment opportunity are consistent with its asset, so the rider’s payments can cover the profits promised to the senior and junior investor pools.

Placeholder: always returns true until the real accuracy math is implemented and reviewed. The investment parameter is intentionally unused.

function calcInvestmentOpportunityLevel1SettingsAccuracy(HInvestment storage) internal pure returns (bool);

Returns

NameTypeDescription
<none>boolTrue when the level-1 settings are accurate.

calcSeniorPoolPaidSoFar

Sums how much the senior investor pool has been paid so far on an investment.

An address may hold several senior seats, so a naive sum over the list would count that investor’s aggregate payout history once per seat. The mapping {HInvestment.seniorInvestors} points at the investor’s most recent seat, so only that seat’s entry counts, keeping each investor’s total counted once.

function calcSeniorPoolPaidSoFar(HInvestment storage investment) internal view returns (uint256);

Parameters

NameTypeDescription
investmentHInvestmentThe investment (storage reference).

Returns

NameTypeDescription
<none>uint256The total amount paid to senior investors, in USDC base units (6 decimals).

calcJuniorPoolPaidSoFar

Sums how much the junior investor pool has been paid so far on an investment.

An address may hold several junior seats, so a naive sum over the list would count that investor’s aggregate payout history once per seat. The mapping {HInvestment.juniorInvestors} points at the investor’s most recent seat, so only that seat’s entry counts, keeping each investor’s total counted once.

function calcJuniorPoolPaidSoFar(HInvestment storage investment) internal view returns (uint256);

Parameters

NameTypeDescription
investmentHInvestmentThe investment (storage reference).

Returns

NameTypeDescription
<none>uint256The total amount paid to junior investors, in USDC base units (6 decimals).

calcSharingFormula

Splits one rider payment across the owner and the two investor pools.

The rider’s refundable savings charge is collected on top of the payment and is not part of this split. The split is driven by what each pool is still owed, not by configurable percentages: the payment first fills the senior pool up to its remaining target (seats x (buying price + profit)), then the junior pool up to its target, and the owner receives whatever is left. Because the asset’s sell price covers the two pool targets plus {HAsset.ownerProfit}, this pays every investor their buying price plus profit in full and leaves the owner exactly its promised profit (plus any surplus when the sell price exceeds the targets). The owner absorbs integer-division dust.

function calcSharingFormula(HInvestment storage investment, HAsset storage asset, uint256 amount)
    internal
    view
    returns (HSharingBreakdown memory);

Parameters

NameTypeDescription
investmentHInvestmentThe investment being paid (storage reference).
assetHAssetThe asset backing the investment (storage reference).
amountuint256The rider payment to split, in USDC base units (6 decimals) (excludes the savings charge).

Returns

NameTypeDescription
<none>HSharingBreakdownThe per-bucket breakdown.

calcRiderAmountToPayNow

Computes the payment a rider owes for the next instalment of an investment.

The rider owes {HAsset.sellPrice} in total and the outstanding balance is that minus what they have paid. The periodic minimum is capped by the outstanding balance on the final instalment, then the asset’s per-period savings charge is always added on top so the rider’s refundable savings keep accumulating.

function calcRiderAmountToPayNow(HInvestment storage investment, HAsset storage asset)
    internal
    view
    returns (uint256);

Parameters

NameTypeDescription
investmentHInvestmentThe investment being paid (storage reference).
assetHAssetThe asset backing the investment (storage reference).

Returns

NameTypeDescription
<none>uint256The amount due for the next instalment, in USDC base units (6 decimals).

maybeUpdateInvestmentStatus

Advances an investment’s lifecycle status when its current state and timing allow it.

State machine: every transition goes through setInvestmentStatus, which also keeps the stats counters in step.

  • Pending -> AcceptingInvestments once the admin-set window opens (timeNextInvestmentStart <= now <= timeNextInvestmentEnd).
  • AcceptingInvestments -> InvestmentDone once the asset’s senior and junior pools are both filled, recording {HInvestment.investmentsDoneAt}.
  • InvestmentDone -> AcceptingRiders once the admin-set hire window opens (timeNextRiderHireRequestStart <= now <= timeNextRiderHireRequestEnd). It runs lazily on first use (e.g. when an investor or rider calls in), so the status catches up. After each status update it returns.
function maybeUpdateInvestmentStatus(bytes32 investmentId) internal;

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment to update.

actionMaybeUpdateStatus

Provided for frontend to call when they suspect a status should have changed but it did not. e.g. When start investment countdown has begone but investment is still pending. We can’t run a cron job so it has to be triggered somehow minimally.

function actionMaybeUpdateStatus(bytes32 _investmentId) external;

upsertInvestor

Creates an investor profile for an address, or updates it if it exists.

function upsertInvestor(address investorAddress, HInvestor memory investorProfile) internal;

Parameters

NameTypeDescription
investorAddressaddressThe investor’s address.
investorProfileHInvestorThe profile fields (name, country, age) to store.

upsertRider

Creates a rider profile for an address, or updates it if it exists.

function upsertRider(address riderAddress, bytes32 riderName, bytes32 riderCountry, uint8 riderAge) internal;

Parameters

NameTypeDescription
riderAddressaddressThe rider’s address.
riderNamebytes32The rider’s name.
riderCountrybytes32The rider’s country.
riderAgeuint8The rider’s age.

recordDistribution

Records the accounting for one rider payment split across its beneficiaries.

Writes the payout state and moves no tokens, so the caller can keep the transfer step last (checks-effects-interactions). The owner and each investor in a tier share their pool equally; an empty tier or any division remainder stays in the contract. The rider’s savings charge is recorded separately by recordRiderSavings.

function recordDistribution(
    HInvestment storage investment,
    bytes32 investmentId,
    HSharingBreakdown memory breakdown,
    HPayoutType payoutType
) internal;

Parameters

NameTypeDescription
investmentHInvestmentThe investment the payment belongs to (storage reference).
investmentIdbytes32The id of the investment.
breakdownHSharingBreakdownThe owner/investor split of the payment.
payoutTypeHPayoutTypeWhat produced the payout ({HPayoutType.RiderPaying} or {HPayoutType.RiderSavingsSlash}); stored on every entry it creates.

recordRiderSavings

Records the rider’s per-period savings charge for an investment.

The savings are held in the contract (not transferred): they are refunded when the rider completes and can be slashed if the rider defaults.

function recordRiderSavings(HInvestment storage investment, uint256 amount) internal;

Parameters

NameTypeDescription
investmentHInvestmentThe investment the payment belongs to (storage reference).
amountuint256The savings charge to add, in USDC base units (6 decimals).

recordRiderSavingsSlash

Records a slash of the rider’s savings for an investment.

Reduces the rider’s refundable savings, appends a global slash entry, links it to the investment, and updates the investment and platform slash totals. Writes state only: the caller splits and transfers the slashed amount separately, as those are part of the normal payout accounting.

function recordRiderSavingsSlash(HInvestment storage investment, bytes32 investmentId, uint256 amount) internal;

Parameters

NameTypeDescription
investmentHInvestmentThe investment the slash belongs to (storage reference).
investmentIdbytes32The id of the investment.
amountuint256The amount to slash, in USDC base units (6 decimals).

applyRiderSavingsSlash

Validates and applies a slash of the rider’s savings for an investment.

Reverts when the slash exceeds the rider’s saved amount. Reduces the savings, records the slash, then splits the amount to the owner and investor pools as {HPayoutType.RiderSavingsSlash} payouts and transfers them.

function applyRiderSavingsSlash(HInvestment storage investment, bytes32 investmentId, uint256 amount) internal;

Parameters

NameTypeDescription
investmentHInvestmentThe investment the slash belongs to (storage reference).
investmentIdbytes32The id of the investment.
amountuint256The amount to slash, in USDC base units (6 decimals); must be non-zero.

recordTierPayouts

Records an equal-share payout for every investor in a tier.

function recordTierPayouts(
    HInvestment storage investment,
    bytes32 investmentId,
    address[] storage tierInvestors,
    uint256 poolAmount,
    HPayoutType payoutType
) internal;

Parameters

NameTypeDescription
investmentHInvestmentThe investment the payment belongs to (storage reference).
investmentIdbytes32The id of the investment.
tierInvestorsaddress[]The tier’s investor list.
poolAmountuint256The total amount to split across the tier, in USDC base units (6 decimals).
payoutTypeHPayoutTypeWhat produced the payout, stored on every entry it creates.

transferDistribution

Transfers the owner and investor shares of a recorded distribution.

Call only after recordDistribution: this is the token-moving step.

function transferDistribution(HInvestment storage investment, HSharingBreakdown memory breakdown) internal;

Parameters

NameTypeDescription
investmentHInvestmentThe investment the payment belongs to (storage reference).
breakdownHSharingBreakdownThe owner/investor/savings split of the payment.

transferTierShares

Transfers an equal share of a tier’s pool to each of its investors.

function transferTierShares(address[] storage tierInvestors, uint256 poolAmount) internal;

Parameters

NameTypeDescription
tierInvestorsaddress[]The tier’s investor list.
poolAmountuint256The total amount to split across the tier, in USDC base units (6 decimals).

setAssetActive

Applies an asset’s active flag and keeps {HStats.activeAssets} in step.

The single write point for {HAsset.active} after creation, so the counter cannot drift.

function setAssetActive(HAsset storage asset, bool newActive) internal;

Parameters

NameTypeDescription
assetHAssetThe asset to update (storage reference).
newActiveboolThe requested active status.

setInvestmentActive

Applies an investment’s active flag and keeps the running totals in step.

The single write point for {HInvestment.active} after creation. Updates {HStats.activeInvestments} and, via the asset’s active-investment count, {HStats.assetsWithRunningInvestments}.

function setInvestmentActive(HInvestment storage investment, bool newActive) internal;

Parameters

NameTypeDescription
investmentHInvestmentThe investment to update (storage reference).
newActiveboolThe requested active status.

setInvestmentStatus

Applies an investment’s lifecycle status and keeps the running totals in step.

The single write point for {HInvestment.status} after creation, so the {HStats.investmentsInHire} and {HStats.investmentsPaidInFull} counters and the per-asset completion count cannot drift.

function setInvestmentStatus(HInvestment storage investment, HInvestmentStatus newStatus) internal;

Parameters

NameTypeDescription
investmentHInvestmentThe investment to update (storage reference).
newStatusHInvestmentStatusThe requested lifecycle status.

getOwnerUsdToken

Returns the USDC token used by the platform.

function getOwnerUsdToken() external view returns (IERC20);

Returns

NameTypeDescription
<none>IERC20The {IERC20} owner USDC token.

getOwner2

Returns the secondary owner, who may run admin actions but not manage owners.

function getOwner2() external view returns (address);

Returns

NameTypeDescription
<none>addressThe owner2 address, or address(0) when unset.

getVehicleNft

Returns the vehicle-title NFT collection used for asset titles.

Lets callers reach the collection (e.g. to read token metadata) through the platform address alone, without a separately configured NFT address.

function getVehicleNft() external view returns (HenriableVehicleNFT);

Returns

NameTypeDescription
<none>HenriableVehicleNFTThe {HenriableVehicleNFT} collection.

getAssetsCount

Returns the total number of registered assets.

function getAssetsCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {assetsList}.

getAssetAt

Returns the asset registered at an index.

function getAssetAt(uint256 index) external view returns (HAsset memory);

Parameters

NameTypeDescription
indexuint256The index into {assetsList}.

Returns

NameTypeDescription
<none>HAssetThe {HAsset} at the given index.

getAsset

Returns an asset’s configuration by id.

function getAsset(bytes32 assetId) external view returns (HAsset memory);

Parameters

NameTypeDescription
assetIdbytes32The id of the asset.

Returns

NameTypeDescription
<none>HAssetThe {HAsset} for the given id (zero-filled if it does not exist).

getAssetExists

Returns whether an asset id is registered.

function getAssetExists(bytes32 assetId) external view returns (bool);

Parameters

NameTypeDescription
assetIdbytes32The id of the asset.

Returns

NameTypeDescription
<none>boolTrue if the asset is registered.

getAssetStorage

Returns a storage reference to a registered asset.

Hides the index + 1 bookkeeping so callers can read or write fields directly, e.g. getAssetStorage(id).active = false;. Reverts if the asset is not registered.

function getAssetStorage(bytes32 assetId) internal view returns (HAsset storage);

Parameters

NameTypeDescription
assetIdbytes32The id of the asset.

Returns

NameTypeDescription
<none>HAssetA storage reference to the asset.

getAssetInvestmentIds

Returns the ids of the investments created for an asset.

function getAssetInvestmentIds(bytes32 assetId) external view returns (bytes32[] memory);

Parameters

NameTypeDescription
assetIdbytes32The id of the asset.

Returns

NameTypeDescription
<none>bytes32[]An array of investment ids.

getAssetInvestmentStorage

Returns a storage reference to an investment created for an asset.

Hides the index + 1 bookkeeping used by {assetInvestments}.

function getAssetInvestmentStorage(bytes32 assetId, uint256 position) internal view returns (HInvestment storage);

Parameters

NameTypeDescription
assetIdbytes32The id of the asset.
positionuint256The position in the asset’s investment list.

Returns

NameTypeDescription
<none>HInvestmentA storage reference to the investment.

getAssetInvestmentsCount

Returns the number of investments created for an asset.

function getAssetInvestmentsCount(bytes32 assetId) external view returns (uint256);

Parameters

NameTypeDescription
assetIdbytes32The id of the asset.

Returns

NameTypeDescription
<none>uint256The count of investments for the asset.

getInvestmentsCount

Returns the total number of investment opportunities.

function getInvestmentsCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {investmentsList}.

getInvestmentIndex

Returns the 1-based index in {investmentsList} for an investment id.

Positions are 1-based (0 means “not registered”); to index the array directly, callers subtract 1.

function getInvestmentIndex(bytes32 investmentId) external view returns (uint256);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>uint256The 1-based index, or 0 if the investment is not registered.

getInvestmentStorage

Returns a storage reference to a registered investment.

Hides the index + 1 bookkeeping so callers can read or write fields directly, e.g. getInvestmentStorage(id).active = false;. Reverts if the investment is not registered.

function getInvestmentStorage(bytes32 investmentId) internal view returns (HInvestment storage);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>HInvestmentA storage reference to the investment.

getInvestmentCore

Returns the core (half-investment) fields of an investment.

function getInvestmentCore(uint256 index)
    external
    view
    returns (
        bytes32 investmentId,
        bool active,
        bytes32 assetId,
        uint256 investmentsDoneAt,
        uint256 riderInitialBuyinUsd,
        uint256 periodicRiderPayingInMinutes,
        uint256 minimumRiderPeriodicPayingsUsd,
        HInvestmentStatus status
    );

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
investmentIdbytes32Unique investment id.
activeboolWhether the opportunity is active.
assetIdbytes32Asset being invested in.
investmentsDoneAtuint256Time the funding target was reached.
riderInitialBuyinUsduint256Initial rider buy-in, in USDC base units (6 decimals).
periodicRiderPayingInMinutesuint256Length of a rider period, in minutes.
minimumRiderPeriodicPayingsUsduint256Minimum rider payment per period, in USDC base units (6 decimals).
statusHInvestmentStatusCurrent lifecycle status.

getInvestmentSchedule

Returns the scheduling fields of an investment.

function getInvestmentSchedule(uint256 index)
    external
    view
    returns (
        uint256 timeNextInvestmentStart,
        uint256 timeNextInvestmentEnd,
        uint256 timeNextRiderHireRequestStart,
        uint256 timeNextRiderHireRequestEnd
    );

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
timeNextInvestmentStartuint256Start of the next investment round.
timeNextInvestmentEnduint256End of the next investment round.
timeNextRiderHireRequestStartuint256Start of the next hire-request round.
timeNextRiderHireRequestEnduint256End of the next hire-request round.

getInvestmentRiderInfo

Returns the rider hire-purchase fields of an investment.

function getInvestmentRiderInfo(uint256 index)
    external
    view
    returns (
        address riderWhoHiredIt,
        uint256 riderNextPayinDate,
        uint256 nftTokenId,
        uint256 timeRiderCompletedPayments,
        uint256 timeNftTransferedToRider
    );

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
riderWhoHiredItaddressAddress of the rider who hired the asset.
riderNextPayinDateuint256Time the rider’s next payment is due.
nftTokenIduint256Token id of the NFT title for the asset.
timeRiderCompletedPaymentsuint256Time the rider completed all payments.
timeNftTransferedToRideruint256Time the NFT title was transferred to the rider.

getTitleInfo

Returns what a title token needs for its metadata: the asset name and rider.

Read by {HenriableVehicleNFT.tokenURI} so the on-chain card can show the vehicle and the rider. Returns zero values for an unknown token, or before a rider is hired, which the metadata treats as “omit”.

function getTitleInfo(uint256 tokenId) external view returns (bytes32 assetName, address rider);

Parameters

NameTypeDescription
tokenIduint256The title’s token id.

Returns

NameTypeDescription
assetNamebytes32The bytes32 name of the asset the title represents.
rideraddressThe rider who hired the asset, or address(0) if none yet.

getSeniorInvestorsList

Returns the list of senior investors for an investment.

function getSeniorInvestorsList(uint256 index) external view returns (address[] memory);

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
<none>address[]The senior investor addresses.

getJuniorInvestorsList

Returns the list of junior investors for an investment.

function getJuniorInvestorsList(uint256 index) external view returns (address[] memory);

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
<none>address[]The junior investor addresses.

getSeniorInvestorIndex

Returns a senior investor’s 1-based index within an investment’s senior list.

Positions are 1-based; 0 means the investor is not in the list. When an investor holds several seats, this is their most recently taken seat.

function getSeniorInvestorIndex(bytes32 investmentId, address investor) external view returns (uint256);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.
investoraddressThe investor address.

Returns

NameTypeDescription
<none>uint256The 1-based index, or 0 if not present.

getJuniorInvestorIndex

Returns a junior investor’s 1-based index within an investment’s junior list.

Positions are 1-based; 0 means the investor is not in the list. When an investor holds several seats, this is their most recently taken seat.

function getJuniorInvestorIndex(bytes32 investmentId, address investor) external view returns (uint256);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.
investoraddressThe investor address.

Returns

NameTypeDescription
<none>uint256The 1-based index, or 0 if not present.

getRidersHirePurchaseRequestsList

Returns the rider ids of the hire-purchase requests for an investment.

function getRidersHirePurchaseRequestsList(uint256 index) external view returns (bytes32[] memory);

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
<none>bytes32[]An array of rider ids.

getInvestmentHireRequestStorage

Returns a storage reference to a hire-purchase request of an investment.

Hides the index + 1 bookkeeping used by the investment’s request list.

function getInvestmentHireRequestStorage(bytes32 investmentId, uint256 position)
    internal
    view
    returns (HRiderHirePurchaseRequest storage);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.
positionuint256The position in the investment’s hire-request list.

Returns

NameTypeDescription
<none>HRiderHirePurchaseRequestA storage reference to the hire-purchase request.

getRiderHirePurchaseRequestIndex

Returns a rider’s 1-based position in an investment’s hire-request list.

Positions are 1-based; 0 means the rider has no request for the investment.

function getRiderHirePurchaseRequestIndex(bytes32 investmentId, address rider) external view returns (uint256);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.
rideraddressThe rider address.

Returns

NameTypeDescription
<none>uint256The 1-based position, or 0 if not present.

getInvestmentRiderPayinsHistory

Returns the rider payment history of an investment.

function getInvestmentRiderPayinsHistory(uint256 index) external view returns (HPayingHistory memory);

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
<none>HPayingHistoryThe {HPayingHistory} for the investment’s rider.

getInvestmentRiderSavings

Returns the rider savings record of an investment.

function getInvestmentRiderSavings(uint256 index) external view returns (HRiderSavingsHistory memory);

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
<none>HRiderSavingsHistoryThe {HRiderSavingsHistory} for the investment’s rider.

getInvestorPayoutHistory

Returns one investor’s payout history for an investment.

function getInvestorPayoutHistory(bytes32 investmentId, address investor)
    external
    view
    returns (HInvestorPayoutsHistory memory);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.
investoraddressThe investor address.

Returns

NameTypeDescription
<none>HInvestorPayoutsHistoryThe {HInvestorPayoutsHistory} for that investor.

getInvestmentPayoutHistory

Returns the aggregate payout history of an investment.

function getInvestmentPayoutHistory(uint256 index) external view returns (HInvestmentPayoutsHistory memory);

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
<none>HInvestmentPayoutsHistoryThe {HInvestmentPayoutsHistory} for the investment.

getInvestmentContractPayoutHistory

Returns the contract/owner payout history recorded on an investment.

function getInvestmentContractPayoutHistory(uint256 index) external view returns (HOwnerPayoutsHistory memory);

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
<none>HOwnerPayoutsHistoryThe {HOwnerPayoutsHistory} for the investment.

getInvestorsCount

Returns the total number of registered investors.

function getInvestorsCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {investorsList}.

getInvestorIndex

Returns an investor’s 1-based index in {investorsList}.

Positions are 1-based (0 means “not registered”); to index the array directly, callers subtract 1.

function getInvestorIndex(address investor) external view returns (uint256);

Parameters

NameTypeDescription
investoraddressThe investor address.

Returns

NameTypeDescription
<none>uint256The 1-based index, or 0 if the investor is not registered.

getInvestorStorage

Returns a storage reference to a registered investor.

Hides the index + 1 bookkeeping so callers can read or write fields directly. Reverts if the investor is not registered.

function getInvestorStorage(address investor) internal view returns (HInvestor storage);

Parameters

NameTypeDescription
investoraddressThe investor address.

Returns

NameTypeDescription
<none>HInvestorA storage reference to the investor.

getInvestorAt

Returns the investor registered at an index.

function getInvestorAt(uint256 index) external view returns (HInvestor memory);

Parameters

NameTypeDescription
indexuint256The index into {investorsList}.

Returns

NameTypeDescription
<none>HInvestorThe {HInvestor} at the given index.

getInvestor

Returns the investor profile for an address.

function getInvestor(address investor) external view returns (HInvestor memory);

Parameters

NameTypeDescription
investoraddressThe investor address.

Returns

NameTypeDescription
<none>HInvestorThe {HInvestor} for the address (zero-filled if not registered).

getRidersCount

Returns the total number of registered riders.

function getRidersCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {ridersList}.

getRiderIndex

Returns a rider’s 1-based index in {ridersList}.

Positions are 1-based (0 means “not registered”); to index the array directly, callers subtract 1.

function getRiderIndex(address rider) external view returns (uint256);

Parameters

NameTypeDescription
rideraddressThe rider address.

Returns

NameTypeDescription
<none>uint256The 1-based index, or 0 if the rider is not registered.

getRiderStorage

Returns a storage reference to a registered rider.

Hides the index + 1 bookkeeping so callers can read or write fields directly. Reverts if the rider is not registered.

function getRiderStorage(address rider) internal view returns (HRider storage);

Parameters

NameTypeDescription
rideraddressThe rider address.

Returns

NameTypeDescription
<none>HRiderA storage reference to the rider.

getRiderAt

Returns the rider registered at an index.

function getRiderAt(uint256 index) external view returns (HRider memory);

Parameters

NameTypeDescription
indexuint256The index into {ridersList}.

Returns

NameTypeDescription
<none>HRiderThe {HRider} at the given index.

getRider

Returns the rider profile for an address.

function getRider(address rider) external view returns (HRider memory);

Parameters

NameTypeDescription
rideraddressThe rider address.

Returns

NameTypeDescription
<none>HRiderThe {HRider} for the address (zero-filled if not registered).

getHireRequestsCount

Returns the total number of rider hire-purchase requests.

function getHireRequestsCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {ridersHirePurchasesRequestsList}.

getHireRequestAt

Returns a rider hire-purchase request at an index.

function getHireRequestAt(uint256 index) external view returns (HRiderHirePurchaseRequest memory);

Parameters

NameTypeDescription
indexuint256The index into {ridersHirePurchasesRequestsList}.

Returns

NameTypeDescription
<none>HRiderHirePurchaseRequestThe {HRiderHirePurchaseRequest} at the given index.

getRejectedRiderIndex

Returns whether/how a rider was rejected for an investment.

function getRejectedRiderIndex(bytes32 riderId, bytes32 investmentId) external view returns (uint256);

Parameters

NameTypeDescription
riderIdbytes32The id of the rider.
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>uint256A non-zero marker if the rider was rejected, otherwise zero.

getRiderPayinsCount

Returns the total number of rider payments.

function getRiderPayinsCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {riderPayinsList}.

getRiderPayinAt

Returns a rider payment at an index.

function getRiderPayinAt(uint256 index) external view returns (HRiderPayin memory);

Parameters

NameTypeDescription
indexuint256The index into {riderPayinsList}.

Returns

NameTypeDescription
<none>HRiderPayinThe {HRiderPayin} at the given index.

getRiderPayinIndexes

Returns the payment indices recorded on an investment.

function getRiderPayinIndexes(bytes32 investmentId) external view returns (uint256[] memory);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>uint256[]The 1-based indices into the global rider payins list.

getRiderAmountToPayNow

Returns how much the calling rider must pay for the next instalment of an investment, including the asset’s per-period savings charge.

Reads the investment’s rider payment history for the outstanding balance. Returns 0 when the caller is not the investment’s hired rider or the rider has already met the target total.

function getRiderAmountToPayNow(bytes32 investmentId) external view returns (uint256);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>uint256The amount the caller should pay now, in USDC base units (6 decimals).

getRiderSavingsSlashesCount

Returns the total number of rider savings slashes.

function getRiderSavingsSlashesCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {riderSavingsSlashesList}.

getRiderSavingsSlashAt

Returns a rider savings slash at an index.

function getRiderSavingsSlashAt(uint256 index) external view returns (HRiderSavingsSlash memory);

Parameters

NameTypeDescription
indexuint256The index into {riderSavingsSlashesList}.

Returns

NameTypeDescription
<none>HRiderSavingsSlashThe {HRiderSavingsSlash} at the given index.

getRiderSavingsSlashIndex

Returns the most recent savings slash index for an investment.

function getRiderSavingsSlashIndex(bytes32 investmentId) external view returns (uint256);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>uint256The 1-based index into {riderSavingsSlashesList}, or 0 if none.

getInvestmentRiderSavingsSlashes

Returns the savings slash indices recorded on an investment.

function getInvestmentRiderSavingsSlashes(bytes32 investmentId) external view returns (uint256[] memory);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>uint256[]The 1-based indices into the global rider savings slashes list.

getInvestmentRiderSavingsSlashesHistory

Returns the aggregate savings slash history of an investment.

function getInvestmentRiderSavingsSlashesHistory(uint256 index)
    external
    view
    returns (HRiderSavingsSlashesHistory memory);

Parameters

NameTypeDescription
indexuint256The index into {investmentsList}.

Returns

NameTypeDescription
<none>HRiderSavingsSlashesHistoryThe {HRiderSavingsSlashesHistory} for the investment.

getInvestorPayoutsCount

Returns the total number of investor payouts.

function getInvestorPayoutsCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {investorPayoutList}.

getInvestorPayoutAt

Returns an investor payout at an index.

function getInvestorPayoutAt(uint256 index) external view returns (HInvestorPayout memory);

Parameters

NameTypeDescription
indexuint256The index into {investorPayoutList}.

Returns

NameTypeDescription
<none>HInvestorPayoutThe {HInvestorPayout} at the given index.

getInvestorPayoutIndex

Returns an investor’s payout index for an investment.

function getInvestorPayoutIndex(address investor, bytes32 investmentId) external view returns (uint256);

Parameters

NameTypeDescription
investoraddressThe investor address.
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>uint256The index into {investorPayoutList}.

getOwnerPayoutsCount

Returns the total number of owner payouts.

function getOwnerPayoutsCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {ownerPayoutsList}.

getOwnerPayoutAt

Returns an owner payout at an index.

function getOwnerPayoutAt(uint256 index) external view returns (HOwnerPayout memory);

Parameters

NameTypeDescription
indexuint256The index into {ownerPayoutsList}.

Returns

NameTypeDescription
<none>HOwnerPayoutThe {HOwnerPayout} at the given index.

getOwnerPayoutIndex

Returns the most recent owner payout index for an investment.

function getOwnerPayoutIndex(bytes32 investmentId) external view returns (uint256);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>uint256The index into {ownerPayoutsList}.

getOwnerPayoutHistory

Returns the aggregate owner payout history for an investment.

function getOwnerPayoutHistory(bytes32 investmentId) external view returns (HOwnerPayoutsHistory memory);

Parameters

NameTypeDescription
investmentIdbytes32The id of the investment.

Returns

NameTypeDescription
<none>HOwnerPayoutsHistoryThe {HOwnerPayoutsHistory} for the investment.

getCompletedRiderInvestmentsCount

Returns the number of completed rider investments.

function getCompletedRiderInvestmentsCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {completedRiderInvestmentList}.

getCompletedRiderInvestmentAt

Returns a completed rider investment index.

function getCompletedRiderInvestmentAt(uint256 index) external view returns (uint256);

Parameters

NameTypeDescription
indexuint256The index into {completedRiderInvestmentList}.

Returns

NameTypeDescription
<none>uint256The 1-based index into {investmentsList} (subtract 1 to index the array).

getInvestmentsCompletedCount

Returns the number of investments marked complete.

function getInvestmentsCompletedCount() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The length of {investmentsCompleted}.

getInvestmentsCompletedAt

Returns a completed investment index.

function getInvestmentsCompletedAt(uint256 index) external view returns (uint256);

Parameters

NameTypeDescription
indexuint256The index into {investmentsCompleted}.

Returns

NameTypeDescription
<none>uint256The 1-based index into {investmentsList} (subtract 1 to index the array).

getInvestors

Returns every registered investor profile.

Returns the whole list in one call; the paginated getInvestorsCount / getInvestorAt pair remains available for callers that page.

function getInvestors() external view returns (HInvestor[] memory);

Returns

NameTypeDescription
<none>HInvestor[]All entries of {investorsList}.

getRiders

Returns every registered rider profile.

Returns the whole list in one call; the paginated getRidersCount / getRiderAt pair remains available for callers that page.

function getRiders() external view returns (HRider[] memory);

Returns

NameTypeDescription
<none>HRider[]All entries of {ridersList}.

getHireRequests

Returns every rider hire-purchase request ever made.

Returns the whole list in one call; the paginated getHireRequestsCount / getHireRequestAt pair remains available for callers that page.

function getHireRequests() external view returns (HRiderHirePurchaseRequest[] memory);

Returns

NameTypeDescription
<none>HRiderHirePurchaseRequest[]All entries of {ridersHirePurchasesRequestsList}.

getInvestorPayouts

Returns every investor payout ever made.

Returns the whole list in one call; the paginated getInvestorPayoutsCount / getInvestorPayoutAt pair remains available for callers that page.

function getInvestorPayouts() external view returns (HInvestorPayout[] memory);

Returns

NameTypeDescription
<none>HInvestorPayout[]All entries of {investorPayoutList}.

getRiderPayins

Returns every rider periodic payment ever made.

Returns the whole list in one call; the paginated getRiderPayinsCount / getRiderPayinAt pair remains available for callers that page.

function getRiderPayins() external view returns (HRiderPayin[] memory);

Returns

NameTypeDescription
<none>HRiderPayin[]All entries of {riderPayinsList}.

getOwnerPayouts

Returns every owner/contract payout ever made.

Returns the whole list in one call; the paginated getOwnerPayoutsCount / getOwnerPayoutAt pair remains available for callers that page.

function getOwnerPayouts() external view returns (HOwnerPayout[] memory);

Returns

NameTypeDescription
<none>HOwnerPayout[]All entries of {ownerPayoutsList}.

getRiderSavingsSlashes

Returns every rider savings slash ever applied.

Returns the whole list in one call; the paginated getRiderSavingsSlashesCount / getRiderSavingsSlashAt pair remains available for callers that page.

function getRiderSavingsSlashes() external view returns (HRiderSavingsSlash[] memory);

Returns

NameTypeDescription
<none>HRiderSavingsSlash[]All entries of {riderSavingsSlashesList}.

HMath

Git Source

Title: HMath

Author: Obiaraije Henry

Stateless numeric helpers shared across the Henriable contracts.

Functions

min

Returns the smaller of two values.

function min(uint256 a, uint256 b) internal pure returns (uint256);

Parameters

NameTypeDescription
auint256First value to compare.
buint256Second value to compare.

Returns

NameTypeDescription
<none>uint256The smaller of a and b.

max

Returns the larger of two values.

function max(uint256 a, uint256 b) internal pure returns (uint256);

Parameters

NameTypeDescription
auint256First value to compare.
buint256Second value to compare.

Returns

NameTypeDescription
<none>uint256The larger of a and b.

Contents

MockUSDC

Git Source

Inherits: ERC20

Title: MockUSDC

Author: Obiaraije Henry

Minimal 6-decimal ERC20 token standing in for USDC on a local network.

Used by the deploy script; the platform only touches it through {IERC20}. It exposes an unrestricted mint faucet so local balances can be funded; never use it outside a test network.

Functions

constructor

Deploys the mock token with the USDC name and symbol.

constructor() ERC20("USD Coin", "USDC");

mint

Mints amount tokens to to.

Deliberately unrestricted: this is a local test faucet with no access control, so any address can be funded. Do not deploy on a real network.

function mint(address to, uint256 amount) external;

Parameters

NameTypeDescription
toaddressThe address receiving the newly minted tokens.
amountuint256The amount to mint, in token units (6 decimals).

decimals

USDC uses 6 decimals rather than the ERC20 default of 18.

function decimals() public pure override returns (uint8);

Returns

NameTypeDescription
<none>uint8The number of decimals, 6.

Contents

HAsset

Git Source

Title: HAsset

Defines an asset (e.g. Keke, Car, Bike, Truck) that riders can acquire through hire purchase and that investors can fund.

The struct holds both the physical/economic settings of the asset and the aggregate economics of the investor pools funding it. The admin configures these values, and they are validated (via the asset/opportunity accuracy calculators) to guarantee that the amount riders pay over time covers the profits promised to senior and junior investors.

struct HAsset {
/// @notice Unique identifier of the asset.
/// @dev    Used as the primary key for assets and referenced by investments.
bytes32 id;

/// @notice Whether the asset is currently active and available for investment.
/// @dev    May only be toggled off while no investor has invested in any
///         investment that uses this asset.
bool active;

/// @notice Human-readable name of the asset (e.g. "Keke-0001-2026", "Bike-0001-2026").
bytes32 name;

/// @notice Purchase/cost price of the asset, in USDC base units (6 decimals).
/// @dev    The amount required to acquire one unit of the asset.
uint256 costPrice;

/// @notice Total amount the rider would need to pay over the life of the hire purchase, in USDC base units (6 decimals).
/// @dev    Must be large enough to cover the asset cost plus the profits
///         promised to both the senior and junior investor pools (except the fixed extra charge for rider slashable, refundable, savings).
uint256 sellPrice;

/// @notice Buying price contributed by one senior investor, in USDC base units (6 decimals).
/// @dev    The senior pool total is this times {totalNumberOfSeniorInvestorsNeeded}.
uint256 oneSeniorInvestorBuyingPrice;

/// @notice Profit one senior investor earns over time, in USDC base units (6 decimals).
/// @dev    Senior investors are paid first and carry the lowest risk/reward.
uint256 oneSeniorInvestorProfit;

/// @notice Buying price contributed by one junior investor, in USDC base units (6 decimals).
/// @dev    The junior pool total is this times {totalNumberOfJuniorInvestorsNeeded}.
uint256 oneJuniorInvestorBuyingPrice;

/// @notice Profit one junior investor earns over time, in USDC base units (6 decimals).
/// @dev    Junior investors are paid after senior investors and carry the
///         higher risk/reward.
uint256 oneJuniorInvestorProfit;

/// @notice At every paying, this amount is charged extra from the rider and saved for the rider,
///         in USDC base units (6 decimals).
///         Then on the day of ownership transfer, its refunded to the rider.
///         This would help with repairs and reward for their faithfulness through the process.
///         If they default, these savings could be slashed as penalty and to fund investor payouts.
uint256 riderSlashableRepayableExtraCharge;

/// @notice Profit owner will earn over time, in USDC base units (6 decimals).
uint256 ownerProfit;

/// @notice Reserved. Payouts are driven by each pool's buying price plus profit (see
///         {HAsset.oneSeniorInvestorBuyingPrice}/{HAsset.oneSeniorInvestorProfit} and their
///         junior equivalents), so this share no longer affects any payout.
uint256 sharingPercentSeniorInvestor;

/// @notice Reserved. Payouts are driven by each pool's buying price plus profit, so this
///         share no longer affects any payout.
uint256 sharingPercentJuniorInvestor;

/// @notice Reserved. The owner receives the remainder after both pools are paid up to
///         their buying price plus profit, so this share no longer affects any payout.
uint256 sharingPercentOwner;

/// @notice Reserved. Payouts are driven by each pool's buying price plus profit, so this
///         share no longer affects any payout.
uint256 sharingPercentJuniorInvestorAfterSeniorPaid;

/// @notice Reserved. The owner receives the remainder after both pools are paid up to
///         their buying price plus profit, so this share no longer affects any payout.
uint256 sharingPercentOwnerAfterSeniorPaid;

/// @notice Number of senior investor slots required to fund one unit of this asset.
/// @dev    Used to know when the senior pool is fully funded.
uint256 totalNumberOfSeniorInvestorsNeeded;

/// @notice Number of junior investor slots required to fund one unit of this asset.
/// @dev    Used to know when the junior pool is fully funded.
uint256 totalNumberOfJuniorInvestorsNeeded;
}

HInvestment

Git Source

Title: HInvestment

The full on-chain record of an investment opportunity.

Solidity structs cannot inherit, so the admin-supplied creation settings and the contract-filled fields are combined into this single struct and demarcated by the editor-folds below.

struct HInvestment {
//<editor-fold desc="Fields: Admin Supplied">

/// @notice Unique identifier of this investment slot.
bytes32 investmentId;

/// @notice Identifier of the {HAsset} this opportunity invests in.
bytes32 assetId;

/// @notice Initial buy-in a rider must pay to start the hire purchase, in USDC base units (6 decimals).
uint256 riderInitialBuyinUsd;

/// @notice Length of each rider payment period, in minutes.
uint256 periodicRiderPayingInMinutes;

/// @notice Minimum amount a rider must pay per period, in USDC base units (6 decimals).
uint256 minimumRiderPeriodicPayingsUsd;

//</editor-fold desc="Fields: Admin Supplied">

//<editor-fold desc="Fields: Contract Filled">

/// @notice Whether this opportunity is active.
/// @dev    May only be changed while no investor has invested in it. Forced
///         active when the opportunity is created.
bool active;

/// @notice Ordered list of senior investor addresses that have invested.
address[] seniorInvestorsList;

/// @notice Maps a senior investor address to its index in {seniorInvestorsList}.
mapping(address => uint256) seniorInvestors;

/// @notice Ordered list of junior investor addresses that have invested.
address[] juniorInvestorsList;

/// @notice Maps a junior investor address to its index in {juniorInvestorsList}.
mapping(address => uint256) juniorInvestors;

/// @notice Timestamp at which the investment finished raising its target.
uint256 investmentsDoneAt;

/// @notice Current lifecycle state of the investment.
HInvestmentStatus status;

//</editor-fold desc="Fields: Contract Filled">

//<editor-fold desc="Fields: Scheduling">

/// @notice Timestamp at which the next investment round is allowed to start.
uint256 timeNextInvestmentStart;

/// @notice Timestamp at which the next investment round ends.
uint256 timeNextInvestmentEnd;

/// @notice Timestamp at which the next rider hire-request round starts.
uint256 timeNextRiderHireRequestStart;

/// @notice Timestamp at which the next rider hire-request round ends.
uint256 timeNextRiderHireRequestEnd;

//</editor-fold desc="Fields: Scheduling">

//<editor-fold desc="Fields: Rider Hire Purchase">

/// @notice Indices (into the riders' request list) of hire requests for this investment.
uint256[] ridersHirePurchaseRequestsList;

/// @notice Maps a rider address to its index in {ridersHirePurchaseRequestsList}.
mapping(address => uint256) ridersHirePurchaseRequests;

/// @notice Address of the rider who has hired (or will hire) this asset.
address riderWhoHiredIt;

/// @notice Timestamp at which the rider's next periodic payment is due.
uint256 riderNextPayinDate;

/// @notice Aggregate record of the rider's payments toward this investment.
HPayingHistory riderPayinsHistory;

/// @notice Indices (into the global rider payins list) of this investment's payments,
///         stored as `index + 1`.
uint256[] riderPayinIndexes;

//</editor-fold desc="Fields: Rider Hire Purchase">

//<editor-fold desc="Fields: Rider Savings">

/// @notice Record of savings accumulated from the rider's payments (slashable).
HRiderSavingsHistory riderSavings;

/// @notice Indices (into the global rider savings slashes list) of the slashes applied to
///         this investment's rider savings, stored as `index + 1`.
uint256[] riderSavingsSlashes;

/// @notice Aggregate record of the savings slashed from this investment's rider.
HRiderSavingsSlashesHistory riderSavingsSlashesHistory;

//</editor-fold desc="Fields: Rider Savings">

//<editor-fold desc="Fields: Payouts">

/// @notice Per-investor payout history for this investment.
mapping(address => HInvestorPayoutsHistory) investorPayoutHistory;

/// @notice Aggregate payout history for this investment across all investors.
HInvestmentPayoutsHistory investmentPayoutHistory;

/// @notice Payout history for the contract/owner on this investment.
HOwnerPayoutsHistory contractPayoutHistory;

//</editor-fold desc="Fields: Payouts">

//<editor-fold desc="Fields: Completion">

/// @notice Token id of the NFT title minted for the asset in this investment.
uint256 nftTokenId;

/// @notice Timestamp at which the rider completed all payments.
uint256 timeRiderCompletedPayments;

/// @notice Timestamp at which the NFT title was transferred to the rider.
uint256 timeNftTransferedToRider;
//</editor-fold desc="Fields: Completion">
}

HInvestor

Git Source

Title: HInvestor

Profile of an investor participating in the platform.

struct HInvestor {
/// @notice Wallet address of the investor.
address investorAddress;

/// @notice Investor's name.
bytes32 name;

/// @notice Investor's country.
bytes32 country;

/// @notice Investor's age in years; must be at least 18.
uint8 age;
}

HRider

Git Source

Title: HRider

Profile of a rider who acquires an asset through hire purchase.

struct HRider {
/// @notice Wallet address of the rider.
address riderAddress;

/// @notice Rider's name.
bytes32 name;

/// @notice Rider's country.
bytes32 country;

/// @notice Rider's age in years; must be at least 18.
uint8 age;
}

HRiderHirePurchaseRequest

Git Source

Title: HRiderHirePurchaseRequest

A rider’s request to hire the asset backing a given investment.

struct HRiderHirePurchaseRequest {
/// @notice Identifier of the rider making the request.
/// @dev    The rider's address encoded as bytes32.
bytes32 riderId;

/// @notice Identifier of the investment the rider is requesting.
bytes32 investmentId;

/// @notice Timestamp at which the request was made.
uint256 timeRequested;
}

HRiderPayin

Git Source

Title: HRiderPayin

A single periodic payment made by a rider under a hire purchase.

struct HRiderPayin {
/// @notice Identifier of the paying rider.
bytes32 riderId;

/// @notice Identifier of the investment the payment is for.
bytes32 investmentId;

/// @notice Amount paid in this instalment, in USDC base units (6 decimals).
uint256 amountPaid;

/// @notice Timestamp at which the payment was made.
uint256 timePaid;

/// @notice Address the payment was transferred into.
address addressPaidInto;
}

HSharingBreakdown

Git Source

Title: HSharingBreakdown

Split of one rider periodic payment across the owner and the investor pools.

Returned by the sharing calculator; the caller credits each party. The rider’s refundable savings charge is collected separately and is not part of this split.

struct HSharingBreakdown {
/// @notice Amount credited to the contract owner, in USDC base units (6 decimals).
uint256 ownerAmount;

/// @notice Total amount credited to the senior investor pool, in USDC base units (6 decimals).
uint256 seniorInvestorsAmount;

/// @notice Total amount credited to the junior investor pool, in USDC base units (6 decimals).
uint256 juniorInvestorsAmount;
}

HPayingHistory

Git Source

Title: HPayingHistory

Aggregate record of a rider’s payments toward one investment.

The total the rider owes is {HAsset.sellPrice}; this struct only tracks what they have paid so far against it.

struct HPayingHistory {
/// @notice Address of the rider.
address riderAddress;

/// @notice Total amount the rider has paid so far, in USDC base units (6 decimals).
uint256 totalAmountPaid;

/// @notice Total number of payments the rider has made.
uint256 totalNumberOfTimesPaid;
}

HOwnerPayout

Git Source

Title: HOwnerPayout

A single payout made to the contract/owner.

struct HOwnerPayout {
/// @notice Identifier of the investment the payout came from.
bytes32 investmentId;

/// @notice Amount paid out, in USDC base units (6 decimals).
uint256 amountPaid;

/// @notice Timestamp at which the payout was made.
uint256 timePaid;

/// @notice Address the payout was transferred into.
address addressPaidInto;

/// @notice What produced the payout ({HPayoutType.RiderPaying} or
///         {HPayoutType.RiderSavingsSlash}).
HPayoutType payoutType;
}

HOwnerPayoutsHistory

Git Source

Title: HOwnerPayoutsHistory

Aggregate record of payouts made to the contract/owner.

struct HOwnerPayoutsHistory {
/// @notice Total amount paid out so far, in USDC base units (6 decimals).
uint256 totalAmountPaid;

/// @notice Total number of payouts made.
uint256 totalNumberOfTimesPaid;
}

HInvestorPayout

Git Source

Title: HInvestorPayout

A single payout made to an investor.

struct HInvestorPayout {
/// @notice Identifier of the investment the payout came from.
bytes32 investmentId;

/// @notice Amount paid out, in USDC base units (6 decimals).
uint256 amountPaid;

/// @notice Timestamp at which the payout was made.
uint256 timePaid;

/// @notice Address the payout was transferred into.
address addressPaidInto;

/// @notice What produced the payout ({HPayoutType.RiderPaying} or
///         {HPayoutType.RiderSavingsSlash}).
HPayoutType payoutType;
}

HInvestorPayoutsHistory

Git Source

Title: HInvestorPayoutsHistory

Aggregate record of a single investor’s payouts for one investment.

struct HInvestorPayoutsHistory {
/// @notice Address of the investor.
address investorAddress;

/// @notice Identifier of the investment.
bytes32 investmentId;

/// @notice Total amount paid to the investor so far, in USDC base units (6 decimals).
uint256 totalAmountPaid;

/// @notice Total number of payouts made to the investor.
uint256 totalNumberOfTimesPaid;

/// @notice Total amount the investor is due to receive over time, in USDC base units (6 decimals).
uint256 targetTotalPay;
}

HInvestmentPayoutsHistory

Git Source

Title: HInvestmentPayoutsHistory

Aggregate record of all payouts made on one investment.

struct HInvestmentPayoutsHistory {
/// @notice Identifier of the investment.
bytes32 investmentId;

/// @notice Total amount paid out so far on the investment, in USDC base units (6 decimals).
uint256 totalAmountPaid;

/// @notice Total number of payouts made on the investment.
uint256 totalNumberOfTimesPaid;

/// @notice Total amount due to be paid out on the investment, in USDC base units (6 decimals).
uint256 targetTotalPay;
}

HRiderSavingsHistory

Git Source

Title: HRiderSavingsHistory

Record of a rider’s savings for one investment.

A portion of every periodic payment is set aside as savings. The savings can be slashed if the rider defaults or pays late, and are refunded in full if the rider stays committed through the end of the hire purchase, giving them capital for repairs and cash once they own the vehicle.

struct HRiderSavingsHistory {
/// @notice Address of the rider.
address riderAddress;

/// @notice Total amount saved so far, in USDC base units (6 decimals).
uint256 totalAmountSaved;

/// @notice Individual savings entries.
HRiderSavings[] savings;
}

HRiderSavings

Git Source

Title: HRiderSavings

A single savings entry made by a rider.

struct HRiderSavings {
/// @notice Address of the rider.
address riderAddress;

/// @notice Amount saved in this entry, in USDC base units (6 decimals).
uint256 amountSaved;

/// @notice Timestamp at which the amount was saved.
uint256 timeSaved;
}

HRiderSavingsSlash

Git Source

Title: HRiderSavingsSlash

A single slash applied to a rider’s savings for one investment.

The slash is split to the owner and investor pools by {HPayoutType.RiderSavingsSlash} payouts, and reduces the savings that would otherwise be refunded at completion.

struct HRiderSavingsSlash {
/// @notice Identifier of the investment the rider was saving on.
bytes32 investmentId;

/// @notice Address of the rider whose savings were slashed.
address riderAddress;

/// @notice Amount slashed in this entry, in USDC base units (6 decimals).
uint256 amountSlashed;

/// @notice Timestamp at which the slash was applied.
uint256 timeSlashed;
}

HRiderSavingsSlashesHistory

Git Source

Title: HRiderSavingsSlashesHistory

Aggregate record of the savings slashed from a rider on one investment.

struct HRiderSavingsSlashesHistory {
/// @notice Address of the rider.
address riderAddress;

/// @notice Total amount slashed so far, in USDC base units (6 decimals).
uint256 totalAmountSlashed;

/// @notice Total number of slashes applied.
uint256 totalNumberOfTimesSlashed;
}

HStats

Git Source

Title: HStats

Aggregate platform statistics for the frontend, returned by {Henriable.getStats}.

Every field is maintained in O(1) as the contract is used, so the getter never loops. Investor figures count filled slots, not distinct addresses (an address may hold a senior and a junior slot, and slots in several investments). The totals derived from list lengths are filled in by the getter at read time.

struct HStats {
//<editor-fold desc="Fields: Assets">

/// @notice Total number of registered assets.
uint256 totalAssets;

/// @notice Number of assets currently active.
uint256 activeAssets;

/// @notice Number of assets with at least one active investment.
uint256 assetsWithRunningInvestments;

/// @notice Number of assets with at least one completed investment.
uint256 assetsWithCompletedInvestments;

//</editor-fold desc="Fields: Assets">

//<editor-fold desc="Fields: Investments">

/// @notice Total number of investment opportunities ever created.
uint256 totalInvestments;

/// @notice Number of investment opportunities currently active.
uint256 activeInvestments;

/// @notice Number of investments currently in {HInvestmentStatus.Hired}.
uint256 investmentsInHire;

/// @notice Number of investments in {HInvestmentStatus.Complete}.
uint256 investmentsPaidInFull;

//</editor-fold desc="Fields: Investments">

//<editor-fold desc="Fields: People">

/// @notice Total number of registered investors.
uint256 totalInvestors;

/// @notice Total senior investor slots filled across all investments.
uint256 totalSeniorPositions;

/// @notice Total junior investor slots filled across all investments.
uint256 totalJuniorPositions;

/// @notice Total number of registered riders.
uint256 totalRiders;

/// @notice Total number of rider hire-purchase requests.
uint256 totalHireRequests;

//</editor-fold desc="Fields: People">

//<editor-fold desc="Fields: Amounts">

/// @notice Gross amount senior investors have invested so far, in USDC base units (6 decimals).
uint256 totalSeniorInvestedUsd;

/// @notice Gross amount junior investors have invested so far, in USDC base units (6 decimals).
uint256 totalJuniorInvestedUsd;

/// @notice Gross amount riders have paid in, including refundable savings charges,
///         in USDC base units (6 decimals).
uint256 totalPayinsUsd;

/// @notice Cumulative amount paid out to senior investors, in USDC base units (6 decimals).
uint256 totalSeniorPayoutsUsd;

/// @notice Cumulative amount paid out to junior investors, in USDC base units (6 decimals).
uint256 totalJuniorPayoutsUsd;

/// @notice Cumulative amount paid out to the owner, in USDC base units (6 decimals).
uint256 totalOwnerPayoutsUsd;

/// @notice Amount currently held as refundable rider savings, in USDC base units (6 decimals).
uint256 riderSavingsHeldUsd;

/// @notice Cumulative amount of rider savings slashed so far, in USDC base units (6 decimals).
uint256 totalRiderSavingsSlashedUsd;
//</editor-fold desc="Fields: Amounts">
}

HInvestmentType

Git Source

Title: HInvestmentType

Identifies which investor tier a position belongs to.

enum HInvestmentType {
/// @notice Senior investor: paid first, with lower risk and lower reward.
Senior,
/// @notice Junior investor: paid after senior investors, with higher risk and higher reward.
Junior
}

HInvestmentStatus

Git Source

Title: HInvestmentStatus

Lifecycle states of an investment opportunity.

enum HInvestmentStatus {
/// @notice Investment added but investing has not begun.
Pending,
/// @notice Investors can currently invest.
AcceptingInvestments,
/// @notice All required investors are in place.
InvestmentDone,
/// @notice Riders can now request to hire the asset.
AcceptingRiders,
/// @notice A rider has been hired and is making periodic payments.
Hired,
/// @notice Rider finished paying and the NFT title has been transferred.
Complete
}

HPayoutType

Git Source

Title: HPayoutType

Identifies what produced a payout entry, so payouts can be told apart by source.

enum HPayoutType {
/// @notice The payout was funded by a rider's periodic payment.
RiderPaying,
/// @notice The payout was funded by a slash of the rider's savings.
RiderSavingsSlash
}

HOwner2IsOwner1

Git Source

Thrown when owner2 is set to the same address as owner1.

error HOwner2IsOwner1(address owner1);

Parameters

NameTypeDescription
owner1addressThe current owner1 address.

HAssetInvalidId

Git Source

Thrown when an asset is registered without a non-zero id.

error HAssetInvalidId();

HAssetAlreadyExists

Git Source

Thrown when registering an asset whose id is already in use.

error HAssetAlreadyExists(bytes32 id);

Parameters

NameTypeDescription
idbytes32The already-existing asset id.

HAssetDoesNotExist

Git Source

Thrown when an operation references an asset id that is not registered.

error HAssetDoesNotExist(bytes32 id);

Parameters

NameTypeDescription
idbytes32The unknown asset id.

HAssetHasNonPendingInvestments

Git Source

Thrown when an asset’s status is changed while one of its investments is no longer pending.

error HAssetHasNonPendingInvestments(bytes32 id);

Parameters

NameTypeDescription
idbytes32The asset id.

HAssetInvalidName

Git Source

Thrown when an asset is registered without a name.

error HAssetInvalidName();

HAssetInvalidCostPrice

Git Source

Thrown when an asset is registered with a zero cost price.

error HAssetInvalidCostPrice();

HAssetInvalidSellPrice

Git Source

Thrown when an asset is registered with a zero sell price.

error HAssetInvalidSellPrice();

HAssetNotActive

Git Source

Thrown when an operation references an asset that is not active.

error HAssetNotActive(bytes32 id);

Parameters

NameTypeDescription
idbytes32The inactive asset id.

HAssetInvalidSettings

Git Source

Thrown when an asset’s pricing settings cannot cover the payouts it promises, i.e. the rider’s sell price does not cover the investor principal and profits.

error HAssetInvalidSettings();

HInvestmentInvalidId

Git Source

Thrown when an investment is registered without a non-zero id.

error HInvestmentInvalidId();

HInvestmentAlreadyExists

Git Source

Thrown when registering an investment whose id is already in use.

error HInvestmentAlreadyExists(bytes32 id);

Parameters

NameTypeDescription
idbytes32The already-existing investment id.

HInvestmentDoesNotExist

Git Source

Thrown when an operation references an investment id that is not registered.

error HInvestmentDoesNotExist(bytes32 id);

Parameters

NameTypeDescription
idbytes32The unknown investment id.

HInvestmentInvalidRiderInitialBuyin

Git Source

Thrown when an investment is registered with a zero rider initial buy-in.

error HInvestmentInvalidRiderInitialBuyin();

HInvestmentRiderInitialBuyinTooHigh

Git Source

Thrown when an investment’s initial rider buy-in is not below the asset’s sell price.

The buy-in is credited toward the rider’s hire-purchase total, so it must be less than the sell price or the rider would have nothing left to pay over the term.

error HInvestmentRiderInitialBuyinTooHigh(uint256 buyin, uint256 sellPrice);

Parameters

NameTypeDescription
buyinuint256The supplied initial buy-in, in USDC base units (6 decimals).
sellPriceuint256The asset’s sell price, in USDC base units (6 decimals).

HInvestmentInvalidPeriodicRiderPayingInMinutes

Git Source

Thrown when an investment is registered with a zero periodic rider paying period.

error HInvestmentInvalidPeriodicRiderPayingInMinutes();

HInvestmentInvalidMinimumRiderPeriodicPayings

Git Source

Thrown when an investment is registered with a zero minimum rider periodic payment.

error HInvestmentInvalidMinimumRiderPeriodicPayings();

HInvestmentHasInvestors

Git Source

Thrown when an investment’s active flag is changed after an investor has joined.

error HInvestmentHasInvestors(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HInvestmentInvalidSettings

Git Source

Thrown when an investment’s level-1 settings are not accurate enough to cover the profits promised to senior and junior investors.

error HInvestmentInvalidSettings();

HInvestmentNotPending

Git Source

Thrown when next-round times are changed on an investment whose current status does not allow it (no longer Pending, or, when extending a round, no longer AcceptingInvestments).

error HInvestmentNotPending(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HInvestmentNotDone

Git Source

Thrown when an operation requires an investment that has finished raising funds ({HInvestmentStatus.InvestmentDone}) but it has not.

error HInvestmentNotDone(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HInvestmentInvalidStartTime

Git Source

Thrown when an investment round’s start time is not in the future.

error HInvestmentInvalidStartTime(uint256 startTime);

Parameters

NameTypeDescription
startTimeuint256The invalid start timestamp.

HInvestmentInvalidEndTime

Git Source

Thrown when an investment round’s end time is not after its start time.

error HInvestmentInvalidEndTime(uint256 startTime, uint256 endTime);

Parameters

NameTypeDescription
startTimeuint256The proposed start timestamp.
endTimeuint256The invalid end timestamp.

HInvestmentNotActive

Git Source

Thrown when investing in an investment that is not active.

error HInvestmentNotActive(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HInvestmentNotAccepting

Git Source

Thrown when investing in an investment that is not accepting investments.

error HInvestmentNotAccepting(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HInvestmentInvalidAmount

Git Source

Thrown when the approved USDC amount does not match the required slot amount.

error HInvestmentInvalidAmount(uint256 provided, uint256 required);

Parameters

NameTypeDescription
provideduint256The approved amount supplied.
requireduint256The required per-slot amount.

HInvestmentNoOpenSlot

Git Source

Thrown when investing in a tier whose investor slots are all filled.

error HInvestmentNoOpenSlot(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HInvestorDoesNotExist

Git Source

Thrown when an operation references an investor that is not registered.

error HInvestorDoesNotExist(address investor);

Parameters

NameTypeDescription
investoraddressThe unknown investor address.

HInvestorInvalidAge

Git Source

Thrown when an investor is younger than the required minimum age.

error HInvestorInvalidAge(uint8 age);

Parameters

NameTypeDescription
ageuint8The supplied age.

HInvestmentNotAcceptingRiders

Git Source

Thrown when a rider registers for an investment that is not accepting riders.

error HInvestmentNotAcceptingRiders(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HRiderInvalidName

Git Source

Thrown when a rider is registered without a name.

error HRiderInvalidName();

HRiderInvalidCountry

Git Source

Thrown when a rider is registered without a country.

error HRiderInvalidCountry();

HRiderInvalidAge

Git Source

Thrown when a rider is younger than the required minimum age.

error HRiderInvalidAge(uint8 age);

Parameters

NameTypeDescription
ageuint8The supplied age.

HRiderInsufficientInitialBuyin

Git Source

Thrown when a rider’s approved initial buy-in is below the required amount.

error HRiderInsufficientInitialBuyin(uint256 provided, uint256 required);

Parameters

NameTypeDescription
provideduint256The approved amount supplied.
requireduint256The required initial buy-in.

HRiderAlreadyRegistered

Git Source

Thrown when a rider tries to register twice for the same investment.

error HRiderAlreadyRegistered(bytes32 investmentId, address rider);

Parameters

NameTypeDescription
investmentIdbytes32The investment id.
rideraddressThe rider address.

HInvestmentAlreadyHasHiredRider

Git Source

Thrown when a rider is accepted for an investment that already has a hired rider.

error HInvestmentAlreadyHasHiredRider(bytes32 investmentId);

Parameters

NameTypeDescription
investmentIdbytes32The investment id.

HRiderDidNotApplyForHire

Git Source

Thrown when accepting a rider who never applied to hire the investment.

error HRiderDidNotApplyForHire(bytes32 investmentId, address rider);

Parameters

NameTypeDescription
investmentIdbytes32The investment id.
rideraddressThe applicant address.

HRiderAlreadyRejectedForHire

Git Source

Thrown when accepting a rider who was already rejected for the investment.

error HRiderAlreadyRejectedForHire(bytes32 investmentId, address rider);

Parameters

NameTypeDescription
investmentIdbytes32The investment id.
rideraddressThe applicant address.

HRiderDoesNotExist

Git Source

Thrown when an operation references a rider that is not registered.

error HRiderDoesNotExist(address rider);

Parameters

NameTypeDescription
rideraddressThe unknown rider address.

HInvestmentNotHired

Git Source

Thrown when a rider pays on an investment whose hire purchase is not running.

error HInvestmentNotHired(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HRiderNotHired

Git Source

Thrown when an address that is not the hired rider tries to pay.

error HRiderNotHired(bytes32 investmentId, address rider);

Parameters

NameTypeDescription
investmentIdbytes32The investment id.
rideraddressThe caller address.

HRiderPaymentsCompleted

Git Source

Thrown when a rider pays after already completing the required total.

error HRiderPaymentsCompleted(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HRiderPaymentsNotCompleted

Git Source

Thrown when ownership is transferred before the rider has completed all payments.

error HRiderPaymentsNotCompleted(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HInvestmentAlreadyComplete

Git Source

Thrown when ownership is transferred for an investment that is already complete.

error HInvestmentAlreadyComplete(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HRiderPayinBelowMinimum

Git Source

Thrown when a rider’s approved payment is below the minimum periodic payment.

error HRiderPayinBelowMinimum(uint256 provided, uint256 required);

Parameters

NameTypeDescription
provideduint256The approved amount supplied.
requireduint256The required minimum periodic payment.

HRiderPayinLate

Git Source

Thrown when a rider pays after the due date ({HInvestment.riderNextPayinDate}).

error HRiderPayinLate(bytes32 id);

Parameters

NameTypeDescription
idbytes32The investment id.

HInvestmentInvalidRiderNextPayinDate

Git Source

Thrown when the hired rider’s next payment date is not extended to a later time.

error HInvestmentInvalidRiderNextPayinDate(uint256 newTime, uint256 currentTime);

Parameters

NameTypeDescription
newTimeuint256The proposed new next-payment timestamp.
currentTimeuint256The investment’s current next-payment timestamp.

HRiderSavingsSlashExceedsSaved

Git Source

Thrown when a savings slash exceeds the rider’s currently saved amount.

error HRiderSavingsSlashExceedsSaved(uint256 amountSlashed, uint256 totalSaved);

Parameters

NameTypeDescription
amountSlasheduint256The requested slash, in USDC base units (6 decimals).
totalSaveduint256The rider’s current saved amount, in USDC base units (6 decimals).

HVehicleNftUnauthorizedMinter

Git Source

Thrown when an address that is not the configured vehicle-title NFT minter attempts to mint a title.

error HVehicleNftUnauthorizedMinter(address caller);

Parameters

NameTypeDescription
calleraddressThe unauthorized caller.

Constants

Git Source

USDC_UNIT

uint256 constant USDC_UNIT = 10 ** 6

Counter

Git Source

State Variables

number

uint256 public number

Functions

setNumber

function setNumber(uint256 newNumber) public;

increment

function increment() public;

Henriable

Git Source

Inherits: HHelper

Title: The Henriable contract.

Author: Obiaraije Henry

All USD amounts are expressed as integers in USDC base units (6 decimals), i.e. micro-USDC, where 1 USDC == USDC_UNIT == 10 ** 6 base units (e.g. $14 == 14_000_000 and $0.14 == 140_000). The contract never converts or scales; it moves the raw base-unit value. Callers must therefore pass base units: the frontend uses viem’s parseUnits(x, 6), not a whole-dollar number.

Functions

constructor

Deploys Henriable, setting the contract owner, the USDC token and the NFT.

constructor(address initialOwner, IERC20 ownerUsdToken_, HenriableVehicleNFT vehicleNft_)
    HHelper(initialOwner, ownerUsdToken_, vehicleNft_);

Parameters

NameTypeDescription
initialOwneraddressThe address that will own the contract.
ownerUsdToken_IERC20The USDC token used for investments, payouts and payments.
vehicleNft_HenriableVehicleNFTThe vehicle-title NFT collection, whose minter must be set to this contract so it can mint and later transfer asset titles.

actionAdminSetOwner2

Sets the secondary owner, who may run every admin action but not manage owners.

Only owner1 ({onlyOwner}) may call this, so owner2 cannot set or clear owners. Pass address(0) to clear owner2; rejected when _owner2 is owner1 itself. States updated: {owner2}.

function actionAdminSetOwner2(address _owner2) external onlyOwner;

Parameters

NameTypeDescription
_owner2addressThe new secondary owner address, or address(0) to clear it.

actionAdminAddAsset

Adds a new asset that can be invested in.

Only an owner (admin: owner1 or owner2) may call this. Validates the asset, forces it active, appends it to {assetsList} and records its position in the {assets} index mapping. States updated: {assetsList}, {assets}.

function actionAdminAddAsset(HAsset calldata _asset) external onlyOwners;

Parameters

NameTypeDescription
_assetHAssetThe asset configuration to register. Its id must be unique and non-zero, name non-empty, and costPrice/sellPrice non-zero. The active field is ignored and set to true.

actionAdminChangeAssetStatus

Enables or disables an asset.

Only an owner (admin: owner1 or owner2) may call this. The asset status may only change while every investment that depends on the asset is still {HInvestmentStatus.Pending}; once any such investment has moved past Pending, the asset is locked. Disabling an asset also disables its still-pending investments. Enabling an asset does NOT re-enable those investments. States updated: {assetsList}, and {investmentsList} when disabling.

function actionAdminChangeAssetStatus(bytes32 _assetId, bool _status) external onlyOwners;

Parameters

NameTypeDescription
_assetIdbytes32The id of the asset to update.
_statusboolThe new active status.

actionAdminAddInvestment

Adds a new investment opportunity against an existing active asset.

Only an owner (admin: owner1 or owner2) may call this. {HInvestment} contains mappings and therefore cannot be passed as a calldata parameter, so the admin-supplied settings are taken as individual arguments. Validates them, checks the level-1 settings against the asset, forces the opportunity active and Pending, appends it to {investmentsList}, records its position in the {investments} index mapping and links it to its asset via {assetInvestments}. States updated: {investmentsList}, {investments}, {assetInvestments}.

function actionAdminAddInvestment(
    bytes32 _investmentId,
    bytes32 _assetId,
    uint256 _riderInitialBuyinUsd,
    uint256 _periodicRiderPayingInMinutes,
    uint256 _minimumRiderPeriodicPayingsUsd
) external onlyOwners;

Parameters

NameTypeDescription
_investmentIdbytes32Unique, non-zero id of the new investment slot.
_assetIdbytes32Id of the asset to invest in; must exist and be active.
_riderInitialBuyinUsduint256Initial rider buy-in, in USDC base units (6 decimals); must be non-zero. Callers pass base units (frontend: parseUnits(x, 6)).
_periodicRiderPayingInMinutesuint256Length of a rider period, in minutes; must be non-zero.
_minimumRiderPeriodicPayingsUsduint256Minimum rider payment per period, in USDC base units (6 decimals); must be non-zero. Callers pass base units.

actionAdminChangeInvestmentStatus

Enables or disables an investment opportunity.

Only an owner (admin: owner1 or owner2) may call this. The investment’s active flag may only change while no investor has joined it; once the senior or junior pool has any investor, the opportunity is locked. States updated: {investmentsList}.

function actionAdminChangeInvestmentStatus(bytes32 _investmentId, bool _status) external onlyOwners;

Parameters

NameTypeDescription
_investmentIdbytes32The id of the investment to update.
_statusboolThe new active status.

actionAdminSetNextInvestmentTimes

Sets the start and end of the next investment round for an investment.

Only an owner (admin: owner1 or owner2) may call this. The opportunity must still be Pending or in investment(to extend it) and its asset active, and the round must be in the future with _endTime after _startTime. States updated: {investmentsList}.

function actionAdminSetNextInvestmentTimes(bytes32 _investmentId, uint256 _startTime, uint256 _endTime)
    external
    onlyOwners;

Parameters

NameTypeDescription
_investmentIdbytes32The id of the investment to schedule.
_startTimeuint256Timestamp at which the next investment round starts; must be in the future.
_endTimeuint256Timestamp at which the next investment round ends; must be after _startTime.

actionAdminClearNextInvestmentsTimes

Clears the start and end of the next investment round for an investment.

Only an owner (admin: owner1 or owner2) may call this. The opportunity must still be Pending; both times are reset to 0. States updated: {investmentsList}.

function actionAdminClearNextInvestmentsTimes(bytes32 _investmentId) external onlyOwners;

Parameters

NameTypeDescription
_investmentIdbytes32The id of the investment whose round is cleared.

actionInvest

Lets an address invest into a tier of an active investment opportunity.

The investor is {msg.sender}; _investor only supplies name, country and age. The asset defines the per-investor buying price and the number of slots, so the required amount is the tier’s one-investor buying price. The contract pulls exactly that amount from the investor’s USDC. An address may call this repeatedly and in either tier, taking one seat per call, so the same address can appear more than once in a tier’s list (each entry is a separate seat and is paid its own equal share). States updated: {investorsList}, {investors}, and the chosen tier’s list and mapping inside {investmentsList}.

function actionInvest(
    HInvestor calldata _investor,
    HInvestmentType _investmentType,
    bytes32 _investmentId,
    uint256 _approvedUSDAmount
) external;

Parameters

NameTypeDescription
_investorHInvestorThe investor profile (name, country, age) to create or update.
_investmentTypeHInvestmentTypeThe tier to invest in (Senior or Junior).
_investmentIdbytes32The id of the investment to invest in.
_approvedUSDAmountuint256The USDC amount approved for this slot, in USDC base units (6 decimals); must equal the slot amount. Callers pass base units (frontend: parseUnits(x, 6)).

actionSetNextHireRoundTime

Sets the window during which riders may request to hire an investment’s asset.

Only an owner (admin: owner1 or owner2) may call this. The opportunity must be funded ({HInvestmentStatus.InvestmentDone}) or, once a hire round is open, still accepting riders ({HInvestmentStatus.AcceptingRiders}) so an in-progress round can be extended. The window must be in the future with _endTime after _startTime. States updated: {investmentsList}.

function actionSetNextHireRoundTime(bytes32 _investmentId, uint256 _startTime, uint256 _endTime)
    external
    onlyOwners;

Parameters

NameTypeDescription
_investmentIdbytes32The id of the investment to schedule.
_startTimeuint256Timestamp at which the next hire-request round starts; must be in the future.
_endTimeuint256Timestamp at which the next hire-request round ends; must be after _startTime.

actionClearNextHireTime

Clears the rider hire-request window for a funded investment.

Only an owner (admin: owner1 or owner2) may call this. The opportunity must be funded ({HInvestmentStatus.InvestmentDone}); both hire-request times are reset to 0. States updated: {investmentsList}.

function actionClearNextHireTime(bytes32 _investmentId) external onlyOwners;

Parameters

NameTypeDescription
_investmentIdbytes32The id of the investment whose hire window is cleared.

actionRiderRegisterAsARider

Registers the caller as a rider for a funded investment’s hire round.

The rider is {msg.sender}; the initial buy-in is pulled into the contract so it can be refunded if the rider is later rejected. Registration is only allowed while the investment is {HInvestmentStatus.AcceptingRiders}. States updated: {ridersList}, {riders}, {ridersHirePurchasesRequestsList}, {ridersHireRequestPurchasesByInvestment}, and the investment’s {HInvestment.ridersHirePurchaseRequestsList}/{ridersHirePurchaseRequests}.

function actionRiderRegisterAsARider(
    bytes32 _name,
    bytes32 _country,
    uint8 _age,
    uint256 _approvedInitialBuyin,
    bytes32 _investmentId
) external;

Parameters

NameTypeDescription
_namebytes32The rider’s name; must be non-zero.
_countrybytes32The rider’s country; must be non-zero.
_ageuint8The rider’s age; must be at least 18.
_approvedInitialBuyinuint256The USDC the rider approved, in USDC base units (6 decimals); must cover the required buy-in. Callers pass base units.
_investmentIdbytes32The id of the investment to register for.

actionAdminAcceptRider

Accepts one applicant as the rider for a funded investment and rejects the rest.

Only an owner (admin: owner1 or owner2) may call this. The investment must still be {HInvestmentStatus.AcceptingRiders} and have no hired rider yet; the chosen rider must exist, must have applied for the investment (recorded by actionRiderRegisterAsARider) and must not have been rejected before. Every other applicant is marked rejected via {rejectedRiders} and has their initial buy-in refunded. The accepted rider’s buy-in is credited toward their hire-purchase total and shared with the investor pools and the owner immediately. The investment then moves to {HInvestmentStatus.Hired} and the rider’s next payment date is set one period from now. States updated: {HInvestment.riderWhoHiredIt}, {HInvestment.riderNextPayinDate}, {HInvestment.status}, {rejectedRiders}, {HInvestment.riderPayinsHistory}, {ownerPayoutsList}, {investorPayoutList} and the payout histories.

function actionAdminAcceptRider(bytes32 _investmentId, address _riderAddress) external onlyOwners;

Parameters

NameTypeDescription
_investmentIdbytes32The id of the investment whose rider is being chosen.
_riderAddressaddressThe applicant to accept as the hired rider.

actionAdminExtendRiderPayingForInvestment

Pushes the hired rider’s next payment due date to a later time and optionally slashes an amount from the rider’s accumulated savings.

Only an owner (admin: owner1 or owner2) may call this. The opportunity must be {HInvestmentStatus.Hired}, i.e. a rider is currently paying, and the new date must be later than the current next-payment date. Extending the date gives the rider more time before actionRiderPayin treats their next payment as late. When _slashAmount is non-zero it must not exceed the rider’s saved amount; the savings are reduced by it and the amount is split to the owner and investor pools exactly like a rider payment, recorded as {HPayoutType.RiderSavingsSlash} payouts and tracked via {HRiderSavingsSlash}. States updated: {HInvestment.riderNextPayinDate}, {HInvestment.riderSavings}, {riderSavingsSlashesList}, and the investment’s slash and payout histories.

function actionAdminExtendRiderPayingForInvestment(
    bytes32 _investmentId,
    uint256 _newNextPayinDate,
    uint256 _slashAmount
) external onlyOwners;

Parameters

NameTypeDescription
_investmentIdbytes32The id of the investment whose rider payment is extended.
_newNextPayinDateuint256The new timestamp at which the rider’s next payment is due; must be later than the investment’s current next-payment date.
_slashAmountuint256Amount to slash from the rider’s savings, in USDC base units (6 decimals); 0 extends without slashing. Callers pass base units.

actionRiderPayin

Lets the hired rider make a periodic payment under a running hire purchase.

The rider must be the one hired on an investment in {HInvestmentStatus.Hired}, must not have completed the target total, and must make a non-late payment of at least {HInvestment.minimumRiderPeriodicPayingsUsd} plus the asset’s {HAsset.riderSlashableRepayableExtraCharge}. The extra charge is set aside as the rider’s savings, and the rest is split by {calcSharingFormula} and credited to the owner and the senior/junior investor pools. States updated: {riderPayinsList}, {riderPayins}, {HInvestment.riderPayinsHistory}, {HInvestment.riderNextPayinDate}, {ownerPayoutsList}, {investorPayoutList}, {HInvestment.riderSavings}, {HInvestment.investmentPayoutHistory} and the per-investor payout histories.

function actionRiderPayin(bytes32 _investmentId, uint256 _approvedUSD) external;

Parameters

NameTypeDescription
_investmentIdbytes32The id of the investment being paid.
_approvedUSDuint256The USDC the rider is paying, in USDC base units (6 decimals); must be at least the minimum plus the savings charge. Callers pass base units (frontend: parseUnits(x, 6)).

actionTransferOwnershipToRider

Completes a hire purchase by transferring ownership of the asset to the rider.

Only valid once the rider has finished paying. The rider’s accumulated savings are refunded to them and cleared, the investment is marked {HInvestmentStatus.Complete}, the completion timestamps are recorded and the investment is appended to the completed lists. It cannot run twice, so the completion side effects happen exactly once. States updated: {HInvestment.riderSavings}, {investmentsList}, {completedRiderInvestmentList} and {investmentsCompleted}.

function actionTransferOwnershipToRider(address _rider, bytes32 _investmentId) public;

Parameters

NameTypeDescription
_rideraddressThe rider receiving ownership; must be the investment’s hired rider.
_investmentIdbytes32The id of the investment being completed.

getStats

Returns every aggregate statistic the frontend displays in one call.

Counters are maintained in O(1) as the contract is used; the totals that mirror list lengths are filled in here so there is a single source of truth. Investor figures count filled slots, not distinct addresses.

function getStats() external view returns (HStats memory);

Returns

NameTypeDescription
<none>HStatsplatformStats The platform-wide statistics.

IVehicleTitleInfo

Git Source

The slice of the platform the title NFT reads for its metadata.

Functions

getTitleInfo

function getTitleInfo(uint256 tokenId) external view returns (bytes32 assetName, address rider);

Parameters

NameTypeDescription
tokenIduint256The title’s token id.

Returns

NameTypeDescription
assetNamebytes32The bytes32 asset name.
rideraddressThe rider who hired the asset, or address(0).

HenriableVehicleNFT

Git Source

Inherits: ERC721, Ownable

Title: Henriable vehicle-title NFT.

Author: Obiaraije Henry

An ERC721 that represents the title deed of one physical asset. The platform mints it to itself once an investment is fully funded and transfers it to the rider when the hire purchase is paid in full. The token is a standard, transferable ERC721 after the rider receives it.

Kept as its own contract (not inherited by {Henriable}) so the platform’s business logic stays under the EIP-170 code-size limit. Minting is gated by a single {minter} address, which the owning deployer sets to the {Henriable} address during deployment; the deployer keeps admin control of the collection.

State Variables

minter

The only address allowed to mint titles (the {Henriable} platform).

address(0) means no address can mint until the owner sets it.

address internal minter

nextTokenId

Id that will be assigned to the next minted title.

Ids start at 1 so 0 keeps meaning “no title”, matching the platform.

uint256 internal nextTokenId

Functions

constructor

Deploys the collection with its name and symbol.

constructor(address initialOwner) ERC721("Henriable Vehicle Title", "HVT") Ownable(initialOwner);

Parameters

NameTypeDescription
initialOwneraddressThe address that owns the collection and may set the minter.

onlyMinter

Restricts a function to the configured {minter}.

Reverts with {HVehicleNftUnauthorizedMinter} when the caller is not the minter.

modifier onlyMinter() ;

setMinter

Sets the address allowed to mint titles, typically the platform.

Only the collection owner may call this. Pass address(0) to disable minting. States updated: {minter}.

function setMinter(address newMinter) external onlyOwner;

Parameters

NameTypeDescription
newMinteraddressThe address allowed to mint, or address(0) to disable minting.

mint

Mints the next title to an address.

Only the {minter} may call this. Ids are assigned sequentially from 1. States updated: {nextTokenId}.

function mint(address to) external onlyMinter returns (uint256 tokenId);

Parameters

NameTypeDescription
toaddressThe address that will own the newly minted title.

Returns

NameTypeDescription
tokenIduint256The id of the newly minted title.

tokenURI

Returns the token’s metadata as an on-chain base64 data URI.

ERC721 metadata JSON with an inline base64 SVG image, so titles render in Etherscan, wallets and marketplaces with no off-chain hosting. The owner on the card is read live, so it updates when the platform transfers the deed. Reverts for a non-existent token (via {_requireOwned}).

function tokenURI(uint256 tokenId) public view override returns (string memory);

Parameters

NameTypeDescription
tokenIduint256The title’s token id.

Returns

NameTypeDescription
<none>stringA data:application/json;base64,... URI.

_renderSvg

Renders the title card as an SVG.

function _renderSvg(uint256 tokenId, address owner, string memory asset, address rider)
    internal
    pure
    returns (string memory);

Parameters

NameTypeDescription
tokenIduint256The title’s token id.
owneraddressThe current owner, shown on the card.
assetstringThe asset name (empty when unknown).
rideraddressThe hired rider (address(0) when unknown), shown on the card.

Returns

NameTypeDescription
<none>stringThe SVG markup.

_titleInfo

Reads the asset name and rider from the platform (the minter).

Returns zero values when the minter is unset or is not a contract that answers, so metadata degrades gracefully instead of reverting.

function _titleInfo(uint256 tokenId) internal view returns (bytes32 assetName, address rider);

Parameters

NameTypeDescription
tokenIduint256The title’s token id.

Returns

NameTypeDescription
assetNamebytes32The bytes32 asset name, or bytes32(0).
rideraddressThe rider, or address(0).

_bytes32ToString

Decodes a bytes32 asset name into a trimmed ASCII string.

function _bytes32ToString(bytes32 value) internal pure returns (string memory);

Parameters

NameTypeDescription
valuebytes32The bytes32 to decode.

Returns

NameTypeDescription
<none>stringThe decoded string (up to the first zero byte).

getMinter

Returns the address allowed to mint titles.

function getMinter() external view returns (address);

Returns

NameTypeDescription
<none>addressThe configured minter, or address(0) when unset.

getNextTokenId

Returns the id that will be assigned to the next minted title.

function getNextTokenId() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256The next token id.