Skip to content

enhanced-events: Enhance Protocol Observability via Event Updates#51

Open
aniket866 wants to merge 3 commits into
DjedAlliance:mainfrom
aniket866:enhanced-events
Open

enhanced-events: Enhance Protocol Observability via Event Updates#51
aniket866 wants to merge 3 commits into
DjedAlliance:mainfrom
aniket866:enhanced-events

Conversation

@aniket866

@aniket866 aniket866 commented Feb 16, 2026

Copy link
Copy Markdown

Addressed Issues:

Closes #50

Summary of Changes

  • Added three new data points to all core trading events (BoughtStableCoins, SoldStableCoins, BoughtReserveCoins, SoldReserveCoins, and SoldBothCoins) in both Djed.sol and DjedShu.sol:

  • postRatio: The reserve ratio immediately after the transaction.

currentPrice: The exact price used for the trade.

newReserveBalance: The total amount of Base Coin (e.g., ETH/ADA) held in the contract after the trade.

Why?

  • Real-Time Monitoring: Currently, events only log basic amounts and addresses. Adding these fields allows dashboards to show the protocol's health (ratio and reserves) instantly without making extra, delayed queries to the blockchain.

  • Bot & Arbitrage Efficiency: Arbitrage bots and liquidators need to know the exact state of the protocol to function. By including the price and ratio in the event log, bots can react faster to market changes, which helps maintain the stablecoin’s peg.

  • Reduced Complexity: External applications no longer need to manually calculate the "post-trade" state; they can simply read it directly from the transaction logs.

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions.
  • If applicable, I have made corresponding changes or additions to the documentation.
  • If applicable, I have made corresponding changes or additions to tests.
  • My changes generate no new warnings or errors.
  • I have joined the Stability Nexus's Discord server and I will share a link to this PR with the project maintainers there.
  • I have read the Contribution Guidelines.
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.

⚠️ AI Notice - Important!

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.

@Zahnentferner Please review

Summary by CodeRabbit

  • New Features
    • Transaction events for all buy/sell operations now include post-transaction ratio, current price, and updated reserve balance, providing richer context for each trade (stable coin buys/sells, reserve coin buys/sells, and combined sells).

@coderabbitai

coderabbitai Bot commented Feb 16, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Event signatures for buy/sell operations in the Djed contracts were extended to include postRatio, currentPrice, and newReserveBalance; all emit sites were updated to compute and pass these additional values.

Changes

