Skip to main content

LoanConsolidator

Git Source

Inherits: IERC3156FlashBorrower, Policy, RolesConsumer, ReentrancyGuard

A policy that consolidates loans taken with a single Cooler contract into a single loan using Maker flashloans. This policy can be used to consolidate loans within the same Clearinghouse, or from one Clearinghouse to another. This also enables migration between debt denominated in different assets (such as DAI and USDS).

*This policy uses the IERC3156FlashBorrower interface to interact with Maker flashloans. This contract utilises the following roles:

  • loan_consolidator_admin: Can set the fee percentage
  • emergency_shutdown: Can activate and deactivate the contract*

State Variables

CHREG

The Clearinghouse registry module

The value is set when the policy is activated

CHREGv1 internal CHREG;

TRSRY

The treasury module

The value is set when the policy is activated

TRSRYv1 internal TRSRY;

RGSTY

The contract registry module

The value is set when the policy is activated

RGSTYv1 internal RGSTY;

DAI

The DAI token

The value is set when the policy is activated

IERC20 internal DAI;

USDS

The USDS token

The value is set when the policy is activated

IERC20 internal USDS;

GOHM

The gOHM token

The value is set when the policy is activated

IERC20 internal GOHM;

MIGRATOR

The DAI \<\> USDS Migrator

The value is set when the policy is activated

IDaiUsdsMigrator internal MIGRATOR;

FLASH

The ERC3156 flash loan provider

The value is set when the policy is activated

IERC3156FlashLender internal FLASH;

ONE_HUNDRED_PERCENT

The denominator for percentage calculations

uint256 public constant ONE_HUNDRED_PERCENT = 100e2;

feePercentage

Percentage of the debt to be paid as a fee

In terms of ONE_HUNDRED_PERCENT

uint256 public feePercentage;

consolidatorActive

Whether the contract is active

Note that this is different to the policy activation status

bool public consolidatorActive;

ROLE_ADMIN

The role required to call admin functions

bytes32 public constant ROLE_ADMIN = "loan_consolidator_admin";

ROLE_EMERGENCY_SHUTDOWN

The role required to call emergency shutdown functions

bytes32 public constant ROLE_EMERGENCY_SHUTDOWN = "emergency_shutdown";

Functions

constructor

Constructor for the Loan Consolidator

*This function will revert if:

  • The fee percentage is above ONE_HUNDRED_PERCENT
  • The kernel address is zero*
constructor(address kernel_, uint256 feePercentage_) Policy(Kernel(kernel_));

configureDependencies

Define module dependencies for this policy.

function configureDependencies() external override returns (Keycode[] memory dependencies);

Returns

NameTypeDescription
dependenciesKeycode[]- Keycode array of module dependencies.

requestPermissions

Function called by kernel to set module function permissions.

This policy does not require any permissions

function requestPermissions() external pure override returns (Permissions[] memory requests);

Returns

NameTypeDescription
requestsPermissions[]- Array of keycodes and function selectors for requested permissions.

consolidate

Consolidate loans (taken with a single Cooler contract) into a single loan by using flashloans. Unlike consolidateWithNewOwner(), the owner of the new Cooler must be the same as the Cooler being repaid. The caller will be required to provide additional funds to cover accrued interest on the Cooler loans and the lender and protocol fees (if applicable). Use the requiredApprovals() function to determine the amount of funds and approvals required. It is expected that the caller will have already provided approval for this contract to spend the required tokens. See requiredApprovals() for more details.

*This function will revert if:

  • The caller is not the 'coolerFrom' and 'coolerTo' owner.
  • The caller has not approved this contract to spend the reserve token of clearinghouseTo_ in order to pay the interest, lender and protocol fees.
  • The caller has not approved this contract to spend the gOHM escrowed by coolerFrom_.
  • clearinghouseFrom_ or clearinghouseTo_ is not registered with the Clearinghouse registry.
  • coolerFrom_ or coolerTo_ is not a valid Cooler for the respective Clearinghouse.
  • Consolidation is taking place within the same Cooler, and less than two loans are being consolidated.
  • The available funds are less than the required flashloan amount.
  • The contract is not active.
  • The contract has not been activated as a policy.
  • Re-entrancy is detected.*
function consolidate(
address clearinghouseFrom_,
address clearinghouseTo_,
address coolerFrom_,
address coolerTo_,
uint256[] calldata ids_
) public onlyPolicyActive onlyConsolidatorActive nonReentrant;

Parameters

