Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: atomic ntoken #326

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

contracts=$( (git diff --cached --name-only --diff-filter=ACMR | grep -Ei "\.sol$") || true)
scripts=$( (git diff --cached --name-only --diff-filter=ACMR | grep -Ei "\.ts$") || true)
if [ -z "${contracts}" ] && [ -z "${scripts}" ]; then
exit 0
fi
make format
if [ ! -z "${contracts}" ]; then
yarn typechain
git add $(echo "$contracts" | paste -s -d " " -)
fi

yarn typechain
yarn lint
if [ ! -z "${scripts}" ]; then
git add $(echo "$scripts" | paste -s -d " " -)
fi
69 changes: 69 additions & 0 deletions contracts/interfaces/IAtomicCollateralizableERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.10;

/**
* @title IAtomicCollateralizableERC721
* @author Parallel
* @notice Defines the basic interface for an AtomicCollateralizableERC721.
**/
interface IAtomicCollateralizableERC721 {
/**
* @dev get the collateralized atomic token balance of a specific user
*/
function atomicCollateralizedBalanceOf(address user)
external
view
returns (uint256);

/**
* @dev get the atomic token balance of a specific user
*/
function atomicBalanceOf(address user) external view returns (uint256);

/**
* @dev get the token balance of a specific user
*/
function underlyingBalancesOf(address user)
external
view
returns (
uint256,
uint256,
uint256,
uint256
);

/**
* @dev get the atomic token id of a specific user of specific index
*/
function atomicTokenOfOwnerByIndex(address user, uint256 index)
external
view
returns (uint256);

/**
* @dev check if specific token is atomic (has multiplier)
*/
function isAtomicToken(uint256 tokenId) external view returns (bool);

/**
* @dev check if specific token has atomic pricing (has atomic oracle wrapper)
*/
function isAtomicPricing() external view returns (bool);

/**
* @dev get the trait multiplier of specific token
*/
function getTraitMultiplier(uint256 tokenId)
external
view
returns (uint256);

/**
* @dev get the trait multiplier sum of all collateralized tokens
*/
function getTraitMultiplierSumOfAllCollateralized(address user)
external
view
returns (uint256);
}
2 changes: 0 additions & 2 deletions contracts/interfaces/INToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,4 @@ interface INToken is
address airdropContract,
bytes calldata airdropParams
) external;

function getAtomicPricingConfig() external view returns (bool);
}
26 changes: 26 additions & 0 deletions contracts/protocol/libraries/helpers/Helpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ pragma solidity 0.8.10;

import {IERC20} from "../../../dependencies/openzeppelin/contracts/IERC20.sol";
import {DataTypes} from "../types/DataTypes.sol";
import {WadRayMath} from "../../libraries/math/WadRayMath.sol";
import {IAtomicCollateralizableERC721} from "../../../interfaces/IAtomicCollateralizableERC721.sol";

