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(broker-bank): smart contract as broker for bank transfers #134

Merged
merged 10 commits into from
Mar 19, 2024

Conversation

Unique-Divine
Copy link
Member

@Unique-Divine Unique-Divine commented Mar 8, 2024

Abstract / Context

🚧 (Still draft, but you can review the main logic now). 🚧

Tasks for Completion

  • Impl main business logic for execute and query
  • Collect address(es) to be used in production by 8am UTC on Mar 12, 2024.
  • test: Finish unit test coverage of execute paths
  • test: E2E "dress rehearsal" with peers for sanity check

Commit Log

  • docs(cw3-flex-msig): Add usage guide to README
  • clean: ignore schema directories
  • feat(broker-bank): smart contract as broker for bank transfers

Summary by CodeRabbit

  • New Features

    • Introduced a broker-bank contract with functionalities for bank transactions, managing permissions, and querying status.
    • Added error handling for various contract operations.
  • Refactor

    • Reorganized message structures in the core-shifter contract for improved clarity.
  • Chores

    • Updated .gitignore to exclude specific schema directories.
    • Made TestResult type alias available for general use by removing conditional compilation attribute.

Copy link
Contributor

coderabbitai bot commented Mar 8, 2024

Walkthrough

The updates introduce a comprehensive suite of functionalities for a broker-bank smart contract, focusing on operational permissions, error handling, event management, and state management. Key features include initializing contracts, managing bank sends, toggling operations, updating ownership, editing operators, withdrawing funds, and handling permissions. Additionally, there's a reorganization in the core-shifter contract and a minor update in the standard library to support testing.

Changes

