-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add Vesting struct to IMergeV2 interface #82
Conversation
This commit adds a new struct called Vesting to the IMergeV2 interface. The Vesting struct has two properties: amount and end. This struct will be used to track vested amounts of tokens. Co-authored-by: [Author Name]
WalkthroughThe changes in this pull request primarily involve the introduction of a new smart contract named Changes
Possibly related PRs
Suggested labels
Suggested reviewers
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: 21
🧹 Outside diff range and nitpick comments (8)
contracts/interfaces/IMergeV2.sol (3)
4-7
: LGTM! Consider adding inline documentation.The
Vesting
struct is well-defined with appropriate field types. It effectively captures the essential information for token vesting.Consider adding inline documentation to clarify the purpose of each field:
/// @dev Represents vesting information for tokens struct Vesting { uint256 amount; /// @dev The amount of tokens vested uint256 end; /// @dev The timestamp when the vesting period ends }
Line range hint
10-16
: Consider extending the interface to support vesting operations.While the
Vesting
struct has been added, theIMergeV2
interface doesn't include any methods to interact with vesting information. Consider adding methods to fully utilize the new struct.Here are some suggestions for new methods:
interface IMergeV2 { // ... existing methods ... /// @dev Get the vesting information for a user function getVesting(address user) external view returns (Vesting memory); /// @dev Update the vesting information for a user function updateVesting(address user, uint256 amount, uint256 end) external; /// @dev Check if tokens are vested for a user function isVested(address user) external view returns (bool); /// @dev Get the total vested amount for a user function getVestedAmount(address user) external view returns (uint256); }These additions would provide a comprehensive set of operations for managing vesting within the interface.
Line range hint
1-16
: Summary: NewVesting
struct added, consider full integration with interfaceThe addition of the
Vesting
struct is a good start for implementing vesting functionality. However, theIMergeV2
interface hasn't been updated to fully utilize this new struct.Next steps to consider:
- Add inline documentation to the
Vesting
struct as suggested earlier.- Extend the
IMergeV2
interface with methods to interact with vesting information, as proposed in the previous comment.- Ensure that the implementation of these new methods aligns with the overall architecture and security considerations of the contract system.
- Update any related contracts or tests to reflect these changes in the interface.
These steps will help to fully integrate the vesting functionality into your contract system.
ignition/modules/VultMerge.ts (2)
6-11
: LGTM: Module structure is correct. Consider making vesting period configurable.The module definition, contract instantiation, and export are implemented correctly. However, to improve flexibility, consider making the vesting period configurable, similar to the address constants.
Example refactor:
export default buildModule("VultMergeModule", m => { const vestingPeriod = parseInt(process.env.VESTING_PERIOD || "7", 10); const merge = m.contract("VultMerge", [WEWE_ADDRESS, VULT_IOU_ADDRESS, vestingPeriod]); return { merge }; });This change allows the vesting period to be set via an environment variable, defaulting to 7 if not specified.
1-11
: Overall assessment: Well-structured module with room for improved configurability.The
VultMergeModule
is correctly implemented using Hardhat Ignition'sbuildModule
function. It properly sets up the VultMerge contract with the necessary parameters. To enhance the module's flexibility and maintainability, consider implementing the suggested changes regarding the use of environment variables for addresses and making the vesting period configurable.These improvements will make the deployment process more adaptable to different environments and easier to maintain in the long run.
contracts/core/merge/DynamicEater.sol (1)
51-51
: Clarify the comment regarding vesting logicThe comment
// If transfer, dont vest
seems misleading. The conditionif (vestingDuration != 0)
checks if vesting is enabled, and if so, it updates the vesting records.Consider updating the comment for clarity:
-// If transfer, dont vest +// If vesting is enabled, update vesting recordscontracts/core/merge/VultMerge.sol (2)
95-95
: Clarify Error Message inrequire
StatementThe error message "Insufficient balance to eat" may be confusing in the context of the
mergeAndSell
function. Consider updating the message to better reflect the action being performed.Apply this diff to improve the error message:
- require(balance >= amount, "VultMerge: Insufficient balance to eat"); + require(balance >= amount, "VultMerge: Insufficient balance to merge and sell");
109-109
: Clarify Error Message inrequire
StatementThe error message "Insufficient balance to eat" might not clearly convey the issue in the
merge
function. Updating it can enhance user understanding.Apply this diff to improve the error message:
- require(balance >= amount, "VultMerge: Insufficient balance to eat"); + require(balance >= amount, "VultMerge: Insufficient balance to merge");
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (14)
- contracts/core/merge/DynamicEater.sol (1 hunks)
- contracts/core/merge/VultMerge.sol (1 hunks)
- contracts/interfaces/IMergeV2.sol (1 hunks)
- ignition/deployments/chain-8453/artifacts/MergeFactoryModule#MergeFactory.dbg.json (0 hunks)
- ignition/deployments/chain-8453/artifacts/MergeFactoryModule#MergeFactory.json (0 hunks)
- ignition/deployments/chain-8453/artifacts/MergeModule#MergeWithMarket.dbg.json (0 hunks)
- ignition/deployments/chain-8453/artifacts/MergeModule#MergeWithMarket.json (0 hunks)
- ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.dbg.json (1 hunks)
- ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.json (1 hunks)
- ignition/deployments/chain-8453/deployed_addresses.json (1 hunks)
- ignition/deployments/chain-8453/journal.jsonl (1 hunks)
- ignition/modules/VultMerge.ts (1 hunks)
- package.json (1 hunks)
- test/VultMerge.test.ts (1 hunks)
💤 Files with no reviewable changes (4)
- ignition/deployments/chain-8453/artifacts/MergeFactoryModule#MergeFactory.dbg.json
- ignition/deployments/chain-8453/artifacts/MergeFactoryModule#MergeFactory.json
- ignition/deployments/chain-8453/artifacts/MergeModule#MergeWithMarket.dbg.json
- ignition/deployments/chain-8453/artifacts/MergeModule#MergeWithMarket.json
✅ Files skipped from review due to trivial changes (1)
- ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.dbg.json
🧰 Additional context used
🪛 Biome
test/VultMerge.test.ts
[error] 5-5: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
🔇 Additional comments (7)
ignition/deployments/chain-8453/deployed_addresses.json (1)
1-3
: LGTM! Deployment address updated for VultMerge.The changes in this file accurately reflect the architectural updates described in the PR objectives and AI summary. The new VultMerge module has been added with a valid Ethereum address.
To ensure the deployment process was executed correctly, please run the following verification script:
This script will help verify that the deployment process was executed correctly, ensuring that the VultMerge module is properly deployed and the old modules are removed.
✅ Verification successful
VultMerge deployment successfully verified.
All verification tests passed, confirming that the VultMerge module is correctly deployed and the old modules have been removed as intended.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the deployment of VultMerge and removal of old modules # Test 1: Check if VultMerge is properly deployed echo "Verifying VultMerge deployment:" rg -q "0x59fA35216Afb2Ab62e254246E13b412656a71831" ignition/deployments/chain-8453/journal.jsonl && echo "VultMerge address found in deployment journal" || echo "VultMerge address not found in deployment journal" # Test 2: Ensure old modules are no longer present echo "Verifying removal of old modules:" ! rg -q "ChaosModule#CHAOS" ignition/deployments/chain-8453/journal.jsonl && echo "ChaosModule removed" || echo "ChaosModule still present" ! rg -q "UniAdaptorModule#UniswapV3ViaRouter" ignition/deployments/chain-8453/journal.jsonl && echo "UniAdaptorModule removed" || echo "UniAdaptorModule still present" ! rg -q "MergeModule#MergeWithMarket" ignition/deployments/chain-8453/journal.jsonl && echo "MergeModule removed" || echo "MergeModule still present" ! rg -q "MergeFactoryModule#MergeFactory" ignition/deployments/chain-8453/journal.jsonl && echo "MergeFactoryModule removed" || echo "MergeFactoryModule still present" # Test 3: Check if VultMerge is properly referenced in the module file echo "Verifying VultMerge module definition:" rg -q "VultMerge" ignition/modules/VultMerge.ts && echo "VultMerge found in module definition" || echo "VultMerge not found in module definition"Length of output: 1115
ignition/modules/VultMerge.ts (1)
1-1
: LGTM: Import statement is correct.The import of
buildModule
from the Hardhat Ignition package is appropriate for creating the deployment module.package.json (2)
22-22
: LGTM: New deployment script for VultMergeThe addition of the
deploy:vult:base
script aligns with the PR objectives. It correctly deploys the newVultMerge.ts
module on the base network with verification, which is a good practice for contract deployment.
21-22
: Update deployment scripts to reflect current project structureThe changes in the
package.json
file reflect the transition fromMergeWithMarket
toVultMerge
, which aligns with the PR objectives. However, there's an inconsistency:
- The old
deploy:mergewithmarket
script is still present, despite the removal of the corresponding contract.- A new
deploy:vult:base
script has been correctly added for the newVultMerge
contract.To maintain a clean and accurate project configuration:
- Remove the unused
deploy:mergewithmarket
script.- Ensure all other scripts in the file are still relevant to the current project structure.
- Consider adding a comment or documentation explaining the purpose of the
VultMerge
deployment, especially if it's replacing functionality previously handled byMergeWithMarket
.These changes will help prevent confusion and ensure that the
package.json
accurately represents the current state of the project.To ensure all scripts are relevant, you can run:
This will help identify any other potential mismatches between deployment scripts and actual contract files.
✅ Verification successful
Review Comment Adjustment
The
MergeWithMarket
contract is still present in the codebase, so thedeploy:mergewithmarket
script remains necessary for deployment. Please ensure the contract is fully removed before deleting its deployment script.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: List all deployment scripts and their corresponding contract files echo "Deployment scripts:" grep -E '"deploy:.*"' package.json echo -e "\nCorresponding contract files:" fd -e sol -e ts . contracts ignitionLength of output: 3598
ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.json (2)
1-585
: Overall structure and content of the VultMerge contract artifactThe file
VultMergeModule#VultMerge.json
contains the contract ABI, bytecode, and deployed bytecode for the VultMerge contract. This artifact is crucial for deploying and interacting with the contract on chain-8453. Let's review some key aspects:
Constructor (lines 7-25):
The constructor takes three parameters:_wewe
and_vult
(both addresses) and_vestingDuration
(uint32). This aligns with the PR objectives of introducing a new Vesting struct.Events (lines 48-122):
The contract defines several events, includingMerged
,OwnershipTransferred
,Paused
,RateChanged
, andUnpaused
. These events provide important visibility into the contract's state changes.Key Functions:
merge
(lines 235-245): Implements the core merging functionality.mergeAndSell
(lines 248-268): Combines merging and selling operations.claim
(lines 163-167): Allows users to claim tokens, likely related to vesting.setVestingDuration
(lines 399-410): Allows updating the vesting duration.Security Considerations:
- The contract implements ownership controls (e.g.,
transferOwnership
,renounceOwnership
).- Pause functionality is present (
togglePause
,paused
), allowing for emergency stops.Vesting Implementation:
- The
vestings
mapping (lines 499-513) stores vesting information, includingamount
andend
, which aligns with the PR objectives.The contract structure and functionality appear to be consistent with the PR objectives of introducing a Vesting struct and implementing related functionality.
499-513
: Vesting struct implementationThe
vestings
mapping implements the Vesting struct as mentioned in the PR objectives:"vestings": { "inputs": [ { "internalType": "address", "name": "", "type": "address" } ], "name": "vestings", "outputs": [ { "internalType": "uint256", "name": "amount", "type": "uint256" }, { "internalType": "uint256", "name": "end", "type": "uint256" } ], "stateMutability": "view", "type": "function" }This implementation correctly includes the
amount
andend
properties as specified in the PR objectives. The mapping allows for efficient lookup of vesting information by address.ignition/deployments/chain-8453/journal.jsonl (1)
3-7
: LGTMThe deployment entries for
VultMerge
are correctly specified, and the transaction details confirm a successful deployment.
ignition/modules/VultMerge.ts
Outdated
const WEWE_ADDRESS = "0x6b9bb36519538e0C073894E964E90172E1c0B41F"; | ||
const VULT_IOU_ADDRESS = "0x299A57C1f6761b3dB304dc8B18bb4E60A1CF37b6"; |
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.
🛠️ Refactor suggestion
Consider using environment variables for addresses.
While the constants are correctly defined, hardcoding addresses directly in the deployment script may reduce maintainability and flexibility. Consider using environment variables or a configuration file for these values to make the deployment process more adaptable to different environments.
Example refactor:
import dotenv from 'dotenv';
dotenv.config();
const WEWE_ADDRESS = process.env.WEWE_ADDRESS || "0x6b9bb36519538e0C073894E964E90172E1c0B41F";
const VULT_IOU_ADDRESS = process.env.VULT_IOU_ADDRESS || "0x299A57C1f6761b3dB304dc8B18bb4E60a1CF37b6";
Don't forget to add the necessary environment variables to your .env
file and update the package.json
to include the dotenv
package.
@@ -18,7 +18,8 @@ | |||
"deploy:bro:base": "npx hardhat ignition deploy ignition/modules/BroMerge.ts --network base --verify", | |||
"deploy:factory": "npx hardhat ignition deploy ignition/modules/MergeFactory.ts --network base --verify", | |||
"deploy:uni": "npx hardhat ignition deploy ignition/modules/UniAdaptor.ts --network base --verify", | |||
"deploy:mergewithmarket": "npx hardhat ignition deploy ignition/modules/MergeWithMarket.ts --network base --verify" | |||
"deploy:mergewithmarket": "npx hardhat ignition deploy ignition/modules/MergeWithMarket.ts --network base --verify", |
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.
Remove unused deployment script
The deploy:mergewithmarket
script is no longer needed as the MergeWithMarket
contract has been removed from the project. Keeping unused scripts can lead to confusion and potential errors in the future.
Consider removing this line:
- "deploy:mergewithmarket": "npx hardhat ignition deploy ignition/modules/MergeWithMarket.ts --network base --verify",
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
"deploy:mergewithmarket": "npx hardhat ignition deploy ignition/modules/MergeWithMarket.ts --network base --verify", |
"inputs": [], | ||
"name": "InvalidTokenReceived", | ||
"type": "error" | ||
}, | ||
{ | ||
"inputs": [], | ||
"name": "MergeLocked", | ||
"type": "error" | ||
}, | ||
{ | ||
"inputs": [], | ||
"name": "VultToWeweNotAllwed", | ||
"type": "error" | ||
}, | ||
{ | ||
"inputs": [], | ||
"name": "ZeroAmount", | ||
"type": "error" |
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.
Potential issue with error naming
There's a typo in one of the error definitions:
{
"inputs": [],
"name": "VultToWeweNotAllwed",
"type": "error"
}
The error name "VultToWeweNotAllwed" is misspelled. It should be "VultToWeweNotAllowed".
Consider fixing the typo in the error name:
- "name": "VultToWeweNotAllwed",
+ "name": "VultToWeweNotAllowed",
{ | ||
"inputs": [ | ||
{ | ||
"internalType": "uint32", | ||
"name": "duration", | ||
"type": "uint32" | ||
} | ||
], | ||
"name": "setVestingDuration", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" |
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.
Vesting duration setter function
The contract includes a function to set the vesting duration:
{
"inputs": [
{
"internalType": "uint32",
"name": "duration",
"type": "uint32"
}
],
"name": "setVestingDuration",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
This function allows for updating the vesting duration, which is a crucial parameter for the vesting mechanism. However, there are a few considerations:
- The function doesn't seem to have any access control. It's recommended to restrict this function to only be callable by the contract owner or a designated admin.
- There's no event emitted when the vesting duration is changed. Adding an event would improve transparency and make it easier for off-chain services to track changes.
Consider adding access control and an event to the setVestingDuration
function. Here's a suggested implementation:
function setVestingDuration(uint32 duration) external onlyOwner {
vestingDuration = duration;
emit VestingDurationChanged(duration);
}
event VestingDurationChanged(uint32 newDuration);
Also, ensure that changing the vesting duration doesn't affect existing vesting schedules, or clearly document the behavior if it does.
"inputs": [ | ||
{ | ||
"internalType": "uint256", | ||
"name": "amount", | ||
"type": "uint256" | ||
} | ||
], | ||
"name": "merge", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" |
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.
Review of merge and mergeAndSell functions
The contract includes two key functions for merging:
merge
function:
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "merge",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
mergeAndSell
function:
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "contract IAMM",
"name": "amm",
"type": "address"
},
{
"internalType": "bytes",
"name": "extraData",
"type": "bytes"
}
],
"name": "mergeAndSell",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
These functions seem to implement the core functionality of the VultMerge contract. However, there are a few points to consider:
-
The
merge
function doesn't provide any return value or emit an event. It might be helpful to emit an event for better transparency and easier off-chain tracking. -
The
mergeAndSell
function interacts with an external AMM contract. This introduces potential security risks if the AMM contract is malicious or compromised.
Consider the following improvements:
- Add an event emission to the
merge
function:
event Merged(address indexed user, uint256 amount);
function merge(uint256 amount) external {
// existing logic
emit Merged(msg.sender, amount);
}
- For the
mergeAndSell
function, implement additional safety checks:
function mergeAndSell(uint256 amount, IAMM amm, bytes calldata extraData) external {
require(isWhitelistedAMM(address(amm)), "AMM not whitelisted");
// existing logic
}
function isWhitelistedAMM(address amm) public view returns (bool) {
// Implementation to check if the AMM is whitelisted
}
These changes would enhance the security and transparency of the merging operations.
Also applies to: 248-268
contracts/core/merge/VultMerge.sol
Outdated
} | ||
|
||
// vult in, wewe out | ||
uint256 weweOut = _getRate() * amount * _ratePrecision; |
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.
Potential Calculation Error in weweOut
The calculation of weweOut
may contain a logic error. Multiplying _getRate()
, amount
, and _ratePrecision
might not yield the correct result due to incorrect handling of precision.
Verify the calculation formula to ensure it accurately computes weweOut
. If _getRate()
returns a rate scaled by _ratePrecision
, consider adjusting the formula:
- uint256 weweOut = _getRate() * amount * _ratePrecision;
+ uint256 weweOut = (amount * _getRate()) / _ratePrecision;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
uint256 weweOut = _getRate() * amount * _ratePrecision; | |
uint256 weweOut = (amount * _getRate()) / _ratePrecision; |
Undefined Variable: _ratePrecision
The variable _ratePrecision
used in the calculation of weweOut
is not declared in this contract. Ensure that _ratePrecision
is defined or imported appropriately to avoid compilation errors.
If _ratePrecision
is intended to represent a precision factor for rate calculations, define it in the contract. For example:
+ uint256 private constant _ratePrecision = 1e18; // Adjust the value as needed
Committable suggestion was skipped due to low confidence.
contracts/core/merge/VultMerge.sol
Outdated
if (lockedStatus != LockedStatus.TwoWay) { | ||
revert VultToWeweNotAllwed(); | ||
} |
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.
Correction Needed: Typo in Revert Function Name
The error function VultToWeweNotAllwed()
contains a typo. It should be VultToWeweNotAllowed()
to match the intended error name and improve code clarity.
Apply this diff to correct the typo:
- revert VultToWeweNotAllwed();
+ revert VultToWeweNotAllowed();
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if (lockedStatus != LockedStatus.TwoWay) { | |
revert VultToWeweNotAllwed(); | |
} | |
if (lockedStatus != LockedStatus.TwoWay) { | |
revert VultToWeweNotAllowed(); | |
} |
contracts/core/merge/VultMerge.sol
Outdated
_currentHeld -= amount; | ||
vestings[msg.sender].amount = 0; | ||
|
||
IERC20(wewe).transfer(msg.sender, amount); |
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.
Use safeTransfer
Instead of transfer
for ERC20 Transfers
In line 119, IERC20(wewe).transfer(msg.sender, amount);
uses the standard transfer
method. To ensure safer token transfers and handle non-standard ERC20 implementations, it's recommended to use safeTransfer
from the SafeERC20
library.
Apply this diff to use safeTransfer
:
- IERC20(wewe).transfer(msg.sender, amount);
+ IERC20(wewe).safeTransfer(msg.sender, amount);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
IERC20(wewe).transfer(msg.sender, amount); | |
IERC20(wewe).safeTransfer(msg.sender, amount); | |
``` | |
Note: To use `safeTransfer`, you'll need to import and use the `SafeERC20` library from OpenZeppelin. Make sure to add the following import at the beginning of your contract file if it's not already present: | |
```solidity | |
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
``` | |
And add the following using statement in your contract: | |
```solidity | |
using SafeERC20 for IERC20; |
test/VultMerge.test.ts
Outdated
import { expect } from "chai"; | ||
import { ethers } from "hardhat"; | ||
|
||
describe.only("Vult Merge Contract", function () { |
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.
Remove '.only' to run all test suites
The .only
method is used to focus on a single test suite during debugging. It should be removed to ensure that all tests are executed.
Apply this diff to remove .only
:
-describe.only("Vult Merge Contract", function () {
+describe("Vult Merge Contract", function () {
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
describe.only("Vult Merge Contract", function () { | |
describe("Vult Merge Contract", function () { |
🧰 Tools
🪛 Biome
[error] 5-5: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
test/VultMerge.test.ts
Outdated
await expect(vult.approveAndCall(merge, 100, "0x")).to.be.revertedWithCustomError(merge, "VultToWeweNotAllwed"); | ||
await merge.setLockedStatus(1); | ||
await expect(vult.approveAndCall(merge, 100, "0x")).to.be.revertedWithCustomError(merge, "VultToWeweNotAllwed"); | ||
}); |
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
Multiple Occurrences of Misspelled Error 'VultToWeweNotAllwed' Found
The misspelled error name 'VultToWeweNotAllwed'
appears in several files. Please correct all instances to 'VultToWeweNotAllowed'
:
test/VultMerge.test.ts
test/Merge.test.ts
contracts/interfaces/IMerge.sol
contracts/core/merge/VultMerge.sol
contracts/core/Merge.sol
ignition/deployments/chain-8453/build-info/4a8496326ea78d00d21b66cae1dc11a6.json
🔗 Analysis chain
Correct the misspelled error name 'VultToWeweNotAllwed'
The custom error name 'VultToWeweNotAllwed'
appears to be misspelled. It should likely be 'VultToWeweNotAllowed'
.
Apply this diff to correct the spelling in the test:
await expect(vult.approveAndCall(merge, 100, "0x")).to.be.revertedWithCustomError(merge, "VultToWeweNotAllwed");
-await expect(vult.approveAndCall(merge, 100, "0x")).to.be.revertedWithCustomError(merge, "VultToWeweNotAllwed");
+await expect(vult.approveAndCall(merge, 100, "0x")).to.be.revertedWithCustomError(merge, "VultToWeweNotAllowed");
Run the following script to check for other occurrences of the misspelled error name in the codebase:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find all occurrences of 'VultToWeweNotAllwed' in the codebase.
# Expectation: The misspelled error name should not appear elsewhere.
rg 'VultToWeweNotAllwed'
Length of output: 20979
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: 11
🧹 Outside diff range and nitpick comments (5)
test/VultMerge.test.ts (4)
19-22
: Define constants for magic numbers.Consider extracting magic numbers into named constants at the top of the file for better maintainability and readability.
+const MAX_SUPPLY = 1000n; +const VESTING_PERIOD = 60; + // Max supply of 1000 tokens to eat -const maxSupply = 1000n // ethers.parseEther("1000"); -const vestingPeriod = 60; +const maxSupply = MAX_SUPPLY; +const vestingPeriod = VESTING_PERIOD;
38-46
: Add more comprehensive initial setup test cases.The initial setup tests should be expanded to include:
- Validation of vestingPeriod and maxSupply
- Negative test cases for invalid constructor parameters
- Edge cases for zero addresses
Example test cases to add:
it("Should set the correct vesting period and max supply", async function () { const { merge } = await loadFixture(deployFixture); expect(await merge.vestingPeriod()).to.equal(VESTING_PERIOD); expect(await merge.maxSupply()).to.equal(MAX_SUPPLY); }); it("Should revert with zero addresses", async function () { const Merge = await ethers.getContractFactory("VultMerge"); await expect( Merge.deploy(ethers.ZeroAddress, ethers.ZeroAddress, VESTING_PERIOD, MAX_SUPPLY) ).to.be.revertedWith("Invalid address"); });
54-55
: Define constant for expected rate value.The magic number
12000000n
should be defined as a constant with a clear name explaining its significance.+const INITIAL_RATE = 12000000n; // 120% represented as basis points let rate = await merge.getRate(); -expect(rate).to.be.eq(12000000n); // Starting rate should be 120% +expect(rate).to.be.eq(INITIAL_RATE);
48-80
: Expand test coverage for dynamic rates.The dynamic rates test suite should be expanded to include:
- Edge cases for minimum and maximum rates
- Validation of vesting end time calculation
- Multiple merge operations and their impact on rates
Example additional test case:
it("Should respect minimum rate boundary", async () => { const { merge, otherAccount } = await loadFixture(deployFixture); // Perform multiple merges to reduce rate for(let i = 0; i < 10; i++) { await merge.connect(otherAccount).merge(100n); } const rate = await merge.getRate(); expect(rate).to.be.gte(await merge.MIN_RATE()); });contracts/core/merge/DynamicEater.sol (1)
11-11
: Remove unnecessary import ofconsole.sol
The
console.sol
import is typically used for debugging during development and should not be included in production code.Apply this diff to remove the import:
-import "hardhat/console.sol";
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (4)
- contracts/core/merge/DynamicEater.sol (1 hunks)
- contracts/core/merge/VultMerge.sol (1 hunks)
- package.json (1 hunks)
- test/VultMerge.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- package.json
🧰 Additional context used
🪛 Biome
test/VultMerge.test.ts
[error] 5-5: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
🔇 Additional comments (2)
test/VultMerge.test.ts (1)
12-16
: Add validation for empty token names and symbols.The test deploys tokens with empty strings for name and symbol. Consider adding test cases to verify behavior with valid token metadata.
contracts/core/merge/DynamicEater.sol (1)
88-89
: Verify the calculation ofweweToTransfer
In the
_merge
function,weweToTransfer
is calculated using_totalVested + amount
:uint256 weweToTransfer = getTotalWeWe(_totalVested + amount);Please verify whether the intention is to calculate based on
amount
or the cumulative_totalVested + amount
. Using_totalVested + amount
might not reflect the correct amount of Wewe tokens to transfer for the current merge.
test/VultMerge.test.ts
Outdated
const vested = await merge.vestings(otherAccount.address); | ||
|
||
expect(vested.amount).to.be.eq(50n); |
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
Based on the test file content, I can now provide a final response:
The review comment is valid - end time validation is missing
The test only validates the vested amount (vested.amount
) but doesn't check the end
timestamp, even though a vestingPeriod
of 60 is set in the fixture. The test should verify that the vesting end time is correctly calculated by adding the vesting period to the current timestamp.
Key points:
- The test fixture sets
vestingPeriod = 60
- The
vested
struct is retrieved but onlyamount
is verified - No assertions are made for the vesting end time
🔗 Analysis chain
Verify vesting end time calculation.
The test only verifies the vested amount but should also validate the end
timestamp in the Vesting struct.
Let me try a different search pattern to gather more context about the vesting tests and structure.
Let me try one more search to find the complete test context and vesting implementation.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if there are any tests validating vesting end time
rg -A 5 'vesting.*end' test/
Length of output: 30
Script:
#!/bin/bash
# First, let's look at the vesting struct definition and usage
ast-grep --pattern 'struct Vesting {
$$$
}'
# Also search for any vesting-related test assertions
rg -A 3 'expect.*vesting' test/
Length of output: 525
Script:
#!/bin/bash
# Look for the complete test file content
cat test/VultMerge.test.ts
# Search for vestings mapping or function definition
ast-grep --pattern 'function vestings($$$) {
$$$
}'
Length of output: 2804
contracts/core/merge/VultMerge.sol
Outdated
require(balance >= amount, "VultMerge: Insufficient balance to eat"); | ||
|
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.
Update error messages to match the function's action
The error message "Insufficient balance to eat" does not align with the action performed by the functions mergeAndSell
and merge
, which is merging tokens. For clarity and consistency, consider updating the error message to "Insufficient balance to merge".
Apply this diff to correct the error messages:
In the mergeAndSell
function:
- require(balance >= amount, "VultMerge: Insufficient balance to eat");
+ require(balance >= amount, "VultMerge: Insufficient balance to merge");
In the merge
function:
- require(balance >= amount, "VultMerge: Insufficient balance to eat");
+ require(balance >= amount, "VultMerge: Insufficient balance to merge");
Also applies to: 100-101
contracts/core/merge/VultMerge.sol
Outdated
_merge(amount, _token, msg.sender); | ||
|
||
// Approve the AMM to use the tokens now in this contract | ||
IERC20(_token).approve(address(amm), amount); |
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.
Use safeApprove
instead of approve
for ERC20 approvals
To ensure safer interactions with ERC20 tokens and handle non-standard implementations, it's recommended to use safeApprove
from the SafeERC20
library instead of the standard approve
method.
Apply this diff to update the approval method:
- IERC20(_token).approve(address(amm), amount);
+ IERC20(_token).safeApprove(address(amm), amount);
Committable suggestion was skipped due to low confidence.
abstract contract DynamicEater is IWeweReceiver, ReentrancyGuard, Pausable, Ownable { | ||
int256 internal constant RATE_PRECISSION = 100_000; | ||
address internal _token; | ||
address public wewe; |
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.
Initialize the wewe
token address
The wewe
token address is declared but not initialized in the contract. This could lead to unexpected behavior if the address remains unset.
Consider adding a setter function to initialize the wewe
address:
function setWeweAddress(address _wewe) external onlyOwner {
require(_wewe != address(0), "DynamicEater: Invalid WEWE address");
wewe = _wewe;
}
Ensure that this function is called with the correct address before the contract is used.
|
||
abstract contract DynamicEater is IWeweReceiver, ReentrancyGuard, Pausable, Ownable { | ||
int256 internal constant RATE_PRECISSION = 100_000; | ||
address internal _token; |
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.
Initialize the _token
address
Similarly, the _token
address is declared but not set. This is required for the _merge
function to operate correctly.
Consider adding a setter function for _token
:
function setTokenAddress(address token) external onlyOwner {
require(token != address(0), "DynamicEater: Invalid token address");
_token = token;
}
function _getRate(uint256 x1, uint256 x2) internal view returns (uint256) { | ||
// Slope is a constant, from max rate at 0 tokens, to min rate at max supply | ||
int256 dxdy = (minRate - maxRate) / int256(maxSupply); |
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.
Prevent division by zero in _getRate
function
The _getRate
function divides by int256(maxSupply)
, which could result in a division by zero error if maxSupply
is zero.
Add a check to ensure maxSupply
is not zero:
function _getRate(uint256 x1, uint256 x2) internal view returns (uint256) {
+ require(maxSupply != 0, "DynamicEater: maxSupply cannot be zero");
// Slope is a constant, from max rate at 0 tokens, to min rate at max supply
int256 dxdy = (minRate - maxRate) / int256(maxSupply);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
function _getRate(uint256 x1, uint256 x2) internal view returns (uint256) { | |
// Slope is a constant, from max rate at 0 tokens, to min rate at max supply | |
int256 dxdy = (minRate - maxRate) / int256(maxSupply); | |
function _getRate(uint256 x1, uint256 x2) internal view returns (uint256) { | |
require(maxSupply != 0, "DynamicEater: maxSupply cannot be zero"); | |
// Slope is a constant, from max rate at 0 tokens, to min rate at max supply | |
int256 dxdy = (minRate - maxRate) / int256(maxSupply); |
vestings[msg.sender] = Vesting({ | ||
amount: weweToTransfer + vestedAmount, | ||
end: block.timestamp + vestingDuration * 1 minutes | ||
}); |
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.
Ensure consistent use of from
and msg.sender
When updating the vesting records, you are assigning the new vesting to vestings[msg.sender]
, while the from
address is used for other operations. This inconsistency can lead to incorrect vesting assignments.
Apply this diff to use the from
address consistently:
uint256 vestedAmount = vestings[from].amount;
-vestings[msg.sender] = Vesting({
+vestings[from] = Vesting({
amount: weweToTransfer + vestedAmount,
end: block.timestamp + vestingDuration * 1 minutes
});
Committable suggestion was skipped due to low confidence.
/// @notice Wewe token approveAndCall function | ||
function receiveApproval( | ||
address from, | ||
uint256 amount, | ||
address, | ||
bytes calldata | ||
) external nonReentrant whenNotPaused { |
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.
Restrict receiveApproval
function to the wewe
token contract
The receiveApproval
function should only be callable by the wewe
token contract to prevent unauthorized access.
Add a modifier to restrict access:
+modifier onlyWeweToken() {
+ require(msg.sender == wewe, "DynamicEater: Unauthorized sender");
+ _;
+}
/// @notice Wewe token approveAndCall function
function receiveApproval(
address from,
uint256 amount,
address,
bytes calldata
) external nonReentrant whenNotPaused onlyWeweToken {
// After wewe approve and call, it will call this function
require(_token != address(0), "Eater: Token address not set");
Committable suggestion was skipped due to low confidence.
IERC20(wewe).transferFrom(msg.sender, address(this), amount); | ||
} |
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.
🛠️ Refactor suggestion
Use SafeERC20
for token transfers
Direct use of transferFrom
can lead to issues if the token does not return a boolean value. It's safer to use OpenZeppelin's SafeERC20
library to handle token transfers.
Update the code to use SafeERC20
:
+import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+
+using SafeERC20 for IERC20;
function _deposit(uint256 amount) internal {
- IERC20(wewe).transferFrom(msg.sender, address(this), amount);
+ IERC20(wewe).safeTransferFrom(msg.sender, address(this), amount);
}
Committable suggestion was skipped due to low confidence.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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
🧹 Outside diff range and nitpick comments (6)
ignition/modules/DynamicEaterModule.ts (3)
4-5
: Consider making addresses configurable based on deployment environment.Hardcoding contract addresses directly in the module can cause issues when deploying to different networks (mainnet, testnet, local). Consider:
- Moving these addresses to a configuration file
- Using environment variables
- Adding network-specific address mapping
Example configuration approach:
// config/addresses.ts export const ADDRESSES = { mainnet: { WEWE: "0x6b9bb36519538e0C073894E964E90172E1c0B41F", VULT_IOU: "0x299A57C1f6761b3dB304dc8B18bb4E60a1CF37b6" }, testnet: { // testnet addresses } };
8-9
: Document the vesting period unit and consider using a more explicit constant.The
vestingPeriod
value of 60 lacks context about its time unit (seconds, minutes, days?). This could lead to confusion or incorrect vesting durations.Consider using a more explicit constant:
const VESTING_PERIOD_SECONDS = 60; // or minutes/days as appropriate const MAX_SUPPLY_TOKENS = "100";
1-13
: Consider adding deployment verification and documentation.The module would benefit from:
- Post-deployment verification steps
- Documentation about the expected behavior
- Network-specific configurations
Consider extending the module with verification:
export default buildModule("DynamicEaterModule", async (m) => { // ... existing code ... // Add post-deployment verification m.afterDeploy(async (_, contracts) => { const dynamicEater = contracts.merge; const vestingPeriodValue = await dynamicEater.vestingPeriod(); const maxSupplyValue = await dynamicEater.maxSupply(); console.log("Deployment verified:"); console.log(`Vesting Period: ${vestingPeriodValue}`); console.log(`Max Supply: ${maxSupplyValue}`); }); return { merge }; });test/DynamicEater.test.ts (3)
12-14
: Add documentation for empty constructor parameters.The Vult contract is deployed with empty strings for name and symbol parameters. This should be documented or use meaningful test values.
- const vult = await Vult.deploy("", ""); - const mockVult = await Vult.deploy("", ""); + // Deploy Vult tokens with test name/symbol + const vult = await Vult.deploy("Test Vult", "tVULT"); + const mockVult = await Vult.deploy("Mock Vult", "mVULT");
18-22
: Extract magic numbers into named constants.Magic numbers should be defined as constants at the top of the test file for better maintainability.
// Add at the top of the file const TEST_MAX_SUPPLY = 1000n; const TEST_VESTING_PERIOD = 60; const TEST_APPROVAL_AMOUNT = 10000n;
54-55
: Document the rate calculation.The initial rate value of 12000000n (120%) should be documented and possibly defined as a constant.
- expect(rate).to.be.eq(12000000n); // Starting rate should be 120% + const INITIAL_RATE = 12000000n; // 120% represented as 12000000/10000000 + expect(rate).to.be.eq(INITIAL_RATE);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (7)
- contracts/core/merge/DynamicEater.sol (1 hunks)
- ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.dbg.json (1 hunks)
- ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.json (1 hunks)
- ignition/deployments/chain-8453/deployed_addresses.json (1 hunks)
- ignition/deployments/chain-8453/journal.jsonl (1 hunks)
- ignition/modules/DynamicEaterModule.ts (1 hunks)
- test/DynamicEater.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.dbg.json
- ignition/deployments/chain-8453/deployed_addresses.json
🧰 Additional context used
🔇 Additional comments (11)
ignition/modules/DynamicEaterModule.ts (1)
10-10
: Verify contract parameters and add input validation.The contract deployment parameters should be validated before passing to the constructor to prevent deployment with incorrect values.
Consider adding parameter validation:
if (vestingPeriod <= 0) { throw new Error("Vesting period must be positive"); } if (maxSupply.lte(0)) { throw new Error("Max supply must be positive"); }Let's verify the contract's existence and interface:
test/DynamicEater.test.ts (2)
1-4
: LGTM: Appropriate test dependencies imported.The imports include all necessary testing utilities from Hardhat and Chai.
27-33
: Verify token transfer and approval success.Token transfers and approvals should be verified to ensure the test setup is correct.
- await vult.transfer(otherAccount, 1000n); + await expect(vult.transfer(otherAccount, 1000n)) + .to.emit(vult, 'Transfer') + .withArgs(owner.address, otherAccount.address, 1000n); + expect(await vult.balanceOf(otherAccount.address)).to.equal(1000n); - await wewe.approve(mergeAddress, 10000n); - await vult.connect(otherAccount).approve(mergeAddress, 10000n); + await expect(wewe.approve(mergeAddress, 10000n)) + .to.emit(wewe, 'Approval') + .withArgs(owner.address, mergeAddress, 10000n); + await expect(vult.connect(otherAccount).approve(mergeAddress, 10000n)) + .to.emit(vult, 'Approval') + .withArgs(otherAccount.address, mergeAddress, 10000n);ignition/deployments/chain-8453/journal.jsonl (4)
3-3
: Review the vesting duration parameter.The vesting duration is set to 60 seconds, which is extremely short for a production environment. Typical vesting periods are measured in months or years.
Consider adjusting the vesting duration to a more realistic timeframe. For example:
- 180 days (15,552,000 seconds)
- 365 days (31,536,000 seconds)
- 4 years (126,144,000 seconds)
5-6
: Transaction fees are optimized for Base chain.The gas configuration is appropriate:
- maxFeePerGas: 7,020,676 wei
- maxPriorityFeePerGas: 1,000,000 wei
These values are reasonable for Base chain.
6-7
: Deployment completed successfully.The contract was successfully deployed to
0xA12E4BbEC5565c7e8470fd5B0A42301cc8fF50C6
with:
- Block number: 21426392
- Block hash: 0x1a9cdd6bc23786170150a47c9fe68f5cf3fbe1c242e7badfc0321620f1674399
- Ownership transferred to deployer: 0x0625db97368df1805314e68d0e63e5eb154b9ae6
3-7
: Verify contract addresses and initial token amount.Let's verify the constructor arguments:
✅ Verification successful
Let me gather more information about these addresses by checking for any configuration files or deployment records that might reference them.
Let me search for the contract implementation to understand the role of these addresses and verify the token amount.
Let me check the README and deployment artifacts to understand the purpose of these addresses.
Let me check the build info to understand the constructor parameters and contract purpose.
Based on the gathered information, I can now provide a final verification response:
Constructor arguments and token amounts are correctly configured
The deployment is correctly configured with:
- WeWe token (0x6b9bb36519538e0C073894E964E90172E1c0B41F) documented in README.md as deployed on Base
- VULT IOU token (0x299A57C1f6761b3dB304dc8B18bb4E60A1CF37b6) referenced in DynamicEaterModule
- Vesting duration of 60 blocks
- Max supply of 100 tokens (100000000000000000000 in wei, 18 decimals)
The deployment was successful with the contract deployed at 0xA12E4BbEC5565c7e8470fd5B0A42301cc8fF50C6.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the token addresses and their roles # Test 1: Check if the addresses are valid contracts echo "Verifying contract addresses..." for addr in "0x6b9bb36519538e0C073894E964E90172E1c0B41F" "0x299A57C1f6761b3dB304dc8B18bb4E60A1CF37b6"; do # Search for contract verification status or interface implementations rg -l "contract.*$addr" || echo "Warning: No contract found for $addr" done # Test 2: Check token amount format echo "Verifying token amount..." # 100000000000000000000 should be 100 tokens with 18 decimals if [[ "100000000000000000000" != "100"+"0"*18 ]]; then echo "Warning: Token amount format might be incorrect" fiLength of output: 932
Script:
#!/bin/bash # Search for address references in configuration files and deployment records echo "Searching for address references in configs and deployments..." # Look for files containing these addresses rg -A 5 -B 5 "0x6b9bb36519538e0C073894E964E90172E1c0B41F|0x299A57C1f6761b3dB304dc8B18bb4E60A1CF37b6" --type yaml --type json --type jsonl # Check for any contract deployment or verification records echo -e "\nChecking deployment records..." fd -e json -e yaml -e jsonl . deployments/ -x grep -l "0x6b9bb36519538e0C073894E964E90172E1c0B41F\|0x299A57C1f6761b3dB304dc8B18bb4E60A1CF37b6" {} \; # Look for any contract interfaces or related code echo -e "\nSearching for contract interfaces..." rg "interface.*(?:VultMerge|Merge)" --type solidityLength of output: 15596
Script:
#!/bin/bash # Search for VultMerge contract implementation echo "Searching for VultMerge contract..." fd -e sol . -x cat {} \; | grep -A 20 "contract VultMerge" # Look for any configuration or documentation about these addresses echo -e "\nSearching for address documentation..." rg -i "0x6b9bb36519538e0C073894E964E90172E1c0B41F|0x299A57C1f6761b3dB304dc8B18bb4E60A1CF37b6" --type md --type txt # Check for any deployment scripts or configurations echo -e "\nSearching deployment configurations..." fd -e ts -e js -e json . -x grep -l "VultMerge" {} \;Length of output: 806
Script:
#!/bin/bash # Check README content around the WeWe address echo "Checking README context..." rg -B 2 -A 2 "0x6b9bb36519538e0C073894E964E90172E1c0B41F" README.md # Check deployed addresses configuration echo -e "\nChecking deployed addresses..." cat ./ignition/deployments/chain-8453/deployed_addresses.json # Check VultMerge artifact for constructor documentation echo -e "\nChecking VultMerge artifact..." cat ./ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.json | grep -A 10 "constructor"Length of output: 1003
Script:
#!/bin/bash # Check build info for constructor and contract details echo "Checking build info..." cat ./ignition/deployments/chain-8453/build-info/dce1aa99dc63d048e261276b9113459d.json | jq -r '.output.contracts[] | select(. != null) | .[] | select(.abi != null) | .abi[] | select(.type == "constructor")' # Look for any comments or documentation in the contract echo -e "\nSearching for contract documentation..." cat ./ignition/deployments/chain-8453/build-info/dce1aa99dc63d048e261276b9113459d.json | jq -r '.sources[] | select(. != null) | .' # Check if the second address is documented echo -e "\nChecking second address documentation..." rg -B 2 -A 2 "0x299A57C1f6761b3dB304dc8B18bb4E60A1CF37b6" .Length of output: 15468
ignition/deployments/chain-8453/artifacts/VultMergeModule#VultMerge.json (4)
1-31
: LGTM: Constructor parameters are well-designed.The constructor parameters provide all necessary configuration for a vesting contract:
- Token addresses (_wewe, _vult)
- Vesting duration (_vestingDuration)
- Maximum supply limit (_maxSupply)
32-51
: LGTM: Error definitions are appropriate.The custom errors provide clear failure cases for the contract operations.
52-128
: LGTM: Events provide good visibility into contract state changes.The events are well-designed with:
- Appropriate use of indexed parameters for efficient filtering
- Coverage of all important state changes
- Standard OpenZeppelin events for common functionality
464-468
:⚠️ Potential issueAdd access control to setVirtualWeweBalance function.
The function can modify critical balance state but lacks access control modifiers.
Add the onlyOwner modifier to restrict access:
- "stateMutability": "nonpayable", + "stateMutability": "nonpayable", + "modifiers": [{"name": "onlyOwner"}],Likely invalid or redundant comment.
test/DynamicEater.test.ts
Outdated
describe("Initial Setup", function () { | ||
it("Should set the correct initial values", async function () { | ||
const { merge, vult, wewe } = await loadFixture(deployFixture); | ||
expect(await merge.wewe()).to.equal(await wewe.getAddress()); | ||
expect(await merge.getToken()).to.equal(await vult.getAddress()); | ||
expect(await merge.weweBalance()).to.equal(0); | ||
expect(await merge.lockedStatus()).to.equal(0); | ||
}); | ||
}); |
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.
🛠️ Refactor suggestion
Add comprehensive initial state validation tests.
The initial setup tests should validate all important contract parameters and include negative test cases.
Add these test cases:
it("Should set the correct vesting period", async function () {
const { merge } = await loadFixture(deployFixture);
expect(await merge.vestingPeriod()).to.equal(vestingPeriod);
});
it("Should set the correct max supply", async function () {
const { merge } = await loadFixture(deployFixture);
expect(await merge.maxSupply()).to.equal(maxSupply);
});
it("Should revert when initialized with zero vesting period", async function () {
const Merge = await ethers.getContractFactory("DynamicEater");
await expect(
Merge.deploy(wewe.address, vult.address, 0, maxSupply)
).to.be.revertedWith("Invalid vesting period");
});
test/DynamicEater.test.ts
Outdated
describe("Vult merge with dynamic rates", () => { | ||
it("Should decrease deposit rates", async () => { | ||
const { merge, vult, wewe, otherAccount } = await loadFixture(deployFixture); | ||
|
||
const mergeAddress = await merge.getAddress(); | ||
|
||
let rate = await merge.getRate(); | ||
expect(rate).to.be.eq(12000000n); // Starting rate should be 120% | ||
|
||
// Deposit 1200 wewe to setup the merge | ||
await merge.deposit(1000n); | ||
|
||
expect(await wewe.balanceOf(mergeAddress)).to.be.eq(1000n); | ||
|
||
// Other account should have 1000 vult | ||
expect(await vult.balanceOf(otherAccount.address)).to.be.eq(1000n); | ||
|
||
let totalVested = await merge.totalVested(); | ||
expect(totalVested).to.be.eq(0); | ||
|
||
const slope = await merge.slope(); | ||
expect(slope).to.be.eq(-70); | ||
|
||
const reward = await merge.getTotalWeWe(100n); | ||
expect(reward).to.be.eq(50n); | ||
|
||
// Merge 100 vult to wewe | ||
await merge.connect(otherAccount).merge(100n); | ||
const vested = await merge.vestings(otherAccount.address); | ||
|
||
expect(vested.amount).to.be.eq(50n); | ||
}); | ||
}); |
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.
🛠️ Refactor suggestion
Add edge cases for dynamic rate testing.
The test suite should include additional scenarios to thoroughly test the dynamic rate mechanism.
Add these test cases:
it("Should handle minimum rate correctly", async () => {
const { merge, otherAccount } = await loadFixture(deployFixture);
// Perform multiple merges to reach minimum rate
for(let i = 0; i < 10; i++) {
await merge.connect(otherAccount).merge(100n);
}
const rate = await merge.getRate();
expect(rate).to.be.gte(await merge.MIN_RATE());
});
it("Should maintain correct state after multiple merges", async () => {
const { merge, vult, wewe, otherAccount } = await loadFixture(deployFixture);
const initialBalance = await vult.balanceOf(otherAccount.address);
const mergeAmount = 50n;
// Perform multiple merges
await merge.connect(otherAccount).merge(mergeAmount);
await merge.connect(otherAccount).merge(mergeAmount);
// Verify final state
expect(await vult.balanceOf(otherAccount.address)).to.eq(initialBalance - (mergeAmount * 2n));
const vested = await merge.vestings(otherAccount.address);
expect(vested.amount).to.be.gt(0);
});
test/DynamicEater.test.ts
Outdated
await merge.connect(otherAccount).merge(100n); | ||
const vested = await merge.vestings(otherAccount.address); | ||
|
||
expect(vested.amount).to.be.eq(50n); |
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.
Validate complete vesting struct properties.
The test only validates the vesting amount but should also check the end timestamp.
await merge.connect(otherAccount).merge(100n);
const vested = await merge.vestings(otherAccount.address);
- expect(vested.amount).to.be.eq(50n);
+ expect(vested.amount).to.be.eq(50n);
+ const blockTimestamp = (await ethers.provider.getBlock('latest')).timestamp;
+ expect(vested.end).to.be.eq(blockTimestamp + vestingPeriod);
Committable suggestion was skipped due to low confidence.
function setTreasury(address _treasury) external onlyOwner { | ||
treasury = _treasury; | ||
} |
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.
Add input validation for setTreasury
function
To prevent setting the treasury address to the zero address, consider adding a check to validate the input.
Apply this diff to add input validation:
function setTreasury(address _treasury) external onlyOwner {
+ require(_treasury != address(0), "DynamicEater: Invalid treasury address");
treasury = _treasury;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
function setTreasury(address _treasury) external onlyOwner { | |
treasury = _treasury; | |
} | |
function setTreasury(address _treasury) external onlyOwner { | |
require(_treasury != address(0), "DynamicEater: Invalid treasury address"); | |
treasury = _treasury; | |
} |
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: 8
🧹 Outside diff range and nitpick comments (9)
test/DynamicEater.test.ts (4)
38-63
: Remove or implement the commented test case.The commented test case contains valuable test scenarios. Either implement it or remove it to maintain code cleanliness.
If you'd like, I can help implement this test case with proper assertions and vesting end time validation as suggested in the past review comments.
77-79
: Document the reasoning behind approximate assertions.The test uses
approximately
with different margins (100 for both cases). Add comments explaining why these specific margins are acceptable for the scalar calculations.scalar = await merge.getScalar(10000); +// Allow for small variations due to rounding in rate calculations expect(scalar).to.be.approximately(11900, 100); scalar = await merge.getScalar(500000); +// Larger amounts may have wider variations due to cumulative rounding expect(scalar).to.be.approximately(6200, 100);Also applies to: 86-88
80-84
: Clean up debugging artifacts.Remove commented-out expectations and console.log statements used for debugging.
- // reward = await merge.getTotalWeWe(30000); - // expect(reward).to.be.eq(117); - - // reward = await merge.getTotalWeWe(100000); - // expect(reward).to.be.eq(109); - console.log("wewe", weweRecieved.toString());Also applies to: 92-93
37-94
: Consider expanding test coverage for edge cases.While the current test covers basic scenarios, consider adding these test cases:
- Maximum supply reached scenario
- Multiple deposits affecting rates
- Error cases (invalid amounts, paused contract)
- Boundary conditions for scalar calculations
This will ensure the contract behaves correctly under all conditions.
Would you like me to help implement these additional test cases?
🧰 Tools
🪛 Biome
[error] 65-65: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
contracts/core/merge/DynamicEater.sol (4)
11-14
: Add documentation for the Vesting struct.Consider adding NatSpec documentation to explain the purpose and units of each field in the Vesting struct.
+/// @notice Struct to track vested token amounts and their release time struct Vesting { + /// @notice Amount of tokens vested uint256 amount; + /// @notice Timestamp when the vesting period ends (in seconds since Unix epoch) uint256 end; }
26-29
: Improve documentation for rate constants and ratio.The rate constants and ratio need clearer documentation:
- The rate constants use scaled values but the scaling factor isn't clearly documented
- The ratio is hardcoded without explanation
- int256 public constant maxRate = 12 * 10 ** 4; // 120% or 1.2 - int256 public constant minRate = 5 * 10 ** 4; // 50% or 0.5 - uint32 public vestingDuration; - uint256 public ratio = 131; // scaled from 1.31 + /// @notice Maximum rate scaled by RATE_PRECISION (120% = 1.2) + int256 public constant maxRate = 12 * 10 ** 4; + /// @notice Minimum rate scaled by RATE_PRECISION (50% = 0.5) + int256 public constant minRate = 5 * 10 ** 4; + /// @notice Duration of the vesting period in minutes + uint32 public vestingDuration; + /// @notice Conversion ratio scaled by 100 (1.31 = 131) + uint256 public constant RATIO = 131;
33-55
: Add NatSpec documentation for view functions.The view functions lack proper documentation explaining their purpose and return values.
+ /// @notice Check if an account can claim their vested tokens + /// @param account Address to check + /// @return bool True if the account's vesting period has ended function canClaim(address account) external view returns (bool) { return vestings[account].end <= block.timestamp; } + /// @notice Get the amount of tokens vested for an account + /// @param account Address to check + /// @return uint256 Amount of tokens vested function balanceOf(address account) external view returns (uint256) { return vestings[account].amount; }
240-242
: Add events for important state changes.Consider adding events for important state changes to improve contract transparency and off-chain tracking:
event Merged(uint256 amount, address indexed account); event RateChanged(uint256 newRate); + event MaxSupplyUpdated(uint256 newMaxSupply); + event VestingDurationUpdated(uint32 newDuration); + event TreasuryUpdated(address newTreasury);ignition/deployments/chain-8453/journal.jsonl (1)
5-5
: Review gas configuration.The transaction uses:
- maxFeePerGas: 7,933,270 wei
- maxPriorityFeePerGas: 1,000,000 wei
Consider implementing dynamic gas pricing for more cost-effective deployments.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (8)
- contracts/core/merge/DynamicEater.sol (1 hunks)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.dbg.json (1 hunks)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.json (1 hunks)
- ignition/deployments/chain-8453/deployed_addresses.json (1 hunks)
- ignition/deployments/chain-8453/journal.jsonl (1 hunks)
- ignition/modules/DynamicEaterModule.ts (1 hunks)
- package.json (1 hunks)
- test/DynamicEater.test.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.dbg.json
🚧 Files skipped from review as they are similar to previous changes (2)
- ignition/deployments/chain-8453/deployed_addresses.json
- package.json
🧰 Additional context used
🪛 Gitleaks
ignition/modules/DynamicEaterModule.ts
5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 Biome
test/DynamicEater.test.ts
[error] 65-65: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
🔇 Additional comments (9)
ignition/modules/DynamicEaterModule.ts (2)
1-2
: LGTM! Imports are appropriate.The necessary dependencies for Hardhat Ignition module development are correctly imported.
7-13
: 🛠️ Refactor suggestionVerify vesting period and add configuration options.
Several concerns with the current implementation:
- The vesting period of 60 seems unusually short. Please verify if this is intentional.
- The time unit for
vestingPeriod
is not documented (seconds/blocks?).- Both
vestingPeriod
andmaxSupply
should be configurable for different deployments.Consider making these values configurable:
-const vestingPeriod = 60; -const maxSupply = ethers.parseEther("600000"); +const vestingPeriod = process.env.VESTING_PERIOD ? parseInt(process.env.VESTING_PERIOD) : 60; +const maxSupply = process.env.MAX_SUPPLY ? + ethers.parseEther(process.env.MAX_SUPPLY) : + ethers.parseEther("600000");Also, add documentation explaining the time unit:
// vestingPeriod: Duration of the vesting period in [blocks/seconds]
✅ Verification successful
Let me try different search strategies to gather more context about the vesting implementation.
The error in the previous attempt was due to the
--type sol
flag. Let's try these broader searches to understand:
- Where the DynamicEater contract is implemented
- Any vesting period references in the codebase
- Test files that might document the expected behavior
This will help verify if the vesting period is intentionally short and understand its time unit.
Let me check the DynamicEater contract implementation to understand the vesting period unit.
The search results revealed:
- Different vesting periods are used across contracts:
- DynamicEater: 60
- MergeWithMarket: 7
- GenericMerge tests: 1440
- We need to check the contract implementation to understand:
- The time unit (blocks/seconds)
- How the vesting period is enforced
- If 60 is a reasonable value for this specific contract
Based on the contract implementation and tests, I can now provide a final response:
The vesting period implementation is correct and intentional
The vesting period of 60 in DynamicEaterModule.ts is correct because:
- The time unit is explicitly defined in the contract as minutes (
end: block.timestamp + vestingDuration * 1 minutes
)- 60 minutes (1 hour) is an appropriate vesting duration for this specific contract as it's a dynamic eater contract with a variable rate mechanism
- The contract allows the owner to modify the vesting duration via
setVestingDuration()
if neededHowever, I agree with adding documentation for clarity:
-const vestingPeriod = 60; +// Duration of the vesting period in minutes (60 = 1 hour) +const vestingPeriod = 60;Making it configurable via environment variables is unnecessary since:
- The contract already has an owner-controlled
setVestingDuration()
function for post-deployment adjustments- The value is deployment-specific and intentionally set to 1 hour for this dynamic eater implementation
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for other vesting period implementations in the codebase for consistency rg -A 2 "vestingPeriod|vesting.*period" --type ts --type solLength of output: 90
Script:
#!/bin/bash # Search for DynamicEater contract implementation fd "DynamicEater" --type f # Search for vesting related terms with ripgrep, without file type filter rg -A 2 "vestingPeriod|vesting.*period" # Search for contract deployment tests that might reveal the intended vesting period usage fd "test|spec" --type f | rg -l "DynamicEater"Length of output: 4078
Script:
#!/bin/bash # Check DynamicEater contract implementation for vesting period usage cat contracts/core/merge/DynamicEater.sol # Also check test implementation for more context cat test/DynamicEater.test.tsLength of output: 11726
test/DynamicEater.test.ts (1)
1-9
: LGTM! Well-structured test setup.The import statements and constant definitions follow best practices, with clear naming and appropriate use of
ethers.parseEther
for token amounts.ignition/deployments/chain-8453/journal.jsonl (3)
6-7
: Successful deployment confirmed at0xC5c643b2F782d830abfca2ebe486a48a8245dE27
.The deployment was successful with:
- Block number: 21439578
- Block hash: 0xe448b3b51bcca495201eca4ce33cb44990181a843062e540d2a991c0a33423a1
- Ownership transferred to deployer (0x0625db97368df1805314e68d0e63e5eb154b9ae6)
3-4
: Review constructor parameters for DynamicEater contract.The contract is initialized with:
- Token address:
0x6b9bb36519538e0C073894E964E90172E1c0B41F
- Market address:
0x7bCD8185B7f4171017397993345726E15457B1eE
- Duration: 60 (likely in days)
- Amount: 600,000 tokens (600000000000000000000000 in wei)
#!/bin/bash # Description: Verify token and market addresses exist in other configuration files # Look for address references to ensure consistency rg "0x6b9bb36519538e0C073894E964E90172E1c0B41F|0x7bCD8185B7f4171017397993345726E15457B1eE" --type json
1-3
: Verify the chain ID configuration.The deployment is targeting chain ID 8453 (Base). Ensure this is the intended network for deployment.
✅ Verification successful
Let me try a different approach to verify the chain ID configuration by searching for both chain ID and network references in all file types, not just JSON.
This should help us find any references to the Base network or chain ID 8453 in configuration files, deployment scripts, or documentation.
Based on the search results, I can now generate a final response as we have sufficient evidence about the chain ID configuration across the codebase.
Chain ID configuration is correctly configured and consistent
The deployment to chain ID 8453 (Base) is properly configured and consistent across the codebase:
- Hardhat configuration (
hardhat.config.ts
) explicitly defines Base network with chain ID 8453- The deployment journal file is correctly placed in
ignition/deployments/chain-8453/
- README.md confirms Base as the target blockchain
- All network-specific configurations align with the Base network settings
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify chain ID configuration consistency across the project # Look for any chain ID references in configuration files rg -l "8453|base" --type jsonLength of output: 104
Script:
#!/bin/bash # Search for chain ID and network references across all files rg -i "8453|base.*chain|chain.*base" -A 2 -B 2 # Also check for any configuration or deployment related files fd -e json -e ts -e js -e yaml -e toml "config|deploy" --exec grep -l -i "8453\|base"Length of output: 14136
ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.json (3)
52-108
: Well-implemented security measures.The contract implements robust security features:
- Ownable pattern for access control
- Pause mechanism for emergency stops
- Reentrancy protection
- Events for state changes
219-231
: Ensure safe mathematical operations.The rate calculation:
- Should use SafeMath or unchecked blocks appropriately
- Could be simplified to reduce gas costs
- Needs clear documentation for the mathematical formula
#!/bin/bash # Description: Check for SafeMath usage or unchecked blocks ast-grep --pattern 'using SafeMath for uint256;' ast-grep --pattern 'unchecked { $$$ }'
7-31
:⚠️ Potential issueAdd zero address validation in constructor.
The constructor should validate that
_wewe
and_vult
addresses are not zero addresses to prevent potential issues with token transfers and contract functionality.#!/bin/bash # Description: Check if zero address validation exists in the contract source ast-grep --pattern 'require($_address != address(0), $_)'
const WEWE_ADDRESS = "0x6b9bb36519538e0C073894E964E90172E1c0B41F"; | ||
const TOKEN = "0x7bCD8185B7f4171017397993345726E15457B1eE"; |
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.
🛠️ Refactor suggestion
Consider making addresses configurable.
Hardcoding Ethereum addresses in source code limits deployment flexibility and makes environment-specific deployments more difficult. Consider:
- Moving these to a configuration file
- Using environment variables
- Making them parameters of the buildModule function
Example implementation using environment variables:
-const WEWE_ADDRESS = "0x6b9bb36519538e0C073894E964E90172E1c0B41F";
-const TOKEN = "0x7bCD8185B7f4171017397993345726E15457B1eE";
+const WEWE_ADDRESS = process.env.WEWE_ADDRESS || "0x6b9bb36519538e0C073894E964E90172E1c0B41F";
+const TOKEN = process.env.TOKEN_ADDRESS || "0x7bCD8185B7f4171017397993345726E15457B1eE";
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const WEWE_ADDRESS = "0x6b9bb36519538e0C073894E964E90172E1c0B41F"; | |
const TOKEN = "0x7bCD8185B7f4171017397993345726E15457B1eE"; | |
const WEWE_ADDRESS = process.env.WEWE_ADDRESS || "0x6b9bb36519538e0C073894E964E90172E1c0B41F"; | |
const TOKEN = process.env.TOKEN_ADDRESS || "0x7bCD8185B7f4171017397993345726E15457B1eE"; |
🧰 Tools
🪛 Gitleaks
5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
test/DynamicEater.test.ts
Outdated
async function deployFixture() { | ||
const [owner, otherAccount] = await ethers.getSigners(); | ||
|
||
const Wewe = await ethers.getContractFactory("Wewe"); | ||
const wewe = await Wewe.deploy(); | ||
const Token = await ethers.getContractFactory("MockToken"); | ||
const token = await Token.deploy("Token", "TKN"); | ||
|
||
// Max supply of 1000 tokens to eat | ||
const vestingPeriod = 60; | ||
const Merge = await ethers.getContractFactory("DynamicEater"); | ||
const merge = await Merge.deploy(await wewe.getAddress(), await token.getAddress(), vestingPeriod, maxSupply); | ||
|
||
const isPaused = await merge.paused(); | ||
expect(isPaused).to.be.false; | ||
|
||
await token.transfer(otherAccount, WE_WE_TO_DEPOSIT); | ||
|
||
const mergeAddress = await merge.getAddress(); | ||
|
||
// Arrange | ||
await wewe.approve(mergeAddress, ONE_MILLION); | ||
await token.connect(otherAccount).approve(mergeAddress, ONE_MILLION); | ||
|
||
return { owner, otherAccount, token, wewe, merge }; | ||
} |
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.
🛠️ Refactor suggestion
Add comprehensive initial state validation in the fixture.
While the fixture is well-structured following the AAA pattern, consider adding more comprehensive initial state validations as suggested in the past review comments.
Add these validations after line 24:
// Validate initial contract state
expect(await merge.vestingPeriod()).to.equal(vestingPeriod);
expect(await merge.maxSupply()).to.equal(maxSupply);
// Validate initial token balances
expect(await token.balanceOf(otherAccount.address)).to.equal(0);
expect(await wewe.balanceOf(mergeAddress)).to.equal(0);
test/DynamicEater.test.ts
Outdated
// // expect(vested.amount).to.be.eq(50n); | ||
// }); | ||
|
||
it.only("Should decrease deposit rates with large numbers", async () => { |
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.
Remove .only
from the test case.
The .only
modifier prevents other tests from running. This is typically used during development and should be removed before merging.
-it.only("Should decrease deposit rates with large numbers", async () => {
+it("Should decrease deposit rates with large numbers", async () => {
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
it.only("Should decrease deposit rates with large numbers", async () => { | |
it("Should decrease deposit rates with large numbers", async () => { |
🧰 Tools
🪛 Biome
[error] 65-65: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
function slope() public view returns (int256) { | ||
int256 delta_x = int256(maxSupply) / 10 ** 18; // Scaling factor to 1 | ||
int256 _slope = (maxRate - minRate) / delta_x; // Calculating slope from max and min values | ||
return _slope * 10 ** 4; // Scaling factor to 1_000 | ||
} |
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.
Prevent potential overflow in slope calculation.
The slope calculation could overflow with large maxSupply values due to the scaling operations.
function slope() public view returns (int256) {
- int256 delta_x = int256(maxSupply) / 10 ** 18; // Scaling factor to 1
+ // Prevent overflow by checking maxSupply
+ require(maxSupply <= type(uint256).max / 10 ** 4, "DynamicEater: maxSupply too large");
+ int256 delta_x = int256(maxSupply) / 10 ** 18;
int256 _slope = (maxRate - minRate) / delta_x; // Calculating slope from max and min values
return _slope * 10 ** 4; // Scaling factor to 1_000
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
function slope() public view returns (int256) { | |
int256 delta_x = int256(maxSupply) / 10 ** 18; // Scaling factor to 1 | |
int256 _slope = (maxRate - minRate) / delta_x; // Calculating slope from max and min values | |
return _slope * 10 ** 4; // Scaling factor to 1_000 | |
} | |
function slope() public view returns (int256) { | |
// Prevent overflow by checking maxSupply | |
require(maxSupply <= type(uint256).max / 10 ** 4, "DynamicEater: maxSupply too large"); | |
int256 delta_x = int256(maxSupply) / 10 ** 18; | |
int256 _slope = (maxRate - minRate) / delta_x; // Calculating slope from max and min values | |
return _slope * 10 ** 4; // Scaling factor to 1_000 | |
} |
function _merge(uint256 amount, address token, address from) internal { | ||
uint256 weweToTransfer = getTotalWeWe(_totalVested + amount); | ||
_totalVested += weweToTransfer; | ||
|
||
require( | ||
weweToTransfer <= IERC20(wewe).balanceOf(address(this)), | ||
"DynamicEater: Insufficient token balance to transfer" | ||
); | ||
|
||
// Merge tokens from sender | ||
IERC20(token).transferFrom(from, address(this), amount); | ||
|
||
// If transfer, dont vest | ||
if (vestingDuration != 0) { | ||
// Curent vested | ||
uint256 vestedAmount = vestings[from].amount; | ||
vestings[from] = Vesting({ | ||
amount: weweToTransfer + vestedAmount, | ||
end: block.timestamp + vestingDuration * 1 minutes | ||
}); | ||
} else { | ||
// Transfer Wewe tokens to sender | ||
IERC20(wewe).transfer(from, weweToTransfer); | ||
} | ||
|
||
emit Merged(amount, from); | ||
} |
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.
Add event emission for vesting updates.
The _merge function should emit an event when updating vesting records to maintain transparency and enable off-chain tracking.
+ event VestingUpdated(address indexed account, uint256 amount, uint256 end);
function _merge(uint256 amount, address token, address from) internal {
// ... existing code ...
if (vestingDuration != 0) {
uint256 vestedAmount = vestings[from].amount;
vestings[from] = Vesting({
amount: weweToTransfer + vestedAmount,
end: block.timestamp + vestingDuration * 1 minutes
});
+ emit VestingUpdated(from, weweToTransfer + vestedAmount, block.timestamp + vestingDuration * 1 minutes);
}
Committable suggestion was skipped due to low confidence.
constructor(address _wewe, address _vult, uint32 _vestingDuration, uint256 _maxSupply) { | ||
wewe = _wewe; | ||
_token = _vult; | ||
vestingDuration = _vestingDuration; | ||
maxSupply = _maxSupply; | ||
} | ||
|
||
function setMaxSupply(uint256 _maxSupply) external onlyOwner { | ||
maxSupply = _maxSupply; | ||
} |
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.
Add input validation for constructor and setter functions.
The constructor and setter functions lack crucial input validation:
- Constructor should validate non-zero addresses
- setMaxSupply should validate the new value
constructor(address _wewe, address _vult, uint32 _vestingDuration, uint256 _maxSupply) {
+ require(_wewe != address(0), "DynamicEater: Invalid wewe address");
+ require(_vult != address(0), "DynamicEater: Invalid vult address");
+ require(_maxSupply > 0, "DynamicEater: Invalid max supply");
wewe = _wewe;
_token = _vult;
vestingDuration = _vestingDuration;
maxSupply = _maxSupply;
}
function setMaxSupply(uint256 _maxSupply) external onlyOwner {
+ require(_maxSupply > 0, "DynamicEater: Invalid max supply");
+ require(_maxSupply >= _totalVested, "DynamicEater: Max supply below total vested");
maxSupply = _maxSupply;
}
Committable suggestion was skipped due to low confidence.
{ | ||
"inputs": [ | ||
{ | ||
"internalType": "uint32", | ||
"name": "duration", | ||
"type": "uint32" | ||
} | ||
], | ||
"name": "setVestingDuration", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" | ||
}, |
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.
🛠️ Refactor suggestion
Consider adding vesting constraints and cliff period.
The vesting implementation could be improved by:
- Adding a cliff period to prevent immediate token claims
- Setting a maximum vesting duration to prevent excessively long vesting periods
- Adding validation for the duration parameter
function setVestingDuration(uint32 duration) nonpayable {
+ require(duration <= MAX_VESTING_DURATION, "Duration too long");
+ require(duration >= MIN_VESTING_DURATION, "Duration too short");
vestingDuration = duration;
}
Committable suggestion was skipped due to low confidence.
{ | ||
"inputs": [], | ||
"name": "sweep", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" | ||
}, |
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
Based on the code analysis from the provided results, I can now generate the final response:
The sweep function requires additional safety measures
The sweep function in DynamicEater.sol has the following concerns:
- It uses direct ERC20.transfer() without checking return values, which could silently fail for non-compliant tokens
- While it has onlyOwner access control, it lacks additional safety checks like:
- No timelock/delay mechanism
- No emergency-only restrictions
- The function doesn't use SafeERC20 library despite the contract importing OpenZeppelin contracts
The current implementation:
function sweep() external onlyOwner {
uint256 balance = IERC20(wewe).balanceOf(address(this));
require(balance > 0, "DynamicEater: No balance to sweep");
IERC20(wewe).transfer(owner(), balance);
}
While the function has basic checks like onlyOwner modifier and non-zero balance requirement, using direct transfer calls without SafeERC20 wrapper could lead to silent failures with certain token implementations.
🔗 Analysis chain
Enhance token transfer safety in sweep function.
The sweep function should:
- Check return values of ERC20 transfers
- Consider using SafeERC20 for consistent token handling
- Add access control to prevent unauthorized sweeping
Let me gather more information about the token handling in this contract by checking for ERC20-related code patterns.
The previous ast-grep command might have failed due to syntax. Let's also try with a more flexible pattern:
This will help us understand:
- How the sweep function is implemented
- Whether SafeERC20 or direct ERC20 calls are used
- The overall token handling patterns in the contract
- Contract dependencies and imports
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if SafeERC20 is used in the contract
ast-grep --pattern 'using SafeERC20 for IERC20;'
Length of output: 91
Script:
#!/bin/bash
# Search for ERC20 transfer patterns and sweep function implementation
rg -U "function sweep.*?\{.*?\}" -A 10
rg "transfer\(" -A 2
rg "safeTransfer\(" -A 2
rg "IERC20" -A 2
# Check contract imports
fd "DynamicEater" --exec cat {}
Length of output: 156010
Script:
ast-grep --pattern 'using $_ for $_'
Length of output: 38
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: 1
🧹 Outside diff range and nitpick comments (2)
test/DynamicEater.test.ts (2)
38-63
: Restore and update the commented test cases.The commented out test contains valuable scenarios for testing rate decrease functionality. Consider uncommenting and updating these tests to ensure comprehensive coverage of the core functionality.
Would you like me to help update and restore these test cases with proper assertions and current contract behavior?
95-96
: Remove console.log statement.Console.log statements should not be committed in test files as they create noise in test output.
-console.log("wewe", weweRecieved.toString());
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
- contracts/core/merge/DynamicEater.sol (1 hunks)
- test/DynamicEater.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- contracts/core/merge/DynamicEater.sol
🧰 Additional context used
🪛 Biome
test/DynamicEater.test.ts
[error] 65-65: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
🔇 Additional comments (3)
test/DynamicEater.test.ts (3)
1-9
: LGTM! Well-structured test setup with clear constant definitions.The imports and constant definitions follow testing best practices, with proper use of ethers.parseEther for token amounts.
10-35
: 🛠️ Refactor suggestionAdd comprehensive initial state validations in the fixture.
While the fixture follows good practices with the AAA pattern, it should include validations for key contract parameters and initial token states.
Add these validations after the pause check:
// Validate initial contract state expect(await merge.vestingPeriod()).to.equal(vestingPeriod); expect(await merge.maxSupply()).to.equal(maxSupply); // Validate initial token balances and approvals expect(await token.balanceOf(otherAccount.address)).to.equal(100000); expect(await wewe.balanceOf(mergeAddress)).to.equal(0); expect(await token.allowance(otherAccount.address, mergeAddress)).to.equal(ONE_MILLION); expect(await wewe.allowance(owner.address, mergeAddress)).to.equal(ONE_MILLION);
65-65
:⚠️ Potential issueRemove
.only
from the test case.The
.only
modifier prevents other tests from running. This should be removed before merging.-it.only("Should decrease deposit rates with large numbers", async () => { +it("Should decrease deposit rates with large numbers", async () => {🧰 Tools
🪛 Biome
[error] 65-65: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
test/DynamicEater.test.ts
Outdated
let scalar = await merge.getScalar(1000); | ||
// expect(reward).to.be.eq(120); | ||
expect(scalar).to.be.eq(11989); | ||
|
||
scalar = await merge.getScalar(10000); | ||
expect(scalar).to.be.approximately(11900, 100); | ||
|
||
// reward = await merge.getTotalWeWe(30000); | ||
// expect(reward).to.be.eq(117); | ||
|
||
// reward = await merge.getTotalWeWe(100000); | ||
// expect(reward).to.be.eq(109); | ||
|
||
scalar = await merge.getScalar(500000); | ||
expect(scalar).to.be.approximately(6200, 100); | ||
|
||
let weweRecieved = await merge.getTotalWeWe(1000); | ||
expect(weweRecieved).to.be.approximately(1570, 10); | ||
|
||
weweRecieved = await merge.getTotalWeWe(500000); | ||
expect(weweRecieved).to.be.approximately(403938, 10); | ||
|
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.
🛠️ Refactor suggestion
Document expected behavior and explain magic numbers.
The test lacks clear documentation of the expected behavior and uses magic numbers in expectations without explanation.
Add comments explaining the expected behavior and define constants for the expected values:
// Define expected scalar values based on contract parameters
const EXPECTED_SCALAR_SMALL = 11989; // Expected scalar for 1000 tokens
const EXPECTED_SCALAR_MEDIUM = 11900; // Expected scalar for 10000 tokens
const EXPECTED_SCALAR_LARGE = 6200; // Expected scalar for 500000 tokens
// Test scalar values at different deposit amounts
let scalar = await merge.getScalar(1000);
expect(scalar).to.be.eq(EXPECTED_SCALAR_SMALL, "Incorrect scalar for small deposit");
scalar = await merge.getScalar(10000);
expect(scalar).to.be.approximately(EXPECTED_SCALAR_MEDIUM, 100, "Incorrect scalar for medium deposit");
scalar = await merge.getScalar(500000);
expect(scalar).to.be.approximately(EXPECTED_SCALAR_LARGE, 100, "Incorrect scalar for large deposit");
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: 7
🧹 Outside diff range and nitpick comments (3)
test/DynamicEater.test.ts (1)
6-8
: Document magic numbers and constants.Add comments explaining the significance of these constants and their relationship to the contract's functionality.
+// Maximum supply cap for the DynamicEater contract const maxSupply = ethers.parseEther("600000"); +// Constants for approval amounts and initial deposits const ONE_MILLION = ethers.parseEther("1000000"); const WE_WE_TO_DEPOSIT = ethers.parseEther("1000000");ignition/deployments/chain-8453/journal.jsonl (1)
5-5
: Consider optimizing gas fees for deployment.The transaction uses relatively high gas fees:
- Max fee per gas: 20.534072 gwei
- Max priority fee: 1 gwei
These values could be optimized based on current network conditions.
ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.json (1)
32-108
: Consider adding event for vesting duration changes.While the contract has good event coverage, it's missing an event for tracking vesting duration changes. This would improve transparency and make it easier to track important parameter changes.
Add the following event:
+ event VestingDurationChanged(uint32 oldDuration, uint32 newDuration);
And emit it in setVestingDuration:
function setVestingDuration(uint32 duration) nonpayable { + uint32 oldDuration = vestingDuration; vestingDuration = duration; + emit VestingDurationChanged(oldDuration, duration); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (6)
- contracts/core/merge/DynamicEater.sol (1 hunks)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.dbg.json (1 hunks)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.json (1 hunks)
- ignition/deployments/chain-8453/deployed_addresses.json (1 hunks)
- ignition/deployments/chain-8453/journal.jsonl (1 hunks)
- test/DynamicEater.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.dbg.json
- ignition/deployments/chain-8453/deployed_addresses.json
🧰 Additional context used
🪛 Biome
test/DynamicEater.test.ts
[error] 120-120: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
🔇 Additional comments (13)
test/DynamicEater.test.ts (2)
10-35
: 🛠️ Refactor suggestionEnhance fixture with comprehensive state validation.
The fixture should validate all critical initial contract states.
Add these validations after the pause check:
// Validate initial contract state expect(await merge.vestingPeriod()).to.equal(vestingPeriod); expect(await merge.maxSupply()).to.equal(maxSupply); // Validate initial token balances and approvals expect(await token.balanceOf(otherAccount.address)).to.equal(100000); expect(await wewe.balanceOf(mergeAddress)).to.equal(0); expect(await wewe.allowance(owner.address, mergeAddress)).to.equal(ONE_MILLION); expect(await token.allowance(otherAccount.address, mergeAddress)).to.equal(ONE_MILLION);
65-118
: 🛠️ Refactor suggestionImprove test structure and documentation.
The test case needs better organization and documentation:
- Magic numbers in expectations need explanation
- Related assertions should be grouped
- Test is skipped without explanation
Refactor the test case to improve clarity:
it("Should calculate rates with small numbers", async () => { const { merge, token, wewe, otherAccount } = await loadFixture(deployFixture); // Setup: Deposit initial WeWe tokens await merge.deposit(WE_WE_TO_DEPOSIT); describe("Scalar calculations", () => { // Expected scalar values based on contract parameters const EXPECTED_SCALAR_SMALL = 11989; // For 1000 tokens const EXPECTED_SCALAR_MEDIUM = 11900; // For 10000 tokens const EXPECTED_SCALAR_LARGE = 6200; // For 500000 tokens it("Should calculate correct scalar values", async () => { expect(await merge.getScalar(1000)).to.be.eq(EXPECTED_SCALAR_SMALL); expect(await merge.getScalar(10000)).to.be.approximately(EXPECTED_SCALAR_MEDIUM, 100); expect(await merge.getScalar(500000)).to.be.approximately(EXPECTED_SCALAR_LARGE, 100); }); }); describe("WeWe token calculations", () => { it("Should calculate correct WeWe amounts", async () => { expect(await merge.getTotalWeWe(1000)).to.be.approximately(1570, 10); expect(await merge.getTotalWeWe(500000)).to.be.approximately(403938, 10); }); }); describe("Merge functionality", () => { it("Should handle merge operation correctly", async () => { // Verify initial state expect(await merge.totalVested()).to.be.eq(0); expect(await wewe.balanceOf(otherAccount.address)).to.be.eq(0); expect(await token.balanceOf(otherAccount.address)).to.be.eq(100000); // Perform merge await merge.connect(otherAccount).merge(100000); // Verify post-merge state expect(await wewe.balanceOf(otherAccount.address)).to.be.eq(0); expect(await token.balanceOf(otherAccount.address)).to.be.eq(0); // Verify vesting const vested = await merge.vestings(otherAccount.address); expect(vested.amount).to.be.eq(141925); }); }); });contracts/core/merge/DynamicEater.sol (7)
1-15
: LGTM! Well-structured contract with appropriate security imports.The contract structure is well-organized, using OpenZeppelin's security contracts and defining a clear Vesting struct that matches the PR objectives.
34-56
: LGTM! Clear and concise view functions.The view functions are well-implemented and provide appropriate access to contract state.
181-228
: LGTM! Well-implemented token management functions.The functions have appropriate access control and handle token operations correctly.
254-256
: LGTM! Appropriate events for tracking state changes.The events provide adequate information for tracking merges and rate changes.
50-52
:⚠️ Potential issueAdd zero address validation in setTreasury function.
The function should prevent setting the treasury to the zero address.
Apply this diff:
function setTreasury(address _treasury) external onlyOwner { + require(_treasury != address(0), "DynamicEater: Invalid treasury address"); treasury = _treasury; }
Likely invalid or redundant comment.
235-243
:⚠️ Potential issueFix the double invocation issue in whenClaimable modifier.
The modifier contains two instances of
_;
, which will cause the function body to execute twice whenvestingDuration == 0
.Apply this diff:
modifier whenClaimable(address account) { - // Set to 0 to disable vesting - if (vestingDuration == 0) { - _; - } - - require(vestings[account].end <= block.timestamp, "DynamicEater: Vesting not ended"); + // If vesting is enabled, check if vesting has ended + if (vestingDuration != 0) { + require(vestings[account].end <= block.timestamp, "DynamicEater: Vesting not ended"); + } _; }Likely invalid or redundant comment.
73-77
:⚠️ Potential issuePrevent potential overflow in slope calculation.
The slope calculation could overflow with large maxSupply values due to the scaling operations.
Apply this diff:
function slope() public view returns (int256) { + require(maxSupply <= type(uint256).max / 10 ** 4, "DynamicEater: maxSupply too large"); int256 delta_x = int256(maxSupply) / 10 ** 18; // Scaling factor to 1 int256 _slope = (maxRate - minRate) / delta_x; // Calculating slope from max and min values return _slope * 10 ** 4; // Scaling factor to 1_000 }
Likely invalid or redundant comment.
ignition/deployments/chain-8453/journal.jsonl (3)
6-6
: Ownership transfer event verified successfully.The deployment logs show a successful ownership transfer event (topic: 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0) from zero address to deployer (0x0625db97368df1805314e68d0e63e5eb154b9ae6).
7-7
: Contract deployed successfully at 0xC0AD7eDc382a47afaCBD4EFFf84b66DDC7bc72d4.Deployment completed successfully on Base network (chain ID: 8453) at block 21452054.
3-3
: Verify the constructor arguments for the DynamicEater contract.The contract is initialized with:
- Token address:
0x6b9bb36519538e0C073894E964E90172E1c0B41F
- Market address:
0x7bCD8185B7f4171017397993345726E15457B1eE
- Vesting duration: 60 seconds
- Initial amount: 600,000 tokens (assuming 18 decimals)
✅ Verification successful
Let me try different shell scripts to gather more information about the contract and its dependencies.
Let me check the DynamicEater contract implementation to verify the constructor arguments.
Based on the gathered information, I can now provide the final verification response:
Constructor arguments for DynamicEater contract are correctly configured
The constructor arguments in the deployment journal match the contract's requirements and configuration:
wewe
(0x6b9bb36519538e0C073894E964E90172E1c0B41F): Verified as the WeWe token address on Base chain_vult
(0x7bCD8185B7f4171017397993345726E15457B1eE): Matches the TOKEN constant in DynamicEaterModule_vestingDuration
(60): Matches the vestingPeriod in DynamicEaterModule, representing 60 minutes_maxSupply
(600,000 tokens): Matches the maxSupply in DynamicEaterModule, correctly parsed in weiThe contract implementation shows these parameters are used appropriately:
- WeWe token for rewards distribution
- Vesting duration for time-locked rewards
- Max supply for controlling the total amount of tokens that can be processed
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify token contract existence and interface compatibility # Test: Check if the token address implements ERC20 interface ast-grep --pattern 'interface IERC20 { $$$ function balanceOf(address) external returns (uint256); $$$ }' # Test: Check if the market address exists and is a contract rg -l "contract.*Market.*{"Length of output: 245
Script:
#!/bin/bash # Search for DynamicEater contract definition and its constructor ast-grep --pattern 'contract DynamicEater { $$$ constructor($$$) { $$$ } $$$ }' # Search for any references to the token address rg "0x6b9bb36519538e0C073894E964E90172E1c0B41F" # Search for any references to the market address rg "0x7bCD8185B7f4171017397993345726E15457B1eE" # Find DynamicEater contract file fd "DynamicEater" # Look for Market contract definition with fixed pattern rg "contract\s+\w*Market\w*" # Look for IERC20 interface with fixed pattern rg "interface\s+IERC20"Length of output: 6620
Script:
#!/bin/bash # Get the DynamicEater contract implementation cat contracts/core/merge/DynamicEater.sol # Check the DynamicEaterModule for deployment configuration cat ignition/modules/DynamicEaterModule.ts # Check if the token address is a known contract (WeWe token) cat README.md | grep -A 5 "WeWe"Length of output: 9900
ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.json (1)
7-31
: LGTM: Constructor parameters are well-designed.The constructor parameters provide good control over the vesting mechanism:
- Token and treasury addresses for proper asset management
- Vesting duration with reasonable uint32 limit
- Max supply for controlled token distribution
test/DynamicEater.test.ts
Outdated
it.only("Should calcuate rates with large numbers", async () => { | ||
const { merge } = await loadFixture(deployFixture); | ||
|
||
// Deposit wewe to setup the merge | ||
await merge.deposit(WE_WE_TO_DEPOSIT); | ||
|
||
let scalar = await merge.getScalar(ethers.parseEther("1000")); | ||
expect(scalar).to.be.eq(11989); | ||
|
||
let weweRecieved = await merge.getTotalWeWe(ethers.parseEther("1000")); | ||
console.log(weweRecieved.toString()); | ||
// expect(weweRecieved).to.be.approximately(1570, 10); | ||
}); |
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.
Clean up test implementation.
The test case has several issues:
.only
modifier prevents other tests from running- Contains debug console.log statement
- Has commented out expectations
Apply these changes:
-it.only("Should calcuate rates with large numbers", async () => {
+it("Should calculate rates with large numbers", async () => {
const { merge } = await loadFixture(deployFixture);
// Deposit wewe to setup the merge
await merge.deposit(WE_WE_TO_DEPOSIT);
let scalar = await merge.getScalar(ethers.parseEther("1000"));
expect(scalar).to.be.eq(11989);
let weweRecieved = await merge.getTotalWeWe(ethers.parseEther("1000"));
- console.log(weweRecieved.toString());
- // expect(weweRecieved).to.be.approximately(1570, 10);
+ expect(weweRecieved).to.be.approximately(
+ ethers.parseEther("1570"),
+ ethers.parseEther("10")
+ );
});
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
it.only("Should calcuate rates with large numbers", async () => { | |
const { merge } = await loadFixture(deployFixture); | |
// Deposit wewe to setup the merge | |
await merge.deposit(WE_WE_TO_DEPOSIT); | |
let scalar = await merge.getScalar(ethers.parseEther("1000")); | |
expect(scalar).to.be.eq(11989); | |
let weweRecieved = await merge.getTotalWeWe(ethers.parseEther("1000")); | |
console.log(weweRecieved.toString()); | |
// expect(weweRecieved).to.be.approximately(1570, 10); | |
}); | |
it("Should calculate rates with large numbers", async () => { | |
const { merge } = await loadFixture(deployFixture); | |
// Deposit wewe to setup the merge | |
await merge.deposit(WE_WE_TO_DEPOSIT); | |
let scalar = await merge.getScalar(ethers.parseEther("1000")); | |
expect(scalar).to.be.eq(11989); | |
let weweRecieved = await merge.getTotalWeWe(ethers.parseEther("1000")); | |
expect(weweRecieved).to.be.approximately( | |
ethers.parseEther("1570"), | |
ethers.parseEther("10") | |
); | |
}); |
🧰 Tools
🪛 Biome
[error] 120-120: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
function setMaxSupply(uint256 _maxSupply) external onlyOwner { | ||
maxSupply = _maxSupply; | ||
} |
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.
Add validation in setMaxSupply function.
The function should ensure the new max supply is not less than the current total vested amount.
Apply this diff:
function setMaxSupply(uint256 _maxSupply) external onlyOwner {
+ require(_maxSupply >= _totalVested, "DynamicEater: Max supply below total vested");
maxSupply = _maxSupply;
}
Committable suggestion was skipped due to low confidence.
contract DynamicEater is IWeweReceiver, ReentrancyGuard, Pausable, Ownable { | ||
int256 internal constant RATE_PRECISION = 100_000; | ||
address internal _token; | ||
address public wewe; | ||
address public treasury; | ||
|
||
uint256 internal _totalVested; | ||
uint256 internal _currentHeld; | ||
uint256 public maxSupply; // Max supply of tokens to eat | ||
|
||
int256 public constant maxRate = 12 * 10 ** 4; // 120% or 1.2 | ||
int256 public constant minRate = 5 * 10 ** 4; // 50% or 0.5 | ||
uint32 public vestingDuration; | ||
uint256 public ratio = 131; // scaled from 1.31 | ||
|
||
mapping(address => Vesting) public vestings; | ||
mapping(address => bool) public whiteList; | ||
|
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.
Add input validation for token addresses and document ratio constant.
- The
wewe
and_token
addresses should be validated to prevent zero address assignments. - The
ratio
constant's value (131) needs documentation explaining its significance and calculation.
Apply this diff to add address validation:
constructor(address _wewe, address _vult, uint32 _vestingDuration, uint256 _maxSupply) {
+ require(_wewe != address(0), "DynamicEater: Invalid wewe address");
+ require(_vult != address(0), "DynamicEater: Invalid vult address");
wewe = _wewe;
_token = _vult;
vestingDuration = _vestingDuration;
maxSupply = _maxSupply;
}
Committable suggestion was skipped due to low confidence.
function _merge(uint256 amount, address token, address from) internal { | ||
uint256 weweToTransfer = getTotalWeWe(_totalVested + amount); | ||
|
||
require( | ||
weweToTransfer <= IERC20(wewe).balanceOf(address(this)), | ||
"DynamicEater: Insufficient token balance to transfer" | ||
); | ||
|
||
// Merge tokens from sender | ||
IERC20(token).transferFrom(from, address(this), amount); | ||
|
||
// If transfer, dont vest | ||
if (vestingDuration != 0) { | ||
// Curent vested | ||
uint256 vestedAmount = vestings[from].amount; | ||
vestings[from] = Vesting({ | ||
amount: weweToTransfer + vestedAmount, | ||
end: block.timestamp + vestingDuration * 1 minutes | ||
}); | ||
|
||
_totalVested += weweToTransfer; | ||
} else { | ||
// Transfer Wewe tokens to sender | ||
IERC20(wewe).transfer(from, weweToTransfer); | ||
} | ||
|
||
emit Merged(amount, from); | ||
} |
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.
Use SafeERC20 and emit vesting events.
- Direct use of
transferFrom
can be unsafe with non-compliant tokens - Vesting updates should emit events for transparency
Apply these changes:
+import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+
+using SafeERC20 for IERC20;
+event VestingUpdated(address indexed account, uint256 amount, uint256 end);
function _merge(uint256 amount, address token, address from) internal {
// ... existing code ...
- IERC20(token).transferFrom(from, address(this), amount);
+ IERC20(token).safeTransferFrom(from, address(this), amount);
if (vestingDuration != 0) {
// ... existing code ...
vestings[from] = Vesting({
amount: weweToTransfer + vestedAmount,
end: block.timestamp + vestingDuration * 1 minutes
});
+ emit VestingUpdated(from, weweToTransfer + vestedAmount, block.timestamp + vestingDuration * 1 minutes);
}
Committable suggestion was skipped due to low confidence.
{ | ||
"inputs": [ | ||
{ | ||
"internalType": "uint256", | ||
"name": "amount", | ||
"type": "uint256" | ||
} | ||
], | ||
"name": "merge", | ||
"outputs": [], | ||
"stateMutability": "nonpayable", | ||
"type": "function" |
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.
Add reentrancy protection to merge function.
The merge
function handles token transfers but lacks explicit reentrancy protection. While the contract seems to inherit ReentrancyGuard (based on the bytecode), the merge
function should be explicitly marked as nonReentrant
.
Add the nonReentrant modifier:
-function merge(uint256 amount) nonpayable {
+function merge(uint256 amount) nonpayable nonReentrant {
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
{ | |
"inputs": [ | |
{ | |
"internalType": "uint256", | |
"name": "amount", | |
"type": "uint256" | |
} | |
], | |
"name": "merge", | |
"outputs": [], | |
"stateMutability": "nonpayable", | |
"type": "function" | |
{ | |
"inputs": [ | |
{ | |
"internalType": "uint256", | |
"name": "amount", | |
"type": "uint256" | |
} | |
], | |
"name": "merge", | |
"outputs": [], | |
"stateMutability": "nonpayable nonReentrant", | |
"type": "function" |
"inputs": [ | ||
{ | ||
"internalType": "address", | ||
"name": "", | ||
"type": "address" | ||
} | ||
], | ||
"name": "vestings", | ||
"outputs": [ | ||
{ | ||
"internalType": "uint256", | ||
"name": "amount", | ||
"type": "uint256" | ||
}, | ||
{ | ||
"internalType": "uint256", | ||
"name": "end", | ||
"type": "uint256" | ||
} | ||
], | ||
"stateMutability": "view", | ||
"type": "function" | ||
}, |
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.
🛠️ Refactor suggestion
Consider enhancing the Vesting struct with additional fields.
The current Vesting struct could be improved by adding:
start
timestamp for better trackingcliff
period before vesting beginsreleased
amount to track claimed tokens
Consider updating the struct:
struct Vesting {
uint256 amount;
uint256 end;
+ uint256 start;
+ uint256 cliff;
+ uint256 released;
}
Committable suggestion was skipped due to low confidence.
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: 14
🧹 Outside diff range and nitpick comments (9)
ignition/modules/DynamicEaterModule.ts (1)
8-8
: Clarify the time unit for vestingPeriod.The
vestingPeriod
value of 60 lacks a clear indication of its time unit (seconds, minutes, hours, etc.). Consider adding a comment or using a descriptive constant name.-const vestingPeriod = 60; +// Vesting period in minutes +const VESTING_PERIOD_MINUTES = 60;contracts/core/adaptors/UniswapV3ViaRouterFomo.sol (4)
6-10
: Clean up imports for better maintainability.
- The
IUniswapV3
import appears to be unused- Consider using named imports consistently (e.g., for TransferHelper)
-import {IUniswapV3} from "../../core/adaptors/IUniswapV3.sol"; -import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol"; +import {TransferHelper} from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
12-16
: Consider making token addresses configurable.Hardcoding addresses reduces contract flexibility and complicates deployments to different networks. Consider passing these as constructor parameters or using an upgradeable configuration pattern.
contract UniswapV3ViaRouterFomo is BaseUniRouter, IAMM { - address private constant wrappedETH = 0x4200000000000000000000000000000000000006; - address private constant USDC = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; + address private immutable wrappedETH; + address private immutable USDC; - constructor() Ownable() {} + constructor( + address _wrappedETH, + address _USDC + ) Ownable() { + require(_wrappedETH != address(0), "Invalid WETH"); + require(_USDC != address(0), "Invalid USDC"); + wrappedETH = _wrappedETH; + USDC = _USDC; + }
19-26
: Improve unimplemented function handling.For better gas efficiency and developer experience:
- Use custom errors instead of string messages
- Add TODO comments explaining the intended implementation
+ /// @dev Custom errors + error NotImplemented(); + + // TODO: Implement buy function to swap WEWE for other tokens function buy( uint256 amount, address token, address recipient, bytes calldata extraData ) external returns (uint256) { - revert("Not implemented"); + revert NotImplemented(); } + // TODO: Implement sellAndBuy function to perform a swap and immediate buy function sellAndBuy( uint256 amount, address token, address recipient, bytes calldata extraData ) external returns (uint256) { - revert("Not implemented"); + revert NotImplemented(); }Also applies to: 44-51
12-12
: Well-structured AMM adaptor implementation.The contract follows a clean architecture by:
- Properly implementing the IAMM interface
- Extending BaseUniRouter for core functionality
- Maintaining separation of concerns
Consider documenting the contract's role in the system with NatSpec comments.
test/DynamicEater.test.ts (1)
5-8
: Consider organizing and documenting constants.Group related constants and add documentation to explain their purpose and relationships:
describe("Dynamic Merge Contract", function () { + // Contract configuration const maxSupply = ethers.parseEther("600000"); + + // Test amounts const ONE_MILLION = ethers.parseEther("1000000"); const WE_WE_TO_DEPOSIT = ethers.parseEther("1000000");contracts/core/merge/DynamicEater.sol (1)
27-30
: Document rate calculations and constants.The rate constants and ratio lack clear documentation explaining:
- How the rates (120% and 50%) were determined
- Why the ratio is set to 131
- The relationship between these values
Add detailed comments explaining the rate calculation model and its parameters.
ignition/deployments/chain-8453/journal.jsonl (1)
5-5
: Consider optimizing gas fees for Base chain.The transaction uses:
- maxFeePerGas: 38055324 (~38 Gwei)
- maxPriorityFeePerGas: 1000000 (1 Gwei)
These values might be higher than necessary for Base chain. Consider using Base's gas price oracle for optimal values.
ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.json (1)
387-425
: Consider adding timelock for critical operations.Critical functions like
setMaxSupply
,setTreasury
,setVestingDuration
, andsetWhiteList
could benefit from timelock protection to:
- Give users time to react to parameter changes
- Prevent malicious owner actions
- Align with DeFi best practices
Consider implementing OpenZeppelin's TimelockController or a custom timelock mechanism.
Also applies to: 426-438
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (8)
- contracts/core/adaptors/UniswapV3ViaRouterFomo.sol (1 hunks)
- contracts/core/merge/DynamicEater.sol (1 hunks)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.dbg.json (1 hunks)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.json (1 hunks)
- ignition/deployments/chain-8453/deployed_addresses.json (1 hunks)
- ignition/deployments/chain-8453/journal.jsonl (1 hunks)
- ignition/modules/DynamicEaterModule.ts (1 hunks)
- test/DynamicEater.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.dbg.json
- ignition/deployments/chain-8453/deployed_addresses.json
🧰 Additional context used
🪛 Gitleaks
ignition/modules/DynamicEaterModule.ts
5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 Biome
test/DynamicEater.test.ts
[error] 120-120: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
🔇 Additional comments (8)
ignition/modules/DynamicEaterModule.ts (2)
9-9
: Verify token decimals configuration.The code assumes 9 decimal places for token precision. Please verify this matches the token contract's decimals configuration.
#!/bin/bash # Description: Check token decimals configuration # Search for decimals() function in token contracts ast-grep --pattern 'function decimals() public view returns (uint8) { $$$ }' | rg -A 5 "0xd327d36EB6E1f250D191cD62497d08b4aaa843Ce"
4-5
: Verify contract addresses for the target network.Please ensure these addresses are valid for the intended deployment network. Consider adding network validation to prevent deployment to incorrect networks.
✅ Verification successful
Let me try to gather more information about how these addresses are being used in the codebase.
This will help us understand:
- Where else these addresses are used
- If there are any network-specific configurations
- How these addresses are actually used in the module
Let me try to verify if these addresses are being used consistently across the codebase and if they are properly configured for the target network.
This will help us understand:
- If these addresses are properly configured for specific networks
- If there are any network-specific tests or validations
- If the deployment configuration matches the target network
Based on the gathered information, I can now generate a final response since we have confirmed that:
- The addresses are consistently used across the codebase
- The chainId 8453 (Base network) is properly configured in hardhat.config.ts
- The addresses are used in test constants and appear to be network-specific
Contract addresses are properly configured for Base network deployment
The addresses
WEWE_ADDRESS
(0x6b9bb36519538e0C073894E964E90172E1c0B41F) andTOKEN
(0xd327d36EB6E1f250D191cD62497d08b4aaa843Ce) are consistently used across the codebase and the project is properly configured for Base network (chainId: 8453). The addresses are:
- Referenced in test constants
- Used in hardhat tasks
- Configured alongside other Base network contract addresses
- Deployed in a context where chainId 8453 is explicitly set
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify if the addresses exist on the target network # Note: This script helps identify potential issues but final verification # should be done in the actual deployment environment # Check if addresses follow ERC20 patterns ast-grep --pattern 'contract $_ is ERC20 { $$$ }' | rg -A 5 "0x6b9bb36519538e0C073894E964E90172E1c0B41F|0xd327d36EB6E1f250D191cD62497d08b4aaa843Ce"Length of output: 360
Script:
#!/bin/bash # Search for files containing these addresses and show their context rg -l "0x6b9bb36519538e0C073894E964E90172E1c0B41F|0xd327d36EB6E1f250D191cD62497d08b4aaa843Ce" # Look for any configuration files that might contain network-specific addresses fd -e json -e ts -e js | xargs rg "addresses|contracts" -A 5 # Check the DynamicEaterModule implementation to understand how these addresses are used fd -e ts "DynamicEaterModule" --exec cat {}Length of output: 98590
Script:
#!/bin/bash # Check for any network configuration files that might contain these addresses fd -e json -e ts "config|network" | xargs rg "0x6b9bb36519538e0C073894E964E90172E1c0B41F|0xd327d36EB6E1f250D191cD62497d08b4aaa843Ce" # Check if these addresses are used in test files with network-specific context rg -C 5 "network.*base|chainId.*8453|0x6b9bb36519538e0C073894E964E90172E1c0B41F|0xd327d36EB6E1f250D191cD62497d08b4aaa843Ce" test/ # Look for deployment configurations or network-specific settings fd -e ts -e json "hardhat.config|deploy" | xargs rg "networks.*base|chainId.*8453"Length of output: 2576
🧰 Tools
🪛 Gitleaks
5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
contracts/core/merge/DynamicEater.sol (2)
74-78
:⚠️ Potential issuePrevent potential overflow in slope calculation.
The slope calculation could overflow with large maxSupply values due to the scaling operations.
Add overflow protection:
function slope() public view returns (int256) { + require(maxSupply <= type(uint256).max / 10 ** 4, "DynamicEater: maxSupply too large"); int256 delta_x = int256(maxSupply) / 10 ** 18; // Scaling factor to 1 int256 _slope = (maxRate - minRate) / delta_x; // Calculating slope from max and min values return _slope * 10 ** 4; // Scaling factor to 1_000 }
Likely invalid or redundant comment.
211-223
:⚠️ Potential issueRestrict
receiveApproval
function to the Wewe token contract.The function should only be callable by the Wewe token contract to prevent unauthorized access.
Add a modifier to restrict access:
+modifier onlyWeweToken() { + require(msg.sender == wewe, "DynamicEater: Unauthorized sender"); + _; +} function receiveApproval( address from, uint256 amount, address, bytes calldata - ) external nonReentrant whenNotPaused { + ) external nonReentrant whenNotPaused onlyWeweToken { // After wewe approve and call, it will call this function require(_token != address(0), "DynamicEater: Token address not set"); _merge(amount, _token, from); }Likely invalid or redundant comment.
ignition/deployments/chain-8453/journal.jsonl (2)
6-7
: LGTM! Deployment completed successfully.The contract was successfully deployed to Base chain:
- Contract address: 0x4Fb6b659bc243a7354A6FFDD66c0bC1F1241F66c
- Transaction hash: 0x8819d464f777a26b0bd7ee08cc0a16600c8f342f22199e7cdcbd60a5ccfb47ab
3-3
: Verify the constructor arguments for the DynamicEater contract.The constructor is initialized with:
- Two addresses that need role verification
- Duration of 60 (units needed)
- Amount of 600000000000000 (decimal places needed)
✅ Verification successful
Let me analyze the results and generate additional verification scripts to better understand the contract and its parameters.
The first search helped identify that WEWE_ADDRESS is consistently used across the codebase and is listed in README.md as "WeWe" token. The second address (TOKEN) appears to be another token contract. I need to verify the contract implementation to understand the duration unit and amount calculation before providing the final response.
Let me modify the search to find the relevant information without relying on file extensions.
The previous search had issues with file type recognition. These new commands should help us understand the contract implementation, duration units, and token decimals without relying on specific file extensions.
Based on the search results, I can now provide a complete verification of the constructor arguments:
Constructor arguments for DynamicEater are correctly configured
The parameters are:
- WEWE_ADDRESS (0x6b9bb36519538e0C073894E964E90172E1c0B41F): Verified as WeWe token on Base chain
- TOKEN (0xd327d36EB6E1f250D191cD62497d08b4aaa843Ce): Target token to be "eaten"
- vestingDuration (60): Verified as minutes from contract implementation:
end: block.timestamp + vestingDuration * 1 minutes
- maxSupply (600000000000000): Correctly scaled for 9 decimals as shown in module:
const maxSupply = ethers.parseUnits("600000", 9);🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the token decimals and roles of the addresses # Test 1: Check if the first address is a token contract rg -A 2 "0x6b9bb36519538e0C073894E964E90172E1c0B41F" # Test 2: Check if the second address has a specific role rg -A 2 "0xd327d36EB6E1f250D191cD62497d08b4aaa843Ce" # Test 3: Search for duration constants to verify the unit (days/months) ast-grep --pattern 'const $_: $_ = 60'Length of output: 32618
Script:
#!/bin/bash # Check DynamicEater contract implementation to understand the duration unit and amount ast-grep --pattern 'contract DynamicEater { $$$ }' # Check for any duration-related constants or documentation rg -A 2 "duration|period|vesting" --type ts --type sol # Check for any token decimals information rg -A 2 "decimals|DECIMALS" --type ts --type solLength of output: 218
Script:
#!/bin/bash # Find DynamicEater contract implementation fd -e sol . | xargs rg -A 5 "contract DynamicEater" # Check for duration/vesting related code without file type restriction rg -A 2 "duration|period|vesting" # Check for decimals information without file type restriction rg -A 2 "decimals|DECIMALS" # Find any documentation about the contract parameters fd "README|docs" | xargs rg -A 3 "DynamicEater|vesting"Length of output: 66673
ignition/deployments/chain-8453/artifacts/DynamicEaterModule#DynamicEater.json (2)
1-5
: LGTM! Contract structure follows best practices.The contract is well-structured and uses standard OpenZeppelin contracts for core functionality:
- Ownable for access control
- ReentrancyGuard for security
- Pausable for emergency stops
7-31
: Please clarify the purpose of the_vult
parameter.While most constructor parameters are self-explanatory, the purpose of the
_vult
parameter is not clear from the contract interface. Consider adding documentation to explain its role.Let me check if there's any usage pattern of this parameter:
✅ Verification successful
Based on the search results, I can now provide a clear explanation of the
_vult
parameter's purpose:The
_vult
parameter represents the token address that users can merge into the DynamicEater contractThe evidence from the codebase shows that:
- The
_vult
address is stored as_token
in the constructor and is used as the source token that users can merge- The contract allows users to merge this token to receive
wewe
tokens based on a dynamic rate- The token is used in key operations:
merge()
function accepts this token from usersmergeAndSell()
function accepts and sells this token through an AMM- The contract tracks balances and vesting of this token
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for _vult usage patterns rg -U "vult|_vult" -A 5Length of output: 37669
export default buildModule("DynamicEaterModule", m => { | ||
const vestingPeriod = 60; | ||
const maxSupply = ethers.parseUnits("600000", 9); | ||
const merge = m.contract("DynamicEater", [WEWE_ADDRESS, TOKEN, vestingPeriod, maxSupply]); | ||
|
||
return { merge }; | ||
}); |
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.
🛠️ Refactor suggestion
Consider adding input validation and documentation.
The module would benefit from:
- Input parameter validation
- JSDoc documentation describing the module's purpose and parameters
- Error handling for contract deployment failures
+/**
+ * @title DynamicEater Module
+ * @dev Deploys DynamicEater contract with vesting functionality
+ * @param WEWE_ADDRESS The address of the WEWE token
+ * @param TOKEN The address of the vesting token
+ * @param vestingPeriod The duration of the vesting period in minutes
+ * @param maxSupply The maximum token supply with 9 decimals
+ */
export default buildModule("DynamicEaterModule", m => {
+ // Validate addresses
+ if (!ethers.isAddress(WEWE_ADDRESS) || !ethers.isAddress(TOKEN)) {
+ throw new Error("Invalid token addresses");
+ }
+
+ // Validate vesting period
+ if (vestingPeriod <= 0) {
+ throw new Error("Vesting period must be positive");
+ }
+
const vestingPeriod = 60;
const maxSupply = ethers.parseUnits("600000", 9);
const merge = m.contract("DynamicEater", [WEWE_ADDRESS, TOKEN, vestingPeriod, maxSupply]);
Committable suggestion was skipped due to low confidence.
function sell( | ||
uint256 amount, | ||
address token, | ||
address recipient, | ||
bytes calldata extraData | ||
) external returns (uint256) { | ||
uint24 feeTokenWeth = 10000; | ||
uint24 feeWethToUsdc = 500; | ||
uint24 feeUsdcToWewe = 10000; | ||
bytes memory path = abi.encodePacked(token, feeTokenWeth, wrappedETH, feeWethToUsdc, USDC, feeUsdcToWewe, wewe); | ||
uint256 amountOut = _swapMultihop(token, amount, path, msg.sender, recipient, 0); | ||
|
||
emit Sold(amount, amountOut, token, recipient); | ||
return amountOut; | ||
} |
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
Now I have enough context to generate the final response since I can see that:
- The
wewe
token address is defined in the base contractBaseUniRouter
as a constant:0x6b9bb36519538e0C073894E964E90172E1c0B41F
- The contract inherits from
BaseUniRouter
and implementsIAMM
interface - The base contract already has fee validation in place
Security improvements needed, but review needs adjustment
The review comment's concerns about input validation and slippage protection are valid, but some modifications are needed:
- The
wewe
token concern can be dismissed as it's properly defined in the base contract - The hardcoded fee tiers concern can be removed as there's already fee validation in the base contract
- The remaining security improvements are valid and should be implemented:
- Add zero-address validation for token and recipient
- Add amount validation
- Add slippage protection using
extraData
Recommended changes:
function sell(
uint256 amount,
address token,
address recipient,
bytes calldata extraData
) external returns (uint256) {
+ require(token != address(0), "Invalid token");
+ require(recipient != address(0), "Invalid recipient");
+ require(amount > 0, "Invalid amount");
+
+ // Extract minimum output amount from extraData
+ uint256 minAmountOut = abi.decode(extraData, (uint256));
+ require(minAmountOut > 0, "Invalid minAmountOut");
+
uint24 feeTokenWeth = 10000;
uint24 feeWethToUsdc = 500;
uint24 feeUsdcToWewe = 10000;
bytes memory path = abi.encodePacked(token, feeTokenWeth, wrappedETH, feeWethToUsdc, USDC, feeUsdcToWewe, wewe);
- uint256 amountOut = _swapMultihop(token, amount, path, msg.sender, recipient, 0);
+ uint256 amountOut = _swapMultihop(token, amount, path, msg.sender, recipient, minAmountOut);
emit Sold(amount, amountOut, token, recipient);
return amountOut;
}
🔗 Analysis chain
Add input validation and slippage protection.
Several security concerns need to be addressed:
- Missing input validation for token and recipient addresses
- No validation for amount
- Zero slippage protection (amountOutMinimum = 0) which could lead to sandwich attacks
- Hardcoded fee tiers might not be optimal for all token pairs
Apply these security improvements:
function sell(
uint256 amount,
address token,
address recipient,
bytes calldata extraData
) external returns (uint256) {
+ require(token != address(0), "Invalid token");
+ require(recipient != address(0), "Invalid recipient");
+ require(amount > 0, "Invalid amount");
+
uint24 feeTokenWeth = 10000;
uint24 feeWethToUsdc = 500;
uint24 feeUsdcToWewe = 10000;
bytes memory path = abi.encodePacked(token, feeTokenWeth, wrappedETH, feeWethToUsdc, USDC, feeUsdcToWewe, wewe);
- uint256 amountOut = _swapMultihop(token, amount, path, msg.sender, recipient, 0);
+
+ // Extract minimum output amount from extraData
+ uint256 minAmountOut = abi.decode(extraData, (uint256));
+ require(minAmountOut > 0, "Invalid minAmountOut");
+
+ uint256 amountOut = _swapMultihop(token, amount, path, msg.sender, recipient, minAmountOut);
emit Sold(amount, amountOut, token, recipient);
return amountOut;
}
Let me try a different approach to verify the codebase.
The previous search failed because of file type issues. Let's try these broader searches to understand the contract structure and wewe token definition. This will help us verify the security concerns and validate the review comment's suggestions.
Let me check the base contract and inheritance to understand the complete context.
This will help us understand where wewe
is defined and verify the contract's inheritance structure to make a proper assessment of the security concerns.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Verify the wewe token address definition
rg -l "wewe\s*=.*" --type sol
Length of output: 59
Script:
#!/bin/bash
# Search for wewe token definition and usage
rg -l "wewe"
# Search for the base contract that might define wewe
rg -A 5 "contract UniswapV3ViaRouterFomo"
# Look for inheritance or state variables
ast-grep --pattern 'contract UniswapV3ViaRouterFomo $_'
Length of output: 1588
Script:
#!/bin/bash
# Check BaseUniRouter for wewe definition
rg -A 10 "contract BaseUniRouter" contracts/core/adaptors/BaseUniRouter.sol
# Check IAMM interface
rg -A 10 "interface IAMM"
# Look for wewe address definition in the contract
rg "wewe\s*=|wewe.*constant" contracts/core/adaptors/UniswapV3ViaRouterFomo.sol
Length of output: 3472
// // Working with small value | ||
// it.only("Should decrease deposit rates", async () => { | ||
// const { merge, token, wewe, otherAccount } = await loadFixture(deployFixture); | ||
|
||
// const mergeAddress = await merge.getAddress(); | ||
|
||
// let rate = await merge.getRate(); | ||
// expect(rate).to.be.eq(12000000000n); // Starting rate should be 120% | ||
|
||
// await merge.deposit(1000n); | ||
|
||
// let totalVested = await merge.totalVested(); | ||
// expect(totalVested).to.be.eq(0); | ||
|
||
// const slope = await merge.slope(); | ||
// expect(slope).to.be.eq(116666666666n); | ||
|
||
// const reward = await merge.getTotalWeWe(100n); | ||
// expect(reward).to.be.eq(120000116666666666n); | ||
|
||
// // // Merge 100 vult to wewe | ||
// // await merge.connect(otherAccount).merge(100n); | ||
// // const vested = await merge.vestings(otherAccount.address); | ||
|
||
// // expect(vested.amount).to.be.eq(50n); | ||
// }); |
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.
Remove or implement commented test case.
The commented test case contains valuable test scenarios for rate calculation and vesting verification. Either remove it or implement it properly.
Would you like me to help implement this test case properly?
// let totalVested = await merge.totalVested(); | ||
// expect(totalVested).to.be.eq(0); | ||
|
||
// const slope = await merge.slope(); | ||
// expect(slope).to.be.eq(116666666666n); | ||
|
||
// const reward = await merge.getTotalWeWe(100n); | ||
// expect(reward).to.be.eq(120000116666666666n); | ||
|
||
// // // Merge 100 vult to wewe | ||
// // await merge.connect(otherAccount).merge(100n); | ||
// // const vested = await merge.vestings(otherAccount.address); | ||
|
||
// // expect(vested.amount).to.be.eq(50n); | ||
// }); | ||
|
||
it.skip("Should calcuate rates with small numbers", async () => { | ||
const { merge, token, wewe, otherAccount } = await loadFixture(deployFixture); | ||
|
||
const mergeAddress = await merge.getAddress(); | ||
|
||
// Deposit wewe to setup the merge | ||
await merge.deposit(WE_WE_TO_DEPOSIT); | ||
|
||
// let scalar = await merge.getScalar(1000); | ||
// // expect(reward).to.be.eq(120); | ||
// expect(scalar).to.be.eq(11989); | ||
|
||
// scalar = await merge.getScalar(10000); | ||
// expect(scalar).to.be.approximately(11900, 100); | ||
|
||
// // reward = await merge.getTotalWeWe(30000); | ||
// // expect(reward).to.be.eq(117); | ||
|
||
// // reward = await merge.getTotalWeWe(100000); | ||
// // expect(reward).to.be.eq(109); | ||
|
||
// scalar = await merge.getScalar(500000); | ||
// expect(scalar).to.be.approximately(6200, 100); | ||
|
||
// let weweRecieved = await merge.getTotalWeWe(1000); | ||
// expect(weweRecieved).to.be.approximately(1570, 10); | ||
|
||
// weweRecieved = await merge.getTotalWeWe(500000); | ||
// expect(weweRecieved).to.be.approximately(403938, 10); | ||
|
||
let totalVested = await merge.totalVested(); | ||
expect(totalVested).to.be.eq(0); | ||
|
||
let weweBalance = await wewe.balanceOf(otherAccount.address); | ||
expect(weweBalance).to.be.eq(0); | ||
|
||
let tokenBalance = await token.balanceOf(otherAccount.address); | ||
expect(tokenBalance).to.be.eq(100000); | ||
|
||
// Merge 100,000 vult to wewe | ||
await merge.connect(otherAccount).merge(100000); | ||
// expect(await merge.totalVested()).to.be.eq(100000); | ||
|
||
// not yet sent | ||
weweBalance = await wewe.balanceOf(otherAccount.address); | ||
expect(weweBalance).to.be.eq(0); | ||
|
||
tokenBalance = await token.balanceOf(otherAccount.address); | ||
expect(tokenBalance).to.be.eq(0); | ||
|
||
// Get the vested amount | ||
const vested = await merge.vestings(otherAccount.address); | ||
expect(vested.amount).to.be.eq(141925); | ||
}); | ||
|
||
it.only("Should calcuate rates with large numbers", async () => { | ||
const { merge, otherAccount } = await loadFixture(deployFixture); | ||
|
||
// Deposit wewe to setup the merge | ||
await merge.deposit(ethers.parseEther("1000000")); | ||
|
||
// let scalar = await merge.getScalar(ethers.parseEther("1000")); | ||
// expect(scalar).to.be.eq(11989); | ||
|
||
// let weweRecieved = await merge.getTotalWeWe(ethers.parseEther("1000")); | ||
// // console.log(weweRecieved.toString()); | ||
// // expect(weweRecieved).to.be.approximately(1570, 10); | ||
|
||
await merge.connect(otherAccount).merge(ethers.parseEther("100000")); | ||
|
||
let vested = await merge.vestings(otherAccount.address); | ||
console.log(vested.amount.toString()); | ||
|
||
await merge.connect(otherAccount).merge(ethers.parseEther("100000")); | ||
|
||
vested = await merge.vestings(otherAccount.address); | ||
console.log(vested.amount.toString()); | ||
|
||
await merge.connect(otherAccount).merge(ethers.parseEther("100000")); | ||
|
||
vested = await merge.vestings(otherAccount.address); | ||
console.log(vested.amount.toString()); | ||
}); | ||
}); |
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.
🛠️ Refactor suggestion
Add test coverage for edge cases.
The test suite is missing important edge cases for the dynamic rate mechanism:
- Maximum supply reached
- Zero amount merges
- Minimum rate scenarios
Add these test cases:
it("Should revert when max supply is reached", async () => {
const { merge, otherAccount } = await loadFixture(deployFixture);
await merge.deposit(WE_WE_TO_DEPOSIT);
// Attempt to merge more than max supply
await expect(
merge.connect(otherAccount).merge(ethers.parseEther("700000"))
).to.be.revertedWith("Max supply exceeded");
});
it("Should revert on zero amount merge", async () => {
const { merge, otherAccount } = await loadFixture(deployFixture);
await merge.deposit(WE_WE_TO_DEPOSIT);
await expect(
merge.connect(otherAccount).merge(0)
).to.be.revertedWith("Amount must be greater than 0");
});
it("Should handle minimum rate correctly", async () => {
const { merge, otherAccount } = await loadFixture(deployFixture);
await merge.deposit(WE_WE_TO_DEPOSIT);
// Perform multiple merges to reach minimum rate
for(let i = 0; i < 5; i++) {
await merge.connect(otherAccount).merge(ethers.parseEther("100000"));
}
const rate = await merge.getRate();
expect(rate).to.be.gte(await merge.MIN_RATE());
});
🧰 Tools
🪛 Biome
[error] 120-120: Don't focus the test.
The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.(lint/suspicious/noFocusedTests)
it.skip("Should calcuate rates with small numbers", async () => { | ||
const { merge, token, wewe, otherAccount } = await loadFixture(deployFixture); | ||
|
||
const mergeAddress = await merge.getAddress(); | ||
|
||
// Deposit wewe to setup the merge | ||
await merge.deposit(WE_WE_TO_DEPOSIT); | ||
|
||
// let scalar = await merge.getScalar(1000); | ||
// // expect(reward).to.be.eq(120); | ||
// expect(scalar).to.be.eq(11989); | ||
|
||
// scalar = await merge.getScalar(10000); | ||
// expect(scalar).to.be.approximately(11900, 100); | ||
|
||
// // reward = await merge.getTotalWeWe(30000); | ||
// // expect(reward).to.be.eq(117); | ||
|
||
// // reward = await merge.getTotalWeWe(100000); | ||
// // expect(reward).to.be.eq(109); | ||
|
||
// scalar = await merge.getScalar(500000); | ||
// expect(scalar).to.be.approximately(6200, 100); | ||
|
||
// let weweRecieved = await merge.getTotalWeWe(1000); | ||
// expect(weweRecieved).to.be.approximately(1570, 10); | ||
|
||
// weweRecieved = await merge.getTotalWeWe(500000); | ||
// expect(weweRecieved).to.be.approximately(403938, 10); | ||
|
||
let totalVested = await merge.totalVested(); | ||
expect(totalVested).to.be.eq(0); | ||
|
||
let weweBalance = await wewe.balanceOf(otherAccount.address); | ||
expect(weweBalance).to.be.eq(0); | ||
|
||
let tokenBalance = await token.balanceOf(otherAccount.address); | ||
expect(tokenBalance).to.be.eq(100000); | ||
|
||
// Merge 100,000 vult to wewe | ||
await merge.connect(otherAccount).merge(100000); | ||
// expect(await merge.totalVested()).to.be.eq(100000); | ||
|
||
// not yet sent | ||
weweBalance = await wewe.balanceOf(otherAccount.address); | ||
expect(weweBalance).to.be.eq(0); | ||
|
||
tokenBalance = await token.balanceOf(otherAccount.address); | ||
expect(tokenBalance).to.be.eq(0); | ||
|
||
// Get the vested amount | ||
const vested = await merge.vestings(otherAccount.address); | ||
expect(vested.amount).to.be.eq(141925); | ||
}); |
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.
Fix skipped test case implementation.
The test case has several issues:
- Uses
.skip
modifier - Contains commented out assertions
- Missing validation for vesting end timestamp
Apply these changes:
-it.skip("Should calcuate rates with small numbers", async () => {
+it("Should calculate rates with small numbers", async () => {
const { merge } = await loadFixture(deployFixture);
// Deposit wewe to setup the merge
await merge.deposit(WE_WE_TO_DEPOSIT);
- // let scalar = await merge.getScalar(1000);
- // expect(scalar).to.be.eq(11989);
+ let scalar = await merge.getScalar(1000);
+ expect(scalar).to.be.eq(11989);
// Get the vested amount
const vested = await merge.vestings(otherAccount.address);
expect(vested.amount).to.be.eq(141925);
+ const blockTimestamp = (await ethers.provider.getBlock('latest')).timestamp;
+ expect(vested.end).to.be.eq(blockTimestamp + vestingPeriod);
Committable suggestion was skipped due to low confidence.
modifier whenClaimable(address account) { | ||
// Set to 0 to disable vesting | ||
if (vestingDuration == 0) { | ||
_; | ||
} | ||
|
||
require(vestings[account].end <= block.timestamp, "DynamicEater: Vesting not ended"); | ||
_; | ||
} |
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.
Fix the double invocation issue in whenClaimable
modifier.
The modifier contains two instances of _;
, which will cause the function body to execute twice when vestingDuration == 0
.
Apply this fix:
modifier whenClaimable(address account) {
- // Set to 0 to disable vesting
- if (vestingDuration == 0) {
- _;
- }
-
- require(vestings[account].end <= block.timestamp, "DynamicEater: Vesting not ended");
+ // If vesting is enabled, check if vesting has ended
+ if (vestingDuration != 0) {
+ require(vestings[account].end <= block.timestamp, "DynamicEater: Vesting not ended");
+ }
_;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
modifier whenClaimable(address account) { | |
// Set to 0 to disable vesting | |
if (vestingDuration == 0) { | |
_; | |
} | |
require(vestings[account].end <= block.timestamp, "DynamicEater: Vesting not ended"); | |
_; | |
} | |
modifier whenClaimable(address account) { | |
// If vesting is enabled, check if vesting has ended | |
if (vestingDuration != 0) { | |
require(vestings[account].end <= block.timestamp, "DynamicEater: Vesting not ended"); | |
} | |
_; | |
} |
modifier whenSolvent(uint256 amountToMerge) { | ||
uint256 newAmountToVest = (amountToMerge * _getCurrentRate(amountToMerge)) / 100_000; | ||
require( | ||
IERC20(wewe).balanceOf(address(this)) >= _totalVested + newAmountToVest, | ||
"DynamicEater: Insufficient Wewe balance" | ||
); | ||
_; | ||
} |
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.
Add overflow protection in whenSolvent
modifier.
The calculation of newAmountToVest
could potentially overflow with large values of amountToMerge
.
Add SafeMath or unchecked block based on your requirements:
modifier whenSolvent(uint256 amountToMerge) {
+ require(amountToMerge <= type(uint256).max / _getCurrentRate(amountToMerge), "DynamicEater: Amount too large");
uint256 newAmountToVest = (amountToMerge * _getCurrentRate(amountToMerge)) / 100_000;
require(
IERC20(wewe).balanceOf(address(this)) >= _totalVested + newAmountToVest,
"DynamicEater: Insufficient Wewe balance"
);
_;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
modifier whenSolvent(uint256 amountToMerge) { | |
uint256 newAmountToVest = (amountToMerge * _getCurrentRate(amountToMerge)) / 100_000; | |
require( | |
IERC20(wewe).balanceOf(address(this)) >= _totalVested + newAmountToVest, | |
"DynamicEater: Insufficient Wewe balance" | |
); | |
_; | |
} | |
modifier whenSolvent(uint256 amountToMerge) { | |
require(amountToMerge <= type(uint256).max / _getCurrentRate(amountToMerge), "DynamicEater: Amount too large"); | |
uint256 newAmountToVest = (amountToMerge * _getCurrentRate(amountToMerge)) / 100_000; | |
require( | |
IERC20(wewe).balanceOf(address(this)) >= _totalVested + newAmountToVest, | |
"DynamicEater: Insufficient Wewe balance" | |
); | |
_; | |
} |
function mergeAndSell(uint256 amount, IAMM amm, bytes calldata extraData) external nonReentrant whenNotPaused { | ||
uint256 balance = IERC20(_token).balanceOf(msg.sender); | ||
require(balance >= amount, "DynamicEater: Insufficient balance to eat"); | ||
|
||
_merge(amount, _token, msg.sender); | ||
|
||
// Approve the AMM to use the tokens now in this contract | ||
IERC20(_token).approve(address(amm), amount); | ||
|
||
// Sell the tokens, can fund the contract with the token | ||
address recipient = treasury == address(0) ? address(this) : treasury; | ||
amm.sell(amount, _token, recipient, extraData); | ||
} |
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.
Add slippage protection in mergeAndSell
.
The function lacks slippage protection when selling tokens through the AMM, which could lead to unfavorable trades.
Consider adding minimum output amount parameter and validation:
function mergeAndSell(
uint256 amount,
IAMM amm,
+ uint256 minOutput,
bytes calldata extraData
) external nonReentrant whenNotPaused {
// ... existing code ...
// Sell the tokens with slippage protection
address recipient = treasury == address(0) ? address(this) : treasury;
- amm.sell(amount, _token, recipient, extraData);
+ uint256 outputAmount = amm.sell(amount, _token, recipient, extraData);
+ require(outputAmount >= minOutput, "DynamicEater: Insufficient output amount");
}
Committable suggestion was skipped due to low confidence.
function _merge(uint256 amount, address token, address from) internal { | ||
// uint256 weweToTransfer = getTotalWeWe(_totalVested + amount, 0); | ||
uint256 y1 = 0; | ||
if (_totalVested > 0) y1 = (_totalVested / 8571) * 10 ** 9; // come down the pervious value to the y axis intercept | ||
|
||
uint256 weweToTransfer = getTotalWeWe(amount, y1); | ||
|
||
require( | ||
weweToTransfer <= IERC20(wewe).balanceOf(address(this)), | ||
"DynamicEater: Insufficient token balance to transfer" | ||
); | ||
|
||
// Merge tokens from sender | ||
IERC20(token).transferFrom(from, address(this), amount); | ||
|
||
// If transfer, dont vest | ||
if (vestingDuration != 0) { | ||
// Curent vested | ||
uint256 vestedAmount = vestings[from].amount; | ||
vestings[from] = Vesting({ | ||
amount: weweToTransfer + vestedAmount, | ||
end: block.timestamp + vestingDuration * 1 minutes | ||
}); | ||
|
||
_totalVested += weweToTransfer; | ||
} else { | ||
// Transfer Wewe tokens to sender | ||
IERC20(wewe).transfer(from, weweToTransfer); | ||
} | ||
|
||
emit Merged(amount, from); | ||
} |
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.
Use SafeERC20 for token transfers and emit vesting events.
The _merge
function has potential security and transparency issues:
- Direct use of
transferFrom
can be unsafe with non-compliant tokens - Vesting updates should emit events for transparency
Apply these security improvements:
+import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+
+using SafeERC20 for IERC20;
+
+event VestingUpdated(address indexed account, uint256 amount, uint256 end);
function _merge(uint256 amount, address token, address from) internal {
// ... existing code ...
- IERC20(token).transferFrom(from, address(this), amount);
+ IERC20(token).safeTransferFrom(from, address(this), amount);
if (vestingDuration != 0) {
vestings[from] = Vesting({
amount: weweToTransfer + vestedAmount,
end: block.timestamp + vestingDuration * 1 minutes
});
+ emit VestingUpdated(from, weweToTransfer + vestedAmount, block.timestamp + vestingDuration * 1 minutes);
}
}
Committable suggestion was skipped due to low confidence.
{ | ||
"anonymous": false, | ||
"inputs": [ | ||
{ | ||
"indexed": false, | ||
"internalType": "uint256", | ||
"name": "amount", | ||
"type": "uint256" | ||
}, | ||
{ | ||
"indexed": true, | ||
"internalType": "address", | ||
"name": "account", | ||
"type": "address" | ||
} | ||
], | ||
"name": "Merged", | ||
"type": "event" | ||
}, | ||
{ | ||
"anonymous": false, | ||
"inputs": [ | ||
{ | ||
"indexed": true, | ||
"internalType": "address", | ||
"name": "previousOwner", | ||
"type": "address" | ||
}, | ||
{ | ||
"indexed": true, | ||
"internalType": "address", | ||
"name": "newOwner", | ||
"type": "address" | ||
} | ||
], | ||
"name": "OwnershipTransferred", | ||
"type": "event" | ||
}, | ||
{ | ||
"anonymous": false, | ||
"inputs": [ | ||
{ | ||
"indexed": false, | ||
"internalType": "address", | ||
"name": "account", | ||
"type": "address" | ||
} | ||
], | ||
"name": "Paused", | ||
"type": "event" | ||
}, | ||
{ | ||
"anonymous": false, | ||
"inputs": [ | ||
{ | ||
"indexed": false, | ||
"internalType": "uint256", | ||
"name": "newRate", | ||
"type": "uint256" | ||
} | ||
], | ||
"name": "RateChanged", | ||
"type": "event" | ||
}, | ||
{ | ||
"anonymous": false, | ||
"inputs": [ | ||
{ | ||
"indexed": false, | ||
"internalType": "address", | ||
"name": "account", | ||
"type": "address" | ||
} | ||
], | ||
"name": "Unpaused", | ||
"type": "event" | ||
}, |
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.
🛠️ Refactor suggestion
Consider adding vesting-related events.
While the contract has good event coverage for most actions, it's missing events for vesting-related state changes. Consider adding:
- VestingCreated(address indexed account, uint256 amount, uint256 end)
- VestingClaimed(address indexed account, uint256 amount)
This would improve transparency and make it easier to track vesting activities off-chain.
This commit adds a new struct called Vesting to the IMergeV2 interface. The Vesting struct has two properties: amount and end. This struct will be used to track vested amounts of tokens.
Co-authored-by: [Author Name]
Summary by CodeRabbit
New Features
DynamicEater
smart contract for managing token vesting and transfers.DynamicEater
contract.UniswapV3ViaRouterFomo
for facilitating token swaps on the Uniswap V3 platform.Bug Fixes
Documentation
DynamicEater
module.Tests
DynamicEater
contract, focusing on dynamic rates and large deposit scenarios.