Skip to content

Commit

Permalink
Merge pull request #42 from bcnmy/HYP-331
Browse files Browse the repository at this point in the history
HP-25: C4 Audit Fixes, Dynamic Fee Changes
  • Loading branch information
tomarsachin2271 authored May 2, 2022
2 parents 4e46a5d + 3368dfc commit cb76e25
Show file tree
Hide file tree
Showing 26 changed files with 5,927 additions and 94 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,4 @@ typechain
#Hardhat files
cache
artifacts
.openzeppelin
.openzeppelin
57 changes: 43 additions & 14 deletions contracts/hyphen/LiquidityFarming.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ contract HyphenLiquidityFarming is
lpToken = _lpToken;
}

function setTrustedForwarder(address _tf) external onlyOwner {
_setTrustedForwarder(_tf);
}

/// @notice Initialize the rewarder pool.
/// @param _baseToken Base token to be used for the rewarder pool.
/// @param _rewardToken Reward token to be used for the rewarder pool.
Expand Down Expand Up @@ -183,6 +187,7 @@ contract HyphenLiquidityFarming is
/// @notice Sets the sushi per second to be distributed. Can only be called by the owner.
/// @param _rewardPerSecond The amount of Sushi to be distributed per second.
function setRewardPerSecond(address _baseToken, uint256 _rewardPerSecond) external onlyOwner {
require(_rewardPerSecond <= 10**40, "ERR__REWARD_PER_SECOND_TOO_HIGH");
rewardRateLog[_baseToken].push(RewardsPerSecondEntry(_rewardPerSecond, block.timestamp));
emit LogRewardPerSecond(_baseToken, _rewardPerSecond);
}
Expand Down Expand Up @@ -245,17 +250,35 @@ contract HyphenLiquidityFarming is
/// @param _nftId LP token nftId to withdraw.
/// @param _to The receiver of `amount` withdraw benefit.
function withdraw(uint256 _nftId, address payable _to) external whenNotPaused nonReentrant {
address msgSender = _msgSender();
uint256 nftsStakedLength = nftIdsStaked[msgSender].length;
uint256 index = getStakedNftIndex(_msgSender(), _nftId);
_withdraw(_nftId, _to, index);
}

