Skip to content

refactor: add shared safe math helpers#1240

Open
junghoon-vans wants to merge 5 commits intomainfrom
GSW-2578-safemath-packaging
Open

refactor: add shared safe math helpers#1240
junghoon-vans wants to merge 5 commits intomainfrom
GSW-2578-safemath-packaging

Conversation

@junghoon-vans
Copy link
Copy Markdown
Member

@junghoon-vans junghoon-vans commented Apr 8, 2026

Summary

  • add a shared gnsmath safe math package with coverage for common checked arithmetic helpers used across contract code
  • replace duplicated per-module safe math utilities in pool, router, staker, governance, launchpad, protocol fee, emission, position, and GNS code with the shared helpers
  • remove now-redundant local helper files and update affected tests to follow the shared implementation

Summary by CodeRabbit

  • New Features

    • Added a centralized safe arithmetic library for bounds-checked integer math and conversions.
  • Refactor

    • Replaced local numeric helpers across the codebase with the centralized safe arithmetic routines for consistent overflow/underflow handling.
    • Removed many legacy in-file numeric helper implementations.
  • Tests

    • Added comprehensive unit tests for the new safe arithmetic; updated many existing tests to use the new library and adjusted panic/message expectations.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0a15aa1-cf06-41ab-8e38-e2e3af86e0b1

📥 Commits

Reviewing files that changed from the base of the PR and between 4a08cbf and e3683fb.

📒 Files selected for processing (15)
  • contract/r/gnoswap/gov/staker/v1/emission_reward_state.gno
  • contract/r/gnoswap/gov/staker/v1/getter.gno
  • contract/r/gnoswap/gov/staker/v1/protocol_fee_reward_state.gno
  • contract/r/gnoswap/gov/staker/v1/staker_delegate.gno
  • contract/r/gnoswap/gov/staker/v1/staker_delegate_test.gno
  • contract/r/gnoswap/launchpad/v1/reward_manager.gno
  • contract/r/gnoswap/pool/v1/pool.gno
  • contract/r/gnoswap/pool/v1/position_test.gno
  • contract/r/gnoswap/position/v1/position.gno
  • contract/r/gnoswap/position/v1/utils_test.gno
  • contract/r/gnoswap/protocol_fee/v1/protocol_fee_test.gno
  • contract/r/gnoswap/router/v1/exact_in.gno
  • contract/r/gnoswap/router/v1/exact_out.gno
  • contract/r/gnoswap/staker/v1/staker.gno
  • contract/r/gnoswap/staker/v1/staker_test.gno
✅ Files skipped from review due to trivial changes (3)
  • contract/r/gnoswap/launchpad/v1/reward_manager.gno
  • contract/r/gnoswap/gov/staker/v1/staker_delegate.gno
  • contract/r/gnoswap/protocol_fee/v1/protocol_fee_test.gno
🚧 Files skipped from review as they are similar to previous changes (5)
  • contract/r/gnoswap/gov/staker/v1/getter.gno
  • contract/r/gnoswap/position/v1/position.gno
  • contract/r/gnoswap/gov/staker/v1/staker_delegate_test.gno
  • contract/r/gnoswap/pool/v1/pool.gno
  • contract/r/gnoswap/gov/staker/v1/protocol_fee_reward_state.gno

Walkthrough

Adds a new package gnsmath with panic-on-error safe integer math and conversion helpers, and replaces numerous in-repo local safe math helpers with calls to gnsmath.Safe* across the codebase (logic unchanged; helpers removed/updated).

Changes

Cohort / File(s) Summary
New gnsmath package
contract/p/gnoswap/gnsmath/safe_math.gno, contract/p/gnoswap/gnsmath/safe_math_test.gno
Introduces centralized safe int math/conversion APIs (SafeAddInt64, SafeSubInt64, SafeMulInt64, SafeMulDivInt64, SafeConvertToInt64/Uint64/Int128, SafeParseInt64, SafeUint64ToInt64, SafeAbsInt64) with comprehensive tests.
Emission
contract/r/gnoswap/emission/... (distribution.gno, emission.gno, getter.gno, utils.gno, security_test.gno)
Replaces local safe add/sub helpers with gnsmath.Safe*; deleted duplicate helpers in utils.
GNS
contract/r/gnoswap/gns/... (emission_state.gno, gns.gno, halving.gno, utils.gno, utils_test.gno)
Swapped local safe arithmetic for gnsmath calls and removed prior in-file helpers/tests.
Governance (v1)
contract/r/gnoswap/gov/governance/v1/... (multiple files)
Replaced local safe add/sub/muldiv with gnsmath.Safe* in voting/quorum calculations; removed corresponding local helpers and tests.
Gov Staker
contract/r/gnoswap/gov/staker/... (many files)
Centralized safe math to gnsmath across delegation, withdraws, reward state/manager, snapshots, and related tests; deleted local helper implementations.
Launchpad
contract/r/gnoswap/launchpad/v1/... (multiple files)
Replaced local safe arithmetic/conversion with gnsmath.Safe* for tier distribution, rewards, and deposits; removed local helpers and related tests.
Pool & Swap
contract/r/gnoswap/pool/v1/... (pool, swap, transfer, protocol_fee, utils, tests)
Replaced local conversion/arith helpers with gnsmath equivalents (int128/int64/u256 conversions, add/sub/muldiv, parse); removed many local utilities and extensive test cases.
Position
contract/r/gnoswap/position/v1/... (burn, position, utils, tests)
Uses gnsmath conversions/add/sub in fee and owed-token logic; removed local utility functions and tests.
Protocol Fee
contract/r/gnoswap/protocol_fee/v1/...
Swapped local safe arithmetic for gnsmath in distribution and accumulator updates; deleted local utils.
Router
contract/r/gnoswap/router/v1/... (base, exact_in/out, router_dry, utils, tests)
Centralized parsing and safe arithmetic to gnsmath for route processing, amount parsing, mul/div calculations; removed local safe helpers and related tests.
Staker (v1)
contract/r/gnoswap/staker/v1/... (reward calculations, utils, tests)
Replaced many reward/time arithmetic helpers with gnsmath.Safe*; removed multiple local utilities and tests.
Misc other modules
assorted files across repo (gov, emission, staker, launchpad, pool, router, protocol_fee, position)
Numerous import updates to gno.land/p/gnoswap/gnsmath and wholesale replacement of local safe* helpers with gnsmath.Safe* calls.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main objective of the pull request: extracting and centralizing safe math helpers into a shared gnsmath package and refactoring all modules to use them.
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.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch GSW-2578-safemath-packaging

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.

@junghoon-vans
Copy link
Copy Markdown
Member Author

Gas Report Comparison
Name Metric Latest Previous Change %
TickMathGetSqrtRatioAtTick (minTick) Gas Used 1,554,188 1,554,188 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 677,324 677,324 0 0.00%
TickMathGetSqrtRatioAtTick (maxTick) Gas Used 873,832 873,832 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 863,400 863,400 0 0.00%
TickMathGetSqrtRatioAtTick (zero) Gas Used 134,165 134,165 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 134,165 134,165 0 0.00%
TickMathGetSqrtRatioAtTick Gas Used 652,288 652,288 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 641,936 641,936 0 0.00%
TickMathGetTickAtSqrtRatio Gas Used 1,792,151 1,792,151 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 1,635,447 1,635,447 0 0.00%
GetLiquidityForAmounts Gas Used 1,442,983 1,442,983 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 1,422,279 1,422,279 0 0.00%
GetAmountsForLiquidity Gas Used 1,333,931 1,333,931 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 1,333,931 1,333,931 0 0.00%
LiquidityMathAddDelta (positive) Gas Used 231,282 231,282 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 198,242 198,242 0 0.00%
LiquidityMathAddDelta (negative) Gas Used 211,276 211,276 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 211,276 211,276 0 0.00%
LiquidityMathAddDelta Gas Used 198,242 198,242 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 198,242 198,242 0 0.00%
GetAmount0Delta Gas Used 3,945,306 3,945,226 +80 ⚠️ 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 1,486,687 1,486,687 0 0.00%
GetAmount1Delta Gas Used 1,050,679 1,050,599 +80 ⚠️ 0.01%
Storage Diff 0 0 0 0.00%
CPU Cycles 1,040,247 1,040,247 0 0.00%
SwapMathComputeSwapStep Gas Used 1,879,867 1,879,867 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 1,859,003 1,859,003 0 0.00%
Propose Community Pool Spend Gas Used 4,331,189 2,697,859 +1,633,330 ⚠️ 60.54%
Storage Diff 13,370 13,370 0 0.00%
CPU Cycles 1,339,441 1,125,879 +213,562 ⚠️ 18.97%
Propose Parameter Change Gas Used 3,959,262 2,136,692 +1,822,570 ⚠️ 85.30%
Storage Diff 12,351 12,351 0 0.00%
CPU Cycles 1,593,902 1,380,340 +213,562 ⚠️ 15.47%
Vote Gas Used 1,100,583 1,100,461 +122 ⚠️ 0.01%
Storage Diff 2,064 2,064 0 0.00%
CPU Cycles 872,097 871,975 +122 ⚠️ 0.01%
Execute Gas Used 3,927,078 3,926,956 +122 ⚠️ 0.00%
Storage Diff 76 76 0 0.00%
CPU Cycles 522,037 521,915 +122 ⚠️ 0.02%
Propose Text Gas Used 3,113,066 1,290,496 +1,822,570 ⚠️ 141.23%
Storage Diff 11,152 11,152 0 0.00%
CPU Cycles 1,181,946 968,384 +213,562 ⚠️ 22.05%
Propose Text with Inactive: 100 Gas Used 2,295,889 2,082,327 +213,562 ⚠️ 10.26%
Storage Diff 11,654 11,654 0 0.00%
CPU Cycles 1,542,180 1,328,618 +213,562 ⚠️ 16.07%
CollectUndelegatedGns (100 delegations, 1 withdraws) Gas Used 39,006,916 38,994,716 +12,200 ⚠️ 0.03%
Storage Diff 0 0 0 0.00%
CPU Cycles 33,496,142 33,483,942 +12,200 ⚠️ 0.04%
CollectUndelegatedGns (10 delegations, 10 withdraws) Gas Used 4,424,873 4,418,163 +6,710 ⚠️ 0.15%
Storage Diff 0 0 0 0.00%
CPU Cycles 3,799,547 3,792,837 +6,710 ⚠️ 0.18%
CollectUndelegatedGns (10 delegations, 1 withdraws) Gas Used 1,714,188 1,712,968 +1,220 ⚠️ 0.07%
Storage Diff 0 0 0 0.00%
CPU Cycles 1,091,402 1,090,182 +1,220 ⚠️ 0.11%
CollectUndelegatedGns (10 delegations, 50 withdraws) Gas Used 16,462,881 16,431,771 +31,110 ⚠️ 0.19%
Storage Diff 0 0 0 0.00%
CPU Cycles 15,835,747 15,804,637 +31,110 ⚠️ 0.20%
CollectUndelegatedGns (10 delegations, 5 withdraws) Gas Used 2,917,808 2,914,148 +3,660 ⚠️ 0.13%
Storage Diff 0 0 0 0.00%
CPU Cycles 2,295,022 2,291,362 +3,660 ⚠️ 0.16%
CollectUndelegatedGns (1 delegation, 10 withdraws) Gas Used 359,007 358,336 +671 ⚠️ 0.19%
Storage Diff 0 0 0 0.00%
CPU Cycles 220,281 219,610 +671 ⚠️ 0.31%
CollectUndelegatedGns (1 delegation, 1 withdraws) Gas Used 274,938 274,816 +122 ⚠️ 0.04%
Storage Diff 0 0 0 0.00%
CPU Cycles 136,212 136,090 +122 ⚠️ 0.09%
CollectUndelegatedGns (1 delegation, 50 withdraws) Gas Used 732,727 729,616 +3,111 ⚠️ 0.43%
Storage Diff 0 0 0 0.00%
CPU Cycles 593,921 590,810 +3,111 ⚠️ 0.53%
CollectUndelegatedGns (1 delegation, 5 withdraws) Gas Used 312,302 311,936 +366 ⚠️ 0.12%
Storage Diff 0 0 0 0.00%
CPU Cycles 173,576 173,210 +366 ⚠️ 0.21%
CollectReward (100 delegations, 1 withdraws) Gas Used 1,327,780 1,327,531 +249 ⚠️ 0.02%
Storage Diff 1,384 1,384 0 0.00%
CPU Cycles 813,367 813,118 +249 ⚠️ 0.03%
CollectReward (10 delegations, 10 withdraws) Gas Used 1,326,712 1,326,463 +249 ⚠️ 0.02%
Storage Diff 1,378 1,378 0 0.00%
CPU Cycles 813,367 813,118 +249 ⚠️ 0.03%
CollectReward (10 delegations, 1 withdraws) Gas Used 1,326,364 1,326,115 +249 ⚠️ 0.02%
Storage Diff 1,378 1,378 0 0.00%
CPU Cycles 813,019 812,770 +249 ⚠️ 0.03%
CollectReward (10 delegations, 50 withdraws) Gas Used 1,326,712 1,326,463 +249 ⚠️ 0.02%
Storage Diff 1,378 1,378 0 0.00%
CPU Cycles 813,367 813,118 +249 ⚠️ 0.03%
CollectReward (10 delegations, 5 withdraws) Gas Used 1,326,423 1,326,174 +249 ⚠️ 0.02%
Storage Diff 1,378 1,378 0 0.00%
CPU Cycles 813,078 812,829 +249 ⚠️ 0.03%
CollectReward (1 delegation, 10 withdraws) Gas Used 2,010,166 2,009,917 +249 ⚠️ 0.01%
Storage Diff 1,402 1,402 0 0.00%
CPU Cycles 812,373 812,124 +249 ⚠️ 0.03%
CollectReward (1 delegation, 1 withdraws) Gas Used 2,005,058 2,004,809 +249 ⚠️ 0.01%
Storage Diff 1,402 1,402 0 0.00%
CPU Cycles 807,265 807,016 +249 ⚠️ 0.03%
CollectReward (1 delegation, 50 withdraws) Gas Used 2,010,225 2,009,976 +249 ⚠️ 0.01%
Storage Diff 1,402 1,402 0 0.00%
CPU Cycles 812,432 812,183 +249 ⚠️ 0.03%
CollectReward (1 delegation, 5 withdraws) Gas Used 2,010,166 2,009,917 +249 ⚠️ 0.01%
Storage Diff 1,402 1,402 0 0.00%
CPU Cycles 812,373 812,124 +249 ⚠️ 0.03%
Delegate Gas Used 9,354,276 8,490,884 +863,392 ⚠️ 10.17%
Storage Diff 19,365 19,366 -1 ⚡️ -0.01%
CPU Cycles 873,015 872,405 +610 ⚠️ 0.07%
Undelegate Gas Used 2,463,661 2,462,774 +887 ⚠️ 0.04%
Storage Diff 927 927 0 0.00%
CPU Cycles 1,250,696 1,249,809 +887 ⚠️ 0.07%
Undelegate (5 delegations, cached external calls) Gas Used 7,049,451 7,045,016 +4,435 ⚠️ 0.06%
Storage Diff 3,450 3,450 0 0.00%
CPU Cycles 5,914,683 5,910,248 +4,435 ⚠️ 0.08%
Delegate (cached external calls) Gas Used 2,317,400 2,316,790 +610 ⚠️ 0.03%
Storage Diff 10,609 10,609 0 0.00%
CPU Cycles 1,254,293 1,253,683 +610 ⚠️ 0.05%
Undelegate (early exit, 3 of 10 delegations) Gas Used 4,874,502 4,870,987 +3,515 ⚠️ 0.07%
Storage Diff 2,076 2,076 0 0.00%
CPU Cycles 3,922,138 3,918,623 +3,515 ⚠️ 0.09%
Redelegate Gas Used 3,516,634 3,515,320 +1,314 ⚠️ 0.04%
Storage Diff 10,846 10,846 0 0.00%
CPU Cycles 2,042,793 2,041,479 +1,314 ⚠️ 0.06%
Redelegate (50 of 100 delegations, optimized) Gas Used 70,577,899 70,527,037 +50,862 ⚠️ 0.07%
Storage Diff 10,895 10,895 0 0.00%
CPU Cycles 62,648,354 62,597,444 +50,910 ⚠️ 0.08%
Undelegate (50 delegatees, large AVL traversal) Gas Used 2,295,475 2,294,588 +887 ⚠️ 0.04%
Storage Diff 723 723 0 0.00%
CPU Cycles 1,372,077 1,371,190 +887 ⚠️ 0.06%
CollectDepositGns (deposit 1/5, remaining 4) Gas Used 2,559,379 2,558,344 +1,035 ⚠️ 0.04%
Storage Diff 202 202 0 0.00%
CPU Cycles 1,309,594 1,308,559 +1,035 ⚠️ 0.08%
CollectDepositGns (deposit 2/5, remaining 3) Gas Used 2,345,044 2,344,009 +1,035 ⚠️ 0.04%
Storage Diff -3,687 -3,687 0 0.00%
CPU Cycles 1,164,994 1,163,959 +1,035 ⚠️ 0.09%
CollectDepositGns (deposit 3/5, remaining 2) Gas Used 2,294,038 2,293,003 +1,035 ⚠️ 0.05%
Storage Diff -3,870 -3,870 0 0.00%
CPU Cycles 1,142,647 1,141,612 +1,035 ⚠️ 0.09%
CollectDepositGns (deposit 4/5, remaining 1) Gas Used 2,334,838 2,333,803 +1,035 ⚠️ 0.04%
Storage Diff -3,870 -3,870 0 0.00%
CPU Cycles 1,153,713 1,152,678 +1,035 ⚠️ 0.09%
CollectDepositGns (deposit 5/5, remaining 0) Gas Used 2,221,171 2,220,136 +1,035 ⚠️ 0.05%
Storage Diff -5,972 -5,972 0 0.00%
CPU Cycles 1,140,384 1,139,349 +1,035 ⚠️ 0.09%
Launchpad CollectDepositGns Gas Used 2,274,164 2,273,129 +1,035 ⚠️ 0.05%
Storage Diff -1,864 -1,864 0 0.00%
CPU Cycles 1,233,905 1,232,870 +1,035 ⚠️ 0.08%
CollectProtocolFee (1 token) Gas Used 1,411,317 1,411,068 +249 ⚠️ 0.02%
Storage Diff 3,433 3,433 0 0.00%
CPU Cycles 873,762 873,513 +249 ⚠️ 0.03%
CollectProtocolFee (2 tokens) Gas Used 2,081,419 2,081,015 +404 ⚠️ 0.02%
Storage Diff 6,832 6,832 0 0.00%
CPU Cycles 1,368,930 1,368,526 +404 ⚠️ 0.03%
CollectProtocolFee (5 tokens) Gas Used 4,128,825 4,127,956 +869 ⚠️ 0.02%
Storage Diff 17,020 17,020 0 0.00%
CPU Cycles 2,861,496 2,860,627 +869 ⚠️ 0.03%
Launchpad CollectProtocolFee (tokens: 10) Gas Used 7,530,246 7,528,602 +1,644 ⚠️ 0.02%
Storage Diff 34,261 34,261 0 0.00%
CPU Cycles 5,328,501 5,326,857 +1,644 ⚠️ 0.03%
Launchpad CollectRewardByDepositId Gas Used 975,776 975,262 +514 ⚠️ 0.05%
Storage Diff 2,047 2,047 0 0.00%
CPU Cycles 525,412 524,898 +514 ⚠️ 0.10%
Create Launchpad Project Gas Used 11,354,682 9,052,636 +2,302,046 ⚠️ 25.43%
Storage Diff 29,077 29,077 0 0.00%
CPU Cycles 2,428,072 1,787,996 +640,076 ⚠️ 35.80%
Launchpad DepositGns Gas Used 6,107,666 6,160,202 -52,536 ⚡️ -0.85%
Storage Diff 12,122 12,122 0 0.00%
CPU Cycles 1,300,130 1,299,642 +488 ⚠️ 0.04%
Launchpad TransferLeftFromProjectByAdmin Gas Used 1,293,040 1,309,726 -16,686 ⚡️ -1.27%
Storage Diff 5 5 0 0.00%
CPU Cycles 443,268 441,682 +1,586 ⚠️ 0.36%
CreatePool Gas Used 7,021,483 7,039,755 -18,272 ⚡️ -0.26%
Storage Diff 11,336 11,336 0 0.00%
CPU Cycles 2,186,419 2,186,419 0 0.00%
Mint (fee:3000, wide range) Gas Used 18,326,421 17,727,834 +598,587 ⚠️ 3.38%
Storage Diff 17,577 17,577 0 0.00%
CPU Cycles 12,213,710 11,779,539 +434,171 ⚠️ 3.69%
Swap (gns -> wugnot, fee:500) Gas Used 30,785,906 30,842,873 -56,967 ⚡️ -0.18%
Storage Diff 735 735 0 0.00%
CPU Cycles 19,177,698 19,177,454 +244 ⚠️ 0.00%
DecreaseLiquidity Gas Used 12,730,291 11,858,621 +871,670 ⚠️ 7.35%
Storage Diff -2,106 -2,106 0 0.00%
CPU Cycles 10,574,304 9,702,634 +871,670 ⚠️ 8.98%
IncreaseLiquidity Gas Used 9,811,789 9,376,712 +435,077 ⚠️ 4.64%
Storage Diff -2,138 -2,138 0 0.00%
CPU Cycles 9,129,288 8,694,211 +435,077 ⚠️ 5.00%
Mint (bar:foo:500) Gas Used 21,710,046 21,156,739 +553,307 ⚠️ 2.62%
Storage Diff 17,580 17,580 0 0.00%
CPU Cycles 11,391,785 10,957,614 +434,171 ⚠️ 3.96%
CollectFee (with unwrap) Gas Used 5,898,441 5,497,104 +401,337 ⚠️ 7.30%
Storage Diff 4,343 4,343 0 0.00%
CPU Cycles 2,870,642 2,434,537 +436,105 ⚠️ 17.91%
DecreaseLiquidity (w. Remove) Gas Used 12,161,590 11,289,920 +871,670 ⚠️ 7.72%
Storage Diff 4,338 4,338 0 0.00%
CPU Cycles 8,637,362 7,765,692 +871,670 ⚠️ 11.22%
Mint (reposition) Gas Used 13,427,476 12,993,305 +434,171 ⚠️ 3.34%
Storage Diff 15,014 15,014 0 0.00%
CPU Cycles 12,263,777 11,829,606 +434,171 ⚠️ 3.67%
SetPoolTier (tier 1) Gas Used 4,290,852 4,318,143 -27,291 ⚡️ -0.63%
Storage Diff 21,548 21,548 0 0.00%
CPU Cycles 1,348,155 1,348,155 0 0.00%
StakeToken Gas Used 10,016,036 10,015,609 +427 ⚠️ 0.00%
Storage Diff 22,020 22,020 0 0.00%
CPU Cycles 6,636,691 6,636,264 +427 ⚠️ 0.01%
ExactInSingleSwapRoute(grc20) - fee:10000 Gas Used 28,787,847 28,938,212 -150,365 ⚡️ -0.52%
Storage Diff 2,283 2,284 -1 ⚡️ -0.04%
CPU Cycles 10,315,637 10,314,635 +1,002 ⚠️ 0.01%
ExactInSingleSwapRoute(grc20) - fee:100 Gas Used 30,730,576 30,880,941 -150,365 ⚡️ -0.49%
Storage Diff 2,387 2,388 -1 ⚡️ -0.04%
CPU Cycles 12,298,110 12,297,108 +1,002 ⚠️ 0.01%
ExactInSingleSwapRoute(grc20) - fee:3000 Gas Used 29,208,057 29,358,422 -150,365 ⚡️ -0.51%
Storage Diff 2,285 2,286 -1 ⚡️ -0.04%
CPU Cycles 10,319,982 10,318,980 +1,002 ⚠️ 0.01%
ExactInSingleSwapRoute(grc20) - fee:500 Gas Used 28,668,894 28,819,259 -150,365 ⚡️ -0.52%
Storage Diff 2,283 2,284 -1 ⚡️ -0.04%
CPU Cycles 10,238,076 10,237,074 +1,002 ⚠️ 0.01%
ExactInSwapRoute(grc20) - fee:10000 Gas Used 28,525,288 28,675,653 -150,365 ⚡️ -0.52%
Storage Diff 2,283 2,284 -1 ⚡️ -0.04%
CPU Cycles 10,053,078 10,052,076 +1,002 ⚠️ 0.01%
ExactInSwapRoute(grc20) - fee:100 Gas Used 30,474,561 30,624,926 -150,365 ⚡️ -0.49%
Storage Diff 2,387 2,388 -1 ⚡️ -0.04%
CPU Cycles 12,042,095 12,041,093 +1,002 ⚠️ 0.01%
ExactInSwapRoute(grc20) - fee:3000 Gas Used 28,948,770 29,099,135 -150,365 ⚡️ -0.52%
Storage Diff 2,285 2,286 -1 ⚡️ -0.04%
CPU Cycles 10,060,695 10,059,693 +1,002 ⚠️ 0.01%
ExactInSwapRoute(grc20) - fee:500 Gas Used 28,412,879 28,563,244 -150,365 ⚡️ -0.53%
Storage Diff 2,283 2,284 -1 ⚡️ -0.04%
CPU Cycles 9,982,061 9,981,059 +1,002 ⚠️ 0.01%
ExactOutSingleSwapRoute(grc20) - fee:10000 Gas Used 29,576,077 29,726,130 -150,053 ⚡️ -0.50%
Storage Diff 2,283 2,284 -1 ⚡️ -0.04%
CPU Cycles 11,114,299 11,112,905 +1,394 ⚠️ 0.01%
ExactOutSingleSwapRoute(grc20) - fee:100 Gas Used 31,448,761 31,598,814 -150,053 ⚡️ -0.47%
Storage Diff 2,387 2,388 -1 ⚡️ -0.04%
CPU Cycles 13,026,727 13,025,333 +1,394 ⚠️ 0.01%
ExactOutSingleSwapRoute(grc20) - fee:3000 Gas Used 29,930,826 30,080,879 -150,053 ⚡️ -0.50%
Storage Diff 2,285 2,286 -1 ⚡️ -0.04%
CPU Cycles 11,063,535 11,062,141 +1,394 ⚠️ 0.01%
ExactOutSingleSwapRoute(grc20) - fee:500 Gas Used 29,406,396 29,556,449 -150,053 ⚡️ -0.51%
Storage Diff 2,283 2,284 -1 ⚡️ -0.04%
CPU Cycles 10,996,362 10,994,968 +1,394 ⚠️ 0.01%
ExactOutSwapRoute(grc20) - fee:10000 Gas Used 29,324,806 29,474,859 -150,053 ⚡️ -0.51%
Storage Diff 2,283 2,284 -1 ⚡️ -0.04%
CPU Cycles 10,863,028 10,861,634 +1,394 ⚠️ 0.01%
ExactOutSwapRoute(grc20) - fee:100 Gas Used 31,204,034 31,354,087 -150,053 ⚡️ -0.48%
Storage Diff 2,387 2,388 -1 ⚡️ -0.04%
CPU Cycles 12,782,000 12,780,606 +1,394 ⚠️ 0.01%
ExactOutSwapRoute(grc20) - fee:3000 Gas Used 29,682,827 29,832,880 -150,053 ⚡️ -0.50%
Storage Diff 2,285 2,286 -1 ⚡️ -0.04%
CPU Cycles 10,815,536 10,814,142 +1,394 ⚠️ 0.01%
ExactOutSwapRoute(grc20) - fee:500 Gas Used 29,161,669 29,311,722 -150,053 ⚡️ -0.51%
Storage Diff 2,283 2,284 -1 ⚡️ -0.04%
CPU Cycles 10,751,635 10,750,241 +1,394 ⚠️ 0.01%
BuildSingleHopRoutePath Gas Used 1,743,185 1,743,185 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 23,398 23,398 0 0.00%
MultiHop ExactIn (2 hops) Gas Used 30,159,099 30,157,773 +1,326 ⚠️ 0.00%
Storage Diff 2,504 2,504 0 0.00%
CPU Cycles 20,898,606 20,897,360 +1,246 ⚠️ 0.01%
MultiHop ExactOut (2 hops) Gas Used 33,930,112 33,928,352 +1,760 ⚠️ 0.01%
Storage Diff 26 26 0 0.00%
CPU Cycles 32,530,618 32,528,858 +1,760 ⚠️ 0.01%
MultiHop ExactIn (3 hops) Gas Used 32,393,521 32,392,031 +1,490 ⚠️ 0.00%
Storage Diff 235 235 0 0.00%
CPU Cycles 30,490,165 30,488,675 +1,490 ⚠️ 0.00%
MultiHop ExactOut (3 hops) Gas Used 51,858,613 51,856,548 +2,065 ⚠️ 0.00%
Storage Diff 14 14 0 0.00%
CPU Cycles 49,971,217 49,969,152 +2,065 ⚠️ 0.00%
MultiRoute ExactIn (50:50 split) Gas Used 32,550,738 32,549,004 +1,734 ⚠️ 0.01%
Storage Diff 112 112 0 0.00%
CPU Cycles 30,118,313 30,116,579 +1,734 ⚠️ 0.01%
MultiRoute ExactOut (50:50 split) Gas Used 45,234,111 45,231,863 +2,248 ⚠️ 0.00%
Storage Diff 2 2 0 0.00%
CPU Cycles 42,840,990 42,838,742 +2,248 ⚠️ 0.01%
CollectReward (only Internal Reward) Gas Used 12,727,594 12,724,362 +3,232 ⚠️ 0.03%
Storage Diff 7,112 7,112 0 0.00%
CPU Cycles 9,670,405 9,667,173 +3,232 ⚠️ 0.03%
CollectReward 2nd (only Internal Reward) Gas Used 11,615,650 11,612,418 +3,232 ⚠️ 0.03%
Storage Diff 2,879 2,879 0 0.00%
CPU Cycles 9,554,550 9,551,318 +3,232 ⚠️ 0.03%
CollectReward With External Rewards (1 incentives) Gas Used 18,884,542 18,879,725 +4,817 ⚠️ 0.03%
Storage Diff 7,719 7,719 0 0.00%
CPU Cycles 15,266,981 15,262,164 +4,817 ⚠️ 0.03%
CollectReward With External Rewards 2nd (1 incentives) Gas Used 17,430,048 17,425,231 +4,817 ⚠️ 0.03%
Storage Diff 2,908 2,908 0 0.00%
CPU Cycles 14,902,892 14,898,075 +4,817 ⚠️ 0.03%
CollectReward With External Rewards (5 incentives) Gas Used 43,649,912 43,638,755 +11,157 ⚠️ 0.03%
Storage Diff 9,556 9,556 0 0.00%
CPU Cycles 37,224,439 37,213,282 +11,157 ⚠️ 0.03%
CollectReward With External Rewards 2nd (5 incentives) Gas Used 41,638,398 41,627,241 +11,157 ⚠️ 0.03%
Storage Diff 2,879 2,879 0 0.00%
CPU Cycles 36,417,650 36,406,493 +11,157 ⚠️ 0.03%
CreateExternalIncentive Gas Used 3,039,231 3,039,694 -463 ⚡️ -0.02%
Storage Diff 30,770 30,770 0 0.00%
CPU Cycles 1,575,717 1,575,656 +61 ⚠️ 0.00%
EndExternalIncentive Gas Used 1,330,303 1,330,644 -341 ⚡️ -0.03%
Storage Diff -5,300 -5,300 0 0.00%
CPU Cycles 746,108 745,925 +183 ⚠️ 0.02%
RegisterInitializer (v1) Gas Used 328,756 328,756 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 35,764 35,764 0 0.00%
RegisterInitializer (v2) Gas Used 35,764 35,764 0 0.00%
Storage Diff 0 0 0 0.00%
CPU Cycles 35,764 35,764 0 0.00%

@junghoon-vans junghoon-vans changed the title Add shared safe math helpers and reuse them across contracts refactor: add shared safe math helpers Apr 8, 2026
Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (4)
contract/r/gnoswap/staker/v1/reward_calculation_pool_tier.gno (1)

107-108: Inconsistent use of safe arithmetic for timestamp addition.

Line 101 uses gnsmath.SafeAddInt64(currentTime, 1) for overflow-safe addition, but Line 107 uses plain currentTime+1. For consistency and safety, both additions should use the safe helper.

♻️ Suggested fix for consistency
-	pools.set(initialPoolPath, sr.NewPool(initialPoolPath, currentTime+1))
-	result.changeTier(currentTime+1, pools, initialPoolPath, 1)
+	pools.set(initialPoolPath, sr.NewPool(initialPoolPath, gnsmath.SafeAddInt64(currentTime, 1)))
+	result.changeTier(gnsmath.SafeAddInt64(currentTime, 1), pools, initialPoolPath, 1)
contract/r/gnoswap/pool/v1/position.gno (1)

6-6: Consider renaming the import alias for clarity.

The package gno.land/p/gnoswap/gnsmath is imported as plp, which may be a legacy alias from when the package had different contents. For consistency with other files in this PR that use gnsmath as the alias, consider updating this alias.

