-
Notifications
You must be signed in to change notification settings - Fork 11
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: initial airdrop contract #110
Conversation
Warning Rate Limit Exceeded@k-yang has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 9 seconds before requesting another review. How to resolve this issue?After the wait time has elapsed, a review can be triggered using the @coderabbitai review command. Alternatively, push new commits to this PR. We recommend that you space out your commits to avoid hitting the rate limit. How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. WalkthroughThe project has been updated to include an airdrop smart contract for blockchain campaigns, with features such as campaign instantiation, migration, reward distribution, claim management, fund withdrawal, and querying functionalities. Test suites for these operations have also been added to ensure proper functionality, alongside updates to the Changes
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? TipsChat with CodeRabbit Bot (
|
Codecov ReportAttention:
Additional details and impacted files
|
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 Status
Actionable comments generated: 5
Configuration used: CodeRabbit UI
Files ignored due to filter (3)
- Cargo.lock
- Cargo.toml
- contracts/airdrop/Cargo.toml
Files selected for processing (15)
- .gitignore (1 hunks)
- contracts/airdrop/.cargo/config (1 hunks)
- contracts/airdrop/src/contract.rs (1 hunks)
- contracts/airdrop/src/lib.rs (1 hunks)
- contracts/airdrop/src/msg.rs (1 hunks)
- contracts/airdrop/src/state.rs (1 hunks)
- contracts/airdrop/src/tests/execute/claim.rs (1 hunks)
- contracts/airdrop/src/tests/execute/mod.rs (1 hunks)
- contracts/airdrop/src/tests/execute/reward_all.rs (1 hunks)
- contracts/airdrop/src/tests/execute/withdraw.rs (1 hunks)
- contracts/airdrop/src/tests/instantiate.rs (1 hunks)
- contracts/airdrop/src/tests/mod.rs (1 hunks)
- contracts/airdrop/src/tests/query/campaign.rs (1 hunks)
- contracts/airdrop/src/tests/query/mod.rs (1 hunks)
- contracts/airdrop/src/tests/query/user_pool.rs (1 hunks)
Files skipped from review due to trivial changes (2)
- .gitignore
- contracts/airdrop/src/tests/query/mod.rs
Additional comments: 17
contracts/airdrop/src/tests/execute/mod.rs (1)
- 1-3: The module declarations for
claim
,reward_all
, andwithdraw
are correctly organized.contracts/airdrop/src/tests/mod.rs (1)
- 1-3: The module declarations for
instantiate
,execute
, andquery
are correctly organized.contracts/airdrop/src/lib.rs (1)
- 1-6: The public module declarations and conditional compilation for the test module are correctly implemented.
contracts/airdrop/.cargo/config (1)
- 1-4: The aliases for building WebAssembly targets and running schema generation are well-defined and follow common Rust practices.
contracts/airdrop/src/state.rs (1)
- 1-17: The
Campaign
struct and storage constants are correctly defined and follow CosmWasm conventions for state management.contracts/airdrop/src/msg.rs (1)
- 1-41: The message structures and enums for contract interactions are correctly defined and follow CosmWasm conventions.
contracts/airdrop/src/tests/query/campaign.rs (1)
- 1-46: The test function
test_query_campaign
is well-structured and follows best practices for testing in CosmWasm contracts.contracts/airdrop/src/tests/query/user_pool.rs (1)
- 1-62: The test functions
test_query_user_pool
andtest_query_user_pool_empty
are well-structured and follow best practices for testing in CosmWasm contracts.contracts/airdrop/src/tests/instantiate.rs (1)
- 1-62: The test functions for instantiation are well-structured and follow best practices for testing in CosmWasm contracts.
contracts/airdrop/src/tests/execute/claim.rs (1)
- 1-78: The test function
test_claim
is well-structured and follows best practices for testing in CosmWasm contracts.contracts/airdrop/src/tests/execute/reward_all.rs (1)
- 1-87: The test function
test_reward_users_fully_allocated
is well-structured and follows best practices for testing in CosmWasm contracts.contracts/airdrop/src/tests/execute/withdraw.rs (1)
- 1-103: The test functions for withdrawal are well-structured and follow best practices for testing in CosmWasm contracts.
contracts/airdrop/src/contract.rs (5)
1-7: Imports have been organized and appear to be correctly scoped. Ensure that all imported modules are used within the file to avoid unnecessary dependencies.
17-18: Ensure that the environment variables
CARGO_PKG_NAME
andCARGO_PKG_VERSION
are properly set during the build process to reflect the correct contract name and version.Verification successful
The environment variables
CARGO_PKG_NAME
andCARGO_PKG_VERSION
are standard variables that Cargo, the Rust package manager, automatically sets during the build process. They do not need to be explicitly set in a configuration file. The usage of these variables incontract.rs
is correct and will reflect the package name and version as specified in the project'sCargo.toml
file at build time.* 20-51: The instantiation logic checks for a single coin and native tokens, which is good practice for contract initialization. However, ensure that the `query_bonded_denom` function is reliable and that the error messages are clear and informative.Scripts Executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify that the environment variables are set in the build process grep -E "CARGO_PKG_NAME|CARGO_PKG_VERSION" .cargo/configLength of output: 105
Script:
#!/bin/bash # Search for CI/CD configuration files or build scripts that might set the environment variables rg "CARGO_PKG_NAME|CARGO_PKG_VERSION" --files-with-matchesLength of output: 263
- 79-93: The
execute
function correctly delegates to specific functions based on the message type. Ensure that each handler function (reward_users
,claim
,withdraw
) is thoroughly tested to handle all possible inputs and scenarios.Verification successful
The shell script output indicates that there are indeed tests for the
reward_users
,claim
, andwithdraw
functions within thecontracts/airdrop/src/tests/
directory. The tests are located in different files:
reward_users
is tested inquery/user_pool.rs
andexecute/reward_all.rs
.claim
is tested inexecute/claim.rs
.withdraw
is tested inexecute/withdraw.rs
, with multiple scenarios such as successful withdrawal, attempting to withdraw too much, and unauthorized withdrawal attempts.Based on this information, the review comment is addressed, and the handler functions appear to be covered by tests.
* 218-226: The `query` function correctly routes to the appropriate query handler based on the message type. Ensure that each query handler function is thoroughly tested to handle all possible inputs and scenarios.Scripts Executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify that handler functions are covered by tests rg --type rust "fn (reward_users|claim|withdraw)" contracts/airdrop/src/tests/Length of output: 80
Script:
#!/bin/bash # Search for test cases that call the handler functions, which might be named differently rg --type rust "reward_users|claim|withdraw" contracts/airdrop/src/tests/Length of output: 2351
Verification successful
The shell script results confirm the existence of a
tests
directory withincontracts/airdrop/src/
. Additionally, the output indicates that there are test files specifically for querying functionality, as seen incontracts/airdrop/src/tests/query/user_pool.rs
andcontracts/airdrop/src/tests/query/campaign.rs
. These files contain tests forquery_user_reward
andquery_campaign
, respectively.Based on this information, it can be concluded that:
- The
query
function's handlers,query_campaign
andquery_user_reward
, are indeed covered by tests.- The tests are organized within the
query
submodule under thetests
directory.Given this context, the review comment suggesting that the query handler functions should be thoroughly tested is consistent with the codebase, as tests for these functions do exist.
Scripts Executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Verify that query handler functions are covered by tests rg --type rust "fn (query_campaign|query_user_reward)" contracts/airdrop/src/tests/Length of output: 85
Script:
#!/bin/bash # Step 1: Confirm the existence of the test directory and test files within `contracts/airdrop/src/`. fd --type d "tests" contracts/airdrop/src/ # Step 2: If the test directory exists, search for any rust files within it to confirm the presence of test files. fd --type f --extension rs contracts/airdrop/src/tests/ # Step 3: Perform a broader search for test cases related to the query functionality within the airdrop contract. rg --type rust "query" contracts/airdrop/src/tests/Length of output: 1431
contracts/airdrop/src/contract.rs
Outdated
pub fn reward_users( | ||
deps: DepsMut, | ||
_env: Env, | ||
info: MessageInfo, | ||
requests: Vec<RewardUserRequest>, | ||
) -> Result<Response, StdError> { | ||
let mut res = vec![]; | ||
|
||
for req in requests { | ||
let mut campaign = CAMPAIGN.load(deps.storage)?; | ||
|
||
if campaign.owner != info.sender { | ||
res.push(RewardUserResponse { | ||
user_address: req.user_address.clone(), | ||
success: false, | ||
error_msg: "Unauthorized".to_string(), | ||
}); | ||
continue; | ||
} | ||
|
||
if campaign.unallocated_amount < req.amount { | ||
res.push(RewardUserResponse { | ||
user_address: req.user_address.clone(), | ||
success: false, | ||
error_msg: "Not enough funds in campaign".to_string(), | ||
}); | ||
continue; | ||
} | ||
|
||
match USER_REWARDS.may_load(deps.storage, req.user_address.clone())? { | ||
Some(mut user_reward) => { | ||
user_reward += req.amount; | ||
USER_REWARDS.save( | ||
deps.storage, | ||
req.user_address.clone(), | ||
&user_reward, | ||
)?; | ||
} | ||
None => { | ||
USER_REWARDS.save( | ||
deps.storage, | ||
req.user_address.clone(), | ||
&req.amount, | ||
)?; | ||
} | ||
}; | ||
campaign.unallocated_amount -= req.amount; | ||
CAMPAIGN.save(deps.storage, &campaign)?; | ||
|
||
res.push(RewardUserResponse { | ||
user_address: req.user_address.clone(), | ||
success: true, | ||
error_msg: "".to_string(), | ||
}); | ||
} | ||
|
||
return Ok(Response::new() | ||
.add_attribute("method", "reward_users") | ||
.set_data(to_json_binary(&res).unwrap())); | ||
} |
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.
The reward_users
function performs several checks and updates state accordingly. Ensure that the error messages are clear and that the function handles potential race conditions when updating the campaign state.
- let mut campaign = CAMPAIGN.load(deps.storage)?;
+ let mut campaign = CAMPAIGN.load(deps.storage).map_err(|_| StdError::generic_err("Failed to load campaign data"))?;
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.
pub fn reward_users( | |
deps: DepsMut, | |
_env: Env, | |
info: MessageInfo, | |
requests: Vec<RewardUserRequest>, | |
) -> Result<Response, StdError> { | |
let mut res = vec![]; | |
for req in requests { | |
let mut campaign = CAMPAIGN.load(deps.storage)?; | |
if campaign.owner != info.sender { | |
res.push(RewardUserResponse { | |
user_address: req.user_address.clone(), | |
success: false, | |
error_msg: "Unauthorized".to_string(), | |
}); | |
continue; | |
} | |
if campaign.unallocated_amount < req.amount { | |
res.push(RewardUserResponse { | |
user_address: req.user_address.clone(), | |
success: false, | |
error_msg: "Not enough funds in campaign".to_string(), | |
}); | |
continue; | |
} | |
match USER_REWARDS.may_load(deps.storage, req.user_address.clone())? { | |
Some(mut user_reward) => { | |
user_reward += req.amount; | |
USER_REWARDS.save( | |
deps.storage, | |
req.user_address.clone(), | |
&user_reward, | |
)?; | |
} | |
None => { | |
USER_REWARDS.save( | |
deps.storage, | |
req.user_address.clone(), | |
&req.amount, | |
)?; | |
} | |
}; | |
campaign.unallocated_amount -= req.amount; | |
CAMPAIGN.save(deps.storage, &campaign)?; | |
res.push(RewardUserResponse { | |
user_address: req.user_address.clone(), | |
success: true, | |
error_msg: "".to_string(), | |
}); | |
} | |
return Ok(Response::new() | |
.add_attribute("method", "reward_users") | |
.set_data(to_json_binary(&res).unwrap())); | |
} | |
pub fn reward_users( | |
deps: DepsMut, | |
_env: Env, | |
info: MessageInfo, | |
requests: Vec<RewardUserRequest>, | |
) -> Result<Response, StdError> { | |
let mut res = vec![]; | |
for req in requests { | |
let mut campaign = CAMPAIGN.load(deps.storage).map_err(|_| StdError::generic_err("Failed to load campaign data"))?; | |
if campaign.owner != info.sender { | |
res.push(RewardUserResponse { | |
user_address: req.user_address.clone(), | |
success: false, | |
error_msg: "Unauthorized".to_string(), | |
}); | |
continue; | |
} | |
if campaign.unallocated_amount < req.amount { | |
res.push(RewardUserResponse { | |
user_address: req.user_address.clone(), | |
success: false, | |
error_msg: "Not enough funds in campaign".to_string(), | |
}); | |
continue; | |
} | |
match USER_REWARDS.may_load(deps.storage, req.user_address.clone())? { | |
Some(mut user_reward) => { | |
user_reward += req.amount; | |
USER_REWARDS.save( | |
deps.storage, | |
req.user_address.clone(), | |
&user_reward, | |
)?; | |
} | |
None => { | |
USER_REWARDS.save( | |
deps.storage, | |
req.user_address.clone(), | |
&req.amount, | |
)?; | |
} | |
}; | |
campaign.unallocated_amount -= req.amount; | |
CAMPAIGN.save(deps.storage, &campaign)?; | |
res.push(RewardUserResponse { | |
user_address: req.user_address.clone(), | |
success: true, | |
error_msg: "".to_string(), | |
}); | |
} | |
return Ok(Response::new() | |
.add_attribute("method", "reward_users") | |
.set_data(to_json_binary(&res).unwrap())); | |
} |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@CodeRabbit review |
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.
Review Status
Actionable comments generated: 2
Configuration used: CodeRabbit UI
Files selected for processing (11)
- contracts/airdrop/README.md (1 hunks)
- contracts/airdrop/src/contract.rs (1 hunks)
- contracts/airdrop/src/msg.rs (1 hunks)
- contracts/airdrop/src/state.rs (1 hunks)
- contracts/airdrop/src/tests/execute/claim.rs (1 hunks)
- contracts/airdrop/src/tests/execute/mod.rs (1 hunks)
- contracts/airdrop/src/tests/execute/reward_users.rs (1 hunks)
- contracts/airdrop/src/tests/execute/withdraw.rs (1 hunks)
- contracts/airdrop/src/tests/instantiate.rs (1 hunks)
- contracts/airdrop/src/tests/query/campaign.rs (1 hunks)
- contracts/airdrop/src/tests/query/user_pool.rs (1 hunks)
Files skipped from review as they are similar to previous changes (9)
- contracts/airdrop/src/contract.rs
- contracts/airdrop/src/msg.rs
- contracts/airdrop/src/state.rs
- contracts/airdrop/src/tests/execute/claim.rs
- contracts/airdrop/src/tests/execute/mod.rs
- contracts/airdrop/src/tests/execute/withdraw.rs
- contracts/airdrop/src/tests/instantiate.rs
- contracts/airdrop/src/tests/query/campaign.rs
- contracts/airdrop/src/tests/query/user_pool.rs
## Withdraw | ||
|
||
Only the contract owner can withdraw from the contract (not the managers). The `withdraw` exists to withdraw any leftover tokens after the campaign has ended. There is no check for if the total outstanding reward amount is greater than the amount of funds left in the contract. `withdraw` should only be called after the campaign ends because it could leave the contract in a state where it cannot fulfill a user's outstanding reward amount. Additional funds can be sent to the contract to reverse the withdrawal. |
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.
The withdraw
function description indicates a potential risk of withdrawing funds needed for outstanding rewards. Consider implementing a check to prevent withdrawing funds that would leave the contract unable to fulfill outstanding reward amounts.
instantiate( | ||
deps.as_mut(), | ||
env.clone(), | ||
mock_info("owner", &coins(1000, "")), | ||
InstantiateMsg { | ||
campaign_id: "campaign_id".to_string(), | ||
campaign_name: "campaign_name".to_string(), | ||
campaign_description: "campaign_description".to_string(), | ||
managers: vec![Addr::unchecked("manager1"), Addr::unchecked("manager2")], | ||
}, | ||
) | ||
.unwrap(); |
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.
Consider replacing unwrap()
with expect("custom error message")
to provide more context in case of an error, which can aid in debugging test failures.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@CodeRabbit review |
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 Status
Actionable comments generated: 0
Configuration used: CodeRabbit UI
Files selected for processing (1)
- contracts/airdrop/src/contract.rs (1 hunks)
Files skipped from review as they are similar to previous changes (1)
- contracts/airdrop/src/contract.rs
commit 2296e6f1e91db857318632089aa2cf888a4ce8ae Merge: c14558d dd90c9a Author: Unique-Divine <[email protected]> Date: Wed Jan 10 21:25:41 2024 -0600 Merge branch 'main' into realu/std commit c14558de1b80b739701ff3b146355ea815327192 Author: Unique-Divine <[email protected]> Date: Wed Jan 10 15:43:00 2024 -0600 quick save wip! commit dd90c9a Merge: bd792e4 902b788 Author: Jonathan Gimeno <[email protected]> Date: Fri Jan 5 19:00:52 2024 +0100 Merge pull request #111 from NibiruChain/feat/some-refactors-airdrop add some refactors to airdrop contract commit 902b788 Author: Jonathan Gimeno <[email protected]> Date: Fri Jan 5 17:41:16 2024 +0100 remove unused imports commit 0f337d1 Author: Jonathan Gimeno <[email protected]> Date: Fri Jan 5 17:39:21 2024 +0100 it fails when there are not enough funds commit 1d42220 Author: Jonathan Gimeno <[email protected]> Date: Fri Jan 5 17:31:57 2024 +0100 add some refactors, part 1 commit bd792e4 Author: Kevin Yang <[email protected]> Date: Wed Jan 3 14:22:23 2024 -0800 feat: initial airdrop contract (#110) * feat: initial airdrop contract * create cargo config * test: instantiate message * remove owner from instantiate msg * test: query functions * test: execute functions * feat: add manager list * feat: implement manager list * Create README.md * Update contracts/airdrop/src/contract.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update contracts/airdrop/src/contract.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update contracts/airdrop/src/contract.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update contracts/airdrop/src/contract.rs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix error messages --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> commit 88ae9fd Author: Matthias <[email protected]> Date: Wed Jan 3 15:32:33 2024 +0100 doc: Add cookbook for live smart contracts (#109)
* fix(bash-rs): type corrections for e2e tests * build: artifacts + just tidy + docs + merge main commit 2296e6f1e91db857318632089aa2cf888a4ce8ae Merge: c14558d dd90c9a Author: Unique-Divine <[email protected]> Date: Wed Jan 10 21:25:41 2024 -0600 Merge branch 'main' into realu/std commit c14558de1b80b739701ff3b146355ea815327192 Author: Unique-Divine <[email protected]> Date: Wed Jan 10 15:43:00 2024 -0600 quick save wip! commit dd90c9a Merge: bd792e4 902b788 Author: Jonathan Gimeno <[email protected]> Date: Fri Jan 5 19:00:52 2024 +0100 Merge pull request #111 from NibiruChain/feat/some-refactors-airdrop add some refactors to airdrop contract commit 902b788 Author: Jonathan Gimeno <[email protected]> Date: Fri Jan 5 17:41:16 2024 +0100 remove unused imports commit 0f337d1 Author: Jonathan Gimeno <[email protected]> Date: Fri Jan 5 17:39:21 2024 +0100 it fails when there are not enough funds commit 1d42220 Author: Jonathan Gimeno <[email protected]> Date: Fri Jan 5 17:31:57 2024 +0100 add some refactors, part 1 commit bd792e4 Author: Kevin Yang <[email protected]> Date: Wed Jan 3 14:22:23 2024 -0800 feat: initial airdrop contract (#110) * feat: initial airdrop contract * create cargo config * test: instantiate message * remove owner from instantiate msg * test: query functions * test: execute functions * feat: add manager list * feat: implement manager list * Create README.md * Update contracts/airdrop/src/contract.rs ---
An airdrop contract that allows rewarding users.
Summary by CodeRabbit
New Features
Documentation
.gitignore
to exclude specific files from version control.Tests
Chores