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

epic(contracts-lockup): test for all execute calls and lock lifecycle clarity #154

Merged
merged 3 commits into from
Sep 25, 2024

Conversation

Unique-Divine
Copy link
Member

@Unique-Divine Unique-Divine commented Sep 25, 2024

This increased test coverage a lot, both in terms of LOC and paths encountered in the logic.

  • epic(lockup): docs, tests for all execute calls, and lock lifecycle clarity
  • test(lockup): add test for edge case of withdrawal at exact maturity

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new dependency for enhanced error handling.
    • Added structured state management for locks, improving clarity and functionality.
    • Implemented a comprehensive suite of tests for contract functionalities.
  • Bug Fixes

    • Improved event handling and state checks in lock-related functions.
  • Documentation

    • Updated serialization methods for message structures to streamline code.
  • Refactor

    • Renamed event functions for better clarity and consistency.

@Unique-Divine Unique-Divine changed the title epic(lockup): dcs, test for all execute calls, and lock lifecycle clarity epic(contracts-lockup): test for all execute calls and lock lifecycle clarity Sep 25, 2024
Copy link
Contributor

coderabbitai bot commented Sep 25, 2024

Walkthrough

The pull request introduces several changes to the smart contract codebase, primarily focusing on enhancing the functionality and maintainability of the lockup contract. Key modifications include the addition of a new dependency in the Cargo.toml, updates to the logic and event handling within several functions in contract.rs, the introduction of a comprehensive test suite in contract_test.rs, and improvements in serialization using the cw_serde macro in msgs.rs. Additionally, a new Lock structure and state management system are implemented in state.rs.

Changes

File Path Change Summary
contracts/lockup/Cargo.toml Added anyhow crate as a workspace dependency.
contracts/lockup/src/contract.rs Updated logic and control flow in execute_withdraw_funds, execute_initiate_unlock, and execute_lock. Renamed event functions for clarity.
contracts/lockup/src/contract_test.rs Introduced a test suite covering lock creation, unlock initiation, and fund withdrawal functionalities.
contracts/lockup/src/events.rs Renamed event creation functions from new_ to event_ for consistency.
contracts/lockup/src/lib.rs Added a new public module contract_test for testing purposes, conditionally compiled with #[cfg(test)].
contracts/lockup/src/msgs.rs Replaced JsonSchema trait with #[cw_serde] macro for InstantiateMsg, ExecuteMsg, and QueryMsg.
contracts/lockup/src/state.rs Introduced Lock struct with new fields, a LockState enum, and a method for managing lock states.

Possibly related PRs

  • feat: Upgrade everything for CosmWasm V2 #148: This PR involves updates to dependencies and modifications in the contract.rs file, which may relate to the overall structure and functionality of contracts, similar to the changes made in the main PR regarding the Cargo.toml and contract logic.

Poem

🐇 In the meadow where contracts play,
New locks and states come out to sway.
With events renamed, and tests in line,
Our code now dances, oh so fine!
Dependencies added, all in a row,
Hopping along, watch our features grow! 🌱


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.

@Unique-Divine Unique-Divine added type: enhancement New feature or request type: test Adds missing tests or improves existing tests labels Sep 25, 2024
Copy link

codecov bot commented Sep 25, 2024

Codecov Report

Attention: Patch coverage is 99.45652% with 1 line in your changes missing coverage. Please review.

Project coverage is 88.67%. Comparing base (2ddfecf) to head (1920652).
Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
contracts/lockup/src/contract.rs 95.45% 1 Missing ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
contracts/lockup/src/contract_test.rs 100.00% <100.00%> (ø)
contracts/lockup/src/events.rs 100.00% <100.00%> (+75.00%) ⬆️
contracts/lockup/src/msgs.rs 100.00% <100.00%> (ø)
contracts/lockup/src/state.rs 100.00% <100.00%> (ø)
contracts/lockup/src/contract.rs 97.87% <95.45%> (+16.96%) ⬆️

Copy link
Contributor

@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 (7)
contracts/lockup/Cargo.toml (1)

24-24: Approved: Addition of anyhow dependency

The addition of the anyhow crate as a workspace dependency is a good choice for improving error handling capabilities in the project. This change is consistent with the existing dependency management style in the Cargo.toml file.

Consider adding a brief comment in the Cargo.toml file or in the relevant code files explaining the reason for introducing the anyhow crate. This will help other developers understand the motivation behind this addition and how it relates to the project's goals, especially in the context of enhancing testing and documentation for the lockup functionality.

contracts/lockup/src/msgs.rs (1)

Line range hint 15-36: LGTM! Consider refactoring to reduce duplication in QueryMsg.

