Skip to content

Conversation

@junghoon-vans
Copy link
Member

@junghoon-vans junghoon-vans commented Jan 4, 2026

Summary by CodeRabbit

  • New Features

    • TWAP now returns harmonic mean liquidity alongside price for improved accuracy.
  • Bug Fixes

    • Tightened sqrtPrice validation to exclude the upper boundary to avoid edge-case invalid prices.
  • Documentation

    • Clarified protocol fee options and expanded security guidance (reentrancy, griefing, recovery).
  • Tests

    • Added extensive oracle, pool, position, and end-to-end scenario tests covering boundary and balance-conservation cases.

✏️ Tip: You can customize this high-level summary in your review settings.

Add extensive edge case test coverage for pool/v1 package:

- TestSafeAddInt64 (16 cases): int64 addition overflow/underflow
- TestSafeSubInt64 (18 cases): int64 subtraction overflow/underflow
- TestPosition_TickBoundaryEdgeCases (5 cases): MIN_TICK/MAX_TICK boundaries
- TestPosition_LiquidityBoundaryEdgeCases (4 cases): maxLiquidityPerTick limits
- TestPosition_AmountOverflowEdgeCases (1 case): TokensOwed overflow detection
@junghoon-vans junghoon-vans self-assigned this Jan 4, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 4, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno is excluded by !**/*filetest.gno

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Refactors oracle/twap to return harmonic mean liquidity, tightens sqrtPriceX96 upper-bound validation to be exclusive, simplifies position key API, renames observation cardinality to "Next", and updates many tests, mocks, proxies, and scenario files to match the new APIs and behavior.

Changes

Cohort / File(s) Change Summary
Price Validation Tightening
contract/r/gnoswap/pool/v1/factory_param.gno
Upper bound check for sqrtPriceX96 changed to exclude maxSqrtRatio (Gt → Gte); comment added documenting valid range as [minSqrtRatio, maxSqrtRatio).
Position Key Refactor
contract/r/gnoswap/pool/v1/position.gno, contract/r/gnoswap/pool/v1/pool.gno, ...*_test.gno
getPositionKey signature simplified to return string only; removed error handling at call sites and adjusted tests.
Cardinality → Next Rename
contract/r/gnoswap/pool/oracle.gno, contract/r/gnoswap/pool/v1/oracle.gno, contract/r/gnoswap/pool/v1/pool.gno, contract/r/gnoswap/pool/proxy.gno, contract/r/gnoswap/pool/v1/_helper_test.gno
Renamed cardinalityLimitcardinalityNext, CardinalityLimitCardinalityNext, SetCardinalityLimitSetCardinalityNext, and IncreaseObservationCardinalityLimitIncreaseObservationCardinalityNext across APIs, implementations, events and tests.
TWAP Return & Oracle Redesign
contract/r/gnoswap/pool/getter.gno, contract/r/gnoswap/pool/v1/getter.gno, contract/r/gnoswap/pool/types.gno, contract/r/gnoswap/pool/v1/oracle.gno, contract/r/gnoswap/pool/v1/getter.gno
Extended GetTWAP signature to (int32, *u256.Uint, error) to return harmonic mean liquidity; substantially refactored oracle internals (transform, observe, getTWAP), added binarySearch, revised interpolation and liquidity cumulatives, and added errObservationTooOld.
Test Removals / Additions
contract/r/gnoswap/pool/v1/factory_param_test.gno, contract/r/gnoswap/pool/v1/manager_test.gno, contract/r/gnoswap/pool/v1/pool_test.gno, contract/r/gnoswap/pool/v1/position_test.gno, contract/r/gnoswap/pool/v1/utils_test.gno
Removed TestValidateSqrtPriceX96; added extensive boundary/unit tests (sqrtPrice, feeProtocol, collectToken, safe int ops, position/amount edge cases) and updated many test call sites to new APIs.
Oracle Tests & Specs
contract/r/gnoswap/pool/v1/oracle_test.gno, contract/r/gnoswap/pool/v1/oracle_integration_test.gno, contract/r/gnoswap/pool/v1/oracle_spec_test.gno
Updated tests for three-value getTWAP return, renamed cardinality refs to Next, added harmonic-mean liquidity tests and broad oracle spec tests.
Mocks / Upgrade / Scenario Updates
contract/r/gnoswap/pool/v1/_helper_test.gno, contract/r/scenario/upgrade/..., contract/r/scenario/pool/*, contract/r/scenario/position/*
Updated mocks and upgrade wrappers to new GetTWAP signature and IncreaseObservationCardinalityNext; added many scenario filetests validating boundary alignment, TWAP behavior, and balance conservation; introduced BalanceSnapshot structs in scenario files.
API / Proxy / Getter Adjustments
contract/r/gnoswap/pool/proxy.gno, contract/r/gnoswap/pool/getter.gno, contract/r/gnoswap/pool/v1/getter.gno, contract/r/gnoswap/pool/v1/getter.gno
Propagated GetTWAP signature change through getters, proxies and interface/type definitions; adjusted proxy method names for cardinality.
Docs & Manager Comments
contract/r/gnoswap/pool/README.md, contract/r/gnoswap/pool/v1/README.md, contract/r/gnoswap/pool/v1/manager.gno
Updated protocol-fee description, added swap callback docs and security notes; expanded manager comment on fee protocol semantics (documentation-only).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Pool
    participant Oracle
    participant TWAPCalc

    Client->>Pool: GetTWAP(poolPath, secondsAgo)
    Pool->>Oracle: getTWAP(pool, secondsAgo)
    Oracle->>Oracle: validate secondsAgo (not too old)
    Oracle->>Oracle: locate observations (binarySearch / circular buffer)
    Oracle->>TWAPCalc: interpolate tick & compute cumulatives
    TWAPCalc->>TWAPCalc: calculate arithmetic mean tick
    TWAPCalc->>TWAPCalc: compute harmonic mean liquidity (secondsPerLiquidity)
    TWAPCalc->>Oracle: return (meanTick, harmonicMeanLiquidity)
    Oracle->>Pool: return (twapTick, harmonicMeanLiquidity, err)
    Pool->>Client: return (twapTick, harmonicMeanLiquidity, err)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'chore(pool): codebase maintenance and cleanup' is too vague and generic. It uses non-descriptive terms like 'maintenance and cleanup' that don't convey meaningful information about the specific changes made. Replace with a more specific title that captures the primary changes, such as 'chore(pool): refactor oracle API and position key handling' or similar to reflect the main architectural updates.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Add comprehensive edge case tests to existing test files:

pool_test.gno (+116 lines):
- TestCollectToken_EdgeCases (12 cases): collectToken function with various combinations of request/owed/balance amounts

manager_test.gno (+136 lines):
- TestSetFeeProtocol_BoundaryValues (13 cases): fee protocol validation including boundary values (0, 4-10, invalid ranges)

utils_test.gno (+82 lines):
- TestU256Min_EdgeCases (9 cases): u256Min utility function with zero, equal, and max values

Total: 34 new test cases covering previously untested edge cases
@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch from 15d991a to 5ca5130 Compare January 4, 2026 13:25
- factory_param.gno used `Gt(maxSqrtRatio)` (inclusive)
- tick_math.gno uses `Gte(maxSqrtRatio)` (exclusive)
- Pools at MAX_SQRT_RATIO passed validation but panicked in TickMathGetTickAtSqrtRatio

- Changed to `Gte(maxSqrtRatio)` to match half-open interval [MIN, MAX)
- Added CreatePool boundary value tests (MIN, MAX, MAX±1)
- Updated test expectations to reflect correct behavior

- Prevents invalid pools from being created at MAX_SQRT_RATIO
- Ensures validation matches actual usage in tick conversion
@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch from 1cf2965 to e551901 Compare January 5, 2026 05:06
@junghoon-vans junghoon-vans marked this pull request as ready for review January 5, 2026 05:21
Copy link

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno (1)

70-98: Consider adding positive assertions for Tests 1 and 2.

The current tests verify that MIN_SQRT_RATIO and MAX_SQRT_RATIO - 1 don't panic, which is appropriate for boundary regression testing. However, you could strengthen the validation by adding assertions that:

  • The pools were successfully created (e.g., verify pool exists)
  • The returned tick values are within expected ranges

This would provide more confidence that the boundary values are not just "not rejected" but actually produce valid results.

Example enhancement
// After line 82, add:
poolKey := pool.GetPoolKey(barPath, fooPath, 500)
if !pool.DoesPoolExist(poolKey) {
    println("[✗] Pool creation failed for MIN_SQRT_RATIO")
}

// After line 77, add:
if tickAtMin < common.MinTick() || tickAtMin > common.MaxTick() {
    println("[✗] Tick out of valid range for MIN_SQRT_RATIO")
}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e551901 and 5dec3e8.

📒 Files selected for processing (1)
  • contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (42)
  • GitHub Check: test-gnoswap (gnoswap/scenario/upgrade, contract/r/scenario/upgrade)
  • GitHub Check: test-gnoswap (gnoswap/scenario/halt, contract/r/scenario/halt)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/scenario/pool, contract/r/scenario/pool)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gns, contract/r/scenario/gns)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gov/staker, contract/r/scenario/gov/staker)
  • GitHub Check: test-gnoswap (gnoswap/pool, contract/r/gnoswap/pool)
  • GitHub Check: test-gnoswap (gnoswap/int256, contract/p/gnoswap/int256)
  • GitHub Check: test-gnoswap (gnoswap/gov/governance/v1, contract/r/gnoswap/gov/governance/v1)
  • GitHub Check: test-gnoswap (gnoswap/gov/xgns, contract/r/gnoswap/gov/xgns)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/launchpad, contract/r/scenario/launchpad)
  • GitHub Check: test-gnoswap (gnoswap/gns, contract/r/gnoswap/gns)
  • GitHub Check: test-gnoswap (gnoswap/gov/staker/v1, contract/r/gnoswap/gov/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/emission, contract/r/gnoswap/emission)
  • GitHub Check: test-gnoswap (gnoswap/position/v1, contract/r/gnoswap/position/v1)
  • GitHub Check: test-gnoswap (gnoswap/rbac, contract/r/gnoswap/rbac)
  • GitHub Check: test-gnoswap (gnoswap/referral, contract/r/gnoswap/referral)
  • GitHub Check: test-gnoswap (gnoswap/scenario/patterns/callback, contract/r/scenario/patterns/callback)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/staker, contract/r/gnoswap/staker)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gov/governance, contract/r/scenario/gov/governance)
  • GitHub Check: test-gnoswap (gnoswap/position, contract/r/gnoswap/position)
  • GitHub Check: test-gnoswap (gnoswap/scenario/protocol_fee, contract/r/scenario/protocol_fee)
  • GitHub Check: test-gnoswap (gnoswap/router/v1, contract/r/gnoswap/router/v1)
  • GitHub Check: test-gnoswap (gnoswap/gnft, contract/r/gnoswap/gnft)
  • GitHub Check: test-gnoswap (gnoswap/common, contract/r/gnoswap/common)
  • GitHub Check: test-gnoswap (gnoswap/launchpad/v1, contract/r/gnoswap/launchpad/v1)
  • GitHub Check: test-gnoswap (gnoswap/protocol_fee, contract/r/gnoswap/protocol_fee)
  • GitHub Check: test-gnoswap (gnoswap/scenario/emission, contract/r/scenario/emission)
  • GitHub Check: test-gnoswap (gnoswap/uint256, contract/p/gnoswap/uint256)
  • GitHub Check: test-gnoswap (gnoswap/version_manager, contract/p/gnoswap/version_manager)
  • GitHub Check: test-gnoswap (gnoswap/fuzz, contract/p/gnoswap/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/access, contract/r/gnoswap/access)
  • GitHub Check: test-gnoswap (gnoswap/gov/governance, contract/r/gnoswap/gov/governance)
  • GitHub Check: test-gnoswap (gnoswap/community_pool, contract/r/gnoswap/community_pool)
  • GitHub Check: test-gnoswap (gnoswap/rbac, contract/p/gnoswap/rbac)
  • GitHub Check: test-gnoswap (gnoswap/gnsmath, contract/p/gnoswap/gnsmath)
  • GitHub Check: run-integration-test
🔇 Additional comments (1)
contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno (1)

49-184: Well-structured regression test.

The test properly:

  • Uses testing.SetRealm() to establish admin context for pool creation
  • Uses cross keyword correctly for realm-crossing calls to gns.Approve() and pool.CreatePool()
  • Employs panic recovery blocks to verify boundary rejection behavior
  • Documents expected behavior and results with clear diagnostic output
  • Aligns with the PR objective of ensuring tick_math and pool validation share consistent boundary semantics

The half-open interval [MIN, MAX) validation is correctly verified across all four test cases.

@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch from 5dec3e8 to aa1e362 Compare January 5, 2026 09:11
Add scenario test to verify that pool.validateSqrtPriceX96 and
tick_math.TickMathGetTickAtSqrtRatio use consistent boundary validation.

Test Coverage:
- MIN_SQRT_RATIO (inclusive, should accept)
- MAX_SQRT_RATIO - 1 (valid, should accept)
- MAX_SQRT_RATIO (exclusive, should reject)
- MAX_SQRT_RATIO + 1 (invalid, should reject)

This regression test prevents future drift between the two validation
functions, ensuring they both use half-open interval [MIN, MAX).

Related: e551901
@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch 2 times, most recently from 94b627e to 37b63da Compare January 5, 2026 09:21
Verify that tick_math.TickMathGetTickAtSqrtRatio and pool validation
use consistent boundary checks for sqrtPriceX96 range.

Test cases:
- MIN_SQRT_RATIO (inclusive): Both should accept
- MAX_SQRT_RATIO - 1 (valid): Both should accept
- MAX_SQRT_RATIO (exclusive): Both should reject
- MAX_SQRT_RATIO + 1 (out of range): Both should reject
Copy link

@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.

Actionable comments posted: 4

♻️ Duplicate comments (1)
contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno (1)

27-27: Remove unused constant.

The maxApprove constant is defined but never used in the test.

🧹 Nitpick comments (4)
contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno (2)

78-97: Improve panic verification to catch false negatives.

The recover blocks only print messages when a panic occurs. If common.TickMathGetTickAtSqrtRatio or pool.CreatePool unexpectedly succeed (no panic), the test silently continues without verification. This could mask regressions where MAX_SQRT_RATIO boundary validation is inadvertently relaxed.

🔎 Proposed fix to verify panics occurred
 println("[INFO] Test 3: MAX_SQRT_RATIO (exclusive)")
+tickMathPanicked := false
 func() {
 	defer func() {
 		if r := recover(); r != nil {
 			println("[EXPECTED] tick_math rejected")
+			tickMathPanicked = true
 		}
 	}()
 	common.TickMathGetTickAtSqrtRatio(maxSqrtRatio)
 }()
+if !tickMathPanicked {
+	panic("tick_math should have rejected MAX_SQRT_RATIO")
+}

+poolPanicked := false
 func() {
 	defer func() {
 		if r := recover(); r != nil {
 			println("[EXPECTED] pool rejected")
+			poolPanicked = true
 		}
 	}()
 	gns.Approve(cross, poolAddr, poolCreationFee)
 	pool.CreatePool(cross, barPath, bazPath, 10000, MAX_SQRT_RATIO)
 }()
+if !poolPanicked {
+	panic("pool should have rejected MAX_SQRT_RATIO")
+}
 println()

99-118: Improve panic verification to catch false negatives.

Similar to Test 3, the recover blocks for MAX_SQRT_RATIO + 1 don't verify that panics actually occurred. If the validation unexpectedly passes, the test continues silently.

🔎 Proposed fix to verify panics occurred
 println("[INFO] Test 4: MAX_SQRT_RATIO + 1 (out of range)")
+tickMathPanicked := false
 func() {
 	defer func() {
 		if r := recover(); r != nil {
 			println("[EXPECTED] tick_math rejected")
+			tickMathPanicked = true
 		}
 	}()
 	common.TickMathGetTickAtSqrtRatio(maxPlusOne)
 }()
+if !tickMathPanicked {
+	panic("tick_math should have rejected MAX_SQRT_RATIO + 1")
+}

+poolPanicked := false
 func() {
 	defer func() {
 		if r := recover(); r != nil {
 			println("[EXPECTED] pool rejected")
+			poolPanicked = true
 		}
 	}()
 	gns.Approve(cross, poolAddr, poolCreationFee)
 	pool.CreatePool(cross, fooPath, quxPath, 100, maxPlusOne.ToString())
 }()
+if !poolPanicked {
+	panic("pool should have rejected MAX_SQRT_RATIO + 1")
+}
 println()
contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno (1)

301-302: Clarify the purpose of this error comment.

The trailing error comment lacks context. Is this an expected test failure, a known environment limitation, or a TODO to fix? Consider either:

  1. Adding a more descriptive comment explaining the expected behavior
  2. Removing it if it was debugging output
  3. Creating an issue to track if it's a bug

Would you like me to open an issue to track this permission error, or can you clarify the intended behavior?

contract/r/scenario/pool/swap_balance_extreme_filetest.gno (1)

179-193: Consider adding assertion checks for critical invariants.

While filetests primarily validate through output inspection, adding explicit panic-based assertions for critical invariants (like conservation laws) would make failures more obvious and prevent silent issues.

🔎 Example: Add assertion for conservation
 conservationBar := adminBarChange + poolBarChange
 conservationFoo := adminFooChange + poolFooChange
 println("[VERIFY] Conservation (CRITICAL):")
 println("[VERIFY] - Admin bar + Pool bar:", conservationBar, "(MUST be 0)")
 println("[VERIFY] - Admin foo + Pool foo:", conservationFoo, "(MUST be 0)")
+
+if conservationBar != 0 || conservationFoo != 0 {
+    panic("Conservation violated: tokens were created or destroyed")
+}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dec3e8 and 37b63da.

📒 Files selected for processing (4)
  • contract/r/scenario/pool/swap_balance_extreme_filetest.gno
  • contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno
  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
  • contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno
  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
  • contract/r/scenario/pool/swap_balance_extreme_filetest.gno
  • contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno
  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
  • contract/r/scenario/pool/swap_balance_extreme_filetest.gno
  • contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: test-gnoswap (gnoswap/uint256, contract/p/gnoswap/uint256)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/scenario/pool, contract/r/scenario/pool)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/halt, contract/r/scenario/halt)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/int256, contract/p/gnoswap/int256)
  • GitHub Check: run-integration-test
🔇 Additional comments (15)
contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno (2)

64-65: Correct realm crossing syntax.

All calls to gns.Approve and pool.CreatePool properly use the cross keyword for realm boundary crossing. This follows Gno best practices for crossing functions.

Also applies to: 73-74, 94-95, 115-116


31-35: Proper test realm setup.

The test correctly initializes the admin realm using testing.NewUserRealm and sets the caller context with testing.SetRealm, following Gno testing best practices. The boundary value calculations using uint256 arithmetic are appropriate.

Also applies to: 53-58

contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno (3)

1-46: LGTM!

The imports, constants, and global variables are appropriately structured for a filetest scenario. The use of blank imports for realm registration side effects and the defined price bounds are correct.


204-286: Test logic is well-structured.

The exact output swap test appropriately mirrors the exact input test structure. The verification of balance conservation and liquidity usage is comprehensive.


288-299: LGTM!

The mock swap callback correctly handles token transfers for positive deltas (tokens owed to the pool) and properly sets the admin realm context for authorization.

contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno (5)

1-46: LGTM!

The imports and constants are well-structured. The blank imports for side-effect initialization (rbac, pool/v1, protocol_fee/v1) are appropriate for filetest setup.


48-65: LGTM!

The main function provides clear orchestration of the test scenario with descriptive output for each phase.


67-96: LGTM!

The setup function properly manages realm context - using adminRealm for pool creation and token approvals, then switching to posRealm for minting. This follows the expected access control patterns where different roles perform their authorized operations.


98-179: LGTM!

The test function follows a clear pattern: capture before state, execute swap, capture after state, verify balance conservation. The verification logic correctly checks that adminChange + poolChange = 0 for both tokens, ensuring no tokens are created or destroyed.


181-258: LGTM!

The exact output swap test follows the same verification pattern as exact input, correctly using a negative amount ("-50000") to specify exact output semantics and validating the inverse price movement direction.

contract/r/scenario/pool/swap_balance_extreme_filetest.gno (5)

1-46: Test setup follows Gno testing patterns correctly.

The realm context setup using testing.NewUserRealm() and testing.NewCodeRealm() is appropriate for scenario testing. The constants and imports are well-organized for testing extreme swap scenarios.


71-104: Proper realm crossing and test context management.

The setup correctly uses testing.SetRealm() to switch execution context and applies the cross keyword for all realm-crossing function calls (CreatePool, Approve, Mint). This follows Gno testing best practices.


140-142: Callback realm parameter usage is correct.

The anonymous callback function properly declares the cur realm parameter, which is required for functions that cross realm boundaries. The callback then delegates to mockSwapCallback() which doesn't need the realm parameter since it's a helper function that uses testing.SetRealm() internally.


364-375: Mock callback implementation is appropriate for testing.

The callback correctly uses testing.SetRealm() to establish context and applies the cross keyword when calling SafeGRC20Transfer(). Returning nil unconditionally is acceptable for this test scenario.


377-378: This is a standard filetest format pattern. The // Error: comment documents the expected test output for the filetest execution. Multiple other filetest files in the repository (e.g., swap_balance_single_in_range_filetest.gno, swap_balance_mixed_positions_filetest.gno, tickmath_boundary_alignment_filetest.gno) use the same pattern to document expected errors or results. No clarification or removal is needed.

Likely an incorrect or invalid review comment.

Add scenario tests to verify token balance conservation during swaps:

- Single in-range position: Tests ExactInput/ExactOutput swaps with balance verification
- Mixed positions: Tests swaps with in-range and out-of-range positions
- Extreme price movements: Tests balance conservation under large swaps

Each test verifies:
- Balance conservation: admin + pool = 0 for each token
- Price movement: tick changes as expected
- Correct swap amounts
@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch from 37b63da to 9f5f9de Compare January 5, 2026 10:11
junghoon-vans and others added 4 commits January 5, 2026 19:41
Update protocol fee documentation to accurately reflect code validation:
- Change "0-10%" to "0% or 4-10%"
- Values 1, 2, 3 are not valid (enforced by isValidFeeProtocolValue)

Updated in both Configuration and Fee Mechanics sections.
Remove dead code from tickmath boundary alignment test.
The maxApprove constant was defined but never used.
Refactor pool scenario tests to follow router test conventions with
improved balance tracking and exact conservation validation.

Changes:
- Add BalanceSnapshot pattern with protocol fee tracking
- Implement exact balance conservation validation (user + pool + protocol fee = 0)
- Enable protocol fee with SetFeeProtocol in init()
- Use independent PKGPATHs for isolated test execution
- Simplify tickmath boundary test to avoid cross-realm panic issues

All tests now pass independently and as part of the full test suite.
Copy link

@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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno (1)

249-260: [CRITICAL] Realm Context: Remove testing.SetRealm from callback—causes permission denial

Line 250 calls testing.SetRealm(adminRealm) inside the callback while pool.Swap is executing under routerRealm (set at lines 153, 191). This realm context switch mid-execution breaks caller verification in GetTokenTeller(), causing "read permission denied" errors when SafeGRC20Transfer attempts token operations.

Problem: The callback inherits the cur realm parameter but immediately overwrites the realm context. Token transfer operations then fail because the realm identity no longer matches the approved caller context.

Fix:

 func mockSwapCallback(token0Path string, token1Path string, amount0Delta int64, amount1Delta int64) error {
-	testing.SetRealm(adminRealm)
-
 	if amount0Delta > 0 {
 		common.SafeGRC20Transfer(cross, token0Path, poolAddr, amount0Delta)
 	}
 	if amount1Delta > 0 {
 		common.SafeGRC20Transfer(cross, token1Path, poolAddr, amount1Delta)
 	}

 	return nil
 }

Rationale: The realm context is already correctly established before pool.Swap is called (lines 153, 191). The callback must preserve this context for token operations to succeed. Other working test scenarios (referenced in past reviews) omit testing.SetRealm() inside callbacks for this reason.

Based on past review comments, this issue has been flagged before and must be resolved.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37b63da and be7c731.

📒 Files selected for processing (6)
  • contract/r/gnoswap/pool/README.md
  • contract/r/gnoswap/pool/v1/README.md
  • contract/r/scenario/pool/swap_balance_extreme_filetest.gno
  • contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno
  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
  • contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno
🚧 Files skipped from review as they are similar to previous changes (3)
  • contract/r/scenario/pool/tickmath_boundary_alignment_filetest.gno
  • contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno
  • contract/r/scenario/pool/swap_balance_extreme_filetest.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/upgrade, contract/r/scenario/upgrade)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (3)
contract/r/gnoswap/pool/README.md (1)

12-12: Excellent clarification of Protocol Fee boundaries.

The updates from "0-10%" to "0% or 4-10%" correctly reflect the tightened validation rules and improve documentation clarity by explicitly excluding the intermediate range (1-3%). Both the Configuration section and Fee Mechanics section are consistently updated, ensuring readers understand that protocol fees can only be disabled (0%) or configured within 4-10%.

Also applies to: 190-190

contract/r/gnoswap/pool/v1/README.md (2)

12-12: Protocol fee constraint clarification looks good.

The update from "0-10%" to "0% or 4-10%" accurately reflects the tightened fee protocol options mentioned in the PR summary, providing clearer guidance on valid protocol fee configurations.


77-135: New Swap Callback documentation section is a valuable addition.

The comprehensive callback documentation covering signature, delta convention, direction examples, and security notes significantly improves developer guidance. The delta convention table (lines 88-92) and swap direction examples (lines 94-104) clearly explain the positive/negative semantics.

The security recommendation (line 131) to verify the caller is particularly important for preventing unauthorized callback invocations.

Update callback example to follow actual codebase patterns:
- Use common.SafeGRC20Transfer with cross keyword for realm-crossing calls
- Return error instead of panic for unauthorized caller
- Align with router/v1 implementation pattern
Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (5)
contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno (1)

32-33: Unused variable routerAddr.

The routerAddr variable is declared but never used in this test file. Consider removing it to keep the code clean.

🔎 Proposed fix
 var (
 	adminAddr, _    = access.GetAddress(prbac.ROLE_ADMIN.String())
 	adminRealm      = testing.NewUserRealm(adminAddr)
 	positionAddr, _ = access.GetAddress(prbac.ROLE_POSITION.String())
 	posRealm        = testing.NewUserRealm(positionAddr)
-	routerAddr, _   = access.GetAddress(prbac.ROLE_ROUTER.String())
 	poolAddr, _     = access.GetAddress(prbac.ROLE_POOL.String())

 	poolCreationFee int64 = 100_000_000
 )
contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno (1)

38-39: Unused variable routerAddr.

Similar to the other scenario file, routerAddr is declared but never used. Consider removing it.

🔎 Proposed fix
 var (
 	adminAddr, _    = access.GetAddress(prbac.ROLE_ADMIN.String())
 	adminRealm      = testing.NewUserRealm(adminAddr)
 	positionAddr, _ = access.GetAddress(prbac.ROLE_POSITION.String())
 	posRealm        = testing.NewUserRealm(positionAddr)
-	routerAddr, _   = access.GetAddress(prbac.ROLE_ROUTER.String())
 	poolAddr, _     = access.GetAddress(prbac.ROLE_POOL.String())

 	poolCreationFee int64 = 100_000_000
 )
contract/r/gnoswap/pool/v1/README.md (2)

87-92: Markdown formatting: add blank lines around table.

The static analysis tool flagged that tables should be surrounded by blank lines for proper rendering.

🔎 Proposed fix
 **Delta Convention**:
+
 | Delta | Meaning |
 |-------|---------|
 | Positive (`> 0`) | Amount the pool must RECEIVE (input token) |
 | Negative (`< 0`) | Amount the pool has SENT (output token) |
+
 **Swap Direction Examples**:

159-164: Markdown formatting: fenced code blocks should specify a language.

Consider adding a language identifier (e.g., text or leave empty with triple backticks) for formula/pseudo-code blocks. This is a minor linting suggestion.

🔎 Proposed fix
 **Range Liquidity Formula**:
-```
+```text
 L = amount / (sqrt(upper) - sqrt(lower))        // current < lower
 L = amount * sqrt(current) / (upper - current)  // lower < current < upper
 L = amount / (sqrt(current) - sqrt(lower))      // current > upper
</details>

</blockquote></details>
<details>
<summary>contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno (1)</summary><blockquote>

`40-41`: **Unused variable `routerAddr`.**

Consistent with other scenario files, `routerAddr` is declared but not used.


<details>
<summary>🔎 Proposed fix</summary>

```diff
 var (
 	adminAddr, _    = access.GetAddress(prbac.ROLE_ADMIN.String())
 	adminRealm      = testing.NewUserRealm(adminAddr)
 	positionAddr, _ = access.GetAddress(prbac.ROLE_POSITION.String())
 	posRealm        = testing.NewUserRealm(positionAddr)
-	routerAddr, _   = access.GetAddress(prbac.ROLE_ROUTER.String())
 	poolAddr, _     = access.GetAddress(prbac.ROLE_POOL.String())

 	poolCreationFee int64 = 100_000_000
 )
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be7c731 and c208d83.

📒 Files selected for processing (6)
  • contract/r/gnoswap/pool/README.md
  • contract/r/gnoswap/pool/v1/README.md
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
🚧 Files skipped from review as they are similar to previous changes (1)
  • contract/r/gnoswap/pool/README.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
🪛 markdownlint-cli2 (0.18.1)
contract/r/gnoswap/pool/v1/README.md

88-88: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)


160-160: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


183-183: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/position/v1, contract/r/gnoswap/position/v1)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/launchpad/v1, contract/r/gnoswap/launchpad/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (7)
contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno (2)

58-158: LGTM!

The test scenario is well-structured with clear logging, proper realm switching for different operations, and a logical flow: pool creation → liquidity provision → time skip → swap → time skip → TWAP queries. The test covers the intended use case of verifying TWAP calculations across price changes.


160-171: LGTM!

The mock callback correctly handles the delta convention (positive = amount pool must receive) and uses SafeGRC20Transfer for token transfers. Appropriate for a test scenario.

contract/r/scenario/pool/oracle_twap_basic_filetest.gno (1)

55-132: LGTM!

This basic TWAP scenario provides good coverage for the steady-state case where no price changes occur. The test correctly verifies that TWAP remains at tick 0 after 4 hours of elapsed time without any swaps.

contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno (1)

55-194: LGTM!

This test effectively validates TWAP behavior with increased observation cardinality. The bidirectional swaps (token0→token1, then token1→token0) and shorter time intervals provide good coverage for the cardinality feature.

contract/r/gnoswap/pool/v1/README.md (1)

107-127: LGTM!

The callback example has been improved:

  • Uses SafeGRC20Transfer with the cross keyword for realm-crossing calls
  • Returns an error for unauthorized callers (consistent with the function's error return type)
  • Clear documentation of the security check and delta handling
contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno (2)

57-245: LGTM!

This is the most comprehensive TWAP scenario test, covering:

  • Cardinality increase (200 slots)
  • Multiple swaps in both directions
  • Additional position minting mid-test
  • TWAP queries across various intervals

The test effectively validates that the TWAP oracle correctly weights price changes and handles liquidity modifications.


247-258: LGTM!

The mock callback follows the same pattern as other scenario files, maintaining consistency across the test suite.

@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch from 6de17b8 to bc75d51 Compare January 6, 2026 05:55
Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (3)
contract/r/gnoswap/pool/v1/README.md (1)

208-241: Pool Creation Griefing recovery mechanism is well-documented and actionable.

The expanded section clearly explains the problem, impact, recovery strategy, and prevention measures. The atomic recovery example (lines 227–235) is practical and helps readers understand the arbitrage-as-recovery pattern. The mention that gas and protocol fees are the net cost is transparent.

One minor enhancement: consider noting that the "wide-range liquidity" mentioned in step 1 should be defined more specifically (e.g., "full range from min to max tick" or "as wide as economically feasible given the distorted price").

contract/r/scenario/pool/oracle_twap_basic_filetest.gno (1)

43-49: Unused constants: halfBlocks and halfSeconds.

These constants are defined but not used in this file. They appear to be shared across TWAP test scenarios for consistency.

Consider removing unused constants or extracting shared constants to a common test utilities package if multiple filetest scenarios need them.

contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno (1)

32-33: Unused variable: routerAddr.

routerAddr is retrieved but never used. The swap operation uses testing.NewCodeRealm("gno.land/r/gnoswap/router") directly instead.

🔎 Proposed fix
 var (
 	adminAddr, _    = access.GetAddress(prbac.ROLE_ADMIN.String())
 	adminRealm      = testing.NewUserRealm(adminAddr)
 	positionAddr, _ = access.GetAddress(prbac.ROLE_POSITION.String())
 	posRealm        = testing.NewUserRealm(positionAddr)
-	routerAddr, _   = access.GetAddress(prbac.ROLE_ROUTER.String())
 	poolAddr, _     = access.GetAddress(prbac.ROLE_POOL.String())
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c208d83 and 2d894f7.

📒 Files selected for processing (7)
  • contract/r/gnoswap/pool/README.md
  • contract/r/gnoswap/pool/v1/README.md
  • contract/r/gnoswap/pool/v1/manager.gno
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
✅ Files skipped from review due to trivial changes (1)
  • contract/r/gnoswap/pool/v1/manager.gno
🚧 Files skipped from review as they are similar to previous changes (3)
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/gnoswap/pool/README.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (47)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gns, contract/r/scenario/gns)
  • GitHub Check: test-gnoswap (gnoswap/scenario/rbac, contract/r/scenario/rbac)
  • GitHub Check: test-gnoswap (gnoswap/scenario/launchpad, contract/r/scenario/launchpad)
  • GitHub Check: test-gnoswap (gnoswap/scenario/protocol_fee, contract/r/scenario/protocol_fee)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/halt, contract/r/scenario/halt)
  • GitHub Check: test-gnoswap (gnoswap/scenario/patterns/callback, contract/r/scenario/patterns/callback)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/referral, contract/r/gnoswap/referral)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gov/staker, contract/r/scenario/gov/staker)
  • GitHub Check: test-gnoswap (gnoswap/router, contract/r/gnoswap/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gov/governance, contract/r/scenario/gov/governance)
  • GitHub Check: test-gnoswap (gnoswap/scenario/pool, contract/r/scenario/pool)
  • GitHub Check: test-gnoswap (gnoswap/scenario/emission, contract/r/scenario/emission)
  • GitHub Check: test-gnoswap (gnoswap/gov/governance/v1, contract/r/gnoswap/gov/governance/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/staker, contract/r/gnoswap/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/upgrade, contract/r/scenario/upgrade)
  • GitHub Check: test-gnoswap (gnoswap/position/v1, contract/r/gnoswap/position/v1)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/pool, contract/r/gnoswap/pool)
  • GitHub Check: test-gnoswap (gnoswap/gov/staker, contract/r/gnoswap/gov/staker)
  • GitHub Check: test-gnoswap (gnoswap/rbac, contract/r/gnoswap/rbac)
  • GitHub Check: test-gnoswap (gnoswap/protocol_fee, contract/r/gnoswap/protocol_fee)
  • GitHub Check: test-gnoswap (gnoswap/launchpad/v1, contract/r/gnoswap/launchpad/v1)
  • GitHub Check: test-gnoswap (gnoswap/protocol_fee/v1, contract/r/gnoswap/protocol_fee/v1)
  • GitHub Check: test-gnoswap (gnoswap/gns, contract/r/gnoswap/gns)
  • GitHub Check: test-gnoswap (gnoswap/halt, contract/r/gnoswap/halt)
  • GitHub Check: test-gnoswap (gnoswap/position, contract/r/gnoswap/position)
  • GitHub Check: test-gnoswap (gnoswap/gov/staker/v1, contract/r/gnoswap/gov/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/launchpad, contract/r/gnoswap/launchpad)
  • GitHub Check: test-gnoswap (gnoswap/gov/governance, contract/r/gnoswap/gov/governance)
  • GitHub Check: test-gnoswap (gnoswap/gov/xgns, contract/r/gnoswap/gov/xgns)
  • GitHub Check: test-gnoswap (gnoswap/community_pool, contract/r/gnoswap/community_pool)
  • GitHub Check: test-gnoswap (gnoswap/common, contract/r/gnoswap/common)
  • GitHub Check: test-gnoswap (gnoswap/gnsmath, contract/p/gnoswap/gnsmath)
  • GitHub Check: test-gnoswap (gnoswap/gnft, contract/r/gnoswap/gnft)
  • GitHub Check: test-gnoswap (gnoswap/int256, contract/p/gnoswap/int256)
  • GitHub Check: test-gnoswap (gnoswap/uint256, contract/p/gnoswap/uint256)
  • GitHub Check: test-gnoswap (gnoswap/emission, contract/r/gnoswap/emission)
  • GitHub Check: test-gnoswap (gnoswap/rbac, contract/p/gnoswap/rbac)
  • GitHub Check: test-gnoswap (gnoswap/access, contract/r/gnoswap/access)
  • GitHub Check: test-gnoswap (gnoswap/store, contract/p/gnoswap/store)
  • GitHub Check: test-gnoswap (gnoswap/fuzz, contract/p/gnoswap/fuzz)
  • GitHub Check: run-integration-test
🔇 Additional comments (10)
contract/r/gnoswap/pool/v1/README.md (3)

77-135: Excellent callback documentation improvements addressing previous feedback.

The Swap Callback section is comprehensive and now correctly implements the guidance from the prior review:

  • Uses error returns instead of panics (line 114) ✓
  • Includes the cross keyword for realm-crossing transfers (lines 119, 123) ✓
  • Provides clear Delta Convention table and practical examples ✓
  • Emphasizes caller verification for security ✓

One minor discrepancy to clarify: the callback signature shown on line 84 (_ *pool.CallbackMarker parameter) doesn't match the example implementation on line 108 (which omits it). If the CallbackMarker is genuinely optional or filtered during marshaling, consider adding an explanatory comment. If it's required, the example should include the parameter (even if unused via _).

Please confirm whether the CallbackMarker parameter is actually part of the callback signature or if it's documentation of the internal marshaling behavior. If it's part of the signature, update line 108 to match line 84.


12-12: Protocol fee description is now consistent.

Lines 12 and 188–192 now both state "0% or 4–10% of swap fees" (with denominator 4–10), which aligns well with the tightened validation mentioned in the PR objectives. This clarity helps operators understand fee configurability.

Also applies to: 188-192


29-29: Security-conscious documentation: price range and validation clarity.

Line 29 adds "Liquidity activated only when price in range" to the Tick System, reinforcing the core mechanism. Line 41 transparently states "No price validation performed (see Security Considerations)", which is important given the Pool Creation Griefing section discusses this risk. This honest framing helps developers make informed decisions about when to use price validation externally.

Also applies to: 41-41

contract/r/scenario/pool/oracle_twap_basic_filetest.gno (3)

1-21: LGTM - Clean imports and package setup for filetest scenario.

The imports are well-organized, with side-effect imports for realm initialization (_ "gno.land/r/gnoswap/pool/v1" and _ "gno.land/r/gnoswap/protocol_fee/v1") appropriately used for ensuring the realms are loaded.


55-132: Well-structured TWAP test scenario with clear documentation.

The test follows a logical flow:

  1. Pool creation with proper realm context
  2. Liquidity minting with position realm
  3. Initial TWAP query (0 seconds ago)
  4. Time skip simulation
  5. Multiple TWAP queries at different intervals

The use of testing.SkipHeights() to simulate time passage is appropriate for testing time-dependent oracle behavior.


134-169: Expected output documentation is comprehensive.

The output block clearly documents the expected TWAP behavior, confirming that the tick remains at 0 throughout since no price-changing swaps occurred. This serves as both documentation and verification.

contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno (4)

1-21: LGTM - Imports match the basic TWAP test with same dependencies.

The import structure is consistent with the basic TWAP test file, which is good for maintainability across the test suite.


104-126: Swap execution with proper realm context and callback handling.

The swap is correctly executed with the router realm context, and the callback function handles the token transfers. The use of minPrice as the price limit for a zeroForOne=true swap is correct (pushing price down to minimum).


160-171: Callback implementation is correct for test purposes.

The mockSwapCallback properly handles token transfers based on the delta signs. Setting the realm to adminRealm before transfers ensures the caller has the necessary token approvals to complete the swap.

Note: In production callback implementations, this realm manipulation wouldn't be possible, but it's appropriate for test scenarios.


198-208: Expected TWAP values correctly demonstrate weighted averaging.

The expected outputs show proper TWAP behavior:

  • 1h TWAP (-1906): Reflects only the post-swap price since the swap happened 2 hours ago
  • 2h TWAP (-1906): Matches the post-swap tick since the price change occurred exactly 2 hours ago
  • 4h TWAP (-953): Correctly weighted average of tick 0 (2h) and tick -1906 (2h), yielding approximately -953

This validates the oracle's time-weighted price calculation.

@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch 2 times, most recently from cae36db to 7d3f5f0 Compare January 6, 2026 10:36
Copy link

@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.

Actionable comments posted: 1

🧹 Nitpick comments (7)
contract/r/gnoswap/pool/v1/README.md (3)

87-92: Add blank lines around table (markdown lint MD058).

Tables should be surrounded by blank lines for proper markdown formatting. Add a blank line before line 88 (before the | header) to comply with markdown style guidelines.

🔎 Proposed formatting fix
 **Delta Convention**:
+
 | Delta | Meaning |
 |-------|---------|
 | Positive (`> 0`) | Amount the pool must RECEIVE (input token) |
 | Negative (`< 0`) | Amount the pool has SENT (output token) |
+

160-160: Add language identifier to fenced code block (markdown lint MD040).

The liquidity math formula code block is missing a language specifier. Use ```text or ```go depending on whether this is pseudocode or Go code.

🔎 Proposed fix
-L = amount / (sqrt(upper) - sqrt(lower))        // current < lower
+L = amount / (sqrt(upper) - sqrt(lower))        // current < lower
 L = amount * sqrt(current) / (upper - current)  // lower < current < upper
 L = amount / (sqrt(current) - sqrt(lower))      // current > upper
+```

Note: Replace opening fence on line 160 from ``` to ```text (or ```go if representing executable code).


183-183: Add language identifier to fenced code block (markdown lint MD040).

The fee calculation code block is missing a language specifier. Use ```text for pseudocode representation.

🔎 Proposed fix
-```
+```text
 fees = feeGrowthInside * liquidity
 feeGrowthInside = feeGrowthGlobal - feeGrowthOutside
-```
+```
contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno (1)

36-41: Consider checking errors from access.GetAddress in test setup.

The error returns from access.GetAddress are discarded. While this test assumes successful setup, explicitly checking these errors would make test failures more debuggable if the access configuration is incorrect.

Optional improvement for test robustness
 var (
-	adminAddr, _    = access.GetAddress(prbac.ROLE_ADMIN.String())
+	adminAddr       = mustGetAddress(prbac.ROLE_ADMIN.String())
 	adminRealm      = testing.NewUserRealm(adminAddr)
-	positionAddr, _ = access.GetAddress(prbac.ROLE_POSITION.String())
+	positionAddr    = mustGetAddress(prbac.ROLE_POSITION.String())
 	posRealm        = testing.NewUserRealm(positionAddr)
-	routerAddr, _   = access.GetAddress(prbac.ROLE_ROUTER.String())
-	poolAddr, _     = access.GetAddress(prbac.ROLE_POOL.String())
+	routerAddr      = mustGetAddress(prbac.ROLE_ROUTER.String())
+	poolAddr        = mustGetAddress(prbac.ROLE_POOL.String())

 	poolCreationFee int64 = 100_000_000
 )
+
+func mustGetAddress(role string) address {
+	addr, err := access.GetAddress(role)
+	if err != nil {
+		panic("setup failed: " + err.Error())
+	}
+	return addr
+}
contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno (1)

28-36: Consider checking errors from access.GetAddress in test setup.

Similar to the other TWAP tests, the error returns are discarded. For improved test debuggability, consider validating these during setup.

contract/r/scenario/pool/oracle_twap_basic_filetest.gno (1)

28-35: Consider checking errors from access.GetAddress in test setup.

For consistency with the recommendation on the other TWAP tests, consider validating setup errors.

contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno (1)

36-44: Consider checking errors from access.GetAddress in test setup.

Consistent with the other TWAP scenario tests, validating these errors would improve debuggability.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d894f7 and 7d3f5f0.

📒 Files selected for processing (7)
  • contract/r/gnoswap/pool/README.md
  • contract/r/gnoswap/pool/v1/README.md
  • contract/r/gnoswap/pool/v1/manager.gno
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
🚧 Files skipped from review as they are similar to previous changes (1)
  • contract/r/gnoswap/pool/v1/manager.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
🪛 markdownlint-cli2 (0.18.1)
contract/r/gnoswap/pool/v1/README.md

88-88: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)


160-160: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


183-183: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gns, contract/r/scenario/gns)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/scenario/pool, contract/r/scenario/pool)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/upgrade, contract/r/scenario/upgrade)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/launchpad/v1, contract/r/gnoswap/launchpad/v1)
  • GitHub Check: test-gnoswap (gnoswap/gov/staker/v1, contract/r/gnoswap/gov/staker/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (13)
contract/r/gnoswap/pool/README.md (4)

41-41: Update or remove outdated price validation note.

The note states "No price validation performed," but the PR objective is to tighten sqrtPrice upper-bound validation (making it exclusive). If validation is now enforced, this note should be updated to reflect the new validation behavior and boundaries.

Can you confirm whether price validation is now performed during pool creation, and update the note accordingly if validation is active?


12-12: Configuration and concept updates are clear.

The Protocol Fee range, Max Liquidity Per Tick, and Tick System clarifications are well-defined and align with the validation changes mentioned in the PR objectives.

Also applies to: 16-16, 29-29


77-134: Comprehensive Swap Callback documentation is well-structured.

The new Swap Callback section provides clear guidance on:

  • Callback signature and delta convention (positive/negative meanings)
  • Concrete examples for both swap directions (zeroForOne cases)
  • Implementation example using SafeGRC20Transfer for security
  • Important notes on caller verification, minimum delta transfers, and balance validation

This aligns well with the behavioral changes (error handling vs. panic) and security improvements (SafeGRC20Transfer usage) mentioned in the enriched summary.


188-193: Protocol fees documentation is now clearer.

The update clarifying "Optional 0% or 4-10% of swap fees" with the denominator range (4-10) provides more precision than before, making it easier for integrators to understand the fee configuration options.

contract/r/gnoswap/pool/v1/README.md (2)

108-127: Previous callback issues have been successfully fixed.

The callback implementation example has been corrected from the past review:

  • Line 114: Error handling now returns errors.New() instead of using panic()
  • Lines 119, 123: Calls to common.SafeGRC20Transfer now include the cross keyword for proper realm-crossing calls

These corrections align the example with Gno language requirements and proper error handling patterns.


208-241: Pool Creation Griefing section is well-documented with clear recovery mechanism.

The expanded security section now provides:

  • Clear problem statement and impact analysis
  • Concrete recovery mechanism with step-by-step example
  • Prevention strategies and deterrents

This documentation helps developers understand both the risk and mitigation strategies.

contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno (2)

198-209: LGTM: Proper realm context handling in swap callback.

The mockSwapCallback correctly sets the realm to adminRealm before performing token transfers, which is necessary for the SafeGRC20Transfer calls to succeed with proper authorization context.


57-196: LGTM: Test follows proper Gno patterns.

The test demonstrates correct usage of:

  • testing.SetRealm() to establish caller context
  • cross keyword for realm-crossing function calls
  • testing.SkipHeights() for time simulation
  • Structured scenario logging for clear test output

The test flow logically validates TWAP calculations with increased observation cardinality over multiple time intervals and price changes.

contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno (2)

160-171: LGTM: Proper realm context handling in swap callback.

The callback correctly establishes realm context before token transfers.


58-158: LGTM: Test validates TWAP behavior with price changes.

This scenario correctly tests TWAP weighted averaging by:

  • Maintaining stable price (tick 0) for 2 hours
  • Executing a price-changing swap at the midpoint
  • Maintaining new price for another 2 hours
  • Querying TWAP over 1h, 2h, and 4h intervals

The expected output shows TWAP correctly reflects the weighted average (-953 over 4h = average of 0 for 2h and -1906 for 2h).

contract/r/scenario/pool/oracle_twap_basic_filetest.gno (1)

55-132: LGTM: Clean baseline TWAP test.

This test establishes the baseline TWAP behavior when price remains stable:

  • Creates pool at tick 0
  • Adds liquidity
  • Advances time without any swaps
  • Validates TWAP remains at tick 0 across all query intervals

This provides a good foundation for understanding TWAP behavior before testing more complex scenarios with price changes.

contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno (2)

247-258: LGTM: Proper realm context handling in swap callback.

The callback implementation correctly sets realm context before performing token transfers.


57-245: LGTM: Comprehensive TWAP scenario test.

This test provides excellent coverage of TWAP behavior under realistic conditions:

  • Increased observation cardinality (200 slots)
  • Multiple price-changing swaps over 4 hours
  • Mid-scenario liquidity addition
  • TWAP queries at 1h, 2h, and 4h intervals

The scenario validates that TWAP correctly computes weighted averages across:

  • Multiple price levels (tick 0 → -976 → -560 → -818)
  • Changes in liquidity depth
  • Extended time periods with sufficient observation slots

The expected outputs demonstrate proper TWAP calculation: -818 (1h), -689 (2h), -589 (4h), showing the weighted contribution of earlier price levels diminishing over longer windows.

Copy link

@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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contract/r/gnoswap/pool/v1/position_test.gno (1)

104-138: Possible compile error: undeclared err variable.

Line 119 uses err = but err was never declared in this scope. The previous version likely had positionKey, err := getPositionKey(...) which declared err, but with the signature change, err is no longer declared.

🐛 Proposed fix
 	// Create position through modifyPosition
 	liquidityDelta := i256.MustFromDecimal(scenario.liquidity)
 	params := ModifyPositionParams{
 		owner:          scenario.owner,
 		tickLower:      scenario.tickLower,
 		tickUpper:      scenario.tickUpper,
 		liquidityDelta: liquidityDelta,
 	}

-	_, amount0, amount1, err = modifyPosition(pool, params)
+	_, amount0, amount1, err := modifyPosition(pool, params)
 	if err != nil {
 		t.Fatalf("Failed to create position: %v", err)
 	}
🧹 Nitpick comments (1)
contract/r/gnoswap/pool/v1/position.gno (1)

34-42: Documentation inconsistency with new function signature.

The doc comment on line 34 still states "Returns base64-encoded position key or error" but the function now returns only a string. The signature simplification is appropriate since strconv.Itoa and base64.StdEncoding.EncodeToString have no failure modes for these inputs.

📝 Suggested doc update
-// Returns base64-encoded position key or error.
+// Returns base64-encoded position key.
 // Combines position package path, tickLower, and tickUpper.
 func getPositionKey(
 	tickLower int32,
 	tickUpper int32,
-) string {
+) string {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c78dc6 and 3c0c469.

📒 Files selected for processing (5)
  • contract/r/gnoswap/pool/v1/pool.gno
  • contract/r/gnoswap/pool/v1/pool_test.gno
  • contract/r/gnoswap/pool/v1/position.gno
  • contract/r/gnoswap/pool/v1/position_test.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
🚧 Files skipped from review as they are similar to previous changes (2)
  • contract/r/gnoswap/pool/v1/pool.gno
  • contract/r/gnoswap/pool/v1/pool_test.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/gnoswap/pool/v1/position.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
  • contract/r/gnoswap/pool/v1/position_test.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/gnoswap/pool/v1/position.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
  • contract/r/gnoswap/pool/v1/position_test.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/emission, contract/r/scenario/emission)
  • GitHub Check: run-integration-test
🔇 Additional comments (8)
contract/r/gnoswap/pool/v1/position.gno (1)

378-378: LGTM!

The call site correctly updated to use the simplified single-return signature. The position key generation is now cleaner without unnecessary error handling.

contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno (3)

32-37: LGTM!

Constants for price bounds and max int64 are correctly defined for use in the scenario tests.


366-376: LGTM!

The mock swap callback correctly handles token transfers based on swap direction. Setting the admin realm inside the callback enables cross-realm token transfers as expected in the test context.


181-193: Test expectations contradict tick deletion logic.

The test expects to query tick 9000 after its liquidity is fully removed, but the implementation deletes ticks when they flip from non-zero to zero liquidity. In position.gno (lines 393-397), when liquidityDelta.IsNeg() and flippedLower=true, deleteTick(p, positionParams.tickLower) is called. Subsequently, GetTickInitialized() calls mustGetTick(), which panics if the tick doesn't exist.

This test will panic at line 189 when querying tick 9000. Either:

  1. Remove this check (deleted ticks cannot be queried)
  2. Or modify the implementation to persist ticks with zero liquidity for fee tracking (standard in Uniswap v3)

If persistence is intended, document why ticks remain initialized despite zero liquidity.

contract/r/gnoswap/pool/v1/position_test.gno (4)

248-261: LGTM!

The test correctly adapted to use the single-return getPositionKey signature. The added uniqueness verification (altKey comparison) is a good test enhancement ensuring different tick ranges produce different position keys.


1411-1446: LGTM!

Good addition of boundary edge case tests for tick extremes (MIN_TICK, MAX_TICK, full range). The tests verify that getPositionKey generates valid keys at tick boundaries.


1448-1517: LGTM!

Comprehensive liquidity boundary testing covering minimum values, large values near maxLiquidityPerTick, and overflow scenarios. The tests properly verify both success and panic conditions.


1519-1556: LGTM!

Good addition of overflow edge case testing for TokensOwed values. The test properly verifies that amounts are calculated and tokens owed accumulate correctly during the position lifecycle.

@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch from 3c0c469 to 2d2f135 Compare January 8, 2026 13:20
Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
contract/r/gnoswap/pool/v1/position.gno (1)

19-43: Update documentation to match the simplified function signature.

The documentation on line 34 states "Returns base64-encoded position key or error" but the function now only returns a string (line 39). Update the documentation to reflect that this function always succeeds and returns only the position key.

📝 Suggested documentation fix
 // getPositionKey generates a unique base64-encoded key for a liquidity position.
 //
 // Creates deterministic identifier for position tracking.
 // Ensures unique positions per (position package path, price range) combination.
 //
 // Used internally for position state management.
 //
 // Parameters:
 //   - tickLower: Lower boundary tick of position range
 //   - tickUpper: Upper boundary tick of position range
 //
 // Key Format:
 //
 //	base64(position_package_path + "__" + tickLower + "__" + tickUpper)
 //
-// Returns base64-encoded position key or error.
-// Combines position package path, tickLower, and tickUpper.
+// Returns:
+//   - string: base64-encoded position key
+//
+// Note: Tick validation must be performed by the caller before invoking this function.
 func getPositionKey(
 	tickLower int32,
 	tickUpper int32,
 ) string {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c0c469 and 2d2f135.

📒 Files selected for processing (5)
  • contract/r/gnoswap/pool/v1/pool.gno
  • contract/r/gnoswap/pool/v1/pool_test.gno
  • contract/r/gnoswap/pool/v1/position.gno
  • contract/r/gnoswap/pool/v1/position_test.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/gnoswap/pool/v1/position.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
  • contract/r/gnoswap/pool/v1/pool_test.gno
  • contract/r/gnoswap/pool/v1/position_test.gno
  • contract/r/gnoswap/pool/v1/pool.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/gnoswap/pool/v1/position.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
  • contract/r/gnoswap/pool/v1/pool_test.gno
  • contract/r/gnoswap/pool/v1/position_test.gno
  • contract/r/gnoswap/pool/v1/pool.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test-gnoswap (gnoswap/scenario/upgrade, contract/r/scenario/upgrade)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/staker, contract/r/gnoswap/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (14)
contract/r/gnoswap/pool/v1/position.gno (1)

378-378: LGTM! Simplified position key generation.

The removal of error handling from getPositionKey calls is safe since tick validation occurs earlier in modifyPosition (line 230 via validateTicks). This simplification improves code readability.

contract/r/gnoswap/pool/v1/pool.gno (3)

157-157: LGTM! Consistent with simplified position key API.

Direct assignment reflects the updated getPositionKey signature that no longer returns an error.


211-211: LGTM! Consistent API usage.

Position key generation simplified across all call sites.


258-260: LGTM! More idiomatic error handling.

The short variable declaration pattern (if err := ...; err != nil) is more idiomatic and reduces variable scope.

contract/r/gnoswap/pool/v1/pool_test.gno (3)

638-641: LGTM! Improved boundary test clarity.

The explicit MAX_SQRT_RATIO value and exclusivity clarification improve test documentation. The boundary condition is now clearly documented as exclusive.


750-750: LGTM! Consistent with updated API.

Position key generation updated to match the new signature.


1023-1137: LGTM! Comprehensive edge case coverage for collectToken.

Excellent test coverage including:

  • Equal amounts scenario
  • Minimum value scenarios for each parameter
  • Zero value scenarios
  • MAX_UINT128 boundary cases
  • Small differences detection

This significantly improves the reliability of the token collection logic.

contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno (2)

1-97: LGTM! Well-structured scenario test with clear documentation.

This scenario test effectively validates the complex tick role transition behavior where a tick changes from lower to upper boundary. The step-by-step approach with clear logging makes it easy to understand and debug.


367-377: LGTM! Proper token transfer in swap callback.

The mockSwapCallback correctly uses SafeGRC20Transfer with the cross keyword for realm crossing and handles both zeroForOne directions appropriately.

contract/r/gnoswap/pool/v1/position_test.gno (5)

104-139: LGTM! Simplified position creation helper.

The removal of error handling from getPositionKey (line 108) is consistent with the API change and simplifies the test helper function.


250-263: LGTM! Improved test with uniqueness validation.

The addition of the uniqueness check (lines 255-258) is valuable - it verifies that different tick ranges produce different position keys, which is a critical invariant for the position tracking system.


1413-1447: LGTM! Comprehensive tick boundary edge case coverage.

These tests validate that MIN_TICK and MAX_TICK values are properly handled as position boundaries. The full range test (MIN_TICK to MAX_TICK) is particularly important for ensuring the system can handle the entire valid tick spectrum.


1449-1518: LGTM! Thorough liquidity boundary validation.

Excellent coverage of liquidity edge cases:

  • Minimum viable liquidity (1)
  • Liquidity near maxLiquidityPerTick boundary
  • Overflow detection when exceeding maxLiquidityPerTick

This prevents potential overflow attacks and ensures proper liquidity constraints.


1520-1557: LGTM! Amount overflow protection testing.

The test validates that tokensOwed calculations handle accumulation correctly without overflow, which is critical for preventing loss of funds or state corruption.

Copy link

@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.

Actionable comments posted: 5

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d2f135 and 5bace90.

📒 Files selected for processing (2)
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gov/governance, contract/r/scenario/gov/governance)
  • GitHub Check: test-gnoswap (gnoswap/position/v1, contract/r/gnoswap/position/v1)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/gov/staker/v1, contract/r/gnoswap/gov/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/gnsmath, contract/p/gnoswap/gnsmath)
  • GitHub Check: run-integration-test
🔇 Additional comments (7)
contract/r/gnoswap/pool/v1/oracle.gno (3)

425-427: Improved error message for zero cardinality.

The new error message is clearer than the previous implementation. Good improvement for developer experience.


163-164: Optimization approved: grow now skips pre-initialized slots.

Starting the loop from currentCardinality instead of 0 is a good optimization that avoids unnecessary checks for already-initialized slots.


123-133: Zero liquidity handling is correct and well-tested.

The code at lines 123-133 uses max(1, liquidity) to prevent division by zero in the secondsPerLiquidity calculation. This approach is:

  • Tested: oracle_test.gno includes an explicit "zero liquidity" test case (line 271)
  • Validated: oracle_integration_test.gno confirms the expected calculation when liquidity is zero (line 466)
  • Documented: The behavior is explicitly commented in the code
  • Consistent: The pattern matches how zero liquidity is handled throughout the codebase (sqrt_price_math.gno, position.gno, tick.gno, etc.)

No concerns remain.

contract/r/gnoswap/pool/v1/oracle_integration_test.gno (4)

103-111: Good refactor: dynamic cardinality growth improves test flexibility.

The change from static increaseObservationCardinalityLimitByPool(pool, 10) to dynamic calculation based on scenario count makes the test more maintainable and accurate.


207-209: Test assumes nil afterOrAt, but this may cause issues.

The test expects afterOrAt to be nil when target equals the newest observation (lines 207-209). However, as noted in the oracle.gno review, returning nil for afterOrAt can cause nil pointer dereferences in observeSingle (line 287).

If the oracle.gno implementation is fixed to return beforeOrAt for both values when they're equal, this test assertion should be updated accordingly.

After fixing the nil afterOrAt issue in oracle.gno, update this test:

 	uassert.NoError(t, err, "Should not error when target equals observation")
 	uassert.Equal(t, currentTime, beforeOrAt2.BlockTimestamp(), "Should return exact observation")
-	if afterOrAt2 != nil {
-		t.Fatalf("afterOrAt should be nil when target equals newest observation")
-	}
+	uassert.NotNil(t, afterOrAt2, "afterOrAt should be the same as beforeOrAt when target equals observation")
+	uassert.Equal(t, beforeOrAt2.BlockTimestamp(), afterOrAt2.BlockTimestamp(), "Both should have same timestamp")

465-468: Zero liquidity test calculation is correct.

The test correctly validates that when liquidity is zero, the calculation uses max(1, liquidity) = 1, resulting in secondsPerLiquidity = 100 * 2^128 / 1. This aligns with the oracle.gno implementation.


435-439: Test now correctly expects uninitialized observations to be retrievable.

The updated test (lines 435-439) correctly reflects that observationAt returns uninitialized observations without error, and the caller must check Initialized(). This is a good change that aligns with the oracle.gno behavior.

Add test for repositioning when a tick changes its role from lower
to upper boundary. The test demonstrates:

- Position starts at [9000, 13000] with tick 9000 as lower boundary
- After reposition to [5000, 9000], tick 9000 becomes upper boundary
- Tick liquidityNet changes from positive (lower) to negative (upper)

The test follows the proper reposition pattern:
1. Collect fees from the position
2. Decrease liquidity to make position clear
3. Reposition to new tick range
@junghoon-vans junghoon-vans force-pushed the pool-codebase-maintenance-and-cleanup branch from 5bace90 to 6c99388 Compare January 9, 2026 05:22
Copy link

@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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
contract/r/gnoswap/pool/oracle.gno (1)

131-139: Semantically correct change to default observation initialization.

The change from initialized: true to initialized: false properly reflects that a "default" observation is a placeholder that hasn't been written with actual data yet. This contrasts appropriately with NewObservationState (line 104-109), which creates the first valid observation with initialized: true.

Consider adding documentation to clarify the semantic difference between the constructors:

// NewDefaultObservation creates an uninitialized placeholder observation.
// Used for pre-allocating observation slots before they are written with actual data.
// For creating the initial valid observation, use NewObservationState instead.
func NewDefaultObservation() *Observation {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5bace90 and ee2d35e.

📒 Files selected for processing (3)
  • contract/r/gnoswap/pool/oracle.gno
  • contract/r/gnoswap/pool/v1/manager.gno
  • contract/r/gnoswap/pool/v1/manager_test.gno
🚧 Files skipped from review as they are similar to previous changes (1)
  • contract/r/gnoswap/pool/v1/manager.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/gnoswap/pool/oracle.gno
  • contract/r/gnoswap/pool/v1/manager_test.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/gnoswap/pool/oracle.gno
  • contract/r/gnoswap/pool/v1/manager_test.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (2)
contract/r/gnoswap/pool/v1/manager_test.gno (2)

132-199: LGTM! Comprehensive boundary testing for sqrtPriceX96.

The new test cases thoroughly validate the sqrtPriceX96 boundaries:

  • Correctly tests MIN_SQRT_RATIO (4295128739) as inclusive
  • Correctly tests MAX_SQRT_RATIO (1461446703485210103287273052203988822378723970342) as exclusive
  • Tests both sides of each boundary (±1)
  • Includes edge cases like sqrtPrice=1 and mid-range values

The test coverage ensures the pool creation properly rejects invalid price ranges and accepts valid ones according to the updated boundary semantics.


304-438: The test correctly focuses on boundary value validation for the fee protocol calculation logic. The access control is appropriately enforced at the public API level in the SetFeeProtocol method (capital S), which uses access.AssertIsAdminOrGovernance(caller) to restrict access. The private setFeeProtocol method being tested is a pure utility for validation and state updates, and correctly has no access control checks (as documented in its comment). This is a sound architectural pattern where the public crossing function handles permissions while helper methods handle logic. The test does not need an unauthorized access case because it's testing the internal validation, not the access control layer.

Copy link

@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.

Actionable comments posted: 8

🧹 Nitpick comments (8)
contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno (2)

147-148: Consider handling the error from GetPool.

The error from GetPool is discarded. While this test would panic if the pool doesn't exist, explicit error handling improves debuggability and aligns with defensive coding practices.

💡 Suggested improvement
-	pools, _ := pool.GetPool(bazPath, quxPath, fee500)
+	pools, exists := pool.GetPool(bazPath, quxPath, fee500)
+	if !exists {
+		panic("pool not found")
+	}

264-275: Consider propagating transfer errors from mockSwapCallback.

The callback returns nil unconditionally even if SafeGRC20Transfer might fail. While the conservation checks at the end would catch any discrepancy, surfacing transfer failures early provides clearer diagnostics.

contract/r/scenario/pool/swap_balance_extreme_filetest.gno (1)

286-297: Callback error handling could be improved.

Same observation as the other scenario files: the mockSwapCallback returns nil unconditionally. For consistency and better diagnostics, consider checking if SafeGRC20Transfer has any failure mode that should be propagated.

contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno (1)

60-67: Consider extracting shared utilities across scenario files.

The BalanceSnapshot struct, getBalanceSnapshot, validateBalanceChange, and mockSwapCallback patterns are nearly identical across all three scenario files (with only token names differing). While self-contained tests are valuable, extracting these into a shared test helper package could reduce maintenance burden.

This could be deferred if the preference is to keep scenario tests fully self-contained.

Also applies to: 94-103, 218-247, 249-260

contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno (2)

80-86: Consider documenting the sqrtPriceX96 magic number.

The sqrtPriceX96 value "130621891405341611593710811006" is not immediately obvious. A brief comment explaining this corresponds to tick 10000 would improve test readability.

📝 Suggested documentation
 	ufmt.Println("[INFO] Creating pool at tick 10000")
 	pool.CreatePool(
 		cross,
 		barPath,
 		fooPath,
 		fee500,
+		// sqrtPriceX96 corresponding to tick 10000
 		"130621891405341611593710811006",
 	)

177-186: Consider documenting the full liquidity value.

The hardcoded liquidity "1183668969" should match the liquidity from the initial mint (line 244 of expected output). A brief comment would clarify this dependency.

📝 Suggested documentation
 	ufmt.Println("[INFO] Decreasing liquidity to clear position")
 	_, _, _, _, amount0Decreased, amount1Decreased, _ := position.DecreaseLiquidity(
 		cross,
 		1,
-		"1183668969", // full liquidity
+		"1183668969", // full liquidity from initial mint in mintPosition()
 		"0",
 		"0",
 		9999999999,
 		false,
 	)
contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno (1)

114-166: [MEDIUM] Realm switching in callbacks is risky; create/restore realm explicitly.

Line 116 and Line 146: testing.NewCodeRealm("gno.land/r/gnoswap/router") is recreated multiple times; plus the callback later overrides the realm (Line 195) without restoring it. This can make the scenario brittle as it grows.

Fix: create a routerRealm := testing.NewCodeRealm(...) once and restore realm in mockSwapCallback (see next comment).
-->

contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno (1)

114-221: [MEDIUM] Prefer deterministic realm management across swaps/mints (avoid hidden realm state).

Line 116, 165, 195: Multiple testing.SetRealm(testing.NewCodeRealm(...)) calls plus a callback that mutates realm state makes it easy to accidentally run later steps under the wrong caller.

Fix: create routerRealm once; ensure callback restores realm (see below).
-->

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee2d35e and 251fc24.

📒 Files selected for processing (8)
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
  • contract/r/scenario/pool/swap_balance_extreme_filetest.gno
  • contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno
  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
🚧 Files skipped from review as they are similar to previous changes (2)
  • contract/r/scenario/pool/oracle_twap_with_price_change_filetest.gno
  • contract/r/scenario/pool/oracle_twap_basic_filetest.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/scenario/pool/swap_balance_extreme_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno
  • contract/r/scenario/pool/oracle_twap_full_scenario_filetest.gno
  • contract/r/scenario/pool/swap_balance_extreme_filetest.gno
  • contract/r/scenario/pool/oracle_twap_with_cardinality_filetest.gno
  • contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno
  • contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/router/v1, contract/r/gnoswap/router/v1)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gov/governance, contract/r/scenario/gov/governance)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/position/v1, contract/r/gnoswap/position/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (14)
contract/r/scenario/pool/swap_balance_mixed_positions_filetest.gno (2)

60-67: LGTM! Well-structured balance tracking type.

The BalanceSnapshot struct properly uses unexported fields, preventing external modification. The design cleanly captures all relevant balance components for conservation validation.


233-262: LGTM! Solid balance conservation validation.

The validateBalanceChange function correctly verifies exact conservation by summing user, pool, and protocol fee changes for each token. Using panic for validation failures is appropriate for scenario tests.

contract/r/scenario/pool/swap_balance_extreme_filetest.gno (2)

69-98: LGTM! Comprehensive extreme price movement scenario.

The main function orchestrates a well-structured test flow covering setup, extreme downward movement, extreme upward movement, and maximum slippage. The sequential balance validation after each step ensures conservation is maintained throughout price extremes.


150-183: LGTM! Good coverage of extreme downward price movement.

The test swaps 10M OBL which moves the tick from 0 to -20, validating the pool handles significant price drops correctly while maintaining balance conservation.

contract/r/scenario/pool/swap_balance_single_in_range_filetest.gno (3)

1-26: LGTM! Clean scenario test for basic in-range swaps.

The file is well-organized with clear purpose documentation and proper imports. This provides a good baseline test for the simplest swap scenario.


115-140: LGTM! Pool setup is straightforward and correct.

The setup creates a pool at 1:1 price with a symmetric tick range (-1000 to 1000), which is ideal for testing basic in-range swaps without edge cases.


180-216: LGTM! Exact output swap test is correctly implemented.

The approval buffer of 200000 FOO for an exact output of 50000 BAR appropriately accounts for the unknown input amount plus fees.

contract/r/scenario/position/position_reposition_tick_role_change_filetest.gno (7)

1-47: Test setup and imports look well-structured.

The scenario test is properly organized with clear documentation comments, appropriate imports (including side-effect imports for module registration), and well-defined constants. The global variable initialization pattern using access.GetAddress is appropriate for test scenarios.


49-73: Well-organized test orchestration.

The main function provides a clear, readable test flow with descriptive scenario labels. Each step is properly isolated into its own function, making the test easy to understand and maintain.


93-120: Position minting follows correct patterns.

The function properly sets the realm context, approves tokens before the operation, and mints a position with a clearly defined tick range. The test values are appropriate for the scenario.


136-165: Swap execution correctly simulates router context.

The function appropriately switches realm context to the router before executing the swap, accurately simulating how swaps would occur in production. The callback pattern with cur realm is correctly implemented.


122-134: Tick state validation is correctly implemented.

The function properly retrieves tick info with error handling via panic (appropriate for Gno), and validates the key properties (liquidityGross, liquidityNet, initialized) that demonstrate tick 9000 is functioning as a lower boundary.


223-233: mockSwapCallback uses cross without being a crossing function.

This function uses the cross keyword (lines 227, 229) to call common.SafeGRC20Transfer, but it lacks the cur realm parameter that defines a crossing function. According to Gno semantics, cross should only be used within crossing functions.

Since this is called from within the callback closure (line 155-157) that does have cur realm, consider either:

  1. Adding cur realm parameter to make this a proper crossing function
  2. Inlining the transfer logic directly in the callback
♻️ Option 1: Make it a crossing function
-func mockSwapCallback(token0Path string, token1Path string, amount0Delta int64, amount1Delta int64, zeroForOne bool) error {
+func mockSwapCallback(cur realm, token0Path string, token1Path string, amount0Delta int64, amount1Delta int64, zeroForOne bool) error {
 	testing.SetRealm(adminRealm)
 
 	if zeroForOne {
 		common.SafeGRC20Transfer(cross, token0Path, poolAddr, amount0Delta)
 	} else {
 		common.SafeGRC20Transfer(cross, token1Path, poolAddr, amount1Delta)
 	}
 
 	return nil
 }

And update the call site:

-		return mockSwapCallback(barPath, fooPath, amount0Delta, amount1Delta, true)
+		return mockSwapCallback(cross, barPath, fooPath, amount0Delta, amount1Delta, true)

235-278: Expected output provides comprehensive scenario verification.

The output section correctly validates the complete test scenario, including the critical assertion that tick 9000's liquidityNet transitions from positive (as lower boundary) to negative (as upper boundary). This confirms the tick role change behavior is working as expected.

Copy link

@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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
contract/r/gnoswap/pool/v1/oracle.gno (2)

322-367: [CRITICAL] binarySearch infinite-loop and uninitialized observation handling

Lines 380–415: The loop uses for {} with no termination condition. When i == 0 and r = i - 1 executes, r underflows to 65535 (uint16 max), causing the search to become non-terminating. Additionally, line 390–393 checks err but does not validate atOrAfter.Initialized(), allowing a default/uninitialized observation to propagate and break timestamp ordering invariants.

Problem:

  • Infinite loop with uint16 underflow: r wraps to 65535 when subtracting from 0, and with l == 0, the loop repeats indefinitely.
  • Uninitialized observation: atOrAfter can be uninitialized without raising an error, violating the contract that returned observations must have valid timestamps.

Fix:

for l <= r {
    i = (l + r) / 2
    beforeIndex := uint16(i % uint64(cardinality))
    obs, err := observationAt(os, beforeIndex)
    if err != nil || !obs.Initialized() {
        l = i + 1
        continue
    }
    beforeOrAt = obs

    afterIndex := uint16((i + 1) % uint64(cardinality))
    atOrAfter, err = observationAt(os, afterIndex)
    if err != nil || !atOrAfter.Initialized() {
        l = i + 1
        continue
    }

    targetAtOrAfter := beforeOrAt.BlockTimestamp() <= target
    if targetAtOrAfter && target <= atOrAfter.BlockTimestamp() {
        break
    }

    if !targetAtOrAfter {
        if i == 0 {
            break
        }
        r = i - 1
    } else {
        l = i + 1
    }
}
if beforeOrAt == nil || atOrAfter == nil {
    return nil, nil, errors.New("failed to find surrounding observations")
}
return beforeOrAt, atOrAfter, nil

Rationale: Adding the loop bound l <= r terminates safely when the search space is exhausted. Checking atOrAfter.Initialized() ensures only valid observations are used for timestamp comparisons. The guard if i == 0 { break } prevents underflow of r. The nil-check before return ensures the function never returns uninitialized pointers.


238-317: Line 296-298: Tick cumulative interpolation truncates early, losing precision.

The current formula ((after-before)/observationTimeDelta)*targetDelta performs integer division before multiplication, causing unnecessary precision loss. The standard interpolation form (after-before)*targetDelta/observationTimeDelta reduces truncation error and matches how secondsPerLiquidityCumulativeX128 is interpolated (lines 305-314).

If overflow is a concern for int64, document it explicitly and add overflow detection similar to the MulOverflow check in writeObservation, rather than relying on divide-first to avoid overflow.

Example fix
delta := afterOrAt.TickCumulative() - beforeOrAt.TickCumulative()
tickCumulative := beforeOrAt.TickCumulative() + (delta*targetDelta)/observationTimeDelta
🧹 Nitpick comments (11)
contract/r/scenario/pool/oracle_twap_single_swap_filetest.gno (1)

142-156: Derive poolPath fee from fee500 to prevent accidental mismatch.

Line 144: hard-coding "500" is brittle when fee500 exists.

Proposed change
- poolPath := barPath + ":" + fooPath + ":" + "500"
+ poolPath := barPath + ":" + fooPath + ":" + ufmt.Sprintf("%d", fee500)
contract/r/scenario/pool/oracle_twap_increased_cardinality_filetest.gno (1)

59-205: Avoid silent scenario “passes”: handle returned errors from pool APIs (GetPool/GetTWAP/Swap).

Line 84, Line 118, Line 148, Line 178, Line 182, Line 186: ignoring err risks misleading “[EXPECTED]” logs if the call fails without panicking.

contract/r/gnoswap/pool/v1/oracle.gno (1)

101-148: transform: fix error message and consider nil-liquidity guard.

Line 103-105: condition is timeDelta < 0 but message says “greater than 0” (should be “>= 0”).
Line 115-116 / Line 124: if liquidity can ever be nil at call sites, this will panic.

Proposed change
 if timeDelta < 0 {
-    return nil, errors.New("time delta must be greater than 0")
+    return nil, errors.New("time delta must be >= 0")
 }

+if liquidity == nil {
+    return nil, errors.New("liquidity must not be nil")
+}
contract/r/gnoswap/pool/v1/oracle_integration_test.gno (4)

103-112: Cardinality pre-growth logic: consider also setting Cardinality(...) (or explain why limit-only is enough).

Line 103-111: you grow(...) then only SetCardinalityLimit(neededCard). This relies on writeObservation later growing cardinality when Index == Cardinality-1. If that coupling is intentional, a short comment would help.


71-75: Fix assertion message: logic is “non-increasing as secondsAgo increases”.

Line 73-75: Gte is consistent with the query order [0, 300, 600, 900] (newest → oldest), but the message says “non-decreasing forward in time”, which reads opposite.


262-287: Fix misleading test comment about grow initialization behavior.

Line 262-266: comment says “initialized==true”, but Line 285-287 asserts new slots are not initialized (which matches the updated grow behavior).


364-400: “Reject > max” coverage is currently informational-only; consider asserting the rejection via another API surface.

Line 369-386: since maxObservationCardinality is uint16 max, the test can’t pass > max into grow. If there’s another function that takes a wider type (or validates against maxObservationCardinality with an explicit comparison), it’d be better to assert the error path directly.

contract/r/scenario/pool/oracle_twap_no_price_change_filetest.gno (2)

92-127: Avoid silent scenario “passes”: handle returned errors from GetPool/GetTWAP.

Line 92 and Line 100-122: ignoring err can make the scenario print “expected” values even if TWAP retrieval fails (depending on the API’s failure mode).


97-102: Derive poolPath fee from fee500 to prevent accidental mismatch.

Line 99: hard-coded "500" is brittle.

contract/r/scenario/pool/oracle_twap_multiple_swaps_filetest.gno (2)

59-254: Avoid silent scenario “passes”: handle returned errors from pool APIs (GetPool/GetTWAP/Swap/Mint).

This scenario is especially sensitive to silent failures because the final TWAPs are “expected” logs, not assertions.


223-241: Derive poolPath fee from fee500 to prevent accidental mismatch.

Line 225: hard-coded "500" is brittle.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 251fc24 and 1c6a609.

📒 Files selected for processing (6)
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
  • contract/r/scenario/pool/oracle_twap_increased_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_multiple_swaps_filetest.gno
  • contract/r/scenario/pool/oracle_twap_no_price_change_filetest.gno
  • contract/r/scenario/pool/oracle_twap_single_swap_filetest.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/scenario/pool/oracle_twap_increased_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_single_swap_filetest.gno
  • contract/r/scenario/pool/oracle_twap_no_price_change_filetest.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/scenario/pool/oracle_twap_multiple_swaps_filetest.gno
  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/scenario/pool/oracle_twap_increased_cardinality_filetest.gno
  • contract/r/scenario/pool/oracle_twap_single_swap_filetest.gno
  • contract/r/scenario/pool/oracle_twap_no_price_change_filetest.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/scenario/pool/oracle_twap_multiple_swaps_filetest.gno
  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: test-gnoswap (gnoswap/scenario/upgrade, contract/r/scenario/upgrade)
  • GitHub Check: test-gnoswap (gnoswap/scenario/pool, contract/r/scenario/pool)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gov/governance, contract/r/scenario/gov/governance)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/launchpad/v1, contract/r/gnoswap/launchpad/v1)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/gov/staker/v1, contract/r/gnoswap/gov/staker/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (2)
contract/r/scenario/pool/oracle_twap_single_swap_filetest.gno (1)

162-173: Swap callback realm switch looks intentional; keep it explicit.

Line 163: setting adminRealm inside mockSwapCallback is easy to miss; it’s correct if the intention is “admin pays swap deltas” (vs router/user).

contract/r/gnoswap/pool/v1/oracle.gno (1)

150-172: Growth optimization and cardinality==0 guard look good.

  • Line 163-169: growing only [currentCardinality, nextCardinality) is a clear improvement.
  • Line 425-427: explicit cardinality == 0 error avoids confusing downstream behavior.

Also applies to: 416-452

Copy link

@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.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
contract/r/gnoswap/pool/upgrade_test.gno (1)

1142-1156: [MEDIUM] Rule: Tests must assert success conditions (don’t ignore injected errors)

Line 1143: the “success” path sets GetTWAP to return errors.New(""), but the test then ignores the returned error (Line 1148). This can mask regressions and/or make the test semantically inconsistent.

Fix: return a nil error from the mock, and assert err == nil.

Proposed patch
-        mockPool.Response.Set("GetTWAP", tt.expectedTWAP, u256.Zero(), errors.New(""))
+        mockPool.Response.Set("GetTWAP", tt.expectedTWAP, u256.Zero(), error(nil))

         testing.SetRealm(tt.callerRealm)

         // Action
-        twap, _, _ := GetTWAP(tt.poolPath, tt.secondsAgo)
+        twap, _, err := GetTWAP(tt.poolPath, tt.secondsAgo)

         // Assert
         if mockPool.Response.CallCount("GetTWAP") != 1 {
           t.Error("GetTWAP was not called on the implementation")
         }
+        uassert.NoError(t, err)
         if twap != tt.expectedTWAP {
           t.Errorf("GetTWAP() = %v, want %v", twap, tt.expectedTWAP)
         }
contract/r/gnoswap/pool/v1/oracle.gno (1)

330-422: [CRITICAL] Rule: No unbounded loops / integer underflow in on-chain logic

Line 388: for {} in binarySearch has no l <= r guard; if the search space becomes invalid (e.g., repeated “uninitialized” hits push l past r), this can loop indefinitely (DoS risk).
Line 415: r = i - 1 on uint64 risks underflow if i == 0.

Fix: use a bounded loop (for l <= r) with signed indices (or explicit underflow guards) and return an error when not found.

Proposed patch
 func binarySearch(os *pl.ObservationState,
   currentTime int64,
   target int64,
   index uint16,
   cardinality uint16,
 ) (*pl.Observation, *pl.Observation, error) {
-  l := uint64((index + 1) % cardinality) // oldest observation
-  r := l + uint64(cardinality) - 1       // newest observation
-  var i uint64
-  var beforeOrAt, atOrAfter *pl.Observation
-
-  for {
-    i = (l + r) / 2
-
-    beforeIndex := uint16(i % uint64(cardinality))
+  l := int64((index + 1) % cardinality)       // oldest (logical)
+  r := l + int64(cardinality) - 1             // newest (logical)
+  var beforeOrAt, atOrAfter *pl.Observation
+
+  for l <= r {
+    i := (l + r) / 2
+
+    beforeIndex := uint16(int64(i) % int64(cardinality))
     obs, err := observationAt(os, beforeIndex)
     if err != nil || !obs.Initialized() {
       // we've landed on an uninitialized tick, keep searching higher (more recently)
-      l = i + 1
+      l = i + 1
       continue
     }
     beforeOrAt = obs
 
-    afterIndex := uint16((i + 1) % uint64(cardinality))
+    afterIndex := uint16((int64(i) + 1) % int64(cardinality))
     atOrAfter, err = observationAt(os, afterIndex)
     if err != nil {
-      l = i + 1
+      l = i + 1
       continue
     }
 
     targetAtOrAfter := beforeOrAt.BlockTimestamp() <= target
 
     // check if we've found the answer!
     if targetAtOrAfter && target <= atOrAfter.BlockTimestamp() {
-      break
+      return beforeOrAt, atOrAfter, nil
     }
 
     if !targetAtOrAfter {
-      r = i - 1
+      r = i - 1
     } else {
-      l = i + 1
+      l = i + 1
     }
   }
 
-  return beforeOrAt, atOrAfter, nil
+  return nil, nil, errNotInitializedObservation
 }
🧹 Nitpick comments (5)
contract/r/gnoswap/pool/_mock_test.gno (1)

568-575: [LOW] Rule: Test helper robustness

Line 574: res[0].(int32), res[1].(*u256.Uint), res[2].(error) will panic if a test sets an incomplete/incorrect mock tuple, producing a less actionable failure than a targeted error.

Problem: Makes test failures harder to diagnose when mocks are misconfigured.

Fix:

Proposed diff
 func (m *MockPool) GetTWAP(poolPath string, secondsAgo uint32) (int32, *u256.Uint, error) {
 	res, ok := m.Response.Get("GetTWAP")
 	if !ok {
 		return 0, nil, errors.New("not found")
 	}
 
-	return res[0].(int32), res[1].(*u256.Uint), res[2].(error)
+	if len(res) != 3 {
+		return 0, nil, errors.New("invalid mock response for GetTWAP")
+	}
+	tick, ok0 := res[0].(int32)
+	hml, ok1 := res[1].(*u256.Uint)
+	err, ok2 := res[2].(error)
+	if !ok0 || !ok1 || !ok2 {
+		return 0, nil, errors.New("invalid mock response types for GetTWAP")
+	}
+	return tick, hml, err
 }

Rationale: Keeps the same behavior for correct tests, but yields clearer failures for misconfigured mocks.

contract/r/gnoswap/pool/getter.gno (1)

283-287: [MEDIUM] Rule: Avoid leaking internal mutable pointers in getters

Line 285-287: GetTWAP returns *u256.Uint directly from the implementation, unlike other getters that Clone() before returning.

Problem: If the implementation returns a reference to internal state, callers can mutate it and cause hard-to-track issues.

Fix:

Proposed diff
 func GetTWAP(poolPath string, secondsAgo uint32) (int32, *u256.Uint, error) {
-	return getImplementation().GetTWAP(poolPath, secondsAgo)
+	tick, harmonicMeanLiquidity, err := getImplementation().GetTWAP(poolPath, secondsAgo)
+	if harmonicMeanLiquidity == nil {
+		return tick, nil, err
+	}
+	return tick, harmonicMeanLiquidity.Clone(), err
 }

Rationale: Matches the “return copies” pattern used elsewhere in this proxy (e.g., feeGrowth getters) and makes aliasing behavior explicit.

contract/r/scenario/upgrade/implements/mock/pool/test_impl.gno (1)

543-553: [LOW] Rule: Avoid nil-related panics in mock wrappers using any + type assertions

Line 552: result[1].(*u256.Uint) and result[2].(error) will panic if a mock function returns nil (untyped) for either value.

Fix: guard nils before asserting.

Proposed patch
 func (t *TestPool) GetTWAP(poolPath string, secondsAgo uint32) (int32, *u256.Uint, error) {
   result := t.ExecuteFn(
     "GetTWAP",
     func(args ...any) any {
       r1, r2, r3 := t.instance.GetTWAP(args[0].(string), args[1].(uint32))
       return []any{r1, r2, r3}
     },
     poolPath, secondsAgo,
   ).([]any)
-  return result[0].(int32), result[1].(*u256.Uint), result[2].(error)
+  var liq *u256.Uint
+  if result[1] != nil {
+    liq = result[1].(*u256.Uint)
+  }
+  var err error
+  if result[2] != nil {
+    err = result[2].(error)
+  }
+  return result[0].(int32), liq, err
 }
contract/r/gnoswap/pool/v1/getter.gno (1)

238-252: [LOW] Rule: Exported API docs must match the signature

Line 244-245: comment says “Returns TWAP tick and error” but the signature now returns (int32, *u256.Uint, error) (harmonic mean liquidity added). Please update the doc to prevent misuse.

contract/r/gnoswap/pool/v1/oracle_test.gno (1)

349-474: [LOW] Rule: Prefer explicit table fields over branching on tt.name

Line 415: if tt.name == "correct output for liquidity overflow" couples behavior to a string. Consider adding a boolean like expectMaxUint128 bool in the test table.

Also applies to: 496-514

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c6a609 and 4e08ecc.

⛔ Files ignored due to path filters (5)
  • contract/r/scenario/getter/pool_getter_filetest.gno is excluded by !**/*filetest.gno
  • contract/r/scenario/pool/oracle_twap_increased_cardinality_filetest.gno is excluded by !**/*filetest.gno
  • contract/r/scenario/pool/oracle_twap_multiple_swaps_filetest.gno is excluded by !**/*filetest.gno
  • contract/r/scenario/pool/oracle_twap_no_price_change_filetest.gno is excluded by !**/*filetest.gno
  • contract/r/scenario/pool/oracle_twap_single_swap_filetest.gno is excluded by !**/*filetest.gno