NameTypeDescription
clearinghouseFrom_addressOlympus Clearinghouse that issued the existing loans.
clearinghouseTo_addressOlympus Clearinghouse to be used to issue the consolidated loan.
coolerFrom_addressCooler from which the loans will be consolidated.
coolerTo_addressCooler to which the loans will be consolidated
ids_uint256[]Array containing the ids of the loans to be consolidated.

consolidateWithNewOwner

Consolidate loans (taken with a single Cooler contract) into a single loan by using flashloans. Unlike consolidate(), the owner of the new Cooler can be different from the Cooler being repaid. The caller will be required to provide additional funds to cover accrued interest on the Cooler loans and the lender and protocol fees (if applicable). Use the requiredApprovals() function to determine the amount of funds and approvals required. It is expected that the caller will have already provided approval for this contract to spend the required tokens. See requiredApprovals() for more details.

*This function will revert if:

  • The caller is not the coolerFrom_ owner.
  • coolerFrom_ is the same as coolerTo_ (in which case consolidate() should be used).
  • The owner of coolerFrom_ is the same as coolerTo_ (in which case consolidate() should be used).
  • The caller has not approved this contract to spend the reserve token of clearinghouseTo_ in order to pay the interest, lender and protocol fees.
  • The caller has not approved this contract to spend the gOHM escrowed by the target Cooler.
  • clearinghouseFrom_ or clearinghouseTo_ is not registered with the Clearinghouse registry.
  • coolerFrom_ or coolerTo_ is not a valid Cooler for the respective Clearinghouse.
  • Consolidation is taking place within the same Cooler, and less than two loans are being consolidated.
  • The available funds are less than the required flashloan amount.
  • The contract is not active.
  • The contract has not been activated as a policy.
  • Re-entrancy is detected.*
function consolidateWithNewOwner(
address clearinghouseFrom_,
address clearinghouseTo_,
address coolerFrom_,
address coolerTo_,
uint256[] calldata ids_
) public onlyPolicyActive onlyConsolidatorActive nonReentrant;

Parameters

NameTypeDescription
clearinghouseFrom_addressOlympus Clearinghouse that issued the existing loans.
clearinghouseTo_addressOlympus Clearinghouse to be used to issue the consolidated loan.
coolerFrom_addressCooler from which the loans will be consolidated.
coolerTo_addressCooler to which the loans will be consolidated
ids_uint256[]Array containing the ids of the loans to be consolidated.

_consolidateWithFlashLoan

Internal logic for loan consolidation

*Utilized by consolidate() and consolidateWithNewOwner() This function assumes:

  • The calling external-facing function has checked that the caller is permitted to operate on coolerFrom_.*
function _consolidateWithFlashLoan(
address clearinghouseFrom_,
address clearinghouseTo_,
address coolerFrom_,
address coolerTo_,
uint256[] calldata ids_
) internal;

Parameters

NameTypeDescription
clearinghouseFrom_addressOlympus Clearinghouse that issued the existing loans.
clearinghouseTo_addressOlympus Clearinghouse to be used to issue the consolidated loan.
coolerFrom_addressCooler from which the loans will be consolidated.
coolerTo_addressCooler to which the loans will be consolidated
ids_uint256[]Array containing the ids of the loans to be consolidated.

onFlashLoan

*This function reverts if:

  • The caller is not the flash loan provider
  • The initiator is not this contract*
function onFlashLoan(address initiator_, address, uint256 amount_, uint256 lenderFee_, bytes calldata params_)
external
override
returns (bytes32);

Parameters

NameTypeDescription
initiator_address
<none>address
amount_uint256
lenderFee_uint256
params_bytes

Returns

NameTypeDescription
<none>bytes32The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"

setFeePercentage

Set the fee percentage

*This function will revert if:

  • The contract has not been activated as a policy.
  • The fee percentage is above ONE_HUNDRED_PERCENT
  • The caller does not have the ROLE_ADMIN role*
function setFeePercentage(uint256 feePercentage_) external onlyPolicyActive onlyRole(ROLE_ADMIN);

activate

Activate the contract

*This function will revert if:

  • The contract has not been activated as a policy.
  • The caller does not have the ROLE_EMERGENCY_SHUTDOWN role If the contract is already active, it will do nothing.*
function activate() external onlyPolicyActive onlyRole(ROLE_EMERGENCY_SHUTDOWN);

deactivate

Deactivate the contract

*This function will revert if:

  • The contract has not been activated as a policy.
  • The caller does not have the ROLE_EMERGENCY_SHUTDOWN role If the contract is already deactivated, it will do nothing.*
function deactivate() external onlyPolicyActive onlyRole(ROLE_EMERGENCY_SHUTDOWN);

