Skip to content

Conversation

@anjali78p
Copy link

@anjali78p anjali78p commented Dec 13, 2025

Fixes #18

This PR removes hardcoded StableCoin and ReserveCoin names/symbols and
adds a one-time initialization function to configure token metadata.

This avoids increasing the constructor argument count, as suggested
in the issue.

Summary by CodeRabbit

  • New Features
    • Contract now requires explicit initialization of coins before use via a new initialization function
    • Token metadata fields (names and symbols) are now publicly accessible for both stable and reserve coins

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 13, 2025

Walkthrough

The PR refactors Djed.sol to defer coin instantiation and initialization from the constructor to a new external function initializeCoins(). Token metadata (names and symbols) become configurable public state variables, and a coinsReady modifier guards dependent functionality. Scaling factors transition from immutable to mutable public fields.

Changes

Cohort / File(s) Change Summary
Core initialization refactoring
src/Djed.sol
Added public state variables for configurable coin names and symbols (stableCoinName, stableCoinSymbol, reserveCoinName, reserveCoinSymbol). Introduced initializeCoins() external function to defer coin creation and scaling factor setup from constructor. Added coinsReady modifier to enforce initialization guard. Changed scDecimalScalingFactor and rcDecimalScalingFactor from immutable to mutable. Added private coinsInitialized flag for one-time initialization control.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify coinsInitialized flag prevents multiple calls to initializeCoins() and properly gates initialization
  • Ensure coinsReady modifier is applied to all functions requiring initialized coins (critical for correctness)
  • Confirm scaling factors are correctly computed from the newly created coins' decimal values
  • Check for potential state consistency issues between coin creation and scaling factor setup

Poem

🐰 A rabbit hops with glee so bright,
Configurable coins, oh what a sight!
Names once hardcoded, now take flight,
Custom symbols dance in the light,
Initialization deferred just right! ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: making stablecoin and reservecoin names configurable through the addition of metadata fields and an initialization function.
Linked Issues check ✅ Passed The PR successfully implements the requirement from #18 by adding configurable token metadata fields and an initializeCoins function that allows one-time setup of coin names and symbols after deployment.
Out of Scope Changes check ✅ Passed All changes are directly related to making coin names and symbols configurable as specified in issue #18; no unrelated modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

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

Caution

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

⚠️ Outside diff range comments (1)
src/Djed.sol (1)

47-69: Contract deployment creates a vulnerable window.

The constructor no longer initializes the coins, leaving the contract in an incomplete state immediately after deployment. Between deployment and calling initializeCoins, the contract is:

  1. Unusable for its intended purpose
  2. Vulnerable to frontrunning (see critical issue on lines 72-92)

Ensure deployment scripts atomically call initializeCoins in the same transaction or block to minimize this window.

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

14-19: Consider exposing the initialization status.

The coinsInitialized flag is private, preventing external verification of whether the contract is ready to use. Consider either making it public or adding a getter function so users and integrations can check the contract's initialization state before interacting with it.

Apply this diff to make the initialization status public:

-    bool private coinsInitialized;
+    bool public coinsInitialized;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65fa433 and 9aafe72.

📒 Files selected for processing (1)
  • src/Djed.sol (3 hunks)
🔇 Additional comments (2)
src/Djed.sol (2)

100-100: Incomplete diff prevents full review.

The comment indicates the rest of the contract remains unchanged, but without seeing the complete file, it's impossible to verify:

  1. Whether the coinsReady modifier has been added to all necessary functions
  2. Whether there are any other references to coin contracts that need updating
  3. Whether the changes integrate correctly with existing functionality

Consider providing the full contract file for a comprehensive review.


95-98: This review comment is based on an incorrect assumption. The coinsReady modifier in src/Djed.sol (lines 95-98) is correctly defined, but the file is incomplete—it ends at line 101 with only the modifier definition and no functions that would use it.