📒 Files selected for processing (10)
  • contract/r/gnoswap/pool/_mock_test.gno
  • contract/r/gnoswap/pool/getter.gno
  • contract/r/gnoswap/pool/types.gno
  • contract/r/gnoswap/pool/upgrade_test.gno
  • contract/r/gnoswap/pool/v1/getter.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/gnoswap/pool/v1/oracle_test.gno
  • contract/r/scenario/upgrade/implements/mock/pool/test_impl.gno
  • contract/r/scenario/upgrade/implements/v2_invalid/pool/test_impl.gno
  • contract/r/scenario/upgrade/implements/v3_valid/pool/test_impl.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/gnoswap/pool/getter.gno
  • contract/r/gnoswap/pool/v1/getter.gno
  • contract/r/gnoswap/pool/types.gno
  • contract/r/scenario/upgrade/implements/v2_invalid/pool/test_impl.gno
  • contract/r/gnoswap/pool/v1/oracle_test.gno
  • contract/r/gnoswap/pool/upgrade_test.gno
  • contract/r/gnoswap/pool/_mock_test.gno
  • contract/r/scenario/upgrade/implements/v3_valid/pool/test_impl.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/scenario/upgrade/implements/mock/pool/test_impl.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/gnoswap/pool/getter.gno
  • contract/r/gnoswap/pool/v1/getter.gno
  • contract/r/gnoswap/pool/types.gno
  • contract/r/scenario/upgrade/implements/v2_invalid/pool/test_impl.gno
  • contract/r/gnoswap/pool/v1/oracle_test.gno
  • contract/r/gnoswap/pool/upgrade_test.gno
  • contract/r/gnoswap/pool/_mock_test.gno
  • contract/r/scenario/upgrade/implements/v3_valid/pool/test_impl.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/scenario/upgrade/implements/mock/pool/test_impl.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: test-gnoswap (gnoswap/router/v1, contract/r/gnoswap/router/v1)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/upgrade, contract/r/scenario/upgrade)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/gov/staker/v1, contract/r/gnoswap/gov/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/emission, contract/r/gnoswap/emission)
  • GitHub Check: run-integration-test
