Skip to content

Commit

Permalink
Merge pull request #564 from mysofinance/statemind-resubmission-feedback
Browse files Browse the repository at this point in the history
Statemind resubmission feedback
  • Loading branch information
jpick713 authored Jul 7, 2023
2 parents 386646f + cb0bd3a commit 32bd63b
Show file tree
Hide file tree
Showing 52 changed files with 1,690 additions and 643 deletions.
235 changes: 146 additions & 89 deletions README.md

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions contracts/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ library Errors {
error TransferToWrappedTokenFailed();
error TransferFromWrappedTokenFailed();
error StateAlreadySet();
error IOUCannotBeRedeemedOnChain();
error ReclaimableCollateralAmountZero();
error InvalidSwap();
error InvalidUpfrontFee();
Expand All @@ -106,7 +105,7 @@ library Errors {
error NoDsEth();
error TooShortTwapInterval();
error TooLongTwapInterval();
error SpotPriceDeviatesFromTwapPrice();
error TwapExceedsThreshold();
error Reentrancy();
error TokenNotStuck();
error InconsistentExpTransferFee();
Expand All @@ -115,4 +114,12 @@ library Errors {
error DisallowedSubscriptionLockup();
error IncorrectLoanAmount();
error Disabled();
error CannotRemintUnlessZeroSupply();
error TokensStillMissingFromWrapper();
error OnlyMintFromSingleTokenWrapper();
error NonMintableTokenState();
error NoTokensTransferred();
error TokenAlreadyCountedInWrapper();
error TokenNotOwnedByWrapper();
error TokenDoesNotBelongInWrapper(address tokenAddr, uint256 tokenId);
}
1 change: 1 addition & 0 deletions contracts/Helpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ library Helpers {
bytes memory sig
) internal pure returns (bytes32 r, bytes32 vs) {
require(sig.length == 64, "invalid signature length");
// solhint-disable no-inline-assembly
assembly {
// first 32 bytes, after the length prefix
r := mload(add(sig, 32))
Expand Down
3 changes: 1 addition & 2 deletions contracts/peer-to-peer/AddressRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,7 @@ contract AddressRegistry is Initializable, Ownable2Step, IAddressRegistry {
) {
revert Errors.InvalidNewOwnerProposal();
}
// @dev: access control via super.transferOwnership()
// as well as _newOwnerProposal check against address(0)
// @dev: access control check via super.transferOwnership()
super.transferOwnership(_newOwnerProposal);
}

Expand Down
28 changes: 11 additions & 17 deletions contracts/peer-to-peer/BorrowerGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ contract BorrowerGateway is ReentrancyGuard, IBorrowerGateway {
DataTypesPeerToPeer.OffChainQuote calldata offChainQuote,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple,
bytes32[] calldata proof
) external nonReentrant {
) external nonReentrant returns (DataTypesPeerToPeer.Loan memory) {
_checkDeadlineAndRegisteredVault(
borrowInstructions.deadline,
lenderVault
Expand Down Expand Up @@ -77,6 +77,7 @@ contract BorrowerGateway is ReentrancyGuard, IBorrowerGateway {
borrowInstructions.callbackAddr,
borrowInstructions.callbackData
);
return loan;
}

function borrowWithOnChainQuote(
Expand All @@ -85,7 +86,7 @@ contract BorrowerGateway is ReentrancyGuard, IBorrowerGateway {
calldata borrowInstructions,
DataTypesPeerToPeer.OnChainQuote calldata onChainQuote,
uint256 quoteTupleIdx
) external nonReentrant {
) external nonReentrant returns (DataTypesPeerToPeer.Loan memory) {
// borrow gateway just forwards data to respective vault and orchestrates transfers
// borrow gateway is oblivious towards and specific borrow details, and only fwds info
// vaults needs to check details of given quote and whether it's valid
Expand Down Expand Up @@ -132,6 +133,7 @@ contract BorrowerGateway is ReentrancyGuard, IBorrowerGateway {
borrowInstructions.callbackAddr,
borrowInstructions.callbackData
);
return loan;
}

function repay(
Expand Down Expand Up @@ -178,25 +180,16 @@ contract BorrowerGateway is ReentrancyGuard, IBorrowerGateway {
uint256 maxReclaimableCollAmount = noCompartment
? loan.initCollAmount - loan.amountReclaimedSoFar
: IBaseCompartment(loan.collTokenCompartmentAddr)
.getReclaimableBalance(
loan.initCollAmount,
loan.amountReclaimedSoFar,
loan.collToken
);
.getReclaimableBalance(loan.collToken);

// @dev: amountRepaidSoFar cannot exceed initRepayAmount
uint128 leftRepaymentAmount = loan.initRepayAmount -
loan.amountRepaidSoFar;
uint128 reclaimCollAmount;
if (leftRepaymentAmount == loanRepayInstructions.targetRepayAmount) {
reclaimCollAmount = SafeCast.toUint128(maxReclaimableCollAmount);
} else {
reclaimCollAmount = SafeCast.toUint128(
(maxReclaimableCollAmount *
uint256(loanRepayInstructions.targetRepayAmount)) /
uint256(leftRepaymentAmount)
);
}
uint128 reclaimCollAmount = SafeCast.toUint128(
(maxReclaimableCollAmount *
uint256(loanRepayInstructions.targetRepayAmount)) /
uint256(leftRepaymentAmount)
);
if (reclaimCollAmount == 0) {
revert Errors.ReclaimAmountIsZero();
}
Expand Down Expand Up @@ -275,6 +268,7 @@ contract BorrowerGateway is ReentrancyGuard, IBorrowerGateway {
return (loan, loanId, transferInstructions.upfrontFee);
}

// solhint-disable code-complexity
function _processTransfers(
address lenderVault,
DataTypesPeerToPeer.BorrowTransferInstructions
Expand Down
2 changes: 0 additions & 2 deletions contracts/peer-to-peer/DataTypesPeerToPeer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,6 @@ library DataTypesPeerToPeer {
address collReceiver;
// effective upfront fee in collateral tokens (vault or compartment)
uint256 upfrontFee;
// boolean flag indicating whether transfers relate to loan (or swap)
bool isLoan;
}

struct WrappedERC721TokenInfo {
Expand Down
44 changes: 22 additions & 22 deletions contracts/peer-to-peer/LenderVaultImpl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,8 @@ contract LenderVaultImpl is
IBaseCompartment(_loan.collTokenCompartmentAddr)
.unlockCollToVault(collToken);
} else {
totalUnlockableColl +=
((_loan.initRepayAmount - _loan.amountRepaidSoFar) *
_loan.initCollAmount) /
_loan.initRepayAmount;
totalUnlockableColl += (_loan.initCollAmount -
_loan.amountReclaimedSoFar);
}
_loan.collUnlocked = true;
unchecked {
Expand Down Expand Up @@ -225,7 +223,6 @@ contract LenderVaultImpl is
}
_loan.initRepayAmount = SafeCast.toUint128(repayAmount);
_loans.push(_loan);
transferInstructions.isLoan = true;
} else {
// note: only case left is upfrontFee = 100% and this corresponds to an outright swap;
// check that tenor is zero and earliest repay is nonzero, and compartment is zero, with no compartment transfer fee
Expand Down Expand Up @@ -410,12 +407,8 @@ contract LenderVaultImpl is
}
balances = new uint256[](tokensLen);
_lockedAmounts = new uint256[](tokensLen);
IAddressRegistry _addressRegistry = IAddressRegistry(addressRegistry);
for (uint256 i; i < tokensLen; ) {
if (
tokens[i] == address(0) ||
!_addressRegistry.isWhitelistedERC20(tokens[i])
) {
if (tokens[i] == address(0)) {
revert Errors.InvalidAddress();
}
balances[i] = IERC20Metadata(tokens[i]).balanceOf(address(this));
Expand All @@ -441,8 +434,7 @@ contract LenderVaultImpl is
) {
revert Errors.InvalidNewOwnerProposal();
}
// @dev: access control via super.transferOwnership()
// as well as _newOwnerProposal check against address(0)
// @dev: access control check via super.transferOwnership()
super.transferOwnership(_newOwnerProposal);
}

Expand Down Expand Up @@ -499,21 +491,29 @@ contract LenderVaultImpl is
) {
revert Errors.LtvHigherThanMax();
}
loanPerCollUnit = Math.mulDiv(
quoteTuple.loanPerCollUnitOrLtv,
IOracle(generalQuoteInfo.oracleAddr).getPrice(
(uint256 collTokenPriceRaw, uint256 loanTokenPriceRaw) = IOracle(
generalQuoteInfo.oracleAddr
).getRawPrices(
generalQuoteInfo.collToken,
generalQuoteInfo.loanToken
),
Constants.BASE
);
);
loanPerCollUnit =
Math.mulDiv(
quoteTuple.loanPerCollUnitOrLtv,
collTokenPriceRaw *
10 **
IERC20Metadata(generalQuoteInfo.loanToken)
.decimals(),
loanTokenPriceRaw
) /
Constants.BASE;
}
uint256 unscaledLoanAmount = loanPerCollUnit * netPledgeAmount;
uint256 collTokenDecimals = IERC20Metadata(generalQuoteInfo.collToken)
.decimals();

// calculate loan amount
loanAmount =
unscaledLoanAmount /
(10 ** IERC20Metadata(generalQuoteInfo.collToken).decimals());
loanAmount = unscaledLoanAmount / (10 ** collTokenDecimals);

// calculate repay amount and interest rate factor only for loans
if (upfrontFeePctInBase < Constants.BASE) {
Expand All @@ -533,7 +533,7 @@ contract LenderVaultImpl is
interestRateFactor,
Constants.BASE
) /
(10 ** IERC20Metadata(generalQuoteInfo.collToken).decimals());
(10 ** collTokenDecimals);
}
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/peer-to-peer/callbacks/BalancerV2Looping.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ contract BalancerV2Looping is VaultCallback {
address private constant BALANCER_V2_VAULT =
0xBA12222222228d8Ba445958a75a0704d566BF2C8;

constructor(address _borrowerGateway) VaultCallback(_borrowerGateway) {}
constructor(address _borrowerGateway) VaultCallback(_borrowerGateway) {} // solhint-disable no-empty-blocks

function borrowCallback(
DataTypesPeerToPeer.Loan calldata loan,
Expand Down
2 changes: 1 addition & 1 deletion contracts/peer-to-peer/callbacks/UniV3Looping.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ contract UniV3Looping is VaultCallback {
address private constant UNI_V3_SWAP_ROUTER =
0xE592427A0AEce92De3Edee1F18E0157C05861564;

constructor(address _borrowerGateway) VaultCallback(_borrowerGateway) {}
constructor(address _borrowerGateway) VaultCallback(_borrowerGateway) {} // solhint-disable no-empty-blocks

function borrowCallback(
DataTypesPeerToPeer.Loan calldata loan,
Expand Down
2 changes: 1 addition & 1 deletion contracts/peer-to-peer/callbacks/VaultCallback.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ abstract contract VaultCallback is IVaultCallback {
function _repayCallback(
DataTypesPeerToPeer.Loan calldata loan,
bytes calldata data
) internal virtual {}
) internal virtual {} // solhint-disable no-empty-blocks
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ contract AaveStakingCompartment is BaseCompartment {
}

function getReclaimableBalance(
uint256 /*initCollAmount*/,
uint256 /*amountReclaimedSoFar*/,
address collToken
) external view override returns (uint256) {
return IERC20(collToken).balanceOf(address(this));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import {BaseCompartment} from "../BaseCompartment.sol";
import {Errors} from "../../../Errors.sol";

contract CurveLPStakingCompartment is BaseCompartment {
// solhint-disable no-empty-blocks

using SafeERC20 for IERC20;

address public liqGaugeAddr;
Expand Down Expand Up @@ -113,8 +111,6 @@ contract CurveLPStakingCompartment is BaseCompartment {
}

function getReclaimableBalance(
uint256 /*initCollAmount*/,
uint256 /*amountReclaimedSoFar*/,
address collToken
) external view override returns (uint256 reclaimableCollBalance) {
reclaimableCollBalance = IERC20(collToken).balanceOf(address(this));
Expand Down Expand Up @@ -164,6 +160,7 @@ contract CurveLPStakingCompartment is BaseCompartment {
withdrawAmount,
true
)
// solhint-disable no-empty-blocks
{
// version 3, 4, or 5 gauge
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import {IGLPStakingHelper} from "../../interfaces/compartments/staking/IGLPStaki
import {BaseCompartment} from "../BaseCompartment.sol";

contract GLPStakingCompartment is BaseCompartment {
// solhint-disable no-empty-blocks

using SafeERC20 for IERC20;

// arbitrum WETH address
Expand Down Expand Up @@ -50,8 +48,6 @@ contract GLPStakingCompartment is BaseCompartment {
}

function getReclaimableBalance(
uint256 /*initCollAmount*/,
uint256 /*amountReclaimedSoFar*/,
address collToken
) external view override returns (uint256) {
return IERC20(collToken).balanceOf(address(this));
Expand All @@ -72,9 +68,10 @@ contract GLPStakingCompartment is BaseCompartment {
return;
}

//solhint-ignore-empty-blocks
// solhint-disable no-empty-blocks
try IGLPStakingHelper(FEE_GLP).claim(address(this)) {
// do nothing
// solhint-disable no-empty-blocks
} catch {
// do nothing
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ contract VoteCompartment is BaseCompartment {
}

function getReclaimableBalance(
uint256 /*initCollAmount*/,
uint256 /*amountReclaimedSoFar*/,
address collToken
) external view override returns (uint256) {
return IERC20(collToken).balanceOf(address(this));
Expand Down
6 changes: 4 additions & 2 deletions contracts/peer-to-peer/interfaces/IBorrowerGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface IBorrowerGateway {
* @param offChainQuote quote data (see DataTypesPeerToPeer comments)
* @param quoteTuple quote data (see DataTypesPeerToPeer comments)
* @param proof array of bytes needed for merkle tree verification of quote
* @return loan data
*/
function borrowWithOffChainQuote(
address lenderVault,
Expand All @@ -38,22 +39,23 @@ interface IBorrowerGateway {
DataTypesPeerToPeer.OffChainQuote calldata offChainQuote,
DataTypesPeerToPeer.QuoteTuple calldata quoteTuple,
bytes32[] memory proof
) external;
) external returns (DataTypesPeerToPeer.Loan memory);

/**
* @notice function which allows a borrower to use an onChain quote to borrow
* @param lenderVault address of the vault whose owner(s) enacted onChain quote
* @param borrowInstructions data needed for borrow (see DataTypesPeerToPeer comments)
* @param onChainQuote quote data (see DataTypesPeerToPeer comments)
* @param quoteTupleIdx index of quote tuple array
* @return loan data
*/
function borrowWithOnChainQuote(
address lenderVault,
DataTypesPeerToPeer.BorrowTransferInstructions
calldata borrowInstructions,
DataTypesPeerToPeer.OnChainQuote calldata onChainQuote,
uint256 quoteTupleIdx
) external;
) external returns (DataTypesPeerToPeer.Loan memory);

/**
* @notice function which allows a borrower to repay a loan
Expand Down
14 changes: 14 additions & 0 deletions contracts/peer-to-peer/interfaces/IOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,18 @@ interface IOracle {
address collToken,
address loanToken
) external view returns (uint256 collTokenPriceInLoanToken);

/**
* @notice function checks oracle validity and retrieves prices in base currency unit
* @param collToken address of coll token
* @param loanToken address of loan token
* @return collTokenPriceRaw and loanTokenPriceRaw denominated in base currency unit
*/
function getRawPrices(
address collToken,
address loanToken
)
external
view
returns (uint256 collTokenPriceRaw, uint256 loanTokenPriceRaw);
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,10 @@ interface IBaseCompartment {

/**
* @notice function returns the potentially reclaimable collateral token balance
* @param initCollAmount initially reclaimable collateral amount
* @param amountReclaimedSoFar amount of collateral reclaimed so far
* @param collTokenAddr address of collateral token for which reclaimable balance is being retrieved
* @dev depending on compartment implementation this could be simple balanceOf or eg staked balance call
*/
function getReclaimableBalance(
uint256 initCollAmount,
uint256 amountReclaimedSoFar,
address collTokenAddr
) external view returns (uint256 reclaimableBalance);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface ITwapGetter {
* @param outToken Address of the Out Token
* @param twapInterval Time interval for the twap
* @param uniswapV3Pool Address of the Uniswap V3 Pool
* @return twap The twap for the given uniswap v3 pool
* @return twap The twap (in out token) for the given uniswap v3 pool
*/
function getTwap(
address inToken,
Expand Down
Loading

0 comments on commit 32bd63b

Please sign in to comment.