Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compoundv3 USDT Plugin #1213

Merged
merged 23 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface ITokens {
cUSDCv3?: string
wcUSDCv3?: string
cUSDbCv3?: string
cUSDTv3?: string
ONDO?: string
sFRAX?: string
sDAI?: string
Expand Down Expand Up @@ -233,6 +234,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
apxETH: '0x9Ba021B0a9b958B5E75cE9f6dff97C7eE52cb3E6',
cUSDCv3: '0xc3d688B66703497DAA19211EEdff47f25384cdc3',
wcUSDCv3: '0x27F2f159Fe990Ba83D57f39Fd69661764BEbf37a',
cUSDTv3: '0x3Afdc9BCA9213A35503b077a6072F3D0d5AB0840',
ONDO: '0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3',
sFRAX: '0xA663B02CF0a4b149d2aD41910CB81e23e1c41c32',
sDAI: '0x83f20f44975d03b1b09e64809b757c47f942beea',
Expand Down Expand Up @@ -553,6 +555,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
USDC: '0xaf88d065e77c8cc2239327c5edb3a432268e5831',
USDT: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9',
cUSDCv3: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf',
cUSDTv3: '0xd98Be00b5D27fc98112BdE293e487f8D4cA57d07',
WETH: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1',
WBTC: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
aArbUSDCn: '0x724dc807b04555b71ed48a6896b6f41593b8c637', // aArbUSDCn wraps USDC!
Expand Down
4 changes: 2 additions & 2 deletions contracts/facade/FacadeMonitor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "../interfaces/IRToken.sol";
import "../libraries/Fixed.sol";
import "../p1/RToken.sol";
import "../plugins/assets/compoundv2/DEPRECATED_CTokenWrapper.sol";
import "../plugins/assets/compoundv3/ICusdcV3Wrapper.sol";
import "../plugins/assets/compoundv3/ICFiatV3Wrapper.sol";
import "../plugins/assets/stargate/StargateRewardableWrapper.sol";
import { StaticATokenV3LM } from "../plugins/assets/aave-v3/vendor/StaticATokenV3LM.sol";
import "../plugins/assets/morpho-aave/MorphoAaveV2TokenisedDeposit.sol";
Expand Down Expand Up @@ -174,7 +174,7 @@ contract FacadeMonitor is Initializable, OwnableUpgradeable, UUPSUpgradeable, IF
backingBalance = (cTokenBal * exchangeRate) / 1e18;
availableLiquidity = underlying.balanceOf(address(cToken));
} else if (collType == CollPluginType.COMPOUND_V3) {
ICusdcV3Wrapper cTokenV3Wrapper = ICusdcV3Wrapper(address(erc20));
ICFiatV3Wrapper cTokenV3Wrapper = ICFiatV3Wrapper(address(erc20));
CometInterface cTokenV3 = CometInterface(address(cTokenV3Wrapper.underlyingComet()));
IERC20 underlying = IERC20(cTokenV3.baseToken());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,62 +5,72 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./vendor/CometInterface.sol";
import "./WrappedERC20.sol";
import "./vendor/ICometRewards.sol";
import "./ICusdcV3Wrapper.sol";
import "./ICFiatV3Wrapper.sol";
import "./CometHelpers.sol";

/**
* @title CusdcV3Wrapper
* @notice Wrapper for cUSDCV3 / COMET that acts as a stable-balance ERC20, instead of rebasing
* token. {comet} will be used as the unit for the underlying token, and {wComet} will be used
* as the unit for wrapped tokens.
* @title CFiatV3Wrapper
* @notice Wrapper for Compound V3 fiat coins such as cUSDCv3, cUSDTv3 / COMET that acts
* as a stable-balance ERC20, instead of rebasing token. {comet} will be used as the unit
* for the underlying token, and {wComet} will be used as the unit for wrapped tokens.
*/
contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {
contract CFiatV3Wrapper is ICFiatV3Wrapper, WrappedERC20, CometHelpers {
using SafeERC20 for IERC20;

/// From cUSDCv3, used in principal <> present calculations
uint256 public constant TRACKING_INDEX_SCALE = 1e15;
/// From cUSDCv3, scaling factor for USDC rewards
uint256 public constant RESCALE_FACTOR = 1e12;

CometInterface public immutable underlyingComet;
ICometRewards public immutable rewardsAddr;
IERC20 public immutable rewardERC20;
uint256 public immutable trackingIndexScale;
uint256 public immutable rescaleFactor;
uint256 internal immutable accrualDescaleFactor;
uint256 public immutable multiplier;
uint8 internal immutable cometDecimals;

mapping(address => uint64) public baseTrackingIndex; // uint64 for consistency with CometHelpers
mapping(address => uint256) public baseTrackingAccrued; // uint256 to avoid overflow in L:199
mapping(address => uint256) public rewardsClaimed;

constructor(
address cusdcv3,
address ctokenv3,
address rewardsAddr_,
address rewardERC20_
) WrappedERC20("Wrapped cUSDCv3", "wcUSDCv3") {
if (cusdcv3 == address(0)) revert ZeroAddress();
address rewardERC20_,
string memory name,
string memory symbol,
uint256 rewardMultiplier
) WrappedERC20(name, symbol) {
if (ctokenv3 == address(0)) revert ZeroAddress();

rewardsAddr = ICometRewards(rewardsAddr_);
rewardERC20 = IERC20(rewardERC20_);
underlyingComet = CometInterface(cusdcv3);
underlyingComet = CometInterface(ctokenv3);
cometDecimals = underlyingComet.decimals();
// for principal <> present calculations
trackingIndexScale = underlyingComet.trackingIndexScale();
// scaling factor for rewards
rescaleFactor = 10**(18 - cometDecimals);
accrualDescaleFactor = 10**(cometDecimals - 6);
multiplier = rewardMultiplier;
}

/// @return number of decimals
function decimals() public pure override(IERC20Metadata, WrappedERC20) returns (uint8) {
return 6;
function decimals() public view override(IERC20Metadata, WrappedERC20) returns (uint8) {
return cometDecimals;
}

/// @param amount {Comet} The amount of cUSDCv3 to deposit
/// @param amount {Comet} The amount of cTokenV3 to deposit
function deposit(uint256 amount) external {
_deposit(msg.sender, msg.sender, msg.sender, amount);
}

/// @param dst The dst to deposit into
/// @param amount {Comet} The amount of cUSDCv3 to deposit
/// @param amount {Comet} The amount of cTokenV3 to deposit
function depositTo(address dst, uint256 amount) external {
_deposit(msg.sender, msg.sender, dst, amount);
}

/// @param src The address to deposit from
/// @param dst The address to deposit to
/// @param amount {Comet} The amount of cUSDCv3 to deposit
/// @param amount {Comet} The amount of cTokenV3 to deposit
function depositFrom(
address src,
address dst,
Expand All @@ -70,11 +80,11 @@ contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {
}

/// Only called internally to run the deposit logic
/// Takes `amount` fo cUSDCv3 from `src` and deposits to `dst` account in the wrapper.
/// Takes `amount` fo cTokenV3 from `src` and deposits to `dst` account in the wrapper.
/// @param operator The address calling the contract (msg.sender)
/// @param src The address to deposit from
/// @param dst The address to deposit to
/// @param amount {Comet} The amount of cUSDCv3 to deposit
/// @param amount {Comet} The amount of cTokenv3 to deposit
function _deposit(
address operator,
address src,
Expand Down Expand Up @@ -102,20 +112,20 @@ contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {
_mint(dst, uint104(wrapperPostPrinc - wrapperPrePrinc));
}

/// @param amount {Comet} The amount of cUSDCv3 to withdraw
/// @param amount {Comet} The amount of cTokenV3 to withdraw
function withdraw(uint256 amount) external {
_withdraw(msg.sender, msg.sender, msg.sender, amount);
}

/// @param dst The address to withdraw cUSDCv3 to
/// @param amount {Comet} The amount of cUSDCv3 to withdraw
/// @param dst The address to withdraw cTokenv3 to
/// @param amount {Comet} The amount of cTokenv3 to withdraw
function withdrawTo(address dst, uint256 amount) external {
_withdraw(msg.sender, msg.sender, dst, amount);
}

/// @param src The address to withdraw from
/// @param dst The address to withdraw cUSDCv3 to
/// @param amount {Comet} The amount of cUSDCv3 to withdraw
/// @param dst The address to withdraw cTokenv3 to
/// @param amount {Comet} The amount of cTokenv3 to withdraw
function withdrawFrom(
address src,
address dst,
Expand All @@ -125,12 +135,12 @@ contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {
}

/// Internally called to run the withdraw logic
/// Withdraws `amount` cUSDCv3 from `src` account in the wrapper and sends to `dst`
/// Withdraws `amount` cTokenv3 from `src` account in the wrapper and sends to `dst`
/// @dev Rounds conservatively so as not to over-withdraw from the wrapper
/// @param operator The address calling the contract (msg.sender)
/// @param src The address to withdraw from
/// @param dst The address to withdraw cUSDCv3 to
/// @param amount {Comet} The amount of cUSDCv3 to withdraw
/// @param dst The address to withdraw cTokenv3 to
/// @param amount {Comet} The amount of cTokenv3 to withdraw
function _withdraw(
address operator,
address src,
Expand Down Expand Up @@ -195,7 +205,7 @@ contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {

accrueAccount(src);
uint256 claimed = rewardsClaimed[src];
uint256 accrued = baseTrackingAccrued[src] * RESCALE_FACTOR;
uint256 accrued = (baseTrackingAccrued[src] * rescaleFactor * multiplier) / 1e18;
uint256 owed;
if (accrued > claimed) {
owed = accrued - claimed;
Expand All @@ -210,20 +220,20 @@ contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {
emit RewardsClaimed(rewardERC20, owed);
}

/// Accure the cUSDCv3 account of the wrapper
/// Accure the cTokenv3 account of the wrapper
function accrue() public {
underlyingComet.accrueAccount(address(this));
}

/// @param account The address to accrue, first in cUSDCv3, then locally
/// @param account The address to accrue, first in cTokenv3, then locally
function accrueAccount(address account) public {
underlyingComet.accrueAccount(address(this));
accrueAccountRewards(account);
}

/// Get the balance of cUSDCv3 that is represented by the `accounts` wrapper value.
/// @param account The address to calculate the cUSDCv3 balance of
/// @return {Comet} The cUSDCv3 balance that `account` holds in the wrapper
/// Get the balance of cTokenv3 that is represented by the `accounts` wrapper value.
/// @param account The address to calculate the cTokenv3 balance of
/// @return {Comet} The cTokenv3 balance that `account` holds in the wrapper
function underlyingBalanceOf(address account) public view returns (uint256) {
uint256 balance = balanceOf(account);
if (balance == 0) {
Expand All @@ -239,7 +249,7 @@ contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {
}

/// @param amount The value of {wComet} to convert to {Comet}
/// @return {Comet} The amount of cUSDCv3 represented by `amount of {wComet}
/// @return {Comet} The amount of cTokenv3 represented by `amount of {wComet}
function convertStaticToDynamic(uint104 amount) public view returns (uint256) {
(uint64 baseSupplyIndex, ) = getUpdatedSupplyIndicies();
return presentValueSupply(baseSupplyIndex, amount);
Expand All @@ -260,10 +270,11 @@ contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {
uint256 indexDelta = uint256(trackingSupplyIndex - baseTrackingIndex[account]);
uint256 newBaseTrackingAccrued = baseTrackingAccrued[account] +
(safe104(balanceOf(account)) * indexDelta) /
TRACKING_INDEX_SCALE;
trackingIndexScale /
accrualDescaleFactor;

uint256 claimed = rewardsClaimed[account];
uint256 accrued = newBaseTrackingAccrued * RESCALE_FACTOR;
uint256 accrued = (newBaseTrackingAccrued * rescaleFactor * multiplier) / 1e18;
uint256 owed = accrued > claimed ? accrued - claimed : 0;

return owed;
Expand All @@ -289,7 +300,10 @@ contract CusdcV3Wrapper is ICusdcV3Wrapper, WrappedERC20, CometHelpers {
(, uint64 trackingSupplyIndex) = getSupplyIndices();
uint256 indexDelta = uint256(trackingSupplyIndex - baseTrackingIndex[account]);

baseTrackingAccrued[account] += (safe104(accountBal) * indexDelta) / TRACKING_INDEX_SCALE;
baseTrackingAccrued[account] +=
(safe104(accountBal) * indexDelta) /
trackingIndexScale /
accrualDescaleFactor;
baseTrackingIndex[account] = trackingSupplyIndex;
}

Expand Down
14 changes: 7 additions & 7 deletions contracts/plugins/assets/compoundv3/CTokenV3Collateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "../../../libraries/Fixed.sol";
import "../AppreciatingFiatCollateral.sol";
import "../OracleLib.sol";
import "./ICusdcV3Wrapper.sol";
import "./ICFiatV3Wrapper.sol";
import "./vendor/IComet.sol";

/**
* @title CTokenV3Collateral
* @notice Collateral plugin for Compound V3,
* tok = wcUSDC
* ref = USDC
* tok = wcToken (wcUSDC, wcUSDT, etc)
* ref = USDC/USDT/etc
* tar = USD
* UoA = USD
*/
Expand All @@ -31,8 +31,8 @@ contract CTokenV3Collateral is AppreciatingFiatCollateral {
AppreciatingFiatCollateral(config, revenueHiding)
{
require(config.defaultThreshold != 0, "defaultThreshold zero");
comp = ICusdcV3Wrapper(address(config.erc20)).rewardERC20();
comet = IComet(address(ICusdcV3Wrapper(address(erc20)).underlyingComet()));
comp = ICFiatV3Wrapper(address(config.erc20)).rewardERC20();
comet = IComet(address(ICFiatV3Wrapper(address(erc20)).underlyingComet()));
cometDecimals = comet.decimals();
}

Expand All @@ -46,7 +46,7 @@ contract CTokenV3Collateral is AppreciatingFiatCollateral {
function underlyingRefPerTok() public view virtual override returns (uint192) {
return
shiftl_toFix(
ICusdcV3Wrapper(address(erc20)).exchangeRate(),
ICFiatV3Wrapper(address(erc20)).exchangeRate(),
-int8(cometDecimals),
FLOOR
);
Expand All @@ -55,7 +55,7 @@ contract CTokenV3Collateral is AppreciatingFiatCollateral {
/// Refresh exchange rates and update default status.
/// @dev Should not need to override: can handle collateral with variable refPerTok()
function refresh() public virtual override {
ICusdcV3Wrapper(address(erc20)).accrue();
ICFiatV3Wrapper(address(erc20)).accrue();

CollateralStatus oldStatus = status();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "./vendor/CometInterface.sol";
import "./IWrappedERC20.sol";
import "../../../interfaces/IRewardable.sol";

interface ICusdcV3Wrapper is IWrappedERC20, IRewardable {
interface ICFiatV3Wrapper is IWrappedERC20, IRewardable {
struct UserBasic {
uint104 principal;
uint64 baseTrackingIndex;
Expand Down
2 changes: 1 addition & 1 deletion contracts/plugins/assets/compoundv3/WrappedERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ abstract contract WrappedERC20 is IWrappedERC20 {
/**
* @dev Returns the decimals places of the token.
*/
function decimals() public pure virtual returns (uint8) {
function decimals() public view virtual returns (uint8) {
return 18;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface ICometRewards {
address token;
uint64 rescaleFactor;
bool shouldUpscale;
uint256 multiplier;
}

struct RewardOwed {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "../assets/compoundv3/CusdcV3Wrapper.sol";
import "../assets/compoundv3/ICusdcV3Wrapper.sol";
import "../assets/compoundv3/CFiatV3Wrapper.sol";
import "../assets/compoundv3/ICFiatV3Wrapper.sol";

interface ICusdcV3WrapperMock is ICusdcV3Wrapper {
interface ICFiatV3WrapperMock is ICFiatV3Wrapper {
function setMockExchangeRate(bool setMock, uint256 mockValue) external;
}

contract CusdcV3WrapperMock {
contract CFiatV3WrapperMock {
uint256[20] private __gap;
address internal mockTarget;
mapping(bytes4 => bool) internal isMocking;
Expand All @@ -33,7 +33,7 @@ contract CusdcV3WrapperMock {
if (isMocking[this.exchangeRate.selector]) {
return mockExchangeRate_;
} else {
return CusdcV3Wrapper(mockTarget).exchangeRate();
return CFiatV3Wrapper(mockTarget).exchangeRate();
}
}

Expand Down
8 changes: 5 additions & 3 deletions scripts/addresses/1-tmp-assets-collateral.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
"ETHx": "0x73a36258E6A48D0095D1997Fec7F51e191B4Ec81",
"apxETH": "0x05ffDaAA2aF48e1De1CE34d633db018a28e3B3F5",
"sUSDe": "0x35081Ca24319835e5f759163F7e75eaB753e0b7E",
"pyUSD": "0xa5cde4fB1132daF8f4a0D3140859271208d944E9"
"pyUSD": "0xa5cde4fB1132daF8f4a0D3140859271208d944E9",
"cUSDTv3": "0x1B2256a88Bb9F2E54cC8D355D3161a2F069a320B"
},
"erc20s": {
"stkAAVE": "0x4da27a545c0c5B758a6BA100e3a049001de870f5",
Expand Down Expand Up @@ -125,6 +126,7 @@
"ETHx": "0xA35b1B31Ce002FBF2058D22F30f95D405200A15b",
"apxETH": "0x9Ba021B0a9b958B5E75cE9f6dff97C7eE52cb3E6",
"sUSDe": "0x9D39A5DE30e57443BfF2A8307A4256c8797A3497",
"pyUSD": "0x6c3ea9036406852006290770bedfcaba0e23a0e8"
"pyUSD": "0x6c3ea9036406852006290770bedfcaba0e23a0e8",
"cUSDTv3": "0xEB74EC1d4C1DAB412D5d6674F6833FD19d3118Ce"
}
}
}
Loading
Loading