The QueryMsg enum effectively covers various query scenarios for the lockup contract. The use of cw_serde is appropriate and consistent.

To reduce code duplication, consider introducing a common struct for shared fields:

#[cw_serde]
struct LockQueryParams {
    denom: String,
    unlocking_after: u64,
}

#[cw_serde]
enum QueryMsg {
    LocksByDenomUnlockingAfter(LockQueryParams),
    LocksByDenomAndAddressUnlockingAfter {
        params: LockQueryParams,
        address: String,
    },
    LocksByDenomBetween {
        params: LockQueryParams,
        locked_before: u64,
    },
    LocksByDenomAndAddressBetween {
        params: LockQueryParams,
        address: String,
        locked_before: u64,
    },
}

This refactoring would make the code more maintainable and reduce the risk of inconsistencies when updating query parameters.

contracts/lockup/src/events.rs (1)

5-5: Consider updating the constant name for consistency.

While the function names have been updated for consistency, the constant LOCK_FUNDS_WITHDRAWN doesn't follow the same naming pattern as the other event name constants (COINS_LOCKED_EVENT_NAME and UNLOCK_INITIATION_EVENT_NAME).

Consider renaming LOCK_FUNDS_WITHDRAWN to FUNDS_WITHDRAWN_EVENT_NAME for better consistency:

-pub const LOCK_FUNDS_WITHDRAWN: &str = "funds_withdrawn";
+pub const FUNDS_WITHDRAWN_EVENT_NAME: &str = "funds_withdrawn";

Don't forget to update the constant usage in the event_funds_withdrawn function:

-    Event::new(LOCK_FUNDS_WITHDRAWN)
+    Event::new(FUNDS_WITHDRAWN_EVENT_NAME)
contracts/lockup/src/contract_test.rs (3)

33-65: LGTM: Comprehensive test coverage for the lock functionality.

The test_execute_lock function covers successful lock creation, querying the lock, and error cases effectively. It includes both positive and negative test cases, which is crucial for robust testing.

Consider adding a test case for the maximum allowed lock duration to ensure the contract handles edge cases correctly.


101-148: LGTM: Comprehensive test coverage for the withdraw funds functionality.

The test_execute_withdraw_funds function effectively covers the entire lifecycle of a lock, from creation to withdrawal. It includes tests for premature withdrawal attempts, successful withdrawal, and repeated withdrawal attempts. The use of environment manipulation to simulate time passage is a good technique for testing time-dependent behavior.

Consider adding a test case for partial withdrawals if the contract supports such functionality. If not, it might be worth documenting that all withdrawals are full withdrawals to prevent any confusion.


1-222: Excellent test coverage with room for minor enhancement.

This test file provides comprehensive coverage for all major functionalities of the lockup contract, including lock creation, unlock initiation, fund withdrawal, and lock state transitions. The tests are well-structured, covering both happy paths and error cases, with explicit attention to important edge cases like withdrawal at exact maturity.

The use of helper functions and constants enhances the readability and maintainability of the tests. This aligns well with the PR objectives of enhancing documentation and testing related to the lockup functionality.

To further improve the test suite, consider adding the following:

  1. A test for the maximum lock duration to ensure the contract handles this limit correctly.
  2. If applicable, tests for any query functions that might exist in the contract to ensure they return correct data in various lock states.
  3. If the contract supports it, tests for scenarios involving multiple locks for the same user to ensure proper handling of multiple locks.
contracts/lockup/src/state.rs (1)

7-7: Typo in documentation comment

There's a typographical error in the comment on line 7. The word "ifi" should be "if".

Apply this diff to correct the typo:

-/// height and allows the contract to check ifi a Lock started unlocking or not.
+/// height and allows the contract to check if a Lock started unlocking or not.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8893b69 and 1920652.

🔇 Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • contracts/lockup/Cargo.toml (1 hunks)
  • contracts/lockup/src/contract.rs (4 hunks)
  • contracts/lockup/src/contract_test.rs (1 hunks)
  • contracts/lockup/src/events.rs (1 hunks)
  • contracts/lockup/src/lib.rs (1 hunks)
  • contracts/lockup/src/msgs.rs (2 hunks)
  • contracts/lockup/src/state.rs (1 hunks)
🔇 Additional comments not posted (16)
contracts/lockup/src/lib.rs (1)

7-8: LGTM! The addition of the test module aligns with the PR objectives.

The introduction of the contract_test module, conditionally compiled for tests, is a good practice. This change supports the PR's goal of enhancing testing for the lockup functionality. Making the module public allows for potential use in integration tests, which can be beneficial for comprehensive testing of the contract's behavior.

