Skip to content

Commit

Permalink
Merge branch 'release/v4.10.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerEther committed Aug 25, 2024
2 parents 6ffef09 + 8514e14 commit a3a6060
Show file tree
Hide file tree
Showing 25 changed files with 565 additions and 59 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 4.10.0
### Prudentia
#### Controllers
- Add flag to enable or disable compute ahead logic: If enabled, the controller's computeRate function will calculate the rate on-the-fly with clamping. Otherwise, it will return the last stored rate.
- Add IonicRateController: A RateController that computes rates for Ionic tokens, accruing interest on the underlying tokens before pushing new rates.

## v4.9.1
### Prudentia
#### Controllers
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Adrastia Periphery

[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
![4015 out of 4015 tests passing](https://img.shields.io/badge/tests-4015/4015%20passing-brightgreen.svg?style=flat-square)
![4038 out of 4038 tests passing](https://img.shields.io/badge/tests-4038/4038%20passing-brightgreen.svg?style=flat-square)
![test-coverage 100%](https://img.shields.io/badge/test%20coverage-100%25-brightgreen.svg?style=flat-square)

Adrastia Periphery is a set of Solidity smart contracts that complement the [Adrastia Core](https://github.com/adrastia-oracle/adrastia-core) smart contracts.
Expand Down
5 changes: 4 additions & 1 deletion contracts/rates/ManagedRateController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ contract ManagedRateController is RateController, AccessControlEnumerable {

/**
* @notice Constructs the ManagedRateController contract.
* @param computeAhead_ True if the rates returned by computeRate should be computed on-the-fly with clamping;
* false if the returned rates should be the same as the last pushed rates (from the buffer).
* @param period_ The period for the rate controller.
* @param initialBufferCardinality_ The initial buffer cardinality for the rate controller.
* @param updatersMustBeEoa_ A flag indicating if updaters must be externally owned accounts.
*/
constructor(
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) RateController(period_, initialBufferCardinality_, updatersMustBeEoa_) {
) RateController(computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {
initializeRoles();
}

Expand Down
29 changes: 25 additions & 4 deletions contracts/rates/RateController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ abstract contract RateController is ERC165, HistoricalRates, IRateComputer, IUpd
/// @dev This is a security feature to prevent malicious contracts from updating rates.
bool public immutable updatersMustBeEoa;

/// @notice True if the rates returned by computeRate should be computed on-the-fly with clamping; false if the
/// returned rates should be the same as the last pushed rates (from the buffer).
bool public immutable computeAhead;

/// @notice Maps a token to its rate configuration.
mapping(address => RateConfig) internal rateConfigs;

Expand Down Expand Up @@ -82,14 +86,18 @@ abstract contract RateController is ERC165, HistoricalRates, IRateComputer, IUpd
error PauseStatusUnchanged(address token, bool paused);

/// @notice Creates a new rate controller.
/// @param computeAhead_ True if the rates returned by computeRate should be computed on-the-fly with clamping;
/// false if the returned rates should be the same as the last pushed rates (from the buffer).
/// @param period_ The period of the rate controller, in seconds. This is the frequency at which rates are updated.
/// @param initialBufferCardinality_ The initial capacity of the rate buffer.
/// @param updatersMustBeEoa_ True if all rate updaters must be EOA accounts; false otherwise.
constructor(
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) HistoricalRates(initialBufferCardinality_) {
computeAhead = computeAhead_;
period = period_;
updatersMustBeEoa = updatersMustBeEoa_;
}
Expand Down Expand Up @@ -213,11 +221,24 @@ abstract contract RateController is ERC165, HistoricalRates, IRateComputer, IUpd
}
}

/// @inheritdoc IRateComputer
/// @notice Computes the rate for a token. If computeAhead is true, the rate is computed on-the-fly with clamping;
/// otherwise, the rate is the same as the last pushed rate (from the buffer).
/// @param token The address of the token to compute the rate for.
/// @return rate The rate for the token.
function computeRate(address token) external view virtual override returns (uint64) {
(, uint64 newRate) = computeRateAndClamp(token);
if (computeAhead) {
(, uint64 newRate) = computeRateAndClamp(token);

return newRate;
return newRate;
} else {
BufferMetadata storage meta = rateBufferMetadata[token];
if (meta.size == 0) {
// We've never computed a rate, so revert.
revert InsufficientData(token, 0, 1);
}

return getLatestRate(token).current;
}
}

/// @inheritdoc IPeriodic
Expand Down Expand Up @@ -375,7 +396,7 @@ abstract contract RateController is ERC165, HistoricalRates, IRateComputer, IUpd
return rateBuffers[token][meta.end];
}

/// @notice Computes the rate for the given token.
/// @notice Computes the target rate for the given token (without clamping).
/// @dev This function calculates the rate for the specified token by summing its base rate
/// and the weighted rates of its components. The component rates are computed using the `computeRate`
/// function of each component and multiplied by the corresponding weight, then divided by 10,000.
Expand Down
5 changes: 4 additions & 1 deletion contracts/rates/controllers/CapController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,18 @@ abstract contract CapController is RateController {

/**
* @notice Constructs the CapController contract.
* @param computeAhead_ True if the rates returned by computeRate should be computed on-the-fly with clamping;
* false if the returned rates should be the same as the last pushed rates (from the buffer).
* @param period_ The period of the rate controller.
* @param initialBufferCardinality_ The initial cardinality of the rate buffers.
* @param updatersMustBeEoa_ Whether or not the updaters must be EOA.
*/
constructor(
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) RateController(period_, initialBufferCardinality_, updatersMustBeEoa_) {}
) RateController(computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {}

/**
* @notice Sets the change threshold for the specified token. When the rate changes by more than the threshold, an
Expand Down
5 changes: 4 additions & 1 deletion contracts/rates/controllers/ManagedCapController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ contract ManagedCapController is CapController, AccessControlEnumerable {

/**
* @notice Constructs the ManagedCapController contract.
* @param computeAhead_ True if the rates returned by computeRate should be computed on-the-fly with clamping;
* false if the returned rates should be the same as the last pushed rates (from the buffer).
* @param period_ The period for the rate controller.
* @param initialBufferCardinality_ The initial buffer cardinality for the rate controller.
* @param updatersMustBeEoa_ A flag indicating if updaters must be externally owned accounts.
*/
constructor(
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) CapController(period_, initialBufferCardinality_, updatersMustBeEoa_) {
) CapController(computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {
initializeRoles();
}

Expand Down
5 changes: 4 additions & 1 deletion contracts/rates/controllers/ManagedPidController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ contract ManagedPidController is PidController, AccessControlEnumerable {

/// @notice Constructs the ManagedPidController.
/// @param inputAndErrorOracle_ Oracle to provide input and error values.
/// @param computeAhead_ True if the rates returned by computeRate should be computed on-the-fly with clamping;
/// false if the returned rates should be the same as the last pushed rates (from the buffer).
/// @param period_ The period for the rate controller.
/// @param initialBufferCardinality_ Initial size of the buffer for rate storage.
/// @param updatersMustBeEoa_ Flag to determine if updaters must be externally owned accounts.
constructor(
ILiquidityOracle inputAndErrorOracle_,
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) PidController(inputAndErrorOracle_, period_, initialBufferCardinality_, updatersMustBeEoa_) {
) PidController(inputAndErrorOracle_, computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {
initializeRoles();
}

Expand Down
27 changes: 14 additions & 13 deletions contracts/rates/controllers/PidController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,18 @@ abstract contract PidController is RateController {

/// @notice Constructs the PidController.
/// @param inputAndErrorOracle_ Default oracle to provide input and error values.
/// @param computeAhead_ True if the rates returned by computeRate should be computed on-the-fly with clamping;
/// false if the returned rates should be the same as the last pushed rates (from the buffer).
/// @param period_ The period for the rate controller.
/// @param initialBufferCardinality_ Initial size of the buffer for rate storage.
/// @param updatersMustBeEoa_ Flag to determine if updaters must be externally owned accounts.
constructor(
ILiquidityOracle inputAndErrorOracle_,
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) RateController(period_, initialBufferCardinality_, updatersMustBeEoa_) {
) RateController(computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {
if (period_ == 0) revert InvalidPeriod(period_);

validateInputAndErrorOracle(inputAndErrorOracle_, true);
Expand Down Expand Up @@ -165,18 +168,6 @@ abstract contract PidController is RateController {
}
}

/// @inheritdoc RateController
/// @dev Returns the current rate (latest stored) for the token, reverting if the rate has never been computed.
function computeRate(address token) external view virtual override returns (uint64) {
BufferMetadata storage meta = rateBufferMetadata[token];
if (meta.size == 0) {
// We've never computed a rate, so revert.
revert InsufficientData(token, 0, 1);
}

return getLatestRate(token).current;
}

/// @inheritdoc RateController
/// @dev Updates are not needed if the PID config is uninitialized.
function needsUpdate(bytes memory data) public view virtual override returns (bool b) {
Expand Down Expand Up @@ -409,15 +400,21 @@ abstract contract PidController is RateController {

// Compute output
int256 output = pTerm + pidState.iTerm - dTerm;
// Store last values to be used in the next iteration computation
pidState.lastInput = input;
pidState.lastError = err;

// Set target to the output rate (before clamping) and clamp to the range [0, 2^64) (to fit inside uint64).
if (output < int256(0)) {
target = 0;
} else if (output >= int256(uint256(type(uint64).max))) {
target = type(uint64).max;
} else {
target = uint64(uint256(output));
}

// Clamp the output. Note that clampChange is false here but this parameter is ignored as we indicate this
// is the output rate, signaling to use the rate controller's main clamping function which has change clamping.
output = clampBigSignedRate(token, output, true, false, 0);
// Clamping the output returns a value in the range [0, 2^64), so we can safely cast it to uint64.
current = uint64(uint256(output));
Expand All @@ -430,6 +427,10 @@ abstract contract PidController is RateController {
return target;
}

function computeRateAndClamp(address token) internal view virtual override returns (uint64 target, uint64 newRate) {
(target, newRate, ) = computeNextPidRate(token);
}

/// @inheritdoc RateController
function updateAndCompute(address token) internal virtual override returns (uint64 target, uint64 current) {
PidState memory newPidState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ contract AaveRateController is RateController {
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) RateController(period_, initialBufferCardinality_, updatersMustBeEoa_) {
) RateController(false, period_, initialBufferCardinality_, updatersMustBeEoa_) {
aclManager = aclManager_;
}

Expand Down
11 changes: 10 additions & 1 deletion contracts/rates/controllers/proto/ionic/IonicPidController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ contract IonicPidController is ManagedPidController {
constructor(
IComptroller comptroller_,
ILiquidityOracle inputAndErrorOracle_,
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) ManagedPidController(inputAndErrorOracle_, period_, initialBufferCardinality_, updatersMustBeEoa_) {
)
ManagedPidController(
inputAndErrorOracle_,
computeAhead_,
period_,
initialBufferCardinality_,
updatersMustBeEoa_
)
{
comptroller = comptroller_;
}

Expand Down
45 changes: 45 additions & 0 deletions contracts/rates/controllers/proto/ionic/IonicRateController.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.13;

import "../../../ManagedRateController.sol";
import "../../../../vendor/ionic/IComptroller.sol";
import "../../../../vendor/ionic/ICToken.sol";

contract IonicRateController is ManagedRateController {
IComptroller public immutable comptroller;

error CTokenNotFound(address token);

error FailedToAccrueInterest(address token, address cToken, uint256 errorCode);

constructor(
IComptroller comptroller_,
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) ManagedRateController(computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {
comptroller = comptroller_;
}

/// @dev Overridden to accrue interest for the prior rate before pushing the new rate.
function push(address token, RateLibrary.Rate memory rate) internal virtual override {
// Try and accrue interest if we have a prior rate
if (rateBufferMetadata[token].size > 0) {
address cToken = comptroller.cTokensByUnderlying(token);
if (cToken == address(0)) {
// Note that this check is not applied for the first rate to allow for the initial rate to be set
// before the cToken is added to the comptroller.
revert CTokenNotFound(token);
}

// Accrue interest for the prior rate before pushing the new rate
uint256 accrueCode = ICToken(cToken).accrueInterest();
if (accrueCode != 0) {
revert FailedToAccrueInterest(token, cToken, accrueCode);
}
}

super.push(token, rate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@ import "../../../../vendor/truefi/IAutomatedLineOfCredit.sol";
contract TrueFiAlocPidController is ManagedPidController {
constructor(
ILiquidityOracle inputAndErrorOracle_,
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) ManagedPidController(inputAndErrorOracle_, period_, initialBufferCardinality_, updatersMustBeEoa_) {}
)
ManagedPidController(
inputAndErrorOracle_,
computeAhead_,
period_,
initialBufferCardinality_,
updatersMustBeEoa_
)
{}

/// @dev Overridden to accrue interest for the prior rate before pushing the new rate.
function push(address alocAddress, RateLibrary.Rate memory rate) internal virtual override {
Expand Down
3 changes: 2 additions & 1 deletion contracts/test/rates/RateControllerStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ contract RateControllerStub is ManagedRateController {
mapping(address => OnPauseCall) public onPauseCalls;

constructor(
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) ManagedRateController(period_, initialBufferCardinality_, updatersMustBeEoa_) {}
) ManagedRateController(computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {}

function stubPush(address token, uint64 target, uint64 current, uint32 timestamp) public {
RateLibrary.Rate memory rate;
Expand Down
3 changes: 2 additions & 1 deletion contracts/test/rates/controllers/CapControllerStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ contract CapControllerStub is ManagedCapController {
Config public config;

constructor(
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) ManagedCapController(period_, initialBufferCardinality_, updatersMustBeEoa_) {}
) ManagedCapController(computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {}

function overrideNeedsUpdate(bool overridden, bool needsUpdate_) public {
config.needsUpdateOverridden = overridden;
Expand Down
3 changes: 2 additions & 1 deletion contracts/test/rates/controllers/PidControllerStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ contract PidControllerStub is ManagedPidController, InputAndErrorAccumulatorStub
mapping(address => OnPauseCall) public onPauseCalls;

constructor(
bool computeAhead_,
uint32 period_,
uint8 initialBufferCardinality_,
bool updatersMustBeEoa_
) ManagedPidController(this, period_, initialBufferCardinality_, updatersMustBeEoa_) {}
) ManagedPidController(this, computeAhead_, period_, initialBufferCardinality_, updatersMustBeEoa_) {}

function canUpdate(
bytes memory data
Expand Down
Loading

0 comments on commit a3a6060

Please sign in to comment.