Skip to content

Conversation

@dark-Invincible
Copy link

@dark-Invincible dark-Invincible commented Dec 14, 2025

Resolved #16

Summary by CodeRabbit

  • New Features
    • Added support for multiple blockchain networks including Ethereum Classic Mainnet and testnet environments.
    • Improved transaction signing workflow with dedicated preparation step before final confirmation.
    • Enhanced block explorer integration with dynamic link generation across supported networks.

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

…ETC testnet to Mordor\n\n- Rename ethereum-classic testnet key to mordor-testnet\n- Add ethereum-classic mainnet (chainId 61) with Rivet RPC\n- Set DJED (0xCc3664d7021FD36B1Fe2b136e2324710c8442cCf) and ECSD (0x5A7Ca94F6E969C94bef4CE5e2f90ed9d4891918A)\n- Update explorer links for both networks\n- Add viem chain for ETC mainnet and dynamic wallet switching\n\nFixes DjedAlliance#18
…)\n\n- Add ethereum-classic (chainId 61) with Rivet RPC\n- Set DJED and ECSD addresses\n- Rename testnet key to mordor-testnet\n- Update explorers + dynamic wallet switching\n- Ignore dist/ and local tarballs\n\nFixes DjedAlliance#18
- Remove manual 'Prepare Transaction' button
- Auto-trigger transaction preparation when wallet connects
- Rename 'Send Transaction' to 'Sign & Send' for clarity
- Add preparation state with 'Preparing...' indicator
- Prevent duplicate preparation calls
- Update messaging to reflect automatic flow

Fixes DjedAlliance#16
@coderabbitai
Copy link

coderabbitai bot commented Dec 14, 2025

Walkthrough

The PR adds multi-network support to StablePay SDK by introducing Ethereum Classic Mainnet as a chain option, implementing dynamic network selection via window.__STABLEPAY_SELECTED_NETWORK__, and consolidating transaction preparation and signing into a single "Sign & Send" action with automatic pre-preparation.

Changes

Cohort / File(s) Summary
Chain and network configuration
stablepay-sdk/src/contexts/chains.js, stablepay-sdk/src/utils/config.js
Added etcMainnet chain definition (id: 61) with BlockScout explorer and ETC native currency. Introduced 'mordor-testnet' and 'ethereum-classic' mainnet configurations with explicit stablecoin addresses (0xffD4505B3452Dc22f8473616d50503bA9E1710Ac for Mordor), RPC URIs, chain IDs, and token metadata.
Wallet and network selection infrastructure
stablepay-sdk/src/contexts/WalletContext.jsx, stablepay-sdk/src/widget/Widget.jsx
Added getExpectedChain() helper to dynamically select Mordor or etcMainnet based on window.__STABLEPAY_SELECTED_NETWORK__. Replaced hardcoded Mordor references in wallet/publicClient initialization, chain switching, and error messages. Widget now stores selected network globally before rendering.
Transaction flow consolidation
stablepay-sdk/src/widget/TransactionReview.jsx
Merged two-step transaction flow (Prepare → Send) into single "Sign & Send" button with automatic transaction preparation. Added isPreparingTx state and auto-trigger effect. Updated explorer URL mapping to support 'mordor-testnet' and 'ethereum-classic'. Changed UI messaging from "Transaction ready! Click 'Send Transaction'" to "Ready to sign and send transaction".

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Widget as Widget Component
    participant WalletCtx as WalletContext
    participant ChainCfg as Chain & Config
    participant TxReview as TransactionReview
    participant Wallet as Wallet Client

    User->>Widget: Selects network
    Widget->>Widget: Store selection in window.__STABLEPAY_SELECTED_NETWORK__
    Widget->>WalletCtx: Render with context
    WalletCtx->>WalletCtx: Call getExpectedChain() based on window value
    WalletCtx->>ChainCfg: Fetch chain/config for selected network
    WalletCtx->>Wallet: Initialize walletClient & publicClient with selected chain
    WalletCtx->>Widget: Provide initialized wallet context

    User->>TxReview: Navigate to transaction review
    TxReview->>TxReview: Auto-trigger prepareTransaction effect (if wallet connected)
    TxReview->>Wallet: Prepare transaction for selected network
    Wallet-->>TxReview: Return prepared txData
    TxReview->>TxReview: Update state (isPreparingTx: false, txData populated)
    TxReview->>User: Display "Ready to sign and send transaction"

    User->>TxReview: Click "Sign & Send"
    TxReview->>Wallet: Sign & send prepared transaction
    Wallet-->>TxReview: Return transaction hash
    TxReview->>TxReview: Generate explorer link (dynamic per selected network)
    TxReview->>User: Display transaction confirmation with link
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Areas requiring extra attention:

  • WalletContext.jsx — Review chain selection logic (getExpectedChain()) across wallet initialization, publicClient setup, and chain-switching flows to ensure consistent network handling and no edge cases with missing or invalid window.__STABLEPAY_SELECTED_NETWORK__ values.
  • config.js — Verify stablecoin addresses, RPC URIs, and djedAddress values for both 'mordor-testnet' and 'ethereum-classic' networks are correct and functional.
  • TransactionReview.jsx — Validate the auto-preparation effect guard rails (re-entrancy prevention) and confirm explorer URL mapping covers all supported networks correctly.
  • Widget.jsx — Confirm the global side-effect assignment (window.__STABLEPAY_SELECTED_NETWORK__) is placed early enough in the render path and doesn't cause hydration or timing issues.

