Skip to content

Commit

Permalink
Merge pull request #32 from Gearbox-protocol/configuration-tests
Browse files Browse the repository at this point in the history
feat: add `DefaultDegenNFT`, fix some bugs and add configuration tests
  • Loading branch information
lekhovitsky authored Feb 10, 2025
2 parents 89ed0ea + 32eed86 commit 3d401ed
Show file tree
Hide file tree
Showing 32 changed files with 2,552 additions and 73 deletions.
33 changes: 10 additions & 23 deletions contracts/factories/CreditFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import {ICreditFacadeV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IC
import {ICreditManagerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol";
import {IPoolV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPoolV3.sol";

import {ICreditFactory, CreditFacadeParams} from "../interfaces/factories/ICreditFactory.sol";
import {ICreditConfigureActions} from "../interfaces/factories/ICreditConfigureActions.sol";
import {ICreditFactory} from "../interfaces/factories/ICreditFactory.sol";
import {
CreditFacadeParams,
CreditManagerParams,
ICreditConfigureActions
} from "../interfaces/factories/ICreditConfigureActions.sol";
import {ICreditEmergencyConfigureActions} from "../interfaces/factories/ICreditEmergencyConfigureActions.sol";
import {IFactory} from "../interfaces/factories/IFactory.sol";
import {IContractsRegister} from "../interfaces/IContractsRegister.sol";
Expand All @@ -34,19 +38,6 @@ import {

import {AbstractFactory} from "./AbstractFactory.sol";

struct CreditManagerParams {
uint8 maxEnabledTokens;
uint16 feeInterest;
uint16 feeLiquidation;
uint16 liquidationPremium;
uint16 feeLiquidationExpired;
uint16 liquidationPremiumExpired;
uint128 minDebt;
uint128 maxDebt;
string name;
DeployParams accountFactoryParams;
}

contract CreditFactory is AbstractFactory, ICreditFactory {
/// @notice Contract version
uint256 public constant override version = 3_10;
Expand All @@ -57,9 +48,6 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
/// @notice Address of the bot list contract
address public immutable botList;

/// @notice Address of the WETH token
address public immutable weth;

error DegenNFTIsNotRegisteredException(address degenNFT);

error TargetContractIsNotAllowedException(address targetCotnract);
Expand All @@ -68,7 +56,6 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
/// @param addressProvider_ Address provider contract address
constructor(address addressProvider_) AbstractFactory(addressProvider_) {
botList = _getAddressOrRevert(AP_BOT_LIST, NO_VERSION_CONTROL);
weth = _tryGetAddress(AP_WETH_TOKEN, NO_VERSION_CONTROL);
}

// ---------- //
Expand Down Expand Up @@ -314,8 +301,7 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
}

function _deployCreditConfigurator(address marketConfigurator, address creditManager) internal returns (address) {
address acl = IMarketConfigurator(marketConfigurator).acl();
bytes memory constructorParams = abi.encode(acl, creditManager);
bytes memory constructorParams = abi.encode(creditManager);

return _deployLatestPatch({
contractType: AP_CREDIT_CONFIGURATOR,
Expand All @@ -329,7 +315,6 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
internal
returns (address)
{
address acl = IMarketConfigurator(marketConfigurator).acl();
address contractsRegister = IMarketConfigurator(marketConfigurator).contractsRegister();
address lossPolicy = IContractsRegister(contractsRegister).getLossPolicy(ICreditManagerV3(creditManager).pool());

Expand All @@ -346,8 +331,10 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
botList_ = ICreditFacadeV3(prevCreditFacade).botList();
}

address weth = _tryGetAddress(AP_WETH_TOKEN, NO_VERSION_CONTROL);

bytes memory constructorParams =
abi.encode(acl, creditManager, lossPolicy, botList_, weth, params.degenNFT, params.expirable);
abi.encode(creditManager, lossPolicy, botList_, weth, params.degenNFT, params.expirable);

return _deployLatestPatch({
contractType: AP_CREDIT_FACADE,
Expand Down
7 changes: 1 addition & 6 deletions contracts/factories/LossPolicyFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,7 @@ contract LossPolicyFactory is AbstractMarketFactory, ILossPolicyFactory {
onlyMarketConfigurators
returns (DeployResult memory)
{
if (params.postfix == "ALIASED") {
address decodedPool = abi.decode(params.constructorParams, (address));
if (decodedPool != pool) revert InvalidConstructorParamsException();
} else {
_validateDefaultConstructorParams(pool, params.constructorParams);
}
_validateDefaultConstructorParams(pool, params.constructorParams);

address lossPolicy = _deployLatestPatch({
contractType: _getContractType(DOMAIN_LOSS_POLICY, params.postfix),
Expand Down
15 changes: 8 additions & 7 deletions contracts/factories/PriceOracleFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ contract PriceOracleFactory is AbstractMarketFactory, IPriceOracleFactory {
address priceOracle = _priceOracle(pool);

bytes4 selector = bytes4(callData);
if (selector == IPriceOracleConfigureActions.setPriceFeed.selector) {
if (selector == IPriceOracleEmergencyConfigureActions.setPriceFeed.selector) {
(address token, address priceFeed) = abi.decode(callData[4:], (address, address));
_validatePriceFeed(pool, token, priceFeed, true);
if (block.timestamp < IPriceFeedStore(priceFeedStore).getAllowanceTimestamp(token, priceFeed) + 1 days) {
Expand Down Expand Up @@ -209,6 +209,7 @@ contract PriceOracleFactory is AbstractMarketFactory, IPriceOracleFactory {
}

function _getPriceFeed(address priceOracle, address token, bool reserve) internal view returns (address) {
// FIXME: doesn't work like that for price oracle v3.0
return reserve
? IPriceOracleV3(priceOracle).reservePriceFeeds(token)
: IPriceOracleV3(priceOracle).priceFeeds(token);
Expand All @@ -227,8 +228,8 @@ contract PriceOracleFactory is AbstractMarketFactory, IPriceOracleFactory {

Call[] memory calls = CallBuilder.build(
reserve
? _setReservePriceFeed(priceOracle, token, priceFeed, stalenessPeriod)
: _setPriceFeed(priceOracle, token, priceFeed, stalenessPeriod)
? _setReservePriceFeedCall(priceOracle, token, priceFeed, stalenessPeriod)
: _setPriceFeedCall(priceOracle, token, priceFeed, stalenessPeriod)
);
return _addUpdatableFeeds(priceOracle, priceFeed, calls);
}
Expand All @@ -239,7 +240,7 @@ contract PriceOracleFactory is AbstractMarketFactory, IPriceOracleFactory {
returns (Call[] memory)
{
try IUpdatablePriceFeed(priceFeed).updatable() returns (bool updatable) {
if (updatable) calls = calls.append(_addUpdatablePriceFeed(priceOracle, priceFeed));
if (updatable) calls = calls.append(_addUpdatablePriceFeedCall(priceOracle, priceFeed));
} catch {}
address[] memory underlyingFeeds = IPriceFeed(priceFeed).getUnderlyingFeeds();
uint256 numFeeds = underlyingFeeds.length;
Expand All @@ -249,15 +250,15 @@ contract PriceOracleFactory is AbstractMarketFactory, IPriceOracleFactory {
return calls;
}

function _setPriceFeed(address priceOracle, address token, address priceFeed, uint32 stalenessPeriod)
function _setPriceFeedCall(address priceOracle, address token, address priceFeed, uint32 stalenessPeriod)
internal
pure
returns (Call memory)
{
return Call(priceOracle, abi.encodeCall(IPriceOracleV3.setPriceFeed, (token, priceFeed, stalenessPeriod)));
}

function _setReservePriceFeed(address priceOracle, address token, address priceFeed, uint32 stalenessPeriod)
function _setReservePriceFeedCall(address priceOracle, address token, address priceFeed, uint32 stalenessPeriod)
internal
pure
returns (Call memory)
Expand All @@ -266,7 +267,7 @@ contract PriceOracleFactory is AbstractMarketFactory, IPriceOracleFactory {
Call(priceOracle, abi.encodeCall(IPriceOracleV3.setReservePriceFeed, (token, priceFeed, stalenessPeriod)));
}

function _addUpdatablePriceFeed(address priceOracle, address priceFeed) internal pure returns (Call memory) {
function _addUpdatablePriceFeedCall(address priceOracle, address priceFeed) internal pure returns (Call memory) {
return Call(priceOracle, abi.encodeCall(IPriceOracleV3.addUpdatablePriceFeed, (priceFeed)));
}
}
2 changes: 2 additions & 0 deletions contracts/global/BytecodeRepository.sol
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ contract BytecodeRepository is ImmutableOwnableTrait, SanityCheckTrait, IBytecod
/// @param majorVersion Major version number
/// @return uint256 Latest minor version number
function getLatestMinorVersion(bytes32 _contractType, uint256 majorVersion) external view returns (uint256) {
majorVersion -= majorVersion % 100;
return latestMinorVersion[_contractType][majorVersion];
}

Expand All @@ -510,6 +511,7 @@ contract BytecodeRepository is ImmutableOwnableTrait, SanityCheckTrait, IBytecod
/// @param minorVersion Minor version number
/// @return uint256 Latest patch version number
function getLatestPatchVersion(bytes32 _contractType, uint256 minorVersion) external view returns (uint256) {
minorVersion -= minorVersion % 10;
return latestPatchVersion[_contractType][minorVersion];
}

Expand Down
143 changes: 143 additions & 0 deletions contracts/helpers/DefaultDegenNFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";

import {IDegenNFT} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IDegenNFT.sol";
import {ICreditFacadeV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditFacadeV3.sol";
import {ICreditManagerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol";

import {IContractsRegister} from "../interfaces/IContractsRegister.sol";
import {IMarketConfigurator} from "../interfaces/IMarketConfigurator.sol";

contract DefaultDegenNFT is ERC721, IDegenNFT {
uint256 public constant override version = 3_10;
bytes32 public constant override contractType = "DEGEN_NFT::DEFAULT";

address public immutable marketConfigurator;
address public immutable contractsRegister;
address public minter;

uint256 public totalSupply;

string public baseURI;

event SetMinter(address indexed newMinter);

error CallerIsNotAdminException(address caller);
error CallerIsNotCreditFacadeOrEmergencyAdminException(address caller);
error CallerIsNotMinterException(address caller);
error NotImplementedException();

modifier onlyAdmin() {
_ensureCallerIsAdmin();
_;
}

modifier onlyMinter() {
_ensureCallerIsMinter();
_;
}

modifier onlyCreditFacadeOrEmergencyAdmin() {
_ensureCallerIsCreditFacadeOrEmergencyAdmin();
_;
}

constructor(address marketConfigurator_, string memory name_, string memory symbol_) ERC721(name_, symbol_) {
marketConfigurator = marketConfigurator_;
contractsRegister = IMarketConfigurator(marketConfigurator_).contractsRegister();
}

function serialize() external view override returns (bytes memory) {
return abi.encode(marketConfigurator, minter, name(), symbol(), baseURI, totalSupply);
}

function _baseURI() internal view override returns (string memory) {
return baseURI;
}

function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

return _baseURI();
}

function mint(address to, uint256 amount) external onlyMinter {
uint256 balanceBefore = balanceOf(to);

for (uint256 i = 0; i < amount; ++i) {
uint256 tokenId = (uint256(uint160(to)) << 40) + balanceBefore + i;
_mint(to, tokenId);
}

totalSupply += amount;
}

function burn(address from, uint256 amount) external override onlyCreditFacadeOrEmergencyAdmin {
uint256 balance = balanceOf(from);

for (uint256 i = 0; i < amount; ++i) {
uint256 tokenId = (uint256(uint160(from)) << 40) + balance - i - 1;
_burn(tokenId);
}

totalSupply -= amount;
}

function approve(address, uint256) public pure virtual override {
revert NotImplementedException();
}

function setApprovalForAll(address, bool) public pure virtual override {
revert NotImplementedException();
}

function transferFrom(address, address, uint256) public pure virtual override {
revert NotImplementedException();
}

function safeTransferFrom(address, address, uint256) public pure virtual override {
revert NotImplementedException();
}

function safeTransferFrom(address, address, uint256, bytes memory) public pure virtual override {
revert NotImplementedException();
}

function setMinter(address newMinter) external onlyAdmin {
if (newMinter == minter) return;
minter = newMinter;
emit SetMinter(newMinter);
}

function setBaseUri(string calldata baseURI_) external onlyAdmin {
baseURI = baseURI_;
}

function _ensureCallerIsAdmin() internal view {
if (msg.sender != IMarketConfigurator(marketConfigurator).admin()) revert CallerIsNotAdminException(msg.sender);
}

function _ensureCallerIsCreditFacadeOrEmergencyAdmin() internal view {
if (msg.sender != IMarketConfigurator(marketConfigurator).emergencyAdmin() && !_callerIsCreditFacade()) {
revert CallerIsNotCreditFacadeOrEmergencyAdminException(msg.sender);
}
}

function _callerIsCreditFacade() internal view returns (bool) {
address creditManager = ICreditFacadeV3(msg.sender).creditManager();
if (
ICreditManagerV3(creditManager).creditFacade() != msg.sender
|| !IContractsRegister(contractsRegister).isCreditManager(creditManager)
) return false;

return true;
}

function _ensureCallerIsMinter() internal view {
if (msg.sender != minter) revert CallerIsNotMinterException(msg.sender);
}
}
2 changes: 2 additions & 0 deletions contracts/helpers/DefaultIRM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ contract DefaultIRM is IInterestRateModel {
uint256 public constant override version = 3_10;
bytes32 public constant override contractType = AP_INTEREST_RATE_MODEL_DEFAULT;

function serialize() external pure override returns (bytes memory) {}

function calcBorrowRate(uint256, uint256, bool) external pure override returns (uint256) {
return 0;
}
Expand Down
9 changes: 8 additions & 1 deletion contracts/helpers/DefaultLossPolicy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

import {ILossPolicy} from "@gearbox-protocol/core-v3/contracts/interfaces/base/ILossPolicy.sol";
import {ACLTrait} from "@gearbox-protocol/core-v3/contracts/traits/ACLTrait.sol";
import {AP_LOSS_POLICY_DEFAULT} from "../libraries/ContractLiterals.sol";

contract DefaultLossPolicy is ACLTrait {
contract DefaultLossPolicy is ILossPolicy, ACLTrait {
uint256 public constant version = 3_10;
bytes32 public constant contractType = AP_LOSS_POLICY_DEFAULT;

bool public enabled;

// QUESTION: shouldn't it take pool address and AP so that it can be used with loss policy factory?
// that would make it hard to use in legacy market configurator
constructor(address acl_) ACLTrait(acl_) {}

function serialize() external view override returns (bytes memory) {
return abi.encode(enabled);
}

function isLiquidatable(address, address, bytes calldata) external view returns (bool) {
return enabled;
}
Expand Down
2 changes: 2 additions & 0 deletions contracts/instance/AddressProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,14 @@ contract AddressProvider is ImmutableOwnableTrait, IAddressProvider {
}

function getLatestMinorVersion(string memory key, uint256 majorVersion) external view override returns (uint256) {
majorVersion -= majorVersion % 100;
uint256 latestMinorVersion = latestMinorVersions[key][majorVersion];
if (latestMinorVersion == 0) revert VersionNotFoundException();
return latestMinorVersion;
}

function getLatestPatchVersion(string memory key, uint256 minorVersion) external view override returns (uint256) {
minorVersion -= minorVersion % 10;
uint256 latestPatchVersion = latestPatchVersions[key][minorVersion];
if (latestPatchVersion == 0) revert VersionNotFoundException();
return latestPatchVersion;
Expand Down
2 changes: 2 additions & 0 deletions contracts/interfaces/IMarketConfigurator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ interface IMarketConfigurator is IVersion, IDeployerTrait {

error CreditSuiteNotRegisteredException(address creditManager);

error IncorrectMinorVersionException(uint256 version);

error IncorrectPeripheryContractException(address peripheryContract);

error MarketNotRegisteredException(address pool);
Expand Down
Loading

0 comments on commit 3d401ed

Please sign in to comment.