- (LoanOpenings.sol)
View Source: contracts/modules/LoanOpenings.sol
↗ Extends: LoanOpeningsEvents, VaultController, InterestUser, SwapsUser, ModuleCommonFunctionalities
This contract code comes from bZx. bZx is a protocol for tokenized margin trading and lending https://bzx.network similar to the dYdX protocol.
- This contract contains functions to borrow and trade.
- constructor()
- constructor()
- initialize(address target)
- borrowOrTradeFromPool(bytes32 loanParamsId, bytes32 loanId, bool isTorqueLoan, uint256 initialMargin, struct MarginTradeStructHelpers.SentAddresses sentAddresses, struct MarginTradeStructHelpers.SentAmounts sentValues, bytes loanDataBytes)
- setDelegatedManager(bytes32 loanId, address delegated, bool toggle)
- getEstimatedMarginExposure(address loanToken, address collateralToken, uint256 loanTokenSent, uint256 collateralTokenSent, uint256 interestRate, uint256 newPrincipal)
- getRequiredCollateral(address loanToken, address collateralToken, uint256 newPrincipal, uint256 marginAmount, bool isTorqueLoan)
- getBorrowAmount(address loanToken, address collateralToken, uint256 collateralTokenAmount, uint256 marginAmount, bool isTorqueLoan)
- _borrowOrTrade(struct LoanParamsStruct.LoanParams loanParamsLocal, bytes32 loanId, bool isTorqueLoan, uint256 collateralAmountRequired, uint256 initialMargin, struct MarginTradeStructHelpers.SentAddresses sentAddresses, struct MarginTradeStructHelpers.SentAmounts sentValues, bytes loanDataBytes)
- _finalizeOpen(struct LoanParamsStruct.LoanParams loanParamsLocal, struct LoanStruct.Loan loanLocal, struct MarginTradeStructHelpers.SentAddresses sentAddresses, struct MarginTradeStructHelpers.SentAmounts sentValues, bool isTorqueLoan)
- _emitOpeningEvents(struct LoanParamsStruct.LoanParams loanParamsLocal, struct LoanStruct.Loan loanLocal, struct MarginTradeStructHelpers.SentAddresses sentAddresses, struct MarginTradeStructHelpers.SentAmounts sentValues, uint256 collateralToLoanRate, uint256 margin, bool isTorqueLoan)
- _setDelegatedManager(bytes32 loanId, address delegator, address delegated, bool toggle)
- _isCollateralSatisfied(struct LoanParamsStruct.LoanParams loanParamsLocal, struct LoanStruct.Loan loanLocal, uint256 initialMargin, uint256 newCollateral, uint256 collateralAmountRequired)
- _initializeLoan(struct LoanParamsStruct.LoanParams loanParamsLocal, bytes32 loanId, uint256 initialMargin, struct MarginTradeStructHelpers.SentAddresses sentAddresses, uint256 newPrincipal)
- _initializeInterest(struct LoanParamsStruct.LoanParams loanParamsLocal, struct LoanStruct.Loan loanLocal, uint256 newRate, uint256 newPrincipal, uint256 torqueInterest)
- _getRequiredCollateral(address loanToken, address collateralToken, uint256 newPrincipal, uint256 marginAmount, bool isTorqueLoan)
function () public nonpayable
Source Code
constructor() public {}
Fallback function is to react to receiving value (rBTC).
function () external nonpayable
Source Code
function() external {
revert("fallback not allowed");
}
Set function selectors on target contract. *
function initialize(address target) external nonpayable onlyOwner
Arguments
Name | Type | Description |
---|---|---|
target | address | The address of the target contract. |
Source Code
function initialize(address target) external onlyOwner {
address prevModuleContractAddress = logicTargets[this.borrowOrTradeFromPool.selector];
_setTarget(this.borrowOrTradeFromPool.selector, target);
_setTarget(this.setDelegatedManager.selector, target);
_setTarget(this.getEstimatedMarginExposure.selector, target);
_setTarget(this.getRequiredCollateral.selector, target);
_setTarget(this.getBorrowAmount.selector, target);
emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, "LoanOpenings");
}
Borrow or trade from pool. *
function borrowOrTradeFromPool(bytes32 loanParamsId, bytes32 loanId, bool isTorqueLoan, uint256 initialMargin, struct MarginTradeStructHelpers.SentAddresses sentAddresses, struct MarginTradeStructHelpers.SentAmounts sentValues, bytes loanDataBytes) external payable nonReentrant whenNotPaused
returns(newPrincipal uint256, newCollateral uint256)
Arguments
Name | Type | Description |
---|---|---|
loanParamsId | bytes32 | The ID of the loan parameters. |
loanId | bytes32 | The ID of the loan. If 0, start a new loan. |
isTorqueLoan | bool | Whether the loan is a Torque loan. |
initialMargin | uint256 | The initial amount of margin. |
sentAddresses | struct MarginTradeStructHelpers.SentAddresses | The addresses to send tokens: lender, borrower, receiver and manager: lender: must match loan if loanId provided. borrower: must match loan if loanId provided. receiver: receiver of funds (address(0) assumes borrower address). manager: delegated manager of loan unless address(0). |
sentValues | struct MarginTradeStructHelpers.SentAmounts | The values to send: interestRate: New loan interest rate. newPrincipal: New loan size (borrowAmount + any borrowed interest). interestInitialAmount: New amount of interest to escrow for Torque loan (determines initial loan length). loanTokenReceived: Total loanToken deposit (amount not sent to borrower in the case of Torque loans). collateralTokenSent: Total collateralToken deposit. minEntryPrice: Minimum entry price for checking price divergence (Value of loan token in collateral). |
loanDataBytes | bytes | The payload for the call. These loan DataBytes are additional loan data (not in use for token swaps). * |
Returns
newPrincipal The new loan size.
Source Code
function borrowOrTradeFromPool(
bytes32 loanParamsId,
bytes32 loanId,
bool isTorqueLoan,
uint256 initialMargin,
MarginTradeStructHelpers.SentAddresses calldata sentAddresses,
MarginTradeStructHelpers.SentAmounts calldata sentValues,
bytes calldata loanDataBytes
)
external
payable
nonReentrant
whenNotPaused
returns (uint256 newPrincipal, uint256 newCollateral)
{
require(msg.value == 0 || loanDataBytes.length != 0, "loanDataBytes required with ether");
/// Only callable by loan pools.
require(loanPoolToUnderlying[msg.sender] != address(0), "not authorized");
LoanParams memory loanParamsLocal = loanParams[loanParamsId];
require(loanParamsLocal.id != 0, "loanParams not exists");
/// Get required collateral.
uint256 collateralAmountRequired =
_getRequiredCollateral(
loanParamsLocal.loanToken,
loanParamsLocal.collateralToken,
sentValues.newPrincipal,
initialMargin,
isTorqueLoan
);
require(collateralAmountRequired != 0, "collateral is 0");
return
_borrowOrTrade(
loanParamsLocal,
loanId,
isTorqueLoan,
collateralAmountRequired,
initialMargin,
sentAddresses,
sentValues,
loanDataBytes
);
}
Set the delegated manager. *
function setDelegatedManager(bytes32 loanId, address delegated, bool toggle) external nonpayable whenNotPaused
Arguments
Name | Type | Description |
---|---|---|
loanId | bytes32 | The ID of the loan. If 0, start a new loan. |
delegated | address | The address of the delegated manager. |
toggle | bool | The flag true/false for the delegated manager. |
Source Code
function setDelegatedManager(
bytes32 loanId,
address delegated,
bool toggle
) external whenNotPaused {
require(loans[loanId].borrower == msg.sender, "unauthorized");
_setDelegatedManager(loanId, msg.sender, delegated, toggle);
}
Get the estimated margin exposure. * Margin is the money borrowed from a broker to purchase an investment and is the difference between the total value of investment and the loan amount. Margin trading refers to the practice of using borrowed funds from a broker to trade a financial asset, which forms the collateral for the loan from the broker. *
function getEstimatedMarginExposure(address loanToken, address collateralToken, uint256 loanTokenSent, uint256 collateralTokenSent, uint256 interestRate, uint256 newPrincipal) external view
returns(uint256)
Arguments
Name | Type | Description |
---|---|---|
loanToken | address | The loan token instance address. |
collateralToken | address | The collateral token instance address. |
loanTokenSent | uint256 | The amount of loan tokens sent. |
collateralTokenSent | uint256 | The amount of collateral tokens sent. |
interestRate | uint256 | The interest rate. Percentage w/ 18 decimals. |
newPrincipal | uint256 | The updated amount of principal (current debt). * |
Returns
The margin exposure.
Source Code
function getEstimatedMarginExposure(
address loanToken,
address collateralToken,
uint256 loanTokenSent,
uint256 collateralTokenSent,
uint256 interestRate,
uint256 newPrincipal
) external view returns (uint256) {
uint256 maxLoanTerm = 2419200; // 28 days
uint256 owedPerDay = newPrincipal.mul(interestRate).div(365 * 10**20);
uint256 interestAmountRequired = maxLoanTerm.mul(owedPerDay).div(86400);
uint256 swapAmount = loanTokenSent.sub(interestAmountRequired);
uint256 tradingFee = _getTradingFee(swapAmount);
if (tradingFee != 0) {
swapAmount = swapAmount.sub(tradingFee);
}
uint256 receivedAmount = _swapsExpectedReturn(loanToken, collateralToken, swapAmount);
if (receivedAmount == 0) {
return 0;
} else {
return collateralTokenSent.add(receivedAmount);
}
}
Get the required collateral. *
function getRequiredCollateral(address loanToken, address collateralToken, uint256 newPrincipal, uint256 marginAmount, bool isTorqueLoan) public view
returns(collateralAmountRequired uint256)
Arguments
Name | Type | Description |
---|---|---|
loanToken | address | The loan token instance address. |
collateralToken | address | The collateral token instance address. |
newPrincipal | uint256 | The updated amount of principal (current debt). |
marginAmount | uint256 | The amount of margin of the trade. |
isTorqueLoan | bool | Whether the loan is a Torque loan. * |
Returns
collateralAmountRequired The required collateral.
Source Code
function getRequiredCollateral(
address loanToken,
address collateralToken,
uint256 newPrincipal,
uint256 marginAmount,
bool isTorqueLoan
) public view returns (uint256 collateralAmountRequired) {
if (marginAmount != 0) {
collateralAmountRequired = _getRequiredCollateral(
loanToken,
collateralToken,
newPrincipal,
marginAmount,
isTorqueLoan
);
// p3.9 from bzx peckshield-audit-report-bZxV2-v1.0rc1.pdf
// cannot be applied solely as it drives to some other tests failure
/*
uint256 feePercent = isTorqueLoan ? borrowingFeePercent : tradingFeePercent;
if (collateralAmountRequired != 0 && feePercent != 0) {
collateralAmountRequired = collateralAmountRequired.mul(10**20).divCeil(
10**20 - feePercent // never will overflow
);
}*/
uint256 fee =
isTorqueLoan
? _getBorrowingFee(collateralAmountRequired)
: _getTradingFee(collateralAmountRequired);
if (fee != 0) {
collateralAmountRequired = collateralAmountRequired.add(fee);
}
}
}
Get the borrow amount of a trade loan. *
function getBorrowAmount(address loanToken, address collateralToken, uint256 collateralTokenAmount, uint256 marginAmount, bool isTorqueLoan) public view
returns(borrowAmount uint256)
Arguments
Name | Type | Description |
---|---|---|
loanToken | address | The loan token instance address. |
collateralToken | address | The collateral token instance address. |
collateralTokenAmount | uint256 | The amount of collateral. |
marginAmount | uint256 | The amount of margin of the trade. |
isTorqueLoan | bool | Whether the loan is a Torque loan. * |
Returns
borrowAmount The borrow amount.
Source Code
function getBorrowAmount(
address loanToken,
address collateralToken,
uint256 collateralTokenAmount,
uint256 marginAmount,
bool isTorqueLoan
) public view returns (uint256 borrowAmount) {
if (marginAmount != 0) {
if (isTorqueLoan) {
marginAmount = marginAmount.add(10**20); /// Adjust for over-collateralized loan.
}
uint256 collateral = collateralTokenAmount;
uint256 fee = isTorqueLoan ? _getBorrowingFee(collateral) : _getTradingFee(collateral);
if (fee != 0) {
collateral = collateral.sub(fee);
}
if (loanToken == collateralToken) {
borrowAmount = collateral.mul(10**20).div(marginAmount);
} else {
(uint256 sourceToDestRate, uint256 sourceToDestPrecision) =
IPriceFeeds(priceFeeds).queryRate(collateralToken, loanToken);
if (sourceToDestPrecision != 0) {
borrowAmount = collateral
.mul(10**20)
.mul(sourceToDestRate)
.div(marginAmount)
.div(sourceToDestPrecision);
}
}
/*
// p3.9 from bzx peckshield-audit-report-bZxV2-v1.0rc1.pdf
// cannot be applied solely as it drives to some other tests failure
uint256 feePercent = isTorqueLoan ? borrowingFeePercent : tradingFeePercent;
if (borrowAmount != 0 && feePercent != 0) {
borrowAmount = borrowAmount
.mul(
10**20 - feePercent // never will overflow
)
.divCeil(10**20);
}*/
}
}
Borrow or trade. *
function _borrowOrTrade(struct LoanParamsStruct.LoanParams loanParamsLocal, bytes32 loanId, bool isTorqueLoan, uint256 collateralAmountRequired, uint256 initialMargin, struct MarginTradeStructHelpers.SentAddresses sentAddresses, struct MarginTradeStructHelpers.SentAmounts sentValues, bytes loanDataBytes) internal nonpayable
returns(uint256, uint256)
Arguments
Name | Type | Description |
---|---|---|
loanParamsLocal | struct LoanParamsStruct.LoanParams | The loan parameters. |
loanId | bytes32 | The ID of the loan. If 0, start a new loan. |
isTorqueLoan | bool | Whether the loan is a Torque loan. |
collateralAmountRequired | uint256 | The required amount of collateral. |
initialMargin | uint256 | The initial amount of margin. |
sentAddresses | struct MarginTradeStructHelpers.SentAddresses | The addresses to send tokens: lender, borrower, receiver and manager: lender: must match loan if loanId provided. borrower: must match loan if loanId provided. receiver: receiver of funds (address(0) assumes borrower address). manager: delegated manager of loan unless address(0). |
sentValues | struct MarginTradeStructHelpers.SentAmounts | The values to send: interestRate: New loan interest rate. newPrincipal: New loan size (borrowAmount + any borrowed interest). interestInitialAmount: New amount of interest to escrow for Torque loan (determines initial loan length). loanTokenReceived: Total loanToken deposit (amount not sent to borrower in the case of Torque loans). collateralTokenSent: Total collateralToken deposit. minEntryPrice: Minimum entry price for checking price divergence (Value of loan token in collateral). |
loanDataBytes | bytes | The payload for the call. These loan DataBytes are additional loan data (not in use for token swaps). * |
Returns
The new loan size.
Source Code
function _borrowOrTrade(
LoanParams memory loanParamsLocal,
bytes32 loanId,
bool isTorqueLoan,
uint256 collateralAmountRequired,
uint256 initialMargin,
MarginTradeStructHelpers.SentAddresses memory sentAddresses,
MarginTradeStructHelpers.SentAmounts memory sentValues,
bytes memory loanDataBytes
) internal returns (uint256, uint256) {
require(
loanParamsLocal.collateralToken != loanParamsLocal.loanToken,
"collateral/loan match"
);
require(initialMargin >= loanParamsLocal.minInitialMargin, "initialMargin too low");
/// maxLoanTerm == 0 indicates a Torque loan and requires that torqueInterest != 0
require(
loanParamsLocal.maxLoanTerm != 0 || sentValues.interestInitialAmount != 0, /// torqueInterest
"invalid interest"
);
/// Initialize loan.
Loan storage loanLocal =
loans[
_initializeLoan(
loanParamsLocal,
loanId,
initialMargin,
sentAddresses,
sentValues.newPrincipal
)
];
// Get required interest.
uint256 amount =
_initializeInterest(
loanParamsLocal,
loanLocal,
sentValues.interestRate, /// newRate
sentValues.newPrincipal, /// newPrincipal,
sentValues.interestInitialAmount /// torqueInterest
);
/// substract out interest from usable loanToken sent.
sentValues.loanTokenSent = sentValues.loanTokenSent.sub(amount);
if (isTorqueLoan) {
require(sentValues.loanTokenSent == 0, "surplus loan token");
uint256 borrowingFee = _getBorrowingFee(sentValues.collateralTokenSent);
// need to temp into local state to avoid
address _collateralToken = loanParamsLocal.collateralToken;
address _loanToken = loanParamsLocal.loanToken;
if (borrowingFee != 0) {
_payBorrowingFee(
sentAddresses.borrower, /// borrower
loanLocal.id,
_collateralToken, /// fee token
_loanToken, /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward
borrowingFee
);
sentValues.collateralTokenSent = sentValues.collateralTokenSent.sub(borrowingFee);
}
} else {
/// Update collateral after trade.
uint256 receivedAmount;
(receivedAmount, , sentValues.loanToCollateralSwapRate) = _loanSwap(
loanId,
loanParamsLocal.loanToken,
loanParamsLocal.collateralToken,
sentAddresses.borrower, /// borrower
sentValues.loanTokenSent, /// loanTokenUsable (minSourceTokenAmount)
0, /// maxSourceTokenAmount (0 means minSourceTokenAmount)
0, /// requiredDestTokenAmount (enforces that all of loanTokenUsable is swapped)
false, /// bypassFee
loanDataBytes
);
sentValues.collateralTokenSent = sentValues.collateralTokenSent.add(receivedAmount);
/// Check the minEntryPrice with the rate
require(
sentValues.loanToCollateralSwapRate >= sentValues.minEntryPrice,
"entry price above the minimum"
);
}
/// Settle collateral.
require(
_isCollateralSatisfied(
loanParamsLocal,
loanLocal,
initialMargin,
sentValues.collateralTokenSent,
collateralAmountRequired
),
"collateral insufficient"
);
loanLocal.collateral = loanLocal.collateral.add(sentValues.collateralTokenSent);
if (isTorqueLoan) {
/// reclaiming variable -> interestDuration
sentValues.interestDuration = loanLocal.endTimestamp.sub(block.timestamp);
} else {
/// reclaiming variable -> entryLeverage = 100 / initialMargin
sentValues.entryLeverage = SafeMath.div(10**38, initialMargin);
}
_finalizeOpen(loanParamsLocal, loanLocal, sentAddresses, sentValues, isTorqueLoan);
return (sentValues.newPrincipal, sentValues.collateralTokenSent); /// newPrincipal, newCollateral
}
Finalize an open loan. *
function _finalizeOpen(struct LoanParamsStruct.LoanParams loanParamsLocal, struct LoanStruct.Loan loanLocal, struct MarginTradeStructHelpers.SentAddresses sentAddresses, struct MarginTradeStructHelpers.SentAmounts sentValues, bool isTorqueLoan) internal nonpayable
Arguments
Name | Type | Description |
---|---|---|
loanParamsLocal | struct LoanParamsStruct.LoanParams | The loan parameters. |
loanLocal | struct LoanStruct.Loan | The loan object. |
sentAddresses | struct MarginTradeStructHelpers.SentAddresses | The addresses to send tokens: lender, borrower, receiver and manager: lender: must match loan if loanId provided. borrower: must match loan if loanId provided. receiver: receiver of funds (address(0) assumes borrower address). manager: delegated manager of loan unless address(0). |
sentValues | struct MarginTradeStructHelpers.SentAmounts | The values to send: interestRate: New loan interest rate. newPrincipal: New loan size (borrowAmount + any borrowed interest). interestInitialAmount: New amount of interest to escrow for Torque loan (determines initial loan length). loanTokenReceived: Total loanToken deposit (amount not sent to borrower in the case of Torque loans). collateralTokenSent: Total collateralToken deposit. minEntryPrice: Minimum entry price for checking price divergence (Value of loan token in collateral). |
isTorqueLoan | bool | Whether the loan is a Torque loan. |
Source Code
function _finalizeOpen(
LoanParams memory loanParamsLocal,
Loan storage loanLocal,
MarginTradeStructHelpers.SentAddresses memory sentAddresses,
MarginTradeStructHelpers.SentAmounts memory sentValues,
bool isTorqueLoan
) internal {
/// @dev TODO: here the actual used rate and margin should go.
(uint256 initialMargin, uint256 collateralToLoanRate) =
IPriceFeeds(priceFeeds).getCurrentMargin(
loanParamsLocal.loanToken,
loanParamsLocal.collateralToken,
loanLocal.principal,
loanLocal.collateral
);
require(initialMargin > loanParamsLocal.maintenanceMargin, "unhealthy position");
if (loanLocal.startTimestamp == block.timestamp) {
uint256 loanToCollateralPrecision =
IPriceFeeds(priceFeeds).queryPrecision(
loanParamsLocal.loanToken,
loanParamsLocal.collateralToken
);
uint256 collateralToLoanPrecision =
IPriceFeeds(priceFeeds).queryPrecision(
loanParamsLocal.collateralToken,
loanParamsLocal.loanToken
);
uint256 totalSwapRate = loanToCollateralPrecision.mul(collateralToLoanPrecision);
loanLocal.startRate = isTorqueLoan
? collateralToLoanRate
: totalSwapRate.div(sentValues.loanToCollateralSwapRate);
}
_emitOpeningEvents(
loanParamsLocal,
loanLocal,
sentAddresses,
sentValues,
collateralToLoanRate,
initialMargin,
isTorqueLoan
);
}
Emit the opening events. *
function _emitOpeningEvents(struct LoanParamsStruct.LoanParams loanParamsLocal, struct LoanStruct.Loan loanLocal, struct MarginTradeStructHelpers.SentAddresses sentAddresses, struct MarginTradeStructHelpers.SentAmounts sentValues, uint256 collateralToLoanRate, uint256 margin, bool isTorqueLoan) internal nonpayable
Arguments
Name | Type | Description |
---|---|---|
loanParamsLocal | struct LoanParamsStruct.LoanParams | The loan parameters. |
loanLocal | struct LoanStruct.Loan | The loan object. |
sentAddresses | struct MarginTradeStructHelpers.SentAddresses | The addresses to send tokens: lender, borrower, receiver and manager: lender: must match loan if loanId provided. borrower: must match loan if loanId provided. receiver: receiver of funds (address(0) assumes borrower address). manager: delegated manager of loan unless address(0). |
sentValues | struct MarginTradeStructHelpers.SentAmounts | The values to send: interestRate: New loan interest rate. newPrincipal: New loan size (borrowAmount + any borrowed interest). interestInitialAmount: New amount of interest to escrow for Torque loan (determines initial loan length). loanTokenReceived: Total loanToken deposit (amount not sent to borrower in the case of Torque loans). collateralTokenSent: Total collateralToken deposit. minEntryPrice: Minimum entry price for checking price divergence (Value of loan token in collateral). |
collateralToLoanRate | uint256 | The exchange rate from collateral to loan tokens. |
margin | uint256 | The amount of margin of the trade. |
isTorqueLoan | bool | Whether the loan is a Torque loan. |
Source Code
function _emitOpeningEvents(
LoanParams memory loanParamsLocal,
Loan memory loanLocal,
MarginTradeStructHelpers.SentAddresses memory sentAddresses,
MarginTradeStructHelpers.SentAmounts memory sentValues,
uint256 collateralToLoanRate,
uint256 margin,
bool isTorqueLoan
) internal {
if (isTorqueLoan) {
emit Borrow(
sentAddresses.borrower, /// user (borrower)
sentAddresses.lender, /// lender
loanLocal.id, /// loanId
loanParamsLocal.loanToken, /// loanToken
loanParamsLocal.collateralToken, /// collateralToken
sentValues.newPrincipal, /// newPrincipal
sentValues.collateralTokenSent, /// newCollateral
sentValues.interestRate, /// interestRate
sentValues.interestDuration, /// interestDuration
collateralToLoanRate, /// collateralToLoanRate,
margin /// currentMargin
);
} else {
/// currentLeverage = 100 / currentMargin
margin = SafeMath.div(10**38, margin);
emit Trade(
sentAddresses.borrower, /// user (trader)
sentAddresses.lender, /// lender
loanLocal.id, /// loanId
loanParamsLocal.collateralToken, /// collateralToken
loanParamsLocal.loanToken, /// loanToken
sentValues.collateralTokenSent, /// positionSize
sentValues.newPrincipal, /// borrowedAmount
sentValues.interestRate, /// interestRate,
loanLocal.endTimestamp, /// settlementDate
sentValues.loanToCollateralSwapRate, /// entryPrice (loanToCollateralSwapRate)
sentValues.entryLeverage, /// entryLeverage
margin /// currentLeverage
);
}
}
Set the delegated manager. *
function _setDelegatedManager(bytes32 loanId, address delegator, address delegated, bool toggle) internal nonpayable
Arguments
Name | Type | Description |
---|---|---|
loanId | bytes32 | The ID of the loan. If 0, start a new loan. |
delegator | address | The address of previous manager. |
delegated | address | The address of the delegated manager. |
toggle | bool | The flag true/false for the delegated manager. |
Source Code
function _setDelegatedManager(
bytes32 loanId,
address delegator,
address delegated,
bool toggle
) internal {
delegatedManagers[loanId][delegated] = toggle;
emit DelegatedManagerSet(loanId, delegator, delegated, toggle);
}
Calculate whether the collateral is satisfied. *
function _isCollateralSatisfied(struct LoanParamsStruct.LoanParams loanParamsLocal, struct LoanStruct.Loan loanLocal, uint256 initialMargin, uint256 newCollateral, uint256 collateralAmountRequired) internal view
returns(bool)
Arguments
Name | Type | Description |
---|---|---|
loanParamsLocal | struct LoanParamsStruct.LoanParams | The loan parameters. |
loanLocal | struct LoanStruct.Loan | The loan object. |
initialMargin | uint256 | The initial amount of margin. |
newCollateral | uint256 | The amount of new collateral. |
collateralAmountRequired | uint256 | The amount of required collateral. * |
Returns
Whether the collateral is satisfied.
Source Code
function _isCollateralSatisfied(
LoanParams memory loanParamsLocal,
Loan memory loanLocal,
uint256 initialMargin,
uint256 newCollateral,
uint256 collateralAmountRequired
) internal view returns (bool) {
/// Allow at most 2% under-collateralized.
collateralAmountRequired = collateralAmountRequired.mul(98 ether).div(100 ether);
if (newCollateral < collateralAmountRequired) {
/// Check that existing collateral is sufficient coverage.
if (loanLocal.collateral != 0) {
uint256 maxDrawdown =
IPriceFeeds(priceFeeds).getMaxDrawdown(
loanParamsLocal.loanToken,
loanParamsLocal.collateralToken,
loanLocal.principal,
loanLocal.collateral,
initialMargin
);
return newCollateral.add(maxDrawdown) >= collateralAmountRequired;
} else {
return false;
}
}
return true;
}
Initialize a loan. *
function _initializeLoan(struct LoanParamsStruct.LoanParams loanParamsLocal, bytes32 loanId, uint256 initialMargin, struct MarginTradeStructHelpers.SentAddresses sentAddresses, uint256 newPrincipal) internal nonpayable
returns(bytes32)
Arguments
Name | Type | Description |
---|---|---|
loanParamsLocal | struct LoanParamsStruct.LoanParams | The loan parameters. |
loanId | bytes32 | The ID of the loan. |
initialMargin | uint256 | The amount of margin of the trade. |
sentAddresses | struct MarginTradeStructHelpers.SentAddresses | The addresses to send tokens: lender, borrower, receiver and manager: lender: must match loan if loanId provided. borrower: must match loan if loanId provided. receiver: receiver of funds (address(0) assumes borrower address). manager: delegated manager of loan unless address(0). |
newPrincipal | uint256 | New loan size (borrowAmount + any borrowed interest). |
Returns
The loanId.
Source Code
function _initializeLoan(
LoanParams memory loanParamsLocal,
bytes32 loanId,
uint256 initialMargin,
MarginTradeStructHelpers.SentAddresses memory sentAddresses,
uint256 newPrincipal
) internal returns (bytes32) {
require(loanParamsLocal.active, "loanParams disabled");
address lender = sentAddresses.lender;
address borrower = sentAddresses.borrower;
address manager = sentAddresses.manager;
Loan memory loanLocal;
if (loanId == 0) {
borrowerNonce[borrower]++;
loanId = keccak256(
abi.encodePacked(loanParamsLocal.id, lender, borrower, borrowerNonce[borrower])
);
require(loans[loanId].id == 0, "loan exists");
loanLocal = Loan({
id: loanId,
loanParamsId: loanParamsLocal.id,
pendingTradesId: 0,
active: true,
principal: newPrincipal,
collateral: 0, /// calculated later
startTimestamp: block.timestamp,
endTimestamp: 0, /// calculated later
startMargin: initialMargin,
startRate: 0, /// queried later
borrower: borrower,
lender: lender
});
activeLoansSet.addBytes32(loanId);
lenderLoanSets[lender].addBytes32(loanId);
borrowerLoanSets[borrower].addBytes32(loanId);
} else {
loanLocal = loans[loanId];
require(
loanLocal.active && block.timestamp < loanLocal.endTimestamp,
"loan has ended"
);
require(loanLocal.borrower == borrower, "borrower mismatch");
require(loanLocal.lender == lender, "lender mismatch");
require(loanLocal.loanParamsId == loanParamsLocal.id, "loanParams mismatch");
loanLocal.principal = loanLocal.principal.add(newPrincipal);
}
if (manager != address(0)) {
_setDelegatedManager(loanId, borrower, manager, true);
}
loans[loanId] = loanLocal;
return loanId;
}
Initialize a loan interest. *
function _initializeInterest(struct LoanParamsStruct.LoanParams loanParamsLocal, struct LoanStruct.Loan loanLocal, uint256 newRate, uint256 newPrincipal, uint256 torqueInterest) internal nonpayable
returns(interestAmountRequired uint256)
Arguments
Name | Type | Description |
---|---|---|
loanParamsLocal | struct LoanParamsStruct.LoanParams | The loan parameters. |
loanLocal | struct LoanStruct.Loan | The loan object. |
newRate | uint256 | The new interest rate of the loan. |
newPrincipal | uint256 | The new principal amount of the loan. |
torqueInterest | uint256 | The interest rate of the Torque loan. * |
Returns
interestAmountRequired The interest amount required.
Source Code
function _initializeInterest(
LoanParams memory loanParamsLocal,
Loan storage loanLocal,
uint256 newRate,
uint256 newPrincipal,
uint256 torqueInterest /// ignored for fixed-term loans
) internal returns (uint256 interestAmountRequired) {
/// Pay outstanding interest to lender.
_payInterest(loanLocal.lender, loanParamsLocal.loanToken);
LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id];
LenderInterest storage lenderInterestLocal =
lenderInterest[loanLocal.lender][loanParamsLocal.loanToken];
uint256 maxLoanTerm = loanParamsLocal.maxLoanTerm;
_settleFeeRewardForInterestExpense(
loanInterestLocal,
loanLocal.id,
loanParamsLocal.loanToken, /// fee token
loanParamsLocal.collateralToken, /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward
loanLocal.borrower,
block.timestamp
);
uint256 previousDepositRemaining;
if (maxLoanTerm == 0 && loanLocal.endTimestamp != 0) {
previousDepositRemaining = loanLocal
.endTimestamp
.sub(block.timestamp) /// block.timestamp < endTimestamp was confirmed earlier.
.mul(loanInterestLocal.owedPerDay)
.div(86400);
}
uint256 owedPerDay = newPrincipal.mul(newRate).div(365 * 10**20);
/// Update stored owedPerDay
loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay.add(owedPerDay);
lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.add(owedPerDay);
if (maxLoanTerm == 0) {
/// Indefinite-term (Torque) loan.
/// torqueInterest != 0 was confirmed earlier.
loanLocal.endTimestamp = torqueInterest
.add(previousDepositRemaining)
.mul(86400)
.div(loanInterestLocal.owedPerDay)
.add(block.timestamp);
maxLoanTerm = loanLocal.endTimestamp.sub(block.timestamp);
/// Loan term has to at least be greater than one hour.
require(maxLoanTerm > 3600, "loan too short");
interestAmountRequired = torqueInterest;
} else {
/// Fixed-term loan.
if (loanLocal.endTimestamp == 0) {
loanLocal.endTimestamp = block.timestamp.add(maxLoanTerm);
}
interestAmountRequired = loanLocal
.endTimestamp
.sub(block.timestamp)
.mul(owedPerDay)
.div(86400);
}
loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.add(
interestAmountRequired
);
/// Update remaining lender interest values.
lenderInterestLocal.principalTotal = lenderInterestLocal.principalTotal.add(newPrincipal);
lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal.add(interestAmountRequired);
}
Get the required collateral. *
function _getRequiredCollateral(address loanToken, address collateralToken, uint256 newPrincipal, uint256 marginAmount, bool isTorqueLoan) internal view
returns(collateralTokenAmount uint256)
Arguments
Name | Type | Description |
---|---|---|
loanToken | address | The loan token instance address. |
collateralToken | address | The collateral token instance address. |
newPrincipal | uint256 | The updated amount of principal (current debt). |
marginAmount | uint256 | The amount of margin of the trade. |
isTorqueLoan | bool | Whether the loan is a Torque loan. * |
Returns
collateralTokenAmount The required collateral.
Source Code
function _getRequiredCollateral(
address loanToken,
address collateralToken,
uint256 newPrincipal,
uint256 marginAmount,
bool isTorqueLoan
) internal view returns (uint256 collateralTokenAmount) {
if (loanToken == collateralToken) {
collateralTokenAmount = newPrincipal.mul(marginAmount).div(10**20);
} else {
/// Using the price feed instead of the swap expected return
/// because we need the rate in the inverse direction
/// so the swap is probably farther off than the price feed.
(uint256 sourceToDestRate, uint256 sourceToDestPrecision) =
IPriceFeeds(priceFeeds).queryRate(collateralToken, loanToken);
if (sourceToDestRate != 0) {
collateralTokenAmount = newPrincipal
.mul(sourceToDestPrecision)
.div(sourceToDestRate)
.mul(marginAmount)
.div(10**20);
/*TODO: review
collateralTokenAmount = newPrincipal.mul(sourceToDestPrecision).mul(marginAmount).div(sourceToDestRate).div(10**20);*/
}
}
// ./tests/loan-token/TradingTestToken.test.js
if (isTorqueLoan && collateralTokenAmount != 0) {
collateralTokenAmount = collateralTokenAmount.mul(10**20).div(marginAmount).add(
collateralTokenAmount
);
}
}
- Address
- Administered
- AdminRole
- AdvancedToken
- AdvancedTokenStorage
- Affiliates
- AffiliatesEvents
- ApprovalReceiver
- BProPriceFeed
- CheckpointsShared
- Constants
- Context
- DevelopmentFund
- DummyContract
- EnumerableAddressSet
- EnumerableBytes32Set
- EnumerableBytes4Set
- ERC20
- ERC20Detailed
- ErrorDecoder
- Escrow
- EscrowReward
- FeedsLike
- FeesEvents
- FeeSharingCollector
- FeeSharingCollectorProxy
- FeeSharingCollectorStorage
- FeesHelper
- FourYearVesting
- FourYearVestingFactory
- FourYearVestingLogic
- FourYearVestingStorage
- GenericTokenSender
- GovernorAlpha
- GovernorVault
- IApproveAndCall
- IChai
- IContractRegistry
- IConverterAMM
- IERC1820Registry
- IERC20_
- IERC20
- IERC777
- IERC777Recipient
- IERC777Sender
- IFeeSharingCollector
- IFourYearVesting
- IFourYearVestingFactory
- IFunctionsList
- ILiquidityMining
- ILiquidityPoolV1Converter
- ILoanPool
- ILoanToken
- ILoanTokenLogicBeacon
- ILoanTokenLogicModules
- ILoanTokenLogicProxy
- ILoanTokenModules
- ILoanTokenWRBTC
- ILockedSOV
- IMoCState
- IModulesProxyRegistry
- Initializable
- InterestUser
- IPot
- IPriceFeeds
- IPriceFeedsExt
- IProtocol
- IRSKOracle
- ISovryn
- ISovrynSwapNetwork
- IStaking
- ISwapsImpl
- ITeamVesting
- ITimelock
- IV1PoolOracle
- IVesting
- IVestingFactory
- IVestingRegistry
- IWrbtc
- IWrbtcERC20
- LenderInterestStruct
- LiquidationHelper
- LiquidityMining
- LiquidityMiningConfigToken
- LiquidityMiningProxy
- LiquidityMiningStorage
- LoanClosingsEvents
- LoanClosingsLiquidation
- LoanClosingsRollover
- LoanClosingsShared
- LoanClosingsWith
- LoanClosingsWithoutInvariantCheck
- LoanInterestStruct
- LoanMaintenance
- LoanMaintenanceEvents
- LoanOpenings
- LoanOpeningsEvents
- LoanParamsStruct
- LoanSettings
- LoanSettingsEvents
- LoanStruct
- LoanToken
- LoanTokenBase
- LoanTokenLogicBeacon
- LoanTokenLogicLM
- LoanTokenLogicProxy
- LoanTokenLogicStandard
- LoanTokenLogicStorage
- LoanTokenLogicWrbtc
- LoanTokenSettingsLowerAdmin
- LockedSOV
- MarginTradeStructHelpers
- Medianizer
- ModuleCommonFunctionalities
- ModulesCommonEvents
- ModulesProxy
- ModulesProxyRegistry
- MultiSigKeyHolders
- MultiSigWallet
- Mutex
- Objects
- OrderStruct
- OrigingVestingCreator
- OriginInvestorsClaim
- Ownable
- Pausable
- PausableOz
- PreviousLoanToken
- PreviousLoanTokenSettingsLowerAdmin
- PriceFeedRSKOracle
- PriceFeeds
- PriceFeedsLocal
- PriceFeedsMoC
- PriceFeedV1PoolOracle
- ProtocolAffiliatesInterface
- ProtocolLike
- ProtocolSettings
- ProtocolSettingsEvents
- ProtocolSettingsLike
- ProtocolSwapExternalInterface
- ProtocolTokenUser
- Proxy
- ProxyOwnable
- ReentrancyGuard
- RewardHelper
- RSKAddrValidator
- SafeERC20
- SafeMath
- SafeMath96
- setGet
- SharedReentrancyGuard
- SignedSafeMath
- SOV
- sovrynProtocol
- StakingAdminModule
- StakingGovernanceModule
- StakingInterface
- StakingProxy
- StakingRewards
- StakingRewardsProxy
- StakingRewardsStorage
- StakingShared
- StakingStakeModule
- StakingStorageModule
- StakingStorageShared
- StakingVestingModule
- StakingWithdrawModule
- State
- SwapsEvents
- SwapsExternal
- SwapsImplLocal
- SwapsImplSovrynSwap
- SwapsUser
- TeamVesting
- Timelock
- TimelockHarness
- TimelockInterface
- TokenSender
- UpgradableProxy
- USDTPriceFeed
- Utils
- VaultController
- Vesting
- VestingCreator
- VestingFactory
- VestingLogic
- VestingRegistry
- VestingRegistry2
- VestingRegistry3
- VestingRegistryLogic
- VestingRegistryProxy
- VestingRegistryStorage
- VestingStorage
- WeightedStakingModule
- WRBTC