-
Notifications
You must be signed in to change notification settings - Fork 0
feat: init repo #1
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
base: master
Are you sure you want to change the base?
Conversation
WalkthroughA new Rust testing crate for Injective is established, including project configuration, mock infrastructure for chain and address generation, and comprehensive test utilities for authorization, banking, exchange, insurance, and oracle operations. Changes
Sequence Diagram(s)sequenceDiagram
participant Test
participant MockApp as MockedInjectiveApp
participant Handler as CustomInjectiveHandler
participant Storage as State/ResponseCache
Test->>MockApp: Execute custom message
MockApp->>Handler: route execute
Handler->>Storage: record message
Handler->>Storage: lookup response
alt Response configured
Handler->>Handler: run optional assertion
Handler->>Test: return stored response
else No response
Handler->>Test: return empty AppResponse
end
Test->>MockApp: Query custom data
MockApp->>Handler: route query
Handler->>Storage: record query
Handler->>Storage: lookup response
alt Response configured
Handler->>Handler: run optional assertion
Handler->>Test: return stored Binary
else No response
Handler->>Test: return empty Binary
end
sequenceDiagram
participant Test
participant AddressGen as InjectiveAddressGenerator
participant Crypto as Secp256k1
participant Encoding as bech32/humanize
Test->>AddressGen: generate_inj_address()
AddressGen->>Crypto: gen secret key
Crypto->>Crypto: derive public key
AddressGen->>AddressGen: keccak256(pubkey[0:20])
AddressGen->>Encoding: to_hex(hash) + prefix 0x
AddressGen->>Encoding: addr_to_bech32(hex)
Encoding->>Test: return Addr (inj1...)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (7)
.gitignore (1)
1-2: Good foundation for repository initialization.The basic
.gitignorecorrectly excludes Rust build outputs and JetBrains IDE configuration. For a library crate, this covers the essential patterns.Optional: Consider expanding
.gitignorefor broader tooling support.If the team uses multiple editors or platforms, you may want to add patterns for:
.DS_Store(macOS).vscode/(Visual Studio Code)*.swp,*.swo(Vim)*.iml(IntelliJ project files)These additions would make the repository more inclusive but aren't critical for the initial setup.
src/test_tube/utils.rs (1)
3-19: Consider checking existence of the fallback path.The function checks whether
targetandarch_targetpaths exist (lines 12, 14), but returns the fallback path (line 17) without verification. This meanswasm_filemay return a non-existent path, causingstore_codeto panic later when callingstd::fs::read(line 22).For test utilities, this may be acceptable behavior, but consider either:
- Adding an existence check for the fallback path and returning an error if none exist
- Documenting that this function may return a non-existent path
src/multi_test/chain_mock.rs (3)
17-20: Consider removing unused enum.The
StargateTenum is defined but never used in this file. If it's intended for future use or external consumption, consider documenting its purpose or adding a#[allow(dead_code)]attribute.
73-78: Consider handling serialization errors more gracefully.Both
with_ok_responsemethods use.unwrap()onto_json_binary(), which could panic if serialization fails. Consider using.expect()with a descriptive message to aid debugging when payload serialization fails.Apply this pattern to both methods:
- response: Some(Ok(Some(to_json_binary(payload).unwrap()))), + response: Some(Ok(Some(to_json_binary(payload).expect("Failed to serialize payload")))),Also applies to: 98-102
341-345: Simplify the binary copy implementation.The manual copying with
clone_from_sliceis unnecessary sinceBinaryimplementsClone. The current implementation also allocates a zero-filled vector that is immediately overwritten.Apply this diff to simplify:
fn copy_binary(binary: &Binary) -> Binary { - let mut c: Vec<u8> = vec![0; binary.to_vec().len()]; - c.clone_from_slice(binary); - Binary::new(c) + binary.clone() }src/test_tube/exchange.rs (2)
36-96: Consider extracting the governance module address as a constant.Line 48 hardcodes the governance module address with a comment noting it could change. Consider extracting this as a module-level constant to make it easier to update and reuse across test helpers.
Example:
const GOVERNANCE_MODULE_ADDRESS: &str = "inj10d07y265gmmuvt4z0w9aw880jnsr700jstypyt";
866-887: Remove unnecessary divisions by one.Lines 876 and 884 divide by
Uint128::one(), which has no effect. These operations are unnecessary and should be removed.Apply this diff:
- let total_balance = Uint128::from_str(&trade_deposits_during.deposits[&denom].total_balance) - .unwrap_or(Uint128::zero()) // Use zero if the result is an Err - / Uint128::one(); + let total_balance = Uint128::from_str(&trade_deposits_during.deposits[&denom].total_balance) + .unwrap_or(Uint128::zero()); let effective_position = exchange .query_subaccount_effective_position_in_market(&QuerySubaccountEffectivePositionInMarketRequest { market_id, subaccount_id }) .unwrap(); let effective_margin = effective_position.state.as_ref().map_or(Uint128::zero(), |state| { Uint128::from_str(&state.effective_margin).unwrap_or(Uint128::zero()) - }) / Uint128::one(); + });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (16)
.gitignore(1 hunks)CHANGELOG.md(1 hunks)Cargo.toml(1 hunks)src/lib.rs(1 hunks)src/mocks.rs(1 hunks)src/multi_test/address_generator.rs(1 hunks)src/multi_test/chain_mock.rs(1 hunks)src/multi_test/mod.rs(1 hunks)src/test_tube/authz.rs(1 hunks)src/test_tube/bank.rs(1 hunks)src/test_tube/exchange.rs(1 hunks)src/test_tube/insurance.rs(1 hunks)src/test_tube/mod.rs(1 hunks)src/test_tube/oracle.rs(1 hunks)src/test_tube/utils.rs(1 hunks)src/utils.rs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/mocks.rs (1)
src/utils.rs (1)
human_to_dec(30-32)
src/test_tube/insurance.rs (1)
src/utils.rs (1)
human_to_dec(30-32)
src/multi_test/chain_mock.rs (1)
src/multi_test/address_generator.rs (1)
default(46-50)
src/test_tube/exchange.rs (1)
src/utils.rs (4)
dec_to_proto(26-28)scale_price_quantity_perp_market(64-75)scale_price_quantity_spot_market(54-62)str_coin(48-52)
🔇 Additional comments (10)
src/multi_test/mod.rs (1)
1-2: LGTM!Clean module structure exposing the multi-test infrastructure.
src/test_tube/mod.rs (1)
1-6: LGTM!Well-organized module structure for test-tube utilities covering authorization, banking, exchange, insurance, oracle, and general utilities.
src/lib.rs (1)
1-4: LGTM!Clear and concise crate root that exposes the four main module categories: mocks, multi_test, test_tube, and utils.
src/test_tube/insurance.rs (1)
11-40: LGTM!The insurance fund launch helper provides a clean interface for test setup. The use of
.unwrap()is appropriate for test utilities where panics on failure are acceptable. The fixed initial deposit amount and -1 expiry value are reasonable defaults for testing scenarios.src/test_tube/bank.rs (2)
11-24: LGTM!The
sendhelper provides a clean interface for bank transfers in tests. The use of.unwrap()is appropriate for test utilities.
26-33: LGTM!The
query_balancehelper correctly handles the optional balance response, defaulting to zero when no balance exists. This is a sensible default for test scenarios.src/test_tube/utils.rs (1)
21-24: LGTM!The
store_codehelper provides a convenient wrapper for loading and storing WASM code. The use of.unwrap()is acceptable for test utilities where panics on failure provide clear error messages.Cargo.toml (1)
19-19: Upgrade to a maintained rand version for better maintainability.The rand crate version 0.4.6 is from 2017 and significantly outdated; the current stable version is 0.9.2 (released 2025). While rand 0.4.6 itself has no direct RustSec advisory, rand_core versions 0.4.0–0.4.1 and 0.6.0–0.6.1 contained known vulnerabilities. Verification of Cargo.lock confirms your build pulls in safe rand_core versions (0.4.2, 0.6.4), so no active vulnerability is present. However, using such an old, unmaintained version creates a maintenance liability. Upgrade to a maintained rand version (0.9.x) to reduce long-term risk and stay current with security patches.
src/multi_test/chain_mock.rs (1)
184-288: LGTM!The
Moduleimplementation correctly handles execute, query, and sudo operations with proper guards for assertions and responses. The unwrap calls are safely guarded by preceding checks that verify the data exists.src/test_tube/exchange.rs (1)
1-901: Well-structured test utilities.The exchange test helpers are comprehensive and well-organized, providing useful abstractions for common testing scenarios like market launches, order execution, and price creation. The consistent naming conventions and parameter patterns make the API intuitive to use.
| ## [1.1.1] - 2024-10-02 | ||
|
|
||
| ### Added | ||
|
|
||
| - Added a staking denom to the mocks | ||
|
|
||
| ## [1.1.0] - 2024-08-23 | ||
|
|
||
| ### Changed | ||
|
|
||
| - Updated `lib.rs` so that the re-organisation is adhered to | ||
|
|
||
| ## [1.0.0] - 2024-08-23 | ||
|
|
||
| ### Changed | ||
|
|
||
| - Restructure of architecture to improve organisation of helper functions and also to enable re-use with multiple projects |
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.
Historical changelog entries in an "init repo" PR.
The changelog contains versions dating back to August 2024, which seems inconsistent with this being an initialization PR. This suggests the code is being migrated or extracted from another repository rather than created from scratch. Consider clarifying in the PR description whether this is a migration/extraction, or update the changelog to reflect that this is version 1.2.0 as the initial public release.
🤖 Prompt for AI Agents
In CHANGELOG.md around lines 8–24, the file contains historical version entries
(Aug–Oct 2024) that contradict an "init repo" PR; either clarify in the PR
description that this is a migration/extraction from another repository (mention
source repo and date), or edit the changelog so the initial public release is a
single entry (e.g., v1.2.0 or v1.0.0) reflecting the init PR and move prior
history into a separate MIGRATION.md or archive section labeled "Migrated
history" with the original dates; update the changelog header and PR description
accordingly to ensure consistency.
| min_quantity_tick_size: FPDecimal::must_from_str("10000000000000.0"), // 0.00001 @ 18dp | ||
| relayer_fee_share_rate: FPDecimal::must_from_str("0.4"), | ||
| min_notional: FPDecimal::ZERO, | ||
| } | ||
| } | ||
|
|
||
| // Mock INJ Market | ||
| pub fn mock_derivative_market(market_id: &str) -> DerivativeMarket { | ||
| DerivativeMarket { | ||
| ticker: String::from("INJ:USDT"), | ||
| oracle_base: String::from("inj"), | ||
| oracle_quote: String::from("usdt"), | ||
| oracle_type: OracleType::PriceFeed, | ||
| oracle_scale_factor: 0u32, | ||
| quote_denom: String::from("usdt"), | ||
| market_id: MarketId::unchecked(market_id), | ||
| initial_margin_ratio: FPDecimal::must_from_str("0.195"), | ||
| maintenance_margin_ratio: FPDecimal::must_from_str("0.05"), | ||
| maker_fee_rate: FPDecimal::ZERO, | ||
| taker_fee_rate: FPDecimal::ZERO, | ||
| isPerpetual: true, | ||
| status: MarketStatus::Active, | ||
| min_price_tick_size: FPDecimal::must_from_str("1000.0"), // 0.001 | ||
| min_quantity_tick_size: FPDecimal::must_from_str("0.001"), // 0.001 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the tick size scaling constants.
FPDecimal::must_from_str already takes a human-readable decimal. Feeding it "10000000000000.0" (Line 33) or "1000.0" (Line 55) makes the mock markets report astronomically large tick sizes instead of the intended 0.00001 and 0.001. Any tests depending on these helpers will behave incorrectly (orders will fail validation because of the exaggerated ticks). Please feed the actual human values and let FPDecimal manage the fixed-point scaling.
- min_quantity_tick_size: FPDecimal::must_from_str("10000000000000.0"), // 0.00001 @ 18dp
+ min_quantity_tick_size: FPDecimal::must_from_str("0.00001"), // 0.00001 @ 18dp
...
- min_price_tick_size: FPDecimal::must_from_str("1000.0"), // 0.001
+ min_price_tick_size: FPDecimal::must_from_str("0.001"), // 0.001📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| min_quantity_tick_size: FPDecimal::must_from_str("10000000000000.0"), // 0.00001 @ 18dp | |
| relayer_fee_share_rate: FPDecimal::must_from_str("0.4"), | |
| min_notional: FPDecimal::ZERO, | |
| } | |
| } | |
| // Mock INJ Market | |
| pub fn mock_derivative_market(market_id: &str) -> DerivativeMarket { | |
| DerivativeMarket { | |
| ticker: String::from("INJ:USDT"), | |
| oracle_base: String::from("inj"), | |
| oracle_quote: String::from("usdt"), | |
| oracle_type: OracleType::PriceFeed, | |
| oracle_scale_factor: 0u32, | |
| quote_denom: String::from("usdt"), | |
| market_id: MarketId::unchecked(market_id), | |
| initial_margin_ratio: FPDecimal::must_from_str("0.195"), | |
| maintenance_margin_ratio: FPDecimal::must_from_str("0.05"), | |
| maker_fee_rate: FPDecimal::ZERO, | |
| taker_fee_rate: FPDecimal::ZERO, | |
| isPerpetual: true, | |
| status: MarketStatus::Active, | |
| min_price_tick_size: FPDecimal::must_from_str("1000.0"), // 0.001 | |
| min_quantity_tick_size: FPDecimal::must_from_str("0.001"), // 0.001 | |
| } | |
| min_quantity_tick_size: FPDecimal::must_from_str("0.00001"), // 0.00001 @ 18dp | |
| relayer_fee_share_rate: FPDecimal::must_from_str("0.4"), | |
| min_notional: FPDecimal::ZERO, | |
| } | |
| } | |
| // Mock INJ Market | |
| pub fn mock_derivative_market(market_id: &str) -> DerivativeMarket { | |
| DerivativeMarket { | |
| ticker: String::from("INJ:USDT"), | |
| oracle_base: String::from("inj"), | |
| oracle_quote: String::from("usdt"), | |
| oracle_type: OracleType::PriceFeed, | |
| oracle_scale_factor: 0u32, | |
| quote_denom: String::from("usdt"), | |
| market_id: MarketId::unchecked(market_id), | |
| initial_margin_ratio: FPDecimal::must_from_str("0.195"), | |
| maintenance_margin_ratio: FPDecimal::must_from_str("0.05"), | |
| maker_fee_rate: FPDecimal::ZERO, | |
| taker_fee_rate: FPDecimal::ZERO, | |
| isPerpetual: true, | |
| status: MarketStatus::Active, | |
| min_price_tick_size: FPDecimal::must_from_str("0.001"), // 0.001 | |
| min_quantity_tick_size: FPDecimal::must_from_str("0.001"), // 0.001 | |
| } |
🤖 Prompt for AI Agents
In src/mocks.rs around lines 33 to 57, the tick-size constants are passed to
FPDecimal::must_from_str as already-scaled integers ("10000000000000.0" and
"1000.0") causing huge tick sizes; change those two calls to use the actual
human-readable decimals ("0.00001" for the first min_quantity_tick_size and
"0.001" for min_price_tick_size / min_quantity_tick_size as appropriate) so
FPDecimal handles the fixed-point scaling correctly.
| pub fn launch_spot_market_atom(exchange: &Exchange<InjectiveTestApp>, signer: &SigningAccount, ticker: String) -> String { | ||
| exchange | ||
| .instant_spot_market_launch_v2( | ||
| v2::MsgInstantSpotMarketLaunch { | ||
| sender: signer.address(), | ||
| ticker: "ATOM/USDT".to_owned(), | ||
| base_denom: MOCK_ATOM_DENOM.to_owned(), | ||
| quote_denom: MOCK_QUOTE_DENOM.to_owned(), | ||
| min_price_tick_size: dec_to_proto(FPDecimal::must_from_str("0.000010000000000000")), | ||
| min_quantity_tick_size: dec_to_proto(FPDecimal::must_from_str("100000")), | ||
| min_notional: dec_to_proto(FPDecimal::must_from_str("1")), | ||
| base_decimals: MOCK_ATOM_DECIMALS as u32, | ||
| quote_decimals: MOCK_QUOTE_DECIMALS as u32, | ||
| }, | ||
| signer, | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| get_spot_market_id(exchange, ticker) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix ticker parameter usage inconsistency.
The function accepts a ticker parameter but hardcodes "ATOM/USDT" when launching the market (Line 533), then attempts to retrieve the market using the provided ticker parameter (Line 546). This inconsistency will cause the function to fail when ticker != "ATOM/USDT".
Apply this diff to use the ticker parameter consistently:
v2::MsgInstantSpotMarketLaunch {
sender: signer.address(),
- ticker: "ATOM/USDT".to_owned(),
+ ticker: ticker.clone(),
base_denom: MOCK_ATOM_DENOM.to_owned(),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| pub fn launch_spot_market_atom(exchange: &Exchange<InjectiveTestApp>, signer: &SigningAccount, ticker: String) -> String { | |
| exchange | |
| .instant_spot_market_launch_v2( | |
| v2::MsgInstantSpotMarketLaunch { | |
| sender: signer.address(), | |
| ticker: "ATOM/USDT".to_owned(), | |
| base_denom: MOCK_ATOM_DENOM.to_owned(), | |
| quote_denom: MOCK_QUOTE_DENOM.to_owned(), | |
| min_price_tick_size: dec_to_proto(FPDecimal::must_from_str("0.000010000000000000")), | |
| min_quantity_tick_size: dec_to_proto(FPDecimal::must_from_str("100000")), | |
| min_notional: dec_to_proto(FPDecimal::must_from_str("1")), | |
| base_decimals: MOCK_ATOM_DECIMALS as u32, | |
| quote_decimals: MOCK_QUOTE_DECIMALS as u32, | |
| }, | |
| signer, | |
| ) | |
| .unwrap(); | |
| get_spot_market_id(exchange, ticker) | |
| } | |
| pub fn launch_spot_market_atom(exchange: &Exchange<InjectiveTestApp>, signer: &SigningAccount, ticker: String) -> String { | |
| exchange | |
| .instant_spot_market_launch_v2( | |
| v2::MsgInstantSpotMarketLaunch { | |
| sender: signer.address(), | |
| ticker: ticker.clone(), | |
| base_denom: MOCK_ATOM_DENOM.to_owned(), | |
| quote_denom: MOCK_QUOTE_DENOM.to_owned(), | |
| min_price_tick_size: dec_to_proto(FPDecimal::must_from_str("0.000010000000000000")), | |
| min_quantity_tick_size: dec_to_proto(FPDecimal::must_from_str("100000")), | |
| min_notional: dec_to_proto(FPDecimal::must_from_str("1")), | |
| base_decimals: MOCK_ATOM_DECIMALS as u32, | |
| quote_decimals: MOCK_QUOTE_DECIMALS as u32, | |
| }, | |
| signer, | |
| ) | |
| .unwrap(); | |
| get_spot_market_id(exchange, ticker) | |
| } |
🤖 Prompt for AI Agents
In src/test_tube/exchange.rs around lines 528 to 547, the
launch_spot_market_atom function takes a ticker parameter but hardcodes
"ATOM/USDT" when building v2::MsgInstantSpotMarketLaunch (line ~533), causing a
mismatch with the later get_spot_market_id(exchange, ticker) call; replace the
hardcoded string with the provided ticker (e.g., use ticker.clone() or
ticker.to_owned() for the MsgInstantSpotMarketLaunch.ticker field) so the same
ticker value is used for both launching the market and retrieving its ID.
Summary by CodeRabbit
New Features
Documentation
Chores