Created
April 22, 2023 14:49
-
-
Save bzpassersby/2db9cfc096bea931f36521c20a109417 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: Unlicense | |
pragma solidity ^0.8.13; | |
import "forge-std/Test.sol"; | |
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; | |
import {MathUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; | |
contract LenderCommitmentForwarderExcerpt { | |
enum CommitmentCollateralType { | |
NONE, // no collateral required | |
ERC20, | |
ERC721, | |
ERC1155, | |
ERC721_ANY_ID, | |
ERC1155_ANY_ID | |
} | |
/** | |
* @notice Calculate the amount of collateral required to borrow a loan with _principalAmount of principal | |
* @param _principalAmount The amount of currency to borrow for the loan. | |
* @param _maxPrincipalPerCollateralAmount The ratio for the amount of principal that can be borrowed for each amount of collateral. This is expanded additionally by the principal decimals. | |
* @param _collateralTokenType The type of collateral for the loan either ERC20, ERC721, ERC1155, or None. | |
* @param _collateralTokenAddress The contract address for the collateral for the loan. | |
* @param _principalTokenAddress The contract address for the principal for the loan. | |
*/ | |
function getRequiredCollateral( | |
uint256 _principalAmount, | |
uint256 _maxPrincipalPerCollateralAmount, | |
CommitmentCollateralType _collateralTokenType, | |
address _collateralTokenAddress, | |
address _principalTokenAddress | |
) public view virtual returns (uint256) { | |
if (_collateralTokenType == CommitmentCollateralType.NONE) { | |
return 0; | |
} | |
uint8 collateralDecimals; | |
uint8 principalDecimals = IERC20MetadataUpgradeable( | |
_principalTokenAddress | |
).decimals(); | |
if (_collateralTokenType == CommitmentCollateralType.ERC20) { | |
collateralDecimals = IERC20MetadataUpgradeable( | |
_collateralTokenAddress | |
).decimals(); | |
} | |
/* | |
* The principalAmount is expanded by (collateralDecimals+principalDecimals) to increase precision | |
* and then it is divided by _maxPrincipalPerCollateralAmount which should already been expanded by principalDecimals | |
*/ | |
return | |
MathUpgradeable.mulDiv( | |
_principalAmount, | |
(10 ** (collateralDecimals + principalDecimals)), | |
_maxPrincipalPerCollateralAmount, | |
MathUpgradeable.Rounding.Up | |
); | |
} | |
} | |
contract USDT { | |
function decimals() external view returns (uint8) { | |
return 6; | |
} | |
} | |
contract WETH { | |
function decimals() external view returns (uint8) { | |
return 18; | |
} | |
} | |
contract RequiredCollateralOverScaledTest is | |
LenderCommitmentForwarderExcerpt, | |
Test | |
{ | |
USDT private usdt; | |
WETH private wEth; | |
function setUp() public { | |
usdt = new USDT(); | |
wEth = new WETH(); | |
} | |
/**PrincipalToken WETH: 18 decimals | |
* CollateralToken USDT: 6 decimals | |
* current price: 1 WETH -> 1946.23 USD (https://www.coinbase.com/converter/weth/usd) | |
* maxPrincipalPerCollateralAmount: | |
* For demonstration, Suppose lender set max principal to be roughly 100% of collateral value. | |
* In reality, lender can set this less than 100%, which increase required collateral even more. | |
* (1) Price Ratio is converted based on unit eth, i.e 1 eth of USD and 1 eth of WETH. | |
* 0.00051381388*(10**18)=5.1381388e+14 This number is already expanded by principal decimals. | |
* (2)(For comparison only) Price Ratio is converted based on smallest divisible unit.This method mixes the difference in decimal places with difference in coin values and should not be used. | |
* 10**18/(1946.23*10**6)*(10**18)=5.1381388e+26 This number is already expanded by principal decimals. | |
*/ | |
function test_GetRequiredCollateralOverScaled_method_1_WETH_USDT() public { | |
uint256 principalAmount = 1 * 10 ** 18; //1 WETH | |
uint256 maxPrincipalPerCollateralAmount = 5.1381388 * 10 ** 14; // Expanded by principal decimals | |
CommitmentCollateralType collateralTokenType = CommitmentCollateralType | |
.ERC20; | |
address collateralTokenAddress = address(usdt); | |
address principalTokenAddress = address(wEth); | |
uint256 borrowerExpectedCollateral = 1946.23 * 10 ** 6 + 100; //1 WETH -> 1946.23 USDT + 100 Buffer | |
uint256 requiredCollateral = getRequiredCollateral( | |
principalAmount, | |
maxPrincipalPerCollateralAmount, | |
collateralTokenType, | |
collateralTokenAddress, | |
principalTokenAddress | |
); | |
console.log(requiredCollateral); | |
//Logs: 1946230023992345243768035228 | |
assertEq(requiredCollateral > borrowerExpectedCollateral, true); | |
assertEq(requiredCollateral > 10 ** 9 * 10 ** 6, true); // required more than 1 billion USD as colleteral for 1 WETH loan! | |
} | |
function test_GetRequiredCollateralOverScaled_method_2_WETH_USDT() public { | |
uint256 principalAmount = 1 * 10 ** 18; //1 WETH | |
uint256 maxPrincipalPerCollateralAmount = 5.1381388 * 10 ** 26; // Expanded by principal decimals | |
CommitmentCollateralType collateralTokenType = CommitmentCollateralType | |
.ERC20; | |
address collateralTokenAddress = address(usdt); | |
address principalTokenAddress = address(wEth); | |
uint256 borrowerExpectedCollateral = 1946.23 * 10 ** 6 + 100; //1 WETH -> 1946.23 USDT + 100 Buffer | |
uint256 requiredCollateral = getRequiredCollateral( | |
principalAmount, | |
maxPrincipalPerCollateralAmount, | |
collateralTokenType, | |
collateralTokenAddress, | |
principalTokenAddress | |
); | |
console.log(requiredCollateral); | |
//Logs: 1946230023992346 | |
assertEq(requiredCollateral > borrowerExpectedCollateral, true); | |
assertEq(requiredCollateral > 10 ** 9 * 10 ** 6, true); //require more than 1 billion USD as collateral for 1 WETH loan! | |
} | |
/**PrincipalToken USDT: 6 decimals | |
* CollateralToken WETH: 18 decimals | |
* current price: 1 WETH -> 1946.23 USD (https://www.coinbase.com/converter/weth/usd) | |
* maxPrincipalPerCollateralAmount: | |
* For demonstration, Suppose lender set max principal to be roughly 100% of collateral value. | |
* In reality, lender can set this less than 100%, which increase required collateral even more. | |
* (1) Price Ratio is converted based on unit eth, i.e 1 eth of USD and 1 eth of WETH. | |
* 1946.23e6 This number is already expanded by principal decimals. | |
* (2)(For comparison only) Price Ratio is converted based on smallest divisible unit. This method mixes the difference in decimal places with difference in coin values. | |
* and should not be used. | |
* 1.94623e-3 This number is already expanded by principal decimals. | |
*/ | |
function test_GetRequiredCollateralOverScaled_method_1_USDT_WETH() public { | |
uint256 principalAmount = 1946.23 * 10 ** 6; //1946.23 USDT | |
uint256 maxPrincipalPerCollateralAmount = 1946.23 * 10 ** 6; // Expanded by principal decimals | |
CommitmentCollateralType collateralTokenType = CommitmentCollateralType | |
.ERC20; | |
address collateralTokenAddress = address(usdt); | |
address principalTokenAddress = address(wEth); | |
uint256 borrowerExpectedCollateral = 1 * 10 ** 18 + 100; //1 WETH -> 1946.23 USDT + 100 Buffer | |
uint256 requiredCollateral = getRequiredCollateral( | |
principalAmount, | |
maxPrincipalPerCollateralAmount, | |
collateralTokenType, | |
collateralTokenAddress, | |
principalTokenAddress | |
); | |
console.log(requiredCollateral); | |
//Logs: 1000000000000000000000000 | |
assertEq(requiredCollateral > borrowerExpectedCollateral, true); | |
assertEq(requiredCollateral > 100000 ether, true); // required more than 100000 WETH as colleteral for 1946.23 USDT loan! | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment