Skip to content

Commit

Permalink
Start NFT price floor example
Browse files Browse the repository at this point in the history
  • Loading branch information
Barry Lyndon committed Aug 30, 2022
1 parent 1a9d166 commit 6ec5a2e
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 14 deletions.
36 changes: 22 additions & 14 deletions contracts/mocks/LendingClubMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol";
import "../interfaces/INFTPair.sol";
import "../interfaces/TokenLoanParamsWithOracle.sol";

// Minimal implementation to set up some tests.
contract LendingClubMock {
Expand All @@ -19,40 +20,47 @@ contract LendingClubMock {
nftPair.bentoBox().setMasterContractApproval(address(this), address(nftPair.masterContract()), true, 0, bytes32(0), bytes32(0));
}

function willLend(uint256 tokenId, TokenLoanParams memory requested) external view returns (bool) {
function willLend(
uint256 tokenId,
uint128 valuation,
uint64 duration,
uint16 annualInterestBPS,
uint16 _ltvBPS,
address _oracle
) external view returns (bool) {
if (msg.sender != address(nftPair)) {
return false;
}
TokenLoanParams memory accepted = _lendingConditions(tokenId);
TokenLoanParamsWithOracle memory accepted = _lendingConditions(tokenId)[0];
// Valuation has to be an exact match, everything else must be at least
// as good for the lender as `accepted`.

return
requested.valuation == accepted.valuation &&
requested.duration <= accepted.duration &&
requested.annualInterestBPS >= accepted.annualInterestBPS;
return valuation == accepted.valuation && duration <= accepted.duration && annualInterestBPS >= accepted.annualInterestBPS;
}

function _lendingConditions(uint256 tokenId) private pure returns (TokenLoanParams memory) {
TokenLoanParams memory conditions;
function _lendingConditions(uint256 tokenId) private pure returns (TokenLoanParamsWithOracle[] memory) {
// No specific conditions given, but we'll take all even-numbered
// ones at 100% APY:
if (tokenId % 2 == 0) {
TokenLoanParamsWithOracle[] memory conditions = new TokenLoanParamsWithOracle[](1);
// 256-bit addition fits by the above check.
// Cast is.. relatively safe: this is a mock implementation,
// production use is unlikely to follow this pattern for valuing
// loans, and manipulating the token ID can only break the logic by
// making the loan "safer" for the lender.
conditions.valuation = uint128((tokenId + 1) * 10**18);
conditions.duration = 365 days;
conditions.annualInterestBPS = 10_000;
conditions[0].valuation = uint128((tokenId + 1) * 10**18);
conditions[0].duration = 365 days;
conditions[0].annualInterestBPS = 10_000;
return conditions;
} else {
TokenLoanParamsWithOracle[] memory conditions;
return conditions;
}
return conditions;
}

function lendingConditions(address _nftPair, uint256 tokenId) external view returns (TokenLoanParams memory) {
function lendingConditions(address _nftPair, uint256 tokenId) external view returns (TokenLoanParamsWithOracle[] memory) {
if (_nftPair != address(nftPair)) {
TokenLoanParams memory empty;
TokenLoanParamsWithOracle[] memory empty;
return empty;
} else {
return _lendingConditions(tokenId);
Expand Down
69 changes: 69 additions & 0 deletions contracts/nft-examples/NFTPriceFloor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@boringcrypto/boring-solidity/contracts/libraries/BoringERC20.sol";
import "@boringcrypto/boring-solidity/contracts/BoringOwnable.sol";
import "../interfaces/INFTPair.sol";
import "../interfaces/ILendingClub.sol";
import "../interfaces/TokenLoanParams.sol";

// Not really a min price; if you "sell" to this club you get an option.

contract NFTPriceFloor is BoringOwnable, ILendingClub {
mapping(uint256 => TokenLoanParams) private loanParams;
INFTPair private immutable pair;

constructor(INFTPair nftPair) public {
pair = nftPair;
}

/// @param tokenId The token ID of the loan in question
/// @param params The loan parameters to be offered
function offer(uint256 tokenId, TokenLoanParams memory params) external onlyOwner {
loanParams[tokenId] = params;
}

function willLend(
uint256 tokenId,
uint128 valuation,
uint64 duration,
uint16 annualInterestBPS,
uint16 _ltvBPS,
address oracle
) external view override returns (bool) {
if (msg.sender != address(pair)) {
return false;
}
TokenLoanParams memory params = loanParams[tokenId];
if (valuation > params.valuation) {
return false;
}
if (duration > params.duration) {
return false;
}
if (annualInterestBPS < params.annualInterestBPS) {
return false;
}
if (oracle != address(0)) {
return false;
}
// (If we don't have the funds, just let the transaction revert)

return true;
}

function lendingConditions(address nftPair, uint256 tokenId) external view override returns (TokenLoanParamsWithOracle[] memory) {
TokenLoanParams memory params = loanParams[tokenId];
if (nftPair != address(pair) || params.valuation == 0) {
TokenLoanParamsWithOracle[] memory empty;
return empty;
}
// TODO: Cast?
TokenLoanParamsWithOracle[] memory conditions = new TokenLoanParamsWithOracle[](1);
conditions[0].valuation = params.valuation;
conditions[0].duration = params.duration;
conditions[0].annualInterestBPS = params.annualInterestBPS;
return conditions;
}
}

0 comments on commit 6ec5a2e

Please sign in to comment.