diff --git a/docs/B20/Asset.md b/docs/B20/Asset.md index 20cbaa1..fd7db2d 100644 --- a/docs/B20/Asset.md +++ b/docs/B20/Asset.md @@ -8,7 +8,7 @@ Each account's stored balance is the **raw** balance. A uniform on-chain **multi Read the current multiplier with `multiplier()`; the value is in WAD precision (`1e18`, exposed as `WAD_PRECISION()`). `toScaledBalance(rawBalance)` converts a raw amount to its scaled view, `toRawBalance(scaledBalance)` is the reverse converter (integer-floored, so the round-trip can lose up to one ULP), and `scaledBalanceOf(account)` is a convenience over ERC-20's `balanceOf` that returns the same account's raw balance in its scaled form. -Both multiplier setters validate `newMultiplier` is non-zero and at most `type(uint128).max` (reverting `InvalidMultiplier` otherwise). The `uint128` ceiling is the overflow guard: with supply capped at `type(uint128).max`, a `uint128` multiplier keeps `raw * multiplier` inside `uint256`, so scaled reads can never overflow. +Both multiplier setters validate `newMultiplier` is non-zero and at most `type(uint128).max` (reverting `InvalidMultiplier` otherwise). The `uint128` ceiling is the overflow guard: with supply capped at `type(uint128).max`, a `uint128` multiplier keeps `balance * multiplier` inside `uint256`, so balance-derived reads never overflow. ### Scheduling multiplier updates @@ -29,7 +29,7 @@ The Asset variant conforms to [ERC-8056](https://eips.ethereum.org/EIPS/eip-8056 - `balanceOfUI(account)` aliases `scaledBalanceOf`, and `totalSupplyUI()` returns `totalSupply() * uiMultiplier() / 1e18` (optional Balances extension `0xd890fd71`). - `supportsInterface(bytes4)` (ERC-165, `0x01ffc9a7`) returns `true` for those three IDs and for ERC-165 itself. The optional Conversion extension (`0x57854fc3`) is **not** claimed — the native `toScaledBalance` / `toRawBalance` names are kept unaliased for backwards compatibility. -**Events.** Every multiplier change emits `UIMultiplierUpdated(oldMultiplier, newMultiplier, effectiveAtTimestamp)` — from `setUIMultiplier` and from `updateMultiplier`. `MultiplierUpdateCancelled(cancelledMultiplier, cancelledEffectiveAt)` is emitted by `cancelScheduledMultiplier` and by `updateMultiplier` when it clears a live pending. +**Events.** Every multiplier change emits `UIMultiplierUpdated(oldMultiplier, newMultiplier, effectiveAtTimestamp)` — from `setUIMultiplier` and from `updateMultiplier`. `MultiplierUpdateCancelled(cancelledMultiplier, cancelledEffectiveAt)` is emitted by `cancelScheduledMultiplier` and by `updateMultiplier` when it clears a live pending. The optional ERC-8056 `TransferWithUIAmount` event is intentionally omitted — scaled balances are derivable from the raw `Transfer` and the active multiplier. ### Precision & decimals diff --git a/src/interfaces/IB20Asset.sol b/src/interfaces/IB20Asset.sol index d850c60..0468284 100644 --- a/src/interfaces/IB20Asset.sol +++ b/src/interfaces/IB20Asset.sol @@ -148,6 +148,7 @@ interface IB20Asset is IB20, IERC165, IScaledUIAmount, IScaledUIAmountNewUIMulti /// in shape to wstETH wrapping stETH. /// /// @dev Alias of the ERC-8056 `uiMultiplier()`. + /// /// @return Current (effective) multiplier. function multiplier() external view returns (uint256); @@ -180,17 +181,29 @@ interface IB20Asset is IB20, IERC165, IScaledUIAmount, IScaledUIAmountNewUIMulti /// @notice Schedules a multiplier update to take effect at `effectiveAt` — the standard path /// for corporate actions (splits, reinvested dividends). /// - /// @param newMultiplier New multiplier scaled to `WAD_PRECISION` + /// @dev Reverts with `AccessControlUnauthorizedAccount` when the caller does not hold `OPERATOR_ROLE`. + /// @dev Reverts with `InvalidMultiplier` when `newMultiplier` is zero or above `type(uint128).max`. + /// @dev Reverts with `EffectiveAtInPast` when `effectiveAt` is not in the future. + /// @dev Reverts with `EffectiveAtTooFar` when `effectiveAt` exceeds `type(uint64).max`. + /// @dev Reverts with `ScheduleOverlap` when a live pending update already exists. + /// + /// @param newMultiplier New multiplier scaled to `WAD_PRECISION`. /// @param effectiveAt Timestamp at which `newMultiplier` becomes effective; must be in the future. function setUIMultiplier(uint256 newMultiplier, uint256 effectiveAt) external; /// @notice Cancels the single live pending update, restoring the no-pending state /// (`effectiveAt` resets to 0). + /// + /// @dev Reverts with `AccessControlUnauthorizedAccount` when the caller does not hold `OPERATOR_ROLE`. + /// @dev Reverts with `NoScheduledMultiplier` when there is no live pending update. function cancelScheduledMultiplier() external; /// @notice Instant failsafe / emergency override — sets the current multiplier immediately and /// cancels any live pending update without a scheduling window. - /// Prefer `setUIMultiplier` for routine corporate actions + /// Prefer `setUIMultiplier` for routine corporate actions. + /// + /// @dev Reverts with `AccessControlUnauthorizedAccount` when the caller does not hold `OPERATOR_ROLE`. + /// @dev Reverts with `InvalidMultiplier` when `newMultiplier` is zero or above `type(uint128).max`. /// /// @param newMultiplier New multiplier scaled to `WAD_PRECISION`; must be in `(0, type(uint128).max]`. function updateMultiplier(uint256 newMultiplier) external; diff --git a/src/interfaces/IScaledUIAmount.sol b/src/interfaces/IScaledUIAmount.sol index 017dd7d..f519848 100644 --- a/src/interfaces/IScaledUIAmount.sol +++ b/src/interfaces/IScaledUIAmount.sol @@ -8,12 +8,8 @@ pragma solidity >=0.8.20 <0.9.0; /// `uiMultiplier` (18-decimal WAD, `1e18 = 1.0`) that rescales the *displayed* /// balance without minting, transferring, or rewriting any raw balance. /// -/// @dev Interface ID: `0xa60bf13d` — self-checked via `type(IScaledUIAmount).interfaceId`, which -/// is derived solely from `uiMultiplier()`. ERC-8056 also defines an OPTIONAL -/// `TransferWithUIAmount(address,address,uint256,uint256)` event; B20 deliberately omits it -/// (D9: no per-transfer scaled event, since the multiplier is cosmetic and scaled balances -/// are reconstructable off-chain). Events do not contribute to the interface ID, so omitting -/// it leaves `0xa60bf13d` unchanged. +/// @dev Interface ID: `0xa60bf13d`. The optional `TransferWithUIAmount` event is intentionally +/// not implemented; see `docs/B20/Asset.md` for the rationale. interface IScaledUIAmount { /// @notice Emitted when the UI multiplier is updated. event UIMultiplierUpdated(uint256 oldMultiplier, uint256 newMultiplier, uint256 effectiveAtTimestamp);