/**
* @title Helpers library
*
*/
library Helpers {
using WadRayMath for uint256;

/**
* @notice Fetches the user current stable and variable debt balances
* @param user The user address
Expand All @@ -22,4 +26,26 @@ library Helpers {
{
return (IERC20(debtTokenAddress).balanceOf(user));
}

function isTraitMultiplierEffective(uint256 multiplier)
internal
pure
returns (bool)
{
return multiplier != 0 && multiplier != WadRayMath.WAD;
}

function getTraitBoostedTokenPrice(
address xTokenAddress,
uint256 assetPrice,
uint256 tokenId
) internal view returns (uint256) {
uint256 multiplier = IAtomicCollateralizableERC721(xTokenAddress)
.getTraitMultiplier(tokenId);
if (isTraitMultiplierEffective(multiplier)) {
return assetPrice.wadMul(multiplier);
} else {
return assetPrice;
}
}
}
51 changes: 19 additions & 32 deletions contracts/protocol/libraries/logic/GenericLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {Math} from "../../../dependencies/openzeppelin/contracts/Math.sol";
import {IScaledBalanceToken} from "../../../interfaces/IScaledBalanceToken.sol";
import {INToken} from "../../../interfaces/INToken.sol";
import {ICollateralizableERC721} from "../../../interfaces/ICollateralizableERC721.sol";
import {IAtomicCollateralizableERC721} from "../../../interfaces/IAtomicCollateralizableERC721.sol";
import {IPriceOracleGetter} from "../../../interfaces/IPriceOracleGetter.sol";
import {ReserveConfiguration} from "../configuration/ReserveConfiguration.sol";
import {UserConfiguration} from "../configuration/UserConfiguration.sol";
Expand All @@ -15,7 +16,8 @@ import {WadRayMath} from "../math/WadRayMath.sol";
import {DataTypes} from "../types/DataTypes.sol";
import {ReserveLogic} from "./ReserveLogic.sol";
import {INonfungiblePositionManager} from "../../../dependencies/uniswap/INonfungiblePositionManager.sol";
import {XTokenType} from "../../../interfaces/IXTokenType.sol";
import {XTokenType, IXTokenType} from "../../../interfaces/IXTokenType.sol";
import {Helpers} from "../../libraries/helpers/Helpers.sol";

/**
* @title GenericLogic library
Expand Down Expand Up @@ -363,37 +365,22 @@ library GenericLogic {
DataTypes.CalculateUserAccountDataParams memory params,
CalculateUserAccountDataVars memory vars
) private view returns (uint256 totalValue) {
INToken nToken = INToken(vars.xTokenAddress);
bool isAtomicPrice = nToken.getAtomicPricingConfig();
if (isAtomicPrice) {
uint256 totalBalance = nToken.balanceOf(params.user);

for (uint256 index = 0; index < totalBalance; index++) {
uint256 tokenId = nToken.tokenOfOwnerByIndex(
params.user,
index
);
if (
ICollateralizableERC721(vars.xTokenAddress)
.isUsedAsCollateral(tokenId)
) {
totalValue += _getTokenPrice(
params.oracle,
vars.currentReserveAddress,
tokenId
);
}
}
} else {
uint256 assetPrice = _getAssetPrice(
params.oracle,
vars.currentReserveAddress
);
totalValue =
ICollateralizableERC721(vars.xTokenAddress)
.collateralizedBalanceOf(params.user) *
assetPrice;
}
uint256 assetPrice = _getAssetPrice(
params.oracle,
vars.currentReserveAddress
);

(
,
uint256 atomicBalance,
uint256 collateralizedBalance,

) = IAtomicCollateralizableERC721(vars.xTokenAddress)
.underlyingBalancesOf(params.user);
totalValue = collateralizedBalance * assetPrice;
totalValue += IAtomicCollateralizableERC721(vars.xTokenAddress)
.getTraitMultiplierSumOfAllCollateralized(params.user)
.wadMul(assetPrice);
}

function getLtvAndLTForUniswapV3(
Expand Down
16 changes: 14 additions & 2 deletions contracts/protocol/libraries/logic/LiquidationLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import {Address} from "../../../dependencies/openzeppelin/contracts/Address.sol"
import {IPToken} from "../../../interfaces/IPToken.sol";
import {IWETH} from "../../../misc/interfaces/IWETH.sol";
import {ICollateralizableERC721} from "../../../interfaces/ICollateralizableERC721.sol";
import {IAtomicCollateralizableERC721} from "../../../interfaces/IAtomicCollateralizableERC721.sol";
import {IAuctionableERC721} from "../../../interfaces/IAuctionableERC721.sol";
import {IXTokenType, XTokenType} from "../../../interfaces/IXTokenType.sol";
import {INToken} from "../../../interfaces/INToken.sol";
import {PRBMath} from "../../../dependencies/math/PRBMath.sol";
import {PRBMathUD60x18} from "../../../dependencies/math/PRBMathUD60x18.sol";
Expand All @@ -40,6 +42,7 @@ library LiquidationLogic {
using UserConfiguration for DataTypes.UserConfigurationMap;
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
using PRBMathUD60x18 for uint256;
using WadRayMath for uint256;
using GPv2SafeERC20 for IERC20;

/**
Expand Down Expand Up @@ -776,15 +779,24 @@ library LiquidationLogic {
).collateralizedBalanceOf(params.borrower);

// price of the asset that is used as collateral
if (INToken(superVars.collateralXToken).getAtomicPricingConfig()) {
if (
IXTokenType(superVars.collateralXToken).getXTokenType() ==
GopherJ marked this conversation as resolved.
Show resolved Hide resolved
XTokenType.NTokenUniswapV3
) {
vars.collateralPrice = IPriceOracleGetter(params.priceOracle)
.getTokenPrice(
params.collateralAsset,
params.collateralTokenId
);
} else {
vars.collateralPrice = IPriceOracleGetter(params.priceOracle)
uint256 assetPrice = IPriceOracleGetter(params.priceOracle)
.getAssetPrice(params.collateralAsset);

vars.collateralPrice = Helpers.getTraitBoostedTokenPrice(
superVars.collateralXToken,
assetPrice,
params.collateralTokenId
);
}

if (
Expand Down
18 changes: 7 additions & 11 deletions contracts/protocol/tokenization/NToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract NToken is VersionedInitializable, MintableIncentivizedERC721, INToken {
_setSymbol(nTokenSymbol);

require(underlyingAsset != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
_underlyingAsset = underlyingAsset;
_ERC721Data.underlyingAsset = underlyingAsset;
_ERC721Data.rewardController = incentivesController;

emit Initialized(
Expand Down Expand Up @@ -104,7 +104,7 @@ contract NToken is VersionedInitializable, MintableIncentivizedERC721, INToken {

if (receiverOfUnderlying != address(this)) {
for (uint256 index = 0; index < tokenIds.length; index++) {
IERC721(_underlyingAsset).safeTransferFrom(
IERC721(_ERC721Data.underlyingAsset).safeTransferFrom(
address(this),
receiverOfUnderlying,
tokenIds[index]
Expand Down Expand Up @@ -139,7 +139,7 @@ contract NToken is VersionedInitializable, MintableIncentivizedERC721, INToken {
uint256[] calldata ids
) external override onlyPoolAdmin {
require(
token != _underlyingAsset,
token != _ERC721Data.underlyingAsset,
Errors.UNDERLYING_ASSET_CAN_NOT_BE_TRANSFERRED
);
for (uint256 i = 0; i < ids.length; i++) {
Expand Down Expand Up @@ -192,7 +192,7 @@ contract NToken is VersionedInitializable, MintableIncentivizedERC721, INToken {
override
returns (address)
{
return _underlyingAsset;
return _ERC721Data.underlyingAsset;
}

/// @inheritdoc INToken
Expand All @@ -203,7 +203,7 @@ contract NToken is VersionedInitializable, MintableIncentivizedERC721, INToken {
onlyPool
nonReentrant
{
IERC721(_underlyingAsset).safeTransferFrom(
IERC721(_ERC721Data.underlyingAsset).safeTransferFrom(
address(this),
target,
tokenId
Expand Down Expand Up @@ -235,7 +235,7 @@ contract NToken is VersionedInitializable, MintableIncentivizedERC721, INToken {
uint256 tokenId,
bool validate
) internal virtual {
address underlyingAsset = _underlyingAsset;
address underlyingAsset = _ERC721Data.underlyingAsset;

uint256 fromBalanceBefore;
if (validate) {
Expand Down Expand Up @@ -318,11 +318,7 @@ contract NToken is VersionedInitializable, MintableIncentivizedERC721, INToken {
override
returns (string memory)
{
return IERC721Metadata(_underlyingAsset).tokenURI(tokenId);
}

function getAtomicPricingConfig() external view returns (bool) {
return ATOMIC_PRICING;
return IERC721Metadata(_ERC721Data.underlyingAsset).tokenURI(tokenId);
}

function getXTokenType()
Expand Down
6 changes: 3 additions & 3 deletions contracts/protocol/tokenization/NTokenApeStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ abstract contract NTokenApeStaking is NToken, INTokenApeStaking {
ApeStakingLogic.UnstakeAndRepayParams({
POOL: POOL,
_apeCoinStaking: _apeCoinStaking,
_underlyingAsset: _underlyingAsset,
_underlyingAsset: _ERC721Data.underlyingAsset,
poolId: POOL_ID(),
tokenId: tokenId,
incentiveReceiver: address(0),
Expand All @@ -119,7 +119,7 @@ abstract contract NTokenApeStaking is NToken, INTokenApeStaking {
ApeStakingLogic.UnstakeAndRepayParams({
POOL: POOL,
_apeCoinStaking: _apeCoinStaking,
_underlyingAsset: _underlyingAsset,
_underlyingAsset: _ERC721Data.underlyingAsset,
poolId: POOL_ID(),
tokenId: tokenIds[index],
incentiveReceiver: address(0),
Expand Down Expand Up @@ -183,7 +183,7 @@ abstract contract NTokenApeStaking is NToken, INTokenApeStaking {
ApeStakingLogic.UnstakeAndRepayParams({
POOL: POOL,
_apeCoinStaking: _apeCoinStaking,
_underlyingAsset: _underlyingAsset,
_underlyingAsset: _ERC721Data.underlyingAsset,
poolId: POOL_ID(),
tokenId: tokenId,
incentiveReceiver: incentiveReceiver,
Expand Down
Loading