hBTC
Contract: hBTC
Inherits: ERC20, Blacklistable, AccessControl, Ownable
Decimals: 8
hBTC is the ERC-20 representation of Bitcoin allocated into Syntetika's BTC Basis+ Strategy and redeemable 1:1.
Role-gated minting: only
MINTER_ROLEmay mint.Admin burn authority: the owner can burn from any address without allowance.
Blacklist enforcement: transfers and mints/burns involving blacklisted addresses are blocked; the owner can perform administrative movements even if parties are blacklisted.
8-decimal accounting to match Bitcoin denominations.
State & roles
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
address public minter; // convenience pointer to the current designated minterOwner (Ownable): initial admin. May change the blacklister (via
Blacklistable) and, due to the token’s_updateoverride, can move/burn tokens even if addresses are blacklisted (for administrative recoveries).DEFAULT_ADMIN_ROLE (AccessControl): can grant/revoke roles (incl.
MINTER_ROLE) and callsetMinter.Blacklister (Blacklistable): manages the blacklist set (see events below).
Minter (
MINTER_ROLE): typically the Minter contract; can callmint.
Constructor
constructor(
  string memory _name,
  string memory _symbol,
  address _initialAdmin,
  address _minter
) ERC20(_name, _symbol) Ownable(_initialAdmin)Sets
owner = _initialAdmin,blacklister = _initialAdmin.Grants
DEFAULT_ADMIN_ROLEto_initialAdmin.Sets
minter = _minterand grantsMINTER_ROLEto_minter.
Functions
Decimals
function decimals() public pure override returns (uint8) { return 8; }Always 8. All mint/burn/transfer amounts are in satoshi-like units.
Set minter
function setMinter(address newMinter)
  external onlyRole(DEFAULT_ADMIN_ROLE)Requires:
newMinter != address(0).Effect: revokes
MINTER_ROLEfromminter, updatesminter, and grantsMINTER_ROLEtonewMinter.
Usage: rotate the system minter safely (e.g., to a new Minter contract).
Mint
function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE)Access:
MINTER_ROLEonly.Reverts if:
tois blacklisted and the caller is not the owner (blacklist is enforced in_update).Emits: standard
Transfer(address(0), to, amount).
Integration note: The Minter contract is granted MINTER_ROLE and calls this after receiving the base asset.
Burn (self)
function burn(uint256 amount) externalBurns
amountfrommsg.sender.Emits:
Transfer(msg.sender, address(0), amount).
Burn (from)
function burnFrom(address from, uint256 amount) externalIf
msg.senderis neitherfromnor the owner, the function spends allowance using_spendAllowance(from, msg.sender, amount).Then burns from
from.
Important integration detail:
Minter.redeem() calls hBTC.burnFrom(msg.sender, amount). Since the Minter contract is not the token owner, users must first call:
hBTC.approve(MINTER_ADDRESS, amount)before redeeming, otherwise burnFrom will revert due to insufficient allowance.
Transfer & blacklist enforcement (internal)
function _update(address from, address to, uint256 amount) internal override {
  if (msg.sender != owner()) {
    require(!_isBlacklisted(from) && !_isBlacklisted(to), "Blacklistable: account is blacklisted");
  }
  super._update(from, to, amount);
}Blacklist applies to all movements (transfers, mints, burns) unless the caller is the owner.
The owner bypass allows administrative recovery/seizure actions when required by policy.
Blacklist management (from Blacklistable)
Blacklistable)blacklist(address account)— onlyBlacklister; emitsBlacklisted(account).unBlacklist(address account)— onlyBlacklister; emitsUnBlacklisted(account).updateBlacklister(address newBlacklister)— onlyOwner; emitsBlacklisterChanged(newBlacklister).isBlacklisted(address account) → bool— view.
Effect on transfers/mint/burn: when the caller is not the owner, any move involving a blacklisted from or to reverts with Blacklistable: account is blacklisted.
Events
ERC-20:
Transfer,Approval.AccessControl:
RoleGranted,RoleRevoked(when roles change).Blacklistable:
Blacklisted,UnBlacklisted,BlacklisterChanged.
Common patterns
Grant minter to Minter contract
hBTC.setMinter(MINTER_ADDRESS); // caller has DEFAULT_ADMIN_ROLEUser redeem flow
// 1) Approve the Minter to burn hBTC.approve(MINTER_ADDRESS, amount); // 2) Redeem through Minter (burns & returns base asset; BTC leg handled by custody ops) Minter.redeem(amount);Admin rotation
Use
setMinter(new)to atomically revoke old minter and grant the new address.Use
updateBlacklister(new)from the owner to rotate blacklister.
Security & invariants
Supply control: Only
MINTER_ROLEcan mint; only holders (or owner/minter with allowance) can burn.Compliance at the edge: Blacklisting is enforced on token movements; the owner can execute administrative moves even when addresses are blacklisted.
Unit consistency: 8 decimals across hBTC, Minter base-asset pairing, and StakingVault (constructor checks enforce matching decimals).
Last updated
Was this helpful?