Cohort / File(s) Summary
Djed core contract
src/Djed.sol
Expanded five event declarations to append postRatio, currentPrice, newReserveBalance. Updated corresponding emit calls in buyStableCoins, sellStableCoins, buyReserveCoins, sellReserveCoins, and sellBothCoins to compute and pass newReserveBalance, postRatio, and currentPrice. Minor formatting/comments added around emits.
Djed SHU variant
src/DjedShu.sol
Same event signature expansions and emit updates across buy/sell functions. Added function computePostRatio(uint256 newReserveBalance) internal view returns (uint256) to centralize post-operation ratio computation; emits now include the computed postRatio, currentPrice, and newReserveBalance.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hop through logs with eager cheer,
I stamp the price and ratio near,
Reserve counts tucked in every line,
Off-chain friends can now opine,
Carrot-coded clarity, crisp and clear 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately describes the main change: enhancing event observability through event updates with new fields.
Linked Issues check ✅ Passed The PR fully implements all requirements from issue #50: adds postRatio, currentPrice, and newReserveBalance to all five trading events in both files.
Out of Scope Changes check ✅ Passed All changes directly support issue #50's objectives; only trading events are modified with the three specified fields, no unrelated changes detected.
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
  • Post copyable unit tests in a comment

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/Djed.sol`:
- Around line 103-106: The emitted post-trade ratio uses a stale price: keep scP
= scPrice(msg.value) as the pre-trade/currentPrice but compute postRatio using
scPrice(0) (the post-trade reserve-based price used by ratio() and
isRatioAboveMin) so the event reflects the same post-trade semantics;
specifically in buyStableCoins compute postRatio with scPrice(0) when combining
newReserveBalance and stableCoin.totalSupply() (after mint) and update the same
pattern in buyReserveCoins (use scPrice(0) for postRatio while retaining
scP/scPrice(msg.value) as the currentPrice variable).
- Around line 133-136: The BoughtReserveCoins/SoldReserveCoins/SoldBothCoins
events are emitting scP as the "currentPrice" which misrepresents the actual
per-RC execution price; update the emit calls so BoughtReserveCoins emits rcBP
(the buy-RC per-unit price computed at line with rcBP), SoldReserveCoins emits
rcTargetPrice(scP, 0) (the sell-RC per-unit price), and for SoldBothCoins
include both scP and the RC per-unit price (rcTargetPrice(scP,0)) or rename the
event field to scPrice if you prefer keeping scP; adjust the event signature(s)
and all emit sites (BoughtReserveCoins, SoldReserveCoins, SoldBothCoins) to
match the chosen approach so event consumers receive the actual per-unit
price(s).

In `@src/DjedShu.sol`:
- Around line 108-111: postRatio is computed using different price variants per
function (scMaxPrice(msg.value), scMinPrice(0), scMinPrice(msg.value),
scMaxPrice(0), etc.), making the emitted postRatio ambiguous and sometimes using
a pre-trade/stale price; update all five functions (buyStableCoins,
sellStableCoins, buyReserveCoins, sellReserveCoins, sellBothCoins) to compute
postRatio using a single consistent price definition — prefer scMaxPrice(0) to
match ratioMax() — and always call the price helper with offset 0 (no msg.value)
so post-trade price is consistent; factor the ratio computation into a shared
helper (e.g., computePostRatio or similar) that takes newReserveBalance and
stableCoin.totalSupply() and returns postRatio to avoid duplication and ensure
all events emit the same, unambiguous price.
- Around line 177-178: The require check is using L(scMinPrice(0)) but the
intended stronger bound compares R(0)/L(scMaxPrice(0)) against preR/preL; update
the expression in the require inside sellBoth to use L(scMaxPrice(0)) instead of
L(scMinPrice(0)) so it reads require(R(0) * preL >= preR * L(scMaxPrice(0)),
"sellBoth: ratio decreased"); alternatively, if you intentionally want the
weaker bound keep the code and change the comment to document that the check
uses scMinPrice and thus enforces the weaker inequality; ensure references to
R(0), preR, preL, L(...), scMinPrice(0) and scMaxPrice(0) are consistent.
🧹 Nitpick comments (1)
src/Djed.sol (1)

103-106: Extract postRatio computation into a helper to eliminate duplication.

The identical three-line block (compute newReserveBalance, compute postRatio with the zero-supply guard, emit) is repeated across all five trading functions. A private helper would consolidate the logic, reduce bytecode size, and make future changes (e.g., fixing the price used for ratio) a single-point edit.

Proposed helper
+    function _computePostRatio(uint256 _scPrice) internal view returns (uint256 newReserveBalance, uint256 postRatio) {
+        newReserveBalance = R(0);
+        uint256 totalSC = stableCoin.totalSupply();
+        postRatio = (totalSC == 0 || _scPrice == 0)
+            ? 0
+            : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (totalSC * _scPrice);
+    }

Then each emit site becomes:

(uint256 newReserveBalance, uint256 postRatio) = _computePostRatio(scPrice(0));
emit BoughtStableCoins(msg.sender, receiver, amountSC, msg.value, postRatio, scP, newReserveBalance);

Also applies to: 118-121, 133-136, 149-152, 168-173

Comment thread src/Djed.sol
Comment thread src/Djed.sol Outdated
Comment thread src/DjedShu.sol Outdated
Comment thread src/DjedShu.sol Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/DjedShu.sol (1)

252-255: Redundant scMaxPrice(0) calls — consider returning scP from the helper.

computePostRatio computes scMaxPrice(0) internally, but every call site also passes scMaxPrice(0) separately as the currentPrice argument in the emit. This doubles the oracle read cost per transaction. In sellBothCoins, scMaxPrice(0) is called up to four times.

Returning the price from the helper eliminates the duplication:

♻️ Proposed refactor
-    function computePostRatio(uint256 newReserveBalance) internal view returns (uint256) {
-        uint256 scP = scMaxPrice(0);
-        return (stableCoin.totalSupply() == 0 || scP == 0) ? 0 : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scP);
-    }
+    function computePostRatio(uint256 newReserveBalance) internal view returns (uint256 postRatio, uint256 scP) {
+        scP = scMaxPrice(0);
+        postRatio = (stableCoin.totalSupply() == 0 || scP == 0) ? 0 : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scP);
+    }

Then at each call site (e.g. buyStableCoins):

         uint256 newReserveBalance = R(0);
-        uint256 postRatio = computePostRatio(newReserveBalance);
-        emit BoughtStableCoins(msg.sender, receiver, amountSC, msg.value, postRatio, scMaxPrice(0), newReserveBalance);
+        (uint256 postRatio, uint256 currentPrice) = computePostRatio(newReserveBalance);
+        emit BoughtStableCoins(msg.sender, receiver, amountSC, msg.value, postRatio, currentPrice, newReserveBalance);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (2)
src/Djed.sol (2)

134-136: ⚠️ Potential issue | 🟠 Major

Use the post-trade SC price for buyReserveCoins.postRatio.

postRatio is still dividing by the pre-trade scP from Line 125. After minting RC and retaining fees, the reserve state has changed, so this event can emit a stale ratio even though currentPrice should stay rcBP.

Proposed fix
         uint256 newReserveBalance = R(0);
-        uint256 postRatio = (stableCoin.totalSupply() == 0 || scP == 0) ? 0 : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scP);
+        uint256 scPPost = scPrice(0);
+        uint256 postRatio = (stableCoin.totalSupply() == 0 || scPPost == 0)
+            ? 0
+            : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scPPost);
         emit BoughtReserveCoins(msg.sender, receiver, amountRC, msg.value, postRatio, rcBP, newReserveBalance);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Djed.sol` around lines 134 - 136, The emitted postRatio in