🔇 Additional comments (7)
contract/r/gnoswap/pool/types.gno (1)

213-213: [LOW] Rule: API surface consistency

Line 213: IPoolGetter.GetTWAP now returns (int32, *u256.Uint, error).

Problem: None in this file; aligns interface with the new TWAP API.

Fix: None.

Rationale: Propagates the harmonic-mean liquidity return through the public interface, enabling callers/tests to use it when needed.

contract/r/scenario/upgrade/implements/v2_invalid/pool/test_impl.gno (1)

415-420: [LOW] Rule: Upgrade test harness correctness

Line 415-420: GetTWAP signature updated to (int32, *u256.Uint, error) while preserving the activation gate and delegation.

Problem: None.

Fix: None.

Rationale: Keeps upgrade-scenario behavior the same while matching the new interface/return contract.

contract/r/scenario/upgrade/implements/v3_valid/pool/test_impl.gno (1)

243-245: [LOW] Rule: Upgrade test harness correctness

Line 243-245: GetTWAP signature updated to (int32, *u256.Uint, error) with unchanged delegation.

Problem: None.

Fix: None.

Rationale: Ensures the v3-valid upgrade scenario compiles and exercises the same underlying behavior with the new API shape.

contract/r/gnoswap/pool/v1/oracle_test.gno (1)