The functions mentioned in the review (buyStableCoins, sellStableCoins, buyReserveCoins, sellReserveCoins, sellBothCoins) do not exist in src/Djed.sol. They are implemented in a separate contract, src/DjedShu.sol, which does not inherit from Djed and does not use the coinsReady pattern. DjedShu initializes coins in its constructor instead of via an external initializeCoins() function.

src/Djed.sol appears to be an incomplete base contract stub. If it is intended to be completed with trading functions, the coinsReady modifier should be applied to all functions that access stableCoin, reserveCoin, scDecimalScalingFactor, or rcDecimalScalingFactor. However, as currently committed, the contract contains no such functions.

Likely an incorrect or invalid review comment.

Comment on lines +38 to +39
uint256 public scDecimalScalingFactor;
uint256 public rcDecimalScalingFactor;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Scaling factors are no longer immutable, increasing gas costs.

Changing scDecimalScalingFactor and rcDecimalScalingFactor from immutable to mutable has implications:

  • Gas cost: Every read now requires an SLOAD (~2100 gas cold, 100 gas warm) instead of direct value substitution
  • Safety: These values can theoretically be modified if there's any bug in future code changes

While this change is necessary for the two-step initialization pattern, consider whether the flexibility gained outweighs the increased operational costs, especially if these scaling factors are read frequently in trading functions.