Files Change Summary
.gitignore Added the contracts/*/schema entry.
contracts/broker-bank/... Introduces functionalities for the broker-bank contract, error handling, events, and state management.
contracts/core-shifter/... Reorganized permission messages into a submodule and updated query functionality.
nibiru-std/src/errors.rs Made TestResult a type alias for anyhow::Result<()>.

🐰✨
In the code's woven maze, changes bloom,
A broker-bank, beneath the coder's loom.
Permissions dance, errors take flight,
In the blockchain's glow, a new light.
🌟 Through tests and tales, our journey weaves,
A tale of codes, in bytes, it breathes.
🚀🌙

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>.
    • Generate unit-tests 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 tests 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

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 as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • 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/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

@Unique-Divine Unique-Divine added the type: enhancement New feature or request label Mar 8, 2024
Copy link

codecov bot commented Mar 8, 2024

Codecov Report

Attention: Patch coverage is 93.44978% with 45 lines in your changes are missing coverage. Please review.

Project coverage is 87.94%. Comparing base (902b788) to head (ed5ba4d).
Report is 20 commits behind head on main.

Additional details and impacted files
Files Coverage Δ
contracts/broker-bank/src/state.rs 100.00% <100.00%> (ø)
contracts/core-shifter/src/contract.rs 83.07% <100.00%> (+0.13%) ⬆️
contracts/core-shifter/src/msgs.rs 28.57% <100.00%> (ø)
nibiru-std/src/errors.rs 0.00% <ø> (ø)
contracts/broker-bank/src/msgs.rs 60.00% <60.00%> (ø)
contracts/broker-bank/src/error.rs 25.00% <25.00%> (ø)
contracts/broker-bank/src/oper_perms.rs 91.42% <91.42%> (ø)
contracts/broker-bank/src/tutil.rs 88.63% <88.63%> (ø)
contracts/broker-bank/src/events.rs 58.33% <58.33%> (ø)
contracts/broker-bank/src/contract.rs 96.11% <96.11%> (ø)

... and 12 files with indirect coverage changes

Copy link
Member Author

Choose a reason for hiding this comment

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

Removing #[cfg(test)] is necessary for external crates to import this type.

@Unique-Divine Unique-Divine marked this pull request as ready for review March 12, 2024 02:38
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.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 3ad5d5e and 643143f.
Files ignored due to path filters (2)
  • Cargo.lock is excluded by: !**/*.lock
  • contracts/broker-bank/Cargo.toml is excluded by: !**/*.toml
Files selected for processing (13)
  • .gitignore (1 hunks)
  • contracts/broker-bank/.cargo/config (1 hunks)
  • contracts/broker-bank/src/contract.rs (1 hunks)
  • contracts/broker-bank/src/error.rs (1 hunks)
  • contracts/broker-bank/src/events.rs (1 hunks)
  • contracts/broker-bank/src/lib.rs (1 hunks)
  • contracts/broker-bank/src/msgs.rs (1 hunks)
  • contracts/broker-bank/src/oper_perms.rs (1 hunks)
  • contracts/broker-bank/src/state.rs (1 hunks)
  • contracts/broker-bank/src/tutil.rs (1 hunks)
  • contracts/core-shifter/src/contract.rs (5 hunks)
  • contracts/core-shifter/src/msgs.rs (3 hunks)
  • nibiru-std/src/errors.rs (1 hunks)
Files skipped from review due to trivial changes (2)
  • .gitignore
  • contracts/broker-bank/.cargo/config
Additional comments: 31
contracts/broker-bank/src/lib.rs (2)
  • 4-5: The conditional compilation for the tutil module ensures it's only included during testing, which is a good practice to avoid including test utilities in the production build.
  • 7-11: The conditional compilation to exclude contract.rs when the "library" feature is enabled is a smart way to prevent entry point errors and make the contract usable as a library. This approach enhances modularity and reusability.
contracts/broker-bank/src/events.rs (4)
  • 5-9: The event_bank_send function correctly creates an event for bank sends with appropriate attributes. This follows best practices for event creation in smart contracts, ensuring traceability and transparency of actions.
  • 11-14: The event_toggle_halt function properly creates an event for toggling the halt status of operations. Using the boolean status converted to a string as an attribute is a clear way to indicate the new state.
  • 16-20: The event_withdraw function is well-implemented for creating withdrawal events, including the necessary attributes for tracking the operation. This enhances the auditability of transactions.
  • 22-26: The denom_set_json function provides a utility for serializing a set of denominations to JSON. Leveraging serde_json for this task is efficient and reduces the potential for serialization errors.
contracts/broker-bank/src/state.rs (5)
  • 6-8: The TO_ADDRS constant is correctly defined using Item for a set of addresses. This is a good practice for managing a whitelist of recipient addresses in the contract state.
  • 10-12: The OPERATORS constant uses Item to store a set of operator addresses. This approach is efficient for managing permissions within the smart contract.
  • 14-17: The LOGS constant is well-defined using Deque to maintain a queue of log entries. This is suitable for tracking transaction and event logs over time, allowing for efficient append and retrieval operations.
  • 19-20: The IS_HALTED constant uses Item to store a boolean flag indicating whether operations are currently halted. This is a straightforward and effective way to manage operational status.
  • 22-29: The Log struct is appropriately designed to record execute transaction logs, including block height, sender address, and the event. Using cw_serde for serialization ensures compatibility with the CosmWasm framework.
contracts/broker-bank/src/error.rs (2)
  • 6-39: The ContractError enum comprehensively covers various error scenarios, including standard errors, JSON serialization errors, ownership errors, and specific contract operation errors. This approach ensures robust error handling throughout the contract.
  • 41-44: The implementation of From<serde_json::Error> for ContractError is correctly done, allowing for seamless conversion of JSON serialization errors into contract errors. This enhances error handling consistency.
nibiru-std/src/errors.rs (1)
  • 3-3: Removing the conditional compilation attribute from TestResult makes it universally available, simplifying error handling in tests. This change is beneficial for consistency across the codebase.
contracts/core-shifter/src/msgs.rs (1)
  • 26-45: Moving HasPermsResponse and PermsResponse structs into the operator_perms submodule and updating QueryMsg variants to return types from this module is a good practice for organizing related functionalities. This enhances code maintainability and readability.
contracts/broker-bank/src/tutil.rs (3)
  • 16-35: The setup_contract function provides a flexible way to initialize the contract with custom addresses and operators for testing. This is a valuable utility for creating diverse test scenarios.
  • 38-51: The setup_contract_defaults function simplifies the process of setting up the contract with default parameters for tests. This utility function is helpful for quickly initializing common test setups.
  • 54-61: The utility functions mock_info_for_sender and mock_env_height are well-implemented for creating mock MessageInfo and Env with specific sender and block height. These functions are essential for testing contract behavior under different conditions.
contracts/broker-bank/src/msgs.rs (3)
  • 8-33: The ExecuteMsg enum is well-structured, covering essential contract operations such as bank sends, toggling halt status, withdrawals, and editing operators. This comprehensive coverage ensures that the contract can be interacted with effectively through these messages.
  • 35-44: The QueryMsg enum, including the Perms query, is correctly defined to allow querying the contract's permissions status. This is crucial for external entities to understand the operational status and permissions setup of the contract.
  • 46-58: The InstantiateMsg struct is appropriately designed to capture the necessary information for initializing the contract, including the owner, whitelisted addresses, and operators. This ensures that the contract is set up with clear ownership and operational permissions from the start.
contracts/broker-bank/src/oper_perms.rs (3)
  • 8-12: The Action enum provides a clear and concise way to represent actions related to operator permissions, such as adding or removing an operator. This is an effective approach to encapsulating permission-related actions.
  • 14-24: The response structs HasPermsResponse and PermsResponse are well-defined, offering a structured way to respond to permission queries. This ensures that permission-related information is communicated clearly and effectively.
  • 26-70: The Permissions struct and its associated functions, including loading permissions from storage and asserting operator status, are correctly implemented. These functionalities are essential for managing and validating operator permissions within the contract.
contracts/core-shifter/src/contract.rs (1)
  • 164-170: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [15-176]

The updates to the contract's entry points and permission management logic, including the reorganization of permission-related message structures into a submodule, are correctly implemented. These changes improve the contract's structure and maintainability.

contracts/broker-bank/src/contract.rs (6)
  • 25-41: The instantiation logic correctly sets up the contract version, initializes the owner, and sets initial state values. However, consider validating the to_addrs and opers in InstantiateMsg to ensure they meet any specific criteria (e.g., address format) before saving them to state. This can prevent potential issues with invalid data being stored.
  • 18-23: The ContractError enum is well-defined and covers a broad range of potential error scenarios. Ensure that all functions that can fail are using this enum for error handling, and consider adding more specific errors if any scenarios are not adequately covered.
  • 19-20: Event management through event_bank_send, event_toggle_halt, and event_withdraw functions is implemented correctly. Ensure that all significant contract actions are accompanied by event logging for transparency and auditability.
  • 13-22: State management is handled appropriately with the use of the TO_ADDRS, OPERATORS, and IS_HALTED storage items. Consider adding checks or validations when updating these state items to ensure that only valid data is stored. For example, when adding operators, ensure that the addresses are valid blockchain addresses.
  • 212-227: The query functionalities for permissions status and ownership are implemented correctly. However, ensure that the data returned by these queries does not expose sensitive information or compromise the contract's security. Additionally, consider caching frequently accessed data to improve query performance.
  • 275-698: The tests provided cover a range of functionalities and scenarios, which is excellent for ensuring contract reliability. A few suggestions for improvement:
  • Increase coverage for edge cases and error conditions to ensure robustness.
  • Consider using parameterized tests to reduce code duplication and cover more scenarios with less code.
  • Ensure that all public and critical internal functions are covered by tests.

Comment on lines +44 to +188
cw_ownable::assert_owner(deps.storage, &info.sender)?;
let new_is_halted = !IS_HALTED.load(deps.storage)?;
IS_HALTED.save(deps.storage, &new_is_halted)?;
Ok(Response::new().add_event(event_toggle_halt(&new_is_halted)))
}

ExecuteMsg::UpdateOwnership(action) => {
Ok(execute_update_ownership(deps, env, info, action)?)
}

ExecuteMsg::EditOpers(action) => {
cw_ownable::assert_owner(deps.storage, &info.sender)?;
let mut perms = Permissions::load(deps.storage)?;
let api = deps.api;
match action {
oper_perms::Action::AddOper { address } => {
let addr = api.addr_validate(address.as_str())?;
perms.operators.insert(addr.into_string());
OPERATORS.save(deps.storage, &perms.operators)?;

let res = Response::new().add_attributes(vec![
attr("action", "add_operator"),
attr("address", address),
]);
Ok(res)
}

oper_perms::Action::RemoveOper { address } => {
perms.operators.remove(address.as_str());
OPERATORS.save(deps.storage, &perms.operators)?;

let res = Response::new().add_attributes(vec![
attr("action", "remove_operator"),
attr("address", address),
]);
Ok(res)
}
}
}

ExecuteMsg::WithdrawAll { to } => {
cw_ownable::assert_owner(deps.storage, &info.sender)?;
let to_addr: String = match to {
Some(given_to_addr) => given_to_addr,
None => info.sender.to_string(),
};
let balances = query_bank_balances(contract_addr, deps.as_ref())?;
let tx_msg = BankMsg::Send {
to_address: to_addr.to_string(),
amount: balances.amount.clone(),
};
let event = event_withdraw(
serde_json::to_string(&balances.amount)?.as_str(),
&to_addr,
);
LOGS.push_front(
deps.storage,
&Log {
block_height: env.block.height,
sender_addr: info.sender.to_string(),
event: event.clone(),
},
)?;
Ok(Response::new().add_message(tx_msg).add_event(event))
}

ExecuteMsg::Withdraw { to, denoms } => {
cw_ownable::assert_owner(deps.storage, &info.sender)?;
let to_addr: String = match to {
Some(given_to_addr) => given_to_addr,
None => info.sender.to_string(),
};
let balances: AllBalanceResponse =
query_bank_balances(contract_addr, deps.as_ref())?;
let balances: Vec<cw_std::Coin> = balances
.amount
.iter()
.filter(|b_coin| denoms.contains(&b_coin.denom))
.cloned()
.collect();

let tx_msg = BankMsg::Send {
to_address: to_addr.to_string(),
amount: balances.clone(),
};
let event = event_withdraw(
serde_json::to_string(&balances)?.as_str(),
&to_addr,
);
LOGS.push_front(
deps.storage,
&Log {
block_height: env.block.height,
sender_addr: info.sender.to_string(),
event: event.clone(),
},
)?;
Ok(Response::new().add_message(tx_msg).add_event(event))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The execution logic is well-structured and covers a range of functionalities. A few points to consider:

  • In the BankSend case, ensure that the coins and to fields in the ExecuteMsg are validated before proceeding with the transaction.
  • The assert_not_halted function's logic seems inverted; it should return Ok(()) when is_halted is false, not true. This is critical for the correct functioning of the contract.
  • For EditOpers, consider adding logging for the addition and removal of operators for better auditability.
-        true => Ok(()),
+        false => Ok(()),
-        false => Err(ContractError::OperationsHalted),
+        true => Err(ContractError::OperationsHalted),

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.

Suggested change
#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
let contract_addr = env.contract.address.to_string();
match msg {
ExecuteMsg::BankSend { coins, to } => {
// assert sender is operator
Permissions::assert_operator(deps.storage, info.sender.to_string())?;
// assert: Operator execute calls should not be halted.
let is_halted = IS_HALTED.load(deps.storage)?;
assert_not_halted(is_halted)?;
// assert: Recipient addr must be in the TO_ADDRS set.
if !TO_ADDRS.load(deps.storage)?.contains(&to) {
return Err(ContractError::ToAddrNotAllowed {
to_addr: to.to_string(),
});
}
// Events and tx history logging
let coins_json = serde_json::to_string(&coins)?;
let event = event_bank_send(&coins_json, info.sender.as_str());
LOGS.push_front(
deps.storage,
&Log {
block_height: env.block.height,
sender_addr: info.sender.to_string(),
event: event.clone(),
},
)?;
// Reply with TxMsg to send funds
Ok(Response::new()
.add_message(BankMsg::Send {
to_address: to,
amount: coins,
})
.add_event(event))
}
ExecuteMsg::ToggleHalt {} => {
cw_ownable::assert_owner(deps.storage, &info.sender)?;
let new_is_halted = !IS_HALTED.load(deps.storage)?;
IS_HALTED.save(deps.storage, &new_is_halted)?;
Ok(Response::new().add_event(event_toggle_halt(&new_is_halted)))
}
ExecuteMsg::UpdateOwnership(action) => {
Ok(execute_update_ownership(deps, env, info, action)?)
}
ExecuteMsg::EditOpers(action) => {
cw_ownable::assert_owner(deps.storage, &info.sender)?;
let mut perms = Permissions::load(deps.storage)?;
let api = deps.api;
match action {
oper_perms::Action::AddOper { address } => {
let addr = api.addr_validate(address.as_str())?;
perms.operators.insert(addr.into_string());
OPERATORS.save(deps.storage, &perms.operators)?;
let res = Response::new().add_attributes(vec![
attr("action", "add_operator"),
attr("address", address),
]);
Ok(res)
}
oper_perms::Action::RemoveOper { address } => {
perms.operators.remove(address.as_str());
OPERATORS.save(deps.storage, &perms.operators)?;
let res = Response::new().add_attributes(vec![
attr("action", "remove_operator"),
attr("address", address),
]);
Ok(res)
}
}
}
ExecuteMsg::WithdrawAll { to } => {
cw_ownable::assert_owner(deps.storage, &info.sender)?;
let to_addr: String = match to {
Some(given_to_addr) => given_to_addr,
None => info.sender.to_string(),
};
let balances = query_bank_balances(contract_addr, deps.as_ref())?;
let tx_msg = BankMsg::Send {
to_address: to_addr.to_string(),
amount: balances.amount.clone(),
};
let event = event_withdraw(
serde_json::to_string(&balances.amount)?.as_str(),
&to_addr,
);
LOGS.push_front(
deps.storage,
&Log {
block_height: env.block.height,
sender_addr: info.sender.to_string(),
event: event.clone(),
},
)?;
Ok(Response::new().add_message(tx_msg).add_event(event))
}
ExecuteMsg::Withdraw { to, denoms } => {
cw_ownable::assert_owner(deps.storage, &info.sender)?;
let to_addr: String = match to {
Some(given_to_addr) => given_to_addr,
None => info.sender.to_string(),
};
let balances: AllBalanceResponse =
query_bank_balances(contract_addr, deps.as_ref())?;
let balances: Vec<cw_std::Coin> = balances
.amount
.iter()
.filter(|b_coin| denoms.contains(&b_coin.denom))
.cloned()
.collect();
let tx_msg = BankMsg::Send {
to_address: to_addr.to_string(),
amount: balances.clone(),
};
let event = event_withdraw(
serde_json::to_string(&balances)?.as_str(),
&to_addr,
);
LOGS.push_front(
deps.storage,
&Log {
block_height: env.block.height,
sender_addr: info.sender.to_string(),
event: event.clone(),
},
)?;
Ok(Response::new().add_message(tx_msg).add_event(event))
}
}
true => Err(ContractError::OperationsHalted),
false => Ok(()),

Comment on lines +28 to +33
pub fn event_migrate(_arg0: &u64, _arg1: &bool) -> Event {
// Event::new("migrate_nusd_valuator")
// .add_attribute("id", _arg0.to_string())
// .add_attribute("new_done", _arg1.to_string())
todo!(); // TODO: event migrate
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The event_migrate function is currently a placeholder marked with todo!(). This indicates unfinished functionality that should be addressed before finalizing the contract.

Would you like assistance in implementing the migration event logic, or should this be tracked as a separate task?

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.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 643143f and ed5ba4d.
Files selected for processing (1)
  • contracts/broker-bank/src/contract.rs (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • contracts/broker-bank/src/contract.rs

@matthiasmatt matthiasmatt merged commit 21dbc5c into main Mar 19, 2024
5 checks passed
@matthiasmatt matthiasmatt deleted the ud/broker-bank branch March 19, 2024 12:28
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
Projects
Status: ✅ Completed
Development

Successfully merging this pull request may close these issues.

2 participants