From 77b544e06fd73022651ec9a0acaf61199a07a04c Mon Sep 17 00:00:00 2001 From: Ndifreke000 Date: Sat, 21 Feb 2026 15:42:23 +0100 Subject: [PATCH 1/5] feat(ci): enhance smart contract CI/CD workflow - Add cargo test on every PR - Run cargo clippy for linting - Build WASM for each contract - Add test coverage reporting with tarpaulin - Cache Cargo registry for speed - Upload WASM artifacts Closes #119 --- .github/workflows/contracts.yml | 81 +++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .github/workflows/contracts.yml diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml new file mode 100644 index 0000000..8762168 --- /dev/null +++ b/.github/workflows/contracts.yml @@ -0,0 +1,81 @@ +name: Contracts CI + +on: + push: + branches: [main, develop] + paths: + - 'contracts/**' + - '.github/workflows/contracts.yml' + pull_request: + branches: [main, develop] + paths: + - 'contracts/**' + - '.github/workflows/contracts.yml' + +jobs: + test-contracts: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: wasm32-unknown-unknown + components: clippy, rustfmt + override: true + + - name: Cache Cargo registry + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + contracts/target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Run cargo test + working-directory: contracts/contracts/boxmeout + run: cargo test --features testutils + + - name: Run cargo clippy + working-directory: contracts/contracts/boxmeout + run: cargo clippy --all-targets --features testutils -- -D warnings + + - name: Check formatting + working-directory: contracts/contracts/boxmeout + run: cargo fmt -- --check + + - name: Build WASM contracts + working-directory: contracts/contracts/boxmeout + run: | + cargo build --release --target wasm32-unknown-unknown + ls -lh target/wasm32-unknown-unknown/release/*.wasm + + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin + + - name: Generate test coverage + working-directory: contracts/contracts/boxmeout + run: cargo tarpaulin --features testutils --out Xml --output-dir coverage + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: contracts/contracts/boxmeout/coverage/cobertura.xml + flags: contracts + name: contract-coverage + fail_ci_if_error: false + + - name: Upload WASM artifacts + uses: actions/upload-artifact@v4 + with: + name: contract-wasm + path: contracts/contracts/boxmeout/target/wasm32-unknown-unknown/release/*.wasm + retention-days: 7 From 43d346a35ee327ccf5ff66f09c318c91ea1c17a0 Mon Sep 17 00:00:00 2001 From: Ndifreke000 Date: Sat, 21 Feb 2026 16:44:39 +0100 Subject: [PATCH 2/5] fix(contracts): resolve all clippy warnings - Add allow attribute for too_many_arguments in market.rs - Replace deprecated events.publish with contractevent macro - Remove unused imports and variables in tests - Add allow(dead_code) for test helper functions --- contracts/contracts/boxmeout/src/market.rs | 19 ++++++++++--- contracts/contracts/boxmeout/src/oracle.rs | 27 ++++++++++++------- .../contracts/boxmeout/tests/factory_test.rs | 1 + .../boxmeout/tests/integration_test.rs | 7 +++-- .../contracts/boxmeout/tests/market_test.rs | 10 +++---- 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/contracts/contracts/boxmeout/src/market.rs b/contracts/contracts/boxmeout/src/market.rs index 005ae3e..7485e82 100644 --- a/contracts/contracts/boxmeout/src/market.rs +++ b/contracts/contracts/boxmeout/src/market.rs @@ -180,6 +180,7 @@ pub struct PredictionMarket; #[contractimpl] impl PredictionMarket { /// Initialize a single market instance + #[allow(clippy::too_many_arguments)] pub fn initialize( env: Env, market_id: BytesN<32>, @@ -1123,10 +1124,20 @@ impl PredictionMarket { .set(&Symbol::new(&env, MARKET_STATE_KEY), &STATE_CANCELLED); let timestamp = env.ledger().timestamp(); - env.events().publish( - (Symbol::new(&env, "MarketCancelled"),), - (market_id, creator, timestamp), - ); + + #[contractevent] + pub struct MarketCancelledEvent { + pub market_id: BytesN<32>, + pub creator: Address, + pub timestamp: u64, + } + + MarketCancelledEvent { + market_id, + creator, + timestamp, + } + .publish(&env); } // --- TEST HELPERS (Not for production use, but exposed for integration tests) --- diff --git a/contracts/contracts/boxmeout/src/oracle.rs b/contracts/contracts/boxmeout/src/oracle.rs index 26b2a43..bb79b71 100644 --- a/contracts/contracts/boxmeout/src/oracle.rs +++ b/contracts/contracts/boxmeout/src/oracle.rs @@ -948,16 +948,23 @@ impl OracleManager { .set(&Symbol::new(&env, LAST_OVERRIDE_TIME_KEY), ¤t_time); // 12. Emit EmergencyOverride event with all details - env.events().publish( - (Symbol::new(&env, "EmergencyOverride"),), - ( - market_id, - forced_outcome, - justification_hash, - approvers, - current_time, - ), - ); + #[contractevent] + pub struct EmergencyOverrideEvent { + pub market_id: BytesN<32>, + pub forced_outcome: u32, + pub justification_hash: BytesN<32>, + pub approvers: Vec
, + pub timestamp: u64, + } + + EmergencyOverrideEvent { + market_id, + forced_outcome, + justification_hash, + approvers, + timestamp: current_time, + } + .publish(&env); } /// Get emergency override record for a market (for audit purposes) diff --git a/contracts/contracts/boxmeout/tests/factory_test.rs b/contracts/contracts/boxmeout/tests/factory_test.rs index edeff16..8d7ecde 100644 --- a/contracts/contracts/boxmeout/tests/factory_test.rs +++ b/contracts/contracts/boxmeout/tests/factory_test.rs @@ -268,6 +268,7 @@ fn register_factory(env: &Env) -> Address { } // Helper to create a mock USDC token +#[allow(dead_code)] fn create_mock_token(env: &Env, admin: &Address) -> Address { let token_address = env.register_stellar_asset_contract_v2(admin.clone()); token_address.address() diff --git a/contracts/contracts/boxmeout/tests/integration_test.rs b/contracts/contracts/boxmeout/tests/integration_test.rs index 83424f7..ccfdf91 100644 --- a/contracts/contracts/boxmeout/tests/integration_test.rs +++ b/contracts/contracts/boxmeout/tests/integration_test.rs @@ -42,8 +42,8 @@ fn test_complete_prediction_flow() { let admin = Address::generate(&env); let usdc_token = Address::generate(&env); let _creator = Address::generate(&env); - let user1 = Address::generate(&env); - let user2 = Address::generate(&env); + let _user1 = Address::generate(&env); + let _user2 = Address::generate(&env); // Step 2: Initialize all contracts factory_client.initialize(&admin, &usdc_token, &treasury_id); @@ -120,8 +120,7 @@ fn test_complete_prediction_flow() { // let platform_fees = treasury_client.get_platform_fees(); // assert!(platform_fees > 0); - // Verify complete flow succeeded - assert!(true); // Placeholder until functions implemented + // Verify complete flow succeeded - test passes if no panics occurred } /// Integration test: Market creation and AMM trading flow diff --git a/contracts/contracts/boxmeout/tests/market_test.rs b/contracts/contracts/boxmeout/tests/market_test.rs index 435f0ea..6434af0 100644 --- a/contracts/contracts/boxmeout/tests/market_test.rs +++ b/contracts/contracts/boxmeout/tests/market_test.rs @@ -1,6 +1,6 @@ #![cfg(test)] -use boxmeout::market::{MarketError, MarketState, PredictionMarketClient}; +use boxmeout::market::{MarketError, PredictionMarketClient}; use soroban_sdk::{ testutils::{Address as _, Ledger, LedgerInfo}, token, Address, BytesN, Env, Symbol, @@ -156,7 +156,7 @@ fn test_market_initialize() { #[test] fn test_commit_prediction_happy_path() { let env = create_test_env(); - let (client, _market_id, _creator, admin, usdc_address, _market_contract) = + let (client, _market_id, _creator, _admin, usdc_address, _market_contract) = setup_test_market(&env); // Setup user with USDC balance @@ -205,7 +205,7 @@ fn test_commit_prediction_happy_path() { #[test] fn test_commit_prediction_duplicate_rejected() { let env = create_test_env(); - let (client, _market_id, _creator, admin, usdc_address, _market_contract) = + let (client, _market_id, _creator, _admin, usdc_address, _market_contract) = setup_test_market(&env); let user = Address::generate(&env); @@ -271,7 +271,7 @@ fn test_commit_prediction_negative_amount_rejected() { #[test] fn test_multiple_users_commit() { let env = create_test_env(); - let (client, _market_id, _creator, admin, usdc_address, _market_contract) = + let (client, _market_id, _creator, _admin, usdc_address, _market_contract) = setup_test_market(&env); let token = token::StellarAssetClient::new(&env, &usdc_address); @@ -912,5 +912,5 @@ fn test_get_market_state_serializable() { let _winning_outcome = state.winning_outcome; // If we got here, the struct is properly serializable - assert!(true); + // Verification complete } From 98fe200af3d13c3f373f3aa74994928dd8603ff8 Mon Sep 17 00:00:00 2001 From: Ndifreke000 Date: Sat, 21 Feb 2026 16:55:58 +0100 Subject: [PATCH 3/5] fix(contracts): run cargo fmt to fix whitespace --- contracts/contracts/boxmeout/src/market.rs | 4 ++-- contracts/contracts/boxmeout/src/oracle.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/contracts/boxmeout/src/market.rs b/contracts/contracts/boxmeout/src/market.rs index 7485e82..01aabd3 100644 --- a/contracts/contracts/boxmeout/src/market.rs +++ b/contracts/contracts/boxmeout/src/market.rs @@ -1124,14 +1124,14 @@ impl PredictionMarket { .set(&Symbol::new(&env, MARKET_STATE_KEY), &STATE_CANCELLED); let timestamp = env.ledger().timestamp(); - + #[contractevent] pub struct MarketCancelledEvent { pub market_id: BytesN<32>, pub creator: Address, pub timestamp: u64, } - + MarketCancelledEvent { market_id, creator, diff --git a/contracts/contracts/boxmeout/src/oracle.rs b/contracts/contracts/boxmeout/src/oracle.rs index bb79b71..1139d31 100644 --- a/contracts/contracts/boxmeout/src/oracle.rs +++ b/contracts/contracts/boxmeout/src/oracle.rs @@ -956,7 +956,7 @@ impl OracleManager { pub approvers: Vec
, pub timestamp: u64, } - + EmergencyOverrideEvent { market_id, forced_outcome, From 16a9c26bec9f72c7a4229895aeb7c89e630455d5 Mon Sep 17 00:00:00 2001 From: Ndifreke000 Date: Sat, 21 Feb 2026 17:05:02 +0100 Subject: [PATCH 4/5] fix(ci): correct WASM build artifact paths --- .github/workflows/contracts.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index 8762168..53c10c2 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -54,9 +54,12 @@ jobs: - name: Build WASM contracts working-directory: contracts/contracts/boxmeout + run: cargo build --release --target wasm32-unknown-unknown + + - name: List WASM artifacts run: | - cargo build --release --target wasm32-unknown-unknown - ls -lh target/wasm32-unknown-unknown/release/*.wasm + ls -lh contracts/target/wasm32-unknown-unknown/release/*.wasm + find contracts/target -name "*.wasm" -type f - name: Install cargo-tarpaulin run: cargo install cargo-tarpaulin @@ -77,5 +80,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: contract-wasm - path: contracts/contracts/boxmeout/target/wasm32-unknown-unknown/release/*.wasm + path: contracts/target/wasm32-unknown-unknown/release/boxmeout.wasm retention-days: 7 From 2e9cbcc6a0b415262bb8cfff711caa5c45c34ad8 Mon Sep 17 00:00:00 2001 From: Ndifreke000 Date: Sat, 21 Feb 2026 17:25:17 +0100 Subject: [PATCH 5/5] chore: make check-all.sh executable --- check-all.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 check-all.sh diff --git a/check-all.sh b/check-all.sh old mode 100644 new mode 100755