Possibly related PRs

Suggested reviewers

  • Zahnentferner

🐰✨ A rabbit hops through networks with glee,
One click now handles what took two—you'll see!
Mordor and Classic, both shine in the chain,
No more "Prepare" clicks, just sign in one lane! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Changes include multi-network support additions (etcMainnet, Mordor testnet configs) and global state management that extend beyond issue #16's scope of merging transaction steps. Remove multi-network support changes (chains.js etcMainnet export, config.js network additions, Widget.jsx global state assignment) or link them to separate issues, keeping this PR focused on the transaction step consolidation objective.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main objective: merging prepare and send transaction steps into a single pay action, matching the core requirement.
Linked Issues check ✅ Passed The PR implements all requirements from issue #16: automatic transaction preparation (TransactionReview.jsx), two-step to one-step flow consolidation, and renamed UI from 'Send Transaction' to 'Sign & Send' with updated messaging.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 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.

@dark-Invincible
Copy link
Author

@Zahnentferner I have raised the PR Kindly check

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

Caution

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

⚠️ Outside diff range comments (1)
stablepay-sdk/src/widget/TransactionReview.jsx (1)

8-8: Hardcoded stablecoin contract address may not work for all networks.

STABLECOIN_CONTRACT_ADDRESS is hardcoded, but the SDK now supports multiple networks (Mordor, ETC mainnet, Sepolia, Milkomeda) with different stablecoin addresses defined in config.js.

Consider using the stablecoin address from the network configuration:

-const STABLECOIN_CONTRACT_ADDRESS = "0xdc86935A9597aA3A9008d2f26232233043091284"; 
 // ... inside the component or function:
-          to: STABLECOIN_CONTRACT_ADDRESS,
+          to: contextTransactionDetails.stableCoinAddress || networkConfig.tokens.stablecoin.address,

Verify whether this hardcoded address is intentional for a specific use case or should be dynamic.

🧹 Nitpick comments (2)
stablepay-sdk/src/widget/Widget.jsx (1)

29-30: Side effect in render body - move to useEffect.