BoughtReserveCoins is using the pre-trade scP and can be stale; after minting RC
and applying fees you must recompute the stablecoin price/parameters from the
updated state and use those to compute postRatio. Update the logic around
newReserveBalance/postRatio (referencing newReserveBalance, postRatio,
stableCoin.totalSupply(), scP, currentPrice, rcBP and the BoughtReserveCoins
event) so that you first apply the mint/fee state changes, derive the post-trade
sc price (e.g., use currentPrice or recompute scP from the updated
totalSupply/reserve), then compute postRatio using the post-trade scP and
updated stableCoin.totalSupply() before emitting the event.

38-38: ⚠️ Potential issue | 🟠 Major

SoldBothCoins.currentPrice is still ambiguous.

sellBothCoins executes with both scP and rcP (Lines 159-160), but the event schema only exposes one currentPrice slot and the emit picks rcP. That still falls short of the PR objective to expose the exact price used for the trade from logs alone.

Also applies to: 172-174

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Djed.sol` at line 38, The SoldBothCoins event only exposes a single
ambiguous currentPrice while sellBothCoins computes both scP and rcP; update the
event schema to include distinct price fields (e.g., currentSCPrice and
currentRCPrice or scPrice and rcPrice) and change the emit in sellBothCoins to
pass both scP and rcP accordingly; also apply the same change to the analogous
event/emit pair referenced around lines 172-174 so logs unambiguously show the
exact prices used for the trade.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Djed.sol`:
- Around line 119-121: The emitted postRatio uses the pre-trade price variable
scP but is logged after reserve/supply have been updated; update the code paths
in the sell flow (the blocks that compute newReserveBalance = R(0) and then
compute postRatio) to recompute scP using the post-trade state before
calculating postRatio (i.e., derive the new SC price from the updated reserve
and stableCoin.totalSupply(), or recalculate scP via the same function that
produces the on-chain price after modifying reserves/supply) and then use that
recomputed scP in the postRatio formula (still using scalingFactor,
scDecimalScalingFactor and stableCoin.totalSupply()). Ensure all three sell-side
emit points (those using newReserveBalance, R(0), and dividing by scP) are
changed to use the post-trade scP.

---

Duplicate comments:
In `@src/Djed.sol`:
- Around line 134-136: The emitted postRatio in BoughtReserveCoins is using the
pre-trade scP and can be stale; after minting RC and applying fees you must
recompute the stablecoin price/parameters from the updated state and use those
to compute postRatio. Update the logic around newReserveBalance/postRatio
(referencing newReserveBalance, postRatio, stableCoin.totalSupply(), scP,
currentPrice, rcBP and the BoughtReserveCoins event) so that you first apply the
mint/fee state changes, derive the post-trade sc price (e.g., use currentPrice
or recompute scP from the updated totalSupply/reserve), then compute postRatio
using the post-trade scP and updated stableCoin.totalSupply() before emitting
the event.
- Line 38: The SoldBothCoins event only exposes a single ambiguous currentPrice
while sellBothCoins computes both scP and rcP; update the event schema to
include distinct price fields (e.g., currentSCPrice and currentRCPrice or
scPrice and rcPrice) and change the emit in sellBothCoins to pass both scP and
rcP accordingly; also apply the same change to the analogous event/emit pair
referenced around lines 172-174 so logs unambiguously show the exact prices used
for the trade.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b8d48770-2abc-4f48-857e-6e9606920d2d

📥 Commits

Reviewing files that changed from the base of the PR and between 1eb0f1b and 914e886.

📒 Files selected for processing (2)
  • src/Djed.sol
  • src/DjedShu.sol
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/DjedShu.sol

Comment thread src/Djed.sol
Comment on lines +119 to +121
uint256 newReserveBalance = R(0);
uint256 postRatio = (stableCoin.totalSupply() == 0 || scP == 0) ? 0 : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scP);
emit SoldStableCoins(msg.sender, receiver, amountSC, amountBC, postRatio, scP, newReserveBalance);

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 | 🟠 Major

Compute sell-side postRatio with the post-trade SC price.

These three paths emit postRatio after reserve and/or SC supply changed, but they still divide by the pre-trade scP from Lines 112, 141, and 159. In reserve-constrained states, the logged ratio will drift from the actual post-trade ratio the protocol ended with.

Proposed fix
         uint256 newReserveBalance = R(0);
-        uint256 postRatio = (stableCoin.totalSupply() == 0 || scP == 0) ? 0 : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scP);
+        uint256 scPPost = scPrice(0);
+        uint256 postRatio = (stableCoin.totalSupply() == 0 || scPPost == 0)
+            ? 0
+            : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scPPost);
         emit SoldStableCoins(msg.sender, receiver, amountSC, amountBC, postRatio, scP, newReserveBalance);
         uint256 newReserveBalance = R(0);
-        uint256 postRatio = (stableCoin.totalSupply() == 0 || scP == 0) ? 0 : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scP);
+        uint256 scPPost = scPrice(0);
+        uint256 postRatio = (stableCoin.totalSupply() == 0 || scPPost == 0)
+            ? 0
+            : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scPPost);
         emit SoldReserveCoins(msg.sender, receiver, amountRC, amountBC, postRatio, rcP, newReserveBalance);
         uint256 newReserveBalance = R(0);
-        uint256 postRatio = (stableCoin.totalSupply() == 0 || scP == 0) ? 0 : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scP);
+        uint256 scPPost = scPrice(0);
+        uint256 postRatio = (stableCoin.totalSupply() == 0 || scPPost == 0)
+            ? 0
+            : (newReserveBalance * scalingFactor * scDecimalScalingFactor) / (stableCoin.totalSupply() * scPPost);
         emit SoldBothCoins(msg.sender, receiver, amountSC, amountRC, amountBC, postRatio, rcP, newReserveBalance);

Also applies to: 151-153, 172-174

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Djed.sol` around lines 119 - 121, The emitted postRatio uses the
pre-trade price variable scP but is logged after reserve/supply have been
updated; update the code paths in the sell flow (the blocks that compute
newReserveBalance = R(0) and then compute postRatio) to recompute scP using the
post-trade state before calculating postRatio (i.e., derive the new SC price
from the updated reserve and stableCoin.totalSupply(), or recalculate scP via
the same function that produces the on-chain price after modifying
reserves/supply) and then use that recomputed scP in the postRatio formula
(still using scalingFactor, scDecimalScalingFactor and
stableCoin.totalSupply()). Ensure all three sell-side emit points (those using
newReserveBalance, R(0), and dividing by scP) are changed to use the post-trade
scP.

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.

Enhanced Event Granularity for Improved Contract State Transparency

1 participant