Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/MaxBTCERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@
// Copyright (c) 2024 COSMOS
// Copyright (c) 2025 Structured
// Modifications by Structured:
// - renamed contract to MaxBTCERC20
// - rename contract to MaxBTCERC20
// - set decimals() to 8
// - remove decimals() @dev docstring
// - replace config with dedicated ICS20 storage slot
// - disable renounceOwnership()
pragma solidity ^0.8.28;

import { IMintableAndBurnable } from "./IMintableAndBurnable.sol";
import { ERC20Upgradeable } from "@openzeppelin-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin-contracts/proxy/utils/UUPSUpgradeable.sol";
import { OwnableUpgradeable } from "@openzeppelin-upgradeable/access/OwnableUpgradeable.sol";
import { Ownable2StepUpgradeable } from "@openzeppelin-upgradeable/access/Ownable2StepUpgradeable.sol";
import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";

contract MaxBTCERC20 is IMintableAndBurnable, UUPSUpgradeable, ERC20Upgradeable, OwnableUpgradeable {
contract MaxBTCERC20 is IMintableAndBurnable, UUPSUpgradeable, ERC20Upgradeable, Ownable2StepUpgradeable {
/// @notice Caller is not the ICS20 contract
/// @param caller The address of the caller
error CallerIsNotICS20(address caller);

/// @notice Provided ICS20 address is invalid
error InvalidICS20();

/// @notice ERC-7201 slot for the ICS20 contract address
/// @dev keccak256(abi.encode(uint256(keccak256("maxbtc.erc20.ics20")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ICS20_STORAGE_SLOT = 0xaa9b9403d129a09996409713bb21f8632c135ae1789678b7128d16411b23e500;
Expand Down Expand Up @@ -46,6 +50,10 @@ contract MaxBTCERC20 is IMintableAndBurnable, UUPSUpgradeable, ERC20Upgradeable,
__ERC20_init(name_, symbol_);
__Ownable_init(owner_);

if (ics20_ == address(0)) {
revert InvalidICS20();
}

StorageSlot.getAddressSlot(ICS20_STORAGE_SLOT).value = ics20_;
}

Expand Down Expand Up @@ -74,6 +82,12 @@ contract MaxBTCERC20 is IMintableAndBurnable, UUPSUpgradeable, ERC20Upgradeable,
function _authorizeUpgrade(address) internal view override(UUPSUpgradeable) onlyOwner { }
// solhint-disable-previous-line no-empty-blocks

/// @notice prevents `owner` from renouncing ownership and potentially locking assets forever
/// @dev overrides OwnableUpgradeable's renounceOwnership to always revert
function renounceOwnership() public view override onlyOwner {
revert("Renouncing ownership disabled!");
}

/// @notice Modifier to check if the caller is the ICS20 contract
modifier onlyICS20() {
require(_msgSender() == StorageSlot.getAddressSlot(ICS20_STORAGE_SLOT).value, CallerIsNotICS20(_msgSender()));
Expand Down