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_ROLE
may 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 minter
Owner (Ownable): initial admin. May change the blacklister (via
Blacklistable
) and, due to the token’s_update
override, 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_ROLE
to_initialAdmin
.Sets
minter = _minter
and grantsMINTER_ROLE
to_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_ROLE
fromminter
, updatesminter
, and grantsMINTER_ROLE
tonewMinter
.
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_ROLE
only.Reverts if:
to
is 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) external
Burns
amount
frommsg.sender
.Emits:
Transfer(msg.sender, address(0), amount)
.
Burn (from)
function burnFrom(address from, uint256 amount) external
If
msg.sender
is neitherfrom
nor 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_ROLE
User 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_ROLE
can 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