> For the complete documentation index, see [llms.txt](https://docs.curvance.com/app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.curvance.com/app/developer-docs/protocol-managers/protocolmanager.md).

# ProtocolManager

### Overview

`ProtocolManager` is a capability-gated configuration hub for designated Curvance protocol addresses. It lets a configured authority address modify approved market, IRM, oracle guard, pause, and position-manager settings while enforcing per-period adjustment limits where the manager supports bounded changes.

The contract is intended for internal governance operations. It does not own the target contracts by itself; the target contracts still check `CentralRegistry` permissions when `ProtocolManager` calls them.

### Core Architecture

`ProtocolManager` stores:

| Item                   | Getter                                                                  | Purpose                                                                           |
| ---------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Central registry       | `centralRegistry()`                                                     | Permission hub used by this manager and downstream target contracts.              |
| Operational authority  | `protocolManager()`                                                     | Address allowed to call the bounded operational functions.                        |
| Capability flags       | `canModifyTokenConfig()`, `canModifyPriceGuards()`, and related getters | Immutable switches that decide which operation families this manager can perform. |
| Managed address config | `config(address)`                                                       | Per-address authority bit and `PeriodLimits`.                                     |
| Period adjustments     | `getMarketPeriodAdjustments()` and `getPriceGuardPeriodAdjustments()`   | Per-period accounting used to enforce bounded configuration movement.             |

Only elevated-permission addresses can call `updateManagementConfig()`. All other mutating operations require `msg.sender == protocolManager`, the matching capability flag, and authority over the managed address.

### Managed Configuration

The constructor accepts a `PermsConfig`, an initial list of managed addresses, and one `PeriodLimits` struct for each address.

```solidity
struct ManagementConfig {
    bool hasAuthority;
    PeriodLimits limits;
}

struct PermsConfig {
    bool canModifyPriceGuards;
    bool canDisablePriceGuards;
    bool canModifyTokenConfig;
    bool canModifyIRM;
    bool canUnpause;
    bool canModifyMintStatus;
    bool canModifyCollateralizationStatus;
    bool canModifyBorrowStatus;
    bool canModifyLiquidationStatus;
    bool canModifyRedeemStatus;
    bool canModifyTransferStatus;
    bool canModifyPositionManagers;
}
```

`updateManagementConfig()` can add or remove authority for addresses. When `hasAuthority` is false, the manager deletes that address's stored limits.

### Period Limit Model

`ProtocolManager` buckets adjustments into one-week periods:

| Constant                |        Value | Meaning                                                 |
| ----------------------- | -----------: | ------------------------------------------------------- |
| `PERIOD_DURATION`       |     `604800` | Period length in seconds.                               |
| `_UNIX_START_TIMESTAMP` | `1766966400` | Internal timestamp anchor used to derive period starts. |

For bounded changes, the manager computes:

```
new adjustment = new value - current value + existing adjustment for this period
```

The absolute value of that adjustment must be less than or equal to the relevant `PeriodLimits` field. If it exceeds the limit, the call reverts with `ProtocolManager__ParametersAreInvalid()`.

The adjustment key depends on the operation:

| Operation                         | Adjustment storage key      |
| --------------------------------- | --------------------------- |
| `updateTokenConfig()`             | `n.cToken`                  |
| `updateDynamicIRM()`              | `managedAddress` DynamicIRM |
| `setGuardedPriceConfig()`         | `asset`                     |
| `setGuardedPriceConfigCombined()` | `asset`                     |

### Period Limit Fields

```solidity
struct PeriodLimits {
    uint24 collRatioLimit;
    uint24 collReqSoftLimit;
    uint24 collReqHardLimit;
    uint120 collateralCapLimit;
    uint64 baseInterestRateLimit;
    uint112 debtCapLimit;
    uint64 vertexInterestRateLimit;
    uint64 vertexStartLimit;
    uint16 adjustmentVelocityLimit;
    uint16 decayPerAdjustmentLimit;
    uint24 vertexMultiplierMaxLimit;
    uint96 basePriceUSDLimit;
    uint96 minPriceUSDLimit;
    uint96 basePriceNativeLimit;
    uint96 minPriceNativeLimit;
}
```

The manager validates requested limits against hard maximums before storing them:

<table><thead><tr><th width="340">Constant</th><th align="right">Value</th></tr></thead><tbody><tr><td><code>MAXIMUM_COLL_RATIO_LIMIT</code></td><td align="right"><code>500</code></td></tr><tr><td><code>MAXIMUM_COLL_REQ_LIMIT</code></td><td align="right"><code>500</code></td></tr><tr><td><code>MAXIMUM_COLL_CAP_LIMIT</code></td><td align="right"><code>type(uint112).max</code></td></tr><tr><td><code>MAXIMUM_DEBT_CAP_LIMIT</code></td><td align="right"><code>type(uint104).max</code></td></tr><tr><td><code>MAXIMUM_INTEREST_RATE_LIMIT</code></td><td align="right"><code>1000</code></td></tr><tr><td><code>MAXIMUM_ADJUSTMENT_VELOCITY_LIMIT</code></td><td align="right"><code>300</code></td></tr><tr><td><code>MAXIMUM_DECAY_RATE_LIMIT</code></td><td align="right"><code>120</code></td></tr><tr><td><code>MAXIMUM_VERTEX_MULTIPLIER_MAX_LIMIT</code></td><td align="right"><code>50000</code></td></tr><tr><td><code>MAXIMUM_PRICE_GUARD_PRICE_LIMIT</code></td><td align="right"><code>type(uint88).max</code></td></tr></tbody></table>

`MINIMUM_LIQUIDATION_INCENTIVE` is `150`; `updateTokenConfig()` rejects any token config where `liqIncMin` is below that floor.

### Operation Groups

#### Market Token Config

`updateTokenConfig()` forwards a `MarketManagerIsolated.TokenConfig` to a managed market manager after checking:

* This manager can modify token configs.
* The managed market manager has authority.
* The cToken in the config has authority.
* The cToken is listed in the target market.
* `liqIncMin` is at least `MINIMUM_LIQUIDATION_INCENTIVE`.
* The period adjustments for collateral ratio, collateral requirements, collateral cap, and debt cap stay within the cToken's limits.

The `TokenConfig` field order is:

```solidity
struct TokenConfig {
    address cToken;
    uint256 collRatio;
    uint256 collReqSoft;
    uint256 collReqHard;
    uint256 liqIncBase;
    uint256 liqIncHard;
    uint256 liqIncMin;
    uint256 liqIncMax;
    uint256 closeFactorBase;
    uint256 closeFactorMin;
    uint256 closeFactorMax;
    uint256 collateralCap;
    uint256 debtCap;
}
```

#### Dynamic IRM Config

`updateDynamicIRM()` updates a managed `DynamicIRM`. Inputs use per-year and BPS values, while the target `DynamicIRM` stores per-second rate fields internally. Before forwarding the update, `ProtocolManager` converts the current per-second rates back to BPS for period-limit accounting.

The tracked fields are `baseRatePerYear`, `vertexRatePerYear`, `vertexStart`, `adjustmentVelocity`, `decayPerAdjustment`, and `vertexMultiplierMax`.

#### Price Guards

`setGuardedPriceConfig()` changes a `BaseOracleAdaptor` guard for a specific `(asset, inUSD)` route. The manager requires:

* Authority over the adaptor address.
* Authority over the asset.
* `canModifyPriceGuards == true`.
* The adaptor supports the asset.
* `timestampStart` and `ips` match the current guard values.
* `basePrice` and `minPrice` movements stay within the asset's period limits.

`disableGuardedPriceConfig()` removes an adaptor guard for `(asset, inUSD)`. It uses `canDisablePriceGuards`, not `canModifyPriceGuards`, and does not apply period limits.

#### Combined Aggregator Guards

`setGuardedPriceConfigCombined()` and `disableGuardedPriceConfigCombined()` operate on `CombinedAggregator`, which has one global `pg()` guard instead of per-asset guard storage.

Before mutating the aggregator, `_checkCombinedAggregatorAuthority()` confirms:

1. The caller has normal authority over the aggregator and asset.
2. `centralRegistry.oracleManager()` returns the oracle manager.
3. `IOracleManager.getPricingAdaptors(asset)[0]` returns a chainlink-style adaptor.
4. `IChainlinkStyleAdaptor.assetConfig(asset, inUSD)` points to `managedAddress` as the configured aggregator.

This route check prevents a manager from changing a combined aggregator guard without proving that the aggregator is the active route for the authorized asset and denomination.

#### Pause Flags

`setPaused()` maps `action` values to market pause setters:

<table><thead><tr><th width="92" align="right">Action</th><th width="254">Scope</th><th>Target call</th></tr></thead><tbody><tr><td align="right"><code>0</code></td><td>Market-wide liquidation</td><td><code>setLiquidationPaused(state)</code></td></tr><tr><td align="right"><code>1</code></td><td>Market-wide redeem</td><td><code>setRedeemPaused(state)</code></td></tr><tr><td align="right"><code>2</code></td><td>Market-wide transfer</td><td><code>setTransferPaused(state)</code></td></tr><tr><td align="right"><code>3</code></td><td>Token-level mint</td><td><code>setMintPaused(cToken, state)</code></td></tr><tr><td align="right"><code>4</code></td><td>Token-level collateralization</td><td><code>setCollateralizationPaused(cToken, state)</code></td></tr><tr><td align="right"><code>5</code></td><td>Token-level borrow</td><td><code>setBorrowPaused(cToken, state)</code></td></tr></tbody></table>

For token-level actions, the cToken must also have authority. If `state` is false, `canUnpause` must be true or the call reverts.

#### Position Managers

`updatePositionManager()` adds or removes a position manager on a managed market manager. The operation requires `canModifyPositionManagers` and authority over the market manager.

### Events and Errors

`ProtocolManager` emits `ManagementAuthorityUpdated(address addressManaged, bool manages, PeriodLimits limits)` whenever management configuration changes.

The contract defines these custom errors:

| Error                                     | Meaning                                                                        |
| ----------------------------------------- | ------------------------------------------------------------------------------ |
| `ProtocolManager__ParametersAreInvalid()` | Input validation, limit validation, asset support, or action ID checks failed. |
| `ProtocolManager__Unauthorized()`         | Caller, capability flag, managed address, or asset authority check failed.     |
| `ProtocolManager__MulDivFailed()`         | Internal multiplication/division helper failed.                                |
| `ProtocolManager__UintToIntError()`       | Internal signed conversion would overflow.                                     |

### Integration Considerations

* Confirm the manager contract itself has market permissions before expecting downstream writes to succeed.
* Configure both the target address and any relevant asset address in `config(address)` before using asset-scoped functions.
* Use the current `getPeriodTimestamp()` value when querying period adjustments for the active period.
* Query token config adjustments by cToken address, not by market manager address.
* Query PriceGuard adjustments by asset address, not by adaptor or aggregator address.
* Do not use `disableGuardedPriceConfig*()` as a bounded PriceGuard update. Disabling guards is a separate capability that bypasses period-limit accounting.
* A manager with `canUnpause == false` can still pause any pause scope it is authorized for, but it cannot unpause through `setPaused()`.

### Callable Functions

#### Constructor

```solidity
constructor(
    ICentralRegistry cr,
    address pm,
    ProtocolManager.PermsConfig memory p,
    address[] memory managedAddresses,
    ProtocolManager.PeriodLimits[] memory l
);
```

Deploys the manager, validates the central registry, stores the operational authority, configures the initial managed addresses, and stores immutable capability flags.

#### Configuration Reads

```solidity
function config(address managedAddress)
    external
    view
    returns (bool hasAuthority, ProtocolManager.PeriodLimits memory limits);

function getMarketPeriodAdjustments(
    address managedAddress,
    uint256 periodTimestamp
) external view returns (
    int24 collRatio,
    int24 collReqSoft,
    int24 collReqHard,
    int120 collateralCap,
    int112 debtCap,
    int64 baseInterestRate,
    int64 vertexInterestRate,
    int64 vertexStart,
    int16 adjustmentVelocity,
    int16 decayPerAdjustment,
    int24 vertexMultiplierMax
);

function getPriceGuardPeriodAdjustments(
    address managedAddress,
    uint256 periodTimestamp
) external view returns (
    int96 basePriceUSD,
    int96 minPriceUSD,
    int96 basePriceNative,
    int96 minPriceNative
);

function getPeriodTimestamp() public view returns (uint256 x);
```

Use these reads to inspect the configured managed set, current period bucket, and tracked adjustments. Compiler-generated getters also expose the immutable capability flags, constants, `centralRegistry()`, and `protocolManager()`.

#### Management Configuration

```solidity
function updateManagementConfig(
    address[] memory managedAddresses,
    ProtocolManager.PeriodLimits[] memory l,
    bool hasAuthority
) external;
```

Adds or removes managed-address authority. Only an address with elevated permissions in the central registry can call this function.

#### Token Config

```solidity
function updateTokenConfig(
    address managedAddress,
    MarketManagerIsolated.TokenConfig memory n
) external;
```

Applies a token config to a managed `MarketManagerIsolated`. Requires token-config capability, authority over the market and cToken, and period-limit compliance for the tracked fields.

#### Dynamic IRM

```solidity
function updateDynamicIRM(
    address managedAddress,
    uint256 baseRatePerYear,
    uint256 vertexRatePerYear,
    uint256 vertexStart,
    uint256 adjustmentVelocity,
    uint256 decayPerAdjustment,
    uint256 vertexMultiplierMax,
    bool vertexReset
) external;
```

Updates a managed `DynamicIRM` after applying period-limit accounting to the IRM parameters.

#### Price Guards

```solidity
function setGuardedPriceConfig(
    address managedAddress,
    address asset,
    bool inUSD,
    uint256 timestampStart,
    uint256 ips,
    uint256 basePrice,
    uint256 minPrice
) external;

function setGuardedPriceConfigCombined(
    address managedAddress,
    address asset,
    bool inUSD,
    uint256 timestampStart,
    uint256 ips,
    uint256 basePrice,
    uint256 minPrice
) external;

function disableGuardedPriceConfig(
    address managedAddress,
    address asset,
    bool inUSD
) external;

function disableGuardedPriceConfigCombined(
    address managedAddress,
    address asset,
    bool inUSD
) external;
```

The `set*` functions enforce PriceGuard period limits. The `disable*` functions use the separate disable capability and bypass period-limit accounting.

#### Pause Flags

```solidity
function setPaused(
    address managedAddress,
    address cToken,
    uint8 action,
    bool state
) external;
```

Sets one market-wide or token-level pause flag. Pass `address(0)` for `cToken` when `action` is `0`, `1`, or `2`.

#### Position Managers

```solidity
function updatePositionManager(
    address managedAddress,
    address pm,
    bool add
) external;
```

Adds or removes `pm` as a position manager on a managed `MarketManagerIsolated`.