340-345: LGTM: Updated getTWAP call site to the new 3-return signature

Line 342: twapTick, _, err := getTWAP(...) is the right adaptation.

contract/r/gnoswap/pool/v1/oracle.gno (3)

11-69: LGTM: getTWAP now returns arithmetic mean tick + harmonic mean liquidity with sane guards

Line 19-29: new validations for secondsAgo == 0, nil observation state, and zero cardinality are good.
Line 59-61: returning u256.Zero() when secondsPerLiquidityDelta is zero is a reasonable “no-liquidity” fallback.


109-156: LGTM: transform refactor + zero-liquidity handling looks consistent

Line 131-141: using max(1, liquidity) avoids division-by-zero in seconds-per-liquidity accumulation.


171-224: LGTM: observation growth/write path + cardinality guard

Line 171-177: growing from currentCardinality is a good micro-optimization.
Line 433-435: observe now rejects cardinality == 0, which matches the earlier invariants.

Also applies to: 226-244, 264-325, 425-460

Copy link

@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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
contract/r/gnoswap/pool/v1/oracle.gno (2)

171-177: LGTM: Efficient optimization avoiding redundant checks.

The optimization correctly skips already-initialized slots by starting the loop at currentCardinality rather than 0, reducing unnecessary iterations.


303-305: Integer division truncation in tick interpolation.

