Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: 1 smart contract per activity #195

Merged
merged 5 commits into from
Nov 14, 2024

Conversation

andresaiello
Copy link
Collaborator

@andresaiello andresaiello commented Oct 31, 2024

Summary

  • Refactor IR to have one contract per activity
  • Add test to check compatibility

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced the InstantRewardsFactory contract for easier creation of instant rewards contracts.
    • Launched InstantRewardsV2 with enhanced reward management, including customizable reward timeframes.
  • Improvements

    • Updated function visibility for claim and withdraw in InstantRewards to allow more flexible usage.
    • Added robust error handling and timeframe management in InstantRewardsV2.
  • Tests

    • Implemented comprehensive test suites for both InstantRewardsV2 and InstantRewardsFactory to ensure functionality and security.
  • Address Updates

    • Updated contract addresses for InstantRewards and other entities in the zeta_testnet and zeta_mainnet configurations.

@andresaiello andresaiello requested a review from GMaiolo as a code owner October 31, 2024 15:18
Copy link

coderabbitai bot commented Oct 31, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The pull request introduces significant changes to the InstantRewards smart contract, modifying the visibility of the claim and withdraw functions from external to public virtual. Additionally, it adds two new contracts: InstantRewardsFactory, which facilitates the creation of InstantRewardsV2 contracts, and InstantRewardsV2, which enhances the existing reward system with timeframe management and error handling. A comprehensive test suite for InstantRewardsV2 is also included, ensuring robust validation of its functionalities.

Changes

File Change Summary
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol - Changed visibility of claim and withdraw functions from external to public virtual.
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol - Added InstantRewardsFactory contract with InstantRewardsCreated event and createInstantRewards function.
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol - Introduced InstantRewardsV2 contract with new state variables, error handling, and overridden functions.
packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2-compatibility.ts - Added test suite for InstantRewardsV2 covering claiming, ownership transfer, and withdrawal processes.

Possibly related PRs

Suggested reviewers

  • GMaiolo

Warning

Tool Failures:

Tool Failure Count:


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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (8)
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol (2)

55-55: Consider defining an interface or abstract contract for consistent inheritance.

The change from external to public virtual for claim() and withdraw() suggests these functions are meant to be overridden. To ensure consistent implementation across derived contracts:

  1. Consider extracting an interface or abstract contract
  2. Evaluate if other functions should also be virtual

Example interface structure:

interface IInstantRewards {
    function claim(ClaimData memory claimData) external;
    function withdraw(address wallet, uint256 amount) external;
}

Also applies to: 75-75

🧰 Tools
🪛 GitHub Check: Slither

[warning] 55-67: Low-level calls
Low level call in InstantRewards.claim(InstantRewards.ClaimData) (contracts/instant-rewards/InstantRewards.sol#55-67):
- (success,None) = claimData.to.call{value: claimData.amount}() (contracts/instant-rewards/InstantRewards.sol#63)


Line range hint 55-67: Add validation for zero-amount claims.

While the function is well-protected against reentrancy and follows the checks-effects-interactions pattern, consider adding a zero-amount validation to prevent unnecessary transactions.

 function claim(ClaimData memory claimData) public virtual nonReentrant whenNotPaused {
     claimData.to = msg.sender;
+    if (claimData.amount == 0) revert InvalidAmount();
     _verify(claimData);
packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2-compatibility.ts (4)

8-8: Consider importing and using the ClaimData type definition.

For better type safety, consider importing the ClaimData interface from the contract types instead of from test helpers.


19-19: Add JSDoc documentation for helper functions.

The helper functions encodeTaskId and getClaimDataSigned would benefit from JSDoc documentation explaining their purpose, parameters, and return values.

/**
 * Encodes a task ID string into a bytes32 hash
 * @param taskId - The task ID to encode
 * @returns The keccak256 hash of the encoded task ID
 */
const encodeTaskId = (taskId: string) => utils.keccak256(utils.defaultAbiCoder.encode(["string"], [taskId]));

/**
 * Creates and signs claim data for testing
 * @param chainId - The chain ID for the signature
 * @param verifyingContract - The contract address for the signature
 * @param signer - The signer's address
 * @param amount - The reward amount
 * @param sigExpiration - The signature expiration timestamp
 * @param taskId - The task ID
 * @param to - The recipient address
 * @returns Signed claim data with signature
 */
const getClaimDataSigned = async (...) => {

Also applies to: 21-42


56-81: Enhance test description and add gas assertions.

The test name "Should claim" could be more descriptive. Consider adding gas usage assertions to monitor the efficiency of the claim operation.

-  it("Should claim", async () => {
+  it("should successfully claim rewards with valid signature", async () => {
     // ... existing code ...
     
     const tx = instantRewards.claim(claimDataSigned);
+    const receipt = await (await tx).wait();
+    expect(receipt.gasUsed).to.be.lte(200000); // Adjust threshold as needed
     await expect(tx).to.emit(instantRewards, "Claimed").withArgs(owner.address, taskId, amount);
   });

302-323: Add more edge cases for withdrawal testing.

Consider adding tests for:

  1. Withdrawing exact contract balance
  2. Attempting to withdraw more than available balance
  3. Withdrawing to zero address
it("should revert when withdrawing more than available balance", async () => {
  await ethers.provider.send("evm_increaseTime", [7200]);
  await ethers.provider.send("evm_mine", []);
  
  const amount = utils.parseEther("1");
  await fundContract(amount);
  
  const tx = instantRewards.withdraw(user.address, amount.add(1));
  await expect(tx).to.be.revertedWith("InsufficientBalance");
});

it("should revert when withdrawing to zero address", async () => {
  await ethers.provider.send("evm_increaseTime", [7200]);
  await ethers.provider.send("evm_mine", []);
  
  const tx = instantRewards.withdraw(ethers.constants.AddressZero, utils.parseEther("1"));
  await expect(tx).to.be.revertedWith("InvalidAddress");
});
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol (2)

32-34: Usage of block.timestamp in Time Comparisons

The use of block.timestamp in the isActive() and setTimeframe() functions is acceptable for time-based logic. However, be aware that miners can manipulate the timestamp slightly. Ensure that this potential manipulation does not negatively impact the contract's behavior or security.

Also applies to: 36-43

🧰 Tools
🪛 GitHub Check: Slither

[notice] 32-34: Block timestamp
InstantRewardsV2.isActive() (contracts/instant-rewards/InstantRewardsV2.sol#32-34) uses timestamp for comparisons
Dangerous comparisons:
- block.timestamp >= start && block.timestamp <= end (contracts/instant-rewards/InstantRewardsV2.sol#33)


6-54: Enhance Code Documentation with NatSpec Comments

Consider adding NatSpec comments to the contract, functions, events, and custom errors to improve code readability and provide clear documentation for users and developers.

🧰 Tools
🪛 GitHub Check: Slither

[notice] 32-34: Block timestamp
InstantRewardsV2.isActive() (contracts/instant-rewards/InstantRewardsV2.sol#32-34) uses timestamp for comparisons
Dangerous comparisons:
- block.timestamp >= start && block.timestamp <= end (contracts/instant-rewards/InstantRewardsV2.sol#33)


[notice] 36-43: Block timestamp
InstantRewardsV2.setTimeframe(uint256,uint256) (contracts/instant-rewards/InstantRewardsV2.sol#36-43) uses timestamp for comparisons
Dangerous comparisons:
- start_ < block.timestamp || end_ < block.timestamp (contracts/instant-rewards/InstantRewardsV2.sol#38)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a857666 and e076ca4.

📒 Files selected for processing (4)
  • packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol (2 hunks)
  • packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol (1 hunks)
  • packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol (1 hunks)
  • packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2-compatibility.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Slither
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol

[warning] 55-67: Low-level calls
Low level call in InstantRewards.claim(InstantRewards.ClaimData) (contracts/instant-rewards/InstantRewards.sol#55-67):
- (success,None) = claimData.to.call{value: claimData.amount}() (contracts/instant-rewards/InstantRewards.sol#63)


[notice] 75-82: Reentrancy vulnerabilities
Reentrancy in InstantRewards.withdraw(address,uint256) (contracts/instant-rewards/InstantRewards.sol#75-82):
External calls:
- (success,None) = wallet.call{value: amount}() (contracts/instant-rewards/InstantRewards.sol#78)
Event emitted after the call(s):
- Withdrawn(wallet,amount) (contracts/instant-rewards/InstantRewards.sol#81)


[warning] 75-82: Low-level calls
Low level call in InstantRewards.withdraw(address,uint256) (contracts/instant-rewards/InstantRewards.sol#75-82):
- (success,None) = wallet.call{value: amount}() (contracts/instant-rewards/InstantRewards.sol#78)

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol

[notice] 10-20: Reentrancy vulnerabilities
Reentrancy in InstantRewardsFactory.createInstantRewards(address,uint256,uint256,string) (contracts/instant-rewards/InstantRewardsFactory.sol#10-20):
External calls:
- instantRewards.transferOwnership(owner()) (contracts/instant-rewards/InstantRewardsFactory.sol#17)
Event emitted after the call(s):
- InstantRewardsCreated(address(instantRewards),owner()) (contracts/instant-rewards/InstantRewardsFactory.sol#18)

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol

[notice] 32-34: Block timestamp
InstantRewardsV2.isActive() (contracts/instant-rewards/InstantRewardsV2.sol#32-34) uses timestamp for comparisons
Dangerous comparisons:
- block.timestamp >= start && block.timestamp <= end (contracts/instant-rewards/InstantRewardsV2.sol#33)


[notice] 36-43: Block timestamp
InstantRewardsV2.setTimeframe(uint256,uint256) (contracts/instant-rewards/InstantRewardsV2.sol#36-43) uses timestamp for comparisons
Dangerous comparisons:
- start_ < block.timestamp || end_ < block.timestamp (contracts/instant-rewards/InstantRewardsV2.sol#38)

🔇 Additional comments (7)
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol (3)

1-7: LGTM: Solid foundation with secure contract setup.

The contract uses a recent Solidity version and properly inherits from OpenZeppelin's Ownable2Step, which provides secure two-step ownership transfer.


8-8: LGTM: Well-structured event declaration.

The event properly indexes both the contract address and owner for efficient filtering in event logs.


16-19: Regarding Slither's reentrancy warning: This is a false positive.

The code follows best practices:

  1. The transferOwnership call is to a trusted OpenZeppelin implementation
  2. The InstantRewardsV2 contract is deployed by us
  3. The event emission after state changes is correct

No changes needed.

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol (1)

Line range hint 75-82: LGTM: Reentrancy warning is a false positive.

The function is secure because:

  1. It's protected by onlyOwner
  2. No state changes occur after the ETH transfer
  3. Proper balance checks are in place

The event emission after the transfer is safe in this context.

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol (3)

18-30: Constructor Validations and Initializations are Appropriate

The constructor correctly initializes state variables and includes necessary validations to prevent invalid configurations, ensuring the contract is set up securely.


45-48: Proper Override of claim Function with Active Period Enforcement

The overridden claim function correctly enforces that claims can only be made during the active reward period, enhancing security and contract integrity.


50-53: Ensuring Withdrawals Only When Rewards are Inactive

The withdraw function appropriately prevents withdrawals while the reward is active, maintaining the intended reward distribution logic.

@andresaiello andresaiello force-pushed the andy/pd-6808-1-smart-contract-per-ir-activity branch from e076ca4 to 5715168 Compare November 6, 2024 16:19
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (4)
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol (2)

Line range hint 75-82: Add reentrancy protection to withdraw function.

The withdraw function requires additional security measures:

  1. Missing nonReentrant modifier
  2. Event emission after external call creates reentrancy vulnerability

Apply this diff to fix the issues:

-    function withdraw(address wallet, uint256 amount) public virtual onlyOwner {
+    function withdraw(address wallet, uint256 amount) public virtual nonReentrant onlyOwner {
         if (wallet == address(0)) revert InvalidAddress();
         if (amount > address(this).balance) revert TransferFailed();
+        emit Withdrawn(wallet, amount);
         (bool success, ) = wallet.call{value: amount}("");
         if (!success) revert TransferFailed();
-
-        emit Withdrawn(wallet, amount);
     }

The changes:

  1. Add nonReentrant modifier to prevent reentrancy
  2. Move event emission before external call to follow checks-effects-interactions pattern

Line range hint 55-82: Consider using OpenZeppelin's Address library for ETH transfers.

Both claim and withdraw functions use low-level calls for ETH transfers. Consider using OpenZeppelin's Address.sendValue() for a more secure and standardized implementation.

Import and use the Address library:

 import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
 import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
+import "@openzeppelin/contracts/utils/Address.sol";
 import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

 contract InstantRewards is Ownable2Step, Pausable, ReentrancyGuard, EIP712 {
+    using Address for address payable;

Then replace the low-level calls:

-        (bool success, ) = claimData.to.call{value: claimData.amount}("");
-        if (!success) revert TransferFailed();
+        payable(claimData.to).sendValue(claimData.amount);

-        (bool success, ) = wallet.call{value: amount}("");
-        if (!success) revert TransferFailed();
+        payable(wallet).sendValue(amount);

Benefits:

  • Built-in revert on failure (no need for success check)
  • Gas-forwarding protection
  • Industry-standard implementation
packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2-compatibility.ts (1)

289-300: Enhance ownership transfer test to validate permissions

Consider extending the ownership transfer test to verify that the new owner can execute owner-restricted functions, such as pausing the contract, and that the previous owner no longer has these permissions. This will ensure that ownership transitions are correctly enforced.

For example, after the ownership transfer:

// Verify new owner can pause the contract
await expect(instantRewards.connect(user).pause()).to.not.be.reverted;

// Verify previous owner cannot pause the contract
await expect(instantRewards.connect(owner).pause()).to.be.revertedWith("Ownable: caller is not the owner");
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol (1)

7-7: Declare name as immutable to Optimize Gas Usage

Since name is assigned once during contract deployment and does not change thereafter, you can declare it as immutable to save gas costs and signal its immutability.

Apply this diff to implement the change:

-string public name;
+string public immutable name;

Also applies to: 29-29

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e076ca4 and 5715168.

📒 Files selected for processing (4)
  • packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol (2 hunks)
  • packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol (1 hunks)
  • packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol (1 hunks)
  • packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2-compatibility.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Slither
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol

[warning] 55-67: Low-level calls
Low level call in InstantRewards.claim(InstantRewards.ClaimData) (contracts/instant-rewards/InstantRewards.sol#55-67):
- (success,None) = claimData.to.call{value: claimData.amount}() (contracts/instant-rewards/InstantRewards.sol#63)


[notice] 75-82: Reentrancy vulnerabilities
Reentrancy in InstantRewards.withdraw(address,uint256) (contracts/instant-rewards/InstantRewards.sol#75-82):
External calls:
- (success,None) = wallet.call{value: amount}() (contracts/instant-rewards/InstantRewards.sol#78)
Event emitted after the call(s):
- Withdrawn(wallet,amount) (contracts/instant-rewards/InstantRewards.sol#81)


[warning] 75-82: Low-level calls
Low level call in InstantRewards.withdraw(address,uint256) (contracts/instant-rewards/InstantRewards.sol#75-82):
- (success,None) = wallet.call{value: amount}() (contracts/instant-rewards/InstantRewards.sol#78)

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol

[notice] 10-20: Reentrancy vulnerabilities
Reentrancy in InstantRewardsFactory.createInstantRewards(address,uint256,uint256,string) (contracts/instant-rewards/InstantRewardsFactory.sol#10-20):
External calls:
- instantRewards.transferOwnership(owner()) (contracts/instant-rewards/InstantRewardsFactory.sol#17)
Event emitted after the call(s):
- InstantRewardsCreated(address(instantRewards),owner()) (contracts/instant-rewards/InstantRewardsFactory.sol#18)

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol

[notice] 32-34: Block timestamp
InstantRewardsV2.isActive() (contracts/instant-rewards/InstantRewardsV2.sol#32-34) uses timestamp for comparisons
Dangerous comparisons:
- block.timestamp >= start && block.timestamp <= end (contracts/instant-rewards/InstantRewardsV2.sol#33)


[notice] 36-43: Block timestamp
InstantRewardsV2.setTimeframe(uint256,uint256) (contracts/instant-rewards/InstantRewardsV2.sol#36-43) uses timestamp for comparisons
Dangerous comparisons:
- start_ < block.timestamp || end_ < block.timestamp (contracts/instant-rewards/InstantRewardsV2.sol#38)

🔇 Additional comments (3)
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol (2)

1-7: LGTM! Appropriate contract structure and security measures.

The contract correctly implements Ownable2Step for secure ownership management.


8-9: LGTM! Well-structured event declaration.

The event properly indexes both parameters for efficient filtering in event logs.

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewards.sol (1)

Line range hint 55-67: LGTM: Function visibility change is well-protected.

The change from external to public virtual is appropriate for enabling inheritance while maintaining security:

  • NonReentrant guard prevents reentrancy
  • Follows checks-effects-interactions pattern
  • Proper event emission after state changes

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (10)
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol (3)

8-16: Add event for allowPublicCreation changes.

For better transparency and off-chain tracking, emit an event when the public creation setting changes.

 contract InstantRewardsFactory is Ownable2Step {
     bool public allowPublicCreation = false;
 
     error AccessDenied();
     error InvalidSignerAddress();
     error EmptyName();
     error StartTimeInPast();
     error EndTimeBeforeStart();
 
+    event PublicCreationUpdated(bool indexed allowed);
     event InstantRewardsCreated(address indexed instantRewards, address indexed owner);

22-24: Emit event for allowPublicCreation changes.

Emit the suggested PublicCreationUpdated event when the setting changes.

     function setAllowPublicCreation(bool allowPublicCreation_) external onlyOwner {
         allowPublicCreation = allowPublicCreation_;
+        emit PublicCreationUpdated(allowPublicCreation_);
     }

26-31: Add NatSpec documentation.

Add comprehensive NatSpec documentation to improve code maintainability and developer experience.

+    /// @notice Creates a new InstantRewardsV2 contract
+    /// @param signerAddress The address authorized to sign reward claims
+    /// @param start The timestamp when rewards can start being claimed
+    /// @param end The timestamp when rewards can no longer be claimed
+    /// @param name The name of the rewards contract
+    /// @return The address of the newly created InstantRewardsV2 contract
     function createInstantRewards(
         address signerAddress,
         uint256 start,
         uint256 end,
         string memory name
     ) external returns (address) {
🧰 Tools
🪛 GitHub Check: Slither

[notice] 26-44: Block timestamp
InstantRewardsFactory.createInstantRewards(address,uint256,uint256,string) (contracts/instant-rewards/InstantRewardsFactory.sol#26-44) uses timestamp for comparisons
Dangerous comparisons:
- start < block.timestamp (contracts/instant-rewards/InstantRewardsFactory.sol#34)

packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2.ts (2)

15-21: Add assertions for critical setup steps.

While the setup is functional, it's missing validations for critical steps that could fail silently.

Consider adding these assertions:

 beforeEach(async () => {
   [deployer, owner, signer, user, ...addrs] = await ethers.getSigners();
   const instantRewardsFactoryF = await ethers.getContractFactory("InstantRewardsFactory");
   instantRewardsFactory = (await instantRewardsFactoryF.deploy(owner.address)) as InstantRewardsFactory;
   await instantRewardsFactory.deployed();
+  expect(await instantRewardsFactory.pendingOwner()).to.equal(owner.address);
   await instantRewardsFactory.connect(owner).acceptOwnership();
+  expect(await instantRewardsFactory.owner()).to.equal(owner.address);
 });

45-52: Enhance access control test coverage.

The test case could be more explicit and cover additional edge cases.

Consider these improvements:

-it("Should revert if not owner try to deploy", async () => {
+it("Should revert if non-owner tries to deploy", async () => {
   const currentBlock = await ethers.provider.getBlock("latest");
   const start = currentBlock.timestamp + 1000;
   const end = start + 1000;
   const name = "Instant Rewards";
-  const tx = instantRewardsFactory.createInstantRewards(signer.address, start, end, name);
+  const tx = instantRewardsFactory.connect(user).createInstantRewards(signer.address, start, end, name);
   await expect(tx).to.revertedWith("AccessDenied");
+
+  // Test with zero address signer
+  await expect(
+    instantRewardsFactory.connect(owner).createInstantRewards(ethers.constants.AddressZero, start, end, name)
+  ).to.be.revertedWith("InvalidSignerAddress");
 });
packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2-compatibility.ts (2)

21-42: Consider enhancing error handling in getClaimDataSigned helper.

The helper function should validate input parameters before processing to prevent potential issues.

 const getClaimDataSigned = async (
   chainId: number,
   verifyingContract: string,
   signer: SignerWithAddress,
   amount: BigNumber,
   sigExpiration: number,
   taskId: string,
   to: string
 ) => {
+  if (amount.lte(0)) throw new Error("Amount must be greater than 0");
+  if (sigExpiration <= 0) throw new Error("Signature expiration must be greater than 0");
+  if (!taskId || !to) throw new Error("TaskId and to address are required");
   const claimData: ClaimData = {
     amount,
     sigExpiration,
     taskId,
     to,
   };
   const signature = await getSignature(chainId, verifyingContract, signer, claimData);
   return {
     ...claimData,
     signature,
   };
 };

56-243: Consider organizing tests using nested describe blocks.

The test suite would benefit from better organization using nested describe blocks to group related test cases (e.g., "claim functionality", "error cases", etc.).

Example structure:

describe("InstantRewardsV2", () => {
  describe("claim functionality", () => {
    it("Should claim successfully", async () => {
      // ... existing test
    });
    
    it("Should claim after pause and unpause", async () => {
      // ... existing test
    });
  });

  describe("error cases", () => {
    it("Should revert on invalid claims", async () => {
      // ... existing test
    });
    // ... other error cases
  });
});
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol (3)

25-25: Redundant Signer Address Validation

The check if (signerAddress_ == address(0)) revert InvalidAddress(); duplicates validation that may already exist in the parent InstantRewards contract's constructor. Review the parent contract to determine if this validation is necessary here. Removing redundant checks can simplify the code and reduce gas costs.


36-43: Potential Issue with Timeframe Updates During Inactive Periods

The setTimeframe function prevents updating the timeframe if the reward is currently active by reverting with InstantRewardStillActive. This could limit administrative flexibility. Consider allowing updates to the end time to extend the active period or providing a mechanism to deactivate the reward early if necessary.

Apply this diff to allow adjustments to the end time during the active period:

     function setTimeframe(uint256 start_, uint256 end_) external onlyOwner {
         if (start_ > end_) revert InvalidTimeframe();
-        if (start_ < block.timestamp || end_ < block.timestamp) revert InvalidTimeframe();
-        if (isActive()) revert InstantRewardStillActive();
+        if (start_ < block.timestamp) revert InvalidTimeframe();
         start = start_;
         end = end_;
         emit TimeframeUpdated(start_, end_);
     }

45-48: Ensure Proper Handling of Inactive Claims

In the claim function, when the reward is inactive, the transaction reverts with InstantRewardNotActive. To enhance user experience, consider providing a more informative message or implementing an event that notifies users when claims are attempted outside the active period.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5715168 and 98dd76c.

📒 Files selected for processing (4)
  • packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol (1 hunks)
  • packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol (1 hunks)
  • packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2-compatibility.ts (1 hunks)
  • packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2.ts (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Slither
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol

[notice] 26-44: Block timestamp
InstantRewardsFactory.createInstantRewards(address,uint256,uint256,string) (contracts/instant-rewards/InstantRewardsFactory.sol#26-44) uses timestamp for comparisons
Dangerous comparisons:
- start < block.timestamp (contracts/instant-rewards/InstantRewardsFactory.sol#34)

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol

[notice] 32-34: Block timestamp
InstantRewardsV2.isActive() (contracts/instant-rewards/InstantRewardsV2.sol#32-34) uses timestamp for comparisons
Dangerous comparisons:
- block.timestamp >= start && block.timestamp <= end (contracts/instant-rewards/InstantRewardsV2.sol#33)

🔇 Additional comments (6)
packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol (2)

1-7: LGTM! Solid foundation with secure ownership management.

The contract uses the latest Solidity version and implements secure ownership transfer through Ownable2Step.


26-44: Implementation approved with a note on reentrancy.

The function implements comprehensive input validation and proper access control. While there's a theoretical reentrancy possibility due to the event emission after transferOwnership, the risk is minimal as:

  1. Only the owner's address is used
  2. No state changes occur after the external call
  3. Event emission after external calls is safe in this context
✅ Verification successful

Implementation is secure with proper reentrancy protection

The verification reveals that the implementation is more secure than initially assessed:

  1. The base InstantRewards contract inherits from ReentrancyGuard, providing protection against reentrancy attacks
  2. Ownable2Step is properly implemented in both factory and reward contracts, ensuring secure ownership transfers
  3. The event emission after transferOwnership is safe because:
    • Ownable2Step requires a two-step ownership transfer
    • The base contract includes ReentrancyGuard
    • The factory maintains strict access control
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify if the contract is used in other parts of the codebase
rg "new InstantRewardsFactory" -A 2

Length of output: 37


Script:

#!/bin/bash
# Let's verify the inheritance and usage patterns more comprehensively

# Check for InstantRewardsV2 contract references
rg "InstantRewardsV2" -A 2

# Check for InstantRewardsFactory contract references
rg "InstantRewardsFactory" -A 2

# Look for any potential reentrancy patterns in InstantRewardsV2
ast-grep --pattern 'function transferOwnership($$$) { $$$ }'

Length of output: 5255


Script:

#!/bin/bash
# Let's check the InstantRewards base contract and Ownable2Step implementation

# Check InstantRewards base contract
rg "contract InstantRewards" -A 5

# Check if there are any other state changes in InstantRewardsV2
ast-grep --pattern 'contract InstantRewardsV2 {
  $$$
}'

# Look for the Ownable2Step implementation
rg "Ownable2Step" -A 5

Length of output: 5609

🧰 Tools
🪛 GitHub Check: Slither

[notice] 26-44: Block timestamp
InstantRewardsFactory.createInstantRewards(address,uint256,uint256,string) (contracts/instant-rewards/InstantRewardsFactory.sol#26-44) uses timestamp for comparisons
Dangerous comparisons:
- start < block.timestamp (contracts/instant-rewards/InstantRewardsFactory.sol#34)

packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2.ts (1)

1-13: LGTM! Clean imports and type-safe setup.

The import statements and type declarations are well-structured, utilizing TypeScript's type system effectively through typechain-generated types.

packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol (3)

32-34: Usage of Block Timestamp for Time Comparison

The isActive function relies on block.timestamp for time comparisons. While this is common practice, it's important to be aware that miners can manipulate the timestamp within a permissible range. This could potentially affect the contract's behavior in edge cases. Consider using a more reliable time oracle if absolute precision is required.

🧰 Tools
🪛 GitHub Check: Slither

[notice] 32-34: Block timestamp
InstantRewardsV2.isActive() (contracts/instant-rewards/InstantRewardsV2.sol#32-34) uses timestamp for comparisons
Dangerous comparisons:
- block.timestamp >= start && block.timestamp <= end (contracts/instant-rewards/InstantRewardsV2.sol#33)


50-53: Finalize Reward Period Before Withdrawal

The withdraw function correctly restricts withdrawals while the reward is active by checking if (isActive()) revert InstantRewardStillActive();. This ensures that funds are not prematurely removed from the contract, maintaining the integrity of the reward distribution process.


14-16: Consistent Custom Error Definitions

The custom errors InvalidTimeframe, InstantRewardNotActive, and InstantRewardStillActive enhance clarity and gas efficiency. Ensure that all potential revert reasons throughout the contract use custom errors for consistency and optimal gas usage.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
packages/zevm-app-contracts/scripts/instant-rewards/deploy-v2.ts (1)

29-37: Improve error handling and remove duplicate validation.

The current implementation has duplicate network validation and basic error handling.

 const main = async () => {
-  if (!isProtocolNetworkName(networkName)) throw new Error("Invalid network name");
   await deployInstantRewards();
 };
 
 main().catch((error) => {
-  console.error(error);
+  console.error(`Deployment failed at ${new Date().toISOString()}:`);
+  console.error(`Network: ${networkName}`);
+  console.error(`Error: ${error.message}`);
+  if (error.stack) console.error(`Stack: ${error.stack}`);
   process.exit(1);
 });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 98dd76c and b9381a9.

📒 Files selected for processing (2)
  • packages/zevm-app-contracts/data/addresses.json (1 hunks)
  • packages/zevm-app-contracts/scripts/instant-rewards/deploy-v2.ts (1 hunks)
🔇 Additional comments (3)
packages/zevm-app-contracts/scripts/instant-rewards/deploy-v2.ts (1)

1-8: LGTM! Imports and setup are well-structured.

The necessary dependencies are properly imported, including protocol-specific utilities and type definitions.

packages/zevm-app-contracts/data/addresses.json (2)

Line range hint 27-29: Review mainnet address synchronization.

The mainnet configuration shows updates to ZetaXP and ProofOfLiveness addresses. Given the PR's objective of refactoring InstantRewards, please verify if the mainnet InstantRewards contract should also be updated to reflect the new implementation.

Let's check for any pending mainnet deployments:

#!/bin/bash
# Description: Check for pending mainnet deployments
# Expected: Should find deployment preparations or discussions

# Search for mainnet deployment references
echo "Searching for mainnet deployment references..."
rg -l "mainnet.*deployment" --type md

11-11: Verify the new InstantRewards testnet deployment.

The InstantRewards contract address has been updated in the testnet configuration. Given the critical nature of contract address changes, it's essential to verify this deployment.

Let's verify the contract deployment and implementation:

✅ Verification successful

InstantRewards contract address update appears valid and well-implemented

The address change in testnet reflects a proper upgrade to InstantRewardsV2, which adds important new features:

  • Time-bound rewards with start/end timestamps
  • Named reward programs
  • Enhanced security with active period restrictions
  • Factory pattern implementation for controlled deployments

The deployment process is handled through a well-structured factory contract with proper access controls and validation checks. The implementation shows this is a planned upgrade rather than a simple redeployment.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the new InstantRewards contract deployment on testnet
# Expected: Should find deployment transaction and contract verification status

# Search for deployment transaction
echo "Searching for contract deployment transaction..."
gh api \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  "/repos/zeta-chain/app-contracts/commits" \
  --jq '.[] | select(.commit.message | contains("0xd91164c9671C5A2ee1C95fa34A95C9141dA691D4"))'

Length of output: 432


Script:

#!/bin/bash
# Let's verify the contract address changes by:
# 1. Looking for deployment references in code/docs
# 2. Checking for contract verification details
# 3. Examining related configuration changes

# Search for any references to this contract address
echo "Searching for contract address references..."
rg -i "0xd91164c9671C5A2ee1C95fa34A95C9141dA691D4"

# Look for deployment scripts or configurations
echo -e "\nSearching for deployment configurations..."
fd -e ts -e js -e json . | xargs rg -l "InstantRewards"

# Check for contract verification details
echo -e "\nSearching for verification details..."
fd -e md . | xargs rg -l "InstantRewards.*testnet"

Length of output: 1032


Script:

#!/bin/bash
# Let's examine the deployment scripts and contract implementation

# Check the deployment script content
echo "Examining deployment scripts..."
cat packages/zevm-app-contracts/scripts/instant-rewards/deploy-v2.ts

# Look for contract implementation changes
echo -e "\nChecking contract implementation..."
fd -e sol . | xargs rg -l "InstantRewards"

# Check test files for contract changes
echo -e "\nExamining test files for contract changes..."
cat packages/zevm-app-contracts/test/instant-rewards/instant-rewards-v2.ts

Length of output: 4758


Script:

#!/bin/bash
# Let's examine the contract implementation to understand the changes

# Check the V2 contract implementation
echo "Examining InstantRewardsV2 contract..."
cat packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsV2.sol

# Check the factory contract
echo -e "\nExamining InstantRewardsFactory contract..."
cat packages/zevm-app-contracts/contracts/instant-rewards/InstantRewardsFactory.sol

# Look for any migration scripts
echo -e "\nSearching for migration scripts..."
fd -e ts -e js . | xargs rg -l "migrate.*InstantRewards|upgrade.*InstantRewards"

Length of output: 3656

@andresaiello andresaiello merged commit 0f7b077 into main Nov 14, 2024
11 of 12 checks passed
@andresaiello andresaiello deleted the andy/pd-6808-1-smart-contract-per-ir-activity branch November 14, 2024 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants