Skip to content

Commit

Permalink
refactor: remove lag functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
amusingaxl committed Feb 6, 2025
1 parent da771ad commit 891164c
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 283 deletions.
23 changes: 7 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Direct Stability Parameters Change Module (DSPC)

A module for MakerDAO that enables direct changes to stability parameters (duty, dsr, ssr) through a simple, secure interface with proper constraints and timelocks.
A module for MakerDAO that enables direct changes to stability parameters (duty, dsr, ssr) through a simple, secure interface with proper constraints.

## Overview

Expand All @@ -11,14 +11,13 @@ The DSPC module provides a streamlined way to modify stability parameters in the

## Features

- Batch updates for multiple rate changes
- Immediate updates for multiple rate changes
- Two-level access control:
- Admins can configure the module
- Facilitators can propose and execute rate changes
- Facilitators can execute rate changes
- Rate change constraints:
- Min/max caps per rate
- Maximum change (gap) per update
- Timelock for all rate changes
- Maximum change (step) per update
- Event emission for all actions
- Simple, auditable implementation

Expand Down Expand Up @@ -48,9 +47,6 @@ DSPC dspc = new DSPC(

2. Configure the module parameters:
```solidity
// Set timelock duration
dspc.file("lag", 1 days);
// Configure constraints for a collateral type
dspc.file("ETH-A", "min", 1); // Min rate: 0.01%
dspc.file("ETH-A", "max", 1000); // Max rate: 10%
Expand All @@ -67,25 +63,20 @@ dspc.file("DSR", "step", 50); // Max change: 0.5%
dspc.kiss(facilitatorAddress);
```

4. Propose a batch of rate changes:
4. Execute rate changes:
```solidity
DSPC.ParamChange[] memory updates = new DSPC.ParamChange[](2);
updates[0] = DSPC.ParamChange("ETH-A", 150); // Set ETH-A rate to 1.5%
updates[1] = DSPC.ParamChange("DSR", 75); // Set DSR to 0.75%
dspc.put(updates);
```

5. After the timelock period, execute the changes:
```solidity
dspc.zap();
dspc.set(updates);
```

## Security

The module implements a robust security model:
- Two-level access control (admins and facilitators)
- Rate constraints to prevent extreme changes
- Timelock for all rate modifications
- Maximum step size for rate changes
- Disabling without GSM delay via DSPCMom contract
- Circuit breaker (halt) functionality
- All actions emit events for transparency
Expand Down
136 changes: 15 additions & 121 deletions src/DSPC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface SUSDSLike {

interface ConvLike {
function turn(uint256 bps) external pure returns (uint256 ray);
function back(uint256 ray) external pure returns (uint256 bps);
}

/// @title Direct Stability Parameters Change Module
Expand Down Expand Up @@ -53,14 +54,8 @@ contract DSPC {
mapping(address => uint256) public buds;
/// @notice Mapping of rate constraints
mapping(bytes32 => Cfg) private _cfgs;
/// @notice Pending rate updates
ParamChange[] private _batch;
/// @notice Circuit breaker flag
uint8 public bad;
/// @notice Timelock duration
uint32 public lag;
/// @notice Timestamp when current batch can be executed
uint64 public eta;
uint256 public bad;

// --- Events ---
event Rely(address indexed usr);
Expand All @@ -69,9 +64,7 @@ contract DSPC {
event Diss(address indexed usr);
event File(bytes32 indexed what, uint256 data);
event File(bytes32 indexed id, bytes32 indexed what, uint256 data);
event Put(ParamChange[] updates, uint256 eta);
event Pop(ParamChange[] updates);
event Zap(ParamChange[] updates);
event Set(ParamChange[] updates);

// --- Modifiers ---
modifier auth() {
Expand Down Expand Up @@ -132,19 +125,13 @@ contract DSPC {

/// @notice Configure module parameters
function file(bytes32 what, uint256 data) external auth {
if (what == "lag") {
require(data <= type(uint32).max, "DSPC/lag-overflow");
lag = uint32(data);
} else if (what == "bad") {
if (what == "bad") {
require(data <= 1, "DSPC/invalid-bad-value");
bad = uint8(data);
bad = data;
} else {
revert("DSPC/file-unrecognized-param");
}

// Clear any pending batch when configs change
_pop();

emit File(what, data);
}

Expand All @@ -164,32 +151,14 @@ contract DSPC {
revert("DSPC/file-unrecognized-param");
}

// Clear any pending batch when configs change
_pop();

emit File(id, what, data);
}

/// @notice Cancel a pending batch
function pop() external toll good {
_pop();
}

/// @notice Internal function to cancel a pending batch
/// @dev Clears the pending batch and resets the activation time
function _pop() internal {
if (_batch.length > 0) {
emit Pop(_batch);
delete _batch;
eta = 0;
}
}

/// @notice Schedule a batch of rate updates
function put(ParamChange[] calldata updates) external toll good {
/// @notice Set and apply rate updates immediately
function set(ParamChange[] calldata updates) external toll good {
require(updates.length > 0, "DSPC/empty-batch");

// Validate all updates in the batch
// Validate and execute all updates
for (uint256 i = 0; i < updates.length; i++) {
bytes32 id = updates[i].id;
uint256 bps = updates[i].bps;
Expand All @@ -201,86 +170,20 @@ contract DSPC {
// Check rate change is within allowed gap
uint256 oldBps;
if (id == "DSR") {
oldBps = _back(PotLike(pot).dsr());
oldBps = conv.back(PotLike(pot).dsr());
} else if (id == "SSR") {
oldBps = _back(SUSDSLike(susds).ssr());
oldBps = conv.back(SUSDSLike(susds).ssr());
} else {
(uint256 duty,) = JugLike(jug).ilks(id);
oldBps = _back(duty);
oldBps = conv.back(duty);
}

// Calculate absolute difference between new and old rate, and ensure it doesn't exceed maximum allowed change
uint256 delta = bps > oldBps ? bps - oldBps : oldBps - bps;
require(delta <= cfg.step, "DSPC/delta-above-step");
}

// Store the batch
delete _batch;
for (uint256 i = 0; i < updates.length; i++) {
_batch.push(updates[i]);
}
eta = uint64(block.timestamp + lag);

emit Put(updates, eta);
}

/// @notice Internal function to convert a per-second rate to basis points
/// @param ray The per-second rate to convert
/// @return bps The rate in basis points
function _back(uint256 ray) internal pure returns (uint256 bps) {
// Convert per-second rate to per-year rate using rpow
uint256 yearlyRate = _rpow(ray, 365 days);
// Subtract RAY to get the yearly rate delta and convert to basis points
// Add RAY/2 for rounding: ensures values are rounded up when >= 0.5 and down when < 0.5
return ((yearlyRate - RAY) * BASIS_POINTS + RAY / 2) / RAY;
}

/// @notice Internal function to calculate the result of a rate raised to a power
/// @param x The base rate
/// @param n The exponent
/// @return z The result of x raised to the power of n
function _rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
assembly {
switch x
case 0 {
switch n
case 0 { z := RAY }
default { z := 0 }
}
default {
switch mod(n, 2)
case 0 { z := RAY }
default { z := x }
let half := div(RAY, 2) // for rounding.
for { n := div(n, 2) } n { n := div(n, 2) } {
let xx := mul(x, x)
if iszero(eq(div(xx, x), x)) { revert(0, 0) }
let xxRound := add(xx, half)
if lt(xxRound, xx) { revert(0, 0) }
x := div(xxRound, RAY)
if mod(n, 2) {
let zx := mul(z, x)
if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0, 0) }
let zxRound := add(zx, half)
if lt(zxRound, zx) { revert(0, 0) }
z := div(zxRound, RAY)
}
}
}
}
}

/// @notice Execute a pending batch
function zap() external good {
require(_batch.length > 0, "DSPC/no-pending-batch");
require(block.timestamp >= eta, "DSPC/batch-not-ready");

ParamChange[] memory updates = _batch;

// Execute all updates
for (uint256 i = 0; i < updates.length; i++) {
bytes32 id = updates[i].id;
uint256 ray = conv.turn(updates[i].bps);

// Apply the update immediately
uint256 ray = conv.turn(bps);
if (id == "DSR") {
pot.file("dsr", ray);
} else if (id == "SSR") {
Expand All @@ -290,9 +193,7 @@ contract DSPC {
}
}

delete _batch;
eta = 0;
emit Zap(updates);
emit Set(updates);
}

// --- Getters ---
Expand All @@ -302,11 +203,4 @@ contract DSPC {
function cfgs(bytes32 id) external view returns (Cfg memory) {
return _cfgs[id];
}

/// @notice Get current pending batch
/// @return batch The array of pending rate updates
/// @return eta The timestamp when the batch becomes executable
function batch() external view returns (ParamChange[] memory, uint256) {
return (_batch, eta);
}
}
Loading

0 comments on commit 891164c

Please sign in to comment.