Lines 304-305: The interpolation uses integer division which truncates:

((afterOrAt.TickCumulative()-beforeOrAt.TickCumulative())/observationTimeDelta)*targetDelta

Consideration: This truncates the quotient before multiplying by targetDelta, potentially losing precision. The mathematically equivalent but more precise approach would be:

(afterOrAt.TickCumulative()-beforeOrAt.TickCumulative())*targetDelta/observationTimeDelta

However, the current order may be intentional to prevent intermediate overflow. If (after - before) * targetDelta could exceed int64 max, the current approach is safer.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e08ecc and 97da166.

📒 Files selected for processing (2)
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (11)
contract/r/gnoswap/pool/v1/oracle.gno (5)

11-12: LGTM: Correct calculation of 160-bit maximum.

The calculation correctly computes 2^160 - 1 for use in harmonic mean liquidity calculations.


109-156: LGTM: Safe arithmetic with proper overflow handling.

The transform function correctly:

  • Uses MulOverflow for liquidity delta calculation (line 123) with panic on overflow
  • Employs max(1, liquidity) to avoid division by zero (lines 131-134)
  • Uses MulDiv for safe fixed-point arithmetic (lines 137-141)
  • Clones values before mutation (line 143)

215-223: LGTM: Consistent with updated transform signature.