contracts/lockup/src/msgs.rs (2)

Line range hint 6-13: LGTM! Clear and concise ExecuteMsg definition.

The ExecuteMsg enum effectively captures the main actions of a lockup contract. The use of cw_serde is consistent and appropriate.


Line range hint 1-36: Overall, the changes improve code quality and consistency.

The adoption of the cw_serde macro across all message structures simplifies the code and reduces the potential for serialization-related errors. The message definitions are clear and well-structured, effectively capturing the contract's functionality.

Consider the suggested refactoring for the QueryMsg enum to further improve code maintainability. Otherwise, the changes look good and are ready for integration.

contracts/lockup/src/events.rs (4)

7-11: Improved naming convention for event creation function.

The renaming of new_coins_locked_event to event_coins_locked enhances code consistency and readability. This change aligns well with best practices for function naming in event-driven systems.


13-18: Consistent naming convention applied to unlock initiation event.

The renaming of new_unlock_initiation_event to event_unlock_initiated maintains the improved naming convention established earlier. This change contributes to overall code consistency and readability.


Line range hint 20-24: Consistent naming convention applied to funds withdrawn event.

The renaming of new_funds_withdrawn_event to event_funds_withdrawn completes the application of the improved naming convention across all event creation functions in this file. This change enhances overall code consistency and readability.


Line range hint 1-24: Overall improvement in event handling function naming.

The changes in this file demonstrate a consistent effort to improve the naming convention of event creation functions. This refactoring enhances code readability and maintainability without altering the core functionality of the events system. The new naming pattern (event_* instead of new_*_event) is more intuitive and aligns well with best practices in event-driven systems.

To further improve consistency, consider updating the LOCK_FUNDS_WITHDRAWN constant name as suggested in the previous comment.

contracts/lockup/src/contract_test.rs (4)

1-31: LGTM: Imports and setup function are well-structured.

The imports cover all necessary modules and types for testing. The setup_contract function provides a clean way to initialize the contract for each test, which is a good practice for maintaining test isolation.


67-99: LGTM: Thorough test coverage for the initiate unlock functionality.

The test_execute_initiate_unlock function effectively tests the unlock initiation process, including successful execution, lock state updates, and error handling for invalid scenarios. The inclusion of both positive and negative test cases ensures robust validation of the functionality.


150-183: LGTM: Excellent coverage of all lock states.

The test_lock_state function effectively tests all possible states of a lock: FundedPreUnlock, Unlocking, Matured, and Withdrawn. It creates different lock scenarios and verifies the resulting state, ensuring that the Lock struct behaves correctly in all situations. The approach of using a single lock instance and modifying it to test different states is efficient and clear.


185-222: LGTM: Crucial edge case coverage for withdrawal at exact maturity.

The test_withdraw_at_exact_maturity function effectively addresses a critical edge case by testing the withdrawal of funds at the precise moment of lock maturity. This test ensures that the contract correctly handles withdrawals not just after maturity, but at the exact point of maturity as well.

This is an important scenario to test as it validates that there's no off-by-one error in the maturity calculation, which could potentially lock user funds for an extra block. The test is well-structured, covering the entire process from lock creation to withdrawal, and verifies both the successful fund transfer and the updated lock state.

contracts/lockup/src/state.rs (1)

38-49: Definition of LockState enum looks good

The LockState enum is well-defined and covers all necessary lock lifecycle states.

contracts/lockup/src/contract.rs (4)

61-78: Improved lock state handling in execute_withdraw_funds

The updated match statement enhances clarity by explicitly handling each possible lock state, making the function more maintainable.


100-110: Enhanced logic in execute_initiate_unlock

The match statement in execute_initiate_unlock provides clear logic for state transitions, ensuring correct behavior based on the current lock state.


167-167: Event emission in execute_lock

The code correctly emits an event after locking coins, which is important for transparency and auditing.


6-8: ⚠️ Potential issue

Fix syntax error in use statement

