Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- name: Cache
uses: Swatinem/rust-cache@v2

- name: Format check
run: cargo fmt --all -- --check

- name: Clippy
run: cargo clippy --all-targets --all-features -- -D warnings

- name: Verify fee constants match smart contract
run: |
curl -sf https://raw.githubusercontent.com/Nodus-protocol/Nodus-Protocol-Smart-Contract/main/src/math.rs -o /tmp/sc_math.rs
extract_value() {
grep -E "^\s*pub const ${1}:" "$2" | sed -E 's/.*=\s*([0-9_]+)\s*;.*/\1/' | tr -d '_'
}
SC_FEE_NUM=$(extract_value FEE_NUMERATOR /tmp/sc_math.rs)
SC_FEE_DEN=$(extract_value FEE_DENOMINATOR /tmp/sc_math.rs)
SC_MIN_LIQ=$(extract_value MINIMUM_LIQUIDITY /tmp/sc_math.rs)
CE_FEE_NUM=$(extract_value FEE_NUMERATOR src/pool/math.rs)
CE_FEE_DEN=$(extract_value FEE_DENOMINATOR src/pool/math.rs)
CE_MIN_LIQ=$(extract_value MINIMUM_LIQUIDITY src/pool/math.rs)
echo "Smart Contract: FEE_NUMERATOR=$SC_FEE_NUM FEE_DENOMINATOR=$SC_FEE_DEN MINIMUM_LIQUIDITY=$SC_MIN_LIQ"
echo "Core Engine: FEE_NUMERATOR=$CE_FEE_NUM FEE_DENOMINATOR=$CE_FEE_DEN MINIMUM_LIQUIDITY=$CE_MIN_LIQ"
FAIL=0
if [ "$SC_FEE_NUM" != "$CE_FEE_NUM" ]; then
echo "::error::FEE_NUMERATOR mismatch: SmartContract=$SC_FEE_NUM CoreEngine=$CE_FEE_NUM"
FAIL=1
fi
if [ "$SC_FEE_DEN" != "$CE_FEE_DEN" ]; then
echo "::error::FEE_DENOMINATOR mismatch: SmartContract=$SC_FEE_DEN CoreEngine=$CE_FEE_DEN"
FAIL=1
fi
if [ "$SC_MIN_LIQ" != "$CE_MIN_LIQ" ]; then
echo "::error::MINIMUM_LIQUIDITY mismatch: SmartContract=$SC_MIN_LIQ CoreEngine=$CE_MIN_LIQ"
FAIL=1
fi
if [ "$FAIL" = "1" ]; then
echo "AMM constants have drifted between Smart Contract and Core Engine repos. Update src/pool/math.rs to match src/math.rs in Nodus-Protocol-Smart-Contract."
exit 1
fi
echo "AMM constants verified in sync with Smart Contract."
- name: Test
run: cargo test --all-features

- name: Build
run: cargo build --release
36 changes: 35 additions & 1 deletion src/pool/math.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
// AMM constant-product math — mirrors src/math.rs in the Smart Contract repo.
// Any formula change in the contract must be reflected here.
//
// SYNCHRONIZATION WARNING:
// These constants MUST match `src/math.rs` in the Nodus-Protocol-Smart-Contract
// repository exactly. CI verifies this automatically (see
// .github/workflows/ci.yml, "Verify fee constants match smart contract" step),
// which fetches the live Smart Contract source and fails the build on drift.
//
// If you change a value here, you must also change it in the Smart Contract
// repo in the same release cycle, or swap quotes from this engine will not
// match what the deployed contract actually executes.
//
// Tracking: see https://github.com/Nodus-protocol/Nodus-Protocol-Core-Engine/issues/61
// for the longer-term fix (a shared `nodus-amm-types` crate that both repos
// depend on, eliminating this manual sync requirement entirely).

pub const FEE_NUMERATOR: u128 = 997;
pub const FEE_DENOMINATOR: u128 = 1_000;
Expand Down Expand Up @@ -171,4 +184,25 @@ mod tests {
let large = price_impact_bps(500_000, 1_000_000);
assert!(large > small);
}

/// Defense-in-depth: pins the expected constant values directly in a test,
/// so a local `cargo test` run (not just CI's network-dependent fetch step)
/// catches an accidental change to these numbers before it's committed.
/// If you are intentionally changing the fee, update this test AND the
/// Smart Contract repo's src/math.rs in the same PR/release.
#[test]
fn fee_constants_match_known_smart_contract_values() {
assert_eq!(
FEE_NUMERATOR, 997,
"FEE_NUMERATOR must match Smart Contract src/math.rs"
);
assert_eq!(
FEE_DENOMINATOR, 1_000,
"FEE_DENOMINATOR must match Smart Contract src/math.rs"
);
assert_eq!(
MINIMUM_LIQUIDITY, 1_000,
"MINIMUM_LIQUIDITY must match Smart Contract src/math.rs"
);
}
}
Loading