function getStakedNftIndex(address _staker, uint256 _nftId) public view returns (uint256) {
uint256 nftsStakedLength = nftIdsStaked[_staker].length;
uint256 index;
for (index = 0; index < nftsStakedLength; ++index) {
if (nftIdsStaked[msgSender][index] == _nftId) {
break;
unchecked {
for (index = 0; index < nftsStakedLength; ++index) {
if (nftIdsStaked[_staker][index] == _nftId) {
return index;
}
}
}
revert("ERR__NFT_NOT_STAKED");
}

function _withdraw(
uint256 _nftId,
address payable _to,
uint256 _index
) private {
address msgSender = _msgSender();

require(nftIdsStaked[msgSender][_index] == _nftId, "ERR__NOT_OWNER");
require(nftInfo[_nftId].staker == msgSender, "ERR__NOT_OWNER");
require(nftInfo[_nftId].unpaidRewards == 0, "ERR__UNPAID_REWARDS_EXIST");

require(index != nftsStakedLength, "ERR__NFT_NOT_STAKED");
nftIdsStaked[msgSender][index] = nftIdsStaked[msgSender][nftIdsStaked[msgSender].length - 1];
nftIdsStaked[msgSender][_index] = nftIdsStaked[msgSender][nftIdsStaked[msgSender].length - 1];
nftIdsStaked[msgSender].pop();

_sendRewardsForNft(_nftId, _to);
Expand All @@ -270,6 +293,14 @@ contract HyphenLiquidityFarming is
emit LogWithdraw(msgSender, baseToken, _nftId, _to);
}

function withdrawV2(
uint256 _nftId,
address payable _to,
uint256 _index
) external whenNotPaused nonReentrant {
_withdraw(_nftId, _to, _index);
}

/// @notice Extract all rewards without withdrawing LP tokens
/// @param _nftId LP token nftId for which rewards are to be withdrawn
/// @param _to The receiver of withdraw benefit.
Expand Down Expand Up @@ -332,14 +363,12 @@ contract HyphenLiquidityFarming is
/// @return pool Returns the pool that was updated.
function updatePool(address _baseToken) public whenNotPaused returns (PoolInfo memory pool) {
pool = poolInfo[_baseToken];
if (block.timestamp > pool.lastRewardTime) {
if (totalSharesStaked[_baseToken] > 0) {
pool.accTokenPerShare = getUpdatedAccTokenPerShare(_baseToken);
}
pool.lastRewardTime = block.timestamp;
poolInfo[_baseToken] = pool;
emit LogUpdatePool(_baseToken, pool.lastRewardTime, totalSharesStaked[_baseToken], pool.accTokenPerShare);
if (totalSharesStaked[_baseToken] > 0) {
pool.accTokenPerShare = getUpdatedAccTokenPerShare(_baseToken);
}
pool.lastRewardTime = block.timestamp;
poolInfo[_baseToken] = pool;
emit LogUpdatePool(_baseToken, pool.lastRewardTime, totalSharesStaked[_baseToken], pool.accTokenPerShare);
}

/// @notice View function to see the tokens staked by a given user.
Expand Down
113 changes: 106 additions & 7 deletions contracts/hyphen/LiquidityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ contract LiquidityPool is
string tag
);
event GasFeeWithdraw(address indexed tokenAddress, address indexed owner, uint256 indexed amount);
event TrustedForwarderChanged(address indexed forwarderAddress);
event LiquidityProvidersChanged(address indexed liquidityProvidersAddress);
event TokenManagerChanged(address indexed tokenManagerAddress);
event BaseGasUpdated(uint256 indexed baseGas);
event EthReceived(address, uint256);

// MODIFIERS
Expand Down Expand Up @@ -127,9 +127,7 @@ contract LiquidityPool is
}

function setTrustedForwarder(address trustedForwarder) external onlyOwner {
require(trustedForwarder != address(0), "TrustedForwarder can't be 0");
_trustedForwarder = trustedForwarder;
emit TrustedForwarderChanged(trustedForwarder);
_setTrustedForwarder(trustedForwarder);
}

function setLiquidityProviders(address _liquidityProviders) external onlyOwner {
Expand All @@ -146,6 +144,7 @@ contract LiquidityPool is

function setBaseGas(uint128 gas) external onlyOwner {
baseGas = gas;
emit BaseGasUpdated(baseGas);
}

function getExecutorManager() external view returns (address) {
Expand Down Expand Up @@ -181,6 +180,8 @@ contract LiquidityPool is
uint256 amount,
string calldata tag
) public tokenChecks(tokenAddress) whenNotPaused nonReentrant {
require(toChainId != block.chainid, "To chain must be different than current chain");
require(tokenAddress != NATIVE, "wrong function");
TokenConfig memory config = tokenManager.getDepositConfig(toChainId, tokenAddress);

require(config.min <= amount && config.max >= amount, "Deposit amount not in Cap limit");
Expand Down Expand Up @@ -270,6 +271,7 @@ contract LiquidityPool is
uint256 toChainId,
string calldata tag
) external payable whenNotPaused nonReentrant {
require(toChainId != block.chainid, "To chain must be different than current chain");
require(
tokenManager.getDepositConfig(toChainId, NATIVE).min <= msg.value &&
tokenManager.getDepositConfig(toChainId, NATIVE).max >= msg.value,
Expand Down Expand Up @@ -369,28 +371,125 @@ contract LiquidityPool is
return [amountToTransfer, lpFee, transferFeeAmount, gasFee];
}

function sendFundsToUserV2(
address tokenAddress,
uint256 amount,
address payable receiver,
bytes calldata depositHash,
uint256 nativeTokenPriceInTransferredToken,
uint256 fromChainId
) external nonReentrant onlyExecutor whenNotPaused {
uint256 initialGas = gasleft();
TokenConfig memory config = tokenManager.getTransferConfig(tokenAddress);
require(config.min <= amount && config.max >= amount, "Withdraw amount not in Cap limit");
require(receiver != address(0), "Bad receiver address");

(bytes32 hashSendTransaction, bool status) = checkHashStatus(tokenAddress, amount, receiver, depositHash);

require(!status, "Already Processed");
processedHash[hashSendTransaction] = true;

// uint256 amountToTransfer, uint256 lpFee, uint256 transferFeeAmount, uint256 gasFee
uint256[4] memory transferDetails = getAmountToTransferV2(
initialGas,
tokenAddress,
amount,
nativeTokenPriceInTransferredToken
);

liquidityProviders.decreaseCurrentLiquidity(tokenAddress, transferDetails[0]);

if (tokenAddress == NATIVE) {
(bool success, ) = receiver.call{value: transferDetails[0]}("");
require(success, "Native Transfer Failed");
} else {
SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(tokenAddress), receiver, transferDetails[0]);
}

emit AssetSent(
tokenAddress,
amount,
transferDetails[0],
receiver,
depositHash,
fromChainId,
transferDetails[1],
transferDetails[2],
transferDetails[3]
);
}

/**
* @dev Internal function to calculate amount of token that needs to be transfered afetr deducting all required fees.
* Fee to be deducted includes gas fee, lp fee and incentive pool amount if needed.
* @param initialGas Gas provided initially before any calculations began
* @param tokenAddress Token address for which calculation needs to be done
* @param amount Amount of token to be transfered before deducting the fee
* @param nativeTokenPriceInTransferredToken Price of native token in terms of the token being transferred (multiplied base div), used to calculate gas fee
* @return [ amountToTransfer, lpFee, transferFeeAmount, gasFee ]
*/
function getAmountToTransferV2(
uint256 initialGas,
address tokenAddress,
uint256 amount,
uint256 nativeTokenPriceInTransferredToken
) internal returns (uint256[4] memory) {
TokenInfo memory tokenInfo = tokenManager.getTokensInfo(tokenAddress);
uint256 transferFeePerc = _getTransferFee(tokenAddress, amount, tokenInfo);
uint256 lpFee;
if (transferFeePerc > tokenInfo.equilibriumFee) {
// Here add some fee to incentive pool also
lpFee = (amount * tokenInfo.equilibriumFee) / BASE_DIVISOR;
unchecked {
incentivePool[tokenAddress] += (amount * (transferFeePerc - tokenInfo.equilibriumFee)) / BASE_DIVISOR;
}
} else {
lpFee = (amount * transferFeePerc) / BASE_DIVISOR;
}
uint256 transferFeeAmount = (amount * transferFeePerc) / BASE_DIVISOR;

liquidityProviders.addLPFee(tokenAddress, lpFee);

uint256 totalGasUsed = initialGas + tokenInfo.transferOverhead + baseGas - gasleft();
uint256 gasFee = (totalGasUsed * nativeTokenPriceInTransferredToken * tx.gasprice) / BASE_DIVISOR;

gasFeeAccumulatedByToken[tokenAddress] += gasFee;
gasFeeAccumulated[tokenAddress][_msgSender()] += gasFee;
uint256 amountToTransfer = amount - (transferFeeAmount + gasFee);
return [amountToTransfer, lpFee, transferFeeAmount, gasFee];
}

function _getTransferFee(
address tokenAddress,
uint256 amount,
TokenInfo memory tokenInfo
) private view returns (uint256 fee) {
) private view returns (uint256) {
uint256 currentLiquidity = getCurrentLiquidity(tokenAddress);
uint256 providedLiquidity = liquidityProviders.getSuppliedLiquidityByToken(tokenAddress);

uint256 resultingLiquidity = currentLiquidity - amount;

// We return a constant value in excess state
if (resultingLiquidity > providedLiquidity) {
return tokenManager.excessStateTransferFeePerc(tokenAddress);
}

// Fee is represented in basis points * 10 for better accuracy
uint256 numerator = providedLiquidity * tokenInfo.equilibriumFee * tokenInfo.maxFee; // F(max) * F(e) * L(e)
uint256 numerator = providedLiquidity * providedLiquidity * tokenInfo.equilibriumFee * tokenInfo.maxFee; // F(max) * F(e) * L(e) ^ 2
uint256 denominator = tokenInfo.equilibriumFee *
providedLiquidity *
providedLiquidity +
(tokenInfo.maxFee - tokenInfo.equilibriumFee) *
resultingLiquidity; // F(e) * L(e) + (F(max) - F(e)) * L(r)
resultingLiquidity *
resultingLiquidity; // F(e) * L(e) ^ 2 + (F(max) - F(e)) * L(r) ^ 2

uint256 fee;
if (denominator == 0) {
fee = 0;
} else {
fee = numerator / denominator;
}
return fee;
}

function getTransferFee(address tokenAddress, uint256 amount) external view returns (uint256) {
Expand Down
32 changes: 19 additions & 13 deletions contracts/hyphen/LiquidityProviders.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// $$\ $$\ $$\ $$\ $$\ $$\ $$$$$$$\ $$\ $$\
// $$ | \__| \__| $$ |\__| $$ | $$ __$$\ \__| $$ |
// $$ | $$\ $$$$$$\ $$\ $$\ $$\ $$$$$$$ |$$\ $$$$$$\ $$\ $$\ $$ | $$ | $$$$$$\ $$$$$$\ $$\ $$\ $$\ $$$$$$$ | $$$$$$\ $$$$$$\ $$$$$$$\
// $$\ $$\ $$\ $$\ $$\ $$\ $$$$$$$\ $$\ $$\
// $$ | \__| \__| $$ |\__| $$ | $$ __$$\ \__| $$ |
// $$ | $$\ $$$$$$\ $$\ $$\ $$\ $$$$$$$ |$$\ $$$$$$\ $$\ $$\ $$ | $$ | $$$$$$\ $$$$$$\ $$\ $$\ $$\ $$$$$$$ | $$$$$$\ $$$$$$\ $$$$$$$\
// $$ | $$ |$$ __$$\ $$ | $$ |$$ |$$ __$$ |$$ |\_$$ _| $$ | $$ | $$$$$$$ |$$ __$$\ $$ __$$\\$$\ $$ |$$ |$$ __$$ |$$ __$$\ $$ __$$\ $$ _____|
// $$ | $$ |$$ / $$ |$$ | $$ |$$ |$$ / $$ |$$ | $$ | $$ | $$ | $$ ____/ $$ | \__|$$ / $$ |\$$\$$ / $$ |$$ / $$ |$$$$$$$$ |$$ | \__|\$$$$$$\
// $$ | $$ |$$ | $$ |$$ | $$ |$$ |$$ | $$ |$$ | $$ |$$\ $$ | $$ | $$ | $$ | $$ | $$ | \$$$ / $$ |$$ | $$ |$$ ____|$$ | \____$$\
// $$ | $$ |$$ / $$ |$$ | $$ |$$ |$$ / $$ |$$ | $$ | $$ | $$ | $$ ____/ $$ | \__|$$ / $$ |\$$\$$ / $$ |$$ / $$ |$$$$$$$$ |$$ | \__|\$$$$$$\
// $$ | $$ |$$ | $$ |$$ | $$ |$$ |$$ | $$ |$$ | $$ |$$\ $$ | $$ | $$ | $$ | $$ | $$ | \$$$ / $$ |$$ | $$ |$$ ____|$$ | \____$$\
// $$$$$$$$\ $$ |\$$$$$$$ |\$$$$$$ |$$ |\$$$$$$$ |$$ | \$$$$ |\$$$$$$$ | $$ | $$ | \$$$$$$ | \$ / $$ |\$$$$$$$ |\$$$$$$$\ $$ | $$$$$$$ |
// \________|\__| \____$$ | \______/ \__| \_______|\__| \____/ \____$$ | \__| \__| \______/ \_/ \__| \_______| \_______|\__| \_______/
// $$ | $$\ $$ |
// $$ | \$$$$$$ |
// \__| \______/
// \________|\__| \____$$ | \______/ \__| \_______|\__| \____/ \____$$ | \__| \__| \______/ \_/ \__| \_______| \_______|\__| \_______/
// $$ | $$\ $$ |
// $$ | \$$$$$$ |
// \__| \______/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

Expand Down Expand Up @@ -167,6 +167,10 @@ contract LiquidityProviders is
tokenManager = ITokenManager(_tokenManager);
}

function setTrustedForwarder(address _tf) external onlyOwner {
_setTrustedForwarder(_tf);
}

/**
* @dev To be called post initialization, used to set address of WhiteListPeriodManager Contract
* @param _whiteListPeriodManager address of WhiteListPeriodManager
Expand All @@ -189,8 +193,8 @@ contract LiquidityProviders is
* @return Price of Base token in terms of LP Shares
*/
function getTokenPriceInLPShares(address _baseToken) public view returns (uint256) {
uint256 supply = totalSharesMinted[_baseToken];
if (supply > 0) {
uint256 reserve = totalReserve[_baseToken];
if (reserve > 0) {
return totalSharesMinted[_baseToken] / totalReserve[_baseToken];
}
return BASE_DIVISOR;
Expand Down Expand Up @@ -259,6 +263,7 @@ contract LiquidityProviders is
* record in the newly minted NFT
*/
function addNativeLiquidity() external payable nonReentrant tokenChecks(NATIVE) whenNotPaused {
require(address(liquidityPool) != address(0), "ERR__LIQUIDITY_POOL_NOT_SET");
(bool success, ) = address(liquidityPool).call{value: msg.value}("");
require(success, "ERR__NATIVE_TRANSFER_FAILED");
_addLiquidity(NATIVE, msg.value);
Expand Down Expand Up @@ -296,7 +301,7 @@ contract LiquidityProviders is

uint256 mintedSharesAmount;
// Adding liquidity in the pool for the first time
if (totalReserve[token] == 0) {
if (totalReserve[token] == 0 || totalSharesMinted[token] == 0) {
mintedSharesAmount = BASE_DIVISOR * _amount;
} else {
mintedSharesAmount = (_amount * totalSharesMinted[token]) / totalReserve[token];
Expand Down Expand Up @@ -344,6 +349,7 @@ contract LiquidityProviders is
(address token, , ) = lpToken.tokenMetadata(_nftId);
require(_isSupportedToken(NATIVE), "ERR__TOKEN_NOT_SUPPORTED");
require(token == NATIVE, "ERR__WRONG_FUNCTION");
require(address(liquidityPool) != address(0), "ERR__LIQUIDITY_POOL_NOT_SET");
(bool success, ) = address(liquidityPool).call{value: msg.value}("");
require(success, "ERR__NATIVE_TRANSFER_FAILED");
_increaseLiquidity(_nftId, msg.value);
Expand All @@ -365,7 +371,7 @@ contract LiquidityProviders is
require(_amount != 0, "ERR__INVALID_AMOUNT");
require(nftSuppliedLiquidity >= _amount, "ERR__INSUFFICIENT_LIQUIDITY");
whiteListPeriodManager.beforeLiquidityRemoval(_msgSender(), _tokenAddress, _amount);
// Claculate how much shares represent input amount
// Calculate how much shares represent input amount
uint256 lpSharesForInputAmount = _amount * getTokenPriceInLPShares(_tokenAddress);

// Calculate rewards accumulated
Expand Down
4 changes: 4 additions & 0 deletions contracts/hyphen/interfaces/ITokenManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ interface ITokenManager {
TokenConfig memory config
);

function excessStateTransferFeePerc(address tokenAddress) external view returns (uint256);

function getTokensInfo(address tokenAddress) external view returns (TokenInfo memory);

function getDepositConfig(uint256 toChainId, address tokenAddress) external view returns (TokenConfig memory);

function getTransferConfig(address tokenAddress) external view returns (TokenConfig memory);

function changeExcessStateFee(address _tokenAddress, uint256 _excessStateFeePer) external;
}
Loading

0 comments on commit cb76e25

Please sign in to comment.