Skip to content

Commit

Permalink
Merge branch 'release/v4.11.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerEther committed Aug 27, 2024
2 parents a3a6060 + ed28567 commit e58afa2
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v4.11.0
### Updaters
- Add IonicInterestRateUpdater: A contract that accrues interest to Ionic cTokens before updating the rate controller.

## 4.10.0
### Prudentia
#### Controllers
Expand Down
12 changes: 12 additions & 0 deletions contracts/test/rates/RateControllerStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ contract RateControllerStub is ManagedRateController {
bool needsUpdate;
bool canComputeNextRateOverridden;
bool canComputeNextRate;
bool canUpdateOverridden;
bool canUpdate;
}

struct OnPauseCall {
Expand Down Expand Up @@ -50,11 +52,21 @@ contract RateControllerStub is ManagedRateController {
config.needsUpdate = needsUpdate_;
}

function overrideCanUpdate(bool overridden, bool canUpdate_) public {
config.canUpdateOverridden = overridden;
config.canUpdate = canUpdate_;
}

function overrideCanComputeNextRate(bool overridden, bool canComputeNextRate_) public {
config.canComputeNextRateOverridden = overridden;
config.canComputeNextRate = canComputeNextRate_;
}

function canUpdate(bytes memory data) public view virtual override returns (bool) {
if (config.canUpdateOverridden) return config.canUpdate;
else return super.canUpdate(data);
}

function needsUpdate(bytes memory data) public view virtual override returns (bool) {
if (config.needsUpdateOverridden) return config.needsUpdate;
else return super.needsUpdate(data);
Expand Down
10 changes: 9 additions & 1 deletion contracts/test/vendor/ionic/IonicCTokenStub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ contract IonicCTokenStub is ICToken {

uint256 internal _accrueInterestReturnCode;

address internal _rateComputer;

event InterestAccrued(uint64 rate);

constructor(address underlying_) {
Expand Down Expand Up @@ -51,6 +53,10 @@ contract IonicCTokenStub is ICToken {
_accrueInterestReturnCode = accrueInterestReturnCode_;
}

function stubSetRateComputer(address rateComputer_) external {
_rateComputer = rateComputer_;
}

function getTotalUnderlyingSupplied() external view override returns (uint256) {
return _totalUnderlyingSupplied;
}
Expand Down Expand Up @@ -80,8 +86,10 @@ contract IonicCTokenStub is ICToken {
}

function accrueInterest() external override returns (uint256) {
address computerAddress = _rateComputer == address(0) ? msg.sender : _rateComputer;

// Extract the rate from the message sender
uint64 rate = IRateComputer(msg.sender).computeRate(_underlying);
uint64 rate = IRateComputer(computerAddress).computeRate(_underlying);

// Emit an event
emit InterestAccrued(rate);
Expand Down
100 changes: 100 additions & 0 deletions contracts/updaters/IonicInterestRateUpdater.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.13;

import {IUpdateable} from "@adrastia-oracle/adrastia-core/contracts/interfaces/IUpdateable.sol";
import {RateController} from "../rates/RateController.sol";
import "../vendor/ionic/IComptroller.sol";
import "../vendor/ionic/ICToken.sol";

/**
* @title IonicInterestRateUpdater
* @notice A contract that accrues interest to Ionic cTokens before updating interest rates.
*/
contract IonicInterestRateUpdater is IUpdateable {
/**
* @notice The comptroller contract.
*/
IComptroller public immutable comptroller;

/**
* @notice The rate controller contract.
*/
RateController public immutable rateController;

/**
* An error thrown when the cToken for a token is not found.
* @param token The token address.
*/
error CTokenNotFound(address token);

/**
* An error thrown when failing to accrue interest for a token.
* @param token The token address.
* @param cToken The cToken address.
* @param errorCode The error code returned by the cToken.
*/
error FailedToAccrueInterest(address token, address cToken, uint256 errorCode);

/**
* @notice Constructs a new IonicInterestRateUpdater instance.
* @param comptroller_ The address of the comptroller contract.
* @param rateController_ The address of the rate controller contract.
*/
constructor(IComptroller comptroller_, RateController rateController_) {
comptroller = comptroller_;
rateController = rateController_;
}

/// @inheritdoc IUpdateable
function canUpdate(bytes memory data) public view virtual override returns (bool b) {
return rateController.canUpdate(data);
}

/// @inheritdoc IUpdateable
function needsUpdate(bytes memory data) public view virtual override returns (bool b) {
return rateController.needsUpdate(data);
}

/// @inheritdoc IUpdateable
function lastUpdateTime(bytes memory data) public view virtual override returns (uint256) {
return rateController.lastUpdateTime(data);
}

/// @inheritdoc IUpdateable
function timeSinceLastUpdate(bytes memory data) public view virtual override returns (uint256) {
return rateController.timeSinceLastUpdate(data);
}

/// @inheritdoc IUpdateable
function update(bytes memory data) public virtual override returns (bool b) {
address token = abi.decode(data, (address));
accrueInterest(token);

return rateController.update(data);
}

/**
* @notice Accrues interest for a token, if the rate controller has a prior rate for the token.
* @dev Reverts if we're unable to accrue interest for the token.
* @param token The token address.
*/
function accrueInterest(address token) internal {
// Try and accrue interest if we have a prior rate
// This allows us to create new cTokens and set the initial rate before the cToken is added to the comptroller.
// Otherwise, the cToken will be bricked
if (rateController.getRatesCount(token) > 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);
}
}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adrastia-oracle/adrastia-periphery",
"version": "4.10.0",
"version": "4.11.0",
"main": "index.js",
"author": "TRILEZ SOFTWARE INC.",
"license": "BUSL-1.1",
Expand Down
16 changes: 16 additions & 0 deletions src/time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { ethers } = require("hardhat");

async function currentBlockTimestamp() {
const currentBlockNumber = await ethers.provider.getBlockNumber();

return await blockTimestamp(currentBlockNumber);
}

async function blockTimestamp(blockNum) {
return (await ethers.provider.getBlock(blockNum)).timestamp;
}

module.exports = {
currentBlockTimestamp,
blockTimestamp,
};
Loading

0 comments on commit e58afa2

Please sign in to comment.