♻️ Optional: Update alias for consistency
-	plp "gno.land/p/gnoswap/gnsmath"
+	gnsmath "gno.land/p/gnoswap/gnsmath"

Then update the call sites accordingly (lines 94-95, 106, 111).

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

14-14: LGTM — Safe conversions correctly applied in swap computations.

The SafeConvertToInt64 calls at lines 341, 345, 395, and 400 properly protect against overflow when converting u256.Uint amounts to int64 for balance comparisons and protocol fee accumulation. The SafeAddInt64 calls ensure fee accumulation doesn't overflow.

Note: The import alias plp for gnsmath is unconventional (typically plp suggests "pool package"). Consider renaming to gnsmath for clarity, though this is a minor stylistic point.

Also applies to: 341-341, 345-345, 395-395, 400-400

contract/r/gnoswap/gov/staker/v1/protocol_fee_reward_manager.gno (1)

175-179: Note: Pre-overflow check may be redundant with SafeAddInt64.

Lines 176-178 perform an explicit overflow check before calling gnsmath.SafeAddInt64 at line 179. If SafeAddInt64 panics on overflow (which is the expected behavior for safe math helpers), this pre-check is redundant. However, the current approach provides a more descriptive error message, which may be intentional.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2b05f2b9-f723-4e4c-af68-96d60657c0e3

