Minter
Purpose
Minter is the bridge/orchestrator that:
Accepts baseAsset (e.g., WBTC) deposits and mints the synthetic token (
hilSyntheticToken) 1:1.Burns
hilSyntheticTokenand pays back the baseAsset on redemption.Distributes vault yield by minting
hilSyntheticTokento itself and letting theStakingVaultpull it (via prior approval) into the vault, where it vests into the share price.Enforces compliance (via
Whitelist+IComplianceChecker), role-gated ops, and pausing.
Imports
import {IMinter} from "../interfaces/minter/IMinter.sol";
import {IStakingVault} from "../interfaces/vault/IStakingVault.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20//ERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IMintableERC20} from "../interfaces/token/IMintableERC20.sol";
import {Whitelist} from "../helpers/Whitelist.sol";
import {IVerificationSBT} from "../interfaces/galactica/IVerificationSBT.sol";
import {IComplianceChecker} from "../interfaces/galactica/IComplianceChecker.sol";Roles
DEFAULT_ADMIN_ROLE – master admin; can rotate role addresses and update the compliance checker and manual whitelist.
OPERATOR_ROLE – allowed to move baseAsset from the Minter to
custodian(transferToCustody).PAUSER_ROLE – can
pause()andunpause().DISTRIBUTOR_ROLE – can initiate vault yield distributions.
CUSTODIAN_ROLE – address identity for custody (not used as a caller gate in current functions, but rotated and emitted).
Storage
Notes on storage semantics
Decimals check (constructor):
IERC20Metadata(hilSyntheticToken).decimals() == IERC20Metadata(baseAsset).decimals()is enforced.Approval: In the constructor, Minter calls
so the vault can pull minted yield from the Minter during
StakingVault.distributeYield(...).totalDepositsaccounting:Increments on:
mint,ownerMint,distributeYield.Decrements on:
redeem.Not changed by
transferToCustody. This variable reflects net issues/burns from Minter’s perspective, not the live baseAsset balance held at the contract.
Constructor
Requirements
All addresses must be non-zero; else
AddressCantBeZero().decimals(hilSyntheticToken) == decimals(baseAsset); elseIncompatibleDecimals().
Effects
Sets immutable token refs, installs compliance checker, role holders, and
stakingVault.Grants roles:
DEFAULT_ADMIN_ROLE→_initialAdminCUSTODIAN_ROLE→_custodianOPERATOR_ROLE→_operatorPAUSER_ROLE→_pauserDISTRIBUTOR_ROLE→_distributor
Approves the vault to spend unlimited
hilSyntheticTokenfrom the Minter (used during yield distribution).
Events (from IMinter and Whitelist)
IMinter and Whitelist)event Minted(address indexed to, uint256 amount);event Redeemed(address indexed user, uint256 amount);event FundsTransferredToCustody(uint256 amount, address indexed custodian);event AddressWhitelisted(address indexed user, bool allowed);(emitted byWhitelist)event ComplianceCheckerUpdated(address indexed newChecker);(emitted byWhitelist)
Errors
AddressCantBeZero()IncompatibleDecimals()AddressNotWhitelisted()(fromWhitelist.onlyWhitelisted)
Public / External
ownerMint
What it does
Mints amount of hilSyntheticToken directly to addressTo.
totalDeposits += amount;
Emits Minted(addressTo, amount).
When to use Admin pathway for mints associated with off-chain baseAsset inflows (e.g., native BTC deposit processed by operations).
distributeYield
What it does
Mints
amountofhilSyntheticTokento this Minter.totalDeposits += amount;Calls
stakingVault.distributeYield(amount, timestamp).
How vault pulls
Inside the vault, distributeYield performs:
Because the Minter pre-approved the vault in the constructor, the vault pulls amount from the Minter during this call.
Vesting behavior in vault
The vault then sets its internal vesting window (8h) starting at timestamp (see StakingVault docs). The yield is excluded from totalAssets() until it vests linearly.
Emits
Minted(address(stakingVault), amount) (from Minter)
pause / unpause
Pausing blocks
mint,redeem, andtransferToCustody(viawhenNotPaused).
setOperator / setPauser / setCustodian
Rotate role addresses. Validates non-zero; revokes old role; grants new role; updates stored address.
whitelistAddress (manual whitelist toggle)
Delegates to _whitelistAddress in Whitelist. Emits AddressWhitelisted(user, allowed).
setComplianceChecker
Updates the compliance backend used by Whitelist. Emits ComplianceCheckerUpdated(_complianceChecker).
transferToCustody
Transfers amount of baseAsset from the Minter to custodian.
Emits FundsTransferredToCustody(amount, custodian).
mint (user deposit)
Flow
Pull
amountof baseAsset frommsg.sender→this.Mint
amountofhilSyntheticTokentoto.totalDeposits += amount;emit Minted(to, amount);
Preconditions
Caller and
tomust passWhitelist.isAddressWhitelisted:true if
manualWhitelist[user]is set, elsecomplianceChecker.isCompliant(user).
redeem (user redemption)
Flow
Burn
amountofhilSyntheticTokenfrommsg.senderviaburnFrom.Caller must have set allowance for Minter if token enforces allowance on burnFrom.
Transfer
amountof baseAsset tomsg.sender.totalDeposits -= amount;emit Redeemed(msg.sender, amount);
Last updated
Was this helpful?

