Skip to content
Merged
Show file tree
Hide file tree
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
17 changes: 8 additions & 9 deletions test/lib/mocks/MockB20Storage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ library MockB20Storage {
// Tracks DEFAULT_ADMIN_ROLE holder count so renounceRole can enforce
// LastAdminCannotRenounce in O(1). Bumped on grant, decremented on
// revoke / renounce.
uint256 adminCount;
uint248 adminCount;
// False from etch until the factory sets it true at the end of
// createToken. Packed with adminCount in the same slot to avoid
// dedicating a full slot to a single flag.
bool initialized;
// ---------- Policy slots (PACKED, per-operation) ----------
// Hot-path policy IDs live in per-operation packed slots so each
// op reads exactly one slot, AND so adding granularity to one op
Expand Down Expand Up @@ -99,13 +103,6 @@ library MockB20Storage {
uint256 supplyCap;
// ---------- Permit (EIP-2612) ----------
mapping(address owner => uint256 nonce) nonces;
// ---------- Bootstrap window flag ----------
// False from etch until the factory writes `true` directly (via
// vm.store, mirroring the Rust impl's direct slot write). While
// false, factory-originated calls bypass all token-side authorization
// gates (role / policy / pause checks). Token invariants (supply-cap
// math, balance accounting) are NOT bypassed.
bool initialized;
}

// keccak256(abi.encode(uint256(keccak256("base.b20")) - 1)) & ~bytes32(uint256(0xff))
Expand Down Expand Up @@ -138,12 +135,14 @@ library MockB20Storage {
uint256 internal constant ROLES_OFFSET = 6;
uint256 internal constant ROLE_ADMINS_OFFSET = 7;
uint256 internal constant ADMIN_COUNT_OFFSET = 8;
uint256 internal constant INITIALIZED_OFFSET = 8;
// `initialized` is the high byte in slot 8 (packed after uint248 adminCount).
uint8 internal constant INITIALIZED_BYTE_OFFSET = 31;
uint256 internal constant TRANSFER_POLICY_IDS_OFFSET = 9;
uint256 internal constant MINT_POLICY_IDS_OFFSET = 10;
uint256 internal constant PAUSED_VECTORS_OFFSET = 11;
uint256 internal constant SUPPLY_CAP_OFFSET = 12;
uint256 internal constant NONCES_OFFSET = 13;
uint256 internal constant INITIALIZED_OFFSET = 14;

/// @notice Absolute slot for a top-level field of `Layout`.
/// @dev `STORAGE_LOCATION + offset`. The struct never crosses the
Expand Down
15 changes: 12 additions & 3 deletions test/lib/mocks/MockTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,12 @@ contract MockTokenFactory is ITokenFactory {
// -- 9. Close the bootstrap window by setting initialized=true.
// After this, the factory's privilege is gone; only
// role / policy / pause holders can mutate state.
_writeBool(token, MockB20Storage.slotOf(MockB20Storage.INITIALIZED_OFFSET), true);
_writePackedBool(
token,
MockB20Storage.slotOf(MockB20Storage.INITIALIZED_OFFSET),
MockB20Storage.INITIALIZED_BYTE_OFFSET,
true
);
}

/// @dev `DEFAULT_ADMIN_ROLE` per OZ AccessControl convention.
Expand Down Expand Up @@ -268,8 +273,12 @@ contract MockTokenFactory is ITokenFactory {
vm.store(target, slot, bytes32(value));
}

function _writeBool(address target, bytes32 slot, bool value) internal {
vm.store(target, slot, bytes32(uint256(value ? 1 : 0)));
function _writePackedBool(address target, bytes32 slot, uint8 byteOffset, bool value) internal {
uint256 shift = uint256(byteOffset) * 8;
uint256 mask = uint256(0xff) << shift;
uint256 current = uint256(vm.load(target, slot));
uint256 bit = value ? (uint256(1) << shift) : 0;
vm.store(target, slot, bytes32((current & ~mask) | bit));
}

/// @dev Solidity string storage encoding:
Expand Down