📥 Commits

Reviewing files that changed from the base of the PR and between 3e051b0 and 4a08cbf.

📒 Files selected for processing (84)
  • contract/p/gnoswap/gnsmath/safe_math.gno
  • contract/p/gnoswap/gnsmath/safe_math_test.gno
  • contract/r/gnoswap/emission/distribution.gno
  • contract/r/gnoswap/emission/emission.gno
  • contract/r/gnoswap/emission/getter.gno
  • contract/r/gnoswap/emission/security_test.gno
  • contract/r/gnoswap/emission/utils.gno
  • contract/r/gnoswap/gns/emission_state.gno
  • contract/r/gnoswap/gns/gns.gno
  • contract/r/gnoswap/gns/halving.gno
  • contract/r/gnoswap/gns/utils.gno
  • contract/r/gnoswap/gns/utils_test.gno
  • contract/r/gnoswap/gov/governance/v1/governance_propose.gno
  • contract/r/gnoswap/gov/governance/v1/governance_vote.gno
  • contract/r/gnoswap/gov/governance/v1/proposal_schedule_status.gno
  • contract/r/gnoswap/gov/governance/v1/proposal_status.gno
  • contract/r/gnoswap/gov/governance/v1/proposal_status_test.gno
  • contract/r/gnoswap/gov/governance/v1/proposal_vote_status.gno
  • contract/r/gnoswap/gov/governance/v1/proposal_vote_status_test.gno
  • contract/r/gnoswap/gov/governance/v1/utils.gno
  • contract/r/gnoswap/gov/governance/v1/utils_test.gno
  • contract/r/gnoswap/gov/staker/delegation_withdraw.gno
  • contract/r/gnoswap/gov/staker/util.gno
  • contract/r/gnoswap/gov/staker/v1/delegation.gno
  • contract/r/gnoswap/gov/staker/v1/delegation_withdraw.gno
  • contract/r/gnoswap/gov/staker/v1/emission_reward_manager.gno
  • contract/r/gnoswap/gov/staker/v1/emission_reward_state.gno
  • contract/r/gnoswap/gov/staker/v1/getter.gno
  • contract/r/gnoswap/gov/staker/v1/launchpad_project_deposits.gno
  • contract/r/gnoswap/gov/staker/v1/protocol_fee_reward_manager.gno
  • contract/r/gnoswap/gov/staker/v1/protocol_fee_reward_state.gno
  • contract/r/gnoswap/gov/staker/v1/staker_delegate.gno
  • contract/r/gnoswap/gov/staker/v1/staker_delegate_test.gno
  • contract/r/gnoswap/gov/staker/v1/staker_delegation_snapshot.gno
  • contract/r/gnoswap/gov/staker/v1/state.gno
  • contract/r/gnoswap/gov/staker/v1/util.gno
  • contract/r/gnoswap/gov/staker/v1/util_test.gno
  • contract/r/gnoswap/launchpad/v1/launchpad_project.gno
  • contract/r/gnoswap/launchpad/v1/launchpad_reward.gno
  • contract/r/gnoswap/launchpad/v1/launchpad_reward_test.gno
  • contract/r/gnoswap/launchpad/v1/project.gno
  • contract/r/gnoswap/launchpad/v1/project_tier.gno
  • contract/r/gnoswap/launchpad/v1/reward_manager.gno
  • contract/r/gnoswap/launchpad/v1/reward_state.gno
  • contract/r/gnoswap/launchpad/v1/utils.gno
  • contract/r/gnoswap/launchpad/v1/utils_test.gno
  • 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/gnoswap/pool/v1/protocol_fee.gno
  • contract/r/gnoswap/pool/v1/swap.gno
  • contract/r/gnoswap/pool/v1/transfer.gno
  • contract/r/gnoswap/pool/v1/utils.gno
  • contract/r/gnoswap/pool/v1/utils_test.gno
  • contract/r/gnoswap/position/v1/burn.gno
  • contract/r/gnoswap/position/v1/position.gno
  • contract/r/gnoswap/position/v1/utils.gno
  • contract/r/gnoswap/position/v1/utils_test.gno
  • contract/r/gnoswap/protocol_fee/v1/protocol_fee.gno
  • contract/r/gnoswap/protocol_fee/v1/protocol_fee_state.gno
  • contract/r/gnoswap/protocol_fee/v1/protocol_fee_test.gno
  • contract/r/gnoswap/protocol_fee/v1/utils.gno
  • contract/r/gnoswap/router/v1/base.gno
  • contract/r/gnoswap/router/v1/base_test.gno
  • contract/r/gnoswap/router/v1/dsl_test.gno
  • contract/r/gnoswap/router/v1/exact_in.gno
  • contract/r/gnoswap/router/v1/exact_out.gno
  • contract/r/gnoswap/router/v1/protocol_fee_swap.gno
  • contract/r/gnoswap/router/v1/router_dry.gno
  • contract/r/gnoswap/router/v1/router_test.gno
  • contract/r/gnoswap/router/v1/utils.gno
  • contract/r/gnoswap/router/v1/utils_test.gno
  • contract/r/gnoswap/staker/v1/calculate_pool_position_reward.gno
  • contract/r/gnoswap/staker/v1/external_incentive.gno
  • contract/r/gnoswap/staker/v1/protocol_fee_unstaking.gno
  • contract/r/gnoswap/staker/v1/reward_calculation_incentives.gno
  • contract/r/gnoswap/staker/v1/reward_calculation_pool.gno
  • contract/r/gnoswap/staker/v1/reward_calculation_pool_tier.gno
  • contract/r/gnoswap/staker/v1/reward_calculation_warmup.gno
  • contract/r/gnoswap/staker/v1/staker.gno
  • contract/r/gnoswap/staker/v1/type.gno
  • contract/r/gnoswap/staker/v1/utils.gno
  • contract/r/gnoswap/staker/v1/utils_test.gno
