Skip to content
bankhood.

Smart contracts

Interfaces, events and the deployment address table.

Interfaces are published ahead of deployment so integrators can build against them and reviewers can argue with them. Signatures may change before audit; anything that changes will be listed in the changelog.

Deployment addresses

ContractAddressStatus
BankhoodTokenNot yet deployed
FeeRouterNot yet deployed
ReserveVaultNot yet deployed
DistributorNot yet deployed
TimelockNot yet deployed
LiquidityLockNot yet deployed

IFeeRouter

IFeeRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IFeeRouter {
    /// @notice Accept a fee captured by the token's transfer hook.
    /// @dev Reverts unless the caller is the token contract.
    function collect(address payer, uint256 notional, bool isBuy)
        external
        payable;

    /// @notice Split the pending balance across the four destinations.
    /// @return toReserve Wei forwarded to the reserve vault.
    function flush() external returns (uint256 toReserve);

    /// @notice Basis points of collected ETH earmarked for HOOD.
    function hoodAllocationBps() external view returns (uint16);

    /// @notice Wei collected but not yet split.
    function pending() external view returns (uint256);

    event FeeCollected(
        address indexed payer,
        uint256 notional,
        uint256 feeWei,
        bool isBuy
    );
    event Flushed(
        uint256 toReserve,
        uint256 toLiquidity,
        uint256 toOps,
        uint256 toBuffer
    );
}

IReserveVault

IReserveVault.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IReserveVault {
    struct Fill {
        uint256 ethSpent;      // wei converted for this fill
        uint256 shares;        // 18-decimal share count acquired
        uint256 avgPriceUsd;   // 8-decimal average fill price
        uint64  executedAt;    // block timestamp of the report
    }

    /// @notice Signal that the pending balance has cleared the batch threshold.
    function requestPurchase() external returns (uint256 ethQueued);

    /// @notice Report a completed brokerage fill. Idempotent per orderId.
    /// @dev Restricted to the adapter. Advances the distributor's accumulator.
    function reportFill(
        bytes32 orderId,
        uint256 shares,
        uint256 avgPriceUsd
    ) external;

    /// @notice Total HOOD shares backing outstanding entitlements.
    function totalShares() external view returns (uint256);

    /// @notice Shares held but not yet allocated to holders.
    function unallocatedShares() external view returns (uint256);

    function fillCount() external view returns (uint256);
    function fillAt(uint256 index) external view returns (Fill memory);

    event PurchaseRequested(uint256 ethQueued, uint64 requestedAt);
    event FillReported(
        bytes32 indexed orderId,
        uint256 ethSpent,
        uint256 shares,
        uint256 avgPriceUsd
    );
    event PurchasesPaused(string reason);
}

IDistributor

IDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IDistributor {
    /// @notice Credit a settled batch of shares across taxable supply.
    /// @dev Restricted to the reserve vault.
    function distribute(uint256 shares) external;

    /// @notice Withdraw the caller's entire unclaimed entitlement.
    /// @dev Reverts while the caller's cooldown is active.
    function claim() external returns (uint256 shares);

    /// @notice Settle accrual for an account without withdrawing.
    /// @dev Called by the token on every balance change.
    function settle(address account) external;

    function claimable(address account) external view returns (uint256);
    function nextClaimAt(address account) external view returns (uint64);
    function accSharesPerToken() external view returns (uint256);
    function claimIntervalSeconds() external view returns (uint32);

    event Distributed(uint256 shares, uint256 accIndex, uint64 at);
    event Claimed(address indexed account, uint256 shares, uint64 at);
    event Settled(address indexed account, uint256 credited);
}

Events

The indexer is built entirely from these events; there is no privileged data source behind the explorer.

EventEmitted byFeeds
FeeCollectedFeeRouterExplorer → Fee payments
FlushedFeeRouterReserve totals
PurchaseRequestedReserveVaultPending batch indicator
FillReportedReserveVaultExplorer → Distributions
DistributedDistributorAccrual index history
ClaimedDistributorExplorer → Claims, app history
PurchasesPausedReserveVaultStatus banner

Reading state

Everything the interface displays for a connected wallet comes from three view calls. No signature is required to read any of them.

wagmi example
import { useReadContracts } from "wagmi";

const { data } = useReadContracts({
  contracts: [
    { address: BANK, abi: hsrAbi, functionName: "balanceOf", args: [account] },
    { address: DISTRIBUTOR, abi: distributorAbi, functionName: "claimable", args: [account] },
    { address: DISTRIBUTOR, abi: distributorAbi, functionName: "nextClaimAt", args: [account] },
  ],
});

const [balance, claimable, nextClaimAt] = data ?? [];

Conventions

Units

  • ETH amounts are wei. Never assume a display value has been scaled for you.
  • Share counts carry 18 decimals, matching BANK, so fractional entitlements survive arithmetic without precision loss.
  • USD prices carry 8 decimals, matching common oracle conventions.
  • Rates are basis points. 300 bps is 3%.

Safety

  • Every privileged function is restricted to a named contract, not to an EOA.
  • reportFill is idempotent per orderId, so a replayed report cannot double-credit the reserve.
  • Claims settle before transferring and revert on over-allocation against the vault.
  • Deployment targets Solidity 0.8.24 on Ethereum mainnet with no delegatecall in the value path.