The call site correctly passes lastObservation directly to the refactored transform function.


433-435: LGTM: Improved error message.

The cardinality check is now explicit with a clearer error message.


226-244: Behavioral change: Functions now return uninitialized observations without error.

Lines 228 and 239-240: The checks (!ok || observation == nil) mean lastObservation() and observationAt() return observations even when Initialized() == false, whereas they previously required initialized observations.

Impact: Callers must verify Initialized() before using returned observations. The integration test confirms this is intentional (TestObservationState_ObservationAt, line 435-440). All callers in the file already handle this correctly (lines 361, 393, 402 check Initialized() before proceeding).

contract/r/gnoswap/pool/v1/oracle_integration_test.gno (6)

103-111: LGTM: Test correctly models dynamic cardinality growth.

The test properly calculates needed cardinality, grows the observation state, and sets the cardinality limit to match production usage patterns.


180-192: LGTM: Test correctly expects error for target before oldest observation.

The test verifies that querying a target timestamp before the oldest observation returns the appropriate error, matching the implementation at lines 369-371 in oracle.gno.


207-209: LGTM: Test confirms nil afterOrAt behavior.

The test correctly verifies that when the target equals the newest observation timestamp, afterOrAt should be nil, confirming the behavioral change in getSurroundingObservations.


262-301: LGTM: Test validates grow function creates uninitialized slots.