💤 Files with no reviewable changes (12)
  • contract/r/gnoswap/emission/utils.gno
  • contract/r/gnoswap/launchpad/v1/utils_test.gno
  • contract/r/gnoswap/position/v1/utils.gno
  • contract/r/gnoswap/gov/governance/v1/utils.gno
  • contract/r/gnoswap/protocol_fee/v1/utils.gno
  • contract/r/gnoswap/router/v1/utils_test.gno
  • contract/r/gnoswap/gov/staker/util.gno
  • contract/r/gnoswap/gov/staker/v1/util.gno
  • contract/r/gnoswap/gov/governance/v1/utils_test.gno
  • contract/r/gnoswap/pool/v1/utils_test.gno
  • contract/r/gnoswap/staker/v1/utils.gno
  • contract/r/gnoswap/pool/v1/utils.gno

Comment thread contract/r/gnoswap/gov/staker/v1/getter.gno
Comment on lines +220 to 223
updatedTotalStakedAmount := gnsmath.SafeSubInt64(self.GetTotalStakedAmount(), amount)
if updatedTotalStakedAmount < 0 {
updatedTotalStakedAmount = 0
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the SafeSubInt64 implementation to verify underflow behavior
ast-grep --pattern 'func SafeSubInt64($_, $_) int64 {
  $$$
}'