The use statement on line 6 is missing the opening curly brace {, which will cause a syntax error.

Apply this diff to fix the syntax error:

-use crate::state:{
+use crate::state::{

Likely invalid or redundant comment.

Comment on lines +1 to 4
use cosmwasm_schema::cw_serde;

#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[cw_serde]
pub struct InstantiateMsg {}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

InstantiateMsg Should Not Be Empty

The InstantiateMsg struct is being instantiated with parameters in multiple locations across the codebase. This indicates that it should include the necessary fields rather than remaining empty.

  • Affected Files:
    • contracts/nusd-valuator/src/testing.rs
    • contracts/core-token-vesting-v2/src/testing.rs
    • contracts/core-cw3-fixed-msig/src/integration_tests.rs
    • contracts/broker-bank/src/tutil.rs
    • contracts/core-cw3-flex-msig/src/contract.rs

Please update the InstantiateMsg struct to include all required fields to ensure proper contract initialization.

🔗 Analysis chain

LGTM! Verify if InstantiateMsg should remain empty.

The use of cw_serde macro is a good practice in CosmWasm contracts. It simplifies serialization and reduces potential errors.

However, please confirm if InstantiateMsg should remain empty. If instantiation parameters are needed, they should be added here.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if InstantiateMsg is used with parameters elsewhere in the codebase

# Test: Search for InstantiateMsg usage
rg --type rust "InstantiateMsg\s*\{[^}]+\}" -g '!msgs.rs'

Length of output: 59


Script:

#!/bin/bash
# Description: Use ast-grep to find instances where InstantiateMsg is instantiated with parameters

ast-grep --lang rust --pattern 'InstantiateMsg { $$$ }'

Length of output: 12954

pub owner: String,
/// The duration of the lock in number of blocks.
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider removing redundant duration_blocks field

Since both start_block and end_block are stored, the duration can be calculated as end_block - start_block. Storing duration_blocks may be redundant unless there is a specific reason to keep it.

Comment on lines +51 to +64
impl Lock {
/// Computes the lifecycle state of the Lock
pub fn state(&self, current_block: u64) -> LockState {
if self.funds_withdrawn {
return LockState::Withdrawn;
}

match self.end_block {
NOT_UNLOCKING_BLOCK_IDENTIFIER => LockState::FundedPreUnlock,
end_block if current_block >= end_block => LockState::Matured,
_ => LockState::Unlocking,
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using Option<u64> for end_block instead of a sentinel value

Using Option<u64> for end_block can make the code clearer and more idiomatic by explicitly representing the absence of a value without relying on a sentinel constant.

Apply this diff to update the Lock struct and the state method:

 pub struct Lock {
     /// Unique identifier for the lock.
     pub id: u64,
     /// The amount and denomination of the locked funds.
     pub coin: Coin,
     /// The address of the lock owner.
     pub owner: String,
     /// The duration of the lock in number of blocks.
     pub duration_blocks: u64,
     /// The block height at which the lock was created.
     pub start_block: u64,
-    /// The block height at which the lock ends.
-    ///
-    /// This is set to `NOT_UNLOCKING_BLOCK_IDENTIFIER` when the lock is created,
-    /// and updated to an actual block height when unlocking is initiated.
-    pub end_block: u64,
+    /// The optional block height at which the lock ends.
+    ///
+    /// This is set to `None` when the lock is created and updated to `Some(end_block)`
+    /// when unlocking is initiated.
+    pub end_block: Option<u64>,
     /// Indicates whether the funds have been withdrawn after the lock period.
     pub funds_withdrawn: bool,
 }

 impl Lock {
     /// Computes the lifecycle state of the Lock
     pub fn state(&self, current_block: u64) -> LockState {
         if self.funds_withdrawn {
             return LockState::Withdrawn;
         }

         match self.end_block {
-            NOT_UNLOCKING_BLOCK_IDENTIFIER => LockState::FundedPreUnlock,
+            None => LockState::FundedPreUnlock,
-            end_block if current_block >= end_block => LockState::Matured,
+            Some(end_block) if current_block >= end_block => LockState::Matured,
             _ => LockState::Unlocking,
         }
     }
 }

Committable suggestion was skipped due to low confidence.

@@ -1,13 +1,14 @@
use crate::error::ContractError;
use crate::events::{
new_coins_locked_event, new_funds_withdrawn_event,
new_unlock_initiation_event,
event_coins_locked, event_funds_withdrawn, event_unlock_initiated,
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Old event function names still present in the codebase

  • Functions new_coins_locked_event, new_funds_withdrawn_event, and new_unlock_initiation_event are still defined in contracts/nibi-stargate/src/events.rs.
🔗 Analysis chain

Ensure all event function usages are updated

Since event function names have been changed to improve clarity, please verify that all usages of the old event function names have been updated throughout the codebase.

Run the following script to check for any remaining usages of the old event function names:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find any usages of the old event function names in the codebase.

# Expected result: No matches found.

rg --type rust 'new_coins_locked_event|new_funds_withdrawn_event|new_unlock_initiation_event'

Length of output: 374

@Unique-Divine Unique-Divine merged commit 90bc6e4 into main Sep 25, 2024
5 checks passed
@Unique-Divine Unique-Divine deleted the ud/lockup branch September 25, 2024 09:57
This was referenced Sep 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement New feature or request type: test Adds missing tests or improves existing tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant