Platform: Code4rena
Start Date: 10/03/2022
Pot Size: $75,000 USDT
Total HM: 25
Participants: 54
Period: 7 days
Judge: pauliax
Total Solo HM: 10
Id: 97
League: ETH
Rank: 19/54
Findings: 4
Award: $730.31
🌟 Selected for report: 0
🚀 Solo Findings: 0
https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L170 https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L293 https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L379 https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityPool.sol#L407
Some ERC20 tokens, such as USDT, allow for charging a fee any time transfer()
or transferFrom()
is called. If a contract does not allow for amounts to change after transfers, subsequent transfer operations based on the original amount will revert()
due to the contract having an insufficient balance.
The current implementation does not work properly with fee-on-transfer tokens when calling transfer()
/transferFrom()
The following calls are separate instances of this issue where the call may suddenly start reverting for a fee-on-transfer token when the fee is turned on right before the function call, or has been on prior to the call:
SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(tokenAddress), sender, address(this), amount);
SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(tokenAddress), receiver, amountToTransfer);
SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(tokenAddress), _msgSender(), _gasFeeAccumulated);
SafeERC20Upgradeable.safeTransfer(baseToken, receiver, _tokenAmount);
Code inspection
One way to work around the issue is to measure the contract's balance right before and after the asset-transferring functions, and using the difference rather than the stated transferred amount.
#0 - ankurdubey521
2022-03-30T15:49:00Z
Duplicate of #39
#1 - pauliax
2022-04-26T10:52:16Z
99.257 USDT - $99.26
https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityProviders.sol#L116-L118 https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityProviders.sol#L148-L150 https://github.com/code-423n4/2022-03-biconomy/blob/04751283f85c9fc94fb644ff2b489ec339cd9ffc/contracts/hyphen/LiquidityProviders.sol#L317-L327
By implementing malicious versions of the interfaces required by the contracts used in the set*()
functions, an owner can rug pull user positions.
Even if the owner is benevolent the fact that there is a rug vector available may negatively impact the protocol's reputation. See these examples where similar findings have been flagged as a high-severity issues.
In a single transaction, the owner can transfer ownership to another contract that calls LiquidityProviders.setLpToken()
and LiquidityProviders.setTokenManager()
with malicious versions...
function setLpToken(address _lpToken) external onlyOwner { _setLPToken(_lpToken); }
function setTokenManager(address _tokenManager) external onlyOwner { _setTokenManager(_tokenManager); }
..and calls LiquidityProviders.increaseTokenLiquidity()
with a large _amount
...
...where lpToken.tokenMetadata()
returns an address of a token that has a supply controled by the owner and is accepted by the tokenManager
and which changes the lpToken
back to the original (there is no nonReentrant
modifier on the set functions) after safeTransferFrom()
is called, and changes the ownership back...
function increaseTokenLiquidity(uint256 _nftId, uint256 _amount) external nonReentrant whenNotPaused { (address token, , ) = lpToken.tokenMetadata(_nftId); require(_isSupportedToken(token), "ERR__TOKEN_NOT_SUPPORTED"); require(token != NATIVE, "ERR__WRONG_FUNCTION"); require( IERC20Upgradeable(token).allowance(_msgSender(), address(this)) >= _amount, "ERR__INSUFFICIENT_ALLOWANCE" ); SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(token), _msgSender(), address(liquidityPool), _amount); _increaseLiquidity(_nftId, _amount); }
...so that _increaseLiquidity()
gives the owner arbitrary amounts. The owner can then have the contract call LiquidityProviders.removeLiquidity()
to withdraw.
Alternative scenarios include:
lpToken.tokenMetadata()
return incorrect amounts, leading to the contract's accounting breaking0x00
as the various addresses for the various set*()
functions of the various contractsbaseGas
Code inspection
Don't allow the owner to change contract addresses of existing pools; require old pools to be indefinitely paused and new ones to be started.
#0 - ankurdubey521
2022-03-30T10:27:05Z
I agree this is an issue, but in the current iteration of Hyphen it is still a centralized system, therefore there is an implicit trust in the contract owners and executors. A decentralized version of the Hyphen bridge is in the works and will fix these issues.
#1 - pauliax
2022-04-26T11:09:17Z
🌟 Selected for report: hickuphh3
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xNazgul, 0xngndev, 0xwags, Cantor_Dust, CertoraInc, Dravee, IllIllI, PPrieditis, Ruhum, TerrierLover, WatchPug, XDms, benk10, berndartmueller, bitbopper, catchup, cmichel, cryptphi, csanuragjain, danb, defsec, gzeon, hagrid, hubble, jayjonah8, kenta, kyliek, minhquanym, rfa, robee, saian, samruna, throttle, ye0lde, z3s
182.6856 USDT - $182.69
setTotalCap()
should require that areWhiteListRestrictionsEnabled
is true
function setTotalCap(address _token, uint256 _totalCap) public tokenChecks(_token) onlyOwner { require(totalLiquidity[_token] <= _totalCap, "ERR__TOTAL_CAP_LESS_THAN_SL"); require(_totalCap >= perTokenWalletCap[_token], "ERR__TOTAL_CAP_LT_PTWC"); if (perTokenTotalCap[_token] != _totalCap) { perTokenTotalCap[_token] = _totalCap; emit TotalCapUpdated(_token, _totalCap); } }
The current total may be N, and the cap may be set to N+X. Since areWhiteListRestrictionsEnabled
is not forced to be true
, users may push the total to N+X+Y, and when the restriction is enabled, the total will already be over the limit.
__gap[50]
storage variable to allow for new storage variables in later versionsSee this link for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.
contract LiquidityPoolProxy is TransparentUpgradeableProxy {
contract LPToken is OwnableUpgradeable, ReentrancyGuardUpgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable, ERC2771ContextUpgradeable, Pausable
contract HyphenLiquidityFarming is Initializable, ERC2771ContextUpgradeable, OwnableUpgradeable, Pausable, ReentrancyGuardUpgradeable, IERC721ReceiverUpgradeable
contract LiquidityPool is ReentrancyGuardUpgradeable, Pausable, OwnableUpgradeable, ERC2771ContextUpgradeable {
contract LiquidityProviders is Initializable, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, OwnableUpgradeable, Pausable
contract WhitelistPeriodManager is Initializable, OwnableUpgradeable, Pausable, ERC2771ContextUpgradeable {
getAllNftIdsByUser()
should take in a beginning and ending index to avoid running out of gas due to calls to tokenOfOwnerByIndex()
function getAllNftIdsByUser(address _owner) public view returns (uint256[] memory) {
LPToken
should be defined as is ILPToken
to avoid function signature mistakes leading to incompatibilitycontract LPToken is OwnableUpgradeable, ReentrancyGuardUpgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable, ERC2771ContextUpgradeable, Pausable {
NATIVE
should be an address guaranteed to not match an address a user may useIt's safer to use 0x00
as the native address, since that's what solidity itself does. A user may happen to create a contract or EOA with the current value.
address internal constant NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
The best-practice is to follow the check-effects-interactions pattern
delete nftInfo[_nftId];
totalSharesStaked[baseToken] -= amount;
/// reightful owner of the extra tokens, and ether, protector of mistaken transfers, mother of token reclaimers,
* @dev Internal function to calculate amount of token that needs to be transfered afetr deducting all required fees.
abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
and node_modules/@openzeppelin/contracts-upgradeable/metatx/ERC2771ContextUpgradeable.sol
abstract contract Pausable is Initializable, PausableUpgradeable {
and node_modules/@openzeppelin/contracts/security/Pausable.sol
address
mappings should be combined into a single mapping
of an address
to a struct
, where appropriatemapping(address => address) public rewardTokens; /// @notice Staker => NFTs staked mapping(address => uint256[]) public nftIdsStaked; /// @notice Token => Total Shares Staked mapping(address => uint256) public totalSharesStaked; /// @notice Token => Reward Rate Updation history mapping(address => RewardsPerSecondEntry[]) public rewardRateLog;
mapping(address => uint256) public gasFeeAccumulatedByToken; // Gas fee accumulated by token address => executor address mapping(address => mapping(address => uint256)) public gasFeeAccumulated; // Incentive Pool amount per token address mapping(address => uint256) public incentivePool;
mapping(address => uint256) public totalReserve; // Include Liquidity + Fee accumulated mapping(address => uint256) public totalLiquidity; // Include Liquidity only mapping(address => uint256) public currentLiquidity; // Include current liquidity, updated on every in and out transfer mapping(address => uint256) public totalLPFees; mapping(address => uint256) public totalSharesMinted;
mapping(address => bool) public isExcludedAddress; // Token -> TVL mapping(address => uint256) private totalLiquidity; // Token -> TVL mapping(address => mapping(address => uint256)) public totalLiquidityByLp; /* Caps */ // Token Address -> Limit mapping(address => uint256) public perTokenTotalCap; // Token Address -> Limit mapping(address => uint256) public perTokenWalletCap;
bool public areWhiteListRestrictionsEnabled;
This variable tracks whether the asset caps are enabled, not whether there's an allow-list. Consider renaming to areCapsEnabled
10e18
) rather than exponentiation (e.g. 10**18
)uint256 public constant BASE_DIVISOR = 10**18;
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents' functions and change the visibility from external
to public
.
function setSvgHelper(address _tokenAddress, ISvgHelper _svgHelper) public onlyOwner {
function getAllNftIdsByUser(address _owner) public view returns (uint256[] memory) {
function exists(uint256 _tokenId) public view returns (bool) {
function getEquilibriumFee(address tokenAddress) public view override returns (uint256) {
function getMaxFee(address tokenAddress) public view override returns (uint256) {
function getTokensInfo(address tokenAddress) public view override returns (TokenInfo memory) {
function getDepositConfig(uint256 toChainId, address tokenAddress) public view override returns (TokenConfig memory)
function getTransferConfig(address tokenAddress) public view override returns (TokenConfig memory) {
function getExecutorStatus(address executor) public view override returns (bool status) {
function getAllExecutors() public view override returns (address[] memory) {
function setRewardPerSecond(address _baseToken, uint256 _rewardPerSecond) public onlyOwner {
function getNftIdsStaked(address _user) public view returns (uint256[] memory nftIds) {
function getRewardRatePerSecond(address _baseToken) public view returns (uint256) {
function setTrustedForwarder(address trustedForwarder) public onlyOwner {
function setLiquidityProviders(address _liquidityProviders) public onlyOwner {
function getExecutorManager() public view returns (address) {
function getTotalReserveByToken(address tokenAddress) public view returns (uint256) {
function getSuppliedLiquidityByToken(address tokenAddress) public view returns (uint256) {
function getTotalLPFeeByToken(address tokenAddress) public view returns (uint256) {
function getCurrentLiquidity(address tokenAddress) public view returns (uint256) {
function increaseCurrentLiquidity(address tokenAddress, uint256 amount) public onlyLiquidityPool {
function decreaseCurrentLiquidity(address tokenAddress, uint256 amount) public onlyLiquidityPool {
function getFeeAccumulatedOnNft(uint256 _nftId) public view returns (uint256) {
1e6
) rather than decimal literals (e.g. 1000000
), for readabilityuint256 private constant BASE_DIVISOR = 10000000000; // Basis Points * 100 for better accuracy
rewardAmount = (amount * incentivePool[tokenAddress] * 10000000000) / liquidityDifference;
rewardAmount = rewardAmount / 10000000000;
require()
/revert()
statements should have descriptive reason stringsrequire(newPauser != address(0));
The code is missing a space between if
and its condition statement
if(nftSuppliedLiquidity > eligibleLiquidity) {
if(nftSuppliedLiquidity > eligibleLiquidity) {
only*()
modifiersSee this issue from a prior contest for details
function changeFee( address tokenAddress, uint256 _equilibriumFee, uint256 _maxFee ) external override onlyOwner whenNotPaused {
function setTokenTransferOverhead(address tokenAddress, uint256 gasOverhead) external tokenChecks(tokenAddress) onlyOwner
function setRewardPerSecond(address _baseToken, uint256 _rewardPerSecond) public onlyOwner {
function setTrustedForwarder(address trustedForwarder) public onlyOwner {
function setBaseGas(uint128 gas) external onlyOwner {
function setExecutorManager(address _executorManagerAddress) external onlyOwner {
function sendFundsToUser( address tokenAddress, uint256 amount, address payable receiver, bytes memory depositHash, uint256 tokenGasPrice, uint256 fromChainId ) external nonReentrant onlyExecutor tokenChecks(tokenAddress) whenNotPaused {
function addLPFee(address _token, uint256 _amount) external onlyLiquidityPool tokenChecks(_token) whenNotPaused {
🌟 Selected for report: Dravee
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xNazgul, 0xngndev, 0xwags, Cantor_Dust, CertoraInc, IllIllI, Jujic, Kenshin, Kiep, PPrieditis, TerrierLover, Tomio, WatchPug, antonttc, benk10, berndartmueller, bitbopper, csanuragjain, defsec, gzeon, hagrid, hickuphh3, kenta, minhquanym, oyc_109, pedroais, peritoflores, rfa, robee, saian, samruna, sirhashalot, throttle, wuwe1, z3s
349.1007 USDT - $349.10
(address token, , ) = lpToken.tokenMetadata(_tokenId);
https://en.wikipedia.org/wiki/Memoization
function tokenURI(uint256 tokenId) public view virtual override(ERC721Upgradeable, ERC721URIStorageUpgradeable) returns (string memory)
address[] internal executors; mapping(address => bool) internal executorStatus;
By storing the index of the user in the executors
array as the value for the executorStatus
mapping, you can later delete the user from array using that index, which refunds gas
require(ifEnabled(totalLiquidity[_token] + _amount <= perTokenTotalCap[_token]), "ERR__LIQUIDITY_EXCEEDS_PTTC"); require( ifEnabled(totalLiquidityByLp[_token][_lp] + _amount <= perTokenWalletCap[_token]), "ERR__LIQUIDITY_EXCEEDS_PTWC" );
Wrap the two require statements in two if
-statement that uses the left-most conditions from inside ifEnabled()
, to get rid of the duplicate calls to ifEnabled()
See this issue from a prior contest for details
"@openzeppelin/contracts": "4.3.0",
for (uint256 i = 0; i < nftIds.length; ++i) {
uint256 count = 0;
for (uint256 i = 0; i < _length; ++i) {
uint256 leadingZeroesToAddBeforeDecimal = 0;
for (uint256 index = 0; index < tokenConfig.length; ++index) {
for (uint256 i = 0; i < executorArray.length; ++i) {
for (uint256 i = 0; i < executorArray.length; ++i) {
uint256 accumulator = 0;
for (uint256 i = 0; i < _addresses.length; ++i) {
for (uint256 i = 0; i < _tokens.length; ++i) {
uint256 maxLp = 0;
++i
/i++
should be unchecked{++i}
/unchecked{++i}
when it is not possible for them to overflow, as is the case when used in for
- and while
-loopsfor (uint256 i = 0; i < nftIds.length; ++i) {
for (uint256 i = 0; i < _length; ++i) {
for (uint256 index = 0; index < tokenConfig.length; ++index) {
for (uint256 i = 0; i < executorArray.length; ++i) {
for (uint256 i = 0; i < executorArray.length; ++i) {
for (index = 0; index < nftsStakedLength; ++index) {
for (uint256 i = 0; i < _addresses.length; ++i) {
for (uint256 i = 0; i < _tokens.length; ++i) {
for (uint256 i = 1; i <= totalSupply; ++i) {
<array>.length
should not be looked up in every loop of a for
-loopEven memory arrays incur the overhead of bit tests and bit shifts to calculate the array length
for (uint256 i = 0; i < nftIds.length; ++i) {
for (uint256 index = 0; index < tokenConfig.length; ++index) {
for (uint256 i = 0; i < executorArray.length; ++i) {
for (uint256 i = 0; i < executorArray.length; ++i) {
for (uint256 i = 0; i < _addresses.length; ++i) {
for (uint256 i = 0; i < _tokens.length; ++i) {
calldata
instead of memory
for read-only arguments in external
functions saves gasfunction updateTokenMetadata(uint256 _tokenId, LpTokenMetadata memory _lpTokenMetadata)
uint256[] memory toChainId,
address[] memory tokenAddresses,
TokenConfig[] memory tokenConfig
bytes memory depositHash
string memory tag
string memory tag
bytes memory depositHash,
address[] memory _tokens,
uint256[] memory _totalCaps,
uint256[] memory _perTokenWalletCaps
function setIsExcludedAddressStatus(address[] memory _addresses, bool[] memory _status) external;
function setIsExcludedAddressStatus(address[] memory _addresses, bool[] memory _status) external;
function setBackgroundPngUrl(string memory _backgroundPngUrl) external;
string memory _name,
string memory _symbol,
bytes memory _data
function updateTokenMetadata(uint256 _tokenId, LpTokenMetadata memory _lpTokenMetadata) external;
string memory tag
string memory tag
string memory tag
bytes memory depositHash,
function setIsExcludedAddressStatus(address[] memory _addresses, bool[] memory _status) external onlyOwner {
function setIsExcludedAddressStatus(address[] memory _addresses, bool[] memory _status) external onlyOwner {
address[] memory _tokens,
uint256[] memory _totalCaps,
uint256[] memory _perTokenWalletCaps
internal
functions only called once should be inlined to save gasfunction _getZeroString(uint256 _length) internal pure returns (string memory) {
function _truncateDigitsFromRight(uint256 _number, uint256 _digitsCount) internal pure returns (uint256) {
function _calculatePercentage(uint256 _num, uint256 _denom) internal pure returns (string memory) {
function getAmountToTransfer( uint256 initialGas, address tokenAddress, uint256 amount, uint256 tokenGasPrice ) internal returns (uint256 amountToTransfer) {
function _isSupportedToken(address _token) internal view returns (bool) {
function _changePauser(address newPauser) internal {
return ERC2771Context._msgSender();
return ERC2771ContextUpgradeable._msgSender();
return ERC2771ContextUpgradeable._msgSender();
return ERC2771ContextUpgradeable._msgSender();
return super._msgSender();
return super._msgSender();
private
rather than public
for constants, saves gasIf needed, the value can be read from the verified contract source code
uint256 public constant BASE_DIVISOR = 10**18;
require()
statements that use &&
saves gasSee this issue for an example
require( (toChainId.length == tokenAddresses.length) && (tokenAddresses.length == tokenConfig.length), " ERR_ARRAY_LENGTH_MISMATCH" );
require( tokenManager.getDepositConfig(toChainId, tokenAddress).min <= amount && tokenManager.getDepositConfig(toChainId, tokenAddress).max >= amount, "Deposit amount not in Cap limit" );
require( tokenManager.getDepositConfig(toChainId, NATIVE).min <= msg.value && tokenManager.getDepositConfig(toChainId, NATIVE).max >= msg.value, "Deposit amount not in Cap limit" );
require( tokenManager.getTransferConfig(tokenAddress).min <= amount && tokenManager.getTransferConfig(tokenAddress).max >= amount, "Withdraw amnt not in Cap limits" );
require( _tokens.length == _totalCaps.length && _totalCaps.length == _perTokenWalletCaps.length, "ERR__LENGTH_MISMACH" );
> 0
costs more gas than != 0
when used on a uint
in a require()
statementrequire(_amount > 0, "ERR__AMOUNT_IS_0");
require(_amount > 0, "ERR__AMOUNT_IS_0");
require(lpFeeAccumulated > 0, "ERR__NO_REWARDS_TO_CLAIM");
require()
/revert()
strings longer than 32 bytes cost extra gasrequire(_whiteListPeriodManager != address(0), "ERR_INVALID_WHITELIST_PERIOD_MANAGER");
require(executorStatus[msg.sender], "You are not allowed to perform this operation");
require(_msgSender() == address(liquidityProviders), "Only liquidityProviders is allowed");
require(isPauser(msg.sender), "Only pauser is allowed to perform this operation");
The instances below point to the second access of a state variable within a function
ILiquidityProviders(liquidityProvidersAddress).totalReserve(tokenAddress)
tokenMetadata[tokenId].suppliedLiquidity,
tokenMetadata[tokenId].suppliedLiquidity
ISvgHelper svgHelper = ISvgHelper(svgHelpers[tokenAddress]);
tokensInfo[tokenAddress].maxFee = _maxFee;
tokensInfo[tokenAddress].tokenConfig = transferConfig[tokenAddress];
tokensInfo[tokenAddress].supportedToken,
depositConfig[toChainId[index]][tokenAddresses[index]].max = tokenConfig[index].max;
transferConfig[tokenAddress].max = maxCapLimit;
transferConfig[tokenAddress].max = maxCapLimit;
lpToken.isApprovedForAll(msgSender, address(this)) || lpToken.getApproved(_nftId) == address(this),
lpToken.safeTransferFrom(address(this), msgSender, _nftId);
return accumulator + poolInfo[_baseToken].accTokenPerShare;
IERC20Upgradeable rewardToken = IERC20Upgradeable(rewardTokens[baseToken]);
if (nftIdsStaked[msgSender][index] == _nftId) {
emit LogUpdatePool(_baseToken, pool.lastRewardTime, totalSharesStaked[_baseToken], pool.accTokenPerShare);
rewardRateLog[_baseToken][i].rewardsPerSecond *
return rewardRateLog[_baseToken][rewardRateLog[_baseToken].length - 1].rewardsPerSecond;
tokenManager.getDepositConfig(toChainId, tokenAddress).max >= amount,
tokenManager.getDepositConfig(toChainId, NATIVE).max >= msg.value,
tokenManager.getTransferConfig(tokenAddress).max >= amount,
lpFee = (amount * tokenManager.getTokensInfo(tokenAddress).equilibriumFee) / BASE_DIVISOR;
uint256 maxFee = tokenManager.getTokensInfo(tokenAddress).maxFee;
liquidityProviders.totalLPFees(tokenAddress) -
gasFeeAccumulatedByToken[tokenAddress] = gasFeeAccumulatedByToken[tokenAddress] + gasFee;
gasFeeAccumulatedByToken[tokenAddress] = gasFeeAccumulatedByToken[tokenAddress] - _gasFeeAccumulated;
gasFeeAccumulatedByToken[NATIVE] = gasFeeAccumulatedByToken[NATIVE] - _gasFeeAccumulated;
gasFeeAccumulated[tokenAddress][_msgSender()] = gasFeeAccumulated[tokenAddress][_msgSender()] + gasFee;
gasFeeAccumulated[tokenAddress][_msgSender()] = 0;
gasFeeAccumulated[NATIVE][_msgSender()] = 0;
incentivePool[tokenAddress] = incentivePool[tokenAddress] - rewardAmount;
rewardAmount = (amount * incentivePool[tokenAddress] * 10000000000) / liquidityDifference;
incentivePool[NATIVE] = incentivePool[NATIVE] - rewardAmount;
(incentivePool[tokenAddress] +
(address _tokenAddress, uint256 nftSuppliedLiquidity, uint256 totalNFTShares) = lpToken.tokenMetadata(_nftId);
lpToken.updateTokenMetadata(nftId, data);
lpToken.updateTokenMetadata(_nftId, data);
lpToken.updateTokenMetadata(_nftId, LpTokenMetadata(_tokenAddress, nftSuppliedLiquidity, nftShares));
mintedSharesAmount = (_amount * totalSharesMinted[token]) / totalReserve[token];
emit CurrentLiquidityChanged(tokenAddress, currentLiquidity[tokenAddress]-amount, currentLiquidity[tokenAddress]);
emit CurrentLiquidityChanged(tokenAddress, currentLiquidity[tokenAddress]+amount, currentLiquidity[tokenAddress]);
return totalSharesMinted[_baseToken] / totalReserve[_baseToken];
uint256 liquidity = totalLiquidityByLp[_token][lpToken.ownerOf(i)];
return !areWhiteListRestrictionsEnabled || (areWhiteListRestrictionsEnabled && _cond);
totalLiquidityByLp[_token][_lp] += _amount;
bool
s for storage incurs overhead// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
mapping(address => bool) internal executorStatus;
mapping(bytes32 => bool) public processedHash;
bool public areWhiteListRestrictionsEnabled;
mapping(address => bool) public isExcludedAddress;
payable
If a function modifier such as onlyOwner
is used, the function will revert if a normal user tries to pay the function. Marking the function as payable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.
function setSvgHelper(address _tokenAddress, ISvgHelper _svgHelper) public onlyOwner {
function setLiquidityProviders(address _liquidityProviders) external onlyOwner {
function setWhiteListPeriodManager(address _whiteListPeriodManager) external onlyOwner {
function mint(address _to) external onlyHyphenPools whenNotPaused nonReentrant returns (uint256) {
function updateTokenMetadata(uint256 _tokenId, LpTokenMetadata memory _lpTokenMetadata) external onlyHyphenPools whenNotPaused
function setTokenDecimals(uint256 _tokenDecimals) public onlyOwner {
function changeFee( address tokenAddress, uint256 _equilibriumFee, uint256 _maxFee ) external override onlyOwner whenNotPaused {
function setTokenTransferOverhead(address tokenAddress, uint256 gasOverhead) external tokenChecks(tokenAddress) onlyOwner
function setDepositConfig( uint256[] memory toChainId, address[] memory tokenAddresses, TokenConfig[] memory tokenConfig ) external onlyOwner {
function addSupportedToken( address tokenAddress, uint256 minCapLimit, uint256 maxCapLimit, uint256 equilibriumFee, uint256 maxFee ) external onlyOwner {
function removeSupportedToken(address tokenAddress) external tokenChecks(tokenAddress) onlyOwner {
function updateTokenCap( address tokenAddress, uint256 minCapLimit, uint256 maxCapLimit ) external tokenChecks(tokenAddress) onlyOwner {
function addExecutors(address[] calldata executorArray) external override onlyOwner {
function addExecutor(address executorAddress) public override onlyOwner {
function removeExecutors(address[] calldata executorArray) external override onlyOwner {
function removeExecutor(address executorAddress) public override onlyOwner {
function initalizeRewardPool( address _baseToken, address _rewardToken, uint256 _rewardPerSecond ) external onlyOwner {
function setRewardPerSecond(address _baseToken, uint256 _rewardPerSecond) public onlyOwner {
function reclaimTokens( address _token, uint256 _amount, address payable _to ) external nonReentrant onlyOwner {
function setTrustedForwarder(address trustedForwarder) public onlyOwner {
function setLiquidityProviders(address _liquidityProviders) public onlyOwner {
function setBaseGas(uint128 gas) external onlyOwner {
function setExecutorManager(address _executorManagerAddress) external onlyOwner {
function sendFundsToUser( address tokenAddress, uint256 amount, address payable receiver, bytes memory depositHash, uint256 tokenGasPrice, uint256 fromChainId ) external nonReentrant onlyExecutor tokenChecks(tokenAddress) whenNotPaused {
function withdrawErc20GasFee(address tokenAddress) external onlyExecutor whenNotPaused nonReentrant {
function withdrawNativeGasFee() external onlyExecutor whenNotPaused nonReentrant {
function transfer( address _tokenAddress, address receiver, uint256 _tokenAmount ) external whenNotPaused onlyLiquidityProviders nonReentrant {
function setLpToken(address _lpToken) external onlyOwner {
function increaseCurrentLiquidity(address tokenAddress, uint256 amount) public onlyLiquidityPool {
function decreaseCurrentLiquidity(address tokenAddress, uint256 amount) public onlyLiquidityPool {
function setTokenManager(address _tokenManager) external onlyOwner {
function setWhiteListPeriodManager(address _whiteListPeriodManager) external onlyOwner {
function setLiquidityPool(address _liquidityPool) external onlyOwner {
function addLPFee(address _token, uint256 _amount) external onlyLiquidityPool tokenChecks(_token) whenNotPaused {
function beforeLiquidityAddition( address _lp, address _token, uint256 _amount ) external onlyLiquidityPool whenNotPaused {
function beforeLiquidityRemoval( address _lp, address _token, uint256 _amount ) external onlyLiquidityPool whenNotPaused {
function beforeLiquidityTransfer( address _from, address _to, address _token, uint256 _amount ) external onlyLpNft whenNotPaused {
function setTokenManager(address _tokenManager) external onlyOwner {
function setLiquidityProviders(address _liquidityProviders) external onlyOwner {
function setLpToken(address _lpToken) external onlyOwner {
function setIsExcludedAddressStatus(address[] memory _addresses, bool[] memory _status) external onlyOwner {
function setTotalCap(address _token, uint256 _totalCap) public tokenChecks(_token) onlyOwner {
function setPerTokenWalletCap(address _token, uint256 _perTokenWalletCap) public tokenChecks(_token) onlyOwner {
function setCap( address _token, uint256 _totalCap, uint256 _perTokenWalletCap ) public onlyOwner {
function setCaps( address[] memory _tokens, uint256[] memory _totalCaps, uint256[] memory _perTokenWalletCaps ) external onlyOwner {
function setAreWhiteListRestrictionsEnabled(bool _status) external onlyOwner {
function changePauser(address newPauser) public onlyPauser {
function renouncePauser() external virtual onlyPauser {
function pause() public onlyPauser {
function unpause() public onlyPauser {
revert()/require()
stringspragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;
pragma solidity 0.8.0;