Assigning to window during render is a side effect that violates React's render purity expectations. This runs on every render and could cause timing issues with WalletContext reading stale values.

 const WidgetWithProviders = ({ onClose, buttonSize, networkSelector, onTransactionComplete }) => {
-  // Keep a global reference of current selection for WalletContext
-  window.__STABLEPAY_SELECTED_NETWORK__ = networkSelector?.selectedNetwork || null;
+  // Keep a global reference of current selection for WalletContext
+  React.useEffect(() => {
+    window.__STABLEPAY_SELECTED_NETWORK__ = networkSelector?.selectedNetwork || null;
+    return () => {
+      window.__STABLEPAY_SELECTED_NETWORK__ = null;
+    };
+  }, [networkSelector?.selectedNetwork]);
+
   return (

This ensures the assignment happens after render, cleans up on unmount, and only re-runs when selectedNetwork changes.

stablepay-sdk/src/widget/TransactionReview.jsx (1)

82-87: Missing function dependency in useEffect.

prepareTransaction is called inside the effect but not listed in the dependency array. While the internal guards prevent infinite loops, this is a React anti-pattern that could cause subtle bugs.

Either wrap prepareTransaction in useCallback and add it to dependencies, or inline the preparation logic:

+  const prepareTransaction = useCallback(async () => {
+    // ... existing logic
+  }, [account, contextTransactionDetails, transaction, selectedToken, tradeDataBuySc]);
+
   useEffect(() => {
     if (account && transaction && contextTransactionDetails && !txData && !isPreparingTx) {
       prepareTransaction();
     }
-  }, [account, transaction, contextTransactionDetails, txData, isPreparingTx]);
+  }, [account, transaction, contextTransactionDetails, txData, isPreparingTx, prepareTransaction]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a125cf and 79f213e.

⛔ Files ignored due to path filters (4)
  • stablepay-sdk/dist/esm/index.js is excluded by !**/dist/**
  • stablepay-sdk/dist/umd/index.js is excluded by !**/dist/**
  • stablepay-sdk/dist/umd/index.js.map is excluded by !**/dist/**, !**/*.map
  • stablepay-sdk/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • stablepay-sdk/src/contexts/WalletContext.jsx (5 hunks)
  • stablepay-sdk/src/contexts/chains.js (1 hunks)
  • stablepay-sdk/src/utils/config.js (1 hunks)
  • stablepay-sdk/src/widget/TransactionReview.jsx (7 hunks)
  • stablepay-sdk/src/widget/Widget.jsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
stablepay-sdk/src/contexts/WalletContext.jsx (1)
stablepay-sdk/src/contexts/chains.js (4)
  • etcMainnet (24-42)
  • etcMainnet (24-42)
  • mordor (3-22)
  • mordor (3-22)
stablepay-sdk/src/widget/TransactionReview.jsx (1)
stablepay-sdk/src/contexts/WalletContext.jsx (2)
  • account (18-18)
  • error (21-21)
🔇 Additional comments (4)
stablepay-sdk/src/utils/config.js (1)

60-78: LGTM!

The Ethereum Classic mainnet configuration is well-structured and consistent with the chain definition in chains.js. The chainId (61), RPC URL, and token configurations align properly.

stablepay-sdk/src/contexts/chains.js (1)

24-42: LGTM!

The etcMainnet chain definition follows the correct viem defineChain structure with appropriate chain ID (61), RPC URL, and block explorer configuration for Ethereum Classic mainnet.

stablepay-sdk/src/widget/TransactionReview.jsx (2)

196-209: LGTM!

The explorer URL mapping has been properly extended to support the new networks (mordor-testnet and ethereum-classic), aligning with the multi-network configuration changes.


233-247: UI flow aligns with PR objectives.

The "Sign & Send" button correctly replaces the previous two-step flow, and the "Preparing..." state provides good user feedback during auto-preparation. This achieves the PR goal of merging "Prepare Transaction" and "Send Transaction" into a single user action.

Comment on lines +24 to +29
const expectedChainId = (function(){ return (window.__STABLEPAY_SELECTED_NETWORK__ === 'ethereum-classic-mainnet' ? etcMainnet : mordor).id; })();
const getExpectedChain = () => {
const key = window.__STABLEPAY_SELECTED_NETWORK__;
if (key === 'ethereum-classic-mainnet') return etcMainnet;
return mordor;
};
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

Stale expectedChainId due to one-time computation.

expectedChainId is computed once via IIFE when the component mounts, but getExpectedChain() reads window.__STABLEPAY_SELECTED_NETWORK__ fresh each call. This inconsistency can cause expectedChainId to be stale if the network selection changes after mount.

Consider using getExpectedChain().id directly where needed, or computing expectedChainId consistently:

-  const expectedChainId = (function(){ return (window.__STABLEPAY_SELECTED_NETWORK__ === 'ethereum-classic-mainnet' ? etcMainnet : mordor).id; })();
   const getExpectedChain = () => {
     const key = window.__STABLEPAY_SELECTED_NETWORK__;
     if (key === 'ethereum-classic-mainnet') return etcMainnet;
     return mordor;
   };
+  const expectedChainId = getExpectedChain().id;

Note: This still captures the value once. If dynamic updates are needed during the component lifecycle, consider using state or re-reading via the function.

🤖 Prompt for AI Agents
In stablepay-sdk/src/contexts/WalletContext.jsx around lines 24 to 29, the
variable expectedChainId is computed once via an IIFE and can become stale while
getExpectedChain() reads the global selection each call; replace the one-time
IIFE with a consistent approach such as using getExpectedChain().id directly
wherever needed or converting expectedChainId to a function (e.g., () =>
getExpectedChain().id) or derive it from state/useMemo so it reflects changes to
window.__STABLEPAY_SELECTED_NETWORK__; update all usages accordingly to ensure
consistent, up-to-date chain id reading.

} finally {
setIsConnecting(false);
}
}, []);
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

Missing dependency in useCallback causes stale closure.

connectWallet has an empty dependency array but references expectedChainId from the enclosing scope. If the expected chain ID changes, this callback will use the stale value.

Add the dependency or use getExpectedChain().id inside the callback:

-  }, []);
+  }, [expectedChainId]);

Or refactor to call getExpectedChain().id directly inside the callback to always get the current value.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
}, []);
}, [expectedChainId]);
🤖 Prompt for AI Agents
In stablepay-sdk/src/contexts/WalletContext.jsx around line 107, the
connectWallet useCallback currently has an empty dependency array but reads
expectedChainId from outer scope causing a stale closure; update the callback to
either include expectedChainId (or the getter getExpectedChain) in its
dependency array, or move the call to compute getExpectedChain().id inside the
callback so it always reads the current value; ensure eslint-hooks rules are
satisfied and adjust any upstream state/refs to avoid unnecessary re-creations.

Comment on lines 113 to 127
const handleChainChanged = async (chainIdHex) => {
const newChainId = parseInt(chainIdHex, 16);
setChainId(newChainId);

if (newChainId !== expectedChainId) {
setError(`Wrong network detected. Please switch to Mordor Testnet`);
const expectedId = getExpectedChain().id;
if (newChainId !== expectedId) {
setError(`Wrong network detected. Please switch to ${getExpectedChain().name}`);
return;
}

if (window.ethereum) {
const walletClient = createWalletClient({ chain: mordor, transport: custom(window.ethereum) });
const walletClient = createWalletClient({ chain: getExpectedChain(), transport: custom(window.ethereum) });
setWalletClient(walletClient);
}
};
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

Event handlers defined inside component without memoization.

handleChainChanged and handleAccountsChanged are recreated on every render but are used with addEventListener. This can cause memory leaks since removeListener won't match the same function reference.

Wrap these handlers with useCallback:

-  const handleChainChanged = async (chainIdHex) => {
+  const handleChainChanged = useCallback(async (chainIdHex) => {
     const newChainId = parseInt(chainIdHex, 16);
     setChainId(newChainId);
     // ...
-  };
+  }, []);

-  const handleAccountsChanged = async (accounts) => {
+  const handleAccountsChanged = useCallback(async (accounts) => {
     // ...
-  };
+  }, []);

Then ensure disconnectWallet has these as dependencies, and listener cleanup is reliable.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleChainChanged = async (chainIdHex) => {
const newChainId = parseInt(chainIdHex, 16);
setChainId(newChainId);
if (newChainId !== expectedChainId) {
setError(`Wrong network detected. Please switch to Mordor Testnet`);
const expectedId = getExpectedChain().id;
if (newChainId !== expectedId) {
setError(`Wrong network detected. Please switch to ${getExpectedChain().name}`);
return;
}
if (window.ethereum) {
const walletClient = createWalletClient({ chain: mordor, transport: custom(window.ethereum) });
const walletClient = createWalletClient({ chain: getExpectedChain(), transport: custom(window.ethereum) });
setWalletClient(walletClient);
}
};
const handleChainChanged = useCallback(async (chainIdHex) => {
const newChainId = parseInt(chainIdHex, 16);
setChainId(newChainId);
const expectedId = getExpectedChain().id;
if (newChainId !== expectedId) {
setError(`Wrong network detected. Please switch to ${getExpectedChain().name}`);
return;
}
if (window.ethereum) {
const walletClient = createWalletClient({ chain: getExpectedChain(), transport: custom(window.ethereum) });
setWalletClient(walletClient);
}
}, []);
🤖 Prompt for AI Agents
In stablepay-sdk/src/contexts/WalletContext.jsx around lines 113 to 127, the
event handlers handleChainChanged and handleAccountsChanged are recreated on
every render which breaks removeListener matching and can leak memory; wrap both
handlers in useCallback with correct dependency arrays (including
functions/values they reference like getExpectedChain, setChainId, setError,
createWalletClient, setWalletClient), replace inline definitions with the
memoized callbacks, ensure disconnectWallet and any useEffect that adds
listeners list these callbacks in their dependency arrays, and call
removeListener/unsubscribe with the same memoized function references so cleanup
reliably removes the listeners.

Comment on lines +41 to +59
'mordor-testnet': { // Mordor Testnet (ETC)
uri: 'https://rpc.mordor.etccooperative.org',
chainId: 63,
djedAddress: '0xD4548F4b6d08852B56cdabC6be7Fd90953179d68',
tokens: {
stablecoin: {
symbol: 'ECSD',
address: '0xffD4505B3452Dc22f8473616d50503bA9E1710Ac', // Mordor Stablecoin
address: '0xffD4505B3452Dc22f8473616d50503bA9E1710Ac',
decimals: 18,
isDirectTransfer: true
},
native: {
symbol: 'ETC',
decimals: 18,
isNative: true
}
},
feeUI: 0
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Native token symbol inconsistency with chain definition.

The mordor-testnet native token symbol is ETC here, but in chains.js (line 10), the Mordor chain defines nativeCurrency.symbol as METC. This inconsistency could cause confusion when displaying currency symbols in the UI.

Consider aligning the symbols:

       native: {
-        symbol: 'ETC',
+        symbol: 'METC',
         decimals: 18,
         isNative: true
       }
🤖 Prompt for AI Agents
In stablepay-sdk/src/utils/config.js around lines 41 to 59, the native token
symbol for mordor-testnet is set to "ETC" but the canonical chain definition in
chains.js uses "METC"; update the symbol here to match the chain definition
(change "ETC" to "METC") and search the codebase for any other uses of the
mordor-testnet native symbol to update them consistently; run related UI/display
flows and tests to confirm the change did not break formatting or localization.

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.

Merge "Prepare Transaction" and "Send Transaction" steps into a single "Pay" step

1 participant