onlyConsolidatorActive

Modifier to check that the contract is active

modifier onlyConsolidatorActive();

onlyPolicyActive

Modifier to check that the contract is activated as a policy

modifier onlyPolicyActive();

_getDebtForLoans

Get the total principal and interest for a given set of loans

function _getDebtForLoans(address cooler_, uint256[] calldata ids_) internal view returns (uint256, uint256);

Parameters

NameTypeDescription
cooler_addressCooler contract that issued the loans
ids_uint256[]Array of loan ids to be consolidated

Returns

NameTypeDescription
<none>uint256totalPrincipal_ Total principal
<none>uint256totalInterest_ Total interest

_repayDebtForLoans

Repay the debt for a given set of loans and collect the collateral.

*This function assumes:

  • The cooler owner has granted approval for this contract to spend the gOHM collateral*
function _repayDebtForLoans(address cooler_, uint256[] memory ids_) internal;

Parameters

NameTypeDescription
cooler_addressCooler contract that issued the loans
ids_uint256[]Array of loan ids to be repaid

_isValidClearinghouse

function _isValidClearinghouse(address clearinghouse_) internal view returns (bool);

_isValidCooler

Check if a given cooler was created by the CoolerFactory for a Clearinghouse

This function assumes that the authenticity of the Clearinghouse is already verified

function _isValidCooler(address clearinghouse_, address cooler_) internal view returns (bool);

Parameters

NameTypeDescription
clearinghouse_addressClearinghouse contract
cooler_addressCooler contract

Returns

NameTypeDescription
<none>boolbool Whether the cooler was created by the CoolerFactory for the Clearinghouse

_getClearinghouseReserveToken

Get the reserve token for a given Clearinghouse

This function will revert if the reserve token cannot be determined

function _getClearinghouseReserveToken(address clearinghouse_) internal view returns (address);

Parameters

NameTypeDescription
clearinghouse_addressClearinghouse contract

Returns

NameTypeDescription
<none>addressaddress Reserve token

_getMigrationType

Get the migration type for a given pair of Clearinghouses

This function will revert if the migration type cannot be determined

function _getMigrationType(address clearinghouseFrom_, address clearinghouseTo_)
internal
view
returns (MigrationType migrationType, IERC20 reserveFrom, IERC20 reserveTo);

Parameters

NameTypeDescription
clearinghouseFrom_addressClearinghouse that issued the existing loans
clearinghouseTo_addressClearinghouse to be used to issue the consolidated loan

Returns

NameTypeDescription
migrationTypeMigrationTypeMigration type
reserveFromIERC20Reserve token for the existing loans
reserveToIERC20Reserve token for the consolidated loan

_getFlashloanParameters

Assembles the parameters for a flashloan

function _getFlashloanParameters(
Clearinghouse clearinghouseFrom_,
Clearinghouse clearinghouseTo_,
Cooler coolerFrom_,
Cooler coolerTo_,
uint256[] calldata ids_,
MigrationType migrationType_,
IERC20 reserveFrom_,
IERC20 reserveTo_
) internal view returns (uint256 flashloanAmount, FlashLoanData memory flashloanParams);

Parameters

NameTypeDescription
clearinghouseFrom_ClearinghouseClearinghouse that issued the existing loans
clearinghouseTo_ClearinghouseClearinghouse to be used to issue the consolidated loan
coolerFrom_CoolerCooler contract that issued the existing loans
coolerTo_CoolerCooler contract to be used to issue the consolidated loan
ids_uint256[]Array of loan ids to be consolidated
migrationType_MigrationTypeMigration type
reserveFrom_IERC20Reserve token for the existing loans
reserveTo_IERC20Reserve token for the consolidated loan

Returns

NameTypeDescription
flashloanAmountuint256Amount of the flashloan
flashloanParamsFlashLoanDataFlashloan parameters

getProtocolFee

View function to compute the protocol fee for a given total debt.

function getProtocolFee(uint256 totalDebt_) public view returns (uint256);

requiredApprovals

View function to compute the required approval amounts that the owner of a given Cooler must give to this contract in order to consolidate the loans.

*This function will revert if:

  • The contract has not been activated as a policy.*
function requiredApprovals(address clearinghouseTo_, address coolerFrom_, uint256[] calldata ids_)
external
view
onlyPolicyActive
returns (address, uint256, address, uint256, uint256);

Parameters

NameTypeDescription
clearinghouseTo_addressClearinghouse contract used to issue the consolidated loan.
coolerFrom_addressCooler contract that issued the loans.
ids_uint256[]Array of loan ids to be consolidated.