The test correctly verifies that grow creates observation slots with initialized=false for new slots, as documented in the comment at line 262.


430-440: LGTM: Test documents behavioral change in observationAt.

The test correctly validates that observationAt now returns uninitialized observations without error, requiring callers to check the Initialized() status. The comment at line 435 clearly documents this behavior.


447-469: LGTM: Test validates transform edge cases.

The test correctly:

  • Uses the new transform signature with lastObservation parameter
  • Validates zero liquidity handling with the expected formula: timeDelta * 2^128 / max(1, liquidity)
  • Covers negative time delta error case

Copy link

@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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
contract/r/gnoswap/pool/upgrade_test.gno (1)

1214-1214: Update the test comment to match the renamed function.

The comment still references the old function name "increase observation cardinality limit" but the function has been renamed to IncreaseObservationCardinalityNext.

📝 Proposed fix
-			name: "increase observation cardinality limit is success",
+			name: "increase observation cardinality next is success",
contract/r/gnoswap/pool/v1/oracle_spec_test.gno (1)

811-826: Setup helper ignores error returns from grow and writeObservation.

The setupFiveObservations helper function calls grow and writeObservation without checking their error returns. While this may be acceptable for test setup where failures would cause subsequent assertions to fail anyway, explicit error checking would make test failures more diagnosable.