Comment on lines +72 to 92
function initializeCoins(
string memory _stableCoinName,
string memory _stableCoinSymbol,
string memory _reserveCoinName,
string memory _reserveCoinSymbol
) external {
require(!coinsInitialized, "Coins already initialized");

function ratio() external view returns (uint256) {
return scalingFactor * R(0) / L(scPrice(0));
}
stableCoinName = _stableCoinName;
stableCoinSymbol = _stableCoinSymbol;
reserveCoinName = _reserveCoinName;
reserveCoinSymbol = _reserveCoinSymbol;

// # Public Trading Functions:

function buyStableCoins(address receiver, uint256 feeUI, address ui) external payable nonReentrant {
uint256 scP = scPrice(msg.value);
uint256 amountBC = deductFees(msg.value, feeUI, ui); // side-effect: increases `treasuryRevenue` and pays UI and treasury
uint256 amountSC = (amountBC * scDecimalScalingFactor) / scP;
require(amountSC <= txLimit || stableCoin.totalSupply() < thresholdSupplySC, "buySC: tx limit exceeded");
require(amountSC > 0, "buySC: receiving zero SCs");
stableCoin.mint(receiver, amountSC);
require(isRatioAboveMin(scPrice(0)), "buySC: ratio below min");
emit BoughtStableCoins(msg.sender, receiver, amountSC, msg.value);
}
stableCoin = new Coin(_stableCoinName, _stableCoinSymbol);
reserveCoin = new Coin(_reserveCoinName, _reserveCoinSymbol);

function sellStableCoins(uint256 amountSC, address receiver, uint256 feeUI, address ui) external nonReentrant {
require(stableCoin.balanceOf(msg.sender) >= amountSC, "sellSC: insufficient SC balance");
require(amountSC <= txLimit || stableCoin.totalSupply() < thresholdSupplySC, "sellSC: tx limit exceeded");
uint256 scP = scPrice(0);
uint256 value = (amountSC * scP) / scDecimalScalingFactor;
uint256 amountBC = deductFees(value, feeUI, ui); // side-effect: increases `treasuryRevenue` and pays UI and treasury
require(amountBC > 0, "sellSC: receiving zero BCs");
stableCoin.burn(msg.sender, amountSC);
transfer(receiver, amountBC);
emit SoldStableCoins(msg.sender, receiver, amountSC, amountBC);
}
scDecimalScalingFactor = 10 ** stableCoin.decimals();
rcDecimalScalingFactor = 10 ** reserveCoin.decimals();

function buyReserveCoins(address receiver, uint256 feeUI, address ui) external payable nonReentrant {
uint256 scP = scPrice(msg.value);
uint256 rcBP = rcBuyingPrice(scP, msg.value);
uint256 amountBC = deductFees(msg.value, feeUI, ui); // side-effect: increases `treasuryRevenue` and pays UI and treasury
require(amountBC <= (txLimit * scP) / scDecimalScalingFactor || stableCoin.totalSupply() < thresholdSupplySC, "buyRC: tx limit exceeded");
uint256 amountRC = (amountBC * rcDecimalScalingFactor) / rcBP;
require(amountRC > 0, "buyRC: receiving zero RCs");
reserveCoin.mint(receiver, amountRC);
require(isRatioBelowMax(scPrice(0)) || stableCoin.totalSupply() < thresholdSupplySC, "buyRC: ratio above max");
emit BoughtReserveCoins(msg.sender, receiver, amountRC, msg.value);
coinsInitialized = true;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

CRITICAL: Missing access control allows anyone to initialize coins.

The initializeCoins function has no access control, allowing any external caller to initialize the contract and set token names/symbols. This creates a severe frontrunning vulnerability:

  1. An attacker monitoring the mempool can call initializeCoins before the legitimate deployer
  2. The attacker controls coin names/symbols, potentially setting misleading values like "USDT"/"USDC" to trick users
  3. Once initialized, the configuration cannot be changed due to the coinsInitialized guard

Additionally:

  • No validation on input strings (empty, too long, malicious characters)
  • No event emission to track initialization
  • No way to verify the initializer's identity

Apply this diff to add access control:

+    address public immutable deployer;
+
+    constructor(...) {
+        deployer = msg.sender;
         // ... existing constructor code
     }

     function initializeCoins(
         string memory _stableCoinName,
         string memory _stableCoinSymbol,
         string memory _reserveCoinName,
         string memory _reserveCoinSymbol
     ) external {
+        require(msg.sender == deployer, "Only deployer can initialize");
         require(!coinsInitialized, "Coins already initialized");
+        require(bytes(_stableCoinName).length > 0 && bytes(_stableCoinName).length <= 32, "Invalid stablecoin name");
+        require(bytes(_stableCoinSymbol).length > 0 && bytes(_stableCoinSymbol).length <= 8, "Invalid stablecoin symbol");
+        require(bytes(_reserveCoinName).length > 0 && bytes(_reserveCoinName).length <= 32, "Invalid reservecoin name");
+        require(bytes(_reserveCoinSymbol).length > 0 && bytes(_reserveCoinSymbol).length <= 8, "Invalid reservecoin symbol");

         stableCoinName = _stableCoinName;
         stableCoinSymbol = _stableCoinSymbol;
         reserveCoinName = _reserveCoinName;
         reserveCoinSymbol = _reserveCoinSymbol;

         stableCoin = new Coin(_stableCoinName, _stableCoinSymbol);
         reserveCoin = new Coin(_reserveCoinName, _reserveCoinSymbol);

         scDecimalScalingFactor = 10 ** stableCoin.decimals();
         rcDecimalScalingFactor = 10 ** reserveCoin.decimals();

         coinsInitialized = true;
+        emit CoinsInitialized(_stableCoinName, _stableCoinSymbol, _reserveCoinName, _reserveCoinSymbol);
     }

Also add the event declaration:

event CoinsInitialized(string stableCoinName, string stableCoinSymbol, string reserveCoinName, string reserveCoinSymbol);
🤖 Prompt for AI Agents
In src/Djed.sol around lines 72 to 92, the initializeCoins function lacks access
control and input validation; restrict it so only the contract owner (or a
designated initializer) can call it by adding an owner state variable (set at
construction) and require(msg.sender == owner) or use an onlyOwner modifier,
keep the existing coinsInitialized guard, validate input strings (non-empty and
reasonable max length) before using them, emit a
CoinsInitialized(string,string,string,string) event after successful
initialization, and (optionally) store the initializer address for auditability;
ensure decimals are read after coin deployment and all state changes occur
before emitting the event.

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.

Make the name and symbol of the Stablecoin and the Reservecoin configurable

1 participant