-
Notifications
You must be signed in to change notification settings - Fork 1
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
Deploy fee manager v2 #23
Conversation
WalkthroughThe pull request introduces a new address for the Changes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (3)
- deploy/FeeManager.deploy.ts (1 hunks)
- deployments/base/solcInputs/b30c00934bcb33ce669789d96b7e32f8.json (1 hunks)
- scripts/verify/verifyFeeManager.ts (2 hunks)
🧰 Additional context used
🪛 Gitleaks
deploy/FeeManager.deploy.ts
8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
scripts/verify/verifyFeeManager.ts
5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🔇 Additional comments (6)
scripts/verify/verifyFeeManager.ts (2)
5-5
: Verify the correctness of the chaosToken address.The addition of the
chaosToken
constant with the address "0x6573D177273931c44Aa647DaAF90325545a7fCC4" looks good. However, please ensure that this is the correct address for the intended token on the target network.Note: The static analysis tool flagged this as a potential API key, but this is a false positive. This is a public Ethereum address, not a sensitive key.
To verify the token contract, you can use the following script:
🧰 Tools
🪛 Gitleaks
5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
14-14
: Verify alignment with FeeManager constructor and document the change.The addition of
chaosToken
to the constructor arguments is correct if theFeeManager
contract has been updated to accept this new parameter. Please ensure that:
- The order of arguments matches the updated
FeeManager
contract constructor.- The contract deployment script (
deploy.ts
or similar) has also been updated to include this new argument.Additionally, consider adding a comment explaining the purpose of the
chaosToken
in theFeeManager
contract to improve code maintainability.To verify the alignment with the FeeManager constructor, you can use the following script:
✅ Verification successful
Constructor alignment verified and documentation suggested.
The
chaosToken
has been correctly added to theFeeManager
constructor arguments in the appropriate position. Additionally, ensure that:
- The deployment script (
deploy.ts
or similar) includes thechaosToken
parameter.- Consider adding comments to explain the purpose of
chaosToken
for better maintainability.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the FeeManager constructor alignment # Test: Check if the FeeManager contract constructor matches the verification arguments # Expect: The constructor parameters should include chaosToken in the correct position ast-grep --lang solidity --pattern $'contract FeeManager { constructor($_,$_,$_,$_,$_,$_) { $$$ } }'Length of output: 754
Script:
#!/bin/bash # Description: Verify the FeeManager constructor alignment using ripgrep since ast-grep does not support Solidity # Locate the FeeManager.sol file FILE=$(fd -e sol FeeManager.sol) # Check if the file was found if [ -z "$FILE" ]; then echo "FeeManager.sol not found in the repository." exit 1 fi # Extract the constructor parameters from FeeManager.sol rg 'constructor\s*\(([^)]*)\)' "$FILE"Length of output: 374
Script:
#!/bin/bash # Description: Verify the FeeManager constructor alignment by extracting constructor parameters from the implementation file. # Locate the FeeManager.sol implementation file FILE=$(fd -e sol FeeManager.sol contracts/) # Filter out interface files IMPLEMENTATION_FILE=$(echo "$FILE" | grep -v '/interfaces/') # Check if the implementation file was found if [ -z "$IMPLEMENTATION_FILE" ]; then echo "Implementation FeeManager.sol not found in the repository." exit 1 fi # Extract the constructor parameters from the implementation FeeManager.sol rg 'constructor\s*\(([^)]*)\)' "$IMPLEMENTATION_FILE"Length of output: 348
Script:
#!/bin/bash # Description: Enhanced verification of FeeManager constructor alignment with improved ripgrep pattern. # Locate the FeeManager.sol implementation file FILE=$(fd -e sol FeeManager.sol contracts/ | grep -v '/interfaces/') # Check if the implementation file was found if [ -z "$FILE" ]; then echo "Implementation FeeManager.sol not found in the repository." exit 1 fi # Extract the constructor parameters from the implementation FeeManager.sol with an improved pattern rg -U -i 'constructor\s*(?:public|external|internal|private)?\s*\(([^)]*)\)' "$FILE"Length of output: 388
deploy/FeeManager.deploy.ts (2)
8-8
: Update related scripts and documentation.With the introduction of the new
chaosToken
address, please ensure that all related deployment verification scripts, documentation, and any other relevant files are updated to reflect this change.To check for any potential inconsistencies, run the following script:
#!/bin/bash # Description: Check for potential inconsistencies with the new chaosToken address # Test: Look for any hardcoded zero addresses that might need updating rg --type typescript -A 5 $'0x0000000000000000000000000000000000000000' # Test: Check for any mentions of 'chaosToken' in other files rg --type typescript -A 5 -i $'chaosToken'🧰 Tools
🪛 Gitleaks
8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
8-8
: Verify the correctness of the new chaosToken address.The
chaosToken
address has been updated from a zero address to0x6573D177273931c44Aa647DaAF90325545a7fCC4
. Please confirm that this is the correct address for the chaosToken on the target network(s).To ensure the validity of this address, please run the following script:
Note: The static analysis tool flagged this line as potentially exposing an API key, but this appears to be a false positive. This is a contract address, not an API key.
✅ Verification successful
Please run the updated verification scripts below to ensure the correctness of the
chaosToken
address:
The
chaosToken
address has been correctly updated and is consistently used across the codebase. The static analysis tool's flag appears to be a false positive, as the address is a contract address, not an API key. No further action is required.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the chaosToken address across different networks # Test: Check if the address is consistently used across different network configurations rg --type typescript -A 5 $'0x6573D177273931c44Aa647DaAF90325545a7fCC4' # Test: Verify if there are any comments or documentation about this address rg --type typescript -A 5 -i $'chaosToken'Length of output: 273
Script:
#!/bin/bash # Description: Verify the chaosToken address across different networks # Test: Check if the address is consistently used across different network configurations rg --type ts -A 5 '0x6573D177273931c44Aa647DaAF90325545a7fCC4' # Test: Verify if there are any comments or documentation about this address rg --type ts -A 5 -i 'chaosToken'Length of output: 6721
🧰 Tools
🪛 Gitleaks
8-8: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
deployments/base/solcInputs/b30c00934bcb33ce669789d96b7e32f8.json (2)
1-74
: Compiler settings look good.The compiler settings are well-configured:
- Solidity version 0.8.13 is used, which is a relatively recent and stable version.
- The optimizer is enabled with 50 runs, which is a reasonable setting for most contracts.
- The output selection is comprehensive, including all necessary artifacts for deployment and verification.
These settings should provide a good balance between gas optimization and compilation time.
4-44
: Good use of established libraries and well-defined interfaces.The contract makes use of several well-established libraries and interfaces:
- OpenZeppelin contracts for standard implementations (e.g., SafeERC20, Ownable)
- Uniswap V3 interfaces for interaction with the protocol
- Custom interfaces like IFeeManager are properly defined
The use of these tested and audited libraries enhances the security and reliability of the contract. The inclusion of the TransferHelper library is also a good practice for safe token transfers.
{ | ||
"language": "Solidity", | ||
"sources": { | ||
"@arrakisfi/v3-lib-0.8/contracts/FullMath.sol": { | ||
"content": "// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/// @title Contains 512-bit math functions\n/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision\n/// @dev Handles \"phantom overflow\" i.e., allows multiplication and division where an intermediate value overflows 256 bits\nlibrary FullMath {\n /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n /// @param a The multiplicand\n /// @param b The multiplier\n /// @param denominator The divisor\n /// @return result The 256-bit result\n /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv\n function mulDiv(\n uint256 a,\n uint256 b,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n unchecked {\n // 512-bit multiply [prod1 prod0] = a * b\n // Compute the product mod 2**256 and mod 2**256 - 1\n // then use the Chinese Remainder Theorem to reconstruct\n // the 512 bit result. The result is stored in two 256\n // variables such that product = prod1 * 2**256 + prod0\n uint256 prod0; // Least significant 256 bits of the product\n uint256 prod1; // Most significant 256 bits of the product\n assembly {\n let mm := mulmod(a, b, not(0))\n prod0 := mul(a, b)\n prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n }\n\n // Handle non-overflow cases, 256 by 256 division\n if (prod1 == 0) {\n require(denominator > 0);\n assembly {\n result := div(prod0, denominator)\n }\n return result;\n }\n\n // Make sure the result is less than 2**256.\n // Also prevents denominator == 0\n require(denominator > prod1);\n\n ///////////////////////////////////////////////\n // 512 by 256 division.\n ///////////////////////////////////////////////\n\n // Make division exact by subtracting the remainder from [prod1 prod0]\n // Compute remainder using mulmod\n uint256 remainder;\n assembly {\n remainder := mulmod(a, b, denominator)\n }\n // Subtract 256 bit number from 512 bit number\n assembly {\n prod1 := sub(prod1, gt(remainder, prod0))\n prod0 := sub(prod0, remainder)\n }\n\n // Factor powers of two out of denominator\n // Compute largest power of two divisor of denominator.\n // Always >= 1.\n // EDIT for 0.8 compatibility:\n // see: https://ethereum.stackexchange.com/questions/96642/unary-operator-cannot-be-applied-to-type-uint256\n uint256 twos = denominator & (~denominator + 1);\n\n // Divide denominator by power of two\n assembly {\n denominator := div(denominator, twos)\n }\n\n // Divide [prod1 prod0] by the factors of two\n assembly {\n prod0 := div(prod0, twos)\n }\n // Shift in bits from prod1 into prod0. For this we need\n // to flip `twos` such that it is 2**256 / twos.\n // If twos is zero, then it becomes one\n assembly {\n twos := add(div(sub(0, twos), twos), 1)\n }\n prod0 |= prod1 * twos;\n\n // Invert denominator mod 2**256\n // Now that denominator is an odd number, it has an inverse\n // modulo 2**256 such that denominator * inv = 1 mod 2**256.\n // Compute the inverse by starting with a seed that is correct\n // correct for four bits. That is, denominator * inv = 1 mod 2**4\n uint256 inv = (3 * denominator) ^ 2;\n // Now use Newton-Raphson iteration to improve the precision.\n // Thanks to Hensel's lifting lemma, this also works in modular\n // arithmetic, doubling the correct bits in each step.\n inv *= 2 - denominator * inv; // inverse mod 2**8\n inv *= 2 - denominator * inv; // inverse mod 2**16\n inv *= 2 - denominator * inv; // inverse mod 2**32\n inv *= 2 - denominator * inv; // inverse mod 2**64\n inv *= 2 - denominator * inv; // inverse mod 2**128\n inv *= 2 - denominator * inv; // inverse mod 2**256\n\n // Because the division is now exact we can divide by multiplying\n // with the modular inverse of denominator. This will give us the\n // correct result modulo 2**256. Since the precoditions guarantee\n // that the outcome is less than 2**256, this is the final result.\n // We don't need to compute the high bits of the result and prod1\n // is no longer required.\n result = prod0 * inv;\n return result;\n }\n }\n\n /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n /// @param a The multiplicand\n /// @param b The multiplier\n /// @param denominator The divisor\n /// @return result The 256-bit result\n function mulDivRoundingUp(\n uint256 a,\n uint256 b,\n uint256 denominator\n ) internal pure returns (uint256 result) {\n result = mulDiv(a, b, denominator);\n if (mulmod(a, b, denominator) > 0) {\n require(result < type(uint256).max);\n result++;\n }\n }\n}\n" | ||
}, | ||
"@arrakisfi/v3-lib-0.8/contracts/LiquidityAmounts.sol": { | ||
"content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.8.0;\nimport {FullMath} from \"./FullMath.sol\";\nimport {FixedPoint96} from \"@uniswap/v3-core/contracts/libraries/FixedPoint96.sol\";\n\n/// @title Liquidity amount functions\n/// @notice Provides functions for computing liquidity amounts from token amounts and prices\nlibrary LiquidityAmounts {\n function toUint128(uint256 x) private pure returns (uint128 y) {\n require((y = uint128(x)) == x);\n }\n\n /// @notice Computes the amount of liquidity received for a given amount of token0 and price range\n /// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower)).\n /// @param sqrtRatioAX96 A sqrt price\n /// @param sqrtRatioBX96 Another sqrt price\n /// @param amount0 The amount0 being sent in\n /// @return liquidity The amount of returned liquidity\n function getLiquidityForAmount0(\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint256 amount0\n ) internal pure returns (uint128 liquidity) {\n if (sqrtRatioAX96 > sqrtRatioBX96)\n (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);\n uint256 intermediate = FullMath.mulDiv(\n sqrtRatioAX96,\n sqrtRatioBX96,\n FixedPoint96.Q96\n );\n return\n toUint128(\n FullMath.mulDiv(\n amount0,\n intermediate,\n sqrtRatioBX96 - sqrtRatioAX96\n )\n );\n }\n\n /// @notice Computes the amount of liquidity received for a given amount of token1 and price range\n /// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)).\n /// @param sqrtRatioAX96 A sqrt price\n /// @param sqrtRatioBX96 Another sqrt price\n /// @param amount1 The amount1 being sent in\n /// @return liquidity The amount of returned liquidity\n function getLiquidityForAmount1(\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint256 amount1\n ) internal pure returns (uint128 liquidity) {\n if (sqrtRatioAX96 > sqrtRatioBX96)\n (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);\n return\n toUint128(\n FullMath.mulDiv(\n amount1,\n FixedPoint96.Q96,\n sqrtRatioBX96 - sqrtRatioAX96\n )\n );\n }\n\n /// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current\n /// pool prices and the prices at the tick boundaries\n function getLiquidityForAmounts(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint256 amount0,\n uint256 amount1\n ) internal pure returns (uint128 liquidity) {\n if (sqrtRatioAX96 > sqrtRatioBX96)\n (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);\n\n if (sqrtRatioX96 <= sqrtRatioAX96) {\n liquidity = getLiquidityForAmount0(\n sqrtRatioAX96,\n sqrtRatioBX96,\n amount0\n );\n } else if (sqrtRatioX96 < sqrtRatioBX96) {\n uint128 liquidity0 = getLiquidityForAmount0(\n sqrtRatioX96,\n sqrtRatioBX96,\n amount0\n );\n uint128 liquidity1 = getLiquidityForAmount1(\n sqrtRatioAX96,\n sqrtRatioX96,\n amount1\n );\n\n liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;\n } else {\n liquidity = getLiquidityForAmount1(\n sqrtRatioAX96,\n sqrtRatioBX96,\n amount1\n );\n }\n }\n\n /// @notice Computes the amount of token0 for a given amount of liquidity and a price range\n /// @param sqrtRatioAX96 A sqrt price\n /// @param sqrtRatioBX96 Another sqrt price\n /// @param liquidity The liquidity being valued\n /// @return amount0 The amount0\n function getAmount0ForLiquidity(\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) internal pure returns (uint256 amount0) {\n if (sqrtRatioAX96 > sqrtRatioBX96)\n (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);\n\n return\n FullMath.mulDiv(\n uint256(liquidity) << FixedPoint96.RESOLUTION,\n sqrtRatioBX96 - sqrtRatioAX96,\n sqrtRatioBX96\n ) / sqrtRatioAX96;\n }\n\n /// @notice Computes the amount of token1 for a given amount of liquidity and a price range\n /// @param sqrtRatioAX96 A sqrt price\n /// @param sqrtRatioBX96 Another sqrt price\n /// @param liquidity The liquidity being valued\n /// @return amount1 The amount1\n function getAmount1ForLiquidity(\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) internal pure returns (uint256 amount1) {\n if (sqrtRatioAX96 > sqrtRatioBX96)\n (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);\n\n return\n FullMath.mulDiv(\n liquidity,\n sqrtRatioBX96 - sqrtRatioAX96,\n FixedPoint96.Q96\n );\n }\n\n /// @notice Computes the token0 and token1 value for a given amount of liquidity, the current\n /// pool prices and the prices at the tick boundaries\n function getAmountsForLiquidity(\n uint160 sqrtRatioX96,\n uint160 sqrtRatioAX96,\n uint160 sqrtRatioBX96,\n uint128 liquidity\n ) internal pure returns (uint256 amount0, uint256 amount1) {\n if (sqrtRatioAX96 > sqrtRatioBX96)\n (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);\n\n if (sqrtRatioX96 <= sqrtRatioAX96) {\n amount0 = getAmount0ForLiquidity(\n sqrtRatioAX96,\n sqrtRatioBX96,\n liquidity\n );\n } else if (sqrtRatioX96 < sqrtRatioBX96) {\n amount0 = getAmount0ForLiquidity(\n sqrtRatioX96,\n sqrtRatioBX96,\n liquidity\n );\n amount1 = getAmount1ForLiquidity(\n sqrtRatioAX96,\n sqrtRatioX96,\n liquidity\n );\n } else {\n amount1 = getAmount1ForLiquidity(\n sqrtRatioAX96,\n sqrtRatioBX96,\n liquidity\n );\n }\n }\n}\n" | ||
}, | ||
"@openzeppelin/contracts/access/Ownable.sol": { | ||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() {\n _transferOwnership(_msgSender());\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view virtual returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n }\n\n /**\n * @dev Leaves the contract without owner. It will not be possible to call\n * `onlyOwner` functions anymore. Can only be called by the current owner.\n *\n * NOTE: Renouncing ownership will leave the contract without an owner,\n * thereby removing any functionality that is only available to the owner.\n */\n function renounceOwnership() public virtual onlyOwner {\n _transferOwnership(address(0));\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public virtual onlyOwner {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Internal function without access restriction.\n */\n function _transferOwnership(address newOwner) internal virtual {\n address oldOwner = _owner;\n _owner = newOwner;\n emit OwnershipTransferred(oldOwner, newOwner);\n }\n}\n" | ||
}, | ||
"@openzeppelin/contracts/token/ERC20/IERC20.sol": { | ||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `to`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address to, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `from` to `to` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address from,\n address to,\n uint256 amount\n ) external returns (bool);\n}\n" | ||
}, | ||
"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": { | ||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\nimport \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n /**\n * @dev Deprecated. This function has issues similar to the ones found in\n * {IERC20-approve}, and its usage is discouraged.\n *\n * Whenever possible, use {safeIncreaseAllowance} and\n * {safeDecreaseAllowance} instead.\n */\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender) + value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n unchecked {\n uint256 oldAllowance = token.allowance(address(this), spender);\n require(oldAllowance >= value, \"SafeERC20: decreased allowance below zero\");\n uint256 newAllowance = oldAllowance - value;\n _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n }\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function _callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n // the target address contains contract code and also asserts for success in the low-level call.\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length > 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" | ||
}, | ||
"@openzeppelin/contracts/utils/Address.sol": { | ||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a delegate call.\n *\n * _Available since v3.4._\n */\n function functionDelegateCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(isContract(target), \"Address: delegate call to non-contract\");\n\n (bool success, bytes memory returndata) = target.delegatecall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n" | ||
}, | ||
"@openzeppelin/contracts/utils/Context.sol": { | ||
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n" | ||
}, | ||
"@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol": { | ||
"content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.5.0;\n\n/// @title Callback for IUniswapV3PoolActions#swap\n/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface\ninterface IUniswapV3SwapCallback {\n /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.\n /// @dev In the implementation you must pay the pool tokens owed for the swap.\n /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.\n /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.\n /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.\n /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by\n /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.\n /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call\n function uniswapV3SwapCallback(\n int256 amount0Delta,\n int256 amount1Delta,\n bytes calldata data\n ) external;\n}\n" | ||
}, | ||
"@uniswap/v3-core/contracts/libraries/FixedPoint96.sol": { | ||
"content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity >=0.4.0;\n\n/// @title FixedPoint96\n/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)\n/// @dev Used in SqrtPriceMath.sol\nlibrary FixedPoint96 {\n uint8 internal constant RESOLUTION = 96;\n uint256 internal constant Q96 = 0x1000000000000000000000000;\n}\n" | ||
}, | ||
"contracts/FeeManager.sol": { | ||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.13;\n\nimport {IFeeManager} from \"./interfaces/IFeeManager.sol\";\nimport {FullMath} from \"@arrakisfi/v3-lib-0.8/contracts/LiquidityAmounts.sol\";\nimport {\n IERC20,\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IV3SwapRouter} from \"./univ3-0.8/IV3SwapRouter.sol\";\nimport {ISwapRouter02} from \"./univ3-0.8/ISwapRouter02.sol\";\nimport {TransferHelper} from \"./univ3-0.8/TransferHelper.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract FeeManager is IFeeManager, Ownable {\n using SafeERC20 for IERC20;\n\n uint256 public rate;\n IERC20 public immutable chaos;\n IERC20 public immutable vault;\n IERC20 public immutable usdc;\n ISwapRouter02 public immutable router;\n uint256 public accumulatedRewardsPerShare;\n mapping(address => uint256) public rewardDebt;\n uint256 public constant REWARDS_PRECISION = 1e24;\n uint8 private constant _USDC_DECIMALS = 6;\n uint8 private constant _CHAOS_DECIMALS = 18;\n uint24 public immutable feeTier;\n\n event RewardsClaimed(\n address indexed user,\n uint256 usdcAmount,\n uint256 chaosAmount\n );\n\n event RewardsConvertedToUsdc(\n uint256 usdcAmount\n );\n\n modifier onlyVault() {\n require(address(vault) == msg.sender, \"Only vault can call\");\n _;\n }\n\n constructor(\n address vault_,\n address usdc_,\n address chaos_,\n address uniSwapRouter_,\n uint24 feeTier_\n ) {\n require(vault_ != address(0), \"FeeManager: Invalid vault_ address\");\n require(usdc_ != address(0), \"FeeManager: Invalid usdc_ address\");\n require(\n uniSwapRouter_ != address(0),\n \"FeeManager: uniSwapRouter_ address\"\n );\n feeTier = feeTier_;\n vault = IERC20(vault_);\n usdc = IERC20(usdc_);\n chaos = IERC20(chaos_);\n router = ISwapRouter02(uniSwapRouter_);\n\n transferOwnership(msg.sender);\n }\n\n /// @dev Emergency withdraw only called by owner\n function withdrawEmergency() external onlyOwner {\n uint256 balance = usdc.balanceOf(address(this));\n require(balance > 0, \"FeeManager: No USDC to withdraw\");\n usdc.safeTransfer(owner(), balance);\n }\n\n function withdrawalChaos() external onlyOwner {\n uint256 balance = chaos.balanceOf(address(this));\n require(balance > 0, \"FeeManager: No balance to withdrawal\");\n IERC20(chaos).transfer(owner(), balance);\n }\n\n function setRate(uint256 newRate) external onlyOwner {\n rate = newRate;\n }\n\n function setRewardDebt(address _user, uint256 _amount) external onlyVault {\n rewardDebt[_user] = _amount;\n }\n\n function depositFees(\n address token0,\n uint256 fees0,\n address token1,\n uint256 fees1\n ) public onlyVault {\n if (fees0 > 0) {\n IERC20(token0).safeTransferFrom(\n address(vault),\n address(this),\n fees0\n );\n }\n if (fees1 > 0) {\n IERC20(token1).safeTransferFrom(\n address(vault),\n address(this),\n fees1\n );\n }\n uint256 rewards = _convertFeesToUSDC(token0, fees0, token1, fees1);\n accumulatedRewardsPerShare =\n accumulatedRewardsPerShare +\n FullMath.mulDiv(rewards, REWARDS_PRECISION, vault.totalSupply());\n\n emit RewardsConvertedToUsdc(rewards);\n }\n\n function claimFees(address claimer) public {\n uint256 userBalance = vault.balanceOf(claimer);\n uint256 totalReward = FullMath.mulDiv(\n userBalance,\n accumulatedRewardsPerShare,\n REWARDS_PRECISION\n );\n uint256 rewardsToHarvest = totalReward - rewardDebt[claimer];\n\n if (rewardsToHarvest == 0) {\n rewardDebt[claimer] = totalReward;\n return;\n }\n\n rewardDebt[claimer] = totalReward;\n\n uint256 rewardsToHarvestInChaos = (rewardsToHarvest *\n rate *\n 10**(_CHAOS_DECIMALS - _USDC_DECIMALS)) / 100;\n\n usdc.safeTransfer(claimer, rewardsToHarvest);\n\n if (rewardsToHarvestInChaos > 0) {\n chaos.safeTransfer(claimer, rewardsToHarvestInChaos);\n }\n\n emit RewardsClaimed(claimer, rewardsToHarvest, rewardsToHarvestInChaos);\n }\n\n function _swapToUSDC(address token, uint256 feesToken)\n private\n returns (uint256 feesUSDC)\n {\n if (feesToken == 0) {\n return 0;\n }\n\n TransferHelper.safeApprove(token, address(router), feesToken);\n\n IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter\n .ExactInputSingleParams({\n tokenIn: address(token),\n tokenOut: address(usdc),\n fee: feeTier,\n recipient: address(this),\n amountIn: feesToken,\n amountOutMinimum: 0,\n sqrtPriceLimitX96: 0\n });\n\n feesUSDC = router.exactInputSingle(params);\n }\n\n function _convertFeesToUSDC(\n address token0,\n uint256 fee0,\n address token1,\n uint256 fee1\n ) private returns (uint256 usdcFee) {\n if (address(token0) != address(usdc)) {\n usdcFee += _swapToUSDC(address(token0), fee0);\n } else {\n usdcFee += fee0;\n }\n if (address(token1) != address(usdc)) {\n usdcFee += _swapToUSDC(address(token1), fee1);\n } else {\n usdcFee += fee1;\n }\n }\n}\n" | ||
}, | ||
"contracts/interfaces/IFeeManager.sol": { | ||
"content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.13;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ninterface IFeeManager {\n // Getter for the vault address\n function vault() external view returns (IERC20);\n\n // Getter for the USDC address\n function usdc() external view returns (IERC20);\n\n // Getter for accumulatedRewardsPerShare\n function accumulatedRewardsPerShare() external view returns (uint256);\n\n // Getter for a user's rewardDebt\n function rewardDebt(address _user) external view returns (uint256);\n\n // Getter for REWARDS_PRECISION\n function REWARDS_PRECISION() external view returns (uint256);\n\n // Function to emergency withdraw all the USDC of the contract (Only Owner)\n function withdrawEmergency() external;\n\n // Function to withdraw all CHAOS of the contract (Only Owner)\n function withdrawalChaos() external;\n\n // Set conversion rate between USDC and CHAOS (Only Owner)\n function setRate(uint256 rate) external;\n\n // Setter for a user's rewardDebt (can only be called by the vault)\n function setRewardDebt(address _user, uint256 _amount) external;\n\n // Function for users to claim their fees\n function claimFees(address claimer) external;\n\n // Function to deposit fees, can only be called by the vault\n function depositFees(\n address token0,\n uint256 fees0,\n address token1,\n uint256 fees1\n ) external;\n}\n" | ||
}, | ||
"contracts/univ3-0.8/ISwapRouter02.sol": { | ||
"content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.13;\npragma abicoder v2;\n\nimport \"./IV3SwapRouter.sol\";\n\n/// @title Router token swapping functionality\ninterface ISwapRouter02 is IV3SwapRouter {\n\n}\n" | ||
}, | ||
"contracts/univ3-0.8/IV3SwapRouter.sol": { | ||
"content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.13;\npragma abicoder v2;\n\nimport \"@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol\";\n\n/// @title Router token swapping functionality\n/// @notice Functions for swapping tokens via Uniswap V3\ninterface IV3SwapRouter is IUniswapV3SwapCallback {\n struct ExactInputSingleParams {\n address tokenIn;\n address tokenOut;\n uint24 fee;\n address recipient;\n uint256 amountIn;\n uint256 amountOutMinimum;\n uint160 sqrtPriceLimitX96;\n }\n\n struct ExactInputParams {\n bytes path;\n address recipient;\n uint256 amountIn;\n uint256 amountOutMinimum;\n }\n\n /// @notice Swaps `amountIn` of one token for as much as possible of another token\n /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,\n /// and swap the entire amount, enabling contracts to send tokens before calling this function.\n /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata\n /// @return amountOut The amount of the received token\n function exactInputSingle(ExactInputSingleParams calldata params)\n external\n payable\n returns (uint256 amountOut);\n}\n" | ||
}, | ||
"contracts/univ3-0.8/TransferHelper.sol": { | ||
"content": "// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.13;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nlibrary TransferHelper {\n /// @notice Approves the stipulated contract to spend the given allowance in the given token\n /// @dev Errors with 'SA' if transfer fails\n /// @param token The contract address of the token to be approved\n /// @param to The target of the approval\n /// @param value The amount of the given token the target will be allowed to spend\n function safeApprove(\n address token,\n address to,\n uint256 value\n ) internal {\n (bool success, bytes memory data) = token.call(\n abi.encodeWithSelector(IERC20.approve.selector, to, value)\n );\n require(\n success && (data.length == 0 || abi.decode(data, (bool))),\n \"SA\"\n );\n }\n}\n" | ||
} | ||
}, | ||
"settings": { | ||
"optimizer": { | ||
"enabled": true, | ||
"runs": 50 | ||
}, | ||
"outputSelection": { | ||
"*": { | ||
"*": [ | ||
"abi", | ||
"evm.bytecode", | ||
"evm.deployedBytecode", | ||
"evm.methodIdentifiers", | ||
"metadata", | ||
"devdoc", | ||
"userdoc", | ||
"storageLayout", | ||
"evm.gasEstimates" | ||
], | ||
"": [ | ||
"ast" | ||
] | ||
} | ||
}, | ||
"metadata": { | ||
"useLiteralContent": true | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Overall, the FeeManager contract is well-structured but has critical areas that need improvement.
Positive aspects:
- Use of Established Libraries: Integrates reputable libraries like OpenZeppelin and Uniswap V3, ensuring reliable and tested functionality.
- Proper Access Control: Implements the Ownable pattern and custom modifiers (
onlyVault
) to restrict function access appropriately. - Secure Token Transfers: Utilizes SafeERC20 for all token transfer operations, enhancing security against potential token transfer issues.
- High Precision Calculations: Uses a
REWARDS_PRECISION
constant set to1e24
to ensure precise reward calculations and minimize rounding errors.
Main points to address:
-
Safeguards for Emergency Withdrawals:
- Current State: Emergency withdrawal functions (
withdrawEmergency
andwithdrawalChaos
) are restricted toonlyOwner
, which centralizes control. - Recommendation: Implement additional safeguards such as multi-signature ownership or timelocks to mitigate centralization risks and enhance trustworthiness.
- Current State: Emergency withdrawal functions (
-
Bounds Checking in
setRate
Function:- Current State: The
setRate
function allows setting therate
without any bounds, posing a risk of setting extreme or malicious values. - Recommendation: Introduce validation checks to ensure that the
rate
remains within a reasonable and predefined range, preventing potential disruptions in reward mechanisms.
- Current State: The
-
Slippage Protection in
_swapToUSDC
Function:- Current State: The
_swapToUSDC
function setsamountOutMinimum
andsqrtPriceLimitX96
to0
, effectively removing any slippage protection. - Recommendation: Implement slippage guards by setting sensible
amountOutMinimum
values based on current market conditions to protect users from unexpected losses during token swaps.
- Current State: The
Addressing these issues will significantly enhance the security, reliability, and trustworthiness of the FeeManager contract.
🔗 Analysis chain
Overall, the FeeManager contract is well-structured but has room for improvement.
Positive aspects:
- Good use of established libraries (OpenZeppelin, Uniswap V3) and well-defined interfaces.
- Proper access control using Ownable pattern and custom modifiers.
- Use of SafeERC20 for secure token transfers.
- High precision (1e24) for rewards calculations to prevent rounding errors.
Main points to address:
- Implement safeguards for the emergency withdrawal function to mitigate centralization risks.
- Add bounds checking to the
setRate
function to prevent setting extreme values. - Implement slippage protection in the
_swapToUSDC
function to protect users from unexpected losses during token swaps.
Addressing these issues will significantly improve the security and reliability of the contract.
To ensure the contract behaves as expected, consider running the following tests:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Verify the use of SafeERC20 for token transfers
rg --type solidity 'using SafeERC20 for IERC20'
# Check for the presence of access control modifiers
rg --type solidity 'onlyOwner|onlyVault'
# Verify the REWARDS_PRECISION constant
rg --type solidity 'REWARDS_PRECISION = 1e24'
# Look for potential centralization risks in withdrawal functions
rg --type solidity -A 5 'function withdraw'
# Check for bounds in the setRate function
rg --type solidity -A 5 'function setRate'
# Examine the _swapToUSDC function for slippage protection
rg --type solidity -A 10 'function _swapToUSDC'
Length of output: 479
Script:
#!/bin/bash
# Verify the use of SafeERC20 for token transfers
rg --glob '*.sol' 'using SafeERC20 for IERC20'
# Check for the presence of access control modifiers
rg --glob '*.sol' 'onlyOwner|onlyVault'
# Verify the REWARDS_PRECISION constant
rg --glob '*.sol' 'REWARDS_PRECISION\s*=\s*1e24'
# Look for potential centralization risks in withdrawal functions
rg --glob '*.sol' -A 5 'function withdraw'
# Check for bounds in the setRate function
rg --glob '*.sol' -A 5 'function setRate'
# Examine the _swapToUSDC function for slippage protection
rg --glob '*.sol' -A 10 'function _swapToUSDC'
Length of output: 5737
Summary by CodeRabbit
New Features
chaosToken
address for theFeeManager
contract deployment.Bug Fixes
chaosToken
in the verification script.Documentation