♻️ Optional: Add error checking for better diagnostics
 func setupFiveObservations(startTime int64) *pl.ObservationState {
 	os := pl.NewObservationState(startTime)

-	grow(os, 1, 5)
+	if _, err := grow(os, 1, 5); err != nil {
+		panic("setup failed: " + err.Error())
+	}
 	os.SetCardinality(1)
 	os.SetCardinalityNext(5)

-	writeObservation(os, startTime+3, -5, u256.NewUint(5))
-	writeObservation(os, startTime+5, 1, u256.NewUint(2))
-	writeObservation(os, startTime+9, -6, u256.NewUint(4))
-	writeObservation(os, startTime+10, -2, u256.NewUint(4))
-	writeObservation(os, startTime+13, -2, u256.NewUint(9))
-	writeObservation(os, startTime+19, 4, u256.NewUint(2))
+	// Error handling omitted for brevity - failures will cause test assertions to fail
+	_ = writeObservation(os, startTime+3, -5, u256.NewUint(5))
+	_ = writeObservation(os, startTime+5, 1, u256.NewUint(2))
+	_ = writeObservation(os, startTime+9, -6, u256.NewUint(4))
+	_ = writeObservation(os, startTime+10, -2, u256.NewUint(4))
+	_ = writeObservation(os, startTime+13, -2, u256.NewUint(9))
+	_ = writeObservation(os, startTime+19, 4, u256.NewUint(2))

 	return os
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97da166 and 1e04fb0.

⛔ Files ignored due to path filters (3)
  • contract/r/scenario/pool/oracle_twap_increased_cardinality_filetest.gno is excluded by !**/*filetest.gno
  • contract/r/scenario/pool/oracle_twap_multiple_swaps_filetest.gno is excluded by !**/*filetest.gno
  • contract/r/scenario/pool/oracle_twap_single_swap_filetest.gno is excluded by !**/*filetest.gno
📒 Files selected for processing (15)
  • contract/r/gnoswap/pool/_mock_test.gno
  • contract/r/gnoswap/pool/oracle.gno
  • contract/r/gnoswap/pool/proxy.gno
  • contract/r/gnoswap/pool/types.gno
  • contract/r/gnoswap/pool/upgrade_test.gno
  • contract/r/gnoswap/pool/v1/_helper_test.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
  • contract/r/gnoswap/pool/v1/oracle_spec_test.gno
  • contract/r/gnoswap/pool/v1/oracle_test.gno
  • contract/r/gnoswap/pool/v1/pool.gno
  • contract/r/gnoswap/pool/v1/pool_test.gno
  • contract/r/scenario/upgrade/implements/mock/pool/test_impl.gno
  • contract/r/scenario/upgrade/implements/v2_invalid/pool/test_impl.gno
  • contract/r/scenario/upgrade/implements/v3_valid/pool/test_impl.gno
🚧 Files skipped from review as they are similar to previous changes (2)
  • contract/r/gnoswap/pool/_mock_test.gno
  • contract/r/scenario/upgrade/implements/mock/pool/test_impl.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/gnoswap/pool/proxy.gno
  • contract/r/gnoswap/pool/v1/_helper_test.gno
  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
  • contract/r/gnoswap/pool/oracle.gno
  • contract/r/gnoswap/pool/v1/pool_test.gno
  • contract/r/gnoswap/pool/v1/oracle_spec_test.gno
  • contract/r/gnoswap/pool/v1/pool.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/scenario/upgrade/implements/v3_valid/pool/test_impl.gno
  • contract/r/gnoswap/pool/v1/oracle_test.gno
  • contract/r/gnoswap/pool/types.gno
  • contract/r/gnoswap/pool/upgrade_test.gno
  • contract/r/scenario/upgrade/implements/v2_invalid/pool/test_impl.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/gnoswap/pool/proxy.gno
  • contract/r/gnoswap/pool/v1/_helper_test.gno
  • contract/r/gnoswap/pool/v1/oracle_integration_test.gno
  • contract/r/gnoswap/pool/oracle.gno
  • contract/r/gnoswap/pool/v1/pool_test.gno
  • contract/r/gnoswap/pool/v1/oracle_spec_test.gno
  • contract/r/gnoswap/pool/v1/pool.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/scenario/upgrade/implements/v3_valid/pool/test_impl.gno
  • contract/r/gnoswap/pool/v1/oracle_test.gno
  • contract/r/gnoswap/pool/types.gno
  • contract/r/gnoswap/pool/upgrade_test.gno
  • contract/r/scenario/upgrade/implements/v2_invalid/pool/test_impl.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/upgrade, contract/r/scenario/upgrade)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/router/v1, contract/r/gnoswap/router/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (41)
contract/r/gnoswap/pool/oracle.gno (3)

38-45: Clone creates an empty observations map rather than copying existing observations.

The Clone() method creates a new empty map[uint16]*Observation instead of copying the actual observations from the source. This may be intentional for creating a fresh state, but it differs from the Observation.Clone() which performs a deep copy.

If this is intentional (e.g., for creating a template state), consider adding a comment to clarify the design choice. If observations should be preserved, the map entries need to be cloned:

♻️ Optional: Deep copy observations if needed
 func (os *ObservationState) Clone() *ObservationState {
+	clonedObs := make(map[uint16]*Observation, len(os.observations))
+	for k, v := range os.observations {
+		clonedObs[k] = v.Clone()
+	}
 	return &ObservationState{
-		observations:    make(map[uint16]*Observation),
+		observations:    clonedObs,
 		index:           os.index,
 		cardinality:     os.cardinality,
 		cardinalityNext: os.cardinalityNext,
 	}
 }

131-139: Default observation now starts uninitialized.

The change from initialized: true to initialized: false is semantically correct for a "default" observation that hasn't been written yet. This aligns with the oracle pattern where uninitialized slots should be distinguishable from actual observations.


8-13: LGTM! Proper encapsulation and initialization.

The struct uses unexported fields with controlled getter/setter access, following Gno security best practices. The NewObservationState constructor properly initializes the first observation slot with the correct timestamp.

Also applies to: 95-113

contract/r/gnoswap/pool/proxy.gno (1)

30-50: LGTM! API rename is consistent.

The function rename from IncreaseObservationCardinalityLimit to IncreaseObservationCardinalityNext is properly applied to both the proxy function and its delegation call. The documentation comment is updated accordingly.

contract/r/gnoswap/pool/types.gno (2)

36-41: LGTM! Interface method rename is consistent.

The IncreaseObservationCardinalityNext method signature in IPoolManager is consistent with the proxy and implementation layers.


213-213: Interface signature change verified across all implementations.

All implementations of GetTWAP have been updated to match the new return signature (int32, *u256.Uint, error). Found 6 implementations across the codebase (mock tests, v1 getter, test implementations, and main getter function), all with consistent signatures.

contract/r/gnoswap/pool/v1/_helper_test.gno (1)

740-746: LGTM! Test helper rename is consistent with production API.

The test helper function and parameter names are properly updated to match the renamed production API (IncreaseObservationCardinalityNext).

contract/r/gnoswap/pool/v1/pool.gno (3)

387-408: LGTM! Implementation rename with proper event emission.

The function is correctly renamed to IncreaseObservationCardinalityNext, the internal call is updated to increaseObservationCardinalityNextByPool, and the event name matches the function name. The event uses prevAddr which follows the codebase convention per learnings.


258-260: LGTM! Error handling is appropriate.

The inline error check with panic on failure is correct for Gno realm code where errors should abort the transaction.


157-157: The review comment is based on a misunderstanding of the getPositionKey function. The function signature is func getPositionKey(tickLower int32, tickUpper int32) string—it returns only a string, never an error. The function performs simple string concatenation and base64 encoding, neither of which can fail. Direct assignment at lines 157 and 211 is correct and the only way to use this function. No error handling was "removed"—it never existed.

Likely an incorrect or invalid review comment.

contract/r/gnoswap/pool/v1/oracle_spec_test.gno (3)

12-57: LGTM! Comprehensive initialization tests.

The initialization tests properly verify the initial state of ObservationState: index=0, cardinality=1, cardinalityNext=1, and the first observation slot is correctly initialized.


322-326: Edge case handled: uninitialized observation slot check.

Good defensive check for accessing an observation slot that may not exist, using the map lookup idiom to verify the slot is uninitialized.


328-787: Comprehensive observation interpolation test coverage.

The TestOracleObserve suite provides thorough coverage of:

  • Error cases (older observation doesn't exist)
  • Boundary conditions (max/min liquidity)
  • Chronological and reverse-order observations
  • Counterfactual calculations
  • Multiple observation fetching

This aligns well with the TWAP functionality requirements.

contract/r/scenario/upgrade/implements/v3_valid/pool/test_impl.gno (2)

36-38: LGTM! Delegation updated for renamed method.

The test implementation correctly delegates IncreaseObservationCardinalityNext to the underlying v1 instance.


243-245: LGTM! GetTWAP signature updated to match interface.

The delegation correctly returns all three values (int32, *u256.Uint, error) from the underlying implementation.

contract/r/gnoswap/pool/v1/pool_test.gno (4)

489-489: LGTM!

The rename from SetCardinalityLimit to SetCardinalityNext aligns with the broader API refactoring in this PR and maintains semantic clarity.


638-641: LGTM!

The boundary test correctly validates that MAX_SQRT_RATIO (1461446703485210103287273052203988822378723970342) is excluded from valid prices, matching the [minSqrtRatio, maxSqrtRatio) semantics. The error message and business context accurately reflect this boundary condition.


750-750: LGTM!

The simplification from a multi-value return to a single positionKey := getPositionKey(...) is cleaner and aligns with the position.gno changes mentioned in the AI summary.


1023-1137: Excellent test coverage for collectToken edge cases.

The test comprehensively covers:

  • Equal amounts across all inputs
  • Each input being the minimum
  • Zero value handling for each parameter
  • MAX_UINT128 boundary conditions
  • Small differences to verify correct minimum detection

This provides strong confidence in the collectToken function's correctness.

contract/r/gnoswap/pool/v1/oracle_integration_test.gno (5)

103-111: LGTM!

The dynamic cardinality growth pattern is cleaner than the previous static approach. Growing only when needed (neededCard > currentCard) and then setting CardinalityNext aligns with the new observation state semantics.


180-192: LGTM!

The updated test correctly expects an error when the target is before the oldest observation, with proper error code verification ([GNOSWAP-POOL-028]). This aligns with the refined boundary condition handling.


207-209: LGTM!

Good boundary assertion - when target equals the newest observation, afterOrAt should be nil since we're in the same block and don't need a virtual observation.


262-301: LGTM!

The test correctly verifies that grow creates observation slots but does NOT initialize them. This is the expected behavior - slots are pre-allocated but only initialized when actual observations are written.


461-468: LGTM!

The zero liquidity test correctly validates the max(1, liquidity) semantics. The expected calculation 100 * 2^128 / 1 using MulDiv with q128FromDecimal properly verifies the seconds-per-liquidity computation when liquidity is zero.

contract/r/scenario/upgrade/implements/v2_invalid/pool/test_impl.gno (2)

55-60: LGTM!

The rename from IncreaseObservationCardinalityLimit to IncreaseObservationCardinalityNext is consistently applied - method name, activation key, panic message, and delegation target are all updated.


415-419: LGTM!

The GetTWAP signature correctly updated to return (int32, *u256.Uint, error) matching the interface changes. The delegation to t.instance.GetTWAP properly passes through all three return values.

contract/r/gnoswap/pool/v1/oracle_test.gno (5)

117-152: LGTM!

Test cases properly updated to use cardinalityNext field naming consistently. The circular buffer tests correctly verify index wrapping and cardinality behavior.


167-243: LGTM!

Comprehensive test coverage for cardinality management including:

  • Valid increase scenarios
  • Decrease/same value rejection
  • Maximum cardinality acceptance
  • Uninitialized state handling

The error messages are descriptive and align with the implementation.


349-423: Excellent harmonic mean liquidity test coverage.

The test cases correctly replicate Uniswap V3's OracleLibrary.spec.ts scenarios:

  • Tick = 0 verification
  • Negative tick rounding (always toward negative infinity)
  • Liquidity overflow capping to MAX_UINT128

The calculateHarmonicAvgLiq helper accurately implements maxUint160 * period / (delta << 32).


425-474: LGTM!

Good error case coverage for getTWAP:

  • secondsAgo == 0 rejection
  • Uninitialized observation state
  • Zero cardinality (no observations)

These tests ensure the function fails gracefully with descriptive errors.


496-514: LGTM!

The new newObservationStateWithLiquidity helper properly seeds a 2-slot observation state with both tick cumulative delta and seconds-per-liquidity delta, enabling precise harmonic mean liquidity testing.

contract/r/gnoswap/pool/v1/oracle.gno (10)

11-11: LGTM!

The max160 constant (2^160 - 1) is correctly defined for harmonic mean liquidity calculation, matching Uniswap V3's approach.


16-69: LGTM - Solid TWAP implementation with harmonic mean liquidity.

The implementation correctly:

  1. Validates inputs (secondsAgo > 0, observation state initialized, cardinality > 0)
  2. Computes arithmetic mean tick with proper negative rounding (floor toward negative infinity)
  3. Calculates harmonic mean liquidity using secondsAgo * max160 / (delta << 32)
  4. Returns zero liquidity when secondsPerLiquidityDelta is zero

This aligns with Uniswap V3's oracle library pattern.


89-112: LGTM!

The renamed function properly validates:

  • Observation state initialization
  • Maximum cardinality bounds
  • Strictly greater than current requirement

Error handling is clear and follows the established pattern.


114-157: LGTM!

The transform function correctly:

  1. Validates non-negative time delta
  2. Computes tick cumulative as previous + tick * timeDelta
  3. Handles liquidity overflow check
  4. Uses max(1, liquidity) for seconds-per-liquidity calculation to avoid division by zero

218-220: LGTM!

The cardinality growth check correctly triggers when CardinalityNext > Cardinality and the current index is at the buffer boundary. This ensures seamless expansion of the circular buffer.


340-389: LGTM - Well-structured surrounding observations lookup.

The function correctly:

  1. Optimistically checks if target is at/after newest observation
  2. Returns nil for atOrAfter when target equals newest (same block optimization)
  3. Falls back to oldest observation and validates target is not too old
  4. Delegates to binary search for intermediate targets

The edge case handling for uninitialized slots at (index+1) % cardinality falling back to slot 0 is appropriate.


391-436: LGTM - Correct binary search over circular buffer.

The binary search correctly:

  1. Operates over the logical range [oldest, newest] mapped to [l, r]
  2. Handles uninitialized slots by searching higher (more recent)
  3. Uses lte for proper timestamp comparison in circular context
  4. Terminates when target is bracketed by beforeOrAt and atOrAfter

438-475: LGTM!

The observe function correctly iterates through the secondsAgos slice and collects cumulative values using observeSingle. The cardinality validation at the start prevents invalid state errors.


182-200: No issue: lte function safely handles positive int64 timestamps.

The lte function correctly handles int64 timestamps. All timestamps in this codebase come from time.Now().Unix(), which returns positive Unix time values. The function's wraparound logic (adding 1<<32) is intentional for handling timestamp comparisons across observation array boundaries during binary search in the circular buffer. An explicit check at line 116 (if timeDelta < 0) ensures timestamps are monotonic, confirming this function receives only valid positive values.


159-180: No action required — The grow() function correctly handles slot initialization. It allocates storage slots and marks them as uninitialized using the boolean initialized field (set to false by NewDefaultObservation()). The BlockTimestamp(1) is a harmless placeholder value. Code throughout the module uses the .Initialized() method to check slot validity before reading timestamps, so the pattern is sound and poses no logic issues.

Copy link

@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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
contract/r/gnoswap/pool/v1/oracle.gno (1)

155-176: Consider simplifying the zero check for uint16.

The condition currentCardinality <= 0 is effectively currentCardinality == 0 since uint16 cannot be negative. Consider using == 0 for clarity.

♻️ Suggested simplification
-	if currentCardinality <= 0 {
+	if currentCardinality == 0 {
 		return currentCardinality, errors.New("currentCardinality must be greater than 0")
 	}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e04fb0 and 9793f2e.

📒 Files selected for processing (2)
  • contract/r/gnoswap/pool/v1/oracle.gno
  • contract/r/gnoswap/pool/v1/oracle_test.gno
🧰 Additional context used
📓 Path-based instructions (1)
**/*.gno

⚙️ CodeRabbit configuration file

**/*.gno: # Gno Smart Contract Code Review Rules

About Gno

Gno is a blockchain smart contract language based on Go. Key differences from Go:

  • Realms (r/): Stateful contracts with persistent storage
  • Packages (p/): Pure library code without state
  • Realm crossing: Explicit syntax for inter-realm calls using cross keyword
  • Access control: Uses runtime.PreviousRealm() and runtime.OriginCaller() for caller verification
  • State persistence: Global variables in realms persist across transactions
  • Determinism: No goroutines, limited stdlib, panic-based error handling in realms

Critical Security Violations

Flag immediately as [CRITICAL]:

  1. Exported global variables - Allows anyone to modify state
var Admin address  // CRITICAL: Anyone can reassign
  1. Missing access control - No caller verification on state mutations
func DeleteData(_ realm) {
    data.Clear()  // CRITICAL: Anyone can delete
}
  1. Error return for security - Transaction succeeds even when unauthorized
func AdminOnly(_ realm) error {
    if !isAdmin() {
        return errors.New("denied")  // CRITICAL: Should panic
    }
}
  1. Direct external realm modification - Readonly taint violation
otherrealm.GlobalVar = "hacked"  // CRITICAL: Cannot modify external realm state

Required Patterns

Access Control (MUST on all state mutations)

Every function that modifies state MUST verify the caller:

import "chain/runtime"

func MutatingFunction(cur realm, params) {
    // For immediate caller (could be user or realm)
    caller := runtime.PreviousRealm().Address()

    // For original transaction signer (always a user)
    signer := runtime.OriginCaller()

    if caller != authorized {
        panic("unauthorized")  // MUST panic, not return error
    }

    // ... mutation logic ...
}

Why panic? Panics abort and rollback the transaction. Returning errors allows unauthorized transactions t...

Files:

  • contract/r/gnoswap/pool/v1/oracle_test.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
🧠 Learnings (1)
📚 Learning: 2026-01-05T03:20:38.341Z
Learnt from: notJoon
Repo: gnoswap-labs/gnoswap PR: 1118
File: contract/r/gnoswap/access/access.gno:37-43
Timestamp: 2026-01-05T03:20:38.341Z
Learning: In the gnoswap codebase, enforce consistency where event attribute names use prevAddr to refer to the caller/previous realm address in emitted events, as this is the established convention. When reviewing or adding new contracts, prefer using prevAddr over caller for event fields; if a semantic renaming is ever considered, document the rationale and ensure all related events across contracts are updated to maintain consistency.

Applied to files:

  • contract/r/gnoswap/pool/v1/oracle_test.gno
  • contract/r/gnoswap/pool/v1/oracle.gno
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: test-gnoswap (gnoswap/scenario/staker, contract/r/scenario/staker)
  • GitHub Check: test-gnoswap (gnoswap/scenario/router, contract/r/scenario/router)
  • GitHub Check: test-gnoswap (gnoswap/scenario/pool, contract/r/scenario/pool)
  • GitHub Check: test-gnoswap (gnoswap/staker, contract/r/gnoswap/staker)
  • GitHub Check: test-gnoswap (gnoswap/router/v1, contract/r/gnoswap/router/v1)
  • GitHub Check: test-gnoswap (gnoswap/staker/v1, contract/r/gnoswap/staker/v1)
  • GitHub Check: test-gnoswap (gnoswap/test/fuzz, contract/r/gnoswap/test/fuzz)
  • GitHub Check: test-gnoswap (gnoswap/scenario/position, contract/r/scenario/position)
  • GitHub Check: test-gnoswap (gnoswap/scenario/gov/governance, contract/r/scenario/gov/governance)
  • GitHub Check: test-gnoswap (gnoswap/gnft, contract/r/gnoswap/gnft)
  • GitHub Check: test-gnoswap (gnoswap/pool/v1, contract/r/gnoswap/pool/v1)
  • GitHub Check: run-integration-test
🔇 Additional comments (18)
contract/r/gnoswap/pool/v1/oracle.gno (12)

11-11: LGTM: max160 constant initialization.

The max160 constant correctly computes 2^160 - 1 using uint256 operations. This is used for harmonic mean liquidity calculation.


16-65: LGTM: getTWAP implementation with harmonic mean liquidity.

The TWAP calculation correctly:

  • Validates inputs before processing
  • Computes arithmetic mean tick with proper floor rounding for negative values
  • Calculates harmonic mean liquidity using the standard formula (secondsAgo * 2^160) / (delta << 32)

67-83: LGTM: Simple wrapper for observation writes.

The function properly initializes the observation state if needed and delegates to writeObservation.


85-108: LGTM: Cardinality management with proper validation.

All edge cases are validated: uninitialized state, maximum cardinality bounds, and monotonic increase requirement.


110-153: LGTM: Transform function with proper zero-liquidity handling.

The function correctly clamps liquidity to 1 when zero to prevent division by zero while still accumulating secondsPerLiquidity. This matches the expected oracle behavior.


198-234: LGTM: Observation write with cardinality expansion.

The cardinality expansion logic correctly triggers when the buffer is about to wrap and CardinalityNext > Cardinality.


236-243: LGTM: Simple observation getter.


245-254: LGTM: Index-based observation getter.


256-334: LGTM: Single observation query with interpolation.

The function correctly handles:

  • Current time queries (secondsAgo == 0)
  • Exact timestamp matches
  • Linear interpolation between observations using safe math operations

336-385: LGTM: Surrounding observations search with early exits.

The function correctly handles:

  • Optimistic newest-first lookup
  • Exact timestamp matches (returns nil for atOrAfter)
  • Validation that target is within observable window
  • Fallback to binary search for circular buffer navigation

434-471: LGTM: Batch observation query.

The function correctly validates cardinality and aggregates results from observeSingle for multiple time points.


178-196: The 1<<32 wraparound constant in the lte function handles circular buffer semantics for timestamp comparisons, not true int64 overflow. This pattern is from Uniswap V3's Oracle implementation and works correctly for the observation window's expected lifetime. No actual issue found.

contract/r/gnoswap/pool/v1/oracle_test.gno (6)

12-108: LGTM: Comprehensive write operation tests.

Good coverage of edge cases including same-timestamp no-op and past-timestamp error handling.


110-165: LGTM: Circular buffer behavior tests with updated API.

Tests correctly use the renamed cardinalityNext field and verify wrap-around behavior.


245-294: LGTM: Liquidity accumulation tests with various scenarios.

Test cases cover constant, changing, and zero liquidity scenarios with correct expected values.


296-347: LGTM: TWAP rounding verification.

Correctly tests floor rounding behavior for both positive and negative tick deltas.


425-462: LGTM: Error case coverage for getTWAP.

Tests correctly verify error conditions for zero secondsAgo and uninitialized observation state.


464-523: LGTM: Test helper functions.

Helper functions correctly seed observation states and create test pools for various test scenarios.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 9, 2026

@junghoon-vans junghoon-vans merged commit 2791427 into main Jan 9, 2026
59 checks passed
@junghoon-vans junghoon-vans deleted the pool-codebase-maintenance-and-cleanup branch January 9, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants