Minter Contract
The Minter contract is responsible for minting and redeeming leveraged tokens (hsTokens) and pegged tokens (haTokens) in exchange for wrapped collateral.
Integrator cookbook (approve → dry-run → tx): Mint and redeem. Pricing views: Pricing.
Overview
The Minter is a UUPS upgradeable contract that handles the core minting and redemption operations for Harbor Protocol markets. It implements a sophisticated fee/discount system based on collateral ratios and provides price stability through stability pools.
The Solidity implementation in the Harbor repository is organized around Minter_v1.sol; production minters were upgraded to Minter_v2 in March 2026 to fix an incorrect Sail (leveraged-token) minting path in freeRedeemPeggedToken during rebalances. The fix is a minimal change (two lines) to the Sail minting calculation; all markets received the upgrade. For the incident timeline, pool remediation, and verification, see fxUSD–ETH Sail stability pool rebalance remediation.
March 2026 upgrade (Sail minting fix)
A bug in Minter_v1 caused Sail rewards minted to the Sail stability pool during protocol rebalances to be overstated (approximately 23×) when pegged tokens were redeemed via the fee-free path used by the stability pool manager. Collateral ratio restoration per rebalance was correct; only the Sail reward leg was wrong.
Remediation: Upgrade to Minter_v2 with the corrected freeRedeemPeggedToken logic, expanded automated tests, and a one-time on-chain remediation for the affected fxUSD–ETH Sail pool (integral rescale, excess Sail burn, treasury coverage of a small fxSAVE gap where tokens had already left the pool). Full narrative and links to the fix, deployment, and remediation commits: fxUSD–ETH Sail stability pool rebalance remediation.
Contract Architecture
- Upgradeable: Uses UUPS (Universal Upgradeable Proxy Standard) pattern
- Storage: Uses ERC-7201 namespaced storage
- Access Control: Uses BaoOwnableRoles for role-based access control
- Reentrancy Protection: Uses OpenZeppelin's ReentrancyGuardTransientUpgradeable
Immutable Addresses
Set during contract construction (cannot be changed):
- WRAPPED_COLLATERAL_TOKEN: The wrapped collateral token (e.g., wstETH)
- PEGGED_TOKEN: The pegged token contract (haToken, e.g., haETH)
- LEVERAGED_TOKEN: The leveraged token contract (hsToken, e.g., hsETHxFxSAVE)
- _BURN_SIGNATURE: The burn signature type for the pegged token (Burn1Arg, Burn2Arg, or BurnFrom)
Storage State
- peggedTokenBalance: Total pegged tokens minted by this contract (tracks only tokens minted here, not total supply)
- underlyingCollateral: Total underlying collateral backing the tokens
- reservePool: Address of the reserve pool (source of discounts)
- feeReceiver: Address that receives protocol fees
- priceOracle: Address of the price oracle contract
- incentiveConfig: Configuration for fees, discounts, and disallows based on collateral ratio bands
Key Functions
Minting Functions
mintPeggedToken(uint256 wrappedCollateralIn, address receiver, uint256 minPeggedOut)
Mints pegged tokens (haTokens) by depositing wrapped collateral.
Parameters:
wrappedCollateralIn: Amount of wrapped collateral to depositreceiver: Address to receive the minted pegged tokensminPeggedOut: Minimum amount of pegged tokens expected (slippage protection)
Returns:
peggedOut: Amount of pegged tokens minted
Effects:
- Transfers wrapped collateral from sender to contract
- Mints pegged tokens to receiver
- Applies fees based on current collateral ratio
- Updates
peggedTokenBalanceandunderlyingCollateral
mintLeveragedToken(uint256 wrappedCollateralIn, address receiver, uint256 minLeveragedOut)
Mints leveraged tokens (hsTokens) by depositing wrapped collateral.
Parameters:
wrappedCollateralIn: Amount of wrapped collateral to depositreceiver: Address to receive the minted leveraged tokensminLeveragedOut: Minimum amount of leveraged tokens expected (slippage protection)
Returns:
leveragedOut: Amount of leveraged tokens minted
Effects:
- Transfers wrapped collateral from sender to contract
- Mints leveraged tokens to receiver
- Applies fees or discounts based on current collateral ratio
- Updates
underlyingCollateral
Redemption Functions
redeemPeggedToken(uint256 peggedIn, address receiver, uint256 minWrappedCollateralOut)
Redeems pegged tokens (haTokens) for wrapped collateral.
Parameters:
peggedIn: Amount of pegged tokens to redeemreceiver: Address to receive the wrapped collateralminWrappedCollateralOut: Minimum amount of wrapped collateral expected (slippage protection)
Returns:
wrappedCollateralOut: Amount of wrapped collateral returned
Effects:
- Burns pegged tokens from sender
- Transfers wrapped collateral to receiver
- Applies fees or discounts based on current collateral ratio
- May request bonus from reserve pool if discount applies
- Updates
peggedTokenBalanceandunderlyingCollateral
redeemLeveragedToken(uint256 leveragedIn, address receiver, uint256 minWrappedCollateralOut)
Redeems leveraged tokens (hsTokens) for wrapped collateral.
Parameters:
leveragedIn: Amount of leveraged tokens to redeemreceiver: Address to receive the wrapped collateralminWrappedCollateralOut: Minimum amount of wrapped collateral expected (slippage protection)
Returns:
wrappedCollateralOut: Amount of wrapped collateral returned
Effects:
- Burns leveraged tokens from sender
- Transfers wrapped collateral to receiver
- Applies fees based on current collateral ratio
- Updates
underlyingCollateral
Fee-Free Functions (Restricted Access)
These functions are available only to addresses with ZERO_FEE_ROLE:
freeMintPeggedToken(uint256 wrappedCollateralIn, address receiver)freeRedeemPeggedToken(uint256 peggedForCollateral, uint256 peggedForLeveraged, address receiver)freeMintLeveragedToken(uint256 wrappedCollateralIn, address receiver)freeRedeemLeveragedToken(uint256 leveragedIn, address receiver)
Dry Run Functions
View quotes — decode by position (ABI names may differ slightly from Solidity natspec). incentiveRatio == 1e18 means disallowed at the current CR; also treat taken/out == 0 as no-op / revert path. Partial fills can leave taken < in with a ratio ≠ 1e18.
| Function | Returns (ordered) |
|---|---|
mintPeggedTokenDryRun(wrappedCollateralIn) | incentiveRatio, wrappedFee, wrappedCollateralTaken, peggedMinted, price, rate |
mintLeveragedTokenDryRun(wrappedCollateralIn) | incentiveRatio, wrappedFee, wrappedDiscount, wrappedCollateralUsed, leveragedMinted, price, rate |
redeemPeggedTokenDryRun(peggedIn) | incentiveRatio, wrappedFee, wrappedDiscount, peggedRedeemed, wrappedCollateralReturned, price, rate |
redeemLeveragedTokenDryRun(leveragedIn) | incentiveRatio, wrappedFee, leveragedRedeemed, wrappedCollateralReturned, price, rate |
Cookbook: Mint and redeem.
View Functions
Price and Ratio Queries
collateralRatio() returns (uint256)- Current collateral ratio (collateral value / pegged token value)leverageRatio() returns (uint256)- Current leverage ratio (capped at 20x)peggedTokenPrice() returns (uint256)- Current price of pegged tokenleveragedTokenPrice() returns (uint256)- Current NAV of leveraged token
Incentive Ratio Queries
mintPeggedTokenIncentiveRatio() returns (int256)- Fee/discount ratio for minting pegged tokensredeemPeggedTokenIncentiveRatio() returns (int256)- Fee/discount ratio for redeeming pegged tokensmintLeveragedTokenIncentiveRatio() returns (int256)- Fee/discount ratio for minting leveraged tokensredeemLeveragedTokenIncentiveRatio() returns (int256)- Fee/discount ratio for redeeming leveraged tokens
Balance Queries
peggedTokenBalance() returns (uint256)- Total pegged tokens minted by this contractleveragedTokenBalance() returns (uint256)- Total supply of leveraged tokenscollateralTokenBalance() returns (uint256)- Total underlying collateral
Other View Functions
harvestable() returns (uint256)- Amount of wrapped collateral available for harvestingredeemPeggedForCollateralRatio(uint256 targetCollateralRatio)- Calculates how to split pegged redemption to achieve target CRconfig() returns (Config)- Returns the current incentive configuration
Configuration Functions (Owner Only)
updateConfig(Config calldata config_)- Updates fee/discount/disallow configurationupdatePriceOracle(address priceOracle_)- Updates the price oracle addressupdateFeeReceiver(address feeReceiver_)- Updates the fee receiver addressupdateReservePool(address reservePool_)- Updates the reserve pool addressreset()- Resets underlying collateral tracking (for accounting corrections)
Fee, Discount, and Disallow System
The contract implements a sophisticated incentive system based on collateral ratio bands:
- Positive values: Fees (taken from input/output)
- Negative values: Discounts (provided from reserve pool)
- Value == 1 ether: Disallow (action blocked at that collateral ratio)
Fees and discounts are calculated as definite integrals across collateral ratio boundaries, ensuring fairness regardless of transaction size.
Deployed band tables and the critical ratios (hs redeem disallowed only below 100%, leverage cap of 20× at about 105.3%, rebalance thresholds 105–130%): Fee bands.
Dependencies
- LEVERAGED_TOKEN: The leveraged token contract (MintableBurnableERC20)
- PEGGED_TOKEN: The pegged token contract (MintableBurnableERC20)
- WRAPPED_COLLATERAL_TOKEN: The wrapped collateral token (e.g., wstETH)
- priceOracle: Price oracle implementing
IWrappedPriceOracleinterface - reservePool: Reserve pool implementing
IReservePoolinterface (source of discounts) - feeReceiver: Address that receives protocol fees (typically TokenDistributor)
Security Considerations
- All mutating functions are protected by
nonReentrantmodifier - Price oracle provides min/mid/max prices for different use cases
- Minimum output amounts prevent excessive slippage
- Disallow mechanism can block actions at dangerous collateral ratios
- Reserve pool capacity limits discount amounts
- Leverage ratio is capped at 20x
- Contract uses UUPS upgradeable pattern (only owner can upgrade)