Returns

NameTypeDescription
<none>addressowner Owner of the Cooler (address that should grant the approval).
<none>uint256gOhmAmount Amount of gOHM to be approved by the Cooler owner.
<none>addressreserveTo Token that the approval is in terms of
<none>uint256ownerReserveTo Amount of reserveTo to be approved by the Cooler owner. This will be the principal of the consolidated loan.
<none>uint256callerReserveTo Amount of reserveTo that the caller will need to provide.

collateralRequired

Calculates the collateral required to consolidate a set of loans.

Due to rounding, the collateral required for the consolidated loan may be greater than the collateral of the loans being consolidated. This function calculates the additional collateral required.

function collateralRequired(address clearinghouse_, address cooler_, uint256[] memory ids_)
public
view
returns (uint256 consolidatedLoanCollateral, uint256 existingLoanCollateral, uint256 additionalCollateral);

Parameters

NameTypeDescription
clearinghouse_addressClearinghouse contract used to issue the consolidated loan.
cooler_addressCooler contract that issued the loans.
ids_uint256[]Array of loan ids to be consolidated.

Returns

NameTypeDescription
consolidatedLoanCollateraluint256Collateral required for the consolidated loan.
existingLoanCollateraluint256Collateral of the existing loans.
additionalCollateraluint256Additional collateral required to consolidate the loans. This will need to be supplied by the Cooler owner.

fundsRequired

View function to compute the funds required to consolidate a set of loans. The sum of the values must be held in the caller's wallet, in terms of the reserve token.

function fundsRequired(address clearinghouseTo_, address coolerFrom_, uint256[] calldata ids_)
public
view
onlyPolicyActive
returns (address reserveTo, uint256 interest, uint256 lenderFee, uint256 protocolFee);

Parameters

NameTypeDescription
clearinghouseTo_addressClearinghouse contract to be used to issue the consolidated loan.
coolerFrom_addressCooler contract that issued the loans.
ids_uint256[]Array of loan ids to be consolidated.

Returns

NameTypeDescription
reserveToaddressToken the fund amounts are in terms of
interestuint256Total interest
lenderFeeuint256Lender fee
protocolFeeuint256Protocol fee

VERSION

Version of the contract

function VERSION() external pure returns (uint256);

Returns

NameTypeDescription
<none>uint256Version number

Events

ConsolidatorActivated

Emitted when the contract is activated

Note that this is different to activation of the contract as a policy

event ConsolidatorActivated();

ConsolidatorDeactivated

Emitted when the contract is deactivated

Note that this is different to deactivation of the contract as a policy

event ConsolidatorDeactivated();

FeePercentageSet

Emitted when the fee percentage is set

event FeePercentageSet(uint256 feePercentage);

Errors

OnlyThis

Thrown when the caller is not the contract itself.

error OnlyThis();

OnlyLender

Thrown when the caller is not the flash lender.

error OnlyLender();

OnlyCoolerOwner

Thrown when the caller is not the Cooler owner.

error OnlyCoolerOwner();

OnlyConsolidatorActive

Thrown when the contract is not active.

error OnlyConsolidatorActive();

OnlyPolicyActive

Thrown when the contract is not activated as a policy.

error OnlyPolicyActive();

Params_FeePercentageOutOfRange

Thrown when the fee percentage is out of range.

Valid values are 0 <= feePercentage <= 100e2

error Params_FeePercentageOutOfRange();

Params_InvalidAddress

Thrown when the address is invalid.

error Params_InvalidAddress();

Params_InsufficientCoolerCount

Thrown when the caller attempts to consolidate too few cooler loans. The minimum is two.

error Params_InsufficientCoolerCount();

Params_InvalidClearinghouse

Thrown when the Clearinghouse is not registered with the Bophades kernel

error Params_InvalidClearinghouse();

Params_InvalidCooler

Thrown when the Cooler is not created by the CoolerFactory for the specified Clearinghouse

error Params_InvalidCooler();

Structs

FlashLoanData

Data structure used for flashloan parameters

struct FlashLoanData {
Clearinghouse clearinghouseFrom;
Clearinghouse clearinghouseTo;
Cooler coolerFrom;
Cooler coolerTo;
uint256[] ids;
uint256 principal;
uint256 interest;
uint256 protocolFee;
MigrationType migrationType;
IERC20 reserveFrom;
IERC20 reserveTo;
}

Enums

MigrationType

enum MigrationType {
DAI_DAI,
USDS_USDS,
DAI_USDS,
USDS_DAI
}