DocumentationOpen App

Credit Configurator

The CreditConfiguratorV3 is the administrative gateway for the Credit Suite. It validates parameter changes and propagates them to the Credit Manager and Credit Facade. Importantly, it does not store configuration state itself - it acts as a validation layer.

Architecture

User/DAO -> CreditConfiguratorV3 (validation) -> CreditManagerV3/CreditFacadeV3 (state)

The Configurator ensures all changes are valid before forwarding them to the appropriate contract. This separation allows for:

  • Centralized validation logic
  • Consistent access control
  • Audit trail through events

Token & Risk Management

Adding Collateral Tokens

Solidity
function addCollateralToken(address token, uint16 liquidationThreshold);

Validation:

  • Token must be valid ERC-20
  • Must have price feed in PriceOracle
  • Must be quoted in PoolQuotaKeeper
  • LT cannot exceed underlying's LT

For Phantom Tokens:

  • The deposited (underlying) token must already exist as collateral
  • Phantom token represents staked/wrapped position

Adjusting Liquidation Thresholds

Solidity
// Immediate change function setLiquidationThreshold(address token, uint16 liquidationThreshold); // Gradual change (ramping) function rampLiquidationThreshold( address token, uint16 ltFinal, uint40 rampStart, uint24 rampDuration );

Ramping allows gradual LT changes over time, preventing sudden liquidation cascades when risk parameters are adjusted.

Forbidding/Allowing Tokens

FunctionAccessUse Case
forbidToken(address token)Pausable AdminsEmergency: mark token as risky
allowToken(address token)ConfiguratorRestore normal token status

Forbidden tokens still count toward collateral (with safe pricing) but have restrictions on quota increases and balance changes.

TypeScript
// TypeScript: Reading token configuration const creditManager = getContract({ address: cmAddress, abi: creditManagerV3Abi, client: publicClient, }); // Get collateral token data const tokenMask = await creditManager.read.getTokenMaskOrRevert([tokenAddress]); const tokenData = await creditManager.read.collateralTokenByMask([tokenMask]); // Returns: { token, ltInitial, ltFinal, timestampRampStart, rampDuration } // Check if token is forbidden const forbiddenMask = await creditManager.read.forbiddenTokenMask(); const isForbidden = (tokenMask & forbiddenMask) !== 0n;

Fee Management

Configurable Fees

Solidity
function setFees( uint16 feeLiquidation, uint16 liquidationPremium, uint16 feeLiquidationExpired, uint16 liquidationPremiumExpired );
ParameterDescription
feeLiquidationDAO fee on standard liquidations
liquidationPremiumReward for liquidators
feeLiquidationExpiredHigher DAO fee for expired accounts
liquidationPremiumExpiredHigher reward for expired liquidations

Constraints:

  • feeLiquidation <= liquidationPremium
  • feeLiquidationExpired <= feeLiquidation
  • liquidationPremium + feeLiquidation < 100%
  • Fee sum must remain constant (prevents sudden changes)

The relationship ensures liquidators are always incentivized and the protocol takes a smaller cut than the liquidator reward.


Borrowing Limits

Debt Bounds

Solidity
function setDebtLimits(uint128 newMinDebt, uint128 newMaxDebt);

Validation:

  • minDebt <= maxDebt
  • maxDebt * maxEnabledTokens <= minDebt * 100 (safety ratio)
  • USD value of minDebt must be non-zero

The safety ratio ensures accounts aren't opened with tiny debt that would be uneconomical to liquidate.

Per-Block Multiplier

Solidity
function setMaxDebtPerBlockMultiplier(uint8 multiplier); function forbidBorrowing(); // Sets multiplier to 0

forbidBorrowing() is an emergency action available to Pausable Admins. It immediately halts all new borrowing without requiring a DAO vote.


Adapter Management

Allowing Adapters

Solidity
function allowAdapter(address adapter); function forbidAdapter(address adapter);

Validation:

  • Adapter must implement creditManager() returning this Credit Manager
  • Adapter must implement targetContract() returning the DeFi protocol
  • Cannot target the Facade or Manager itself

Registration:

  • Creates bidirectional mapping: adapter <-> targetContract
  • Credit Account can only call whitelisted adapters
  • Each target protocol has exactly one adapter
TypeScript
// TypeScript: Checking adapter status const creditManager = getContract({ address: cmAddress, abi: creditManagerV3Abi, client: publicClient, }); // Get adapter for a target protocol const adapterAddress = await creditManager.read.contractToAdapter([uniswapRouterAddress]); if (adapterAddress === '0x0000000000000000000000000000000000000000') { console.log('No adapter registered for this protocol'); } else { console.log(`Adapter: ${adapterAddress}`); } // Get all adapters const adaptersData = await creditManager.read.adapters();

System Upgrades

Oracle Updates

Solidity
function setPriceOracle(address newPriceOracle);

Allows switching to a new price oracle implementation. The new oracle must support all currently configured collateral tokens.

Facade Migration

Solidity
function setCreditFacade(address newCreditFacade, bool migrateParams);

When migrateParams is true, debt limits and other Facade parameters are copied to the new contract. This enables upgrading the user-facing interface while preserving configuration.

Configurator Upgrade

Solidity
function upgradeCreditConfigurator(address newCreditConfigurator);

Transfers configurator role to a new contract. Used when the validation logic itself needs updating.


Access Control Model

Role Hierarchy

RoleCapabilities
ConfiguratorAll structural changes: tokens, fees, adapters, debt limits, upgrades
Pausable AdminEmergency actions: forbidToken, forbidBorrowing (no DAO vote required)

Cross-Contract Verification

The Credit Manager and Facade verify that configuration calls come from the registered Configurator:

Solidity
modifier creditConfiguratorOnly() { require(msg.sender == creditConfigurator); _; }

This prevents unauthorized parameter changes even if an attacker gains access to admin keys for other contracts.

TypeScript
// TypeScript: Reading configurator address const creditManager = getContract({ address: cmAddress, abi: creditManagerV3Abi, client: publicClient, }); const configuratorAddress = await creditManager.read.creditConfigurator(); console.log(`Configurator: ${configuratorAddress}`); // Check access control roles (from ACL contract) const acl = getContract({ address: aclAddress, abi: aclAbi, client: publicClient, }); const isPausableAdmin = await acl.read.isPausableAdmin([someAddress]); const isConfigurator = await acl.read.isConfigurator([someAddress]);
Sources