Repository: gnoswap-labs/gnoswap

Length of output: 46


🏁 Script executed:

# Search for SafeSubInt64 definition across the codebase
rg "func SafeSubInt64" -A 10

Repository: gnoswap-labs/gnoswap

Length of output: 723


🏁 Script executed:

# Also find the gnsmath package location
fd -t f "gnsmath" -o find . -type d -name "*gnsmath*" | head -20

Repository: gnoswap-labs/gnoswap

Length of output: 191


Remove dead code: SafeSubInt64 panics on underflow, never returns negative values.

Lines 221-223 check if updatedTotalStakedAmount < 0 and clamp to 0. Since SafeSubInt64 panics when subtraction underflows (line in safe_math.gno: if b > 0 && a < math.MinInt64+b { panic(...) }), this result can never be negative. The defensive check is unreachable dead code and should be removed.

Copy link
Copy Markdown
Member

@jinoosss jinoosss left a comment

Choose a reason for hiding this comment

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

Overall, it looks fine.
I’ll double-check to make sure nothing is missing.

@jinoosss
Copy link
Copy Markdown
Member

jinoosss commented Apr 8, 2026

Gas costs are expected to rise in this PR, so it would be a good idea to check the details based on this PR.
gnolang/gno#5415

@junghoon-vans junghoon-vans self-assigned this Apr 8, 2026
Copy link
Copy Markdown
Member

@notJoon notJoon left a comment

Choose a reason for hiding this comment

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

It looks good that the duplicated utility functions have been separated into the p package. We would need to measure whether the cost of defining/creating functions within the realm is higher or whether the cost of importing the p package is higher, but at least in terms of improving cohesion, I think this is a positive change. Could you please resolve the conflicts?

@sonarqubecloud
Copy link
Copy Markdown

@junghoon-vans junghoon-vans added the DO NOT MERGE do not merge this PR label Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DO NOT MERGE do not merge this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants