Skip to content

Instantly share code, notes, and snippets.

@bzpassersby
Created April 22, 2023 14:49
Show Gist options
  • Save bzpassersby/2db9cfc096bea931f36521c20a109417 to your computer and use it in GitHub Desktop.
Save bzpassersby/2db9cfc096bea931f36521c20a109417 to your computer and use it in GitHub Desktop.
//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